XSS via ACME implementations

I wanted to point out a blog post by the company Detectify (same people that found a vuln in the SNI validation a while ago, but a different issue):

They found that some implementations of ACME enable cross site scripting. In the http-01 domain validation method the CA server sends the requester a token and then the server should serve the token concatenated with an account public key hash in a file named after the token.

It turns out some implementations do this in a way that they don’t really care about the token, but always reflect whatever you request under the corresponding url (/.well-known/acme-challenge/token). In some setups this is combined with content sniffing, which enables cross site scripting (i.e. if you reflect html content it will be served as text/html).

I discussed this recently with Frans Rosén (one of the finders) and we wondered if there’s some ACME implementation out there that has this behavior or if those hosters all came up with this on their own.

There are a lot of ACME implementations, but maybe someone here is aware of an implementation that might have this behavior. Would be interesting, because then we could fix this at the source and stop it from proliferating.


You might also be interested in this discussion: Is .well-known/acme-challenge/ an XSS risk

1 Like

Hi @hannob

thanks. This is really not a good idea. My own client sends a 404, if the token is wrong.

But: There are sometimes curious questions:

The key authorization file from the server did not match this challenge [U7Z05MbBan_sQRbagOsMUOrbrefGfGsWiYZqTbXdz_c.Mxd6puZz877ZrQ7u9cPPc2amincbkMvNFjbgIj9OlQU] != [U7Z05MbBan_sQRbagOsMUOrbrefGfGsWiYZqTbXdz_c.JQoYFoTtPDe2MIr4xFKqpRpt0eKti-HMnJ0BIl9eOjE]

The token is correct, the dot is correct. But the hash of the account key is wrong.

And: There were wrong ipv6 - settings, so ipv6 sends another content then ipv4.

Removing the ipv6 - AAAA record solved the problem.

So it looks that these hosters reflect /.well-known/acme-challenge/1234 with the content

1234.[Hash of the public account key]

With the consequence:

If Letsencrypt has this error validating a domain name, Letsencrypt could search all accounts to find the account with [Hash of the public account key] to find such a wrong reflection.

Being able to reflect the requested resource in the body is valuable when you are performing HTTP validation for a domain that is served by many web servers (and are not load-balanced through a single endpoint).

It saves having to deploy challenges to each server, rather each server just has to be preconfigured with the JWK Thumbprint.

acme.sh is one of the clients that documents such a solution: https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode (though it does restrict the character set of the reflected part to that of base64url so it’s probably not vulnerable in most cases).

FWIW, server-side content sniffing a security risk independently of ACME.

I am not sure if the sniffing middleware from the article responds to x-content-type-options: nosniff as browsers do, but maybe it’s a good implementation guideline for acme-challenges anyway.


I think other clients may also have implemented a “stateless mode” directly inspired by the one from acme.sh. But maybe not.



for discussions about this.

@Neilpang, I think @_az is right that your implementation will not create this risk at all, but could you please verify that? Also, are you aware of whether any other implementations were deployed that were inspired by yours?

I agree with you too.
And I will check more details about this issue soon, and restrict the regular expression more.

Also, are you aware of whether any other implementations were deployed that were inspired by yours?

Sorry, I can not remember.


1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.