Skip to content

Commit

Permalink
Support OS fingerprinting
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 655093248
Change-Id: I89e1a894dac6eeaeb868fa14462310ed0fc63ee7
  • Loading branch information
Tsunami Team authored and copybara-github committed Jul 23, 2024
1 parent 6c69193 commit b4b2089
Show file tree
Hide file tree
Showing 6 changed files with 535 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
import com.google.tsunami.plugin.annotations.ForOperatingSystemClass;
import com.google.tsunami.plugin.annotations.ForServiceName;
import com.google.tsunami.plugin.annotations.ForSoftware;
import com.google.tsunami.plugin.annotations.ForWebService;
Expand All @@ -37,6 +38,8 @@ abstract class PluginDefinition {

abstract boolean isForWebService();

abstract Optional<ForOperatingSystemClass> targetOperatingSystemClass();

/**
* Unique identifier for the plugin.
*
Expand Down Expand Up @@ -77,14 +80,20 @@ public static PluginDefinition forPlugin(Class<? extends TsunamiPlugin> pluginCl
Optional<ForSoftware> targetSoftware =
Optional.ofNullable(pluginClazz.getAnnotation(ForSoftware.class));
boolean isForWebService = pluginClazz.isAnnotationPresent(ForWebService.class);
Optional<ForOperatingSystemClass> targetOperatingSystemClass =
Optional.ofNullable(pluginClazz.getAnnotation(ForOperatingSystemClass.class));

checkState(
pluginInfo.isPresent(),
"A @PluginInfo annotation is required when creating a PluginDefinition for plugin: %s",
pluginClazz);

return new AutoValue_PluginDefinition(
pluginInfo.get(), targetServiceName, targetSoftware, isForWebService);
pluginInfo.get(),
targetServiceName,
targetSoftware,
isForWebService,
targetOperatingSystemClass);
}

/**
Expand All @@ -96,6 +105,10 @@ public static PluginDefinition forPlugin(Class<? extends TsunamiPlugin> pluginCl
*/
public static PluginDefinition forRemotePlugin(PluginInfo remotePluginInfo) {
return new AutoValue_PluginDefinition(
checkNotNull(remotePluginInfo), Optional.empty(), Optional.empty(), false);
checkNotNull(remotePluginInfo),
Optional.empty(),
Optional.empty(),
false,
Optional.empty());
}
}
81 changes: 75 additions & 6 deletions plugin/src/main/java/com/google/tsunami/plugin/PluginManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.google.tsunami.plugin.annotations.ForOperatingSystemClass;
import com.google.tsunami.proto.MatchedPlugin;
import com.google.tsunami.proto.NetworkService;
import com.google.tsunami.proto.ReconnaissanceReport;
import com.google.tsunami.proto.TargetInfo;
import com.google.tsunami.proto.TargetOperatingSystemClass;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -122,15 +125,25 @@ private static Optional<PluginMatchingResult<VulnDetector>> matchVulnDetectors(
Provider<TsunamiPlugin> vulnDetectorProvider,
ReconnaissanceReport reconnaissanceReport) {
List<NetworkService> matchedNetworkServices;
var allNetworkServices = reconnaissanceReport.getNetworkServicesList();
if (pluginDefinition.targetOperatingSystemClass().isPresent()) {
allNetworkServices =
allNetworkServices.stream()
.filter(
networkService ->
hasMatchingOperatingSystem(
reconnaissanceReport.getTargetInfo(), pluginDefinition))
.collect(toImmutableList());
}
if (!pluginDefinition.targetServiceName().isPresent()
&& !pluginDefinition.targetSoftware().isPresent()
&& !pluginDefinition.isForWebService()) {
// No filtering annotation applied, just match all network services from reconnaissance.
matchedNetworkServices = reconnaissanceReport.getNetworkServicesList();
matchedNetworkServices = allNetworkServices;
} else {
// At least one filtering annotation applied, check services to see if any one matches.
matchedNetworkServices =
reconnaissanceReport.getNetworkServicesList().stream()
allNetworkServices.stream()
.filter(
networkService ->
hasMatchingServiceName(networkService, pluginDefinition)
Expand Down Expand Up @@ -162,17 +175,27 @@ private static Optional<PluginMatchingResult<VulnDetector>> matchRemoteVulnDetec
for (com.google.tsunami.proto.PluginDefinition remotePluginDefinition :
remoteVulnDetector.getAllPlugins()) {
var matchedPluginBuilder = MatchedPlugin.newBuilder();
var allNetworkServices = reconnaissanceReport.getNetworkServicesList();
if (remotePluginDefinition.hasTargetOperatingSystemClass()) {
// Prefiltering based on the Operating System, so the other potential filters are applied
// like if we were in an AND condition.
allNetworkServices =
allNetworkServices.stream()
.filter(
networkService ->
hasMatchingOperatingSystem(
reconnaissanceReport.getTargetInfo(), remotePluginDefinition))
.collect(toImmutableList());
}
if (!remotePluginDefinition.hasTargetServiceName()
&& !remotePluginDefinition.hasTargetSoftware()
&& !remotePluginDefinition.getForWebService()) {
matchedPluginBuilder
.setPlugin(remotePluginDefinition)
.addAllServices(reconnaissanceReport.getNetworkServicesList());
matchedPluginBuilder.setPlugin(remotePluginDefinition).addAllServices(allNetworkServices);
} else {
matchedPluginBuilder
.setPlugin(remotePluginDefinition)
.addAllServices(
reconnaissanceReport.getNetworkServicesList().stream()
allNetworkServices.stream()
.filter(
networkService ->
hasMatchingServiceName(networkService, remotePluginDefinition)
Expand All @@ -184,6 +207,40 @@ private static Optional<PluginMatchingResult<VulnDetector>> matchRemoteVulnDetec
return Optional.of(builder.build());
}

private static boolean hasMatchingOperatingSystem(
TargetInfo targetInfo, PluginDefinition pluginDefinition) {
return hasMatchingOperatingSystem(
targetInfo,
getTargetOperatingSystemClass(pluginDefinition.targetOperatingSystemClass().get()));
}

private static boolean hasMatchingOperatingSystem(
TargetInfo targetInfo, com.google.tsunami.proto.PluginDefinition pluginDefinition) {
return hasMatchingOperatingSystem(targetInfo, pluginDefinition.getTargetOperatingSystemClass());
}

// Determines if the target info has a matching operating system to the plugin definition.
// If the target info has no info about the operating system, it will return false.
private static boolean hasMatchingOperatingSystem(
TargetInfo targetInfo, TargetOperatingSystemClass pluginOs) {
for (var osGuess : targetInfo.getOperatingSystemClassesList()) {
var osGuessAccuracy = osGuess.getAccuracy();
var minAccuracyWanted = pluginOs.getMinAccuracy();
if (minAccuracyWanted != 0 && minAccuracyWanted > osGuessAccuracy) {
continue;
}
if (pluginOs.getVendorList().contains(osGuess.getVendor())) {
return true;
}
if (pluginOs.getOsFamilyList().contains(osGuess.getOsFamily())) {
return true;
}
}

// None of the OS guesses matched.
return false;
}

private static boolean hasMatchingServiceName(
NetworkService networkService, PluginDefinition pluginDefinition) {
String serviceName = networkService.getServiceName();
Expand Down Expand Up @@ -268,4 +325,16 @@ public Builder<T> addAllMatchedServices(Iterable<NetworkService> networkServices
public abstract PluginMatchingResult<T> build();
}
}

private static TargetOperatingSystemClass getTargetOperatingSystemClass(
ForOperatingSystemClass target) {
var builder = TargetOperatingSystemClass.newBuilder().setMinAccuracy(target.minAccuracy());
for (String vendor : target.vendor()) {
builder.addVendor(vendor);
}
for (String osfamily : target.osfamily()) {
builder.addOsFamily(osfamily);
}
return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2020 Google LLC
*
* 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 com.google.tsunami.plugin.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* An annotation for marking the target operating system for a Tsunami {@link
* com.google.tsunami.plugin.VulnDetector} plugin.
*
* <p>If annotated by this annotation, the {@link com.google.tsunami.plugin.VulnDetector} will only
* be executed by the scanner when the target network service is running on the described Operating
* System.
*
* <p>Example usage:
*
* <pre>{@code
* {@literal @}ForOperatingSystemClass({
* vendor = "Linux",
* minAccuracy = 90
* })
* public class ExamplePlugin implements VulnDetector {
* // ...
* }
* }</pre>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ForOperatingSystemClass {

// The vendor of the target operating system, e.g. "Microsoft"
String[] vendor() default "";

// The family of the target operating system, e.g. "Windows"
String[] osfamily() default "";

// The minimum accuracy of the target operating system, e.g. 90
int minAccuracy() default 0;
}
Loading

0 comments on commit b4b2089

Please sign in to comment.