Hi again, welcome back! This series is about the things I am learning on my way to the OSCP exam. This is part 4, and if you would like to see the previous posts, you can do that here. In my last post, I said that I was going to be writing about kernel exploits next. Kernel exploits are cool, and there is plenty to learn about them, but at the same time they are fantastically boring to watch. This did not fully occur to me until the third draft of this post. To me, kernel exploits are kind of like a cheat in a game, if you know the secret it gets you past the challenge, at least until it is patched out. Using kernel exploits to get to root on a box is neat, but not very rewarding – at least not for me.
In my preparations for the OSCP I have built up a nice collection of resources about recent exploits. Anything that makes headlines or bubbles up in my twitter feed goes into a folder. In my down time I do some digging and try to understand how the exploit can be used, and what it will look like. I see if I can find a TryHackMe or HackTheBox machine that I can try this new thing out on. I make my notes, file the bookmarks, and do my writeup. It just so blah. It doesn’t feel like a challenge that is solved, just a thing that was done.
I want to play with applications and find out how to make them do things the developers did not expect. Things that may get me a neat privilege upgrade, a shell, or maybe a database. Once I have the shell, well … that is a problem for another day. Someone probably left a scheduled task, or something laying around that I can play with instead of using kernel exploit anyway. All of this is a roundabout way of saying I don’t want to write about using kernel exploits, I want to write about something more interesting. Learning is supposed to be fun, and I want to write about fun things. Also, patch your stuff!
Earlier this month I participated in the US Cyber Open CTF. This CTF is meant to find folks aged 18-26 to compete on the US Cyber Team in a global competition later this year. I am older than 26, so I cannot be selected for the team, but I can play in the CTF, learn things, and have some fun.
Because CTF events are time-boxed, I usually focus on the challenges that I know how to solve. Like timed tests, it is better to answer more questions than to waste time being stuck on a single problem. This time, I decided that I was going to try all the categories and get through at least one challenge in each, before focusing on the web challenges. I love the web challenges, probably because they make the most sense to me. I enjoy the cryptography challenges too, but I am not very good at them yet. The reversing and forensics categories scare me, more than a little. The rest of this post is going to be about trying something that I am not very good at. It was a lot more fun than I expected it to be.
Manually Reversing Obfuscated Code
In a CTF, reversing usually falls into two categories, binary files, and obfuscated code. I have avoided both, but if I had to pick one, I would prefer to mess with code before decompiling a binary. There are two main reasons for this. I like code in almost any flavor, except Java. The second part is that decompiling an executable does not give you code, it gives you machine-level not-really-code (assembly). Someone out there will find that remark offensive, I do not mean it that way. Some people really love assembly, and that is cool for them. In my head, folks that like assembly probably also think those Primitive Technology videos on YouTube look like a fun way to spend a vacation. It might be fun, but it’s not fun for me. I like my code high-level, and I like to type classes without word-wrap enabled. I admit that it has been at least a dozen years since I last attempted to write Java, the scars run deep though.
If you have not seen obfuscated code before, this is the kind of thing I am talking about. This is JavaScript, though it is nearly impossible to recognize it.
Obfuscation hides the true purpose of the code from any snooping eyes, or malware defense systems. This big blob of brackets is checking a password. The goal of this challenge is to figure out the password, which will be the “flag”.
The best way I can describe how I solved this challenge is that it is like untangling a knot. A knot that hates you, has a lot of loose ends, and is pretending to be a dog. The strategy is to divide and conquer. All of those brackets are actually helpful because they have to be in pairs. This simple rule can be used to identify code blocks. It helps to understand code, but I will admit that JavaScript is not my best language. If I get stuck figuring out how a part works, I just try things and google stuff, like all serious developers.
It may be surprising to find out that the only tools that was required for this challenge are a web browser, and a text editor. For a task like this, a text editor with some intelligence is very helpful. Letting the editor mark the matching brackets will save a lot of time and hassles.
This challenge is about stubbornness. It doesn’t matter where I start, the idea is to start replacing sections of brackets and parenthesis with things are useful.
Starting at the top of the script, where that “Let e=…” bit is. The highlighted section evaluates to 211. I copied the gibberish after “e=(“, up to the closing parenthesis, and pasted that into the console of the browser developer tools.
That might be jumping a head a little too far. There are smaller chunks of code within that block. There are several “!+[]” sections in there. In JavaScript [] is equal to false (0), and !+[] is equal to true (1). I can replace !+[] with the value 1 to make it easier to read. After this replacement, I am left with this odd-looking statement: “+(1+1+[+1]+[+1])”. The console tells me that this is “211” it may not be immediately obvious how it got there. 1+1+1+1=4, but this isn’t math. The extra square-brackets around the +1 change the meaning to “concatenate 2, with 1 and again with 1”, which is 211. That’s one block down, I have no idea how many there are to go.
Most of the time while doing the replacement I need to make small changes. Finding and replacing in big swaths is probably going to lead to problems. Replacing a few larger blocks with find-and-replace does work. That big chunk of code that is highlighted evaluates to “toString”, the (211) that I just explained, changes “toString” into a function call “function toString()”.
After whittling away at this for maybe 30 minutes or so, I have the top section in a very different state… it is still not exactly readable, but it is really close.
The highlighted section of code, which used to fill the entire screen, literally evaluates to “password”. The I left a lot of the weird code in there to show the code using slicing of reserved words to extract individual letters. The process doesn’t take very long, it’s just tedious. And for this challenge, it’s just the beginning of the fun.
Once the code is transformed into something somewhat readable, the password still has to be worked out. This code is using a set of rules to verify the letters of the password. It may not look like it, but this is the most-readable I could make the code.
If you cannot tell the difference between the code in this form, and any other form I’ve shown yet, I don’t blame you. It only makes sense to me because I spent 2 hours, and several cups of coffee, working it out what it means. With this challenge, not only was the code obfuscated, but the logic is obfuscated as well. Its gibberish-inception nonsense, and a terrible way to evaluate a password.
This bit of JavaScript is checking the value passed to it, character by character. It is switching methods for how it checks the character along the way. Here are the rules that it uses:
- the password needs to be 20 characters or more
- the first letter of the password is t
- the sum of the charcodes for characters 0,1, and 2 need to add up to 321
- position 3, 12, and 15 will be “_” characters
- a substring starting at position 4 for 8 chars, is “password”
- position 13 will be charcode 105, which is i
- the 14th position charcode minus 10 is not “i”
- the last 4 characters in the password are 1234
At this point, this is a sudoku puzzle. I can see enough of the code to debug it and add logging statements to check different values. After some time building up the password piece by piece the answer is revealed to be “the_password_is_1234”. Because of a logical gap between the 14th and the last 4 characters of the password, “the_password_is_AnythingIWantCanBeHere_1234” also works.
How might I use this?
You might be wondering why I chose this topic. As I thought about the topic of kernel exploits and this series as a whole, I realized that everything in my original series outline is attack based. I decided that I want to add a few more quirky things to the series, and here we are. The boring showcase of kernel exploits, where I try to make jokes about unclean cows, and potatoes in various stages of existence is out, and a messy pile of JavaScript spaghetti is in, because it was fun for me.
Reversing and de-obfuscation is not going to be a part of the OSCP exam. It is also unlikely to be part of a penetration test. But de-obfuscating things can show up in an investigation or an audit. Some time back, I was reviewing logs from an application. I saw encoded payloads being sent to an application. Big long strings of base-64 encoded text being rudely shoved into a web application. I did not know what I was looking at back then. I thought that decoding and deciphering the data was beyond me, so I notified some folks that something was funky, and they took over. Today I would look at those requests and rip them open and keep digging.
Obfuscated code is widely used in phishing attacks to disguise payloads. There are tools and services that will detonate and analyze the scripts, but it is useful to understand how to do it by hand as well. There are plenty of videos out there that walk through this process with different kinds of payloads. I thought they were interesting, and extremely helpful. When dealing with malicious payloads there is an extra challenge in that copying and pasting things to see what happens could be dangerous. More caution is needed. Smaller steps ensure that dangerous code is not executed accidentally. Even for a CTF challenge, I decided to use a fresh VM to do this work just in case something went sideways.
While working on things that I don’t think I am good at, I start to understand them. When I understand them, they aren’t as scary. Once a topic is no longer scary, it is easier to enjoy it. Later this year Set Solutions will be hosting a CTF. Keep an eye out for news about it coming soon. It should be a lot of fun. Who knows, there might even be some code to deobfuscate in there.
And with that, I leave you with a promise. The next post is 100% guaranteed to be about cracking passwords and hashes. Why am I so sure about that? Because it is REALLY fun. It’s time to bring out the toys. I’m going to brute-force some logins and crack some hashes, while exploring some good and not so good ways of doing things.
***
This blog was written by Greg Porterfield, Senior Security Consultant at Set Solutions.