siunam's Website

My personal website

Home Writeups Blog Projects About E-Portfolio

Juggling Facts

Background

An organization seems to possess knowledge of the true nature of pumpkins. Can you find out what they honestly know and uncover this centuries-long secret once and for all?

Difficulty: Easy

In this challenge, we can spawn a docker instance and download a file:

┌──(root🌸siunam)-[~/ctf/HackTheBoo/Web/Juggling-Facts]
└─# unzip web_juggling_facts.zip 
Archive:  web_juggling_facts.zip
   creating: web_juggling_facts/
  inflating: web_juggling_facts/entrypoint.sh  
   creating: web_juggling_facts/challenge/
  inflating: web_juggling_facts/challenge/Database.php  
   creating: web_juggling_facts/challenge/views/
  inflating: web_juggling_facts/challenge/views/index.php  
   creating: web_juggling_facts/challenge/static/
   creating: web_juggling_facts/challenge/static/css/
  inflating: web_juggling_facts/challenge/static/css/index.css  
   creating: web_juggling_facts/challenge/static/js/
  inflating: web_juggling_facts/challenge/static/js/index.js  
  inflating: web_juggling_facts/challenge/Router.php  
   creating: web_juggling_facts/challenge/models/
  inflating: web_juggling_facts/challenge/models/FactModel.php  
  inflating: web_juggling_facts/challenge/models/Model.php  
   creating: web_juggling_facts/challenge/controllers/
  inflating: web_juggling_facts/challenge/controllers/Controller.php  
  inflating: web_juggling_facts/challenge/controllers/IndexController.php  
  inflating: web_juggling_facts/challenge/index.php  
   creating: web_juggling_facts/config/
  inflating: web_juggling_facts/config/nginx.conf  
  inflating: web_juggling_facts/config/supervisord.conf  
  inflating: web_juggling_facts/config/fpm.conf  
  inflating: web_juggling_facts/build-docker.sh  
  inflating: web_juggling_facts/Dockerfile

Find the flag

Home page:

When I press the Secret Facts, it shows: Secrets can only be accessed by admin.

IndexController.php:

<?php

class IndexController extends Controller
{
    public function __construct()
    {
        parent::__construct();
    }

    public function index($router)
    {
        $router->view('index');
    }

    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':
                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!'
                ]);
        }
    }
}

But I don’t see anything that is vulnerable… I thought it’s doing some REMOTE_ADDR bypass, but no dice.

Let’s take a step back.

Since this challenge’s name is Juggling Facts, I’ll google php juggling:

PHP juggling exploit? I never seen this before.

Now, we can dig deeper in this exploit: PayloadAllTheThings.

Sweat! It seems like the IndexController.php is vulnerable?

Let’s look at the switch statement:

        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':
                return $router->jsonify([
                    'facts' => $this->facts->get_facts('secrets')
                ]);

The first if statement is NOT vulnerable, as it’s using strict comparison (===, !==). So, we have to parse the type POST parameter.

However, the switch statement is vulnerable.

According to offical PHP documentation, switch/case does loose comparison.

It also includes a loose comparison table:

Hmm… What if I send a POST request JSON value: {"type":true}?

Since the case secrets is the first item, it can bypass the REMOTE_ADDR!

Let’s intercept the POST request in Burp Suite, and change the type to true!

Yes! We got the flag!

Conclusion

What we’ve learned:

  1. Exploiting PHP Type Juggling