cscg ist super
This commit is contained in:
115
2026/cscg/crypto/intro2/main.py
Executable file
115
2026/cscg/crypto/intro2/main.py
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from hashlib import sha1
|
||||
from base64 import b64encode, b64decode
|
||||
from secrets import token_hex
|
||||
|
||||
from secret import FLAG
|
||||
|
||||
|
||||
KEY = token_hex(16)
|
||||
|
||||
|
||||
def get_mac(data: bytes) -> str:
|
||||
return sha1(KEY.encode("latin1") + data).hexdigest()
|
||||
|
||||
|
||||
def parse_token(token: str) -> dict:
|
||||
# Decode token
|
||||
token = b64decode(token)
|
||||
|
||||
# Check the MAC
|
||||
token, mac = token.split(b"|mac=")
|
||||
if get_mac(token) != mac.decode("latin1"):
|
||||
return None
|
||||
|
||||
# Parse values
|
||||
values = dict()
|
||||
for part in token.decode("latin1").split("|"):
|
||||
key, value = part.split("=")
|
||||
values[key] = value
|
||||
return values
|
||||
|
||||
|
||||
def generate_token(values: dict) -> str:
|
||||
token = "|".join(f"{key}={value}" for key, value in values.items())
|
||||
secure_token = f"{token}|mac={get_mac(token.encode('latin1'))}"
|
||||
|
||||
return b64encode(secure_token.encode("latin1")).decode("latin1")
|
||||
|
||||
|
||||
def handle_register():
|
||||
name = input("What is you name? ")
|
||||
animal = input("What is your favorite animal? ")
|
||||
|
||||
token = generate_token(
|
||||
{
|
||||
"name": name,
|
||||
"animal": animal,
|
||||
"admin": "false",
|
||||
}
|
||||
)
|
||||
|
||||
print("Here is your access token:", token)
|
||||
|
||||
|
||||
def handle_show_animal_videos():
|
||||
user_data = parse_token(input("Enter access token: "))
|
||||
|
||||
if user_data is None:
|
||||
print("Invalid token.")
|
||||
return
|
||||
|
||||
print(
|
||||
f"\nHere are some {user_data['animal']} videos for you: https://www.youtube.com/results?search_query=funny+{user_data['animal']}+video+compilation"
|
||||
)
|
||||
|
||||
|
||||
def handle_show_flag():
|
||||
user_data = parse_token(input("Enter access token: "))
|
||||
|
||||
if user_data is None:
|
||||
print("Invalid token.")
|
||||
return
|
||||
|
||||
if user_data["admin"] == "true":
|
||||
print("The flag is", FLAG)
|
||||
else:
|
||||
print("You are not an admin.")
|
||||
|
||||
|
||||
def main():
|
||||
while True:
|
||||
# Show main menu
|
||||
|
||||
print(
|
||||
"""
|
||||
1. Register
|
||||
2. Show animal videos
|
||||
3. Show flag
|
||||
4. Exit
|
||||
"""
|
||||
)
|
||||
|
||||
try:
|
||||
choice = int(input("Enter your choice: "))
|
||||
except ValueError:
|
||||
print("Please enter a number next time.")
|
||||
continue
|
||||
except EOFError:
|
||||
break
|
||||
|
||||
if choice == 1:
|
||||
handle_register()
|
||||
elif choice == 2:
|
||||
handle_show_animal_videos()
|
||||
elif choice == 3:
|
||||
handle_show_flag()
|
||||
elif choice == 4:
|
||||
break
|
||||
else:
|
||||
print("Please enter a valid choice.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
1
2026/cscg/crypto/intro2/secret.py
Normal file
1
2026/cscg/crypto/intro2/secret.py
Normal file
@@ -0,0 +1 @@
|
||||
FLAG = "CSCG{i_love_my_mum}"
|
||||
130
2026/cscg/crypto/intro2/solve.py
Normal file
130
2026/cscg/crypto/intro2/solve.py
Normal file
@@ -0,0 +1,130 @@
|
||||
from pwn import context, args, process, remote, success, xor
|
||||
from base64 import b64decode, b64encode
|
||||
from hashlib import sha1
|
||||
import struct
|
||||
|
||||
context.update(arch="amd64", os="linux", log_level="info")
|
||||
|
||||
URL = "jpielr6wv7xor5jf32vxasubni-1024-intro-crypto-2.challenge.cscg.live"
|
||||
PORT = 443
|
||||
|
||||
example_token="name=cato|animal=cat|admin=false|mac=b5d4ddd83235359176d107b7c98bc7f2979cfec3"
|
||||
|
||||
|
||||
class Sha1State:
|
||||
def __init__(self, hex_digest):
|
||||
# Extract the 5 internal 32-bit registers from the hex digest
|
||||
self.h = struct.unpack(">5I", bytes.fromhex(hex_digest))
|
||||
|
||||
def _left_rotate(self, n, b):
|
||||
return ((n << b) | (n >> (32 - b))) & 0xffffffff
|
||||
|
||||
def extend(self, data, total_len_bytes):
|
||||
"""
|
||||
Continues hashing from the current state.
|
||||
total_len_bytes must be the length of (secret + message + padding + extra_data)
|
||||
"""
|
||||
h0, h1, h2, h3, h4 = self.h
|
||||
|
||||
# SHA-1 processes 64-byte blocks
|
||||
for i in range(0, len(data), 64):
|
||||
block = data[i:i+64]
|
||||
if len(block) < 64:
|
||||
# This is the final block of the 'extra_data', it needs its own padding
|
||||
block += self.get_padding(total_len_bytes)
|
||||
|
||||
w = list(struct.unpack(">16I", block[:64]))
|
||||
for j in range(16, 80):
|
||||
w.append(self._left_rotate(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1))
|
||||
|
||||
a, b, c, d, e = h0, h1, h2, h3, h4
|
||||
|
||||
for j in range(80):
|
||||
if 0 <= j <= 19:
|
||||
f = (b & c) | ((~b) & d)
|
||||
k = 0x5A827999
|
||||
elif 20 <= j <= 39:
|
||||
f = b ^ c ^ d
|
||||
k = 0x6ED9EBA1
|
||||
elif 40 <= j <= 59:
|
||||
f = (b & c) | (b & d) | (c & d)
|
||||
k = 0x8F1BBCDC
|
||||
elif 60 <= j <= 79:
|
||||
f = b ^ c ^ d
|
||||
k = 0xCA62C1D6
|
||||
|
||||
a, b, c, d, e = (self._left_rotate(a, 5) + f + e + k + w[j]) & 0xffffffff, \
|
||||
a, self._left_rotate(b, 30), c, d
|
||||
|
||||
h0 = (h0 + a) & 0xffffffff
|
||||
h1 = (h1 + b) & 0xffffffff
|
||||
h2 = (h2 + c) & 0xffffffff
|
||||
h3 = (h3 + d) & 0xffffffff
|
||||
h4 = (h4 + e) & 0xffffffff
|
||||
|
||||
return '%08x%08x%08x%08x%08x' % (h0, h1, h2, h3, h4)
|
||||
|
||||
def get_padding(self, msg_len):
|
||||
padding = b"\x80"
|
||||
padding += b"\x00" * ((56 - (msg_len + 1)) % 64)
|
||||
padding += struct.pack(">Q", msg_len * 8)
|
||||
return padding
|
||||
|
||||
def get_sha1_padding(message_len_bytes):
|
||||
padding = b'\x80'
|
||||
|
||||
num_nulls = (56 - (message_len_bytes + 1)) % 64
|
||||
padding += b'\x00' * num_nulls
|
||||
|
||||
message_len_bits = message_len_bytes * 8
|
||||
padding += struct.pack('>Q', message_len_bits)
|
||||
|
||||
return padding
|
||||
|
||||
def forge_mac(orig_mac: str, key_len: int, orig_data: bytes, added_data: bytes) -> str:
|
||||
state = Sha1State(orig_mac)
|
||||
current_len = key_len + len(orig_data)
|
||||
glue_padding = get_sha1_padding(current_len)
|
||||
|
||||
new_total_len = current_len + len(glue_padding) + len(added_data)
|
||||
|
||||
return state.extend(added_data, new_total_len)
|
||||
|
||||
def start():
|
||||
if args.REMOTE:
|
||||
return remote(URL, PORT, ssl=True)
|
||||
else:
|
||||
return process(["python3", "./main.py"])
|
||||
|
||||
|
||||
def get_token(io):
|
||||
io.sendline(b"1")
|
||||
io.recvuntil(b"? ")
|
||||
io.sendline(b"cato")
|
||||
io.recvuntil(b"? ")
|
||||
io.sendline(b"cat")
|
||||
return io.recvline().split(b" ")[-1].strip()
|
||||
|
||||
def get_flag(io, token):
|
||||
io.sendline(b"3")
|
||||
io.recvuntil(b": ")
|
||||
io.sendline(token)
|
||||
return io.recvline()
|
||||
|
||||
|
||||
io = start()
|
||||
io.recvuntil(b": ")
|
||||
token = b64decode(get_token(io))
|
||||
parts = token.split(b"|mac=")
|
||||
data_to_pad = parts[0]
|
||||
original_mac = parts[1].decode()
|
||||
|
||||
extension = b"|admin=true"
|
||||
key_len = 32
|
||||
glue = get_sha1_padding(key_len + len(data_to_pad))
|
||||
new_mac = forge_mac(original_mac, key_len, data_to_pad, extension)
|
||||
malicious_payload = data_to_pad + glue + extension + b"|mac=" + new_mac.encode()
|
||||
final_token = b64encode(malicious_payload)
|
||||
io.recvuntil(b": ")
|
||||
print(get_flag(io, final_token).decode())
|
||||
|
||||
Reference in New Issue
Block a user