wsattacker.library.intelligentdos.IntelligentDoSLibraryImpl.java Source code

Java tutorial

Introduction

Here is the source code for wsattacker.library.intelligentdos.IntelligentDoSLibraryImpl.java

Source

/**
 * WS-Attacker - A Modular Web Services Penetration Testing Framework Copyright
 * (C) 2013 Christian Altmeier
 *
 * 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.library.intelligentdos;

import static wsattacker.library.intelligentdos.common.RequestType.TAMPERED;
import static wsattacker.library.intelligentdos.common.RequestType.UNTAMPERED;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
import org.apache.commons.math3.stat.descriptive.SynchronizedDescriptiveStatistics;
import org.apache.log4j.Logger;

import wsattacker.library.intelligentdos.common.AttackModel;
import wsattacker.library.intelligentdos.common.DoSParam;
import wsattacker.library.intelligentdos.common.Metric;
import wsattacker.library.intelligentdos.common.RequestType;
import wsattacker.library.intelligentdos.common.SuccessfulAttack;
import wsattacker.library.intelligentdos.common.Threshold;
import wsattacker.library.intelligentdos.dos.CoerciveParsing;
import wsattacker.library.intelligentdos.dos.DoSAttack;
import wsattacker.library.intelligentdos.dos.DoSAttack.PayloadPosition;
import wsattacker.library.intelligentdos.dos.HashCollision;
import wsattacker.library.intelligentdos.dos.XmlAttributeCount;
import wsattacker.library.intelligentdos.dos.XmlElementCount;
import wsattacker.library.intelligentdos.dos.XmlEntityExpansion;
import wsattacker.library.intelligentdos.dos.XmlExternalEntity;
import wsattacker.library.intelligentdos.dos.XmlOverlongNames;
import wsattacker.library.intelligentdos.helper.CommonParamItem;
import wsattacker.library.intelligentdos.position.Position;
import wsattacker.library.intelligentdos.position.PositionIterator;
import wsattacker.library.intelligentdos.success.SuccessDecider;

/**
 * @author Christian Altmeier
 */
public class IntelligentDoSLibraryImpl implements IntelligentDoSLibrary {

    private static final int UTR_THREADS = 4;

    private static final int UTR_REQUESTS = 8;

    public static final int DEFAULT_SERVER_RECOVERY = 20000;

    public static final List<CommonParamItem> COMMONPARAMS = new ArrayList<CommonParamItem>();

    private static final int[][] DEFAULTCOMMONPARAMS = { { 16, 2, 750 }, { 16, 2, 500 }, { 24, 4, 500 },
            { 24, 4, 250 }, { 32, 8, 250 }, { 128, 8, 250 } };

    static {
        for (int[] commonParam : DEFAULTCOMMONPARAMS) {
            COMMONPARAMS.add(new CommonParamItem(commonParam));
        }
    }

    private final static Logger LOG = Logger.getLogger(IntelligentDoSLibraryImpl.class);

    private final String xmlMessage;

    private final PositionIterator positionIterator;

    private boolean hasFurtherAttack;

    boolean noFurtherIterations = false;

    private int serverRecovery = 0;

    private List<CommonParamItem> commonParamList;

    // array with all possible attacks
    private DoSAttack[] attacks = getAllAttacks();

    // attacks
    private Iterator<DoSAttack> attackIterator;

    // attack specific payload positions
    private Iterator<PayloadPosition> payloadPositionIterator;

    // common attack parameter
    private Iterator<CommonParamItem> commonParamsIterator;

    private AttackModel currentAttack;

    // current parameters
    private DoSAttack currentDoSAttack;

    private PayloadPosition currentPayloadPosition;

    private Position currentPosition;

    private CommonParamItem currentCommonParams;

    private SuccessDecider successDecider;

    private Long[] currentUntampered;

    private final List<SuccessfulAttack> successfulAttackList;

    private final List<DoSAttack> notPossibleList;

    private final List<Threshold> thresholdList;

    private double maximumRequestsPerSecond;

    // Init state
    private DoSState doSState = new InitialState();

    private boolean m_needRecovery;

    // statistic about the last request
    private final DescriptiveStatistics testProbeStatisticsShort = new SynchronizedDescriptiveStatistics(10);

