[PWN] ORW-爆破可写内存地址
至于 orw 的概念和流程,就不过多赘述了。常规 orw 的重要一步是将 flag read 到一块可写内存地址,一般直接读到栈顶或者 bss 段,但是有时会遇到 寄存器全部置零 + 没有可用的 bss + 开pie
的情况,无法直接得到可写地址,需要爆破一段地址。
下面假设程序
-
沙箱只允许使用
open
read
write
-
输入长度不限
-
写入 shellcode 区域权限在写入前为 可读可写可执行,写入后为 可读可执行(不可写)
-
运行 shellcode 前所有寄存器置零(除了 rip )
-
x64
-
开启 pie 保护
-
没有 flag 文件路径字符串
牢记 orw 的系统调用方式
-
open :
fd = open(filename,0)
,rax = 2 -
read :
read(fd,bufaddr,size)
,rax = 0 -
write :
write(1,bufaddr,size)
,rax = 1
step1. open
调用 open,需要把寄存器设为如下内容
寄存器 | 值 |
---|---|
rdi | 文件目录的字符串地址 |
rsi | 0,代表只读 |
rax | 2,open 的系统调用号 |
rsi 和 rax 很好布置,关键在于 rdi,程序中没有 flag 文件路径的字符串,又不能直接传 /flag
进去
对于这个问题目前想的有两个办法,一种是在 shellcode 内嵌字符串,一种是先爆破可写地址,/flag
压栈以后用这个字符串的地址去进行 open。下面只演示第一种。
1 | lea rdi, [rip + 0x00] |
将 [rip + 0x00]
作为文件名地址加载到 rdi,其中 0x00
代指一个偏移量
rip 用于指向下一条指令的地址,在完整 shellcode 中,.ascii "./flag\\x00"
距离 lea rdi, [rip + 0x00]
长度是固定的,所以经过替换 0x00
为一个合适的值,就可以让 [rip + 0x00]
指向 .ascii "./flag\\x00"
,实现 让 rdi 指向 文件目录的字符串地址
替换步骤放到最后,姑且留个坑。注意,.ascii "./flag\\x00"
要放到 shellcode 最后。
step2. read
调用 read,需要把寄存器设为以下内容
寄存器 | 值 |
---|---|
rdi | 3,文件描述符 |
rsi | ___,写入位置(未知) |
rdx | 0x50,写入长度(合适即可) |
rax | 0,read的系统调用号 |
先假设写入到 0xabcdef
这个位置
1 | mov rdi, 3 |
(mov rdi, 3
也可以写为 mov rdi, rax
。open 之后会将返回值(文件描述符)保存到 rax,对于这种情况,通常为3)
我们需要做的就是爆破一个所谓的 0xabcdef
去写入 flag
大多数函数的返回值都会保存到 rax 寄存器上,read 也不例外。写入失败(当前写入地址不是可写地址) 和 写入成功(当前地址可写,成功写入内容) 的 返回值 rax 是不同的,可以通过 read 后的 rax 值来判断是否写入成功。如果写入失败就更改写入地址再次尝试,直至成功。
对于返回值,这里引用 csdn
1、如果读取成功,则返回实际读到的字节数。这里又有两种情况:一是如果在读完count要求字节之前已经到达文件的末尾,那么实际返回的字节数将小于count值,但是仍然大于0;二是在读完count要求字节之前,仍然没有到达文件的末尾,这是实际返回的字节数等于要求的count值。
2、如果读取时已经到达文件的末尾,则返回0。
3、如果出错,则返回-1。
这里用 test
+ jnz
汇编进行判断
对于 test 指令详细用法,参考 csdn
1 | mov r15, 0x777777777777 |
这段 shellcode 流程为:
-
指定了一个起始爆破地址,存到 r15。开启 pie 后,从 0 开始爆破显然不现实,给出合适起始值能大大缩减爆破时间。
-
设置好 read 的系统调用号,将 r15 的值作为写入位置,进行 read
-
如果 test read 成功,跳到 write 进行输出。如果失败,跳到 add_r15 增加 r15 的值,然后再次进行 read
结合一下 open 和 read
1 | lea rdi, [rip + 0x00] |
step3. write
按照正常调用即可
寄存器 | 值 |
---|---|
rdi | 1 |
rsi | 输出位置的地址 |
rdx | 0x20 |
rax | 1 |
1 | write: |
raw shellcode
1 | lea rdi, [rip + 0x00] |
patch shellcode
根据上面所说,open 步骤使用的内嵌 "./flag"
字符串,相对偏移是 0x00
,需要对这个值进行修正
1 | raw_shellcode = asm(shellcode) |