-
-
记一次某推上的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
我们仅仅只有一个账户username
为user
, password
为user
我们又怎么能够达到在登陆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期)