    private final DescriptiveStatistics testProbeStatisticsLong = new SynchronizedDescriptiveStatistics(100);

    private final SummaryStatistics untamperedStatistics = new SummaryStatistics();

    public IntelligentDoSLibraryImpl(String xmlMessage, PositionIterator positionIterator) {
        if (xmlMessage == null) {
            throw new IllegalArgumentException("xmlMessage cannot be null!");
        }
        if (positionIterator == null) {
            throw new IllegalArgumentException("positionIterator cannot be null!");
        }

        this.positionIterator = positionIterator;
        this.xmlMessage = xmlMessage;

        successfulAttackList = new ArrayList<SuccessfulAttack>();
        notPossibleList = new ArrayList<DoSAttack>();
        thresholdList = new ArrayList<Threshold>();

        commonParamList = COMMONPARAMS;
    }

    @Override
    public void initialize() {
        attackIterator = Arrays.asList(attacks).iterator();

        for (DoSAttack doSAttack : attacks) {
            doSAttack.initialize();
        }

        currentDoSAttack = attackIterator.next();
        LOG.info("start with " + currentDoSAttack.getName());

        payloadPositionIterator = Arrays.asList(currentDoSAttack.getPossiblePossitions()).iterator();

        while (payloadPositionIterator.hasNext() && !hasFurtherAttack) {
            currentPayloadPosition = payloadPositionIterator.next();

            if (positionIterator.hasNext(currentPayloadPosition)) {
                // only if we have some expansion points in the document, we can start
                // an attack
                hasFurtherAttack = true;
                currentPosition = positionIterator.next(currentPayloadPosition);
                currentAttack = createFirstAttack();
                doSState = new UntamperedState();
            }
        }

    }

    public static DoSAttack[] getAllAttacks() {
        return new DoSAttack[] { new CoerciveParsing(), new XmlElementCount(), new XmlAttributeCount(),
                new XmlEntityExpansion(), new XmlExternalEntity(), new HashCollision(), new XmlOverlongNames() };
    }

    @Override
    public void setAttacks(DoSAttack[] attacks) {
        this.attacks = new DoSAttack[attacks.length];
        System.arraycopy(attacks, 0, this.attacks, 0, attacks.length);
    }

    public void setSuccessDecider(SuccessDecider successDecider) {
        this.successDecider = successDecider;
    }

    public void setCommonParams(List<CommonParamItem> commonParamList) {
        this.commonParamList = commonParamList;
    }

    public void setServerRecoveryTime(int timeInMillies) {
        this.serverRecovery = timeInMillies;
    }

    void setDoSState(DoSState doSState) {
        String format = String.format("switch state from %s to %s", this.doSState.getName(), doSState.getName());
        LOG.trace(format);
        this.doSState = doSState;
    }

    AttackModel getCurrentAttack() {
        return currentAttack;
    }

    void setCurrentAttack(AttackModel currentAttack) {
        this.currentAttack = currentAttack;
    }

    void setHasFurtherAttack(boolean hasFurtherAttack) {
        this.hasFurtherAttack = hasFurtherAttack;
    }

    SuccessDecider getSuccessDecider() {
        return successDecider;
    }

    @Override
    public boolean hasFurtherAttack() {
        return hasFurtherAttack;
    }

    @Override
    public synchronized AttackModel nextAttack() {
        // set hasFurtherAttack to false because we cannot create a new attack
        // as long as we didn't update the measures
        hasFurtherAttack = false;

        return currentAttack;
    }

    @Override
    public synchronized void update(AttackModel attackModel) {
        // update dependent on the state
        doSState.update(this, attackModel);
    }

    @Override
    public void updateTestProbes(Metric metric) {

        // update dependent on the state
        doSState.updateTestProbes(metric);
        synchronized (doSState) {
            testProbeStatisticsShort.addValue(metric.getDuration());
            testProbeStatisticsLong.addValue(metric.getDuration());

            // long round_s = Math.round( testProbeStatisticsShort.getMean() / 100000.0 );
            // long round_l = Math.round( testProbeStatisticsLong.getMean() / 100000.0 );
            // double utr = Math.round( untamperedStatistics.getMean() / 100000.0 );
        }
    }

