Joshua Campbell

@joshua.erik.campbell

Tales From the Deep Web: Hiring In the Hacker Underground

If you’re reading HackerNoon, you’re probably pretty tech-savvy, and the chances are pretty good that if you don’t work in a tech sector, you’ve at least looked into it and tried to gain employment. Maybe you went to college and got a CS Degree, maybe you don’t have a degree. If you don’t have a degree, you’ve probably read countless articles outlining how to put passion projects up on GitHub, how to build and build and build until you have a nice portfolio, or to try getting exposure by starting a YouTube channel or a tech blog. Maybe you’re trying to get exposure and demonstrate your knowledge by writing for a publication like HackerNoon.

These are all great ways to build up your résumé and try landing that first developer job. This is essentially the same thing as building up a brand for brand recognition and reputation, only the brand is you. Essentially, landing your first gig boils down to “Make as many cool things as you can and slap your name on it, then show it off wherever you can.” After all, without a degree, you have to demonstrate that you understand what you’re doing, right? And how do you do that without a portfolio?

This is all find and dandy, in the surface world. Today, I’m putting the “hacker” in HackerNoon, and talking about the digital underground, the world of the Black Hat Hackers. In the Deep Web, you do not want anything you do to be tied to your name or identity. Any cool projects you’ve made and put up on GitHub will get you identified in the world of criminals, law enforcement, journalists and spies. Of course, if you made it onto a real forum with gatekeepers that keep skids and noobs out, chances are you had to prove yourself. In this world, demonstrating skills can get you through the door, but may not necessarily be legal. If you have to break a law to get into an underground hangout, having your real identity doxxed is not only dangerous for your freedom, it also permanently destroys your reputation, both underground and in the surface world.

This then begs the question: how do hackers and programmers get recruited in the underworld, especially if they practice good OpSec and don’t try using the same handles and identities across various forums?

To answer this question, I’m going to show you a real-life recruitment example taken from an underground forum. I will not name the forum, but I can confirm it no longer exists, vanishing in January of 2017 without warning. I can demonstrate this, because the challenge does not require one to break any laws or pop any machines in the wild, but it does force potential recruits to demonstrate practical knowledge of various disciplines and concepts.

Underground, anyone can be anyone. You don’t know who is doing the recruiting until you’ve passed, and even then, you can’t be sure. Maybe it’s a crew looking to weaponize NSA exploits into a ransomware worm, maybe it’s Booz Allen Hamilton looking for talent they haven’t been able to find submitting applications on their website, maybe it’s the FBI looking skilled hackers, or the FBI looking for black hats to keep an eye on.

I don’t know who was behind this particular challenge, and the contact email presented at the end of the challenge, assuming you get that far, is a temporary email service that would have stopped being active in mid-2017 at the latest. I was not the first to solve it — far from it — and never bothered trying to make contact with the creator of the challenge. It was, however, a lot of fun to try and crack!

The First Puzzle

This may be the only time you ever see how people get recruited in the hacker underground.

The above image is the only file attached; clearly, it’s just a black and white JPEG. You don’t need to use a magnifying glass to look for fine print, nor use photoshop to enhance the contrast in hopes something appears. No, this is the first challenge: how much do you know about JPEGs?

Using the command line, a cursory glance for file information revealed a comment: TmljZSB0cnku. To the uninitiated, it was a string of random garbage. To me, this looks like something in Base64. Decoding the string in Base64, we see the message: “Nice try.”

Of course it wasn’t going to be that easy.

When I was in high school, I started going on 4chan in 2006. These were it’s wild west days, so much more so than it is today. 4chan is an imageboard, meaning you could only post images. There was a neat little trick, however, that I learned from there, that let us trade other types of files like PDF’s. To do this, we compressed the file we wanted to trade into a ZIP file. We then were able to add the raw bytes of the ZIP file to the end of the JPEG, after the EOF header. The JPEG is able to hold extra bytes after the end of the image, and ZIP files can have bytes padding before the start of the ZIP file.

My gut feeling said this was probably a good place to start. I know that the start of file bytes for a JPEG are 0xFFD8, and the end of file bytes are 0xFFD9. So, all I would have to do to check is to open 14.jpg in GHEX, and try to find the bytes 0xFFD9. If there was anything after that, I was on the right track. After pulling the file up in GHEX, we can see that 0xFFD9 was located about 2/3rds of the way through the file, with more after it. Even better, we can see the following 2 bytes are 0x504B, ASCII characters “PK”. This means it’s a PK Zip file, and this was indeed the trick being used.

We can also see the string “private.jpg” a few lines below, showing us what’s been zipped up.

Using the terminal to unzip the embedded ZIP file is as easy as “unzip 14.jpg”. You can see unzip telling us there were 522366 bytes that made up 14.jpg that were written before the start of file headers for the PK Zip file. We can also see that it unzipped the hidden file “private.jpg”, that we could see in our hex editor just above.

