Skip to content

Commit

Permalink
Break up callback uri for linux payload by adding a empty val.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 564763573
Change-Id: I74929a10555589ad2611ff7587d69697e525597d
  • Loading branch information
maoning authored and copybara-github committed Sep 12, 2023
1 parent 231018c commit 93dee00
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
import com.google.tsunami.plugin.TcsClient;
Expand All @@ -31,8 +32,11 @@

/** Holds the generate function to get a detection payload given config parameters */
public final class PayloadGenerator {
@VisibleForTesting static final String UNDEF_VAL = "${TCS_UNDEF}";
private static final int SECRET_LENGTH = 8;
private static final String TOKEN_CALLBACK_SERVER_URL = "$TSUNAMI_PAYLOAD_TOKEN_URL";
private static final String TOKEN_CALLBACK_SERVER_URL_LINUX_RCE =
"$TSUNAMI_PAYLOAD_TOKEN_URL_LINUX_RCE";
private static final String TOKEN_RANDOM_STRING = "$TSUNAMI_PAYLOAD_TOKEN_RANDOM";

private final TcsClient tcsClient;
Expand Down Expand Up @@ -112,34 +116,42 @@ private boolean isMatchingPayload(PayloadDefinition p, PayloadGeneratorConfig c)
private Payload convertParsedPayload(PayloadDefinition p, PayloadGeneratorConfig c) {
String secret = secretGenerator.generate(SECRET_LENGTH);
if (p.getUsesCallbackServer().getValue()) {
String callbackUri = tcsClient.getCallbackUri(secret);
return new Payload(
p.getPayloadString()
.getValue()
.replace(TOKEN_CALLBACK_SERVER_URL, tcsClient.getCallbackUri(secret)),
.replace(
TOKEN_CALLBACK_SERVER_URL_LINUX_RCE, generateLinuxRceCallbackUri(callbackUri))
.replace(TOKEN_CALLBACK_SERVER_URL, callbackUri),
(Validator) (unused) -> tcsClient.hasOobLog(secret),
PayloadAttributes.newBuilder().setUsesCallbackServer(true).build(),
c);
} else {
String payloadString = p.getPayloadString().getValue().replace(TOKEN_RANDOM_STRING, secret);
Validator v;
switch (p.getValidationType()) {
case VALIDATION_REGEX:
String processedRegex =
p.getValidationRegex().getValue().replace(TOKEN_RANDOM_STRING, secret);
v =
(Validator)
(Optional<ByteString> input) ->
input.map(i -> i.toStringUtf8().matches(processedRegex)).orElse(false);
return new Payload(
payloadString,
v,
PayloadAttributes.newBuilder().setUsesCallbackServer(false).build(),
c);
default:
throw new NotImplementedException(
"Validation type %s not implemented.", p.getValidationType());
}
}
String payloadString = p.getPayloadString().getValue().replace(TOKEN_RANDOM_STRING, secret);
Validator v;
switch (p.getValidationType()) {
case VALIDATION_REGEX:
String processedRegex =
p.getValidationRegex().getValue().replace(TOKEN_RANDOM_STRING, secret);
v =
(Validator)
(Optional<ByteString> input) ->
input.map(i -> i.toStringUtf8().matches(processedRegex)).orElse(false);
return new Payload(
payloadString,
v,
PayloadAttributes.newBuilder().setUsesCallbackServer(false).build(),
c);
default:
throw new NotImplementedException(
"Validation type %s not implemented.", p.getValidationType());
}
}

private static String generateLinuxRceCallbackUri(String callbackUri) {
return callbackUri.substring(0, callbackUri.length() / 2)
+ UNDEF_VAL
+ callbackUri.substring(callbackUri.length() / 2);
}

/** Guice interface for injecting parsed payloads from payload_definitions.yaml */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ payloads:
interpretation_environment: LINUX_SHELL
execution_environment: EXEC_INTERPRETATION_ENVIRONMENT
uses_callback_server: true
payload_string: curl $TSUNAMI_PAYLOAD_TOKEN_URL
payload_string: curl $TSUNAMI_PAYLOAD_TOKEN_URL_LINUX_RCE
vulnerability_type:
- REFLECTIVE_RCE
- BLIND_RCE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.google.tsunami.plugin.payload;

import static com.google.common.truth.Truth.assertThat;
import static com.google.tsunami.plugin.payload.PayloadGenerator.UNDEF_VAL;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
Expand Down Expand Up @@ -90,6 +91,7 @@ public void generate_withLinuxConfiguration_returnsCurlPayload() {
Payload payload = payloadGenerator.generate(LINUX_REFLECTIVE_RCE_CONFIG);

assertThat(payload.getPayload()).contains("curl");
assertThat(payload.getPayload()).contains(UNDEF_VAL);
assertThat(payload.getPayload()).contains(mockCallbackServer.getHostName());
assertThat(payload.getPayload()).contains(Integer.toString(mockCallbackServer.getPort(), 10));
assertTrue(payload.getPayloadAttributes().getUsesCallbackServer());
Expand All @@ -100,6 +102,7 @@ public void generate_withLinuxConfiguration_returnsPrintfPayload() {
Payload payload = payloadGenerator.generateNoCallback(LINUX_REFLECTIVE_RCE_CONFIG);

assertThat(payload.getPayload()).isEqualTo(CORRECT_PRINTF);
assertThat(payload.getPayload()).doesNotContain(UNDEF_VAL);
assertFalse(payload.getPayloadAttributes().getUsesCallbackServer());
}

Expand Down
68 changes: 41 additions & 27 deletions plugin_server/py/plugin/payload/payload_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ class PayloadGenerator:

SECRET_LENGTH = 8
TOKEN_CALLBACK_SERVER_URL = '$TSUNAMI_PAYLOAD_TOKEN_URL'
TOKEN_CALLBACK_SERVER_URL_LINUX_RCE = '$TSUNAMI_PAYLOAD_TOKEN_URL_LINUX_RCE'
TOKEN_RANDOM_STRING = '$TSUNAMI_PAYLOAD_TOKEN_RANDOM'
UNDEF_VAL = '${TCS_UNDEF}'

def __init__(
self,
Expand Down Expand Up @@ -110,9 +112,13 @@ def _parse_payload(
"""Create payload from the selected payload definition."""
secret = self.payload_secret_generator.generate(self.SECRET_LENGTH)
if bool(payload.uses_callback_server.ByteSize()):
callback_uri = self.tcs_client.get_callback_uri(secret)
payload_string = payload.payload_string.value.replace(
self.TOKEN_CALLBACK_SERVER_URL_LINUX_RCE,
_generate_linux_rce_callback_uri(callback_uri),
).replace(
self.TOKEN_CALLBACK_SERVER_URL,
self.tcs_client.get_callback_uri(secret),
callback_uri,
)
validator = type(
'PayloadValidator',
Expand All @@ -125,32 +131,32 @@ def _parse_payload(
pg.PayloadAttributes(uses_callback_server=True),
config,
)
else:
payload_string = payload.payload_string.value.replace(
self.TOKEN_RANDOM_STRING, secret
)
if payload.validation_type != pg.PayloadValidationType.Value(
'VALIDATION_REGEX'
):
raise NotImplementedError(
'Validation type %s not supported.'
% pg.PayloadGeneratorConfig.VulnerabilityType.Name(
config.vulnerability_type)
)
regex = payload.validation_regex.value.replace(
self.TOKEN_RANDOM_STRING, secret
)
validator = type(
'PayloadValidator',
(Validator,),
{'is_executed': _is_executed(regex)},
)()
return Payload(
payload_string,
validator,
pg.PayloadAttributes(uses_callback_server=False),
config,
)

payload_string = payload.payload_string.value.replace(
self.TOKEN_RANDOM_STRING, secret
)
if payload.validation_type != pg.PayloadValidationType.Value(
'VALIDATION_REGEX'
):
raise NotImplementedError(
'Validation type %s not supported.'
% pg.PayloadGeneratorConfig.VulnerabilityType.Name(
config.vulnerability_type)
)
regex = payload.validation_regex.value.replace(
self.TOKEN_RANDOM_STRING, secret
)
validator = type(
'PayloadValidator',
(Validator,),
{'is_executed': _is_executed(regex)},
)()
return Payload(
payload_string,
validator,
pg.PayloadAttributes(uses_callback_server=False),
config,
)

def _payload_matches_config(
self,
Expand All @@ -177,3 +183,11 @@ def check_payload_execution(_, data: Optional[bytes]) -> bool:
return bool(re.compile(regex).search(string)) or False

return check_payload_execution


def _generate_linux_rce_callback_uri(callback_uri: str) -> str:
return (
callback_uri[0 : len(callback_uri) // 2]
+ PayloadGenerator.UNDEF_VAL
+ callback_uri[len(callback_uri) // 2 :]
)
14 changes: 12 additions & 2 deletions plugin_server/py/plugin/payload/payload_generator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ def test_is_callback_server_enabled_returns_true(self):
self.assertTrue(self.payload_generator.is_callback_server_enabled())

@parameterized.named_parameters(
('linux_config', LINUX_REFLECTIVE_RCE_CONFIG, 'curl'),
(
'linux_config',
LINUX_REFLECTIVE_RCE_CONFIG,
'curl',
),
(
'ssrf_config',
ANY_SSRF_CONFIG,
Expand All @@ -55,10 +59,16 @@ def test_generate_with_callback_returns_payload(
):
payload = self.payload_generator.generate(config)
self.assertIn(expected_payload, payload.payload)
self.assertIn(_IP_ADDRESS, payload.payload)
self.assertIn(
_IP_ADDRESS, payload.payload.replace(PayloadGenerator.UNDEF_VAL, '')
)
self.assertIn(str(_PORT), payload.payload)
self.assertTrue(payload.get_payload_attributes().uses_callback_server)

def test_generate_with_callback_returns_undef_val_placeholder(self):
payload = self.payload_generator.generate(LINUX_REFLECTIVE_RCE_CONFIG)
self.assertIn(PayloadGenerator.UNDEF_VAL, payload.payload)

@parameterized.named_parameters(
(
'linux_config',
Expand Down

0 comments on commit 93dee00

Please sign in to comment.