    /*
     * (non-Javadoc)
     * @see wsattacker.library.intelligentdos.IntelligentDoSLibrary#getSuccessfulAttacks ()
     */
    @Override
    public List<SuccessfulAttack> getSuccessfulAttacks() {
        return new ArrayList<SuccessfulAttack>(successfulAttackList);
    }

    @Override
    public List<DoSAttack> getNotPossible() {
        return new ArrayList<DoSAttack>(notPossibleList);
    }

    @Override
    public List<Threshold> getThresholds() {
        return new ArrayList<Threshold>(thresholdList);
    }

    private AttackModel createFirstAttack() {
        // TODO this has to be bound to the attack
        if (currentDoSAttack.hasFurtherParams()) {
            currentDoSAttack.nextParam();
        }

        // common params
        commonParamsIterator = createCommonParamsIterator();
        currentCommonParams = commonParamsIterator.next();

        return createNewUntampered(false);
    }

    AttackModel createNextAttack() {
        return createNextAttack(false);
    }

    AttackModel createNextAttack(boolean needRecovery) {

        do {
            iterate();
        } while (!noFurtherIterations && isNotWise());

        AttackModel attack = null;
        if (!noFurtherIterations) {
            if (currentUntampered == null) {
                attack = createNewUntampered(needRecovery || m_needRecovery);
                m_needRecovery = false;
            } else {
                attack = createNewTampered(needRecovery);
            }
        }

        return attack;
    }

    private boolean isNotWise() {
        boolean wise = false;

        if (currentPosition == null) {
            return true;
        }

        for (DoSAttack doSAttack : notPossibleList) {
            if (currentDoSAttack.getName().equals(doSAttack.getName())) {
                return true;
            }
        }

        if (maximumRequestsPerSecond > 0 && currentCommonParams.getReuqestsPerSecond() > maximumRequestsPerSecond) {
            return true;
        }

        for (Threshold threshold : thresholdList) {
            // Same DoS Attack and params > 0
            if (currentDoSAttack.getName().equals(threshold.getMinimum().getName())
                    && currentDoSAttack.compareTo(threshold.getMinimum()) > 0) {
                return true;
            }
        }

        for (SuccessfulAttack successfulAttack : successfulAttackList) {
            if (currentPosition.equals(successfulAttack.getPosition())
                    && currentPayloadPosition == successfulAttack.getPayloadPosition()
                    && currentDoSAttack.equals(successfulAttack.getDoSAttack())) {
                return true;
            }
        }

        return wise;
    }

    private void iterate() {
        if (commonParamsIterator.hasNext()) {
            currentCommonParams = commonParamsIterator.next();

        } else if (positionIterator.hasNext(currentPayloadPosition)) {
            // reset common attack parameter
            commonParamsIterator = createCommonParamsIterator();

            currentPosition = positionIterator.next(currentPayloadPosition);

            currentCommonParams = commonParamsIterator.next();

        } else if (payloadPositionIterator.hasNext()) {
            // reset common attack parameter
            commonParamsIterator = createCommonParamsIterator();

            // extension points
            positionIterator.reset();

            currentPayloadPosition = payloadPositionIterator.next();

            currentPosition = positionIterator.next(currentPayloadPosition);

            currentCommonParams = commonParamsIterator.next();

        } else if (currentDoSAttack.hasFurtherParams()) {
            // reset common attack parameter
            commonParamsIterator = createCommonParamsIterator();

            // extension points
            positionIterator.reset();

            // Possible PayloadPositions
            payloadPositionIterator = Arrays.asList(currentDoSAttack.getPossiblePossitions()).iterator();

            // next attack parameter for the concrete dos attack
            currentDoSAttack.nextParam();

            position();

            currentCommonParams = commonParamsIterator.next();

            // reset utr
            currentUntampered = null;

        } else if (attackIterator.hasNext()) {
            // reset common attack parameter
            commonParamsIterator = createCommonParamsIterator();

            // extension points
            positionIterator.reset();

            currentDoSAttack = attackIterator.next();
            LOG.info("start with " + currentDoSAttack.getName());

            // Possible PayloadPositions
            payloadPositionIterator = Arrays.asList(currentDoSAttack.getPossiblePossitions()).iterator();

            // next attack parameter for the concrete dos attack
            currentDoSAttack.nextParam();

            position();

            currentCommonParams = commonParamsIterator.next();

            // reset utr
            currentUntampered = null;

            // request server recovery
            m_needRecovery = true;
        } else {
            // No further attacks
            noFurtherIterations = true;
        }
    }

