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

org.zaproxy.zap.extension.pscan.PassiveScanThread Maven / Gradle / Ivy

Go to download

The Zed Attack Proxy (ZAP) is an easy to use integrated penetration testing tool for finding vulnerabilities in web applications. It is designed to be used by people with a wide range of security experience and as such is ideal for developers and functional testers who are new to penetration testing. ZAP provides automated scanners as well as a set of tools that allow you to find security vulnerabilities manually.

There is a newer version: 2.15.0
Show newest version
package org.zaproxy.zap.extension.pscan;

import net.htmlparser.jericho.MasonTagTypes;
import net.htmlparser.jericho.MicrosoftTagTypes;
import net.htmlparser.jericho.PHPTagTypes;
import net.htmlparser.jericho.Source;

import org.apache.log4j.Logger;
import org.parosproxy.paros.control.Control.Mode;
import org.parosproxy.paros.core.proxy.ProxyListener;
import org.parosproxy.paros.core.scanner.Alert;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.db.TableHistory;
import org.parosproxy.paros.extension.SessionChangedListener;
import org.parosproxy.paros.extension.history.ExtensionHistory;
import org.parosproxy.paros.extension.history.ProxyListenerLog;
import org.parosproxy.paros.model.HistoryReference;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.model.Session;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.zap.extension.alert.ExtensionAlert;

public class PassiveScanThread extends Thread implements ProxyListener, SessionChangedListener {

	private static final Logger logger = Logger.getLogger(PassiveScanThread.class);

    //Could be after the last one that saves the HttpMessage, as this ProxyListener doesn't change the HttpMessage.
	public static final int PROXY_LISTENER_ORDER = ProxyListenerLog.PROXY_LISTENER_ORDER + 1;
	
	@SuppressWarnings("unused")
	private OptionsPassiveScan options = null;
	private PassiveScannerList scannerList = null;
	private int currentId = 1;
	private int lastId = -1;
	private int mainSleep = 5000;
	private int postSleep = 200;
	private volatile boolean shutDown = false;
	
	private final ExtensionHistory extHist;
	private final ExtensionAlert extAlert;
	private final PassiveScanParam pscanOptions;

	private TableHistory historyTable = null;
	private HistoryReference href = null;
	private Session session;

	/**
	 * Constructs a {@code PassiveScanThread} with the given data.
	 *
	 * @param passiveScannerList the passive scanners, must not be {@code null}.
	 * @param extHist the extension to obtain the (cached) history references, might be {@code null}.
	 * @param extensionAlert the extension used to raise the alerts, must not be {@code null}.
	 * @deprecated (2.6.0) Use
	 *             {@link #PassiveScanThread(PassiveScannerList, ExtensionHistory, ExtensionAlert, PassiveScanParam)} instead.
	 *             It will be removed in a future release.
	 */
	@Deprecated
	public PassiveScanThread(PassiveScannerList passiveScannerList, ExtensionHistory extHist, ExtensionAlert extensionAlert) {
		this(passiveScannerList, extHist, extensionAlert, new PassiveScanParam());
	}

	/**
	 * Constructs a {@code PassiveScanThread} with the given data.
	 *
	 * @param passiveScannerList the passive scanners, must not be {@code null}.
	 * @param extHist the extension to obtain the (cached) history references, might be {@code null}.
	 * @param extensionAlert the extension used to raise the alerts, must not be {@code null}.
	 * @param pscanOptions the passive scanner options, must not be {@code null}.
	 * @since 2.6.0
	 */
	public PassiveScanThread (PassiveScannerList passiveScannerList, ExtensionHistory extHist, ExtensionAlert extensionAlert,
			PassiveScanParam pscanOptions) {
		super("ZAP-PassiveScanner");
		this.setDaemon(true);
		
		if (extensionAlert == null) {
			throw new IllegalArgumentException("Parameter extensionAlert must not be null.");
		}
		
		this.scannerList = passiveScannerList;
		
		MicrosoftTagTypes.register();
		PHPTagTypes.register();
		PHPTagTypes.PHP_SHORT.deregister(); // remove PHP short tags otherwise they override processing instructions
		MasonTagTypes.register();

		extAlert = extensionAlert;
		this.extHist = extHist;
		this.pscanOptions = pscanOptions;
	}
	
