Skip to content

Commit

Permalink
oast: First Version
Browse files Browse the repository at this point in the history
Signed-off-by: ricekot <ricekot@gmail.com>
  • Loading branch information
ricekot committed Aug 3, 2021
1 parent cb164d7 commit 843ed2c
Show file tree
Hide file tree
Showing 37 changed files with 2,874 additions and 4 deletions.
2 changes: 1 addition & 1 deletion addOns/oast/gradle.properties
@@ -1,2 +1,2 @@
version=0.0.1
version=0.1.0
release=false
6 changes: 5 additions & 1 deletion addOns/oast/oast.gradle.kts
@@ -1,4 +1,4 @@
description = "OAST Support: Exploit Out-Of-Band Vulnerabilities"
description = "Allows you to exploit out-of-band vulnerabilities"

zapAddOn {
addOnName.set("OAST Support")
Expand All @@ -22,3 +22,7 @@ crowdin {
tokens.put("%helpPath%", resourcesPath)
}
}

dependencies {
testImplementation(project(":testutils"))
}
166 changes: 166 additions & 0 deletions addOns/oast/src/main/java/org/zaproxy/addon/oast/ExtensionOast.java
Expand Up @@ -19,25 +19,191 @@
*/
package org.zaproxy.addon.oast;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.extension.ExtensionAdaptor;
import org.parosproxy.paros.extension.ExtensionHook;
import org.parosproxy.paros.extension.SessionChangedListener;
import org.parosproxy.paros.model.HistoryReference;
import org.parosproxy.paros.model.Session;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.zaproxy.addon.oast.services.boast.BoastOptionsPanelTab;
import org.zaproxy.addon.oast.services.boast.BoastParam;
import org.zaproxy.addon.oast.services.boast.BoastService;
import org.zaproxy.addon.oast.services.callback.CallbackOptionsPanelTab;
import org.zaproxy.addon.oast.services.callback.CallbackService;
import org.zaproxy.addon.oast.ui.OastOptionsPanel;
import org.zaproxy.addon.oast.ui.OastPanel;
import org.zaproxy.zap.extension.help.ExtensionHelp;
import org.zaproxy.zap.utils.ThreadUtils;

public class ExtensionOast extends ExtensionAdaptor {

private static final String NAME = ExtensionOast.class.getSimpleName();
private static final Logger LOGGER = LogManager.getLogger(ExtensionOast.class);
static final int HISTORY_TYPE_OAST = 22; // Equal to HistoryReference.TYPE_OAST

private final Map<String, OastService> services = new HashMap<>();
private OastOptionsPanel oastOptionsPanel;
private OastPanel oastPanel;
private BoastService boastService;
private CallbackService callbackService;

public ExtensionOast() {
super(NAME);
}

@Override
public void init() {
boastService = new BoastService();
callbackService = new CallbackService();
registerOastService(boastService);
registerOastService(callbackService);
}

@Override
public void hook(ExtensionHook extensionHook) {
super.hook(extensionHook);
extensionHook.addApiImplementor(new OastApi());
extensionHook.addSessionListener(new OastSessionChangedListener());

extensionHook.addOptionsParamSet(new BoastParam());
extensionHook.addOptionsParamSet(callbackService.getParam());

extensionHook.addOptionsChangedListener(callbackService);

if (hasView()) {
extensionHook.getHookView().addOptionPanel(getOastOptionsPanel());
getOastOptionsPanel().addServicePanel(new BoastOptionsPanelTab(boastService));
getOastOptionsPanel().addServicePanel(new CallbackOptionsPanelTab(callbackService));
extensionHook.getHookView().addStatusPanel(getOastPanel());
ExtensionHelp.enableHelpKey(getOastPanel(), "oast.tab");
}
}

@Override
public void optionsLoaded() {
callbackService.optionsLoaded();
}

@Override
public void postInit() {
boastService.startService();
callbackService.startService();
}

public void deleteAllCallbacks() {
try {
ThreadUtils.invokeAndWaitHandled(() -> getOastPanel().clearOastRequests());
this.getModel()
.getDb()
.getTableHistory()
.deleteHistoryType(
this.getModel().getSession().getSessionId(), HISTORY_TYPE_OAST);
} catch (DatabaseException e) {
LOGGER.error(e.getMessage(), e);
}
}

private void registerOastService(OastService service) {
if (service == null || service.getName().isEmpty()) {
throw new IllegalArgumentException("Invalid Service Provided");
}
if (services.containsKey(service.getName())) {
throw new IllegalArgumentException("Service Already Exists");
}
if (hasView()) {
service.addOastRequestHandler(o -> getOastPanel().addOastRequest(o));
}
services.put(service.getName(), service);
}

private void unregisterOastService(OastService service) {
services.remove(service.getName());
}

public Map<String, OastService> getOastServices() {
return Collections.unmodifiableMap(services);
}

private OastOptionsPanel getOastOptionsPanel() {
if (oastOptionsPanel == null) {
oastOptionsPanel = new OastOptionsPanel();
}
return oastOptionsPanel;
}

public OastPanel getOastPanel() {
if (oastPanel == null) {
oastPanel = new OastPanel(this);
}
return oastPanel;
}

@Override
public boolean supportsDb(String type) {
return true;
}

@Override
public boolean canUnload() {
return true;
}

@Override
public void unload() {
boastService.stopService();
callbackService.stopService();
unregisterOastService(boastService);
unregisterOastService(callbackService);
}

private class OastSessionChangedListener implements SessionChangedListener {
@Override
public void sessionChanged(Session session) {
ThreadUtils.invokeAndWaitHandled(
() -> {
getOastPanel().clearOastRequests();
addCallbacksFromDatabaseIntoCallbackPanel(session);
});
getOastServices().values().forEach(OastService::sessionChanged);
}

private void addCallbacksFromDatabaseIntoCallbackPanel(Session session) {
if (session == null) {
return;
}

try {
List<Integer> historyIds =
getModel()
.getDb()
.getTableHistory()
.getHistoryIdsOfHistType(session.getSessionId(), HISTORY_TYPE_OAST);

for (int historyId : historyIds) {
HistoryReference historyReference = new HistoryReference(historyId);
OastRequest request = OastRequest.create(historyReference);
getOastPanel().addOastRequest(request);
}
} catch (DatabaseException | HttpMalformedHeaderException e) {
LOGGER.error(e.getMessage(), e);
}
}

@Override
public void sessionAboutToChange(Session session) {}

@Override
public void sessionScopeChanged(Session session) {}

@Override
public void sessionModeChanged(Control.Mode mode) {}
}
}
94 changes: 94 additions & 0 deletions addOns/oast/src/main/java/org/zaproxy/addon/oast/OastRequest.java
@@ -0,0 +1,94 @@
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2021 The ZAP Development Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.addon.oast;