So now we have a new image! Of course, using the info command won’t do us any good, and the bytes 0xFFD9 are indeed the final bytes in the file. And that’s a good thing! After all, if we made it this far, it wouldn’t really be a challenge if we just had to do the same thing all over again, would it? Of course not! The point of this is to demonstrate a wide variety of knowledge.

More Than One Way to Steganograph a Cat

Hackerman challenge: Part II.

The practice of concealing a hidden message, file, image, etc. into another image, file or video is called Steganography. Steganographic exploits against browsers are one of the coolest things I’ve ever seen, especially when they use polyglots (but that’s an article for another day). Steganography can be preferred to cryptography, because with the latter it’s quite obvious something is being hidden and encrypted. A steganographed image can be freely displayed, in the open, hiding in plain sight. Being familiar with steganography, and seeing that I have to do something different than last time, I started hunting for the various Linux based steganography tools to see if I could pull anything out of the new image.

Here, I actually got stuck for a while, until I finally came across the correct tool to use. I threw pretty much everything I had at this image, and it was while I was stuck on this step that others solved it, contacted the creator, and promptly disappeared from the forum. I, however, was determined to get as far as possible, even if I couldn’t finish it.

I spent a lot of time using the wrong tools, until I came across StegHide. StegHide is a command-line Linux tool, that lets you hide information in images, or extract hidden information from inside of them. When telling steghide to extract whatever was hidden inside of a file, it’ll often require a password. Now, the image does say the key we are seeking is 741231478963. Using an incorrect password, steghide tells us it cannot extract information. Using those numbers as the passphrase, steghide tells us it writes the hidden information to a file simply named “encrypted”.

Success. We’re getting somewhere.

The encrypted file is a very, very long string of ASCII characters. Concatenating the encrypted file spits out a very, very long string:

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

Once again, we encounter a string of Base64. We can easily use the command line to decode this, and see what comes out of it. Unfortunately, we aren’t even close to done yet, we’re only about halfway through the challenge. We aren’t lucky enough to get a paragraph of text; when we decode this, what we wind up getting looks more like garbage than the Base64 string does.

It was at this point I left to take a break and get a beer from my favorite cute bartender.

But wait! Here, we can see something we can recognize again from an earlier step: the first 2 characters of the “decrypted” file are PK! This means using Base64 to decode the ASCII string decodes into the raw bytes making up another zip file! And this zip file contains a string shortly after showing us the filename of the zipped file, “message”! So we can run this once again, only telling the terminal to dump the output into a file I call decrypted.

Passwords, Passwords Everywhere.

Once again, we need a password to get our zip file to give us it’s delicious insides. Just like the rest of this challenge, we aren’t able to recycle the previously used password 741231478963. In fact, there isn’t anything here to indicate what the password could be. Since we have nothing, we need to put our processor to use and take advantage of being able to perform thousands of calculations per second to crack the zip file’s password. While there are a handful of websites promising to use GPU’s to crack any file you upload to it, we need to be able to do it all ourselves, without exposing these potentially sensitive files to other web services.

To break the password, we are going to have to use brute force to break the password. There are a lot of ways to go about this, from writing your own bash script or Python script, to using better known programs like John the Ripper. I was personally unable to get zip2john to install, and opted to download fcrackzip and use that. The tool fcrackzip is a command line tool for linux, that can be installed easily from the apt package manager.

Upon trying to run fcrackzip against our decrypted (but still password protected) zip file, using only the — brute-force flag, we immediately encounter a problem: the literally thousands upon thousands of false positives. To filter out false positives, we can use the -u flag, to tell fcrackzip to use the unzip command.

Once again, there is a problem: it doesn’t find the password. The first clue as to why lies in examining the false positives that were reported the first time we ran fcrackzip, in that it starts at 5-character passwords. Perhaps the password is shorter than 5 characters, and fcrackzip is hanging because it’s trying every possible combination of characters longer than the actual password. We can, thankfully, specify the length of the password, by providing the flag “-l 1–4”, to try every other combination we would have skipped without specifying the password length.

Finally, we have no false positives, and we have a definitive answer: the password is “LA”. This allows us to unzip the file, and get the executable named “message”. We finally have arrived at the last file we need to work with.

Into the Catacombs

Upon running the executable, we get the following output:

Now we have a riddle. We have a hexagon, “Conquest of Gaul”, and a code array which says is equal to a long string of byte values. Seeing the value of the bytes being mostly 0x41 and above, with the exception of 0x20 and 0x2e, I can be certain this string of bytes represents ASCII text, including spaces and punctuation. The fastest way to decode the “code” is to throw it into your Python interpreter:

Python 2.7.15+ (default, Nov 27 2018, 23:36:35) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> code = "\x45\x68\x61\x20\x7a\x72\x20\x67\x62\x20\x74\x72\x67\x20\x67\x75\x72\x20\x70\x62\x71\x72\x2e"
>>> print code
Eha zr gb trg gur pbqr.
>>>

There’s text! But, it’s obviously still a code of some sort. What could it be?

