Traccar Unauthenticated LFI v5.8-v6.8.1

Sometimes you search endlessly and find nothing. Other times, the gold just drops into your lap. This is a story about how we accidentally found a pretty impactful vulnerability.

Traccar Unauthenticated LFI v5.8-v6.8.1
💡
This has been assigned CVE-2025-61666.

As a part of our penetration tests, we almost always run a vulnerability scan. Most results can be pretty boring, but in this one test we spotted something interesting.

Nessus found unauthenticated LFI in traccar???

internal monologue: has no one ever ran a vulnerability scan against this application?
Nessus found LFI!

When we tried to reproduce the issue in our lab, we quickly realised only Windows installs were vulnerable. Time to figure out why.

Reproducing the LFI in our lab.

Analysis

After reading some Jetty documentation, we found what looks to be the vulnerable code here.

We've added some annotations to help explain what's going on.

--- snip ---
@Override
// pathinContext is the path of the request e.g. /index.html or /css/style.css
public Resource getResource(String pathInContext) {
    // overrideResource is the base directory from which we search
    // by default this is ./override in versions 6.1 - 6.8.1
    // in versions prior v5.8 - 6.0 this isn't set by default/is disabled
    // It's intended to allow loading custom assets so you
    // can customise your traccar instance without changing code
    // e.g. ./override/logo.png 
    if (overrideResource != null) {
        try {         
            // addPath is from Jetty code.
            Resource override = overrideResource.addPath(pathInContext);
            if (override.exists()) {
                return override;
            }

The user provided path is passed directly into Jetty's Resource.addPath method so we'll need to take a look at Jetty's codebase.

Starting with Jetty documentation, path traversal attempts look like they should be blocked?

doco link
weneedtogodeeper.jpeg

The PathResource implementation of addPath uses URIUtil.canonicalPath to check if the path is safe and will return null if it's dangerous.

pathResource.addPath

Finally following the code here we see additional source code comments indicating that path traversal shouldn't be possible.

canonicalPath called by addPath

Let's mock up a quick test.

--- Testing: Backslashes ---
Path: '..\..\..\blah'
URIUtil.canonicalPath() result: '..\..\..\blah'

--- Testing: Forward slashes with filename ---
Path: '../../../blah'
URIUtil.canonicalPath() result: NULL

oops

That doesn't look good?

Reporting it to the Jetty Team

While we're not deeply familiar with Jetty, the docs and source comments suggest addPath is intended to block path traversal attempts. So we raised it first with the Jetty team to see what they'd say.

We got this response back pretty quickly:

The code that tracar has is vulnerable, not Jetty.

The https://github.com/traccar/traccar/blob/bc2faa140a2a5ba63968b95cd8ddba7578e1b07f/src/main/java/org/traccar/web/DefaultOverrideServlet.java code skips the entire ResourceService and ContextHandler layers.

This also means it skips the entire AliasChecker facility built into Jetty to prevent this kind of access.

File this against tracar.

Moving on.

Making it Impactful

It's not very interesting to just grab random files from Windows so let's see if we can make this more impactful.

The traccar configuration file can be configured to contain multiple passwords. LDAP passwords seem particularly interesting.

free creds

This could be quite bad. An unauthenticated attacker could:

  1. Exfiltrate AD credentials if they are configured.
  2. Use them to try to connect to corporate VPNs etc. or access other domain connected hosts.

Hopefully there's not too many instances exposed to the internet.

hrmmm

I haven't dared send a cheeky GET request to any of the exposed hosts but you can tell some of these hosts run Windows as they also expose other Windows services AND the jetty version header matches vulnerable versions of Traccar.

For example, version 6.8.1 of traccar runs jetty 11.0.25.

good luck, I hope you see the advisory

Versions v6.1 (April 11 2024) - v6.8.1 (July 8 2025) are affected in default installations on Windows.

Versions v5.8 (May 31 2023) - v6.0 (April 7 2024) are affected in non-default configurations if override is enabled on Windows.

Traccar Unauthenticated Local File Inclusion on Windows - Leakage of Traccar Config File
### Summary Default installs of Traccar on Windows between versions 6.1-6.8.1 and non default installs between versions 5.8-6.0 are vulnerable to unauthenticated local file inclusion attacks which…

Looking Elsewhere

If there's one, there's normally more.

With a quick search on Github we found a smaller project called mediatoad which had some familiar looking code.

@Override
public Resource getResource(final String pathInContext) {
	try {
		final String path = StringUtils.removeStartIgnoreCase(pathInContext, "/" + C.STATIC_FILES_PATH_PREFIX);
		final Matcher m = CACHE_BUST_PATTERN.matcher(path);
		if (m.matches()) {
			return this.rootRes.addPath(m.group(1));
		}
		return this.rootRes.addPath(path);
	}

Fortunately in this case, mediatoad is only vulnerable when started with the --webroot argument which is intended for use in local development scenarios.

Retrieving the user configuration file containing password hashes

There's almost certainly more examples out there but that's all we've got time for today.