Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import dev.aikido.agent_api.vulnerabilities.ssrf.SSRFException;
import dev.aikido.agent_api.helpers.logging.LogManager;
import dev.aikido.agent_api.helpers.logging.Logger;
import dev.aikido.agent_api.vulnerabilities.ssrf.IsPrivateIP;
import dev.aikido.agent_api.vulnerabilities.ssrf.StoredSSRFDetector;
import dev.aikido.agent_api.vulnerabilities.ssrf.StoredSSRFException;

Expand Down Expand Up @@ -41,12 +42,12 @@ public static void report(String hostname, InetAddress[] inetAddresses) {
for (int port : ports) {
HostnamesStore.incrementHits(hostname, port);
}
} else {
// We still need to report a hit to the hostname for outbound domain blocking
} else if (!IsPrivateIP.isPrivateIp(hostname)) {
Comment thread
hansott marked this conversation as resolved.
// Literal IPs reach this sink without a real DNS call, so skip private ones as noise.
HostnamesStore.incrementHits(hostname, 0);
}

// Block if the hostname is in the blocked domains list
// Checked here, not at the HTTP client, since not all clients are instrumented and port/context isn't always available.
if (ServiceConfigStore.shouldBlockOutgoingRequest(hostname)) {
logger.debug("Blocking DNS lookup for domain: %s", hostname);
throw BlockedOutboundException.get();
Expand Down
56 changes: 56 additions & 0 deletions agent_api/src/test/java/collectors/DNSRecordCollectorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,62 @@ public void testSSRFStillRunsWhenPendingPortIsZero() {
assertEquals("Aikido Zen has blocked a server-side request forgery", exception.getMessage());
}

@Test
public void testPrivateIpLiteralWithNoPendingPortNotRecorded() {
Context.set(null);
DNSRecordCollector.report("10.20.11.143", new InetAddress[]{inetAddress2});
assertEquals(0, HostnamesStore.getHostnamesAsList().length);
}

@Test
public void testPrivateIpLiteralWithNoPendingPortNotRecordedButBlockedInLockdown() {
ServiceConfigStore.updateFromAPIResponse(new APIResponse(
true, null, 0L, null, null, null, true, List.of(), true, true, List.of()
));
Context.set(null);
assertThrows(BlockedOutboundException.class, () ->
DNSRecordCollector.report("10.20.11.143", new InetAddress[]{inetAddress2})
);
assertEquals(0, HostnamesStore.getHostnamesAsList().length);
}

@Test
public void testPrivateIpLiteralWithPendingPortStillRecordedAndBlockedInLockdown() {
ServiceConfigStore.updateFromAPIResponse(new APIResponse(
true, null, 0L, null, null, null, true, List.of(), true, true, List.of()
));
PendingHostnamesStore.add("10.20.11.143", 443);
Context.set(mock(ContextObject.class));

assertThrows(BlockedOutboundException.class, () ->
DNSRecordCollector.report("10.20.11.143", new InetAddress[]{inetAddress2})
);
Hostnames.HostnameEntry[] entries = HostnamesStore.getHostnamesAsList();
assertEquals(1, entries.length);
assertEquals(443, entries[0].getPort());
}

@Test
public void testSsrfStillDetectedForPrivateIpLiteralWithPendingPort() {
ServiceConfigStore.updateBlocking(true);
PendingHostnamesStore.add("169.254.169.254", 80);
Context.set(new EmptySampleContextObject("http://169.254.169.254:80/latest/meta-data/"));

Exception exception = assertThrows(SSRFException.class, () ->
DNSRecordCollector.report("169.254.169.254", new InetAddress[]{imdsAddress1})
);
assertEquals("Aikido Zen has blocked a server-side request forgery", exception.getMessage());
}

@Test
public void testNamedHostnameResolvingToPrivateIpWithNoPendingPortStillRecorded() {
Context.set(null);
DNSRecordCollector.report("internal-service.local", new InetAddress[]{inetAddress2});
Hostnames.HostnameEntry[] entries = HostnamesStore.getHostnamesAsList();
assertEquals(1, entries.length);
assertEquals("internal-service.local", entries[0].getHostname());
}

@Test
public void testStoredSSRFWithNoContext() throws InterruptedException {
ServiceConfigStore.updateBlocking(true);
Expand Down
Loading