Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bf9a508
SHI exploit prevention on one sink for java.lang.Runtime.exec(java.la…
jandro996 Sep 13, 2024
bf04013
fix spotless
jandro996 Nov 28, 2024
7043b2c
first steps to add cmdArray support (not blocking)
jandro996 Nov 29, 2024
f3bb30f
Fix known addresses
jandro996 Dec 10, 2024
8323532
Fix test
jandro996 Dec 10, 2024
663712b
Add support for arrayCmd methods and more smoke tests
jandro996 Dec 10, 2024
68f08b2
Add support for arrayCmd methods and more smoke tests
jandro996 Dec 10, 2024
3b62d1c
Add support for arrayCmd methods and more smoke tests
jandro996 Dec 10, 2024
d7226e1
Add support for ProcessBuilder
jandro996 Dec 10, 2024
b488d8a
Move to ProcessImplInstrumentation approach
jandro996 Dec 11, 2024
6d97145
Change to cmdi keeping ProcessImpl approach
jandro996 Dec 11, 2024
2c7672e
fix
jandro996 Dec 13, 2024
f3c4fe1
Add SHI
jandro996 Dec 13, 2024
996ab77
Add metrics to cmdi and shi with rule_variant tag
jandro996 Dec 13, 2024
cf855f7
Add another test
jandro996 Dec 13, 2024
92f9021
fix cmdi capability
jandro996 Dec 14, 2024
7846c74
change cmdi payloads
jandro996 Dec 16, 2024
dd3d414
Merge branch 'master' into alejandro.gonzalez/rasp-command-injection
jandro996 Dec 16, 2024
431cba1
format test
jandro996 Dec 16, 2024
650378e
fix comment
jandro996 Dec 16, 2024
c37357d
Merge branch 'master' into alejandro.gonzalez/rasp-command-injection
jandro996 Dec 16, 2024
5615a0a
change Runtime instrumentation to Appsec
jandro996 Dec 16, 2024
f384a09
Merge branch 'master' into alejandro.gonzalez/rasp-command-injection
jandro996 Dec 17, 2024
a2bc8f8
Merge branch 'master' into alejandro.gonzalez/rasp-command-injection
jandro996 Dec 18, 2024
15ba143
Merge branch 'master' into alejandro.gonzalez/rasp-command-injection
jandro996 Dec 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move to ProcessImplInstrumentation approach
  • Loading branch information
jandro996 committed Dec 14, 2024
commit b488d8a3c7689755d2c3c7e61bc440a37264857a
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ private Flow<Void> onNetworkConnection(RequestContext ctx_, String url) {
}
}

