The Official Site of David Guest

HTB Reversing: Behind The Scenes

Since I hadn’t done any RE for a while, I pulled an easy challenge from Hack The Box. The solution turned out to be incredibly simple, but this caught me out for a little while and anyone new to reversing may not know how to overcome what the designer had done. Let’s have a look at this then…

First we’ll just run the code and see what it does. Try to keep your dinner down as I run this completely unvalidated binary as root.

We can quickly determine it’s wanting the password to be passed as one of the arguments.

The next stage for me is to see if strings turns up an easy win for us. It’s supposedly a simple challenge – so there’s a reasonable chance of getting something useful.

Nothing especially helpful. We can see it’s using strncmp though. Moving on, could we can gain some insight using ltrace?

Okay well thats very odd. I wasn’t expecting to see a SIGILL – which is a signal that the program has tried to execute an illegal instruction. You know what, let’s just look at the code. A great tool for RE is Radare or r2 from the command line.

There’s something going on here. Notice that main() appears to finish on a UD2 instruction. But this can’t be all of the code. Time to consult with the x86 manual…

Right – this is the cause of the SIGILL error. It looks like this, then, is an attempt to prevent disassembly any further than this point. But the code does run? Why?

We could jump to a quick solution here but let’s understand what’s going on first. Notice the calls to sigemptyset and sigaction which is setting up handing for the POSIX signals. If you are thinking this sounds like try and except in Python, you’re on the right track.

You can see the first argument, signum, is the signal that the system is going to be changing. Look at the code below:

0x000012cd lea rax, [act]
0x000012d4 mov edx, 0 ; struct sigaction *oldact
0x000012d9 mov rsi, rax ; struct sigaction *act
0x000012dc mov edi, 4 ; int signum
0x000012e1 call sigaction ; sym.imp.sigaction ; int sigaction(int signum, struct sigaction *act, struct sigaction *oldact)

Can you guess what signal is represented by 4? The below shows the appropriate section of the manual:

What a turn up for the books. It’s SIGILL. Now we know that sigaction is ensuring that code continues to execute normally in-spite of this UD2 instruction. Its presence in the code is absolutely messing with our ability to understand more about this program. We’re going to overcome this!

It’s important to note that what I’m about to do is not necessary (at all) for solving this and I can do something far more straightforward by just using gdb – but bear with me! We’re going to patch the code so that we remove the UD2 instructions thus allowing Radare to do a full disassembly and analysis. How do we do that? Using a tool called bbe (think sed but for binaries).

We want to find and replace the UD2 instruction (0x0f 0x0b) with a pair of NOPs (0x90). Thats very straightforward.

bbe -e 's/\x0f\x0b/\x90\x90/g' behindthescenes > challenge

This command is all that is needed to patch the code and remove the offending instructions. I feel compelled to give a health warning here. I’ve used a chainsaw to cut a slice of bread. These bytes could exist elsewhere in the code and be essential to code execution. In this instance, with such a small code base, I thought it very unlikely. On a larger code base this could have caused havoc. Consider yourself warned.

Anyway, when we go back to r2 to look at our new version of the program we see this:

Thats a lot more code! Those pesky UD2’s now show as NOP’s. We can see the strncpy functions in action. Radare has automagically pulled the stored strings in for us as well – you can see the first one is pulled at 0x1342. All we have to do is put the chunks together and we have the flag.

Each of the 4 strings is 4 bytes (3 actual characters plus a terminating NULL) and being compared against what we have entered. Why did the author do this? Doing so means that the default options in strings will not return the flag or any parts of it.

I said earlier that what we’ve have done is not necessary. As you might imagine there are other ways to get this flag. We could have used strings behindthescenes -n 3 -d although we’d needed to have known that’s how the flag was being stored and it can (and will) throw a lot of junk up.

Better still, we could have just used gdb where we’re able to immediately disassemble the code without being blocked by the illegal instruction.

Whilst gdb isn’t making the code nearly as pretty or pulling the strings for us, we do have instant access to the full code and it’s trivial to get the strings. There are 4 strings that need to be pulled (as I mentioned above), the first one starts at address 0x201b with the others stored contiguously after that. The command x/4s 0x201b can be used to pull all of the strings in one go…

…and we have our flag.

Of course there’s an even simpler way. I was following the normal steps to analyse code here – but you don’t have to be constrained. With Radare, if instead of using pdf to show the function you use pd you’ll see the strings immediately. No manipulation required at all.

I will admit that the use of UD2 did throw me for a bit, and I didn’t think the solution was going to be hiding in such obvious plain sight. It would be interesting to know how others approached this.

Leave a Reply

Your email address will not be published.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.