Write-Up: Digital Safety Annex — DSA Exploitation

Author: 0xtor4sec | Date: Apr 16, 2025 | Read time: 4 min

Hi everyone,

This challenge presents a seemingly secure application — “Digital Safety Annex” — where users can store messages, sign them, and later verify or download them.

The system is built around the Digital Signature Algorithm (DSA) to ensure message authenticity and protect user data. Usually, I look for web vulnerabilities or bad authentication logic. But this time, I’m facing something different: a mathematical challenge. If I want to retrieve the flag, I need to understand how DSA works — and more importantly, whether the way it’s implemented here leaves anything exploitable.

📘 Preface: Understanding DSA — Theory Before Action

Before diving into the code, I realize I’m unfamiliar with the cryptographic foundations of the application: the Digital Signature Algorithm (DSA). I take a moment to explore how it works and why it’s considered secure. Here’s what I learn:

Notation:

🔍 Step 1: Exploring the Interface and Locating the Flag

I start by reviewing server.py. It simulates a secure annex where users can store and verify signed messages. One line stands out:

annex.sign("ElGamalSux", FLAG, HTB_PASSWD)

This tells me the flag is stored under the user “ElGamalSux.”

Exploring the menu, I discover that option [4] reveals public DSA parameters and signed logs after admin authentication using the hardcoded password 5up3r_53cur3_P45sw0r6.

🚨 Step 2: Vulnerability Discovery

In _dsa.py, I find the nonce generation:

k = random.randint(self.k_min, k_max)

With k_min = 65500 and a user-based k_max, brute-force becomes feasible.

Using:

x = (s * k - int(h, 16)) * pow(r, -1, q) % q

I can recover the private key once k is known.

🛠 Step 3: Exploiting the Vulnerabilities

Instead of manual input through the menu, I automate the brute-force with:

def brute_force_k(p, q, g, r_target, k_min=65500, k_max=1000000):
    for k in range(k_min, k_max+1):
        if pow(g, k, p) % q == r_target:
            return k
    return None

Once I get k, I compute x and submit it to the server to retrieve the signed message.

🏁 Step 4: Retrieving and Validating the Flag

With recovered x and known k, I pass verification and access the flag.

🧵 Summary of Identified Vulnerabilities

🧠 Conclusion

This challenge highlights how even with full code access, the real breakthrough comes from understanding how DSA fails with weak randomness. The math was the key.

“Don't listen to the person who has all the answers. Listen to the person who has the questions.”