updated sheesh

This commit is contained in:
lightningitoid
2025-10-27 07:46:35 +07:00
parent e7cfcbdf59
commit f61a42fa59
3 changed files with 164 additions and 150 deletions
+60 -48
View File
@@ -5,6 +5,7 @@ import time
import re import re
import os import os
import binascii import binascii
import secrets
class Sheesh(Challenge): class Sheesh(Challenge):
flag_location = 'flags/sheesh.txt' flag_location = 'flags/sheesh.txt'
@@ -33,7 +34,7 @@ class Sheesh(Challenge):
bufsize=0, bufsize=0,
) )
def _read_exact_line(self, proc, timeout=5.0): def _read_exact_line(self, proc, timeout=6.0):
start = time.time() start = time.time()
buf = [] buf = []
r = proc.stdout.read r = proc.stdout.read
@@ -47,13 +48,13 @@ class Sheesh(Challenge):
if ch == "\n": if ch == "\n":
return "".join(buf) return "".join(buf)
def _read_until_prompt(self, proc, token: str, timeout=5.0): def _read_until_prompt(self, proc, token: str, timeout=6.0):
start = time.time() start = time.time()
acc = [] acc = []
r = proc.stdout.read r = proc.stdout.read
while True: while True:
if time.time() - start > timeout: if time.time() - start > timeout:
raise TimeoutError(f"Timeout waiting for '{token}'") raise TimeoutError(f"Timeout waiting for '{token}'")inp
ch = r(1) ch = r(1)
if ch == "" and proc.poll() is not None: if ch == "" and proc.poll() is not None:
raise RuntimeError("Process ended while waiting for prompt") raise RuntimeError("Process ended while waiting for prompt")
@@ -72,6 +73,36 @@ class Sheesh(Challenge):
assert self._HEX_RE.match(hx), f"{label} is not hex" assert self._HEX_RE.match(hx), f"{label} is not hex"
return hx return hx
def test1(self, proc, b16_hex: str) -> str:
assert len(b16_hex) == 32
self._read_until_prompt(proc, "inp: ", timeout=6.0)
self._send_line(proc, b16_hex)
line = self._read_exact_line(proc, timeout=4.0)
assert line.startswith("blk:"), "Expected blk: for 16-byte input"
hx = self._expect_hex_field(line, "blk")
# chall prints an extra blank line after blk:
_ = self._read_exact_line(proc, timeout=4.0)
return hx.lower()
def test2(self, proc, iv_hex: str, ct_hex: str) -> str:
self._read_until_prompt(proc, "inp: ", timeout=6.0)
self._send_line(proc, iv_hex + ct_hex)
ans = self._read_exact_line(proc, timeout=4.0).strip().lower()
assert ans in ("ok", "zzz"), "Padding oracle did not return ok/zzz"
return ans
def test3(self, proc, iv_hex: str, ct_hex: str, trials=9) -> tuple[int, int, str]:
ok_cnt = 0
zzz_cnt = 0
for _ in range(trials):
ans = self.test2(proc, iv_hex, ct_hex)
if ans == "ok":
ok_cnt += 1
else:
zzz_cnt += 1
final = "ok" if ok_cnt > zzz_cnt else "zzz"
return ok_cnt, zzz_cnt, final
def distribute(self, flag): def distribute(self, flag):
try: try:
os.makedirs(os.path.dirname(self.flag_location), exist_ok=True) os.makedirs(os.path.dirname(self.flag_location), exist_ok=True)
@@ -90,7 +121,6 @@ class Sheesh(Challenge):
def check(self): def check(self):
try: try:
# parity check: host vs container flag
with open(self.flag_location, 'r') as f: with open(self.flag_location, 'r') as f:
host_flag = f.read().strip() host_flag = f.read().strip()
container_flag = self._read_container_flag() container_flag = self._read_container_flag()
@@ -99,9 +129,9 @@ class Sheesh(Challenge):
proc = self._spawn() proc = self._spawn()
line1 = self._read_exact_line(proc, timeout=5.0) line1 = self._read_exact_line(proc, timeout=6.0)
line2 = self._read_exact_line(proc, timeout=5.0) line2 = self._read_exact_line(proc, timeout=6.0)
blank = self._read_exact_line(proc, timeout=5.0) blank = self._read_exact_line(proc, timeout=6.0)
assert line1.startswith("iv:") and line2.startswith("ct:"), "Missing banner" assert line1.startswith("iv:") and line2.startswith("ct:"), "Missing banner"
iv_hex = self._expect_hex_field(line1, "iv") iv_hex = self._expect_hex_field(line1, "iv")
ct_hex = self._expect_hex_field(line2, "ct") ct_hex = self._expect_hex_field(line2, "ct")
@@ -110,54 +140,36 @@ class Sheesh(Challenge):
assert len(iv0) == 16 and len(ct0) >= 16 and len(ct0) % 16 == 0, "Bad banner sizes" assert len(iv0) == 16 and len(ct0) >= 16 and len(ct0) % 16 == 0, "Bad banner sizes"
self.logger.info("[ok] banner parsed") self.logger.info("[ok] banner parsed")
self._read_until_prompt(proc, "blob: ", timeout=5.0) block00 = "00" * 16
blk1 = self.test1(proc, block00)
blk2 = self.test1(proc, block00)
blk3 = self.test1(proc, block00)
assert blk1 == blk2 == blk3, "ECB-masked oracle not deterministic for same input"
self.logger.info("[ok] blk determinism (3x same input)")
qA = "00" * 16 block01 = "01" * 16
self._send_line(proc, qA) blk4 = self.test1(proc, block01)
lA = self._read_exact_line(proc, timeout=3.0) assert blk4 != blk1, "ECB-masked oracle returned same block for different input"
assert lA.startswith("blk:"), "Expected blk: for 16-byte blob"
blk1 = self._expect_hex_field(lA, "blk")
_ = self._read_exact_line(proc, timeout=3.0)
self._read_until_prompt(proc, "blob: ", timeout=5.0)
self._send_line(proc, qA)
lA2 = self._read_exact_line(proc, timeout=3.0)
blk2 = self._expect_hex_field(lA2, "blk")
_ = self._read_exact_line(proc, timeout=3.0)
assert blk1 == blk2, "ECB-masked oracle not deterministic for same input"
self.logger.info("[ok] blk determinism")
self._read_until_prompt(proc, "blob: ", timeout=5.0)
qB = "01" * 16
self._send_line(proc, qB)
lB = self._read_exact_line(proc, timeout=3.0)
blk3 = self._expect_hex_field(lB, "blk")
_ = self._read_exact_line(proc, timeout=3.0)
assert blk3 != blk1, "ECB-masked oracle returned same block for different input"
self.logger.info("[ok] blk diversity") self.logger.info("[ok] blk diversity")
self._read_until_prompt(proc, "blob: ", timeout=5.0) self._read_until_prompt(proc, "inp: ", timeout=6.0)
self._send_line(proc, "zzzz") # invalid hex self._send_line(proc, "zzzz") # invalid hex
line_err = self._read_exact_line(proc, timeout=3.0) line_err = self._read_exact_line(proc, timeout=4.0)
assert "err" in line_err.lower(), "Expected 'err' for non-hex input" assert "err" in line_err.lower(), "Expected 'err' for non-hex input"
self.logger.info("[ok] error handling on non-hex") maybe_hmmm = self._read_exact_line(proc, timeout=4.0).strip().lower()
if "hmmm" not in maybe_hmmm:
self.logger.warning(f"[warn] expected 'hmmm' after err, got: {maybe_hmmm!r}")
self.logger.info("[ok] error handling on non-hex input")
self._read_until_prompt(proc, "blob: ", timeout=5.0) ok_cnt, zzz_cnt, final = self.test3(proc, iv_hex, ct_hex, trials=9)
self._send_line(proc, (iv_hex + ct_hex)) self.logger.info(f"[ok] padding oracle responded on banner (ok={ok_cnt}, zzz={zzz_cnt}, final={final})")
ans1 = self._read_exact_line(proc, timeout=3.0).strip().lower()
assert ans1 in ("ok", "no"), "Padding oracle did not return ok/no for iv||ct"
self.logger.info(f"[ok] padding oracle responds ({ans1}) on banner bundle")
self._read_until_prompt(proc, "blob: ", timeout=5.0) iv_synth = secrets.token_hex(16)
iv_synth = os.urandom(16).hex() ct_synth = "00" * (6 * 16) # 6 blocks
ct_synth = ("00" * (6 * 16)) ok_cnt2, zzz_cnt2, final2 = self.test3(proc, iv_synth, ct_synth, trials=9)
self._send_line(proc, iv_synth + ct_synth) self.logger.info(f"[ok] padding oracle responded on synthetic (ok={ok_cnt2}, zzz={zzz_cnt2}, final={final2})")
ans2 = self._read_exact_line(proc, timeout=3.0).strip().lower()
assert ans2 in ("ok", "no"), "Padding oracle did not return ok/no on synthetic 6-block ct"
self.logger.info(f"[ok] padding oracle responds ({ans2}) on synthetic payload")
self._read_until_prompt(proc, "blob: ", timeout=5.0)
self._read_until_prompt(proc, "inp: ", timeout=6.0)
proc.kill() proc.kill()
self.logger.info('Check passed for sheesh') self.logger.info('Check passed for sheesh')
return True return True
+52 -51
View File
@@ -1,80 +1,77 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os, sys, signal, binascii, random import os, sys, binascii, random
from Crypto.Cipher import AES from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad from Crypto.Util.Padding import pad, unpad
from Crypto.Util.number import bytes_to_long, long_to_bytes from Crypto.Util.number import bytes_to_long, long_to_bytes
random.seed(os.urandom(16)) random.seed(os.urandom(16))
K0 = os.urandom(16) k0 = os.urandom(16)
K1 = os.urandom(16) k1 = os.urandom(16)
S0 = os.urandom(16) s0 = os.urandom(16); S1 = os.urandom(16)
S1 = os.urandom(16) m0 = os.urandom(16); M1 = os.urandom(16)
M0 = os.urandom(16)
M1 = os.urandom(16)
with open("/flag.txt","rb") as f: with open("/flag.txt","rb") as f:
flag = f.read() flag = f.read()
def hex_input(q): def hex_input(q):
s = input(q).strip() s = input(q).strip()
try: return binascii.unhexlify(s) try: return binascii.unhexlify(s)
except: print("err"); return None except: print("err"); return None
def enc1(b16: bytes) -> bytes: def xex(b16):
x = AES.new(K1, AES.MODE_ECB).encrypt(b16) x = AES.new(k1, AES.MODE_ECB).encrypt(b16)
return bytes(a ^ b for a, b in zip(x, b16)) return bytes(a ^ b for a,b in zip(x, b16))
def enc2(iv: bytes, m: bytes) -> bytes: def enc1(iv, msg):
return AES.new(K0, AES.MODE_CBC, iv=iv).encrypt(pad(m, 16)) return AES.new(k0, AES.MODE_CBC, iv=iv).encrypt(pad(msg,16))
def enc3(m: bytes) -> bytes: def dec1(iv, c):
return AES.new(K1, AES.MODE_CBC, iv=b"\x00"*16).encrypt(pad(m, 16))[-16:] return unpad(AES.new(k0, AES.MODE_CBC, iv=iv).decrypt(c), 16)
def T(iv: bytes, ct: bytes): def enc2(msg):
return AES.new(k1, AES.MODE_CBC, iv=b"\x00"*16).encrypt(pad(msg,16))[-16:]
def F(iv, ct):
n = len(ct) n = len(ct)
if n < 96 or (n & 15): if n < 96 or (n & 15): return None
return None W = [ct[i:i+16] for i in range(0, n, 16)]
v = memoryview(ct)
W = [bytes(v[i:i+16]) for i in range(0, n, 16)]
m = len(W) m = len(W)
r = ((iv[0] & 7) + 2) % m digest = enc2(iv)
if r: r = ((iv[0]^iv[-1]) & 7) + 2
W = W[r:] + W[:r] r %= m
if r: W = W[r:] + W[:r]
j = 1 + (W[0][0] & 1) if len(W) < 3: return None
if len(W) <= j: A0 = long_to_bytes(bytes_to_long(W[0]) ^ bytes_to_long(m0))
return None A1 = long_to_bytes(bytes_to_long(W[1]) ^ bytes_to_long(M1))
del W[j] j = 1 + (digest[0] & 1)
if len(W) < 2: del W[j]
return None if len(W) < 2: return None
a0 = long_to_bytes(bytes_to_long(W[0]) ^ bytes_to_long(M0)) return b"".join((s0, A0, S1, A1, *W[2:]))
a1 = long_to_bytes(bytes_to_long(W[1]) ^ bytes_to_long(M1))
return b"".join((S0, a0, S1, a1, *W[2:]))
def C(iv: bytes, ct: bytes) -> bool: def O(iv, ct):
z = T(iv, ct) z = F(iv, ct)
if z is None: if z is None:
ok = False ok = False
else: else:
try: try:
x = AES.new(K0, AES.MODE_CBC, iv=iv).decrypt(z) dec1(iv, z)
unpad(x, 16)
ok = True ok = True
except: except:
ok = False ok = False
if random.random() < 0.08: if random.random() < 0.10:
ok = not ok ok = not ok
return ok return ok
iv = os.urandom(16) iv = os.urandom(16)
MK = enc3(iv + iv) MK = enc2(iv + iv)
H0 = (flag + b"\x00"*16)[:16] h0 = (flag + b"\x00"*16)[:16]
H1 = bytes(a ^ b for a, b in zip(H0, MK)) h1 = bytes(a ^ b for a,b in zip(h0, MK))
pt = H1 + flag[16:] pt = h1 + flag[16:]
ct = enc2(iv, pt) ct = enc1(iv, pt)
print("iv:", iv.hex()) print("iv:", iv.hex())
print("ct:", ct.hex()) print("ct:", ct.hex())
@@ -82,17 +79,21 @@ print()
while True: while True:
try: try:
blob = hex_input("blob: ") inp = hex_input("inp: ")
if blob is None: if inp is None:
print("err\n"); continue print("hmmm\n"); continue
L = len(blob) L = len(inp)
if L == 16:
y = enc1(blob) if L == 16:
y = xex(inp)
print("blk:", y.hex()); print() print("blk:", y.hex()); print()
elif L >= 32 and (L % 16) == 0: elif L >= 32 and (L % 16) == 0:
iv, ct = blob[:16], blob[16:] ivq, ctq = inp[:16], inp[16:]
print("ok\n" if C(iv, ct) else "no\n") print("ok\n" if O(ivq, ctq) else "zzz\n")
else: else:
print("err\n") print("hmmm\n")
except EOFError: except EOFError:
break break
+52 -51
View File
@@ -1,80 +1,77 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os, sys, signal, binascii, random import os, sys, binascii, random
from Crypto.Cipher import AES from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad from Crypto.Util.Padding import pad, unpad
from Crypto.Util.number import bytes_to_long, long_to_bytes from Crypto.Util.number import bytes_to_long, long_to_bytes
random.seed(os.urandom(16)) random.seed(os.urandom(16))
K0 = os.urandom(16) k0 = os.urandom(16)
K1 = os.urandom(16) k1 = os.urandom(16)
S0 = os.urandom(16) s0 = os.urandom(16); S1 = os.urandom(16)
S1 = os.urandom(16) m0 = os.urandom(16); M1 = os.urandom(16)
M0 = os.urandom(16)
M1 = os.urandom(16)
with open("/flag.txt","rb") as f: with open("/flag.txt","rb") as f:
flag = f.read() flag = f.read()
def hex_input(q): def hex_input(q):
s = input(q).strip() s = input(q).strip()
try: return binascii.unhexlify(s) try: return binascii.unhexlify(s)
except: print("err"); return None except: print("err"); return None
def enc1(b16: bytes) -> bytes: def xex(b16):
x = AES.new(K1, AES.MODE_ECB).encrypt(b16) x = AES.new(k1, AES.MODE_ECB).encrypt(b16)
return bytes(a ^ b for a, b in zip(x, b16)) return bytes(a ^ b for a,b in zip(x, b16))
def enc2(iv: bytes, m: bytes) -> bytes: def enc1(iv, msg):
return AES.new(K0, AES.MODE_CBC, iv=iv).encrypt(pad(m, 16)) return AES.new(k0, AES.MODE_CBC, iv=iv).encrypt(pad(msg,16))
def enc3(m: bytes) -> bytes: def dec1(iv, c):
return AES.new(K1, AES.MODE_CBC, iv=b"\x00"*16).encrypt(pad(m, 16))[-16:] return unpad(AES.new(k0, AES.MODE_CBC, iv=iv).decrypt(c), 16)
def T(iv: bytes, ct: bytes): def enc2(msg):
return AES.new(k1, AES.MODE_CBC, iv=b"\x00"*16).encrypt(pad(msg,16))[-16:]
def F(iv, ct):
n = len(ct) n = len(ct)
if n < 96 or (n & 15): if n < 96 or (n & 15): return None
return None W = [ct[i:i+16] for i in range(0, n, 16)]
v = memoryview(ct)
W = [bytes(v[i:i+16]) for i in range(0, n, 16)]
m = len(W) m = len(W)
r = ((iv[0] & 7) + 2) % m digest = enc2(iv)
if r: r = ((iv[0]^iv[-1]) & 7) + 2
W = W[r:] + W[:r] r %= m
if r: W = W[r:] + W[:r]
j = 1 + (W[0][0] & 1) if len(W) < 3: return None
if len(W) <= j: A0 = long_to_bytes(bytes_to_long(W[0]) ^ bytes_to_long(m0))
return None A1 = long_to_bytes(bytes_to_long(W[1]) ^ bytes_to_long(M1))
del W[j] j = 1 + (digest[0] & 1)
if len(W) < 2: del W[j]
return None if len(W) < 2: return None
a0 = long_to_bytes(bytes_to_long(W[0]) ^ bytes_to_long(M0)) return b"".join((s0, A0, S1, A1, *W[2:]))
a1 = long_to_bytes(bytes_to_long(W[1]) ^ bytes_to_long(M1))
return b"".join((S0, a0, S1, a1, *W[2:]))
def C(iv: bytes, ct: bytes) -> bool: def O(iv, ct):
z = T(iv, ct) z = F(iv, ct)
if z is None: if z is None:
ok = False ok = False
else: else:
try: try:
x = AES.new(K0, AES.MODE_CBC, iv=iv).decrypt(z) dec1(iv, z)
unpad(x, 16)
ok = True ok = True
except: except:
ok = False ok = False
if random.random() < 0.08: if random.random() < 0.10:
ok = not ok ok = not ok
return ok return ok
iv = os.urandom(16) iv = os.urandom(16)
MK = enc3(iv + iv) MK = enc2(iv + iv)
H0 = (flag + b"\x00"*16)[:16] h0 = (flag + b"\x00"*16)[:16]
H1 = bytes(a ^ b for a, b in zip(H0, MK)) h1 = bytes(a ^ b for a,b in zip(h0, MK))
pt = H1 + flag[16:] pt = h1 + flag[16:]
ct = enc2(iv, pt) ct = enc1(iv, pt)
print("iv:", iv.hex()) print("iv:", iv.hex())
print("ct:", ct.hex()) print("ct:", ct.hex())
@@ -82,17 +79,21 @@ print()
while True: while True:
try: try:
blob = hex_input("blob: ") inp = hex_input("inp: ")
if blob is None: if inp is None:
print("err\n"); continue print("hmmm\n"); continue
L = len(blob) L = len(inp)
if L == 16:
y = enc1(blob) if L == 16:
y = xex(inp)
print("blk:", y.hex()); print() print("blk:", y.hex()); print()
elif L >= 32 and (L % 16) == 0: elif L >= 32 and (L % 16) == 0:
iv, ct = blob[:16], blob[16:] ivq, ctq = inp[:16], inp[16:]
print("ok\n" if C(iv, ct) else "no\n") print("ok\n" if O(ivq, ctq) else "zzz\n")
else: else:
print("err\n") print("hmmm\n")
except EOFError: except EOFError:
break break