Linux进程注入2
2023-2-8 08:2:41 Author: 奶牛安全(查看原文) 阅读量:67 收藏


罗列了Linux内存马的注入手段:tmpfs, gdb, python,dd,系统调用。这些手段简直精彩绝伦。

译自:https://blog.sektor7.net/#!res/2018/pure-in-memory-linux.md

介绍

典型的攻击后活动包含侦察,信息收集和权限提升。有时候,一个黑客可能需要额外功能,比如当目标系统默认没有提供必要工具,或当他想加快攻击后的活动。

大多数情况,专门工具会上传到目标系统运行。这种方法最大的问题是,一旦检测到,留在磁盘上的工具可能会给守护者提供额外信息从而中止整个攻击行为。

近年来,有不少针对Windows无文件代码注入的研究。虽然*Nix家族(特别Linux)并没有类似规模,但过去也是有不少也不起的研究成果:

http://www.hick.org/code/skape/papers/remote-library-injection.pdf

https://grugq.github.io/docs/ul_exec.txt

http://z0mbie.daemonlab.org/infelf.html

http://phrack.org/issues/63/11.html

http://www.securitybyte.org/resources/2011/presentations/runtime-thread-injection-and-execution-in-linux-processes.pdf

https://github.com/mak/pyself

https://blog.gdssecurity.com/labs/2017/9/5/linux-based-inter-process-code-injection-without-ptrace2.html

场景

想象你面对着刚渗透的Linux服务器,想进一步深入而又不想留下任何痕迹。你需要运行额外的工具,但又不想上传任何东西到机器上。或者,因为挂载的分区设置了noexec选项,你根本无法运行任何程序。还有什么办法呢?

本文会展示如何绕过运行限制,使用系统仅有的工具来运行。在“一切皆文件”系统上,这是非常有挑战性,但当你打开思路,使用系统提供的威力,一切皆有可行。

下面是由Sektor7实验室的实验结果。

负载(shellcode)传送

找到一个可靠隐蔽的方法把传送负载/工具到目标机器一直是黑客的挑战。

最常见的方法是建立到一个新连接到存有必需工具的C2或第三方服务器,然后下载工具到感染机器。这有可能会在网络基础设施上产生额外的痕迹(如网络流量,代理日志)。

很多情况下,攻击者忘记目标机器已经有一个打开的控制通道:shell会话。在不需要和外部系统建立新TCP连接情况下,这个会话可以作为数据链来上传负载到目标机器。这种方法的缺点是,一个网络抖动可以导致数据传输和控制通道的丢失。

在本文,这两种传送方式将分别称为带外和带内。后一种选择将用作传输shell代码的主要方式。

img

展示环境

展示和实验会使用下面设置

  • 感染机器:一个kali linux虚拟机
  • 攻击者机器:Arch Linux
  • SSH连接:从攻击者机器到感染机器,模拟shell访问
  • 简单x86_64架构的hello world shellcode

img

译者注:这段代码逻辑如下

  1. 第1行说明是64位系统的代码
  2. 第3行声明_start为全局
  3. 第4行是程序入口
  4. 第5行跳转到22行这个标签,也就是23行开始执行
  5. 第23行执行时,把msg压入到栈中,然后跳到第8行执行
  6. 第8行执行,把msg弹出到rsi寄存器
  7. 第9行把rax寄存器清零
  8. 第10行把1放入rax,说明是调用write系统调用
  9. 第11行把rax的值放到rdi,说明是往标准输出写数据(fd为1)
  10. 第12,13行是把字符串长度放到rdx
  11. 第14行进行系统调用,rax指明系统调用编号,rdi第一个参数,rsi第二个参数,rdx第三个参数

仅在内存的方法

Tmpfs

攻击者存储文件的第一站是tmpfs。它把所有东西放在内核内部缓存,随着它容纳的文件来增长或收缩。另外,从glibc 2.2开始,对于POSIX共享内存,tmpfs应该挂载在/dev/shm上(shm_open(), shm_unlink())

