【BUUCTF】[GXYCTF2019]BabysqliV3.01
进入题目页面如下
是一个登陆界面,尝试万能密码登录
当输入带有符号的用户名时,提示用户不存在,那尝试
弱口令密码
用户名:admin
密码:password
可以,登陆上去了,页面如下
难道是文件上传?
上传了一句话木马的文件并没有显示上传成功及文件路径,上传带有木马的图片转化成了文本且显示太大了
应该换个思路
发现URL里有传参?file=upload
想要获取源码,猜测需要构造伪协议
构造伪协议
1、home.php
http//:a6e52924-fe4f-43a7-8bec-59cf51b383c6.node5.buuoj.cn:81/home.php?file=php://filter/convert.base64-encode/resource=home
2、upload.php
http//:a6e52924-fe4f-43a7-8bec-59cf51b383c6.node5.buuoj.cn:81/home.php?file=php://filter/convert.base64-encode/resource=upload
伪协议知识点
在 Web 安全测试和开发过程中,有时候需要利用伪协议来获取目标文件的源码
PHP 环境下的伪协议利用
1. php://filter
伪协议
- 原理:
php://filter
伪协议允许你在读取文件内容之前对其进行过滤,例如进行 base64 编码,这样可以绕过一些文件包含的限制,并且避免直接执行文件中的代码。 - 假设存在一个文件包含漏洞,目标代码如下
<?php
$file = $_GET['file'];
include($file);
?>
可以构造如下 URL 来获取目标文件的源码
http://example.com/index.php?file=php://filter/read=convert.base64-encode/resource=target_file.php
- 解释:
php://filter
是伪协议的名称。read=convert.base64-encode
表示对读取的文件内容进行 base64 编码。resource=target_file.php
指定要读取的目标文件。
获取到 base64 编码后的内容后,在本地进行解码即可得到目标文件的源码。
2. data://text/plain
伪协议
- 原理:
data://text/plain
伪协议允许你直接在 URL 中嵌入数据,并将其作为文件内容进行处理。 - 针对上述文件包含漏洞,可以构造如下 URL
http://example.com/index.php?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJscyAiKTs/Pg==
这里 PD9waHAgc3lzdGVtKCJscyAiKTs/Pg==
是 <?php system("ls ");?>
的 base64 编码。
- 解释:
data://text/plain
是伪协议名称。;base64
表示后面的数据是经过 base64 编码的。- 编码后的数据就是要执行或包含的内容。
Java 环境下的伪协议利用
1. jar:
伪协议
- 原理:
jar:
伪协议可以用来访问 JAR 文件中的资源。在一些存在文件包含或资源加载漏洞的 Java 应用中,可以利用该伪协议来获取 JAR 文件中的源码。 - 假设存在一个 Java 应用,通过
URLClassLoader
加载外部资源,攻击者可以构造如下 URL:
jar:file:/path/to/your.jar!/com/example/YourClass.class
- 解释:
jar:
是伪协议名称。file:/path/to/your.jar
指定 JAR 文件的路径。!/com/example/YourClass.class
指定 JAR 文件中要访问的具体类文件。
利用伪协议获得home.php和upload.php的源码
访问后得到的页面如下
需要对以上base64编码解码,可以使用下面这个在线工具
base64解码 base64编码 在线base64解码/编码工具 iP138在线工具
按以上方法获得home.php和upload.php的源码
1、home.php源码
<?php
session_start();
echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /> <title>Home</title>";
error_reporting(0);
if(isset($_SESSION['user'])){
if(isset($_GET['file'])){
if(preg_match("/.?f.?l.?a.?g.?/i", $_GET['file'])){
die("hacker!");
}
else{
if(preg_match("/home$/i", $_GET['file']) or preg_match("/upload$/i", $_GET['file'])){
$file = $_GET['file'].".php";
}
else{
$file = $_GET['file'].".fxxkyou!";
}
echo "当前引用的是 ".$file;
require $file;
}
}
else{
die("no permission!");
}
}
?>
2、upload.php源码
<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);
}
}
?>
代码审计
类 Uploader:
构造函数 __construct:
生成一个沙盒目录,路径为当前工作目录下的 uploads 文件夹,以用户会话中的 user 值的 MD5 哈希命名。
若 $_GET['name'] 存在且不包含特定的危险字符串(如 data://、filter://、php:// 或 .),则将其赋值给 $Filename;否则,使用沙盒目录和用户会话中的 user 值加上 .txt 扩展名作为文件名。
初始化 $cmd 为一个提示信息,$token 为用户会话中的 user 值。
方法 upload:
若 $Filename 包含非字母数字字符,将 $cmd 设置为输出非法文件名的错误信息。
若上传文件大小超过 1024 字节,将 $cmd 设置为输出文件过大的错误信息。
若以上条件都不满足,将 $cmd 设置为使用 move_uploaded_file 函数将上传文件移动到指定位置的代码。
魔术方法 __toString:返回 $Filename 的值。
析构函数 __destruct:
检查 $token 是否与用户会话中的 user 值相等,若不相等,将 $cmd 设置为输出令牌检查失败的错误信息。
执行 $cmd 中的代码。
主程序逻辑:
若存在上传文件,创建 Uploader 对象并调用 upload 方法处理上传文件。
尝试读取上传文件的内容,若成功则输出文件路径和内容。
重点看以下代码
有file_get_contents($uploader)函数,这是一个读取文件的函数,猜测可以读取flag文件,通过修改Filename参数来读取flag信息
构造payload如下
http://7273c8cf-da67-4fc9-b0e5-8a1691cef3f8.node5.buuoj.cn:81/home.php?file=upload&name=/var/www/html/flag.php
访问后,上传一个按照规定内的文件,最后利用burpsuite抓包,得到flag