這道題名副其實,果真是套娃,一層一層把我頭都繞暈了
原題位址:https://merak-ctf.site/challenges#%E5%A5%97%E5%A8%83
從題目已經看出他的套路了,打開題目位址一看,标準的開場沒有什麼意外
右鍵檢視源碼,發現有一段注釋
可以觀察出來又是一道if套娃語句,需要一層一層解
同樣先将這段代碼格式化
//1st
$query = $_SERVER['QUERY_STRING'];
if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
die('Y0u are So cutE!');
}
if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
echo "you are going to the next ~";
}
複制
首先将
$_SERVER['QUERY_STRING']
的值賦給變量
$query
關于
$_SERVER['QUERY_STRING']
擷取的值:
1,http://localhost/aaa/ (打開aaa中的index.php)
結果:
$_SERVER['QUERY_STRING'] = "";
$_SERVER['REQUEST_URI'] = "/aaa/";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";
2,http://localhost/aaa/?p=222 (附帶查詢)
結果:
$_SERVER['QUERY_STRING'] = "p=222";
$_SERVER['REQUEST_URI'] = "/aaa/?p=222";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";
3,http://localhost/aaa/index.php?p=222&q=333
結果:
$_SERVER['QUERY_STRING'] = "p=222&q=333";
$_SERVER['REQUEST_URI'] = "/aaa/index.php?p=222&q=333";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";
由執行個體可知:
$_SERVER["QUERY_STRING"] 擷取查詢 語句,執行個體中可知,擷取的是?後面的值
$_SERVER["REQUEST_URI"] 擷取 http://localhost 後面的值,包括/
$_SERVER["SCRIPT_NAME"] 擷取目前腳本的路徑,如:index.php
$_SERVER["PHP_SELF"] 目前正在執行腳本的檔案名
此段原文位址:https://www.cnblogs.com/mitang/p/3874291.html
感謝大佬的測試
然後我們再來看第一層判斷條件為
if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 )
複制
這段代碼表示需要滿足"或"語句
而其中的substr_count函數是用于計算子串在字元串中出現的次數
再看看第二層判斷條件
if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t']))
複制
我們需要同時滿足兩個條件,其中preg_match函數用于比對正規表達式,這裡需要通過get送出參數b_u_p_t,但是參照第一層提到的substr_count函數,是以get參數不能帶下劃線,這時我們可以用點來代替下劃線,同時用%0a來進行過濾正規表達式的條件,最終get傳遞的參數為?b.u.p.t=23333%0a
根據提示進入secrettw.php,仍然是标準套路,提示需要本地才能通路
右鍵檢視源碼,發現一大堆注釋,乍一看像亂碼,實際上ctf裡的注釋大家也都知道是怎麼回事,肯定不是毫無意義的,百度一查,發現是一種叫jother的編碼,直接複制粘貼到浏覽器控制台即可解碼
執行後彈出了一段alert資訊
根據提示用hackbar通過POST送出一個Merak參數
傳回了一段代碼,應該是secrettw.php的部分源碼高亮
源碼如下
Flag is here~But how to get it? <?php
error_reporting(0);
include 'takeip.php';
ini_set('open_basedir','.');
include 'flag.php';
if(isset($_POST['Merak'])){
highlight_file(__FILE__);
die();
}
function change($v){
$v = base64_decode($v);
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) + $i*2 );
}
return $re;
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission! Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?>
複制
可以簡單的分析出secrettw.php的作用
首先用if(isset($_POST['Merak']))函數檢測是否存在參數名為Merak的POST資料,如果不存在則執行下面的語句,如果存在則執行if中的highlight_file函數高亮顯示源碼
我們已經通過POST送出Merak知道了源碼,後面就不用再送出POST了,不然會被highlight_file函數截斷,繼續看下面的語句,中間的change函數暫時不管,是轉換字元用的,後面會提到
後面的$ip = getIp();應該是使用了頭部的takeip.php中的函數來擷取用戶端ip,再将擷取到的ip指派給變量$ip
如果滿足$ip!='127.0.0.1'則執行該if内的語句,但是這段語句沒什麼用,是以我們不用管,第二個if内的語句才是我們需要執行的
第二個if的判斷條件為
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' )
複制
也就是說需要滿足兩個條件
第一個條件$ip === '127.0.0.1',這個很容易滿足,隻要讓get_ip擷取到的值為127.0.0.1就行了,一般隻有XFF和Client-ip這兩種方法,我們可以用burpsuite來送出
第二個條件
file_get_contents($_GET['2333']) === 'todat is a happy day'
首先通過file_get_content函數将整個資料讀入一個字元串中,但是後面的值使用的單引号,并且中間使用===來判斷全等,是以,經過到百度上各種CTF技巧的查找,發現這裡可以使用data:// 來進行轉換,具體用法可以參考:https://www.php.cn/manual/view/285.html
格式為
data://text/plain;base64,
将todat is a happy day進行base64編碼得到dG9kYXQgaXMgYSBoYXBweSBkYXk=,是以需要通過get送出一個名為2333的參數,值為
data://text/plain;base64,dG9kYXQgaXMgYSBoYXBweSBkYXk=
第二個if内的語句
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file']));
複制
還用到了一個名為file的get參數,用于傳回檔案内容,我們需要知道flag.php的内容,是以這裡需要file_get_content的檔案是flag.php
但是這裡要注意file_get_content函數不是直接使用的
$_GET['file']
的值,而是用到了上面說到的change函數來轉換,我們來看一下change函數的作用
function change($v){
$v = base64_decode($v);
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) + $i*2 );
}
return $re;
}
複制
首先定義用法,然後将變量進行base64解碼(這說明後面POST參數file的值必須先進行base64編碼),然後通過一段for循環,這段for循環的作用是先将字元轉換為ASCII碼,再将ASCII碼逐漸+i*2,i初始值為0,然後再轉回字元其中strlen函數作用是計算字元的數目,chr是把ASCII轉成字元,ord是把字元轉成ASCII數字經過對照ASCII碼表和計算,我們需要傳遞到file參數的值為“fj]a&fb(flag.php經過change函數轉換為fj]a&fb)”的base64值,也就是ZmpdYSZmXGI=
順帶一提,takeip.php經過change函數變換,我們需要送出的值為“t_g_af"bXp^”,不過我們用不上,有興趣的可以自己試一試
是以,我們最終送出的兩個get參數為
注意别忘了将ip改為127.0.0.1,這裡get_ip用到的方法為Client-ip
burpsuite改包放行後傳回頁面,右鍵檢視源碼
得到flag:
MRCTF{c323e009-6f72-410a-9dff-96686b411977}