Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Challenge - Vault Template Injection #1189

Merged
merged 11 commits into from
Jan 17, 2024
Merged
2 changes: 1 addition & 1 deletion .github/scripts/.bash_history
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ rm -rf jdk-18_linux-x64_bin.deb
git rebase -i main
git rebase -i master
git stash
export tempPassword="1f94QXGi8zGUNiT91bconrnPLl44bCY59Y8itGyN6Yg="
export tempPassword="dWTZkr5BPvnJYw+8sXtwQX8bCVTtsCrAL//mgrzeYTY="
mvn run tempPassword
k6
npx k6
Expand Down
6 changes: 6 additions & 0 deletions k8s-vault-minkube-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ kubectl exec vault-0 -n vault -- vault secrets enable -path=secret kv-v2
echo "Putting a secret in"
kubectl exec vault-0 -n vault -- vault kv put secret/secret-challenge vaultpassword.password="$(openssl rand -base64 16)"

echo "Putting a challenge key in"
kubectl exec vault-0 -n vault -- vault kv put secret/injected vaultinjected.value="$(openssl rand -base64 16)"

echo "Putting a subkey issue in"
kubectl exec vault-0 -n vault -- vault kv put secret/wrongsecret aaaauser."$(openssl rand -base64 8)"="$(openssl rand -base64 16)"

