GET /jwt
Returns 200 with a JWT the server claims to have accepted, demonstrating broken token-validation logic. The chaos is server acceptance of tokens that should be rejected. Default accepts alg:none tokens with no signature.
mode
Which validation flaw to demonstrate. One of: alg-none-accepted (default; accepts a token with alg:none and no signature), alg-confusion (accepts HS256 when RS256 expected; public key used as HMAC secret), kid-path-traversal (kid contains ../../../etc/passwd; key loaded without sanitizing the path), exp-vs-nbf-edge (nbf is 5 minutes after exp; impossible validity window accepted because nbf check skipped).
build a request:
expect: 200 OK with a JSON body containing the accepted token, its decoded header and payload, and a `note` explaining the validation flaw. The X-Chaos-Jwt-Mode header reflects the active mode.
curl -si 'https://chaos.catastrophic.io/jwt?mode=alg-none-accepted' | grep -E '^(HTTP|content-type|x-chaos-jwt)'
echo
echo "decoded header:"
curl -sS 'https://chaos.catastrophic.io/jwt?mode=alg-none-accepted' | python -c "import sys,json,base64; t=json.load(sys.stdin)['token'].split('.'); print(json.loads(base64.urlsafe_b64decode(t[0]+'=='*(-len(t[0])%4))))"
# Default mode (alg-none-accepted) returns a JWT with alg:none and no
# signature, simulating a broken validator. RFC 7518 §3.6 defines
# "none" as no integrity protection. CVE-2018-1000531 (Inversoft
# prime-jwt) and several others exhibit this exact failure.
import urllib.request, json, base64
resp = urllib.request.urlopen("https://chaos.catastrophic.io/jwt?mode=alg-none-accepted")
print("Content-Type:", resp.headers.get("Content-Type"))
print("X-Chaos-Jwt-Mode:", resp.headers.get("X-Chaos-Jwt-Mode"))
body = json.loads(resp.read())
print("server claims accepted:", body["accepted"])
header_b64 = body["token"].split(".")[0]
header = json.loads(base64.urlsafe_b64decode(header_b64 + "=" * (-len(header_b64) % 4)))
print("token alg:", header.get("alg"))
print("note:", body.get("note"))
const res = await fetch("https://chaos.catastrophic.io/jwt?mode=alg-none-accepted");
console.log("X-Chaos-Jwt-Mode:", res.headers.get("x-chaos-jwt-mode"));
const body = await res.json();
console.log("server claims accepted:", body.accepted);
const headerB64 = body.token.split(".")[0];
const header = JSON.parse(atob(headerB64.replace(/-/g, "+").replace(/_/g, "/")));
console.log("token alg:", header.alg);
console.log("note:", body.note);
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
func main() {
resp, _ := http.Get("https://chaos.catastrophic.io/jwt?mode=alg-none-accepted")
defer resp.Body.Close()
raw, _ := io.ReadAll(resp.Body)
fmt.Println("X-Chaos-Jwt-Mode:", resp.Header.Get("X-Chaos-Jwt-Mode"))
var body map[string]any
json.Unmarshal(raw, &body)
fmt.Println("server claims accepted:", body["accepted"])
token := body["token"].(string)
headerB64 := strings.Split(token, ".")[0]
headerBytes, _ := base64.RawURLEncoding.DecodeString(headerB64)
var header map[string]any
json.Unmarshal(headerBytes, &header)
fmt.Println("token alg:", header["alg"])
}
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
// base64 = "0.22"
use base64::Engine;
fn main() -> Result<(), Box> {
let resp = reqwest::blocking::get("https://chaos.catastrophic.io/jwt?mode=alg-none-accepted")?;
println!("Mode: {:?}", resp.headers().get("x-chaos-jwt-mode"));
let body: serde_json::Value = resp.json()?;
println!("server claims accepted: {}", body["accepted"]);
let token = body["token"].as_str().unwrap();
let header_b64 = token.split('.').next().unwrap();
let header_bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(header_b64)?;
let header: serde_json::Value = serde_json::from_slice(&header_bytes)?;
println!("token alg: {}", header["alg"]);
Ok(())
}
import java.net.URI;
import java.net.http.*;
import java.util.Base64;
public class JwtChaos {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var req = HttpRequest.newBuilder(URI.create("https://chaos.catastrophic.io/jwt?mode=alg-none-accepted")).build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println("X-Chaos-Jwt-Mode: " + resp.headers().firstValue("X-Chaos-Jwt-Mode").orElse(""));
System.out.println("Body: " + resp.body());
// Token header is the first base64url segment of body["token"]
// — decode and inspect alg in a real implementation.
}
}
using System.Text.Json;
using var client = new HttpClient();
var resp = await client.GetAsync("https://chaos.catastrophic.io/jwt?mode=alg-none-accepted");
Console.WriteLine($"X-Chaos-Jwt-Mode: {resp.Headers.GetValues("X-Chaos-Jwt-Mode").FirstOrDefault()}");
var body = JsonDocument.Parse(await resp.Content.ReadAsStringAsync()).RootElement;
Console.WriteLine($"server claims accepted: {body.GetProperty("accepted")}");
var token = body.GetProperty("token").GetString();
var headerB64 = token.Split('.')[0];
var padded = headerB64.PadRight(headerB64.Length + (4 - headerB64.Length % 4) % 4, '=')
.Replace('-', '+').Replace('_', '/');
var header = JsonDocument.Parse(Convert.FromBase64String(padded)).RootElement;
Console.WriteLine($"token alg: {header.GetProperty("alg")}");
require "net/http"
require "json"
require "base64"
uri = URI("https://chaos.catastrophic.io/jwt?mode=alg-none-accepted")
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
res = http.get(uri.request_uri)
puts "X-Chaos-Jwt-Mode: #{res['X-Chaos-Jwt-Mode']}"
body = JSON.parse(res.body)
puts "server claims accepted: #{body['accepted']}"
header_b64 = body["token"].split(".").first
header = JSON.parse(Base64.urlsafe_decode64(header_b64 + "=" * (-header_b64.length % 4)))
puts "token alg: #{header['alg']}"
end
$r = Invoke-RestMethod -Uri 'https://chaos.catastrophic.io/jwt?mode=alg-none-accepted' -ResponseHeadersVariable h
"X-Chaos-Jwt-Mode: $($h['X-Chaos-Jwt-Mode'])"
"server claims accepted: $($r.accepted)"
$headerB64 = $r.token.Split('.')[0]
$padded = $headerB64 + ('=' * ((4 - $headerB64.Length % 4) % 4))
$padded = $padded.Replace('-', '+').Replace('_', '/')
$headerJson = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($padded))
$header = $headerJson | ConvertFrom-Json
"token alg: $($header.alg)"
headers
body