Description: Maybe software isn’t so bad after all…
My current password l3ak{...} seems to be working fine, but
something tells me someone else has been messing around on my machine
💀. Note: This challenge is the revenge challenge
for Software is a scam. It is suggested that you solve that
one first.
Solved by: Vitruvian, hamaj
After solving the electronics homework, we got a seizure by just looking at this circuit.
c1 through c4 is the same as the challenge
Software is a Scam.

~(((c1*c4) + c1) ^ (c2)) = 0x5c
~((c1 & c3) || (c2 ^ c4)) = 0x87
(c3 ^ c4) = 0x0a
(c2 - c1) = 0xe7
(c3 - c1) > 0xf4
On solving this set of equations we got 2 solutions, which were
l3ak and L3AK.
The description mentioned that the current password worked fine,
which is l3ak{...}. We ignored L3AK, for
now.
So we are assuming:
| Pin | Character |
|---|---|
c1 |
l |
c2 |
3 |
c3 |
a |
c4 |
k |
c5 |
{ |
c34 |
} |
If we observe every gate connected to a divider , we observe that all of the gates are commutative gates.
Which means a <gate> b =
b <gate> a.
Commutative gates:
Example:

Here we can derive the output of the divider as:
(c15 | c24) / (c24 | c14)
We know that OR is a commutative gate (which means
a or b = b or a), so the output will be:
((c15 | c24) / (c24 | c14)) = 1
If we look at all the inputs of all dividers, it follows the same logic:
(a <gate> b) / (b <gate> a) = 1
We checked for a couple of them and assumed all were this exact logic because it doesn’t make sense to solve the whole circuit, it had to be a reduction of sorts.
c6 - (c1>>5) mul (1 OR 1<<4) = 20
c7 - (c3>>5) mul (1 OR 1<<6) = b2
c8 - (c4>>5) mul ((1 OR 1<<2) OR 1<<3) = 49
c9 + (c1>>5) mul ((1<<3 OR 1<<4) OR 1<<5) = db
c10 - (c3>>5) mul ((1<<3) OR (1<<1 OR 1<<2)) = 48
c11 + (c4>>5) mul (((1 OR 1<<4) OR 1<<6) OR 1<<7) = d2
c12 + (c1>>5) mul (((1<<1 OR 1<<3) OR 1<<4) OR 1<<5) = e3
c13 XOR ((1 OR 1<<6) mul (c3>>5)) = f0
c14 - (c4>>5) mul (1<<2) = 57
c15 + (c1>>5) mul ((1 OR 1<<1) OR 1<<4) = 8e
c16 - (c3>>5) mul (1 OR 1<<6) = af
c17 + (c4>>5) mul ((1 OR 1<<4) OR 1<<5) = c6
c18 + (c1>>5) mul (1 OR 1<<2) = 6e
c19 + (c3>>5) mul (((1 OR 1<<1) OR 1<<6) OR 1<<7) = b9
c20 + (c3>>5) mul ((1<<1 OR 1<<3) OR 1<<4) = 82
c21 - (c1>>5) mul ((1<<2) OR 1<<4) = 37
c22 - (c3>>5) mul ((1<<1) OR 1<<3) = 55
c23 - (c4>>5) mul ((1 OR 1<<3) OR 1<<5) = fc
c24 - (c1>>5) mul (((1 OR 1<<4) OR 1<<6) OR 1<<7) = bd
c25 - (c3>>5) mul ((1<<1 OR 1<<3) OR 1<<5) = f4
c26 - (c4>>5) mul (1<<4) = 14
c27 + (c1>>5) mul ((1 OR 1<<1) OR 1<<4) = 98
c28 XOR ((c3>>5) mul ((1<<1 OR 1<<2) OR 1<<3)) = 58
c29 + (c4>>5) mul ((1<<1 OR 1<<2) OR 1<<6) = 03
c30 - (c1>>5) mul (((1 OR 1<<1) OR 1<<4) OR 1<<5) = ce
c31 + (c3>>5) mul (1<<1 OR 1<<3) = 66
c32 XOR ((c4>>5) mul ((1<<1 OR 1<<4) OR 1<<6)) = c1
c33 XOR ((c1>>5) mul (1<<1 OR 1<<2)) = 2d
Final script after considering l3ak{} as known
wrapper
from z3 import *
c1 = BitVec('c1', 8)
c3 = BitVec('c3', 8)
c4 = BitVec('c4', 8)
names = [f'c{i}' for i in range(6, 34)]
c = {i: BitVec(f'c{i}', 8) for i in range(6, 34)}
s1 = LShR(c1, 5)
s3 = LShR(c3, 5)
s4 = LShR(c4, 5)
solver = Solver()
solver.add(c1 == 0x6c)
solver.add(c3 == 0x61)
solver.add(c4 == 0x6b)
eqs = [
(6, s1, 0x11, 0x20, 'sub'),
(7, s3, 0x41, 0xb2, 'sub'),
(8, s4, 0x0d, 0x49, 'sub'),
(9, s1, 0x38, 0xdb, 'add'),
(10, s3, 0x0e, 0x48, 'sub'),
(11, s4, 0xd1, 0xd2, 'add'),
(12, s1, 0x3a, 0xe3, 'add'),
(13, s3, 0x41, 0xf0, 'xor'),
(14, s4, 0x04, 0x57, 'sub'),
(15, s1, 0x13, 0x8e, 'add'),
(16, s3, 0x41, 0xaf, 'sub'),
(17, s4, 0x31, 0xc6, 'add'),
(18, s1, 0x05, 0x6e, 'add'),
(19, s3, 0xc3, 0xb9, 'add'),
(20, s3, 0x1a, 0x82, 'add'),
(21, s1, 0x14, 0x37, 'sub'),
(22, s3, 0x0a, 0x55, 'sub'),
(23, s4, 0x29, 0xfc, 'sub'),
(24, s1, 0xd1, 0xbd, 'sub'),
(25, s3, 0x2a, 0xf4, 'sub'),
(26, s4, 0x10, 0x14, 'sub'),
(27, s1, 0x13, 0x98, 'add'),
(28, s3, 0x0e, 0x58, 'xor'),
(29, s4, 0x46, 0x03, 'add'),
(30, s1, 0x33, 0xce, 'sub'),
(31, s3, 0x0a, 0x66, 'add'),
(32, s4, 0x52, 0xc1, 'xor'),
(33, s1, 0x06, 0x2d, 'xor'),
]
for idx, shift_expr, mask, target, op in eqs:
var = c[idx]
term = shift_expr * mask
if op == 'sub':
solver.add(var - term == target)
elif op == 'add':
solver.add(var + term == target)
elif op == 'xor':
solver.add(var ^ term == target)
if solver.check() == sat:
m = solver.model()
def val(v):
return m.eval(v, model_completion=True).as_long()
print(f"c1 = 0x{val(c1):02x}")
print(f"c3 = 0x{val(c3):02x}")
print(f"c4 = 0x{val(c4):02x}")
print()
for i in range(6, 34):
print(f"c{i} = 0x{val(c[i]):02x}")
known = {1: 0x6c, 2: 0x33, 3: 0x61, 4: 0x6b, 5: 0x7b, 34: 0x7d}
all_bytes = dict(known)
all_bytes[1] = val(c1)
all_bytes[3] = val(c3)
all_bytes[4] = val(c4)
for i in range(6, 34):
all_bytes[i] = val(c[i])
flag = ''.join(chr(all_bytes[i]) for i in sorted(all_bytes))
print()
print(f"{flag}")
else:
print("UNSAT")Output:
$ python3 solve.py
c1 = 0x6c
c3 = 0x61
c4 = 0x6b
c6 = 0x53
c7 = 0x75
c8 = 0x70
c9 = 0x33
c10 = 0x72
c11 = 0x5f
c12 = 0x35
c13 = 0x33
c14 = 0x63
c15 = 0x55
c16 = 0x72
c17 = 0x33
c18 = 0x5f
c19 = 0x70
c20 = 0x34
c21 = 0x73
c22 = 0x73
c23 = 0x77
c24 = 0x30
c25 = 0x72
c26 = 0x44
c27 = 0x5f
c28 = 0x72
c29 = 0x31
c30 = 0x67
c31 = 0x48
c32 = 0x37
c33 = 0x3f
l3ak{Sup3r_53cUr3_p4ssw0rD_r1gH7?}
This output is the original password that the author uses to unlock his system.
Unfortunately, this was not the actual flag. After chatting a bit on tickets and reading the description carefully, we understood what was the intended solution.
The description mentioned:
My current password l3ak{...} seems to be working fine, but something tells me someone else has been messing around on my machine 💀.
After that we considered L3AK as the known wrapper
Final Script after considering L3AK as the compromised
wrapper
from z3 import *
c1 = BitVec('c1', 8)
c3 = BitVec('c3', 8)
c4 = BitVec('c4', 8)
names = [f'c{i}' for i in range(6, 34)]
c = {i: BitVec(f'c{i}', 8) for i in range(6, 34)}
s1 = LShR(c1, 5)
s3 = LShR(c3, 5)
s4 = LShR(c4, 5)
solver = Solver()
solver.add(c1 == 0x4c)
solver.add(c3 == 0x41)
solver.add(c4 == 0x4b)
eqs = [
(6, s1, 0x11, 0x20, 'sub'),
(7, s3, 0x41, 0xb2, 'sub'),
(8, s4, 0x0d, 0x49, 'sub'),
(9, s1, 0x38, 0xdb, 'add'),
(10, s3, 0x0e, 0x48, 'sub'),
(11, s4, 0xd1, 0xd2, 'add'),
(12, s1, 0x3a, 0xe3, 'add'),
(13, s3, 0x41, 0xf0, 'xor'),
(14, s4, 0x04, 0x57, 'sub'),
(15, s1, 0x13, 0x8e, 'add'),
(16, s3, 0x41, 0xaf, 'sub'),
(17, s4, 0x31, 0xc6, 'add'),
(18, s1, 0x05, 0x6e, 'add'),
(19, s3, 0xc3, 0xb9, 'add'),
(20, s3, 0x1a, 0x82, 'add'),
(21, s1, 0x14, 0x37, 'sub'),
(22, s3, 0x0a, 0x55, 'sub'),
(23, s4, 0x29, 0xfc, 'sub'),
(24, s1, 0xd1, 0xbd, 'sub'),
(25, s3, 0x2a, 0xf4, 'sub'),
(26, s4, 0x10, 0x14, 'sub'),
(27, s1, 0x13, 0x98, 'add'),
(28, s3, 0x0e, 0x58, 'xor'),
(29, s4, 0x46, 0x03, 'add'),
(30, s1, 0x33, 0xce, 'sub'),
(31, s3, 0x0a, 0x66, 'add'),
(32, s4, 0x52, 0xc1, 'xor'),
(33, s1, 0x06, 0x2d, 'xor'),
]
for idx, shift_expr, mask, target, op in eqs:
var = c[idx]
term = shift_expr * mask
if op == 'sub':
solver.add(var - term == target)
elif op == 'add':
solver.add(var + term == target)
elif op == 'xor':
solver.add(var ^ term == target)
if solver.check() == sat:
m = solver.model()
def val(v):
return m.eval(v, model_completion=True).as_long()
print(f"c1 = 0x{val(c1):02x}")
print(f"c3 = 0x{val(c3):02x}")
print(f"c4 = 0x{val(c4):02x}")
print()
for i in range(6, 34):
print(f"c{i} = 0x{val(c[i]):02x}")
known = {1: 0x4c, 2: 0x33, 3: 0x41, 4: 0x4b, 5: 0x7b, 34: 0x7d}
all_bytes = dict(known)
all_bytes[1] = val(c1)
all_bytes[3] = val(c3)
all_bytes[4] = val(c4)
for i in range(6, 34):
all_bytes[i] = val(c[i])
flag = ''.join(chr(all_bytes[i]) for i in sorted(all_bytes))
print()
print(f"{flag}")
else:
print("UNSAT")Output:
$ python3 solve.py
c1 = 0x4c
c3 = 0x41
c4 = 0x4b
c6 = 0x42
c7 = 0x34
c8 = 0x63
c9 = 0x6b
c10 = 0x64
c11 = 0x30
c12 = 0x6f
c13 = 0x72
c14 = 0x5f
c15 = 0x68
c16 = 0x31
c17 = 0x64
c18 = 0x64
c19 = 0x33
c20 = 0x4e
c21 = 0x5f
c22 = 0x69
c23 = 0x4e
c24 = 0x5f
c25 = 0x48
c26 = 0x34
c27 = 0x72
c28 = 0x44
c29 = 0x77
c30 = 0x34
c31 = 0x52
c32 = 0x65
c33 = 0x21
L3AK{B4ckd0or_h1dd3N_iN_H4rDw4Re!}
Final flag:
L3AK{B4ckd0or_h1dd3N_iN_H4rDw4Re!}