Skip to content

Commit 6b492fd

Browse files
Introduced misconfigurations detection functionallity for the R&D
1 parent 9f01834 commit 6b492fd

File tree

14 files changed

+711
-10
lines changed

14 files changed

+711
-10
lines changed

dd-java-agent/agent-iast/src/main/java/com/datadog/iast/sink/ApplicationModuleImpl.java

Lines changed: 244 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,64 @@
22

33
import static com.datadog.iast.util.StringUtils.endsWithIgnoreCase;
44
import static com.datadog.iast.util.StringUtils.substringTrim;
5+
import static datadog.trace.api.gateway.Events.EVENTS;
56
import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY;
67

78
import com.datadog.iast.Dependencies;
89
import com.datadog.iast.model.Evidence;
910
import com.datadog.iast.model.Location;
1011
import com.datadog.iast.model.Vulnerability;
1112
import com.datadog.iast.model.VulnerabilityType;
13+
import datadog.trace.api.function.TriFunction;
14+
import datadog.trace.api.gateway.CallbackProvider;
15+
import datadog.trace.api.gateway.Events;
16+
import datadog.trace.api.gateway.Flow;
17+
import datadog.trace.api.gateway.RequestContext;
18+
import datadog.trace.api.gateway.RequestContextSlot;
1219
import datadog.trace.api.iast.sink.ApplicationModule;
1320
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
1421
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
22+
23+
import java.io.ByteArrayInputStream;
1524
import java.io.File;
25+
import java.io.FileInputStream;
1626
import java.io.IOException;
27+
import java.io.InputStream;
28+
import java.io.StringReader;
1729
import java.nio.charset.StandardCharsets;
30+
import java.nio.file.DirectoryStream;
1831
import java.nio.file.FileVisitOption;
1932
import java.nio.file.FileVisitResult;
2033
import java.nio.file.FileVisitor;
2134
import java.nio.file.Files;
2235
import java.nio.file.Path;
2336
import java.nio.file.Paths;
2437
import java.nio.file.attribute.BasicFileAttributes;
38+
import java.util.ArrayDeque;
39+
import java.util.ArrayList;
2540
import java.util.Arrays;
2641
import java.util.Collection;
42+
import java.util.Collections;
43+
import java.util.Deque;
2744
import java.util.EnumSet;
2845
import java.util.HashSet;
46+
import java.util.LinkedHashMap;
47+
import java.util.List;
48+
import java.util.Map;
49+
import java.util.Properties;
2950
import java.util.Set;
51+
import java.util.function.BiFunction;
3052
import java.util.regex.Matcher;
3153
import java.util.regex.Pattern;
3254
import java.util.stream.Collectors;
3355
import java.util.stream.Stream;
3456
import javax.annotation.Nonnull;
3557
import javax.annotation.Nullable;
58+
import javax.xml.stream.XMLInputFactory;
59+
import javax.xml.stream.XMLStreamConstants;
60+
import javax.xml.stream.XMLStreamException;
61+
import javax.xml.stream.XMLStreamReader;
62+
3663
import org.slf4j.Logger;
3764
import org.slf4j.LoggerFactory;
3865

@@ -85,6 +112,9 @@ public class ApplicationModuleImpl extends SinkModuleBase implements Application
85112
JETTY_TEST_APP));
86113
public static final String WEB_INF = "WEB-INF";
87114
public static final String WEB_XML = "web.xml";
115+
116+
public static final String TOMCAT_USERS_XML = "tomcat-users.xml";
117+
public static final String TOMCAT_SERVER_XML = "server.xml";
88118
public static final String WEBLOGIC_XML = "weblogic.xml";
89119
public static final String IBM_WEB_EXT_XMI = "ibm-web-ext.xmi";
90120
public static final String IBM_WEB_EXT_XML = "ibm-web-ext.xml";
@@ -124,7 +154,7 @@ public ApplicationModuleImpl(final Dependencies dependencies) {
124154
* @param realPath the real path of the application
125155
*/
126156
@Override
127-
public void onRealPath(final @Nullable String realPath) {
157+
public void onRealPath(String serverInfo, final @Nullable String realPath) {
128158
if (realPath == null) {
129159
return;
130160
}
@@ -133,8 +163,15 @@ public void onRealPath(final @Nullable String realPath) {
133163
return;
134164
}
135165
final AgentSpan span = AgentTracer.activeSpan();
166+
167+
if (serverInfo != null) {
168+
if (serverInfo.contains("Tomcat")) {
169+
checkTomcatVulnerabilities(serverInfo, root, span);
170+
}
171+
}
172+
136173
checkInsecureJSPLayout(root, span);
137-
checkWebXmlVulnerabilities(root, span);
174+
checkWebXmlVulnerabilities(serverInfo, root, span);
138175
// WEBLOGIC
139176
checkWeblogicVulnerabilities(root, span);
140177
// WEBSPHERE
@@ -161,6 +198,87 @@ public void checkSessionTrackingModes(@Nonnull Set<String> sessionTrackingModes)
161198
new Evidence(SESSION_REWRITING_EVIDENCE_VALUE)));
162199
}
163200

