一种Linux下ptrace隐藏注入shellcode技术和防御方法

2023-11-28 12:10

本文主要是介绍一种Linux下ptrace隐藏注入shellcode技术和防御方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、前言

Unix和类Unix操作系统提供的ptrace系统调用支持一个进程控制另一个进程,常被用于程序调试、分析和监测工具,例如gdb、strace等。通过ptrace可以查看和修改被控制进程的内部状态,因此渗透攻击在注入shellcode时也会使用ptrace。本文介绍一种Linux下使用ptrace隐藏注入shellcode的技术和防御方法。

二、背景

不同版本操作系统有各自实现ptrace系统调用的方式,本文只关注Linux环境,因此先简单说明Linux下ptrace系统调用的用法。首先定义控制进程(tracer)和被控制进程(tracee),tracer可以观察和控制tracee的执行流程,检查和修改tracee的内存和寄存器内容,一个tracee只能关联(attach)一个tracer,一个tracer可以关联多个tracee。需要注意的是Linux下一个tracee实际是一个线程,一个包含多个线程的进程中每个线程可以单独关联各自的tracer。所有ptrace功能通过一个接口函数调用,格式如下:

long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);

第一个参数request包含调用的具体功能,后续三个参数的含义和第一个参数相关,不同功能需要设置相应的参数,详细定义可以查看操作系统文档(man
ptrace)。图1演示了tracer控制一个tracee的过程。

图1
ptrace控制流程

图1 ptrace控制流程

tracer调用PTRACE_ATTACH功能关联指定的tracee,向tracee发送SIGSTOP信号,并调用waitpid等待tracee状态改变;

  1. 当tracee状态变成STOP,waitpid返回;

  2. tracer调用PTRACE_SYSCALL功能让tracee进入单步执行状态,并调用waitpid等待tracee状态改变;

  3. 重复步骤2)和步骤3);

  4. tracer调用PTRACE_DETACH功能让tracee恢复运行,并解除关联。

步骤3)中tracer可以检查和修改tracee的内存和寄存器内容,给渗透攻击注入shellcode提供了可能,接下来描述利用ptrace隐藏注入shellcode的技术细节。

三、技术

达成隐藏注入shellcode的目标需要解决三个问题:

  1. shellcode存放在哪里?

  2. 如何执行shellcode?

  3. 如何不被轻易发现正在运行的shellcode?

解决第一个问题,需要了解Linux进程的内存结构,如图2所示。

图2
Linux进程内存结构(x86,x86-64类似)

图2 Linux进程内存结构(x86,x86-64类似)

执行cat /proc//maps可以查看进程的内存结构,图3是top进程的内存结构。

图3
top进程内存结构

图3 top进程内存结构

每行第二个字段表示该段内存的属性,包含’x’的具有执行权限。最容易写入shellcode的位置是Memory Mapping
Segment,可以申请匿名内存段,属性为rwxp,写入shellcode,伪代码如下:

// 关联tracee
ptrace(PTRACE_ATTACH, tracee_pid, NULL, NULL)
waitpid(tracee_pid, 0, 0)
// 修改系统调用为SYS_mmap并单步执行,执行完成以后恢复执行原有代码
mem_addr = remote_mmap(tracee_pid, NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0)
// 写入shellcode到申请的内存段
poke_text(tracee_pid, (size_t) mem_addr, shellcode, SHELL_LEN)

解决了第一个问题:shellcode存放在哪里,接着解决第二个问题:如何执行shellcode,这个问题比较简单,修改寄存器rip为mem_addr,再运行tracee即可执行shellcode,伪代码如下:

