/** * TLS-Attacker - A Modular Penetration Testing Framework for TLS * * Copyright 2014-2016 Ruhr University Bochum / Hackmanit GmbH * * Licensed under Apache License 2.0 * http://www.apache.org/licenses/LICENSE-2.0 */ package de.rub.nds.tlsattacker.attacks.impl; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import de.rub.nds.tlsattacker.attacks.config.PaddingOracleCommandConfig; import de.rub.nds.tlsattacker.modifiablevariable.VariableModification; import de.rub.nds.tlsattacker.modifiablevariable.bytearray.ByteArrayModificationFactory; import de.rub.nds.tlsattacker.modifiablevariable.bytearray.ModifiableByteArray; import de.rub.nds.tlsattacker.tls.Attacker; import de.rub.nds.tlsattacker.tls.config.ConfigHandler; import de.rub.nds.tlsattacker.tls.constants.ConnectionEnd; import de.rub.nds.tlsattacker.tls.exceptions.WorkflowExecutionException; import de.rub.nds.tlsattacker.tls.protocol.ProtocolMessage; import de.rub.nds.tlsattacker.tls.protocol.alert.AlertMessage; import de.rub.nds.tlsattacker.tls.protocol.application.ApplicationMessage; import de.rub.nds.tlsattacker.tls.record.Record; import de.rub.nds.tlsattacker.tls.util.LogLevel; import de.rub.nds.tlsattacker.tls.workflow.TlsContext; import de.rub.nds.tlsattacker.tls.workflow.WorkflowExecutor; import de.rub.nds.tlsattacker.tls.workflow.WorkflowTrace; import de.rub.nds.tlsattacker.transport.TransportHandler; import de.rub.nds.tlsattacker.util.ArrayConverter; /** * Executes a padding oracle attack check. It logs an error in case the tested * server is vulnerable to poodle. * * @author Juraj Somorovsky (juraj.somorovsky@rub.de) */ public class PaddingOracleAttack extends Attacker<PaddingOracleCommandConfig> { private static final Logger LOGGER = LogManager.getLogger(PaddingOracleAttack.class); private final List<ProtocolMessage> lastMessages; public PaddingOracleAttack(PaddingOracleCommandConfig config) { super(config); lastMessages = new LinkedList<>(); } @Override public void executeAttack(ConfigHandler configHandler) { List<Record> records = new LinkedList<>(); records.addAll(createRecordsWithPlainData()); records.addAll(createRecordsWithModifiedMac()); records.addAll(createRecordsWithModifiedPadding()); for (Record record : records) { executeAttackRound(configHandler, record); } LOGGER.info("All the attack runs executed. The following messages arrived at the ends of the connections"); LOGGER.info("If there are different messages, this could indicate the server does not process padding correctly"); LinkedHashSet<ProtocolMessage> pmSet = new LinkedHashSet<>(); for (int i = 0; i < lastMessages.size(); i++) { ProtocolMessage pm = lastMessages.get(i); pmSet.add(pm); Record r = records.get(i); LOGGER.info("----- NEXT TLS CONNECTION WITH MODIFIED APPLICATION DATA RECORD -----"); if (r.getPlainRecordBytes() != null) { LOGGER.info("Plain record bytes of the modified record: "); LOGGER.info(ArrayConverter.bytesToHexString(r.getPlainRecordBytes().getValue())); LOGGER.info("Last protocol message in the protocol flow"); } LOGGER.info(pm.toString()); } List<ProtocolMessage> pmSetList = new LinkedList<>(pmSet); if (pmSet.size() == 1) { LOGGER.log(LogLevel.CONSOLE_OUTPUT, "{}, NOT vulnerable, one message found: {}", config.getConnect(), pmSetList); vulnerable = false; } else { LOGGER.log(LogLevel.CONSOLE_OUTPUT, "{}, Vulnerable (?), more messages found, recheck in debug mode: {}", config.getConnect(), pmSetList); vulnerable = true; } } public void executeAttackRound(ConfigHandler configHandler, Record record) { TransportHandler transportHandler = configHandler.initializeTransportHandler(config); TlsContext tlsContext = configHandler.initializeTlsContext(config); WorkflowExecutor workflowExecutor = configHandler.initializeWorkflowExecutor(transportHandler, tlsContext); WorkflowTrace trace = tlsContext.getWorkflowTrace(); ApplicationMessage applicationMessage = new ApplicationMessage(ConnectionEnd.CLIENT); applicationMessage.addRecord(record); AlertMessage allertMessage = new AlertMessage(ConnectionEnd.SERVER); trace.getProtocolMessages().add(applicationMessage); trace.getProtocolMessages().add(allertMessage); try { workflowExecutor.executeWorkflow(); } catch (WorkflowExecutionException ex) { LOGGER.info("Not possible to finalize the defined workflow: {}", ex.getLocalizedMessage()); } lastMessages.add(trace.getLastProtocolMesssage()); tlsContexts.add(tlsContext); transportHandler.closeConnection(); } private List<Record> createRecordsWithPlainData() { List<Record> records = new LinkedList<>(); for (int i = 0; i < 64; i++) { byte[] padding = createPaddingBytes(i); int messageSize = config.getBlockSize() - (padding.length % config.getBlockSize()); byte[] message = new byte[messageSize]; byte[] plain = ArrayConverter.concatenate(message, padding); Record r = createRecordWithPlainData(plain); records.add(r); } Record r = createRecordWithPlainData(new byte[] { (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255 }); records.add(r); r = createRecordWithPlainData(new byte[] { (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255 }); records.add(r); return records; } private Record createRecordWithPlainData(byte[] plain) { Record r = new Record(); ModifiableByteArray plainData = new ModifiableByteArray(); VariableModification<byte[]> modifier = ByteArrayModificationFactory.explicitValue(plain); plainData.setModification(modifier); r.setPlainRecordBytes(plainData); return r; } private List<Record> createRecordsWithModifiedPadding() { List<Record> records = new LinkedList<>(); Record r = new Record(); ModifiableByteArray padding = new ModifiableByteArray(); VariableModification<byte[]> modifier = ByteArrayModificationFactory.xor(new byte[] { 1 }, 0); padding.setModification(modifier); r.setPadding(padding); records.add(r); return records; } private List<Record> createRecordsWithModifiedMac() { List<Record> records = new LinkedList<>(); Record r = new Record(); ModifiableByteArray mac = new ModifiableByteArray(); VariableModification<byte[]> modifier = ByteArrayModificationFactory.xor(new byte[] { 1, 1, 1 }, 0); mac.setModification(modifier); r.setMac(mac); records.add(r); return records; } private byte[] createPaddingBytes(int padding) { byte[] paddingBytes = new byte[padding + 1]; for (int i = 0; i < paddingBytes.length; i++) { paddingBytes[i] = (byte) padding; } return paddingBytes; } }