Gracker level2 (XOR secrets)

Try the challenge thoroughly before reading the write-up. Otherwise, it's your loss.

We ssh to [email protected] and authenticate using the password we had obtained after solving the previous level.

Recap of previous level.

The recap provides the details as to how the previous challenge had been implemented, and it’s solution which is quite similar to mine. The important thing to notice is that instead of hardcoding the password, Zero Cool had hardcoded the encrypted password(using XOR encryption), and before the comparison was made, he decrypted it. Thus we were able to examine it just before strcmp() was called.

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>

// n0b0dy_gu3sses_thi5_passw0rd
char secret_password[] =  "\x2f\x71\x23\x71\x25\x38\x1e\x26\x34\x72\x32\x32\x24\x32\x1e\x35\x29\x28\x74\x1e\x31\x20\x32\x32\x36\x71\x33\x25"; 
uint8_t XORkey = 0x41;

void spawn_shell() {
    gid_t gid;
    uid_t uid;
    gid = getegid();
    uid = geteuid();
    setresgid(gid, gid, gid);
    setresuid(uid, uid, uid);
    system("/bin/sh");
}

int main (int argc, char *argv[]) {
    
    char password_input[32];
    char *pos;
    int i;

    printf("~Zero Cool Simple Backdoor v2~\nEnter Password:\n");
    read(STDIN_FILENO, password_input, 32);
    password_input[31]='\0';

    if ((pos=strchr(password_input, '\n')) != NULL) *pos = '\0';

    for(i=0; i<strlen(secret_password); i++) {
        secret_password[i] = secret_password[i] ^ XORkey;
    }

    if(strcmp(password_input,secret_password)==0) {
        printf("Correct! Here is the level2 shell.\nRead the level2 password in /home/level2/.pass to login with `ssh [email protected]`\n");
        spawn_shell();
    } else{
        printf("wrong!");
    }

    return 0;
}

Level2

In this level too, Zero Cool has stored the encrypted password. But instead of decrypting it and comparing it to our entered password, the program is encrypting our password and checking it agains his. We can figure that out using the same method to examine the registers just before the call to strcmp().

[email protected]:/matrix/level2$ gdb level2 -q
Reading symbols from level2...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
[...snip...]
   0x00000000004008a2 <+101>:	movzbl -0x40(%rbp,%rax,1),%eax
   0x00000000004008a7 <+106>:	movzbl 0x2005d2(%rip),%edx        # 0x600e80 <XORkey>
   0x00000000004008ae <+113>:	xor    %eax,%edx
   0x00000000004008b0 <+115>:	mov    -0x14(%rbp),%eax
   0x00000000004008b3 <+118>:	cltq   
   0x00000000004008b5 <+120>:	mov    %dl,-0x40(%rbp,%rax,1)
   0x00000000004008b9 <+124>:	addl   $0x1,-0x14(%rbp)
   0x00000000004008bd <+128>:	mov    -0x14(%rbp),%eax
   0x00000000004008c0 <+131>:	movslq %eax,%rbx
   0x00000000004008c3 <+134>:	lea    -0x40(%rbp),%rax
   0x00000000004008c7 <+138>:	mov    %rax,%rdi
   0x00000000004008ca <+141>:	callq  0x400640 <[email protected]>
   0x00000000004008cf <+146>:	cmp    %rax,%rbx
   0x00000000004008d2 <+149>:	jb     0x40089d <main+96>
   0x00000000004008d4 <+151>:	lea    -0x40(%rbp),%rax
   0x00000000004008d8 <+155>:	mov    $0x600e60,%esi
   0x00000000004008dd <+160>:	mov    %rax,%rdi
   0x00000000004008e0 <+163>:	callq  0x4006c0 <[email protected]>
   0x00000000004008e5 <+168>:	test   %eax,%eax
   0x00000000004008e7 <+170>:	jne    0x4008ff <main+194>
   0x00000000004008e9 <+172>:	mov    $0x4009e0,%edi
   0x00000000004008ee <+177>:	callq  0x400620 <[email protected]>
   0x00000000004008f3 <+182>:	mov    $0x0,%eax
   0x00000000004008f8 <+187>:	callq  0x4007e6 <spawn_shell>
   0x00000000004008fd <+192>:	jmp    0x40090e <main+209>
   0x00000000004008ff <+194>:	mov    $0x400a59,%edi
   0x0000000000400904 <+199>:	mov    $0x0,%eax
   0x0000000000400909 <+204>:	callq  0x400680 <printf@plt>
[...snip...] 
End of assembler dump.
(gdb) b *0x00000000004008e0
Breakpoint 1 at 0x4008e0
(gdb) r
Starting program: /matrix/level2/level2 
~Zero Cool Simple Backdoor v3~
Enter Password:
XXXX

Breakpoint 1, 0x00000000004008e0 in main ()
(gdb) x/s $rax
0x7fffffffead0:	"\031\031\031\031"
(gdb) x/s $esi
0x600e60 <secret_password>:	")q6\036(2\036\065)p2\036)u\"*r3\036'q--q6(/&\036,r"
(gdb) 

Also, it is pretty clear that Zero Cool has used XOR encryption(as in the previous level). So what’s the XOR key? The memory address of the XOR key has been provided in the disassmebly. Let’s examine it.

(gdb) x/s 0x600e80
0x600e80 <XORkey>:	"A"
(gdb)

So we read about how the XOR cipher works and how to decipher it. On it’s wikipedia page it is mentioned that To decrypt the output, merely reapplying the XOR function with the key will remove the cipher. We have the XOR encryption of the actual password. So all we have to do is use that as the password we enter, thus resulting in it being deciphered ! The XOR’ed password is )q6\036(2\036\065)p2\036)u\"*r3\036\'q--q6(/&\036,r Since it has some characters which are not printable, we will use the python -c switch to send the input to the program.

(gdb) r <<< $(python -c "print ')q6\036(2\036\065)p2\036)u\"*r3\036\'q--q6(/&\036,r'")
Starting program: /matrix/level2/level2 <<< $(python -c "print ')q6\036(2\036\065)p2\036)u\"*r3\036\'q--q6(/&\036,r'")
~Zero Cool Simple Backdoor v3~
Enter Password:

Breakpoint 1, 0x00000000004008e0 in main ()
(gdb) x/s $rax
0x7fffffffead0:	"h0w_is_th1s_h4ck3r_f0ll0wing_m3"
(gdb)

It turns out we did not need the XOR key at all ! Now that we have the password, we run the binary using it.

[email protected]:/matrix/level2$ ./level2
~Zero Cool Simple Backdoor v3~
Enter Password:
h0w_is_th1s_h4ck3r_f0ll0wing_m3
Correct! Here is the level3 shell.
Read the level3 password in /home/level3/.pass to login with `ssh [email protected]`
$ whoami
level3
$

And Voila! We now have level3 shell using which we can read the level3 password. I am not revealing the password here so that the readers try the challenge on their own.