// 读取tracee寄存器并备份
ptrace(PTRACE_GETREGS, tracee_pid, NULL, &regs)
memcpy(&old_regs, &regs, sizeof(struct user_regs_struct));
// 修改rip为mem_addr(shellcode的地址)
regs.rip = (u_int64_t) mem_addr;
regs.rip += 2
// 设置tracee寄存器
ptrace(PTRACE_SETREGS, tracee_pid, NULL, &regs)
// 执行shellcode,假设shellcode结尾执行了getpid系统调用
for (;;) {ptrace(PTRACE_SYSCALL, tracee_pid, NULL, NULL)waitpid(tracee_pid, 0, 0)ptrace(PTRACE_GETREGS, tracee_pid, 0, &regs)if (regs.orig_rax == 39) {// 已执行getpid系统调用,恢复tracee状态ptrace(PTRACE_SETREGS, tracee_pid, NULL, &old_regs)break}
}
// 恢复tracee运行
ptrace(PTRACE_DETACH, tracee_pid, NULL, NULL)

但是上述代码只是在tracee进程(线程)中执行了一次shellcode,还达不到隐藏注入的目的。一个简单的解决方法是在tracee所在进程中新建一个线程,在新建的线程中执行shellcode,并在shellcode中加入可以持续运行的循环。这时,通过监测进程状态难以发现注入的shellcode;如果tracee所在进程原来就包含多个线程,通过监测线程状态也难以准确判断是否被注入了shellcode;虽然检查tracee进程的内存段可以找到具有执行权限的匿名内存段,但是有些进程本来就存在具有执行权限的匿名内存段,仍然不能准确判断是否存在shellcode。综上所述,这种新建线程中执行shellcode的方式能够解决第三个问题:如何不被轻易发现正在运行的shellcode。伪代码如下:

// 设置新建线程的栈
stack_addr = remote_mmap(tracee_pid, NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0)
stack_top = stack_addr + 4096
poke_text(tracee_pid, (size_t)stack_addr, (char *)&mem_addr, sizeof(void *))
// 修改系统调用为SYS_clone并单步执行,新建线程以后恢复执行原有代码
thread_pid = remote_clone(pid, CLONE_PTRACE | CLONE_SIGHAND | CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES, stack_top)
// 在新建的线程中执行shellcode
ptrace(PTRACE_GETREGS, thread_pid, NULL, &regs)
regs.rip = (u_int64_t) mem_addr;
ptrace(PTRACE_SETREGS, thread_pid, NULL, &regs)
ptrace(PTRACE_DETACH, thread_pid, NULL, NULL)

四、防御

Linux内核使用图4描述的算法检查调用者(caller)相对目标(target)的ptrace访问权限。首先检查调用者和目标是否在同一个线程组,是则允许(allowed)使用ptrace功能;接着根据调用者和目标的用户编号(uid)和组编号(gid)是否一致、目标是否有可转存(dumpable)属性、调用方是否具有CAP_SYS_PTRACE权限,判定是否拒绝(denied)使用ptrace功能;然后调用Linux安全模块(LSM),例如:SELinux、Yama、Smack等,不同的安全模块有各自的检查判定规则;最后如果之前的检查没有拒绝使用ptrace功能,则允许使用。

图4
Linux内核ptrace访问模式检查算法

图4 Linux内核ptrace访问模式检查算法

除了在同一个线程组的情况,获得使用ptrace功能的许可必然经过Linux安全模块(LSM)的检查,因此可以配置LSM限制ptrace功能,以Yama为例:设置参数/proc/sys/kernel/yama/ptrace_scope(直接赋值或修改/etc/sysctl.conf中kernel.yama.ptrace_scope参数)可以控制ptrace的功能,参数值定义如下:

0:一个进程可以对它拥有权限的其它进程使用PTRACE_ATTACH功能。

1:一个进程只能对下属的子进程或线程使用PTRACE_ATTACH功能。

2:只有拥有CAP_SYS_PTRACE权限的进程能够对其它进程使用PTRACE_ATTACH功能。

3:任何进程均不能使用PTRACE_ATTACH或PTRACE_TRACEME功能,并且参数ptrace_scope取值不能改变。

可以根据应用需求,设置合适的/proc/sys/kernel/yama/ptrace_scope值,例如:生产环境设置成3禁用ptrace功能,开发环境设置成1用来调试程序。

此外,根据一个tracee只能关联一个tracer的规则,可以在程序开始使用PTRACE_TRACEME功能将当前线程变成tracee,之后其它进程不能再对其使用PTRACE_ATTACH功能。但是因为PTRACE_ATTACH作用于一个线程,所有相关线程都需要使用PTRACE_TRACEME才能避免所在进程被使用PTRACE_ATTACH功能。

还可以使用prctl系统调用关闭进程的转存功能,具体用法如下:

prctl(PR_SET_DUMPABLE, SUID_DUMP_DISABLE, 0, 0, 0);

使用上述系统调用后,所在进程不能被其它进程(拥有CAP_SYS_PTRACE权限的进程除外)使用PTRACE_ATTACH功能。此方法虽然只适用调用者没有CAP_SYS_PTRACE权限的情况,但能够作用于正在运行的进程,具体做法如下:

  1. 将调用prctl操作做成一个shellcode;

  2. 使用ptrace将1)中生成的shellcode注入正在运行的目标进程并执行;

  3. 恢复目标进程状态继续运行。

