Medical Ref Company

No sensitive information will be share here but contain the general steps that was taken to gain full access to all patient data of a company.

Finding the path

During the assessment of the a medical reference company a interesting feature was discovered, a invite to company. This feature allows you to invite staff members to join your company and share the joy of managing the referrals coming in for doctors and other medical professionals.

All data that you need to supply in the form is the first name, last name and email address. Once the form is submitted, the invitee will receive a email with a unique token to login to the application. Once the user logs in they are reqursted to set a password for thier account and after this they will have access to all patient data on the compay account and will have the abilty to send out new referals.

Hacking at the hash

So a juicy target if we can break the JWT we can set a new userid and company id and again access to the full patient data of any company. I span up hashcat and tried the rockyou.txt password-list, maybe a developer used a common password. As i don't have access to a password cracking station I ran hashcat on my CPU, and a speedy 3-hours later the wordlist was excused and no joy. The developer didn't use any password in the wordlist but maybe the they did use a password in another wordlist but after many hours later hashcat didn't find any password that matched the password used for the JWT. As hashcat was buy running up my power bill and heating up my office in the hot summer afternoon, i reviewed the invite request. There is a few parameters in the actual URL that is not percent in the submit form. Interesting.

Hidden parameter

We have, 'person-given_name' so that is the first name, 'person-family_name' and here is the last name, finally we have the email 'person-email' but then we have a new parameter, 'person-org_id'. Well that is interesting, a user controlled request has a parameter for a org_id. Surely the application has a server side verification check to assure that the org_id match the org_id in the users profile. Surly the developers know that anything coming form a user controlled should be considered as a untrusted and check need to be done to confirm the user has access to preform the actions that they are attempting. Surely, right?

No, there is no server side checks in place. Any user can invite themselves to any organisation in the database and gain access to their patients data. all we need to do is find the org_id and we are golden. So how do we find the org_id? Well there is a really good search feature in the public side of the application that we can use and as there is no rate limitations on the HTTP request and we can supply a org_id in the search, with this we can simple enum all the companies that use this application. Our Org ID was something like 103, so we know that they only use numbers for the org_id and as our id is only 103 we can assume that there isn't many companies in the database. As this is a staging environment that is not surprising. So I setup intruder enum all the companies from 1 to 200.

The intruder found 25 companies in the database. As there is no real personal data i had no issues with inviting myself to another company to show the impact of the issue.

Full access

So I have the HTTP Request to invite a user to the company, I found 24 new companies to invite myself to. All I had to do is to set the targets and fire, that i did. Now i wait for the email to come in, i hit the refresh quite a few times but no email came through. I rechecked my parameters, all looked the same as the ordinal invite request. I pinged the server and the server is still up. The HTTP response looked the same as before. So I created a new alias and fired the HTTP request, 20-min late still no email. I know this is a non-production environment so the emails might be delayed, so as this was late in the afternoon I shutdown my VM and logged off for the day.

The next morning there was still no invite email. So I logged on to the application again, invited one of my alias to the company I am testing from and less then 5-mins I received my invite email. So i replaced the JWT in the payload and fired again. Boom! Invite received to another company. I later found that my ordinal JWT token from the previous day expired and the HTTP response was the same regardless if the JWT is valid or not. A script-kiddy mistake. But regardless I now have access to another company's patient data.

What went wrong

Well its simple, the application trusted the user input fully. There was no server-side checks to verify that the user had the correct permissions pertained to the action that they are trying to reform. All user input should be treated as untruted.

How to fix this

As i said before the application accepts the user input as trusted and there is no server-side checks. This is the cord issue in the matter.

The fix this there are two options

  1. Implement server side check to verify that the user sending the invite has the permission to invite users to the company that match the ord_id. If the user has permission then allow the invite to be send, if not then drop the invite request.

  2. Remove the org_id parameter from the user controlled input, just because the user can supply the ord_id in the submit html form doesn't mean that the parameter is not in the user control. Move the parameter to the server side, instead of taking the parameter from the URL, have the server look up the current ord_id of the user submitting invite.

Last updated