    private void position() {
        currentPosition = null;
        while (payloadPositionIterator.hasNext() && currentPosition == null) {
            currentPayloadPosition = payloadPositionIterator.next();

            if (positionIterator.hasNext(currentPayloadPosition)) {
                currentPosition = positionIterator.next(currentPayloadPosition);
            }
        }
    }

    AttackModel createNewTampered(boolean needRecovery) {
        AttackModel attackModel = createAttack(TAMPERED, currentCommonParams, needRecovery);

        String xmlWithPlaceholder = currentPosition.createPlaceholder(currentPayloadPosition);
        String content = currentDoSAttack.getTamperedRequest(xmlWithPlaceholder, currentPayloadPosition);
        attackModel.setRequestContent(content);

        attackModel.setPayloadPosition(currentPayloadPosition);
        attackModel.setPosition(currentPosition);

        return attackModel;
    }

    AttackModel createNewTampered(DoSAttack doSAttack) {
        AttackModel attackModel = createAttack(TAMPERED, currentCommonParams, false);
        attackModel.setDoSAttack(doSAttack);

        String xmlWithPlaceholder = currentPosition.createPlaceholder(currentPayloadPosition);
        String content = doSAttack.getTamperedRequest(xmlWithPlaceholder, currentPayloadPosition);
        attackModel.setRequestContent(content);

        attackModel.setPayloadPosition(currentPayloadPosition);
        attackModel.setPosition(currentPosition);

        return attackModel;
    }

    AttackModel createNewUntampered(DoSAttack doSAttack) {
        AttackModel attackModel = createAttack(UNTAMPERED, currentCommonParams, false);
        attackModel.setDoSAttack(doSAttack);

        String xmlWithPlaceholder = currentPosition.createPlaceholder(currentPayloadPosition);
        String content = doSAttack.getUntamperedRequest(xmlWithPlaceholder, currentPayloadPosition);
        attackModel.setRequestContent(content);

        attackModel.setPayloadPosition(currentPayloadPosition);
        attackModel.setPosition(currentPosition);

        return attackModel;
    }

    AttackModel createNewUntampered(boolean needRecovery) {
        CommonParamItem utr = new CommonParamItem(UTR_REQUESTS, UTR_THREADS, 750);

        AttackModel attackModel = createNewUntampered(needRecovery, utr);

        return attackModel;
    }

    AttackModel createVerifyUntampered(boolean needRecovery) {
        AttackModel attackModel = createNewUntampered(needRecovery, currentCommonParams);

        return attackModel;
    }

    private AttackModel createNewUntampered(boolean needRecovery, CommonParamItem utr) {
        AttackModel attackModel = createAttack(UNTAMPERED, utr, needRecovery);

        String xmlWithPlaceholder = currentPosition.createPlaceholder(currentPayloadPosition);
        String content = currentDoSAttack.getUntamperedRequest(xmlWithPlaceholder, currentPayloadPosition);
        attackModel.setRequestContent(content);
        return attackModel;
    }

    private AttackModel createAttack(RequestType requestType, CommonParamItem commonParams, boolean needRecovery) {
        AttackModel attackModel = new AttackModel();
        attackModel.setRequestType(requestType);

        try {
            // Clone the dos attack, else we manipulate the "original" and cannot save the state
            attackModel.setDoSAttack(currentDoSAttack.clone());
        } catch (CloneNotSupportedException e) {
            LOG.info(e.getMessage(), e);
        }

        attackModel.setParamItem(commonParams);

        if (needRecovery) {
            attackModel.setServerRecoveryBeforeSend(serverRecovery);
        }

        return attackModel;
    }

    @Override
    public boolean wasSuccessful() {
        return successfulAttackList.size() > 0;
    }

    @Override
    public String getTestProbeContent() {
        return xmlMessage;
    }

