Thursday, October 10, 2024

Wherein We Crack A Simple Program: level Leviathan

...what in the gibson?
 


As I was about to publish my second entry in this reverse engineering series with a slightly harder program, I felt somewhat disappointed. I had promised some basic obfuscation and environment variable techniques to make the challenge more interesting, but I wanted to push it further.

While contemplating additional extra layers of fun and despair, I was reminded of an fun debugging experience from the final level of OverTheWire's Leviathan game. The level featured an executable that required a 4-digit numeric parameter which, when correct, granted access to a shell with elevated privileges - specifically, becoming the next level's user and accessing a restricted file.

Now, I promised no walkthroughs for OTW challenges, true! But let me offer this caveat: 

While what I'll explain in this blog post is (indeed) a potential solution, it's far from the most straightforward or obvious approach. 

If this was your first solution - well... you're my kind of crazy. Give me a call; my borderline-insane friends would love to meet you. No, really.

Fair warning: if you don't want a potential Leviathan solution, stop reading. But my advice? Read on, consider 'my' approach, then devise your own. Remember: the flag isn't the objective. Learning is.

Back to our executable: After solving the level, curiosity drove me to examine it with GDB. I wondered if I could spot the password in the assembly code.

And there it was, in all its hexadecimal glory:



Did you spot it? Here is our baby in all its hexadecimal glory:

0x080491da <+20>: mov DWORD PTR [ebp-0xc],0x1bd3


The flow...

Convert user input to an integer via atoi():

0x08049212 <+76>: call 0x80490a0 <atoi@plt>


Compare it with the stored password:

0x0804921a <+84>: cmp DWORD PTR [ebp-0xc],eax

0x0804921d <+87>: jne 0x804924a <main+132>


If not equal, then we have a bad password, and that's the end of that. But if we get a correct comparison, we escalate privileges:

0x080491f9 <+51>: call 0x8049050 <geteuid@plt>

0x0804922f <+105>: call 0x8049090 <setreuid@plt>


Fun? Yes. But here's where it gets interesting:



When using GDB to bypass the program, even with the correct password, privilege escalation fails. Let's compare that with the direct execution of the program, using the correct password:



...but why?

 

The Hidden Guardian: setuid and Debugging

This behavior stems from a crucial security feature in Unix-like operating systems.

 When a setuid program (one that runs with the privileges of its owner rather than the executing user) is run under a debugger, the operating system automatically drops the setuid privileges.

This protection mechanism prevents malicious users from exploiting debuggers to manipulate privileged programs. Even if you can see the password and execute the code, the debugging context itself prevents the privilege escalation from succeeding.

So, it's not just about the code we're writing and the programs we're running, but als about the environment in which we're in. The operating system itself provides layers of protection that even debuggers can't (easily) circumvent.


You hope you had fun. I learned a ton while playing these games, so I can only highly recommend them.


Or as they used to say in the good old days: two thumbs up. Way up!

No comments:

Post a Comment

How a Spy Pixel Crashed Into My Friend's Vacation

              So it goes.   A friend of mine, a freelancer, recently went on a much-deserved vacation. Like most of us in today's always...