感染机器上的tmpfs挂载情况

img

在默认情况下,/dev/shm挂载是不设置noexec标志的。如果一个偏执管理员一定把它打开,这种攻击方法就失效了,因为数据只能存放而无法执行(execve()会失败)

img

会在下面“系统调用”里继续用到/dev/shm

GDB

gdbLinux默认调试工具。它一般不会安装在生产机器,只会在开发环境或一些嵌入式/专属系统上安装。根据gdb手册

GDB可以做四样主要事情(加那些支持这四样的功能)来帮助定位问题:

  • 启动程序,指定一切可以影响程序的因素
  • 让程序在指定条件下暂停
  • 当程序暂停时,检查所发生的事情
  • 在调试过程中改变程序的内容,可以改变一个问题的效果来进行下一个问题定位

GDB最后一个特性可以用来运行只在内存中的shellcode,不需要涉及到任何文件。

首先,把shellcode转换成字符串

img

然后,在gdb控制情况下运行/bin/bash,在main函数打断点,注入shellcode,然后继续运行

img

python

python作为一种非常流行的解析型编程语言,在大多数Linux都有安装。

它的功能可以通过包括ctype在内的许多模块进行扩展,ctype提供与C兼容的数据类型,并允许调用DLL或共享库中的函数。换句话说,ctype支持构建类似C的脚本,将外部库的强大功能与对内核syscall的直接访问结合在一起。

为了让python在内存中运行shellcode,脚本需要做如下事情:

  • 通过python进程加载libc
  • mmap一块可写和执行权限的内存给shellcode
  • shellcode写入mmap出来的内存
  • 把这块内存转换成执行代码
  • 调用这块内存

下面是用python2写的脚本

img

整个脚本转成base64编码串

img

并使用一行代码发送到目标计算机:

img

自修改dd

在最罕见情况下,上面所有方法都失效,仍然有一个默认在大多数Linux系统都安装的工具可以用。它就是dd,用来转换和拷贝文件。如果把它和procfs文件系统,/proc/self/mem(当前运行进程的内存)特殊文件结合起来,就有运行内存shellcode的可能。为了达成这个目标,需要让dd在运行时修改自身。

默认的dd行为如下

img

运行时自修改的dd行为如下

img

第一件要做的事是在dd进程里找到一个位置可以拷贝shellcode进去。因为是一个运行中的进程修改自身内存,整个过程要求稳定可靠。

一个好的候选是当拷贝/覆盖成功后运行的代码。它直接变为进程退出。shellcode注入应该在PLT(过程链接表),或主代码段exit里或之前进行。

覆盖PLT是非常不稳定,因为当shellcode太长,它会覆盖exit函数使用到一些关键部分。

经过调研,发现fclose是在exit之前调用

img

fclose只在两个地方调用

img

进一步测试显示0x9c2b是运行时使用的,且它后面跟着一大串代码。这一大串代码有可能被覆盖而不会使进程崩溃。

还有两个额外障碍需要克服

  1. 在拷贝后,dd会关闭标准输入,标准输出和标准错误fd

  2. ASLR(地址空间布局随机化)

第一步可以通过bash拷贝标准输入和标准输出来解决

复制文件修饰符

重定向操作符

[n]<&word

用来复制输入文件修饰符。如果word扩展到1或多个数字,用n表示的文件描述符变成这个文件符的拷贝

shellcode前加上dup系统调用

img

第二个问题更困难。现今,大多数Linux发行版,二进制被编译成位置无关执行文件:

img

ASLR默认开启

img

幸好,Linux支持每个进程有不同的执行域。在其中,执行域告诉Linux怎样映射信号和信号处理函数。执行域允许Linux提供在其它Unix环境编译的二进制有限的支持。从2.6.12开始,出现ADDR_NO_RANDOMIZE标志来关闭运行进程的ASLR