import java.util.List;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.model.HistoryReference;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.network.HttpHeader;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.addon.oast.ui.OastTableModel;
import org.zaproxy.zap.view.table.DefaultHistoryReferencesTableEntry;

public class OastRequest extends DefaultHistoryReferencesTableEntry {

private String handler;
private String source;
private String referer;

private OastRequest(HistoryReference historyReference) {
super(historyReference, OastTableModel.COLUMNS);
}

public static OastRequest create(HttpMessage httpMessage, String source, String handler)
throws DatabaseException, HttpMalformedHeaderException {
HistoryReference historyReference =
new HistoryReference(
Model.getSingleton().getSession(),
ExtensionOast.HISTORY_TYPE_OAST,
httpMessage);
historyReference.addTag(source);
historyReference.addTag(handler);
return create(historyReference);
}

public static OastRequest create(HistoryReference historyReference)
throws HttpMalformedHeaderException, DatabaseException {
OastRequest oastRequest = new OastRequest(historyReference);
if (!historyReference.getTags().isEmpty()) {
List<String> tags = historyReference.getTags();
if (isValidIpAddress(tags.get(0))) {
oastRequest.source = tags.get(0);
oastRequest.handler = tags.get(1);
} else {
oastRequest.source = tags.get(1);
oastRequest.handler = tags.get(0);
}
}
oastRequest.referer =
historyReference.getHttpMessage().getRequestHeader().getHeader(HttpHeader.REFERER);
return oastRequest;
}

public String getHandler() {
return handler;
}

public String getSource() {
return source;
}

public String getReferer() {
return referer;
}

private static boolean isValidIpAddress(String ip) {
try {
new URI(ip, true);
return true;
} catch (URIException e) {
return false;
}
}
}
@@ -0,0 +1,25 @@
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2021 The ZAP Development Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.addon.oast;

@FunctionalInterface
public interface OastRequestHandler {
void handle(OastRequest oastRequest);
}
47 changes: 47 additions & 0 deletions addOns/oast/src/main/java/org/zaproxy/addon/oast/OastService.java
@@ -0,0 +1,47 @@
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2021 The ZAP Development Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.addon.oast;

import java.util.ArrayList;
import java.util.List;

public abstract class OastService {

private final List<OastRequestHandler> oastRequestHandlerList = new ArrayList<>();

public abstract String getName();

/** Starts the OastService. This method should be called after ZAP has initialised. */
public abstract void startService();

public abstract void stopService();

public void sessionChanged() {}

public void addOastRequestHandler(OastRequestHandler oastRequestHandler) {
oastRequestHandlerList.add(oastRequestHandler);
}

public void handleOastRequest(OastRequest oastRequest) {
for (OastRequestHandler handler : oastRequestHandlerList) {
handler.handle(oastRequest);
}
}
}

0 comments on commit 843ed2c

Please sign in to comment.