##最后
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

因篇幅有限,仅展示部分资料,有需要的小伙伴,可以【扫下方二维码】免费领取:

这篇关于一种Linux下ptrace隐藏注入shellcode技术和防御方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/429680

相关文章

C++实现回文串判断的两种高效方法

《C++实现回文串判断的两种高效方法》文章介绍了两种判断回文串的方法:解法一通过创建新字符串来处理,解法二在原字符串上直接筛选判断,两种方法都使用了双指针法,文中通过代码示例讲解的非常详细,需要的朋友... 目录一、问题描述示例二、解法一:将字母数字连接到新的 string思路代码实现代码解释复杂度分析三、

mysql8.0无备份通过idb文件恢复数据的方法、idb文件修复和tablespace id不一致处理

《mysql8.0无备份通过idb文件恢复数据的方法、idb文件修复和tablespaceid不一致处理》文章描述了公司服务器断电后数据库故障的过程,作者通过查看错误日志、重新初始化数据目录、恢复备... 周末突然接到一位一年多没联系的妹妹打来电话,“刘哥,快来救救我”,我脑海瞬间冒出妙瓦底,电信火苲马扁.

SpringBoot使用Jasypt对YML文件配置内容加密的方法(数据库密码加密)

《SpringBoot使用Jasypt对YML文件配置内容加密的方法(数据库密码加密)》本文介绍了如何在SpringBoot项目中使用Jasypt对application.yml文件中的敏感信息(如数... 目录SpringBoot使用Jasypt对YML文件配置内容进行加密(例:数据库密码加密)前言一、J

Spring Boot 中正确地在异步线程中使用 HttpServletRequest的方法

《SpringBoot中正确地在异步线程中使用HttpServletRequest的方法》文章讨论了在SpringBoot中如何在异步线程中正确使用HttpServletRequest的问题,... 目录前言一、问题的来源:为什么异步线程中无法访问 HttpServletRequest?1. 请求上下文与线

解读为什么@Autowired在属性上被警告,在setter方法上不被警告问题

《解读为什么@Autowired在属性上被警告,在setter方法上不被警告问题》在Spring开发中,@Autowired注解常用于实现依赖注入,它可以应用于类的属性、构造器或setter方法上,然... 目录1. 为什么 @Autowired 在属性上被警告?1.1 隐式依赖注入1.2 IDE 的警告:

SpringBoot快速接入OpenAI大模型的方法(JDK8)

《SpringBoot快速接入OpenAI大模型的方法(JDK8)》本文介绍了如何使用AI4J快速接入OpenAI大模型,并展示了如何实现流式与非流式的输出,以及对函数调用的使用,AI4J支持JDK8... 目录使用AI4J快速接入OpenAI大模型介绍AI4J-github快速使用创建SpringBoot

linux打包解压命令方式

《linux打包解压命令方式》文章介绍了Linux系统中常用的打包和解压命令,包括tar和zip,使用tar命令可以创建和解压tar格式的归档文件,使用zip命令可以创建和解压zip格式的压缩文件,每... 目录Lijavascriptnux 打包和解压命令打包命令解压命令总结linux 打包和解压命令打

SpringBoot项目注入 traceId 追踪整个请求的日志链路(过程详解)

《SpringBoot项目注入traceId追踪整个请求的日志链路(过程详解)》本文介绍了如何在单体SpringBoot项目中通过手动实现过滤器或拦截器来注入traceId,以追踪整个请求的日志链... SpringBoot项目注入 traceId 来追踪整个请求的日志链路,有了 traceId, 我们在排

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

linux如何复制文件夹并重命名

《linux如何复制文件夹并重命名》在Linux系统中,复制文件夹并重命名可以通过使用“cp”和“mv”命令来实现,使用“cp-r”命令可以递归复制整个文件夹及其子文件夹和文件,而使用“mv”命令可以... 目录linux复制文件夹并重命名我们需要使用“cp”命令来复制文件夹我们还可以结合使用“mv”命令总