要在运行时关闭用户态中的ASLR,可以使用setarch工具设置不同的个性标志:

img

现在所有必要的片段已经就位,可以运行自修改dd

img

系统调用

上面的方法都有一个大的不足:只能够运行shellcode,不能够运行一个执行文件。当需要更复杂的功能,纯汇编shellcode只有有限的用途且无法调整。

从3.17内核版本开始,出现了一个新的系统调用memfd_create,它可以创建一个匿名文件,返回指向匿名文件的fd。这个文件的表现和普通文件没分别。然而,它却只存在内存中,当没有引用指向它,就会自动释放。

换句话来说,Linux内核提供了一个创建内存文件的方法,且内存文件表现和普通文件一样,可以mmap/execve

以下计划包括在虚拟内存中创建基于memfd的文件,并最终把选择的工具上传到受攻击机器,而无需将它们存储在磁盘上:

  • 产生会在内存中创建memfdshellcode
  • 注入shellcodedd
  • 暂停dd
  • 准备上传的工具(这里使用静态链接的uname做例子)
  • 通过带内数据链路(通过Shell会话)将Base64编码的工具直接传输到受害者机器中,并将其传输到memfd文件中
  • 最后,运行该工具

第一步创建新的shellcode。新的shellcode重新打开标准输入和标准输出,调用memfd_create创建内存文件(AAAA),然后,调用pause暂停dd。暂停它是为了不退出,且让它的memfd文件还可以被其它进程访问(通过procfs)。在这个shellcodeexit是不会执行。

img

接着,在dd运行时自修改,暂停它,然后检查memfd是否出现

img

下一步是准备上传的工具。为了能够运行,工具最好是静态链接或者使用目标机器同版本的动态库。

img

base64编码的工具导入到memfd文件再运行它

img

注意memfd文件是可重用的。同一个fd可以通过覆盖方式来存放新的工具

img

如果感染机器运行的内核版本低于3.17呢?

有一个C函数叫shm_open,它会在内存创建一个新POSIX共享对象。实际上,POSIX共享内存对象是一个句柄,其它进程可以使用它来mmap相同的共享内存区域。

看一下shm_open的代码,它在某些shm_name上调用open

img

shm_name又是在shm_dir上申请的

img

shm_dir是_PATH_DEVshm/的拼接:

img

_PATH_DEV的定义是/dev/

可见,shm_open是在tmpfs打开/创建一个文件。

操作安全考虑

在目标机器上的任何攻击性活动都需要考虑副作用。即使尽量不用任何代码接触磁盘,操作也可能会留下一些“残留物”。

这包括(不限于):

  1. 日志(如shell历史)。在这种情况,攻击者需要确保日志被删除或覆盖(有时候由于权限是没办法的)
  2. 进程列表。有时候用户会注意到感染机器跑着其它名字的进程(如/proc//fd/3)。这可以通过在目标进程内改变argv[0]来解决。
  3. 交换。即使shellcode只在虚拟内存里,大多数情况它们会交换到磁盘。它可以通过如下方式解决:
    • mlock,mlockall, mmap,要求root或至少CAP_IPC_LOCK功能
    • sysctl vm.swappiness/proc/sys/vm/swappiness,要求root权限
    • cgroup(memory.swappiness),要求root或能够修改cgroup的权限

最后一个无法保证在高负载情况下,内存管理器不会把进程交换到磁盘里。

=========================================

文中和文末的小广广,渴望你手指的触碰!!!

请关注,转发,点“在看”,谢谢!!

暗号:7b453


文章来源: http://mp.weixin.qq.com/s?__biz=MzU4NjY0NTExNA==&mid=2247488180&idx=1&sn=2d9f8cc71287ea8fcfdbf2a30bb9b3cf&chksm=fdf979a1ca8ef0b7e52b938711242b9da227a223f7c59fa61eced637a00f2e88f0fec48ffcda#rd
如有侵权请联系:admin#unsafe.sh