201+
private void checkTomcatVulnerabilities(String serverInfo, @Nonnull final Path path, final AgentSpan span) {
202+
String tomcatPath = System.getProperty("catalina.home");
203+
204+
RequestContext reqCtx = span.getRequestContext();
205+
CallbackProvider cbp = AgentTracer.get().getCallbackProvider(RequestContextSlot.APPSEC);
206+
TriFunction<RequestContext, String, Map<String, ?>, Flow<Void>> callback =
207+
cbp.getCallback(EVENTS.configurationData());
208+
if (reqCtx == null || callback == null) {
209+
return;
210+
}
211+
212+
try {
213+
214+
// Check for default tomcat applications (Manager, Host Manager, etc)
215+
Path tomcatWebappsPath = Paths.get(tomcatPath, "webapps");
216+
217+
Map<String, Object> webapps = new LinkedHashMap<>();
218+
List<String> appNames = new ArrayList<>();
219+
webapps.put("webapps", appNames);
220+
try (DirectoryStream<Path> stream = Files.newDirectoryStream(tomcatWebappsPath, Files::isDirectory)) {
221+
for (Path p : stream) {
222+
//System.out.println("Folder: " + p.getFileName());
223+
appNames.add(p.getFileName().toString());
224+
}
225+
226+
Flow<Void> flow = callback.apply(reqCtx, serverInfo, webapps);
227+
} catch (IOException e) {
228+
System.err.println("Error reading directory: " + e.getMessage());
229+
}
230+
231+
232+
// Check for Weak credentials in tomcat-users.xml
233+
Path tomcatUsersXmlPath = Paths.get(tomcatPath, "conf", TOMCAT_USERS_XML);
234+
Map<String, List<Map<String, Object>>> tomcatUsersXmlConfig = getParsedXmlContent(tomcatUsersXmlPath);
235+
if (tomcatUsersXmlConfig != null) {
236+
List<Map<String, Object>> userTags = tomcatUsersXmlConfig.get("user");
237+
if (userTags != null) {
238+
for (Map<String, Object> userTag : userTags) {
239+
Flow<Void> flow = callback.apply(reqCtx, serverInfo, userTag);
240+
}
241+
}
242+
}
243+
244+
245+
// Check server.xml
246+
Path serverXmlPath = Paths.get(tomcatPath, "conf", TOMCAT_SERVER_XML);
247+
Map<String, List<Map<String, Object>>> serverXmlConfig = getParsedXmlContent(serverXmlPath);
248+
249+
if (serverXmlConfig != null) {
250+
List<Map<String, Object>> valveTags = serverXmlConfig.get("Valve");
251+
if (valveTags != null) {
252+
for (Map<String, Object> valveTag : valveTags) {
253+
Flow<Void> flow = callback.apply(reqCtx, serverInfo, valveTag);
254+
}
255+
}
256+
List<Map<String, Object>> connectorTags = serverXmlConfig.get("Connector");
257+
if (connectorTags != null) {
258+
for (Map<String, Object> connectorTag : connectorTags) {
259+
Map<String, Object> serviceTag = new LinkedHashMap<>();
260+
serviceTag.put("Connector", connectorTag);
261+
Flow<Void> flow = callback.apply(reqCtx, serverInfo, serviceTag);
262+
}
263+
}
264+
}
265+
266+
// Check jvm properties
267+
Properties properties = System.getProperties();
268+
Map<String, Object> jvmProperties = new LinkedHashMap<>();
269+
Map<String, Object> propertiesTag = new LinkedHashMap<>();
270+
for (String key : properties.stringPropertyNames()) {
271+
propertiesTag.put(key, properties.getProperty(key));
272+
}
273+
jvmProperties.put("Properties", propertiesTag);
274+
Flow<Void> flow = callback.apply(reqCtx, serverInfo, jvmProperties);
275+
276+
} catch (Exception e) {
277+
throw new RuntimeException(e);
278+
}
279+
280+
}
281+
164282
private void checkWebsphereVulnerabilities(@Nonnull final Path path, final AgentSpan span) {
165283
checkWebsphereXMLVulnerabilities(path, span);
166284
checkWebsphereXMIVulnerabilities(path, span);
@@ -199,7 +317,109 @@ private void checkWeblogicVulnerabilities(@Nonnull final Path path, final AgentS
199317
}
200318
}
201319

