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 1, 2021
1 parent daace74 commit 1e4de55
Show file tree
Hide file tree
Showing 37 changed files with 3,019 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
7 changes: 6 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 @@ -11,6 +11,7 @@ zapAddOn {

apiClientGen {
api.set("org.zaproxy.addon.oast.OastApi")
options.set("org.zaproxy.addon.oast.OastParam")
messages.set(file("src/main/resources/org/zaproxy/addon/oast/resources/Messages.properties"))
}
}
Expand All @@ -22,3 +23,7 @@ crowdin {
tokens.put("%helpPath%", resourcesPath)
}
}

dependencies {
testImplementation(project(":testutils"))
}
145 changes: 144 additions & 1 deletion addOns/oast/src/main/java/org/zaproxy/addon/oast/ExtensionOast.java
Expand Up @@ -19,12 +19,38 @@
*/
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.BoastService;
import org.zaproxy.addon.oast.services.callback.CallbackService;
import org.zaproxy.addon.oast.services.oast.OastService;
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 {
public class ExtensionOast extends ExtensionAdaptor implements SessionChangedListener {

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 OastParam param;
private OastOptionsPanel oastOptionsPanel;
private OastPanel oastPanel;

public ExtensionOast() {
super(NAME);
Expand All @@ -33,11 +59,128 @@ public ExtensionOast() {
@Override
public void hook(ExtensionHook extensionHook) {
super.hook(extensionHook);
registerOastService(new BoastService(this));
registerOastService(new CallbackService(this));
extensionHook.addApiImplementor(new OastApi());
extensionHook.addOptionsParamSet(getParam());
extensionHook.addSessionListener(this);
getOastServices().values().forEach(t -> t.hook(extensionHook));
if (hasView()) {
extensionHook.getHookView().addStatusPanel(getOastPanel());
extensionHook.getHookView().addOptionPanel(getOastOptionsPanel());
ExtensionHelp.enableHelpKey(getOastPanel(), "ui.tabs.callbacks");
}
}

@Override
public void optionsLoaded() {
getOastServices().values().forEach(OastService::optionsLoaded);
}

@Override
public void postInit() {
getOastServices().values().forEach(OastService::postInit);
}

@Override
public void unload() {
super.unload();
services.values().forEach(this::unregisterOastService);
}

@Override
public void sessionChanged(Session session) {
ThreadUtils.invokeAndWaitHandled(
() -> {
getOastPanel().clearCallbackRequests();
addCallbacksFromDatabaseIntoCallbackPanel(session);
});
}

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

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

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

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

public void registerOastService(OastService service) {
services.put(service.getName(), service);
}

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

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

OastParam getParam() {
if (param == null) {
param = new OastParam();
}
return param;
}

private OastOptionsPanel getOastOptionsPanel() {
if (oastOptionsPanel == null) {
oastOptionsPanel = new OastOptionsPanel(this);
}
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 sessionAboutToChange(Session session) {}

@Override
public void sessionScopeChanged(Session session) {}

@Override
public void sessionModeChanged(Control.Mode mode) {}
}
67 changes: 67 additions & 0 deletions addOns/oast/src/main/java/org/zaproxy/addon/oast/OastParam.java
@@ -0,0 +1,67 @@
/*
* 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 org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.zaproxy.zap.common.VersionedAbstractParam;

public class OastParam extends VersionedAbstractParam {

/** Update this if params are added, changed or deleted. */
private static final int PARAM_CURRENT_VERSION = 1;

private static final String PARAM_BASE_KEY = "oast";
private static final String PARAM_SERVER = PARAM_BASE_KEY + ".service";

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

private String oastService;

public OastParam() {}

public String getOastService() {
return oastService;
}

public void setOastService(String oastService) {
this.oastService = oastService;
getConfig().setProperty(PARAM_SERVER, oastService);
}

@Override
protected void parseImpl() {
oastService = getString(PARAM_SERVER, Constant.messages.getString("oast.callback.name"));
}

@Override
protected String getConfigVersionKey() {
return PARAM_BASE_KEY + VERSION_ATTRIBUTE;
}

@Override
protected int getCurrentVersion() {
return PARAM_CURRENT_VERSION;
}

@Override
protected void updateConfigsImpl(int fileVersion) {}
}
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 comments on commit 1e4de55

Please sign in to comment.