private Flow<Void> onShellCmd(RequestContext ctx_, Object command) {
private Flow<Void> onShellCmd(RequestContext ctx_, String[] command) {
AppSecRequestContext ctx = ctx_.getData(RequestContextSlot.APPSEC);
if (ctx == null) {
return NoopFlow.INSTANCE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class GatewayBridgeSpecification extends DDSpecification {
TriFunction<RequestContext, UserIdCollectionMode, String, Flow<Void>> userIdCB
TriFunction<RequestContext, UserIdCollectionMode, String, Flow<Void>> loginSuccessCB
TriFunction<RequestContext, UserIdCollectionMode, String, Flow<Void>> loginFailureCB
BiFunction<RequestContext, Object, Flow<Void>> shellCmdCB
BiFunction<RequestContext, String[], Flow<Void>> shellCmdCB

void setup() {
callInitAndCaptureCBs()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package datadog.trace.instrumentation.java.lang;

import datadog.trace.agent.tooling.csi.CallSite;
import datadog.trace.api.appsec.RaspCallSites;
import datadog.trace.api.iast.IastCallSites;
import datadog.trace.api.iast.InstrumentationBridge;
import datadog.trace.api.iast.Sink;
Expand All @@ -12,9 +11,7 @@

// TODO deal with the environment
@Sink(VulnerabilityTypes.COMMAND_INJECTION)
@CallSite(
spi = {IastCallSites.class, RaspCallSites.class},
helpers = ShellCmdRaspHelper.class)
@CallSite(spi = IastCallSites.class)
public class ProcessBuilderCallSite {

@CallSite.Before("java.lang.Process java.lang.ProcessBuilder.start()")
Expand All @@ -25,23 +22,14 @@ public static void beforeStart(@CallSite.This @Nullable final ProcessBuilder sel
// be careful when fetching the environment as it does mutate the instance
final List<String> cmd = self.command();
if (cmd != null && cmd.size() > 0) {
iastCallback(cmd);
raspCallback(cmd);
}
}

private static void iastCallback(List<String> cmd) {
final CommandInjectionModule module = InstrumentationBridge.COMMAND_INJECTION;
if (module != null) {
try {
module.onProcessBuilderStart(cmd);
} catch (final Throwable e) {
module.onUnexpectedException("beforeStart threw", e);
final CommandInjectionModule module = InstrumentationBridge.COMMAND_INJECTION;
if (module != null) {
try {
module.onProcessBuilderStart(cmd);
} catch (final Throwable e) {
module.onUnexpectedException("beforeStart threw", e);
}
}
}
}

private static void raspCallback(List<String> list) {
ShellCmdRaspHelper.INSTANCE.beforeShellCmd(list.toArray(new String[0]));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public static AgentSpan startSpan(@Advice.Argument(0) final String[] command) th
span.setResourceName(ProcessImplInstrumentationHelpers.determineResource(command));
span.setTag("component", "subprocess");
ProcessImplInstrumentationHelpers.setTags(span, command);
ProcessImplInstrumentationHelpers.shiRaspCheck(command);
return span;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package datadog.trace.instrumentation.java.lang;

import datadog.trace.agent.tooling.csi.CallSite;
import datadog.trace.api.appsec.RaspCallSites;
import datadog.trace.api.iast.IastCallSites;
import datadog.trace.api.iast.InstrumentationBridge;
import datadog.trace.api.iast.Sink;
Expand All @@ -11,24 +10,34 @@
import javax.annotation.Nullable;

@Sink(VulnerabilityTypes.COMMAND_INJECTION)
@CallSite(
spi = {IastCallSites.class, RaspCallSites.class},
helpers = ShellCmdRaspHelper.class)
@CallSite(spi = IastCallSites.class)
public class RuntimeCallSite {

@CallSite.Before("java.lang.Process java.lang.Runtime.exec(java.lang.String)")
public static void beforeStart(@CallSite.Argument @Nullable final String command) {
if (command != null) { // runtime fails if null
iastCallback(command);
raspCallback(command);
final CommandInjectionModule module = InstrumentationBridge.COMMAND_INJECTION;
if (module != null) {
try {
module.onRuntimeExec(command);
} catch (final Throwable e) {
module.onUnexpectedException("beforeExec threw", e);
}
}
}
}

@CallSite.Before("java.lang.Process java.lang.Runtime.exec(java.lang.String[])")
public static void beforeExec(@CallSite.Argument @Nullable final String[] cmdArray) {
if (cmdArray != null && cmdArray.length > 0) { // runtime fails if null or empty
iastCallback(cmdArray);
raspCallback(cmdArray);
final CommandInjectionModule module = InstrumentationBridge.COMMAND_INJECTION;
if (module != null) {
try {
module.onRuntimeExec(cmdArray);
} catch (final Throwable e) {
module.onUnexpectedException("beforeExec threw", e);
}
}
}
}

Expand All @@ -37,8 +46,14 @@ public static void beforeExec(
@CallSite.Argument @Nullable final String command,
@CallSite.Argument @Nullable final String[] envp) {
if (command != null) { // runtime fails if null
iastCallback(envp, command);
raspCallback(command);
final CommandInjectionModule module = InstrumentationBridge.COMMAND_INJECTION;
if (module != null) {
try {
module.onRuntimeExec(envp, command);
} catch (final Throwable e) {
module.onUnexpectedException("beforeExec threw", e);
}
}
}
}

Expand All @@ -48,8 +63,14 @@ public static void beforeExec(
@CallSite.Argument @Nullable final String[] cmdArray,
@CallSite.Argument @Nullable final String[] envp) {
if (cmdArray != null && cmdArray.length > 0) { // runtime fails if null or empty
iastCallback(envp, cmdArray);
raspCallback(cmdArray);
final CommandInjectionModule module = InstrumentationBridge.COMMAND_INJECTION;
if (module != null) {
try {
module.onRuntimeExec(envp, cmdArray);
} catch (final Throwable e) {
module.onUnexpectedException("beforeExec threw", e);
}
}
}
}

Expand All @@ -60,8 +81,14 @@ public static void beforeExec(
@CallSite.Argument @Nullable final String[] envp,
@CallSite.Argument @Nullable final File dir) {
if (command != null) { // runtime fails if null
iastCallback(envp, command);
raspCallback(command);
final CommandInjectionModule module = InstrumentationBridge.COMMAND_INJECTION;
if (module != null) {
try {
module.onRuntimeExec(envp, command);
} catch (final Throwable e) {
module.onUnexpectedException("beforeExec threw", e);
}
}
}
}

Expand All @@ -72,60 +99,14 @@ public static void beforeExec(
@CallSite.Argument @Nullable final String[] envp,
@CallSite.Argument @Nullable final File dir) {
if (cmdArray != null && cmdArray.length > 0) { // runtime fails if null or empty
iastCallback(envp, cmdArray);
raspCallback(cmdArray);
}
}

private static void iastCallback(String command) {
final CommandInjectionModule module = InstrumentationBridge.COMMAND_INJECTION;
if (module != null) {
try {
module.onRuntimeExec(command);
} catch (final Throwable e) {
module.onUnexpectedException("iastCallback threw", e);
}
}
}

private static void iastCallback(String[] cmdArray) {
final CommandInjectionModule module = InstrumentationBridge.COMMAND_INJECTION;
if (module != null) {
try {
module.onRuntimeExec(cmdArray);
} catch (final Throwable e) {
module.onUnexpectedException("iastCallback threw", e);
final CommandInjectionModule module = InstrumentationBridge.COMMAND_INJECTION;
if (module != null) {
try {
module.onRuntimeExec(envp, cmdArray);
} catch (final Throwable e) {
module.onUnexpectedException("beforeExec threw", e);
}
}
}
}

private static void iastCallback(String[] envp, String command) {
final CommandInjectionModule module = InstrumentationBridge.COMMAND_INJECTION;
if (module != null) {
try {
module.onRuntimeExec(envp, command);
} catch (final Throwable e) {
module.onUnexpectedException("iastCallback threw", e);
}
}
}

private static void iastCallback(String[] envp, String[] cmdArray) {
final CommandInjectionModule module = InstrumentationBridge.COMMAND_INJECTION;
if (module != null) {
try {
module.onRuntimeExec(envp, cmdArray);
} catch (final Throwable e) {
module.onUnexpectedException("iastCallback threw", e);
}
}
}

private static void raspCallback(String command) {
ShellCmdRaspHelper.INSTANCE.beforeShellCmd(command);
}

private static void raspCallback(String[] cmdArray) {
ShellCmdRaspHelper.INSTANCE.beforeShellCmd(cmdArray);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import datadog.trace.api.gateway.RequestContextSlot
import datadog.trace.api.internal.TraceSegment
import datadog.trace.bootstrap.instrumentation.api.AgentSpan
import datadog.trace.bootstrap.instrumentation.api.AgentTracer
import datadog.trace.bootstrap.instrumentation.api.java.lang.ProcessImplInstrumentationHelpers
import spock.lang.Shared

import java.util.function.BiFunction

import static datadog.trace.api.gateway.Events.EVENTS

class ShellCmdRaspHelperForkedTest extends AgentTestRunner {
class ProcessImplInstrumentationShellCmdRaspForkedTest extends AgentTestRunner {

@Shared
protected static final ORIGINAL_TRACER = AgentTracer.get()
Expand Down Expand Up @@ -49,7 +50,7 @@ class ShellCmdRaspHelperForkedTest extends AgentTestRunner {
injectSysConfig(AppSecConfig.APPSEC_RASP_ENABLED, 'true')
}

void 'test Helper'() {
void 'test shiRaspCheck'() {

setup:
final callbackProvider = Mock(CallbackProvider)
Expand All @@ -58,15 +59,10 @@ class ShellCmdRaspHelperForkedTest extends AgentTestRunner {
tracer.getCallbackProvider(RequestContextSlot.APPSEC) >> callbackProvider

when:
ShellCmdRaspHelper.INSTANCE.beforeShellCmd(*args)
ProcessImplInstrumentationHelpers.shiRaspCheck(['cat etc/password'] as String[])

then:
1 * callbackProvider.getCallback(EVENTS.shellCmd()) >> listener
1 * listener.apply(reqCtx, expected) >> flow

where:
args | expected
['cat etc/password'] | 'cat etc/password'
[['cat etc/password', 'cat etc/password'] as String[]] | ['cat etc/password', 'cat etc/password']
1 * listener.apply(reqCtx, ['cat etc/password']) >> flow
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -573,11 +573,12 @@ class SpringBootSmokeTest extends AbstractAppSecServerSmokeTest {

where:
endpoint | cmd | params
'cmd' | ['cat etc/password'] | null
//TODO these test that receive String cmd instead of String[] cmd are not working due to internally are converted to ["cat", "etc/password"] and the WAF is not blocking them
// 'cmd' | ['cat etc/password'] | null
// 'cmdWithParams' | ['cat etc/password'] | ['param']
// 'cmdParamsAndFile' | ['cat etc/password'] | ['param']
'arrayCmd' | ['cat etc/password', 'cat etc/password'] | null
'cmdWithParams' | ['cat etc/password'] | ['param']
'arrayCmdWithParams' | ['cat etc/password', 'cat etc/password'] | ['param']
'cmdParamsAndFile' | ['cat etc/password'] | ['param']
'arrayCmdWithParamsAndFile' | ['cat etc/password', 'cat etc/password'] | ['param']
'processBuilder' | ['cat etc/password', 'cat etc/password'] | null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,8 @@ public EventType<TriFunction<RequestContext, UserIdCollectionMode, String, Flow<
private static final EventType SHELL_CMD = new ET<>("shell.cmd", SHELL_CDM_ID);

@SuppressWarnings("unchecked")
public EventType<BiFunction<RequestContext, Object, Flow<Void>>> shellCmd() {
return (EventType<BiFunction<RequestContext, Object, Flow<Void>>>) SHELL_CMD;
public EventType<BiFunction<RequestContext, String[], Flow<Void>>> shellCmd() {
return (EventType<BiFunction<RequestContext, String[], Flow<Void>>>) SHELL_CMD;
}

static final int MAX_EVENTS = nextId.get();
Expand Down
Loading