	@Override
	public void run() {
		historyTable = Model.getSingleton().getDb().getTableHistory();
		session = Model.getSingleton().getSession();
		// Get the last id - in case we've just opened an existing session
		currentId = this.getLastHistoryId();
		lastId = currentId;
		
		while (!shutDown) {
			try {
				if (href != null || lastId > currentId ) {
					currentId ++;
				} else {
					// Either just started or there are no new records 
					try {
						Thread.sleep(mainSleep);
						if (shutDown) {
							return;
						}
						lastId = this.getLastHistoryId();
					} catch (InterruptedException e) {
						// New URL, but give it a chance to be processed first
						try {
							Thread.sleep(postSleep);
						} catch (InterruptedException e2) {
							// Ignore
						}
					}
				}
				try {
					href = getHistoryReference(currentId);
					//historyRecord = historyTable.read(currentId);
				} catch (Exception e) {
					if (shutDown) {
						return;
					}
					logger.error("Failed to read record " + currentId + " from History table", e);
				}

				if (href != null && (!pscanOptions.isScanOnlyInScope() || session.isInScope(href))) {
					try {
						// Parse the record
						HttpMessage msg = href.getHttpMessage();
						String response = msg.getResponseHeader().toString() + msg.getResponseBody().toString();
						Source src = new Source(response);
						
						for (PassiveScanner scanner : scannerList.list()) {
							try {
								if (shutDown) {
									return;
								}
								if (scanner.isEnabled() && scanner.appliesToHistoryType(href.getHistoryType())) {
									scanner.setParent(this);
									scanner.scanHttpRequestSend(msg, href.getHistoryId());
									if (msg.isResponseFromTargetHost()) {
										scanner.scanHttpResponseReceive(msg, href.getHistoryId(), src);
									}
								}
							} catch (Throwable e) {
								if (shutDown) {
									return;
								}
								logger.error("Scanner " + scanner.getName() + 
										" failed on record " + currentId + " from History table: "
										+ href.getMethod() + " " + href.getURI(), e);
							}
						}
					} catch (Exception e) {
						if (HistoryReference.getTemporaryTypes().contains(href.getHistoryType())) {
							if (logger.isDebugEnabled()) {
								logger.debug("Temporary record " + currentId + " no longer available:", e);
							}
						} else {
							logger.error("Parser failed on record " + currentId + " from History table", e);
						}
					}
					
				}
			} catch (Exception e) {
				if (shutDown) {
					return;
				}
				logger.error("Failed on record " + currentId + " from History table", e);
			}
		}
		
	}

    private HistoryReference getHistoryReference(final int historyReferenceId) {
        if (extHist != null) {
            return extHist.getHistoryReference(historyReferenceId);
        }

        try {
            return new HistoryReference(historyReferenceId);
        } catch (HttpMalformedHeaderException | DatabaseException e) {
            return null;
        }
    }

	private int getLastHistoryId() {
		return historyTable.lastIndex();
	}
	
	protected int getRecordsToScan() {
		return this.getLastHistoryId() - getLastScannedId();
	}

	private int getLastScannedId() {
		if (currentId > lastId) {
			return currentId - 1;
		}
		return currentId;
	}

	public void raiseAlert(int id, Alert alert) {
		if (shutDown) {
			return;
		}

		if (currentId != id) {
			logger.error("Alert id != currentId! " + id + " " + currentId);
		}

		alert.setSource(Alert.Source.PASSIVE);
	    // Raise the alert
		extAlert.alertFound(alert, href);

	}

    private void notifyHistoryItemChanged(HistoryReference historyReference) {
        if (extHist != null) {
            extHist.notifyHistoryItemChanged(historyReference);
        }
    }
	
	public void addTag(int id, String tag) {
		if (shutDown) {
			return;
		}

		try {
			if (! href.getTags().contains(tag)) {
				href.addTag(tag);
				notifyHistoryItemChanged(href);
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}

	@Override
	public int getArrangeableListenerOrder() {
		return PROXY_LISTENER_ORDER;
	}
	
	@Override
	public boolean onHttpRequestSend(HttpMessage msg) {
		// Ignore
		return true;
	}

	@Override
	public boolean onHttpResponseReceive(HttpMessage msg) {
		// Wakey wakey
		this.interrupt();
		return true;
	}

	@Override
	public void sessionChanged(Session session) {
		// Reset the currentId
		historyTable = Model.getSingleton().getDb().getTableHistory();
		href = null;
		// Get the last id - in case we've just opened an existing session
		currentId = historyTable.lastIndex();
		lastId = currentId;
	}
	
	@Override
	public void sessionScopeChanged(Session session) {
	}

	public void shutdown() {
		this.shutDown = true;
	}
	
	@Override
	public void sessionAboutToChange(Session session) {
	}
	
	@Override
	public void sessionModeChanged(Mode mode) {
		// Ignore
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy