罗列了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
实验室的实验结果。
找到一个可靠隐蔽的方法把传送负载/工具到目标机器一直是黑客的挑战。
最常见的方法是建立到一个新连接到存有必需工具的C2或第三方服务器,然后下载工具到感染机器。这有可能会在网络基础设施上产生额外的痕迹(如网络流量,代理日志)。
很多情况下,攻击者忘记目标机器已经有一个打开的控制通道:shell
会话。在不需要和外部系统建立新TCP连接情况下,这个会话可以作为数据链来上传负载到目标机器。这种方法的缺点是,一个网络抖动可以导致数据传输和控制通道的丢失。
在本文,这两种传送方式将分别称为带外和带内。后一种选择将用作传输shell
代码的主要方式。
展示和实验会使用下面设置
kali linux
虚拟机Arch Linux
SSH
连接:从攻击者机器到感染机器,模拟shell
访问x86_64
架构的hello world shellcode
译者注:这段代码逻辑如下
第1行说明是64位系统的代码 第3行声明 _start
为全局第4行是程序入口 第5行跳转到22行这个标签,也就是23行开始执行 第23行执行时,把 msg
压入到栈中,然后跳到第8行执行第8行执行,把 msg
弹出到rsi
寄存器第9行把 rax
寄存器清零第10行把1放入 rax
,说明是调用write
系统调用第11行把 rax
的值放到rdi
,说明是往标准输出写数据(fd为1)第12,13行是把字符串长度放到 rdx
第14行进行系统调用, rax
指明系统调用编号,rdi
第一个参数,rsi
第二个参数,rdx
第三个参数
攻击者存储文件的第一站是tmpfs
。它把所有东西放在内核内部缓存,随着它容纳的文件来增长或收缩。另外,从glibc
2.2开始,对于POSIX
共享内存,tmpfs
应该挂载在/dev/shm
上(shm_open(), shm_unlink()
)
感染机器上的tmpfs
挂载情况
在默认情况下,/dev/shm
挂载是不设置noexec
标志的。如果一个偏执管理员一定把它打开,这种攻击方法就失效了,因为数据只能存放而无法执行(execve()
会失败)
会在下面“系统调用”里继续用到/dev/shm
gdb
是Linux
默认调试工具。它一般不会安装在生产机器,只会在开发环境或一些嵌入式/专属系统上安装。根据gdb
手册
GDB
可以做四样主要事情(加那些支持这四样的功能)来帮助定位问题:
启动程序,指定一切可以影响程序的因素 让程序在指定条件下暂停 当程序暂停时,检查所发生的事情 在调试过程中改变程序的内容,可以改变一个问题的效果来进行下一个问题定位
GDB
最后一个特性可以用来运行只在内存中的shellcode
,不需要涉及到任何文件。
首先,把shellcode
转换成字符串
然后,在gdb
控制情况下运行/bin/bash
,在main
函数打断点,注入shellcode
,然后继续运行
python
作为一种非常流行的解析型编程语言,在大多数Linux
都有安装。
它的功能可以通过包括ctype
在内的许多模块进行扩展,ctype
提供与C
兼容的数据类型,并允许调用DLL
或共享库中的函数。换句话说,ctype
支持构建类似C的脚本,将外部库的强大功能与对内核syscall
的直接访问结合在一起。
为了让python
在内存中运行shellcode
,脚本需要做如下事情:
python
进程加载libc
mmap
一块可写和执行权限的内存给shellcode
shellcode
写入mmap
出来的内存下面是用python2
写的脚本
整个脚本转成base64
编码串
并使用一行代码发送到目标计算机:
在最罕见情况下,上面所有方法都失效,仍然有一个默认在大多数Linux系统都安装的工具可以用。它就是dd
,用来转换和拷贝文件。如果把它和procfs
文件系统,/proc/self/mem
(当前运行进程的内存)特殊文件结合起来,就有运行内存shellcode
的可能。为了达成这个目标,需要让dd
在运行时修改自身。
默认的dd
行为如下
运行时自修改的dd
行为如下
第一件要做的事是在dd
进程里找到一个位置可以拷贝shellcode
进去。因为是一个运行中的进程修改自身内存,整个过程要求稳定可靠。
一个好的候选是当拷贝/覆盖成功后运行的代码。它直接变为进程退出。shellcode
注入应该在PLT
(过程链接表),或主代码段exit
里或之前进行。
覆盖PLT
是非常不稳定,因为当shellcode
太长,它会覆盖exit
函数使用到一些关键部分。
经过调研,发现fclose
是在exit
之前调用
fclose
只在两个地方调用
进一步测试显示0x9c2b
是运行时使用的,且它后面跟着一大串代码。这一大串代码有可能被覆盖而不会使进程崩溃。
还有两个额外障碍需要克服
dd
会关闭标准输入,标准输出和标准错误fd
ASLR
(地址空间布局随机化)第一步可以通过bash
拷贝标准输入和标准输出来解决
复制文件修饰符
重定向操作符
[n]<&word
用来复制输入文件修饰符。如果
word
扩展到1或多个数字,用n表示的文件描述符变成这个文件符的拷贝
在shellcode
前加上dup
系统调用
第二个问题更困难。现今,大多数Linux
发行版,二进制被编译成位置无关执行文件:
而ASLR
默认开启
幸好,Linux
支持每个进程有不同的执行域。在其中,执行域告诉Linux
怎样映射信号和信号处理函数。执行域允许Linux
提供在其它Unix
环境编译的二进制有限的支持。从2.6.12开始,出现ADDR_NO_RANDOMIZE
标志来关闭运行进程的ASLR
。
要在运行时关闭用户态中的ASLR
,可以使用setarch
工具设置不同的个性标志:
现在所有必要的片段已经就位,可以运行自修改dd
上面的方法都有一个大的不足:只能够运行shellcode
,不能够运行一个执行文件。当需要更复杂的功能,纯汇编shellcode
只有有限的用途且无法调整。
从3.17内核版本开始,出现了一个新的系统调用memfd_create
,它可以创建一个匿名文件,返回指向匿名文件的fd。这个文件的表现和普通文件没分别。然而,它却只存在内存中,当没有引用指向它,就会自动释放。
换句话来说,Linux
内核提供了一个创建内存文件的方法,且内存文件表现和普通文件一样,可以mmap
/execve
。
以下计划包括在虚拟内存中创建基于memfd
的文件,并最终把选择的工具上传到受攻击机器,而无需将它们存储在磁盘上:
memfd
的shellcode
shellcode
到dd
dd
uname
做例子)Shell
会话)将Base64
编码的工具直接传输到受害者机器中,并将其传输到memfd文件中第一步创建新的shellcode
。新的shellcode
重新打开标准输入和标准输出,调用memfd_create
创建内存文件(AAAA
),然后,调用pause
暂停dd
。暂停它是为了不退出,且让它的memfd
文件还可以被其它进程访问(通过procfs
)。在这个shellcode
里exit
是不会执行。
接着,在dd
运行时自修改,暂停它,然后检查memfd
是否出现
下一步是准备上传的工具。为了能够运行,工具最好是静态链接或者使用目标机器同版本的动态库。
把base64
编码的工具导入到memfd
文件再运行它
注意memfd
文件是可重用的。同一个fd可以通过覆盖方式来存放新的工具
有一个C
函数叫shm_open
,它会在内存创建一个新POSIX
共享对象。实际上,POSIX
共享内存对象是一个句柄,其它进程可以使用它来mmap
相同的共享内存区域。
看一下shm_open
的代码,它在某些shm_name
上调用open
shm_name
又是在shm_dir
上申请的
shm_dir
是_PATH_DEV
与shm/
的拼接:
而_PATH_DEV
的定义是/dev/
。
可见,shm_open
是在tmpfs
打开/创建一个文件。
在目标机器上的任何攻击性活动都需要考虑副作用。即使尽量不用任何代码接触磁盘,操作也可能会留下一些“残留物”。
这包括(不限于):
shell
历史)。在这种情况,攻击者需要确保日志被删除或覆盖(有时候由于权限是没办法的)argv[0]
来解决。shellcode
只在虚拟内存里,大多数情况它们会交换到磁盘。它可以通过如下方式解决:mlock
,mlockall
, mmap
,要求root
或至少CAP_IPC_LOCK
功能sysctl vm.swappiness
或/proc/sys/vm/swappiness
,要求root
权限cgroup(memory.swappiness)
,要求root
或能够修改cgroup
的权限最后一个无法保证在高负载情况下,内存管理器不会把进程交换到磁盘里。
=========================================
文中和文末的小广广,渴望你手指的触碰!!!
请关注,转发,点“在看”,谢谢!!
暗号:7b453