    private Iterator<CommonParamItem> createCommonParamsIterator() {
        return commonParamList.iterator();
    }

    public Long[] getCurrentUntampered() {
        Long[] copy = new Long[currentUntampered.length];
        System.arraycopy(currentUntampered, 0, copy, 0, currentUntampered.length);
        return copy;
    }

    void setCurrentUntampered(Long[] currentUntampered) {
        this.currentUntampered = currentUntampered;

        untamperedStatistics.clear();
        for (Long value : currentUntampered) {
            untamperedStatistics.addValue(value);
        }
    }

    boolean addSuccessful(SuccessfulAttack successfulAttack) {
        LOG.info("successful: " + successfulAttack);
        return successfulAttackList.add(successfulAttack);
    }

    void addNotPossible(DoSAttack doSAttack) {
        LOG.info(doSAttack.getName() + " is not possible against this Web Service");
        notPossibleList.add(doSAttack);
    }

    @Override
    public double getMaximumRequestsPerSecond() {
        return maximumRequestsPerSecond;
    }

    void setMaximumRequestsPerSecond(double maximumRequestsPerSecond) {
        double fmrps = ((int) (maximumRequestsPerSecond * 100.0)) / 100.0;
        LOG.info("Threshold detected by " + fmrps + " requests per second.");
        this.maximumRequestsPerSecond = maximumRequestsPerSecond;
    }

    void addThreshold(Threshold threshold) {
        LOG.info(String.format("Threshold detected for %s", threshold.getMinimum().getName()));

        List<DoSParam<?>> minParams = threshold.getMinimum().getCurrentParams();
        List<DoSParam<?>> maxParams = threshold.getMaximum().getCurrentParams();

        for (int index = 0; index < minParams.size(); index++) {
            DoSParam<?> minParam = minParams.get(index);
            DoSParam<?> maxParam = maxParams.get(index);

            if (!minParam.getValueAsString().equals(maxParam.getValueAsString())) {
                LOG.info(" -> " + minParam.getDescription() + " " + minParam.getValueAsString() + " - "
                        + maxParam.getValueAsString());
            }
        }

        thresholdList.add(threshold);
    }

    public static IDoSBuilder create() {
        return new IDoSBuilder();
    }

    public static class IDoSBuilder {

        private int serverRecoveryTime;

        private String xmlMessage;

        private PositionIterator positionIterator;

        private SuccessDecider successDecider;

        private DoSAttack[] dosAttackArray;

        private List<CommonParamItem> commonParamList;

        private IDoSBuilder() {

        }

        public IDoSBuilder withXmlMessage(String xmlMessage) {
            this.xmlMessage = xmlMessage;
            return this;
        }

        public IDoSBuilder withPositionIterator(PositionIterator positionIterator) {
            this.positionIterator = positionIterator;
            return this;
        }

        public IDoSBuilder withAttacks(DoSAttack[] dosAttackArray) {
            this.dosAttackArray = new DoSAttack[dosAttackArray.length];
            System.arraycopy(dosAttackArray, 0, this.dosAttackArray, 0, dosAttackArray.length);

            return this;
        }

        public IDoSBuilder withSuccessDecider(SuccessDecider successDecider) {
            this.successDecider = successDecider;
            return this;
        }

        public IDoSBuilder withCommonParams(List<CommonParamItem> commonParamList) {
            this.commonParamList = commonParamList;
            return this;
        }

        public IDoSBuilder withServerRecoveryTime(int serverRecoveryTime) {
            this.serverRecoveryTime = serverRecoveryTime;
            return this;
        }

        public IntelligentDoSLibrary build() {
            IntelligentDoSLibraryImpl intelligentDoSLibraryImpl = new IntelligentDoSLibraryImpl(this.xmlMessage,
                    this.positionIterator);

            intelligentDoSLibraryImpl.setAttacks(this.dosAttackArray);
            intelligentDoSLibraryImpl.setSuccessDecider(this.successDecider);
            intelligentDoSLibraryImpl.setCommonParams(this.commonParamList);
            intelligentDoSLibraryImpl.setServerRecoveryTime(this.serverRecoveryTime);
            return intelligentDoSLibraryImpl;
        }

    }

}