Show Notes

237 - Reborn Homograph Attacks and Ransacking Passwords

Two core issues here, first is an auth-bypass due to incorrect parsing, and the second is a pretty straight forward command inject in an authenticated feature. There is also a bit of a bonus issue in how they gained access to the source code.

First, the authentication bypass. The application has two layers, the back-end application itself which does no authentication in the code, and the front-end reverse proxy server that does the actual authentication checking before forwarding the request to the back-end. There were a couple endpoint that could be accessed without authentication, so the front-end server would take the requested path and see if it had a prefix matching one of these allow-listed paths. The problem is that it wouldn’t resolve any directory traversals before this check. So a path like /api/v1/allowed/endpoint/../disallowed/endpoint would be able to pass the prefix matching. However the back-end request would end up going to the /api/v1/disallowed/endpoint handler. Giving an attacker unauthenticated access to any endpoint. It is worth noting that for your own testing of this sort of issue you will need to issue teh request manually with something like burp repeater as your browser will resolve that directory traversal before making the request.

The command inject is fairly trivial the License key status check endpoint would take a value from the URL and use it in a command line to start a perl script. Allowing for trivial command injection.

The bonus issue is just a bit fun but because researchers were unable to get a copy of the VM for Pulse Connect Security and the software was behind a sales process they had to find another way to access to code. They targeted a cloud deployment, in order to get access to the filesystem they shutdown the VM, and editted the book arguments to change the init or first process run on startup to /bin/sh. However /bin/sh was blocked by the kernel, //bin/sh however was not blocked.

Disclosure of private report titles on HackerOne if there is a pending email invitation for collaboration (made through the Manager Collaborators invitation panel). With an invite being made any anonymous user anyone can query that report’s title by id on the GraphQL API.

Four issues, two are race conditions, two are due to lack of authorization checks on the API and only enforced on the front-end.

  1. Ability to invite more users beyond the team limit. -This is kinda a race condition but a pretty long window for one. On this unnamed application the Team plan allowed inviting up to three team members. The problem was that this limit was only checked by looking at the current number of team members at the time the invite is sent and there is no apparent limit on the number of invites that can be sent. So as long as the invites are sent before a third member joins the team they will still be able to join the team.
  2. Client-Side Auth - I’m going to merge the second and fourth issues into just one here because to me they have the same sort of root cause. Even though an account may not be authorized to perform an action just as a user with no plan inviting members to their team or lower-privileged members of a team viewing higher-privileged data. Both could be accomplished through making direct requests to the API instead of using the frontend application.
  3. Limit Bypass Race Condition- This one is a bit tighter though and has to do with creating “browser profiles” in the application. This is just a class limit-bypass race. There is a limit on the solo plan of 300 browser profiles, sending many requests at the same time can result in more profiles than should have been allowed being added.

I had to go commit surfing to try and figure this out out as there is no write-up, but looking at the fix commit what I found is a bit of an interesting logic mistake.

The first thing to notice is a bit of weird login, on lines 181-185 there is a check that basically if a password is provided, do a normal userSession->login(...) but if no password is provided, then it fixes the $result to true. That is a bit suspicious to my eye, but it seems the intent is that when using a SAML backend for the login process no password will be provided. Just before this code is a check and function call just for that. The problem is that when they choose to skip validating the password, they don’t ensure SAML was actually used so by providing a blank password one could bypass authentication.

As a bit of a disclaimer though, it is reading the User ID, Password and some options from a JWT, I’m not sure exactly how this JWT is created but it easy to imagine that an attacker does have a decent amount of control over the relevant values for obvious reasons.

This is a take on a somewhat classic Host-header injection attack strategy using a homograph attack to bypass the attempt to prevent such an attack.

The normal strategy is to take some page that will trigger an email containing a link, like a Password Reset page. When you make the request to reset a password, you use your host-header injection techniques. Sometimes this is as easy as just changing the Host: header in the request, though in this case they used the X-Forwarded-Host header. Then when the email is sent it may use the value from the injected Host value as the base-domain for the link it created which can be an attacker controlled domain.

In this situation there seemed to be some check to ensure the header was something that would be allowed. Its unclear what that check was exactly the only examples shown are that did not work, but auth.tá did work. Note the accented a in the domain. That is important, as the intent is like to limit the host’s to just and its subdomains (I can’t imagine why they’d have a check at all if it was just fixed to instead of just using the fixed value). Unicode characters can get tricky when transforming them, and in some cases that ámight get normalized into A or a, then after that normalization it would pass a check for At which point is crafts the URL trusting the Host value, still containing the accented character and sends that to the victims email. If the victim clicks the link in the unsolicited password reset (but legitimately sent) the reset token will be leaked to the attacker.

A timing side-channel vulnerability in darkhttp’s implementation for HTTP-based authentication. Since the auth check is done by a strcmp() call which iterates and compares each character of the string, by observing the amount of time taken in the auth check, each character can be side-channeled to bruteforce the HTTP password. The vulnerability was fixed by using a constant-time string comparison algorithm.