/** * WS-Attacker - A Modular Web Services Penetration Testing Framework Copyright * (C) 2013 Dennis Kupser * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package wsattacker.plugin.xmlencryptionattack.option; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.*; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.xml.sax.SAXException; import wsattacker.library.signatureWrapping.option.Payload; import wsattacker.library.signatureWrapping.util.exception.InvalidPayloadException; import wsattacker.library.xmlencryptionattack.attackengine.AttackConfig; import wsattacker.library.xmlencryptionattack.attackengine.attacker.pkcs1.PKCS1AttackConfig; import static wsattacker.library.xmlencryptionattack.attackengine.oracle.concrete.pkcs1.strategy.PKCS1StrategyFactory.PKCS1Strategy.DIRECT; import wsattacker.library.xmlencryptionattack.detectionengine.detectionmanager.DetectFilterEnum; import wsattacker.library.xmlencryptionattack.detectionengine.detectionmanager.DetectionManager; import wsattacker.library.xmlencryptionattack.detectionengine.detectionmanager.FactoryFilter; import wsattacker.library.xmlencryptionattack.detectionengine.detectionmanager.Pipeline; import wsattacker.library.xmlencryptionattack.detectionengine.detectionstreams.DetectionReport; import wsattacker.library.xmlencryptionattack.detectionengine.filter.info.EncryptionInfo; import wsattacker.library.xmlencryptionattack.detectionengine.filter.info.SignatureInfo; import wsattacker.library.xmlencryptionattack.encryptedelements.AbstractEncryptionElement; import wsattacker.library.xmlencryptionattack.encryptedelements.AbstractRefElement; import wsattacker.library.xmlencryptionattack.encryptedelements.ElementAttackProperties; import wsattacker.library.xmlencryptionattack.encryptedelements.data.EncryptedDataElement; import wsattacker.library.xmlencryptionattack.encryptedelements.key.DataReferenceElement; import wsattacker.library.xmlencryptionattack.encryptedelements.key.EncryptedKeyElement; import wsattacker.library.xmlencryptionattack.util.SimStringStrategyFactory.SimStringStrategy; import wsattacker.library.xmlencryptionattack.util.XMLEncryptionConstants.OracleMode; import wsattacker.library.xmlencryptionattack.util.XMLEncryptionConstants.WrappingAttackMode; import wsattacker.library.xmlencryptionattack.util.XMLEncryptionConstants.XMLEncryptionAttackMode; import wsattacker.library.xmlutilities.dom.DomUtilities; import wsattacker.main.composition.plugin.option.AbstractOption; import wsattacker.main.composition.plugin.option.AbstractOptionMultiFiles; import wsattacker.main.composition.testsuite.CurrentRequestContentChangeObserver; import wsattacker.main.plugin.PluginOptionContainer; import wsattacker.main.plugin.option.OptionSimpleBoolean; import wsattacker.main.plugin.option.OptionSimpleMultiFiles; import wsattacker.main.plugin.option.OptionSoapAction; import wsattacker.plugin.xmlencryptionattack.XMLEncryptionAttack; public final class OptionManagerEncryption implements CurrentRequestContentChangeObserver, PropertyChangeListener { private static final Logger LOG = Logger.getLogger( OptionManagerEncryption.class ); private XMLEncryptionAttack m_Plugin; private DetectionManager m_DetectionManager; private final OptionSoapAction m_OptionSoapAction; private final OptionSimpleBoolean m_OptionUseSchema; private final OptionSimpleMultiFiles m_OptionSchemaFiles; private OptionServerErrorBehaviour m_OptionServerResponse; private final List<AbstractEncryptionElement> m_OptionPayloadList; private OptionPayloadEncryption m_OptionEncPays = null; private final AttackConfig m_AttackCfg; private boolean m_IsInProgress = false; private static final OptionManagerEncryption INSTANCE = new OptionManagerEncryption(); private final Pipeline m_PipeLine; public static OptionManagerEncryption getInstance() { return INSTANCE; } public XMLEncryptionAttack getPlugin() { return m_Plugin; } public void setPlugin( XMLEncryptionAttack plugin ) { if ( this.m_Plugin != null ) { this.m_Plugin.getPluginOptions().addPropertyChangeListener( this ); } this.m_Plugin = plugin; if ( plugin != null ) { this.m_Plugin.getPluginOptions().addPropertyChangeListener( this ); this.m_Plugin.getPluginOptions().setOptions( addConfigOptions() ); } plugin.setAttackCfg( m_AttackCfg ); } public DetectionManager getDetectioManager() { return m_DetectionManager; } /** * Initialization method. */ private OptionManagerEncryption() { this.m_OptionSoapAction = new OptionSoapAction( "Change\nAction?", "Allows to change the SoapAction Header." ); this.m_OptionSchemaFiles = new OptionSimpleMultiFiles( "Used\nSchema\nfiles", "Set the Schema Files.\nSoap11, Soap12, WSA, WSSE, WSU, DS and XPathFilter2\nare included by default." ); this.m_OptionUseSchema = new OptionSimpleBoolean( "Schema?", true, "Use XML Schema." ); this.m_OptionPayloadList = new ArrayList<AbstractEncryptionElement>(); this.m_OptionServerResponse = null; PKCS1AttackConfig pKCS1AttackCFG = new PKCS1AttackConfig(); this.m_AttackCfg = new AttackConfig(); m_AttackCfg.setPKCS1AttackCfg( pKCS1AttackCFG ); initAttackCfg(); m_PipeLine = new Pipeline(); } public void initAttackCfg() { /* Default */ m_AttackCfg.setOracleMode( OracleMode.ERROR_ORACLE ); m_AttackCfg.setWrappingMode( WrappingAttackMode.NO_WRAP ); m_AttackCfg.setXMLEncryptionAttack( XMLEncryptionAttackMode.CBC_ATTACK ); m_AttackCfg.setSimStringStrategyType( SimStringStrategy.DICE_COEFF ); m_AttackCfg.setChosenAttackPayload( null ); m_AttackCfg.setChosenWrapPayload( null ); m_AttackCfg.setStringCmpThresHold( AttackConfig.DEFAULT_STRING_CMP_THRESHOLD ); m_AttackCfg.setStringCmpWrappThreshold( AttackConfig.DEFAULT_STRING_CMP_WRAP_ERROR_THRESHOLD ); m_AttackCfg.getPKCS1AttackCfg().setServerRSAPubKey( null ); m_AttackCfg.getPKCS1AttackCfg().setPKCS1Strategy( DIRECT ); } public OptionServerErrorBehaviour getOptionServerResponse() { return m_OptionServerResponse; } private Logger log() { return Logger.getLogger( getClass() ); } @Override public void currentRequestContentChanged( String newContent, String oldContent ) { if ( !m_IsInProgress ) { m_IsInProgress = true; if ( !oldContent.equals( "" ) ) log().trace( "Current Request Content Changed" ); Document domDoc; try { domDoc = DomUtilities.stringToDom( newContent ); } catch ( SAXException e ) { if ( null != m_DetectionManager ) m_DetectionManager.setInputFile( null ); m_IsInProgress = false; return; } Document copyDomDoc = DomUtilities.createNewDomFromNode( domDoc.getDocumentElement() ); addPayloads( copyDomDoc ); List<AbstractOption> allOptions = addConfigOptions(); if ( m_Plugin != null ) { m_Plugin.getPluginOptions().setOptions( allOptions ); } m_IsInProgress = false; } } private void addPayloads( Document domDoc ) { DetectionReport detectReport = null; m_PipeLine.removeAllFilerFromPipline(); m_PipeLine.addFilerToPipline( FactoryFilter.createFilter( DetectFilterEnum.ENCRYPTIONFILTER ) ); m_PipeLine.addFilerToPipline( FactoryFilter.createFilter( DetectFilterEnum.SIGNATUREFILTER ) ); m_PipeLine.addFilerToPipline( FactoryFilter.createFilter( DetectFilterEnum.TIMESTAMPFILTER ) ); m_DetectionManager = handleDetection( domDoc ); detectReport = m_DetectionManager.getDetectionReport(); EncryptionInfo encInfo = (EncryptionInfo) detectReport.getDetectionInfo( DetectFilterEnum.ENCRYPTIONFILTER ); List<EncryptedKeyElement> encKeyList = encInfo.getEncryptedKeyElements(); List<EncryptedDataElement> encDataList = encInfo.getEncryptedDataElements(); for ( int i = 0; i < m_OptionPayloadList.size(); ++i ) { AbstractEncryptionElement option = m_OptionPayloadList.get( i ); ElementAttackProperties attackProps = option.getAttackProperties(); attackProps.removePropertyChangeListener( this ); } m_OptionPayloadList.clear(); for ( EncryptedKeyElement payload : encKeyList ) { List<AbstractRefElement> keyDatas = payload.getReferenceElementList(); ElementAttackProperties attackProps = payload.getAttackProperties(); if ( attackProps.isSigned() ) { attackProps.setWrappingPayloadElement( payload.getEncryptedElement() ); } for ( int j = 0; keyDatas.size() > j; j++ ) { EncryptedDataElement encData = ( (DataReferenceElement) keyDatas.get( j ) ).getRefEncData(); ElementAttackProperties attackPropsData = encData.getAttackProperties(); if ( attackPropsData.isSigned() ) attackPropsData.setWrappingPayloadElement( encData.getEncryptedElement() ); } attackProps.addPropertyChangeListener( this ); m_OptionPayloadList.add( payload ); } for ( EncryptedDataElement payload : encDataList ) { ElementAttackProperties attackProps = payload.getAttackProperties(); if ( attackProps.isSigned() ) { attackProps.setWrappingPayloadElement( payload.getEncryptedElement() ); } attackProps.addPropertyChangeListener( this ); m_OptionPayloadList.add( payload ); } this.m_OptionEncPays = new OptionPayloadEncryption( m_OptionPayloadList, this ); this.m_OptionServerResponse = new OptionServerErrorBehaviour( m_AttackCfg, detectReport ); m_OptionServerResponse.addPropertyChangeListener( OptionServerErrorBehaviour.PROP_SERVERBEHAVE, this ); if ( null != m_OptionServerResponse ) m_OptionServerResponse.removePropertyChangeListener( OptionServerErrorBehaviour.PROP_SERVERBEHAVE, this ); initAttackCfg(); } public DetectionReport getDetectReport() { return m_DetectionManager.getDetectionReport(); } /** * If no curent request is available, the SignatureManager must be notified. */ @Override public void noCurrentRequestcontent() { if ( m_IsInProgress ) { return; } m_IsInProgress = true; log().trace( "No Current Message" ); if ( null != m_DetectionManager ) m_DetectionManager.setInputFile( null ); clearOptions(); m_IsInProgress = false; } /** * This methods add the default config options to the OptionManagerEncryption. Those are: - Option for changing the * SOAPAction. - Option for aborting the attack if one XSW message is accepted. - Option to not use any XML Schema. * - Option to selected XML Schema files. - Option to add a search string. - The View Button - The Payload-Chooser * Combobox */ private List<AbstractOption> addConfigOptions() { List<AbstractOption> result; if ( getPlugin() == null ) { log().debug( "No plugin set?" ); result = Collections.<AbstractOption> emptyList(); } else { List<AbstractOption> newOptions = new ArrayList<AbstractOption>(); log().info( "Adding OptionSoapAction" ); newOptions.add( 0, m_OptionSoapAction ); log().info( "Adding OptionNoSchema" ); newOptions.add( 1, m_OptionUseSchema ); log().info( "Adding OptionSchemaFiles" ); newOptions.add( 2, m_OptionSchemaFiles ); if ( m_OptionPayloadList.size() > 0 ) { log().info( "Adding Enc Pays " ); newOptions.add( 3, m_OptionEncPays ); log().info( "Adding Server Error Response table " ); newOptions.add( 4, m_OptionServerResponse ); } result = newOptions; } return result; } /** * This function is only needed due to a GUI Bug in WS-Attacker which does not allow to put an AbstractOption at a * specific position. With this function, you can pop AbstractOptions up to one specific one, than add the needed * Options, and afterwards re-add the popped one putOptions. * * @param needle * @return */ public List<AbstractOption> popOptionsUpTo( AbstractOption needle ) { List<AbstractOption> result = new ArrayList<AbstractOption>(); PluginOptionContainer container = getPlugin().getPluginOptions(); if ( !container.contains( needle ) ) { return result; } while ( container.size() > 0 ) { AbstractOption last = container.getByIndex( container.size() - 1 ); if ( last == needle ) { break; } container.remove( last ); result.add( last ); } log().info( "Popped: " + result.toString() ); return result; } /** * This function is only needed due to a GUI Bug in WS-Attacker which does not allow to put an AbstractOption at a * specific position. With this function, you can pop AbstractOptions up to one specific one, than add the needed * Options, and afterwards re-add the popped one putOptions. * * @param needle * @return */ public void putOptions( List<AbstractOption> optionList ) { log().info( "Put: " + optionList.toString() ); PluginOptionContainer container = getPlugin().getPluginOptions(); for ( int i = optionList.size() - 1; i >= 0; --i ) { container.add( optionList.get( i ) ); } } /** * Clear all options consecutively. */ public void clearOptions() { if ( getPlugin() == null ) { log().debug( "No plugin set?" ); } else { log().info( "Clearing Options.." ); PluginOptionContainer container = getPlugin().getPluginOptions(); while ( container.size() > 0 ) { container.remove( container.getByIndex( 0 ) ); } } } /** * Handler if an option value is changed. Changes, e.g. the concrete showed PayloadOption. */ @Override public void propertyChange( PropertyChangeEvent pce ) { PluginOptionContainer container = getPlugin().getPluginOptions(); if ( pce.getSource() instanceof OptionPayloadEncryption ) { m_Plugin.checkState(); } else if ( pce.getSource() == m_OptionSchemaFiles ) { m_Plugin.setUsedSchemaFiles( m_OptionSchemaFiles.getFiles() ); } else if ( pce.getSource() == m_OptionUseSchema ) { log().info( "Remove Schema Files Option" ); if ( !m_OptionUseSchema.isOn() && container.contains( m_OptionSchemaFiles ) ) { container.remove( m_OptionSchemaFiles ); m_Plugin.setSchemaAnalyzerDepdingOnOption(); } else if ( !container.contains( m_OptionSchemaFiles ) ) { log().info( "Add Schema Files Option" ); container.add( 1 + container.indexOf( m_OptionUseSchema ), m_OptionSchemaFiles ); } } getPlugin().checkState(); } public OptionSoapAction getOptionSoapAction() { return m_OptionSoapAction; } public AbstractOptionMultiFiles getOptionSchemaFiles() { return m_OptionSchemaFiles; } public OptionSimpleBoolean getOptionUseSchema() { return m_OptionUseSchema; } private DetectionManager handleDetection( Document attackDoc ) { DetectionManager detectManager = null; detectManager = new DetectionManager( m_PipeLine, attackDoc ); try { detectManager.startDetection(); } catch ( InvalidPayloadException ex ) { LOG.error( ex ); } return detectManager; } private void setPayload() { DetectionReport detectReport = m_DetectionManager.getDetectionReport(); List<Payload> pays = ( (SignatureInfo) detectReport.getDetectionInfo( DetectFilterEnum.SIGNATUREFILTER ) ).getSignatureManager().getPayloads(); for ( int i = 0; pays.size() > i; i++ ) { pays.get( i ).setValue( pays.get( i ).getValue() ); // wrappingElement in original document -> "copy" in wrapping attackdoc // attackDocu for wrapping attacks -> new "copy" wrapping element -> attack element for encryption attack // in encryption attack -> wrappingDoc => "avoided file" -> copy of avoided file + ciphervalue of chosen // encryption attack // copy of "avoided file" is the last document for encryption attack } } }