freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

emp3r0r: Linux下的进程注入与伪装
2022-02-13 14:32:54
所属地 北京

背景

距离《emp3r0r:Linux用户打造的Linux后渗透框架》已经过了一年,本人继续发扬年初做更新(然后鸽一年)的优良传统,水一篇关于Linux进程注入的文章。

本文和我去年在安全客发表的Linux进程注入可能略有重合,但和上次不同的是,emp3r0r加入了一个用户态ELF加载器,将其编译为shared library,并定义了__attribute__((constructor))以便被dlopen直接执行。不要问我为啥会执行,那是一个固定特性,而且你只能在使用dl家族的open时才会被ld.so加载执行,所以你如果要自己写个dlopen的话,你还得写个ELF加载器(等会儿,这个shared library是干啥的来着?)

好接着讲,这个ELF加载器(就叫它loader.so吧)自然首先要被dlopen加载才行,然后我们的emp3r0r agent就可以被加载到它所在的进程里执行了,对于procfs来说,这个被注入的进程还是原样,你用psss这样的工具看到的所有活动也都是被注入的进程发出的。

先上个图吧,这是我刚写的新界面,各位大黑阔可以试用下。

1644733200_6208a3100b2a469a2282e.png!small

技术流程

怎么调用dlopen

上面说过了,我们不会去自己实现一个dlopen,那么就只能调用现成的了。众所周知,dlopenlibdl.so的一个导出函数,那么为了调用它,我们注入的目标进程必须是链接了libdl.so的,也就是说它得事先给我们准备好了。

但如果你看一下就会发现,很多进程是没有加载libdl.so的,毕竟谁闲着没事总跟外部so文件互动呢?好在我们还有其他选择,要知道libc.so是几乎每个Linux进程都会加载的,哪怕它自己静态链接了其他libc.so,道理也是一样的,那么libc.so怎么会有一个dlopen呢?严格来说它并没有,这个函数不是libc导出函数,而是它内部的一个函数,叫做__libc_dlopen_mode,属于是Glibc的一个dlopen实现。

道理懂了,那么我们怎么在目标进程的地址空间里找到__libc_dlopen_mode呢?首先它通常不会位于一个固定的地址,其次ASLR只是把base随机化,但offset是固定的,也就是说我们只要找到libc.so的base,就可以根据offset算出__libc_dlopen_mode的地址。

鉴于我们是直接在emp3r0r agent里实现的,所以这个问题就很容易解决,直接读取procfs里的/proc/pid/maps文件就可以获取一切我们需要的东西。

1644734477_6208a80d67c6687fa161e.png!small?1644734477861

这就是我们要找的东西,从左到位依次是:起止地址(address),权限(permission),偏移量(offset),inode(不知道中文是啥,可以理解成文件系统的操作单位),长度,文件路径。

这里面我们需要关注的就是起始地址,offset,以及路径。

起始地址不必多说,就是我们要的base。权限呢必须带有x也就是执行权限,offset则是在当前base的基础上加上一个值,当计算__libc_dlopen_mode地址的时候,需要在考虑ELF镜像offset的基础上减去这个值。

怎么注入

GDB就可以直接调用进程里的任意函数,你可以在GDB里执行print __libc_dlopen("loader.so", 2),然后GDB直接就会按照你的参数来调用。

但作为一个后渗透工具,不能指望别人还安装一个GDB吧?所以我就把这个部分自己实现了。

思路的话上面也说了,现在我们有了__libc_dlopen_mode的地址,剩下的工作就是shellcode了。

如果你还有疑问,没关系,emp3r0r已经实现了这部分,直接去看代码吧。另外我实现的是动态shellcode,支持可变参数。

注入的思路很简单,就是和上次我在安全客发过的方法一样,使用ptrace把shellcode扔到$rip的位置,执行完了再把原先的register恢复了,当然你也可以不恢复,在我这里的话loader.so会在子进程里执行ELF,如果你不在乎原进程死活的话也可以在当前进程执行ELF。

这里主要讲讲shellcode,这个shellcode只需要做一件事:设置好__libc_dlopen_mode的参数,并按照给定的地址call进去。这个shellcode管杀不管埋,所以可能执行完不能干净的返回,不过没关系,哪怕挂了,我们也可以wait接住,然后ptrace恢复原先的registers(虽然也不干净),具体的cleanup工作我后续有空了再说吧。

1644734407_6208a7c7c819cbab8b8dc.png!small?1644734408142

这里需要注意的是stack alignment,push的字节数必须是16的整数倍,不足的话后面执行会挂。

由于文件路径和__libc_dlopen_mode地址都是动态的,我们需要把shellcode也弄成动态生成的。

首先把shellcode用nasm汇编器汇编成机器码,然后把字节dump出来。我有个脚本,调用rasm2实现。

1644734309_6208a76547162ae07e751.png!small?1644734309556

然后你就可以做一个shellcode模板,具体请参考emp3r0r的源码

1644734351_6208a78f56164ee1f7ee5.png!small?1644734351703

另外如果需要调试shellcode的话,你可以使用GDB的restore功能,把shellcode二进制文件恢复到$rip下面,然后就可以很方便地观察执行情况。

执行restore shellcode.bin binary $rip然后继续执行即可。

1644734962_6208a9f2299609a6fe6ab.png!small?1644734962734

其他

上述技术细节虽然不够详细,但鉴于已经开源,我就不废那么多话了。效果图就是开头的那个截图,不难看到,emp3r0r的代码已经在/sbin/init里运行了,loader.so伪装成了libpam.so注入了systemd的进程。从进程角度来看,整个emp3r0r的活动都是systemd发出的。这个注入目前还不太稳定,需要进一步优化。

另外不知道你注意到没有,我用tmux实现了一个简单的终端分屏界面,这样emp3r0r的一些输出可以从主界面剥离,放到副窗口中,这样的副窗口还包括了一个内置的bashshell,会随着你选择的agent来变化。

“内置的bashshell”是啥呢?就是我把一个完整的纯静态bash可执行文件压缩并base64编码之后塞进emp3r0r,运行时自动释放。

另外我也编译了纯静态的bettercap,效果见Github首页视频

关于前些天挺火的CVE-2021-4034 PwnKit,我也写了个武器化exploit,纯Go零依赖零BS(实名diss下那些说”如果目标环境没有gcc“的人,目标环境直接给你root得了,提权干什么)。已经集成进emp3r0r,以后会武器化更多的提权漏洞,敬请期待。

谢谢大家。

# 内网渗透 # 木马 # linux安全 # linux pwn
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录