Skip to main content
  1. Writeups/

L4ugh CTF 2024: Micro

417 words·2 mins·
L4UGH CTF WEB EZ
Fayred
Author
Fayred
I’m French, and I’m passionate about computers in general, and computer security in particular. A CTF enthusiast and a Bug Bounty novice, I’m primarily interested in learning, having fun, and sharing what I’ve learned.
Table of Contents

Statement
#

Remember Bruh 1,2 ? This is bruh 3 :D
login with admin:admin and you will get the flag :*

Author: abdoghazy

Overview
#

For this web challenge, the source code is provided:

We can see that the server-side languages used are PHP and Python. There are two web applications, one directly accessible from the outside (PHP app) and one that is not (Python app). Indeed, to communicate with the Python web app, you must necessarily go through the PHP one.

If we look at the app.py script, we can see that you need to “log in” to the admin account to access the flag:

@app.route('/login', methods=['POST'])
def handle_request():
    try:
        username = request.form.get('username')
        password = hashlib.md5(request.form.get('password').encode()).hexdigest()
        # Authenticate user
        user_data = authenticate_user(username, password)

        if user_data:
            return "0xL4ugh{Test_Flag}"  
        else:
            return "Invalid credentials"  
    except:
        return "internal error happened"

However, in the index.php file, the Check_Admin() function prevents this:

function Check_Admin($input)
{
    $input=iconv('UTF-8', 'US-ASCII//TRANSLIT', $input);   // Just to Normalize the string to UTF-8
    if(preg_match("/admin/i",$input))
    {
        return true;
    }
    else
    {
        return false;
    }
}

Solution
#

In summary, to solve this challenge, you need to log in to the admin account despite it being blocked by the PHP Check_Admin() function.

HTTP Parameter Pollution
#

The vulnerability to exploit to access the admin account is an HPP. Indeed, the index.php program will read the username parameter from the HTTP request to check if the user is trying to log in to the admin account. If not, the request parameters will be used to make another request to the /login route of app.py (http://127.0.0.1:5000/login).

function send_to_api($data)
{
    $api_url = 'http://127.0.0.1:5000/login';
    $options = [
        'http' => [
            'method' => 'POST',
            'header' => 'Content-Type: application/x-www-form-urlencoded',
            'content' => $data,
        ],
    ];
    $context = stream_context_create($options);
    $result = file_get_contents($api_url, false, $context);
    
    if ($result !== false) 
    {
        echo "Response from Flask app: $result";
    } 
    else 
    {
        echo "Failed to communicate with Flask app.";
    }
}
if(isset($_POST['login-submit']))
{
	if(!empty($_POST['username'])&&!empty($_POST['password']))
	{
        $username=$_POST['username'];
		$password=md5($_POST['password']);
        if(Check_Admin($username) && $_SERVER['REMOTE_ADDR']!=="127.0.0.1")
        {
            die("Admin Login allowed from localhost only : )");
        }
        else
        {
            send_to_api(file_get_contents("php://input"));
        }   

	}
	else
	{
		echo "<script>alert('Please Fill All Fields')</script>";
	}
}

However, index.php will check if the username parameter matches the /admin/i regex only for the last username parameter in the request. Meanwhile, app.py will check the first username parameter for account authentication.

Exploitation
#

To exploit the vulnerability, you need to send two username parameters: the first must be equal to admin because it will be used by app.py, and the second must be different from admin.

Flag: 0xL4ugh{M1cr0_Serv!C3_My_Bruuh}