您现在的位置是:网站首页> 编程资料编程资料
PHP5.0 TIDY_PARSE_FILE缓冲区溢出漏洞的解决方案_php技巧_
2023-05-25
461人已围观
简介 PHP5.0 TIDY_PARSE_FILE缓冲区溢出漏洞的解决方案_php技巧_
漏洞说明
不得不再次吐槽一下exploit-db对exp审核的质量,这个exp仍然不能触发漏洞,修改第一个参数则可以触发,我给出的poc是一个可以触发php漏洞的,问题出现在php_tidy.dll扩展中,对tidy_parse_file的第二个参数,也就是文件绝对路径没有进行长度控制和内容校验,导致在fopen失败后进入失败处理逻辑引发缓冲区溢出,下面对此漏洞进行详细分析。
软件下载:
https://www.exploit-db.com/apps/f8fb5676b6a32f7be1c8d8d373fbc2af-php-5.0.0-Win32.zip (本地下载)
PoC:
漏洞分析
首先介绍一下tidy_parse_file,主要是用来处理html标签,并且对处理字符串进行存储,主要关心的是第二个参数。
php会先调用php_module_startup函数对php.ini配置文件进行一系列初始化,包括获取extension扩展。
0:000> p
eax=10338504 ebx=100671a0 ecx=00a558a8 edx=0012fe30 esi=00a558a0 edi=003b3b98
eip=1004b038 esp=0012fc0c ebp=003b3b98 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
php5ts!zend_llist_apply+0x18:
1004b038 57 push edi
0:000> p
eax=10338504 ebx=100671a0 ecx=00a558a8 edx=0012fe30 esi=00a558a0 edi=003b3b98
eip=1004b039 esp=0012fc08 ebp=003b3b98 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
php5ts!zend_llist_apply+0x19:
1004b039 51 push ecx
0:000> p
eax=10338504 ebx=100671a0 ecx=00a558a8 edx=0012fe30 esi=00a558a0 edi=003b3b98
eip=1004b03a esp=0012fc04 ebp=003b3b98 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
php5ts!zend_llist_apply+0x1a:
1004b03a ffd3 call ebx {php5ts!display_ini_entries+0xb20 (100671a0)}
这个过程不重要,直接略过,在最后会调用php的excute_script用来执行扩展中的API。进入php_tidy.dll动态链接库中。单步跟踪这个过程。
0:000> p
eax=00a1e358 ebx=00a1e358 ecx=00a1d928 edx=00a1e2e0 esi=00000000 edi=003b3bc8
eip=00b526ea esp=0012fad0 ebp=00a1e2e0 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
php_tidy!get_module+0x16ea:
00b526ea 8b442428 mov eax,dword ptr [esp+28h] ss:0023:0012faf8=00a10001
0:000> p
eax=00a10001 ebx=00a1e358 ecx=00a1d928 edx=00a1e2e0 esi=00000000 edi=003b3bc8
eip=00b526ee esp=0012fad0 ebp=00a1e2e0 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
php_tidy!get_module+0x16ee:
00b526ee 57 push edi
0:000> p
eax=00a10001 ebx=00a1e358 ecx=00a1d928 edx=00a1e2e0 esi=00000000 edi=003b3bc8
eip=00b526ef esp=0012facc ebp=00a1e2e0 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
php_tidy!get_module+0x16ef:
00b526ef 50 push eax
0:000> p
eax=00a10001 ebx=00a1e358 ecx=00a1d928 edx=00a1e2e0 esi=00000000 edi=003b3bc8
eip=00b526f0 esp=0012fac8 ebp=00a1e2e0 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
php_tidy!get_module+0x16f0:
00b526f0 51 push ecx
0:000> p
eax=00a10001 ebx=00a1e358 ecx=00a1d928 edx=00a1e2e0 esi=00000000 edi=003b3bc8
eip=00b526f1 esp=0012fac4 ebp=00a1e2e0 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
php_tidy!get_module+0x16f1:
00b526f1 e8ca010000 call php_tidy!get_module+0x18c0 (00b528c0)
0:000> p
ModLoad: 719c0000 719fe000 C:\WINDOWS\System32\mswsock.dll
ModLoad: 76ef0000 76f17000 C:\WINDOWS\system32\DNSAPI.dll
ModLoad: 76f90000 76f96000 C:\WINDOWS\system32\rasadhlp.dll
ModLoad: 60fd0000 61025000 C:\WINDOWS\system32\hnetcfg.dll
ModLoad: 71a00000 71a08000 C:\WINDOWS\System32\wshtcpip.dll
eax=00a24198 ebx=00a1e358 ecx=00a24198 edx=00a10000 esi=00000000 edi=003b3bc8
eip=00b526f6 esp=0012fac4 ebp=00a1e2e0 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
php_tidy!get_module+0x16f6:
00b526f6 83c420 add esp,20h
在00b526f1函数位置,会调用php_tidy的一个函数,单步步过时,可以看到加载了一系列dll,比如DNSAPI,mswsock,可以猜测这个函数的主要功能应该是和第一个参数的服务器建立连接,观察一下这个call函数的参数。
0:000> bp 00b526f1
*** WARNING: Unable to verify checksum for C:\php\ext\php_tidy.dll
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\php\ext\php_tidy.dll -
0:000> g
Breakpoint 1 hit
eax=00a10001 ebx=00a1e358 ecx=00a1d928 edx=00a1e2e0 esi=00000000 edi=003b3bc8
eip=00b526f1 esp=0012fac4 ebp=00a1e2e0 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
php_tidy!get_module+0x16f1:
00b526f1 e8ca010000 call php_tidy!get_module+0x18c0 (00b528c0)
0:000> dd esp
0012fac4 00a1d928 00a10001 003b3bc8 00a1e2e0
0:000> dc 00a1d928
00a1d928 70747468 772f2f3a 622e7777 75646961 http://www.baidu
00a1d938 6d6f632e baadf000 abababab abababab .com............
第一个参数的内容就是tidy_parse_file函数的第一个参数,果然没错,这个函数执行完毕后继续单步跟踪。
0:000> p
eax=00a1e420 ebx=00a1e358 ecx=00a1da80 edx=00a1e3e8 esi=00a1d8b8 edi=003b3bc8
eip=00b5287e esp=0012fae0 ebp=00a1e2e0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
php_tidy!get_module+0x187e:
00b5287e 50 push eax
0:000> p
eax=00a1e420 ebx=00a1e358 ecx=00a1da80 edx=00a1e3e8 esi=00a1d8b8 edi=003b3bc8
eip=00b5287f esp=0012fadc ebp=00a1e2e0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
php_tidy!get_module+0x187f:
00b5287f e83c260000 call php_tidy!get_module+0x3ec0 (00b54ec0)
程序执行到00b5287f地址位置,这里再次调用了一个call函数,来看一下参数情况。
0:000> dd esp
0012fadc 00a1e420 00a1da80 003b3bc8 0012fb880:000> dd 00a1da80
00a1da80 41414141 41414141 41414141 41414141
00a1da90 41414141 41414141 41414141 41414141
00a1daa0 41414141 41414141 41414141 41414141
00a1dab0 41414141 41414141 41414141 41414141
00a1dac0 41414141 41414141 41414141 41414141
00a1dad0 41414141 41414141 41414141 41414141
00a1dae0 41414141 41414141 41414141 41414141
00a1daf0 41414141 41414141 41414141 41414141
终于接收到了畸形字符串,作为第二个参数传入。查看一下这个函数的IDA pro伪代码。
signed int __cdecl sub_10004EC0(int a1, int a2) { signed int result; // eax@2 if ( a1 ) result = sub_1000AC30(a1, a2); else result = -22; return result; }很简单的函数逻辑,第二个参数会作为内部参数再次传递,调用sub_1000AC30,或者直接返回-22,单步跟踪。
0:000> p
eax=00a1e420 ebx=00a1e358 ecx=00a1da80 edx=00a1e3e8 esi=00a1d8b8 edi=003b3bc8
eip=00b54ecc esp=0012fad8 ebp=00a1e2e0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
php_tidy!get_module+0x3ecc:
00b54ecc 51 push ecx
0:000> p
eax=00a1e420 ebx=00a1e358 ecx=00a1da80 edx=00a1e3e8 esi=00a1d8b8 edi=003b3bc8
eip=00b54ecd esp=0012fad4 ebp=00a1e2e0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
php_tidy!get_module+0x3ecd:
00b54ecd 50 push eax
0:000> p
eax=00a1e420 ebx=00a1e358 ecx=00a1da80 edx=00a1e3e8 esi=00a1d8b8 edi=003b3bc8
eip=00b54ece esp=0012fad0 ebp=00a1e2e0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
php_tidy!get_module+0x3ece:
00b54ece e85d5d0000 call php_tidy!get_module+0x9c30 (00b5ac30)
0:000> dd esp
0012fad0 00a1e420 00a1da80 00b52884 00a1e420
0:000> dc 00a1da80
00a1da80 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00a1da90 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
00a1daa0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
这里会进入if语句,直接调用sub_1000AC30函数,参数也继续传递,跟入这个函数。
int __cdecl sub_1000AC30(int a1, int a2) { return sub_1000AC50(a1, (char *)a2, (int)aAscii); }这个也很简单,继续跟入。
signed int __cdecl sub_1000AC50(int a1, char *a2, int a3) { v3 = a1; v20 = *(_DWORD *)(a1 + 1168); v4 = (const char *)sub_1000AB80((int)a2); v5 = (char *)v4; v21 = v4; v6 = a1 + 72; v7 = fopen(v4, Mode); v8 = v7; v19 = v7; v9 = sub_1000BBA0(a3); if ( !v8 || v9 < 0 ) { sub_10013E80(a1, (int)v5, 2); return -1; }在内部函数中,会执行一处fopen操作,打开的对象是v4,v4又是直接从a2获取的,a2就是刚才直接传递的畸形字符串,很显然,打开畸形字符串肯定是失败的。
0:000> p
eax=00000001 ebx=00a1e420 ecx=00000000 edx=00b796a0 esi=00000000 edi=00a1da80
eip=00b5aca7 esp=0012da5c ebp=00a1e468 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
php_tidy!get_module+0x9ca7:
00b5aca7 85f6 test esi,esi
0:000> p
eax=00000001 ebx=00a1e420 ecx=00000000 edx=00b796a0 esi=00000000 edi=00a1da80
eip=00b5aca9 esp=0012da5c ebp=00a1e468 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
php_tidy!get_module+0x9ca9:
00b5aca9 0f8410020000 je php_tidy!get_module+0x9ebf (00b5aebf) [