202-
private void checkWebXmlVulnerabilities(@Nonnull final Path path, final AgentSpan span) {
320+
public static Map<String, List<Map<String, Object>>> parseXmlFromString(String xmlInput) throws XMLStreamException {
321+
XMLInputFactory factory = XMLInputFactory.newInstance();
322+
try (InputStream xmlStream = new ByteArrayInputStream(xmlInput.getBytes())) {
323+
XMLStreamReader reader = factory.createXMLStreamReader(xmlStream);
324+
return parseGroupedElements(reader);
325+
} catch (IOException e) {
326+
throw new RuntimeException("Error reading XML input", e);
327+
}
328+
}
329+
330+
private static Map<String, List<Map<String, Object>>> parseGroupedElements(XMLStreamReader reader) throws XMLStreamException {
331+
Map<String, List<Map<String, Object>>> groupedMap = new LinkedHashMap<>();
332+
333+
while (reader.hasNext()) {
334+
int eventType = reader.next();
335+
336+
switch (eventType) {
337+
case XMLStreamConstants.START_ELEMENT:
338+
String currentElement = reader.getLocalName();
339+
Map<String, String> attributes = getAttributes(reader);
340+
341+
// Создаем элемент
342+
Map<String, Object> element = new LinkedHashMap<>(attributes);
343+
344+
// Добавляем элемент в общую структуру
345+
groupedMap.computeIfAbsent(currentElement, k -> new ArrayList<>()).add(element);
346+
break;
347+
348+
case XMLStreamConstants.END_ELEMENT:
349+
// Просто выходим из цикла на конец элемента
350+
break;
351+
352+
case XMLStreamConstants.CHARACTERS:
353+
// Игнорируем текст, так как у нас только атрибуты нужны
354+
break;
355+
}
356+
}
357+
return groupedMap;
358+
}
359+
360+
private static Map<String, String> getAttributes(XMLStreamReader reader) {
361+
Map<String, String> attributes = new LinkedHashMap<>();
362+
for (int i = 0; i < reader.getAttributeCount(); i++) {
363+
attributes.put(reader.getAttributeLocalName(i), reader.getAttributeValue(i));
364+
}
365+
return attributes;
366+
}
367+
368+
369+
// public static Map<String, Object> parseXmlFromString(String xmlContent) throws Exception {
370+
// Map<String, Object> result = new LinkedHashMap<>();
371+
// Deque<Map<String, Object>> stack = new ArrayDeque<>();
372+
// stack.push(result);
373+
//
374+
// XMLInputFactory factory = XMLInputFactory.newInstance();
375+
// XMLStreamReader reader = factory.createXMLStreamReader(new StringReader(xmlContent));
376+
//
377+
// String currentElement = null;
378+
// while (reader.hasNext()) {
379+
// int event = reader.next();
380+
// switch (event) {
381+
// case XMLStreamConstants.START_ELEMENT:
382+
// currentElement = reader.getLocalName();
383+
// Map<String, Object> newElement = new LinkedHashMap<>();
384+
// stack.peek().merge(currentElement, newElement, (oldValue, newValue) -> {
385+
// if (oldValue instanceof List) {
386+
// ((List<Object>) oldValue).add(newValue);
387+
// return oldValue;
388+
// } else {
389+
// List<Object> list = new ArrayList<>();
390+
// list.add(oldValue);
391+
// list.add(newValue);
392+
// return list;
393+
// }
394+
// });
395+
// stack.push(newElement);
396+
// break;
397+
// case XMLStreamConstants.CHARACTERS:
398+
// if (!reader.isWhiteSpace()) {
399+
// stack.peek().merge(currentElement, reader.getText().trim(), (oldValue, newValue) -> {
400+
// if (oldValue instanceof List) {
401+
// ((List<Object>) oldValue).add(newValue);
402+
// return oldValue;
403+
// } else {
404+
// List<Object> list = new ArrayList<>();
405+
// list.add(oldValue);
406+
// list.add(newValue);
407+
// return list;
408+
// }
409+
// });
410+
// }
411+
// break;
412+
// case XMLStreamConstants.END_ELEMENT:
413+
// stack.pop();
414+
// break;
415+
// }
416+
// }
417+
// return result;
418+
// }
419+
420+
421+
private void checkWebXmlVulnerabilities(final String serverInfo, final Path path, final AgentSpan span) {
422+
203423
String webXmlContent = getXmlContent(path, WEB_XML);
204424
if (webXmlContent == null) {
205425
return;
@@ -377,8 +597,7 @@ private static int getLine(String webXmlContent, int index) {
377597
}
378598

379599
@Nullable
380-
private static String getXmlContent(final Path realPath, final String fileName) {
381-
Path path = realPath.resolve(WEB_INF).resolve(fileName);
600+
private static String readXmlContent(final Path path) {
382601
if (Files.exists(path)) {
383602
try {
384603
return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
@@ -389,6 +608,26 @@ private static String getXmlContent(final Path realPath, final String fileName)
389608
return null;
390609
}
391610

611+
@Nullable
612+
private static Map<String, List<Map<String, Object>>> getParsedXmlContent(final Path path) {
613+
String xmlContent = readXmlContent(path);
614+
if (xmlContent == null) {
615+
return null;
616+
}
617+
try {
618+
return parseXmlFromString(xmlContent);
619+
} catch (Exception e) {
620+
LOGGER.debug(SEND_TELEMETRY, "Failed to parse {}", path, e);
621+
}
622+
return null;
623+
}
624+
625+
@Nullable
626+
private static String getXmlContent(final Path realPath, final String fileName) {
627+
Path path = realPath.resolve(WEB_INF).resolve(fileName);
628+
return readXmlContent(path);
629+
}
630+
392631
private static Collection<Path> findInsecureJspPaths(final Path root) {
393632
try {
394633
final InsecureJspFolderVisitor visitor = new InsecureJspFolderVisitor();

dd-java-agent/appsec/src/main/java/com/datadog/appsec/event/data/KnownAddresses.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ public interface KnownAddresses {
131131
/** Login success business event */
132132
Address<String> LOGIN_SUCCESS = new Address<>("server.business_logic.users.login.success");
133133

134+
Address<String> SERVER_APP_LANGUAGE = new Address<>("server.app.language");
135+
Address<String> SERVER_APP_FRAMEWORK = new Address<>("server.app.framework");
136+
Address<Map<String, ?>> SERVER_APP_CONFIG = new Address<>("server.app.config");
137+
134138
Address<Map<String, Object>> WAF_CONTEXT_PROCESSOR = new Address<>("waf.context.processor");
135139

136140
static Address<?> forName(String name) {
@@ -205,6 +209,12 @@ static Address<?> forName(String name) {
205209
return LOGIN_SUCCESS;
206210
case "server.business_logic.users.login.failure":
207211
return LOGIN_FAILURE;
212+
case "server.app.language":
213+
return SERVER_APP_LANGUAGE;
214+
case "server.app.framework":
215+
return SERVER_APP_FRAMEWORK;
216+
case "server.app.config":
217+
return SERVER_APP_CONFIG;
208218
default:
209219
return null;
210220
}

0 commit comments

Comments
 (0)