在Moctf中看到一道題目:
<?php show_source(__FILE__); $c="<?php exit;?>"; @$c.=$_POST['c']; @$filename=$_POST['file']; if(!isset($filename)) { file_put_contents('tmp.php', ''); } @file_put_contents($filename, $c); include('tmp.php'); ?>
這里簡單分析下題目,並記錄下解題過程。
我們來看第二行的<?php exit;?>,這句話在開頭增加了exit過程,導致即使我們成功寫入一句話,也執行不了(這個過程在實戰中十分常見,通常出現在緩存、配置文件等等地方,不允許用戶直接訪問的文件,都會被加上if(!defined(xxx))exit;之類的限制)。在往下看,我們要post一個c與file兩個變量,而c不用說就是用來繞過exit並執行命令的。而下面的file_put_contents函數的含義為可以將一個字符串寫入文件。所以我們簡單分析得到,我們應該輸入一個c用來繞過exit,並讓c中的內容寫入filename所代表的變量內容里。
又因為這里文件包含了tmp.php,所以我們很容易想到filename應該與tmp.php相關。
也就是說,這道題目的關鍵點在於我們如何繞過這句話。
這里引入一篇blog
https://www.leavesongs.com/PENETRATION/php-filter-magic.html
這里講述了可以利用php偽協議進行繞過,簡單來說,我們如果使用 file=php://filter/write=convert.base64-decode 來進行對file變量的處理,既以base64的編碼來讀。而我們為什么要使用base64呢?因為<?php exit;?>中"<、?、;、>"等符號解碼時都會被忽略,所以命令就變成了--> “phpexit”。也就達到了我們繞過了這個函數。之后我們要想執行我們所想的代碼,可以在phpexit這七個字符后任意加一個字母(因為base64是4個字符一組進行解碼的),所以我們可以寫成
file=php://filter/write=convert.base64-decode/resource=tmp.php
也就是說以base64為編碼對tmp.php讀取。而寫入tmp.php的內容是什么呢?
這里我們可以嵌入php命令來寫入tmp.php中
將命令用base64編碼,得到PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==,然后在原有的(phpexit)基礎上添加上述base64代碼,(****這里我們為了讓其成為八位,所以任意在后面加一個a)得到
phpexitaPD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
而傳過去的值為t=aPD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
也就得到flag了,,233333