POPO (Paillier Operation Performance Optimizer)
HackTheBox
An unknown and shady startup is selling its new product, called "POPO" - Paillier Operation Performance Optimizer. Our colleague Bongio claimed to have identified a confidentiality problem, but has now disappeared under mysterious circumstances. Before she disappeared, she published the suspicious c
$ ls tags/ techniques/
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
POPO (Paillier Operation Performance Optimizer) — HackTheBox
Description
An unknown and shady startup is selling its new product, called "POPO" - Paillier Operation Performance Optimizer. Our colleague Bongio claimed to have identified a confidentiality problem, but has now disappeared under mysterious circumstances. Before she disappeared, she published the suspicious code. It is time to get clarity and rescue her.
The server implements a Paillier homomorphic encryption system with a menu-driven interface offering encryption, knowledge proof (validate_role), homomorphic encryption testing, and optimization reset. The flag is encrypted as the message m during initialization: popo = POPO(bytes_to_long(FLAG)).
Analysis
Paillier Cryptosystem Background
Paillier is an additive homomorphic encryption scheme where:
- Key generation:
n = p·q,g = n+1,λ = lcm(p-1, q-1) - Encryption:
c = g^m · r^n mod n²(whereris a random blinding factor) - Decryption:
m = L(c^λ mod n²) · μ mod n, whereL(x) = (x-1)/nandμ = L(g^λ mod n²)⁻¹ mod n
The Vulnerability: optim State Bug
The anonymize() method has a critical state-dependent behavior controlled by self.optim:
- When
optim=0(first call): properly computeslocal = g^m mod n², then flipsoptimto 1. - When
optim=1(subsequent calls): uses the raw inputmdirectly aslocal— completely skipping the exponentiation step.
Additional Weaknesses
- Nonce reuse: The same
self.ris used for all encryptions when no explicitris provided, meaningb = r^n mod n²is constant across calls. - Private key oracle:
validate_role(gm)returnsλ = lcm(p-1, q-1)if the correctgmis provided — leaking the Paillier private key. - Branch control: Sending
m=0forces theelsebranch (local = self.gm), giving us the encrypted flag component directly.
Solution
Exploit Chain (3 steps to recover gm)
...
$ grep --similar
Similar writeups
- [crypto][free]MadMath— hackthebox
- [pwn][free]0xDiablos— hackthebox
- [crypto][free]Rhome— HackTheBox
- [misc][free]Not Posixtive— HackTheBox
- [crypto][free]AliEnS Challenge Scenario— HackTheBox