<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    記一次某推上的session利用trick

    VSole2023-03-09 14:02:54

    在一次瀏覽某推中發現了發現了了一個web challenge的賞金ctf,這里從來學習一下由于使session_start()報錯引發的危害。

    正文

    題目環境地址

    http://18.185.14.202/chall1/index.php?page=showMeTheCode

    程序分析

    index.php

     define('DEV_MODE', false); class Session{    public static $id = null;    protected static $isInit = false;    protected static $started = false;     public static function start(){        self::$isInit = true;        if (!self::$started) {            if (!is_null(self::$id)) {                session_id(self::$id);                self::$started = session_start();            } else {                self::$started = session_start();                self::$id = session_id();            }        }    }     public static function stop(){        if (self::$started) {            session_write_close();            self::$started = false;        }    }     public static function destroy() {        session_destroy();    }     public static function set($key, $value){        if (!isset($_SESSION) || self::get($key) == $value) {            return;        }        if (!self::$started) {            self::start();            $_SESSION[$key] = $value;            self::stop();        } else {            $_SESSION[$key] = $value;        }    }     public static function get($key){        if (isset($_SESSION)) {            return $_SESSION[$key];        }        return null;    }     public static function isInit(){        return self::$isInit;    }} class User {    private $users;    private $states = ['start', 'checkCreds', 'credsValid', 'userState', 'connected', 'error'];    private $banlist = ['blackhat', 'notkindguy'];    function __construct(){        $userFile = '/users.txt';        $fp = fopen($userFile,'r');        while(($userLine = fgets($fp))!==false){            $user = explode(':',trim($userLine),2);            $this->users[] = $user;        }    }     function login($username, $password){        $state = Session::get('state');        if($state === 'connected' && Session::get('authenticated') === true) exit;        if(method_exists($this,$state)){            $this->$state($username, $password);        } else {            $this->start($username, $password);        }    }     function start($username, $password) {        // NOT IN USE FOR NOW        Session::set('state', 'checkCreds');        $this->login($username, $password);    }     function checkCreds($username, $password) {        foreach($this->users as $user) {            if($username === $user[0] && $password === $user[1]) {                Session::set('state', 'credsValid');                $this->login($username, $password);                return;            }        }        Session::set('state', 'error');        $this->login($username, $password);    }     function credsValid($username, $password) {        Session::set('user', $username);        Session::set('state', 'userState');        $this->login($username, $password);    }     function userState($username, $password) {        if(in_array($username, $this->banlist)) {            Session::set('user',null);            Session::set('state','error');            $this->login($username, $password);            return;        } else {            Session::set('state', 'connected');            $this->login($username, $password);        }    }     function connected($username, $password) {        Session::set('authenticated',true);        echo "Welcome $username, you're connected! Have a great day.";    }     function error($username, $password) {        echo "Your login or password is incorrect, or you're banned :(";        Session::destroy();        return;    }     function getFlag() {        if(Session::get('user') === 'admin' && Session::get('authenticated')) {            echo file_get_contents('/flag.txt');        } else {            echo "No flag for you";        }    }} Session::start();$users = file_get_contents('/users.txt');if(isset($_GET['page'])) {    switch($_GET['page']) {        case 'login':            $user = new User();            $user->login($_GET['username'],$_GET['password']);            break;        case 'flag':            $user = new User();            $user->getFlag();            break;        case 'showMeTheCode':            highlight_file(__FILE__);            exit;        }}
    

    users.txt

    user:user
    

    我們僅僅只有一個賬戶username為user, password為user。

    我們又怎么能夠達到在登陸admin賬戶之后進行flag的獲取?

    那么肯定是需要越權的實現了。

    簡單分析一下代碼吧。

    class Session{    public static $id = null;    protected static $isInit = false;    protected static $started = false;     public static function start(){        self::$isInit = true;        if (!self::$started) {            if (!is_null(self::$id)) {                session_id(self::$id);                self::$started = session_start();            } else {                self::$started = session_start();                self::$id = session_id();            }        }    }     public static function stop(){        if (self::$started) {            session_write_close();            self::$started = false;        }    }     public static function destroy() {        session_destroy();    }     public static function set($key, $value){        if (!isset($_SESSION) || self::get($key) == $value) {            return;        }        if (!self::$started) {            self::start();            $_SESSION[$key] = $value;            self::stop();        } else {            $_SESSION[$key] = $value;        }    }     public static function get($key){        if (isset($_SESSION)) {            return $_SESSION[$key];        }        return null;    }     public static function isInit(){        return self::$isInit;    }}
    

    這個Session類主要是封裝了一些有關session的創建銷毀及擴展了一些功能。

    至于在其下的User類的邏輯。

    存在有一個__construct這個魔術方法,在創建對象的時候將會進行調用。

    主要是從users.txt中讀取賬戶。

    存在有login函數:

    function login($username, $password){    $state = Session::get('state');    if($state === 'connected' && Session::get('authenticated') === true) exit;    if(method_exists($this,$state)){        $this->$state($username, $password);    } else {        $this->start($username, $password);    }}
    

    傳入username和password參數,首先從session中獲取state值,如果其為connected 并且已經被被認證了就會直接退出。

    對應的如果存在有從state中獲得的方法,就會調用其方法。

    如果沒有,就調用start函數。

    function start($username, $password) {    // NOT IN USE FOR NOW    Session::set('state', 'checkCreds');    $this->login($username, $password);}
    

    他會創建一個$_SESSION['state'] = checkCreds,之后再次調用login方法,根據上面的描述將會調用checkCreds方法。

    function checkCreds($username, $password) {    foreach($this->users as $user) {        if($username === $user[0] && $password === $user[1]) {            Session::set('state', 'credsValid');            $this->login($username, $password);            return;        }    }    Session::set('state', 'error');    $this->login($username, $password);}
    

    在這個方法中,進行了身份的校驗,通過從users.txt中獲取的賬戶對傳入的參數username和password進行了判斷,這里進行了強比較,所以也就不存在php的弱比較繞過了。

    如果不滿足校驗將創建一個$_SESSION['state'] = error,之后調用login方法,進而調用了error方法。

    function error($username, $password) {    echo "Your login or password is incorrect, or you're banned :(";    Session::destroy();    return;}
    

    在error方法中將會銷毀掉session并返回null。

    如果通過了前面的校驗,就會創建一個$_SESSION['state'] = credsValid, 之后再次調用login,進而調用了credsValid方法。

    function credsValid($username, $password) {    Session::set('user', $username);    Session::set('state', 'userState');    $this->login($username, $password);}
    

    在這個方法中將會將傳入的username參數創建一個$_SESSION['user'] = $username 和 $_SESSION['state'] = 'userState',之后調用了login方法,進而調用了userState方法。

    function userState($username, $password) {    if(in_array($username, $this->banlist)) {        Session::set('user',null);        Session::set('state','error');        $this->login($username, $password);        return;    } else {        Session::set('state', 'connected');        $this->login($username, $password);    }}
    

    如果傳入的useranme參數在banlist名單中將會出現異常(有一說一,我感覺沒有任何作用)。

    private $banlist = ['blackhat', 'notkindguy'];
    

    如果不存在,就會調用connected方法。

    function connected($username, $password) {    Session::set('authenticated',true);    echo "Welcome $username, you're connected! Have a great day.";}
    

    賦予$_SESSION['authenticated'] = true

    那么我們最后需要達到的目標就是:

    function getFlag() {    if(Session::get('user') === 'admin' && Session::get('authenticated')) {        echo file_get_contents('/flag.txt');    } else {        echo "No flag for you";    }}
    

    不僅需要username 為admin, 而且還是需要認證的admin才會得到flag。

    我們通過上面的分析似乎走進了死胡同,但是還是有可以突破的點。

    突破

    我們通過上面的分析,相信我們能夠注意到在credsValid方法中存在和我們傳入參數進行交互的點。

    他在代碼中將其傳入給了session中的user值,如果我們能夠在這一步使得傳入的username為admin,是不是后面獲取flag就是格外的輕松了呢?

    那是直接傳入admin還是有一個問題!

    那就是調用credsValid方法之前還有一步通過調用了checkCreds判斷了username和password的可用性。

    但是如果我們能夠獲取到credsValid那一步的cookie, 修改我們的cookie值在傳入username=admin是不是就可以成功突破了呢?

    基于這樣的思路,我們翻閱php manual的文檔

    https://www.php.net/manual/en/function.session-id.php

    發現在這里存在有這樣一段話:

    If id is specified and not null, it will replace the current session id. session_id() needs to be called before session_start() for that purpose. Depending on the session handler, not all characters are allowed within the session id. For example, the file session handler only allows characters in the range a-z A-Z 0-9 , (comma) and - (minus)!

    在session_id調用過程中不是所有的字符都能夠存在于cookie中的

    他只允許在a-z A-Z 0-9 , -等字符,如果我們使用特殊字符他是否會報錯呢?報什么錯?有什么危害?

    它能夠使得session_start方法發生錯誤。

    我們可以嘗試一下他的作用。

    curl 'http://18.185.14.202/chall1/index.php?page=login&username=user&password=user' -H 'Cookie: PHPSESSID=$' -v
    

    what's this !

    我們居然得到了很多程序運行中set的cookie值,那么其中一個是否是我們需要的呢?

    當然有!

    curl 'http://18.185.14.202/chall1/index.php?page=login&username=admin&password=123' -H 'Cookie: PHPSESSID=ugr7im9314nhn283bse6j0gf4r' -v
    

    成功登陸上了admin(中途刷新了一下cookie的,所以導致cookie不一樣。

    之后就是通過這個cookie進行flag的獲取。

    curl 'http://18.185.14.202/chall1/index.php?page=flag' -H 'Cookie: PHPSESSID=ugr7im9314nhn283bse6j0gf4r' -v
    

    那么為什么會產生這種錯誤呢,主要是因為沒有對PHPSESSID的值進行校驗,使得產生錯誤,執行了Session類的stop方法中的session_write_close()方法。

    總結

    算是總結了關于php session利用的一個小trick。

    cookiesession
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    會話機制Cookie/Session:在web應用中,常用的會話追蹤機制是CookieSession
    跨語言移植一直是技術領域內難以解決的問題,需要解決語言之間的約束,好在先前我們成功使用 Go 實現了 IIOP 協議通信,有了前車之鑒,所以這次我們將繼續使用跨語言方式實現 Flask Session 偽造。本文以 Apache Superset 權限繞過漏洞(CVE-2023-27524) 為例講述我們是如何在 Go 中實現 Flask 框架的 Session 驗證、生成功能的。
    它最初于2002年在Microsoft Internet Explorer 6 SP1中實現,以防止敏感信息被盜。所有現代的后端語言和環境都支持設置HttpOnly標志。該標志防止通過中間人攻擊盜竊cookie 。請注意,只能在HTTPS連接期間設置此標志。此標志用于幫助防止跨站點請求偽造攻擊。例如,在2019年,Google Chrome瀏覽器更改了SameSite cookie的默認行為。即使cookie標志對于許多攻擊有效,也不能用作跨站點腳本的補救措施。攻擊者可能想出辦法來規避限制。
    淺析JWT安全問題
    2022-08-08 15:44:18
    觀察確定為JWT,將payload處字符base64解碼得??把sub的wiener修改為administrator,重新傳參??成功越權,然后就是刪除用戶即可?然后前往jwt.io生成我們需要的的jwt,把sub和secret進行修改??重新傳包,成功?
    花點時間弄懂XSS攻擊
    2022-06-10 22:31:38
    由于直接在用戶的終端代碼執行,惡意代碼能夠直接獲取用戶的信息,利用這些信息冒充用戶向網站發起攻擊請求.XSS攻擊有哪些類型?反射型XSS反射型XSS漏洞常見于通過URL傳遞參數的功能,如網站搜索,跳轉等。如何防御反射型XSS攻擊對url查詢參數進行轉義后再輸出到頁面。
    二. Apifox 做的改進1. Apifox的整體功能定位Apifox 是 API 文檔、API 調試、API Mock、API 自動化測試一體化協作平臺。支持 Markdown 所見即所得地編寫非 API 文檔的普通文檔。API 文檔支持密碼保護和生效時間,可生成多份不同內容和權限的文檔。支持綁定接口,接口發生變化時,自動更新測試用例。支持執行循環次數和用例之間設置時間間隔。
    程序直接檢查是否存在該sessionid文件,但是如果將sessionid構造為指定路徑下已存在的文件,即可繞過身份驗證邏輯缺陷2:在upload.cgi文件sub_10DC4函數中,存在邏輯缺陷漏洞。
    起源是在某個數據包里看到url=這個關鍵字,當時第一想到會不會有SSRF漏洞。
    [VNCTF2022]gocalc0復現
    2022-05-07 16:02:38
    看雪論壇作者ID:H3h3QAQ
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类