首页
社区
课程
招聘
记一次某推上的session利用trick
发表于: 2022-9-18 19:33 1058

记一次某推上的session利用trick

2022-9-18 19:33
1058

在一次浏览某推中发现了发现了了一个web challenge的赏金ctf,这里从来学习一下由于使session_start()报错引发的危害

题目环境地址

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

index.php

users.txt

我们仅仅只有一个账户usernameuser, passworduser

我们又怎么能够达到在登陆admin账户之后进行flag的获取

那么肯定是需要越权的实现了

简单分析一下代码吧

这个Session类主要是封装了一些有关session的创建销毁及扩展了一些功能

至于在其下的User类的逻辑

存在有一个__construct这个魔术方法,在创建对象的时候将会进行调用

主要是从users.txt中读取账户

存在有login函数

传入username和password参数,首先从session中获取state值,如果其为connected 并且已经被被认证了就会直接退出

对应的如果存在有从state中获得的方法,就会调用其方法

如果没有,就调用start函数

他会创建一个$_SESSION['state'] = checkCreds,之后再次调用login方法,根据上面的描述将会调用checkCreds方法

在这个方法中,进行了身份的校验,通过从users.txt中获取的账户对传入的参数username和password进行了判断,这里进行了强比较,所以也就不存在php的弱比较绕过了

如果不满足校验将创建一个$_SESSION['state'] = error,之后调用login方法,进而调用了error方法

在error方法中将会销毁掉session并返回null

如果通过了前面的校验

就会创建一个$_SESSION['state'] = credsValid, 之后再次调用login,进而调用了credsValid方法

在这个方法中将会将传入的username参数创建一个$_SESSION['user'] = $username$_SESSION['state'] = 'userState',之后调用了login方法,进而调用了userState方法

如果传入的useranme参数在banlist名单中将会出现异常(有一说一,我感觉没有任何作用)

如果不存在,就会调用connected方法

赋予$_SESSION['authenticated'] = true

那么我们最后需要达到的目标就是

不仅需要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方法发生错误

我们可以尝试一下他的作用

what's this !

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

当然有!

成功登陆上了admin(中途刷新了一下cookie的,所以导致cookie不一样

之后就是通过这个cookie进行flag的获取

那么为什么会产生这种错误呢,主要是因为没有对PHPSESSID的值进行校验,使得产生错误,执行了Session类的stop方法中的session_write_close()方法

算是总结了关于php session利用的一个小trick

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

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//