Skip to content

Commit c95b186

Browse files
committed
Add SSRF integration tests
1 parent 27869d4 commit c95b186

File tree

4 files changed

+129
-3
lines changed

4 files changed

+129
-3
lines changed

Diff for: appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy

+51-3
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ trait CommonTests {
275275
assert exploit.frames[2].line == 15
276276
}
277277

278-
static Stream<Arguments> getTestData() {
278+
static Stream<Arguments> getTestLfiData() {
279279
return Arrays.stream(new Arguments[]{
280280
Arguments.of("file_put_contents", "/tmp/dummy", 9),
281281
Arguments.of("readfile", "/tmp/dummy", 15),
@@ -285,8 +285,8 @@ trait CommonTests {
285285
}
286286

287287
@ParameterizedTest
288-
@MethodSource("getTestData")
289-
void 'file_put_contents generates LFI signal'(String target_function, String path, Integer line) {
288+
@MethodSource("getTestLfiData")
289+
void 'filesystem functions generate LFI signal'(String target_function, String path, Integer line) {
290290
HttpRequest req = container.buildReq('/filesystem.php?function='+target_function+"&path="+path).GET().build()
291291
def trace = container.traceFromRequest(req, ofString()) { HttpResponse<String> re ->
292292
assert re.statusCode() == 200
@@ -528,4 +528,52 @@ trait CommonTests {
528528
throw new AssertionError("Module has STATIC_TLS flag: $res.stdout")
529529
}
530530
}
531+
532+
static Stream<Arguments> getTestSsrfData() {
533+
return Arrays.stream(new Arguments[]{
534+
Arguments.of("file_get_contents", 12),
535+
Arguments.of("fopen", 9),
536+
});
537+
}
538+
539+
@ParameterizedTest
540+
@MethodSource("getTestSsrfData")
541+
void 'filesystem functions generate SSRF signal'(String target_function, Integer line) {
542+
HttpRequest req = container.buildReq('/ssrf.php?function='+target_function+"&domain=169.254.169.254").GET().build()
543+
def trace = container.traceFromRequest(req, ofString()) { HttpResponse<String> re ->
544+
assert re.statusCode() == 200
545+
assert re.body().contains('OK')
546+
}
547+
548+
Span span = trace.first()
549+
550+
assert span.metrics."_dd.appsec.enabled" == 1.0d
551+
assert span.metrics."_dd.appsec.waf.duration" > 0.0d
552+
assert span.meta."_dd.appsec.event_rules.version" != ''
553+
554+
InputStream stream = new ByteArrayInputStream( span.meta_struct."_dd.stack".decodeBase64() )
555+
MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(stream)
556+
List<Object> stacks = []
557+
stacks << MsgpackHelper.unpackSingle(unpacker)
558+
Object exploit = stacks.first().exploit.first()
559+
560+
assert exploit.language == "php"
561+
assert exploit.id ==~ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/
562+
assert exploit.frames[0].file == "ssrf.php"
563+
assert exploit.frames[0].function == target_function
564+
assert exploit.frames[0].id == 1
565+
assert exploit.frames[0].line == line
566+
assert exploit.frames[1].file == "ssrf.php"
567+
assert exploit.frames[1].function == "one"
568+
assert exploit.frames[1].id == 2
569+
assert exploit.frames[1].line == 18
570+
assert exploit.frames[2].file == "ssrf.php"
571+
assert exploit.frames[2].function == "two"
572+
assert exploit.frames[2].id == 3
573+
assert exploit.frames[2].line == 22
574+
assert exploit.frames[3].file == "ssrf.php"
575+
assert exploit.frames[3].function == "three"
576+
assert exploit.frames[3].id == 4
577+
assert exploit.frames[3].line == 25
578+
}
531579
}

Diff for: appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/RemoteConfigTests.groovy

+2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ class RemoteConfigTests {
8484
Capability.ASM_CUSTOM_RULES,
8585
Capability.ASM_CUSTOM_BLOCKING_RESPONSE,
8686
Capability.ASM_TRUSTED_IPS,
87+
Capability.ASM_RASP_LFI,
88+
Capability.ASM_RASP_SSRF,
8789
].each { assert it in capSet }
8890

8991
doReq.call(403)

Diff for: appsec/tests/integration/src/test/waf/recommended.json

+48
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,54 @@
8585
"stack_trace"
8686
]
8787
},
88+
{
89+
"id": "rasp-934-100",
90+
"name": "Server-side request forgery exploit",
91+
"tags": {
92+
"type": "ssrf",
93+
"category": "vulnerability_trigger",
94+
"cwe": "918",
95+
"capec": "1000/225/115/664",
96+
"confidence": "1",
97+
"module": "rasp"
98+
},
99+
"conditions": [
100+
{
101+
"parameters": {
102+
"resource": [
103+
{
104+
"address": "server.io.net.url"
105+
}
106+
],
107+
"params": [
108+
{
109+
"address": "server.request.query"
110+
},
111+
{
112+
"address": "server.request.body"
113+
},
114+
{
115+
"address": "server.request.path_params"
116+
},
117+
{
118+
"address": "grpc.server.request.message"
119+
},
120+
{
121+
"address": "graphql.server.all_resolvers"
122+
},
123+
{
124+
"address": "graphql.server.resolver"
125+
}
126+
]
127+
},
128+
"operator": "ssrf_detector"
129+
}
130+
],
131+
"transformers": [],
132+
"on_match": [
133+
"stack_trace"
134+
]
135+
},
88136
{
89137
"id": "blk-001-003",
90138
"name": "Block User Addresses",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
function one() {
4+
$function = $_GET['function'];
5+
$path = 'http://'. $_GET['domain'] .'/somewhere/in/the/app';
6+
7+
switch ($function) {
8+
case 'fopen':
9+
fopen($path, 'r');
10+
break;
11+
default:
12+
$function($path);
13+
break;
14+
}
15+
}
16+
17+
function two() {
18+
one();
19+
}
20+
21+
function three() {
22+
two();
23+
}
24+
25+
three();
26+
27+
28+
echo "OK";

0 commit comments

Comments
 (0)