Expand Down Expand Up @@ -115,6 +118,9 @@ path "secret/data/wrongsecret" {
path "secret/data/application" {
capabilities = ["read"]
}
path "secret/data/injected" {
capabilities = ["read"]
}
EOF'

kubectl exec vault-0 -n vault -- /bin/sh -c 'vault policy write standard_sre - <<EOF
Expand Down
3 changes: 3 additions & 0 deletions k8s/helm-vault-values.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ server:
affinity:
ha:
enabled: true

injector:
enabled: true
18 changes: 17 additions & 1 deletion k8s/secret-challenge-vault-deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ spec:
type: RollingUpdate
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/tls-skip-verify: 'true'
vault.hashicorp.com/agent-inject-status: "update"
vault.hashicorp.com/namespace: "default"
vault.hashicorp.com/log-level: debug
vault.hashicorp.com/agent-inject-secret-challenge46: "secret/data/injected"
vault.hashicorp.com/agent-inject-template-challenge46: |
{{ with secret "/secret/data/injected" }}
{{ range $k, $v := .Data.data }}
{{ printf "%s=%s" $k $v }}
{{ end }}
{{ end }}
vault.hashicorp.com/role: "secret-challenge"
labels:
app: secret-challenge
name: secret-challenge
Expand All @@ -28,9 +42,11 @@ spec:
fsGroup: 2000
runAsGroup: 2000
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
serviceAccountName: vault
containers:
- image: jeroenwillemsen/wrongsecrets:1.8.2-k8s-vault
- image: jeroenwillemsen/wrongsecrets:vaultinjection-2-test-k8s-vault
imagePullPolicy: IfNotPresent
name: secret-challenge
securityContext:
Expand Down
6 changes: 6 additions & 0 deletions scripts/install-vault.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ kubectl exec vault-0 -n vault -- vault secrets enable -path=secret kv-v2
echo "Putting a secret in"
kubectl exec vault-0 -n vault -- vault kv put secret/secret-challenge vaultpassword.password="$(openssl rand -base64 16)"

echo "Putting a challenge key in"
kubectl exec vault-0 -n vault -- vault kv put secret/injected vaultinjected.value="$(openssl rand -base64 16)"

echo "Putting a subkey issue in"
kubectl exec vault-0 -n vault -- vault kv put secret/wrongsecret aaaauser."$(openssl rand -base64 8)"="$(openssl rand -base64 16)"

Expand Down Expand Up @@ -81,6 +84,9 @@ path "secret/data/wrongsecret" {
path "secret/data/application" {
capabilities = ["read"]
}
path "secret/data/injected" {
capabilities = ["read"]
}
EOF'

kubectl exec vault-0 -n vault -- /bin/sh -c 'vault policy write standard_sre - <<EOF
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.owasp.wrongsecrets;

import org.owasp.wrongsecrets.challenges.kubernetes.Vaultinjected;
import org.owasp.wrongsecrets.challenges.kubernetes.Vaultpassword;
import org.owasp.wrongsecrets.definitions.ChallengeDefinitionsConfiguration;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -11,7 +12,7 @@
import org.springframework.context.annotation.ScopedProxyMode;

@SpringBootApplication
@EnableConfigurationProperties(Vaultpassword.class)
@EnableConfigurationProperties({Vaultpassword.class, Vaultinjected.class})
public class WrongSecretsApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.owasp.wrongsecrets.challenges.kubernetes;

import com.google.common.base.Strings;
import org.owasp.wrongsecrets.challenges.FixedAnswerChallenge;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/** This challenge is about having a secrets injected via Vault template. */
@Component
public class Challenge46 extends FixedAnswerChallenge {

private final Vaultinjected vaultinjected;
private final String mockedAnswer;

public Challenge46(Vaultinjected vaultinjected, @Value("${vaultinjected}") String mockedAnswer) {
this.vaultinjected = vaultinjected;
this.mockedAnswer = mockedAnswer;
}

@Override
public String getAnswer() {
return vaultinjected != null && !Strings.isNullOrEmpty(vaultinjected.getValue())
? vaultinjected.getValue()
: mockedAnswer;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.owasp.wrongsecrets.challenges.kubernetes;

import org.springframework.boot.context.properties.ConfigurationProperties;

/** Class used to get value from vault using the springboot cloud integration with vault. */
@ConfigurationProperties("vaultinjected")
public class Vaultinjected {

private String value;

public void setValue(String value) {
this.value = value;
}

public String getValue() {
return value;
}
}
5 changes: 3 additions & 2 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ CHALLENGE33=if_you_see_this_please_use_k8s
ARG_BASED_PASSWORD=if_you_see_this_please_use_docker_instead
DOCKER_ENV_PASSWORD=if_you_see_this_please_use_docker_instead
vaultpassword=if_you_see_this_please_use_K8S_and_Vault
vaultinjected=if_you_see_this_please_use_K8S_and_Vault
spring.cloud.vault.uri=https://tobediefined.org
spring.cloud.vault.authentication=NONE
spring.cloud.vault.role=none
Expand Down Expand Up @@ -75,7 +76,7 @@ management.endpoints.web.exposure.include=auditevents,info,health
#---
spring.config.activate.on-profile=kubernetes-vault
wrongsecretvalue=wrongsecret
spring.config.import=vault://secret/secret-challenge
spring.config.import=vault://secret/secret-challenge,vault://secret/injected
spring.application.name=secret-challenge
spring.cloud.vault.scheme=https://tobediefined.org
spring.cloud.vault.enabled=true
Expand All @@ -92,7 +93,7 @@ asciidoctor.enabled=true
#---
spring.config.activate.on-profile=local-vault
wrongsecretvalue=wrongsecret
spring.config.import=vault://secret/secret-challenge
spring.config.import=vault://secret/secret-challenge,vault://secret/injected
spring.application.name=secret-challenge
spring.cloud.vault.scheme=http
spring.cloud.vault.enabled=true
Expand Down
7 changes: 7 additions & 0 deletions src/main/resources/explanations/challenge46.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
=== HashiCorp Vault Template Injection

Vault template injection via agent injection typically involves injecting a sidecar container,
known as the Vault Agent, alongside your main application container.
The Vault Agent is responsible for interacting with HashiCorp Vault to retrieve secrets and inject them into the application's runtime environment.

Can you find secret injected into application environment?
8 changes: 8 additions & 0 deletions src/main/resources/explanations/challenge46_hint.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
This challenge can be solved using the following steps:

1. Run `kubectl get pods -A` and find secret-challenge-xxx pod name

2. Run `kubectl exec secret-challenge-xxx -c secret-challenge -n default -- cat vault/secrets/challenge46` where `xxx` is the rest of the randomly generated pod name.
to print injected secrets from vault.

Note: if you are running this on a hosted environment, where you do not have access to the Kubernetes cluster, ask the organizer of the hosted solution to execute the commands for you and return the results.
16 changes: 16 additions & 0 deletions src/main/resources/explanations/challenge46_reason.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
*Why Vault Template Injection is not always a good idea?*

While Vault agent injection via templates can be a convenient way to manage secrets in certain scenarios,
there are situations where it might not be the best approach.

Templates might accidentally expose sensitive information in logs or temporary files.
If not properly configured, secrets could end up in places where they are accessible by unauthorized users or processes.
nwolniak marked this conversation as resolved.
Show resolved Hide resolved

Let's consider an example involving a template injection attack in a scripted language like PHP:

. Imagine a scenario where PHP application uses a template with sensitive information
* where template can look like this: $password = "'; system('rm -rf /'); //"
. When the template is processed it can become:
* $connection = "password='; system('rm -rf /'); //"

To prevent such issues its crucial to ensure that the values retrieved from Vault are properly validated.
14 changes: 14 additions & 0 deletions src/main/resources/wrong-secrets-configuration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -738,3 +738,17 @@ configurations:
ctf:
enabled: true
missing_environment: "explanations/missing_vault.adoc"

- name: Challenge 46
short-name: "challenge-46"
sources:
- class-name: "org.owasp.wrongsecrets.challenges.kubernetes.Challenge46"
explanation: "explanations/challenge46.adoc"
hint: "explanations/challenge46_hint.adoc"
reason: "explanations/challenge46_reason.adoc"
environments: [ *k8s_vault ]
difficulty: *expert
category: *vault
ctf:
enabled: true
missing_environment: "explanations/missing_vault.adoc"
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"SPECIAL_K8S_SECRET=test5",
"SPECIAL_SPECIAL_K8S_SECRET=test6",
"vaultpassword=test7",
"vaultinjected=test46",
"secretmountpath=nothere",
"default_aws_value_challenge_9=ACTUAL_ANSWER_CHALLENGE9",
"default_aws_value_challenge_10=ACTUAL_ANSWER_CHALLENGE10",
Expand Down Expand Up @@ -52,6 +53,10 @@ void shouldSpoilExercises() throws Exception {
@Test
void shouldNotShowDisabledChallengeAnywhere() throws Exception {
for (var challenge : challenges.getChallengeDefinitions()) {
var shortname = challenge.name().shortName();
if (shortname.contains("46")) {
continue;
}
mvc.perform(get("/challenge/%s".formatted(challenge.name().shortName())))
.andExpect(status().isOk())
.andExpect(content().string(not(containsString("This challenge has been disabled."))));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.owasp.wrongsecrets.challenges.kubernetes;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;

public class Challenge46Test {

commjoen marked this conversation as resolved.
Show resolved Hide resolved
@Test
void spoilerShouldGiveAnswerWithVault() {
var vaultInjected = new Vaultinjected();
vaultInjected.setValue("answer");
var challenge = new Challenge46(vaultInjected, "");
assertThat(challenge.spoiler().solution()).isNotEmpty();
assertThat(challenge.answerCorrect(challenge.spoiler().solution())).isTrue();
}

@Test
void spoilerShouldGiveAnswer() {
var vaultInjected = new Vaultinjected();
vaultInjected.setValue("");
var challenge = new Challenge46(vaultInjected, "answer");
assertThat(challenge.spoiler().solution()).isEqualTo("answer");
assertThat(challenge.answerCorrect(challenge.spoiler().solution())).isTrue();
}

@Test
void incorrectAnswerShouldNotSolveChallenge() {
var vaultInjected = new Vaultinjected();
vaultInjected.setValue("answer");
var challenge = new Challenge46(vaultInjected, "");
assertThat(challenge.answerCorrect("wrong answer")).isFalse();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"SPECIAL_K8S_SECRET=test5",
"SPECIAL_SPECIAL_K8S_SECRET=test6",
"vaultpassword=test7",
"vaultinjected=test46",
"secretmountpath=nothere"
},
classes = WrongSecretsApplication.class)
Expand Down Expand Up @@ -55,7 +56,8 @@ void shouldNotShowDisabledChallengeAnywhere() throws Exception {
|| shortname.contains("10")
|| shortname.contains("11")
|| shortname.contains("44")
|| shortname.contains("45")) {
|| shortname.contains("45")
|| shortname.contains("46")) {
continue;
}
mvc.perform(get("/challenge/%s".formatted(challenge.name().shortName())))
Expand All @@ -81,7 +83,8 @@ void shouldEnableK8sExercises() throws Exception {
.andExpect(content().string(not(containsString("challenge-6_disabled-link"))))
.andExpect(
content().string(containsString("challenge-7_disabled-link"))) // vault is not visible
.andExpect(content().string(not(containsString("challenge-33_disabled-link"))));
.andExpect(content().string(not(containsString("challenge-33_disabled-link"))))
.andExpect(content().string(containsString("challenge-46_disabled-link")));
}

@Test
Expand Down
Loading