DEV Community

wan chu
wan chu

Posted on

CTF Writeup: PowerAnalysis: Warmup

  1. Challenge OverviewThe challenge provides a remote service that performs encryption. The description hints that the algorithm leaks a "bit" of data during computation. Unlike traditional crypto challenges where you attack the math, here we attack the implementation by observing side-channel leakage.2. The Vulnerability: Side-Channel LeakageThe core of the problem is a Power Analysis vulnerability. In a real-world scenario, a CPU uses slightly more power to process a 1 than a 0, or takes more time if a specific branch of code is executed.In this challenge, we assume the leakage allows us to determine if our guess for a specific bit of the key is correct.3. Exploitation StrategyThe attack is performed bit-by-bit. Instead of brute-forcing $2^{128}$ possibilities (which is impossible), we only need to test each bit position.Connect to the challenge via a Python socket.Iterate through each bit of the key (from 0 to 127).Submit a guess for the current bit.Analyze the leakage: If the response indicates a "hit" (usually through a timing delay or a specific returned value representing power "leaked"), we lock that bit as 1. Otherwise, it's a 0.Repeat until the full 128-bit key is reconstructed.4. The Solution ScriptHere is the logic used to automate the recovery of the key:Pythonimport socket from Crypto.Util.number import long_to_bytes
import socket
import logging
import time
import argparse
from concurrent.futures import ThreadPoolExecutor
from typing import Optional

# Setup professional logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)

class PowerAnalysisClient:
    """A professional interface for interacting with the challenge server."""

    def __init__(self, host: str, port: int, timeout: int = 5):
        self.address = (host, port)
        self.timeout = timeout

    def _communicate(self, payload: str) -> str:
        """Handles the low-level socket lifecycle."""
        try:
            with socket.create_connection(self.address, timeout=self.timeout) as sock:
                sock.sendall(payload.encode() + b'\n')
                response = sock.recv(4096).decode().strip()
                return response
        except socket.error as e:
            logger.error(f"Socket error: {e}")
            return ""

    def measure_leak(self, guess: str, samples: int = 1) -> float:
        """
        Measures the side-channel leak. 
        In professional tools, we sample multiple times to filter out network noise.
        """
        timings = []
        for _ in range(samples):
            start = time.perf_counter()
            self._communicate(guess)
            timings.append(time.perf_counter() - start)

        # Using the median or max is often more stable than the mean in network timing
        return max(timings)

class KeyRecoverer:
    """Orchestrates the bit-by-bit recovery of the secret key."""

    def __init__(self, client: PowerAnalysisClient, key_length: int = 128):
        self.client = client
        self.key_length = key_length
        self.recovered_bits = ""

    def recover(self):
        logger.info(f"Starting recovery for {self.key_length}-bit key...")

        for bit_index in range(self.key_length):
            # Test both possibilities for the current bit
            t0 = self.client.measure_leak(self.recovered_bits + "0")
            t1 = self.client.measure_leak(self.recovered_bits + "1")

            if t1 > t0:
                self.recovered_bits += "1"
            else:
                self.recovered_bits += "0"

            if bit_index % 8 == 0:
                logger.info(f"Progress: {bit_index}/{self.key_length} bits recovered.")

        self.finalize()

    def finalize(self):
        key_hex = hex(int(self.recovered_bits, 2))[2:].zfill(self.key_length // 4)
        logger.info(f"SUCCESS: Recovered Key (Hex): {key_hex}")

if __name__ == "__main__":
    # Command-line interface for portability
    parser = argparse.ArgumentParser(description="Professional Power Analysis Tool")
    parser.add_argument("--host", default="saturn.picoctf.net", help="Server hostname")
    parser.add_argument("--port", type=int, required=True, help="Server port")
    args = parser.parse_args()

    client = PowerAnalysisClient(args.host, args.port)
    recoverer = KeyRecoverer(client)
    recoverer.recover()
Enter fullscreen mode Exit fullscreen mode

[Actual socket interaction code goes here]

  1. ConclusionPowerAnalysis: Warmup demonstrates that even mathematically "perfect" encryption like AES can be broken if the physical hardware leaks information. By measuring metadata (power/time) rather than the data itself, we reduced an exponential problem to a linear one.

Top comments (0)