Above us, we have the phrase “Conquest of Gaul”. This refers to the Roman-Gallic wars, fought between 58–50 BCE. The Gallic tribes were a fearsome force, having invaded northern Italy some 60 years prior, and were only stopped by Gaius Marius at a cost of a lot of Roman blood. Before that, still alive within Roman memory, the Gallic war party lead by Brennus sacked Rome in 390 BC. They laid siege to the only uncaptured part of the city, Capitoline Hill. The Roman leaders negotiated a ransom for their lives, in which Brennus demanded 1,000 lbs of gold. When the Romans saw the weights were rigged in favor of the Gauls, they complained, to which Brennus responded by throwing his sword on the weights, saying “Vae Victus”, or “Woe to the Vanquished.”

So in 58 BC, a confederation of 5 Gallic tribes, called the Helvetii, were preparing a mass migration of 250,000 people to flee violence from the Germanic barbarian tribes to the North and East, in a route that would take them through Roman Gallic territory. As the Helvetii migrated, they plundered and slaughtered weaker tribes, who had existing alliances with Rome. They asked Rome for help, and Caesar answered. Caesar remembered the sacking of Rome in 390 BC, and knew plundering wealthy Gaul for treasure and slaves would help him get out of debts he incurred in his consulship in 59 BC. Subjugating the Gauls would also serve to protect Rome, as they “conquered an empire in self defense”, and could act as a buffer between Rome and the barbarian Germanic tribes. So Caesar goes on to meet the Helvetii as they’re crossing a river, engages them, and the Gallic wars begin.

I highly recommend this episode of Dan Carlin’s Hardcore History to learn more about the Conquest of Gaul.

I spent a long time reading about Rome and Gaul, searching for links between the honeycomb formation pictured and the conquest of Gaul. Until one day, looking at the text, it dawned on me: the text was in ROT-13, also known as a Caesar Cipher. Running the code through ROT-13, it reveals this:

Run me to get the code.

At last, we encounter hacker trickery: the intentional red herring, to throw off those pursuing the challenge. I spent weeks at this step, and it was during this time others solved it and the challenge was closed. I spent so much time learning about Caesar’s conquest and subsequent genocide of Gaul, looking for clues, that I almost forgot I was dealing with hackers. Here, I have an executable program telling me to run it to get the code. And yet, when I run it, I don’t get it. Obviously there is something missing here, and it was probably within the executable itself.

So, what is there to do now, but analyze the actual machine instructions? Perhaps there’s something interesting in there. Running message through GDB, we can analyze the actual machine instructions. This is a long, painstaking process, picking through the instructions to see if there’s anywhere interesting. Almost all of the instructions are MOV, ADD, or CALL instructions in the PLT, mostly PUTS and PUTCHAR. However, 272 lines into the main function, we encounter an interesting set of instructions that doesn’t match the rest: a CMP followed by JNE. This means at this point, the program is comparing 2 values, and will jump to another set of instructions if the 2 values aren’t equal. The value it wants to jump to is 552 lines into the main function, which is at the end of it, essentially terminating the program.

Here, we will put our breakpoint. When the program gets to these instructions, it will compare 0x1 to whatever value is at -0x4(%rbp).

Breakpoint 1, 0x00000000004006a6 in main ()
(gdb) x $rbp-0x4
0x7fffffffda1c: 0x0000000

More Than One Way to Skin a Cat

Okay, we know the value at $rbp-0x4 is 0x0, and is being compared to the value 0x1. If they aren’t the same (which they aren’t right now), then we’ll jump to the end of the program. What we need to do is change the value at $rbp-0x4 to be 0x1, or, we can step through the instruction and change the flags register.

CMP instructions are essentially just subtraction instructions, but the result isn’t stored anywhere. When a subtraction instruction is performed, if the two values are equal, the outcome will be zero. If the result is zero, the Zero Flag is set in the flags register.

The JNE instruction just looks at the flags register, and determines if the previous instructions resulted in the Zero Flag being set. If the Zero Flag is set when you execute a JNE instruction, you don’t jump. If the Zero Flag isn’t set, you jump. Changing the value at $rbp-0x4 to 0x1, and leaving it as 0x0 but changing the Flags Register at the next instruction, leave the JNE performing essentially the same thing.

So, since we’re stopped at the CMP instruction, we may as well just modify the value we can modify at this point.

Breakpoint 1, 0x00000000004006a6 in main ()
(gdb) x $rbp-0x4
0x7fffffffda1c: 0x00000000
(gdb) set {int}0x7fffffffda1c=0x1

And voilà! We have our contact.

So there you have it! This challenge was designed to find people who were intelligent, determined and recognized and understood various concepts such as:

  • Steganography
  • Start of File/End of File headers
  • Base64 encoding
  • Encryption
  • Brute Forcing
  • Ciphers
  • Machine Code/Opcode instructions
  • Debugging

This was hard, but a lot of fun. I hope you enjoyed reading it and this little peak into what working in the underground can be like! If you liked it, hit that clap button! And until next time, Happy Hacking.

More by Joshua Campbell

Topics of interest

More Related Stories