jakec / dec 2020
An incredible and banal thing to do with one particularly idle and yet somehow formative part of your life is to go back to school as an adult. When I did it in 2016, I was either going to enroll in one of the then-new cybersecurity degrees, or plain old Information Technology. As a music journalist staring down the barrel of being not-a-music-journalist but unsure of what shape would fill the not-ness swallowing up the present, broad seemed best. Road tested. Versatile. I took old-fangled route and wouldn’t you know it, ended up running HDs in classes like cryptography anyway. But for the most part it was HEAVY on software development. So now we get to play with a little bit of code.
Today’s scenario: Santa’s devs have set up a website for “elves to upload pictures of any suspicious people hanging around the factory,” but we have to pentest it first. “Pentest” sounds like something a 13 year old would scratch into a desk and its meaning isn’t much less salacious: it means “penetration test”, and unlike a security audit, which aims to comprehensively analyse the vulnerabilities in a system, a penetration test is mostly smaller in scope (although usually part of a security audit.) Just the fact that you can access the inaccessible, and how, is often enough.
So we’re going to break into the nascent surveillance state the elves are establishing. Let’s hit the website.
The site asks me to enter my user ID to access the login form, which the scenario already gave me. This is a light first test to make sure I understand what a GET request is and how parameters can be passed to a website through its URL.
A “GET request” is something all web servers can inherently do. Yesterday, I briefly explained JSON and what a key-value pair is: you have a key, like “username”, which is sort of like a category or a descriptor, and a value, like “jakec”, which is a bit of data corresponding to the key. Username = jakec. If I want to get someone’s username, but I don’t know what their username is going to be, I can write code that will look for the value corresponding with a key, and it’ll return whatever that value is at the time the code runs, or where it runs. GET requests basically involve the same structure, but the information you’re GETting is in the URL of a website itself.
When you go to Google and type in “henry cavill”, the URL might look something like “google.com/search?q=henry cavill”. That “q” (meaning “query”, but which could be “x” or “searchterm” or “bestfriend”) is like the key, and “henry cavill” is like the value. So Google could have a bit of code on their servers that looks for whatever value is associated with the “q” key in that URL and then do something with it (in their case, show me a lot of images of The Man from U.N.C.L.E.). Instead of going to the search bar on Google’s page, you could just change “q=henry cavill” in that URL to “q=elizabeth debicki” and reload the page; it’d perform another GET request and then show you a lot of images of Henry Cavill’s co-star in The Man from U.N.C.L.E., rising Australian icon Elizabeth Debicki.
Google doesn’t always know what you’re going to search for. But it knows it will always associate the search terms with the “q” key (when we’re talking about GET requests, we usually talk about “parameters” rather than “keys”). Rather than writing code like:
And on and on. Instead, they can just write code that says:
And it’ll account for whatever comes after “google.com/search?q=” in that URL. Nice.
This concept is important to get because it’s arguably the most fundamental part of programming. Rather than account for every possible instance, we use variables that store changing pieces of information and reference them instead.
So if I have to make the website perform a GET request with the ID I’ve been given, how do I do that? Just like with the Google example. http://10.10.93.153?id=therandomidiwasgiven
So here we have an upload form. If we look at the source, we can see it’s a basic HTML form that accepts three formats. Now we could upload thousands of rude photos and effectively DoS the service, making it unusable. (DoS means Denial-of-Service, typically flooding a website with traffic until it freezes up or crashes.)
But instead we’re gonna try uploading a program that the website thinks is an image. That program will open a connection from the web server to our computer which will allow us to access files on the server we wouldn’t ordinarily have access to. This is called a reverse shell (“reverse” because it’s a server connecting to us, rather than us connecting to a server) and it’s a very common part of web exploitation.
Kali has plenty of reverse shell scripts but we’ll use this PHP one because that’s what TryHackMe suggests, and I get the feeling they have some idea about what’ll work on the challenge they created. I have to change two variables at the start of the script: The “$ip” variable, which will be associated through the rest of the script with the IP address I give it, and the “$port” variable. A “port” in networking terms is a way of directing network traffic to particular applications, rather than just a particular IP. If an IP address is like your home address, the port is which room you live in. If I know your IP is 169.254.111.111, I can send data to you, and get data as well. But let’s say you’re running a website, an email server, some Windows shared folders, or a media streaming app on your machine. The way my computer knows how to get data/send data from/to those services specifically is they each have a different port. For example, when you type an IP address into a web browser, it’s not just going to 169.254.111.111. It’s actually (probably) going to 169.254.111.111 port 80 (or 169.254.111.111 port 443 for secure connections.) To make the reverse shell open a connection from the web server to my machine, I have to give it a port number to operate on.
NB: An important note from the Discord about port numbers and reverse shells.
4444 was recommended in the walkthrough so rather than try to be cooler than I am and open a connection on port 31337 or something bespoke, opening it on a port I knew would work seemed wise.
Let’s save our code. Speaking of code, when you make an upload form for your website, you’re probably expecting people to upload heaps of neat memes and fun holiday photos. But by default, forms on the web need extra code to validate that they’re actually uploading real images, and not just programs renamed as images. This probably seems bonkers, but most of the web (and the internet in general) is intrinsically vulnerable. All those headlines about cybersecurity being THE concern of the era? This is why. Fundamentally, the building blocks of the web are designed to make stuff work, not protect whatever that stuff is. Having said that, best practices for becoming relatively secure are pretty simple with a little bit of effort. Unfortunately these elves forgot The Web Programmer’s Praxis: If time you don’t put in, you’ll be put in hell / Remember to validate your input to stop reverse shells.
By renaming the file “php-reverse-shell.jpg.php”, the form will see “.jpg”, do ABSOLUTELY NO MORE WORK to determine whether it’s actually an image, and upload it to the server.
Now we need to actually access the script from wherever it’s been uploaded to. A lot of websites have generic folders into which things are uploaded, like “upload” or “images” or something. Here is the way you’re meant to do this next part, using Kali’s totally sweet suite of Hacker Tools:
Open a program called dirbuster.
Set the threads to EXTREME. This makes it go faster.
Let it run. It will rapidly check thousands of URLs to see if it gets any hits, then make a list of every folder on the website.
This is a great recon tool, but despite being advanced technology, it can still take a while to sort through all the potential URLs.
Here is the way I did it: I typed in “10.10.93.153/upload” and it didn’t work. I typed in “10.10.93.153/images” and it didn’t work. I typed in “10.10.93.153/uploads” and it worked.
I opened another terminal window in Kali and started a program called netcat with this command: nc -lvnp 4444.
“nc” is the name of the program (netcat.) “-lvnp” are basically options that I’m setting for what I want the program to do; in this case “l” means “listen for incoming connections”, “v” means give verbose output so I can more easily tell what’s going on, “n” means don’t try to look up any hostnames, and “p” means run on the port I’m giving you: 4444. This is the port I specified earlier in the reverse shell code.
If I go back to the browser and click on my reverse shell, it’ll run on the web server, and therefore get the server to connect back to my computer (listening via netcat on port 4444) as a privileged user.
And from here, THM asks me to find the flag in /var/www/flag.txt, so we run a program called “cat” (concatenate) to display the text in that file. That shows me a big string of text that I put into the TryHackMe challenge site and means I've finished this challenge.
A couple of gotchas that came from helping people in Discord today:
- When you’re editing the reverse shell code, make sure you don’t accidentally edit out any semicolons or equals signs. One person spent a frustrated couple hours trying to get their otherwise perfect process to work until I pointed out that they'd typed “$port 1234;” instead of “$port = 1234;”
- The $ip variable in the code should be your IP, not the IP of the server you’re attacking. As it’s a reverse shell, it needs to know where to look (your computer) from where it runs (the target computer.) Point it back to you by setting this variable with your own IP.
- When you access the script through the browser and it gives you an error message, make sure to check the terminal window where netcat is running. Your browser isn’t going to go, “Hey, great job! Reverse shell: hacktivated!” Only netcat will tell you whether it made a connection or not. A bunch of people hadn’t realised they’d actually done all the right things because of this.