Finding and Disclosing My First CVE’s

CVE’s, short for Common Vulnerabilities and Exposures, are a reference model for publicly known security vulnerabilities. Whenever a vulnerability is identified and disclosed, a new CVE can be requested and issued to document this vulnerability. Over time, as countless vulnerabilities have been discovered and documented, CVE’s have become the de facto method to classify vulnerabilities so that this information can be shared between any interested party. The primary organisation responsible for maintaining the CVE database is Mitre, who also maintain the popular ATT&CK framework.

I’ve known about CVE’s for almost as long as I’ve been interested in cyber security, but I never really knew how the whole process worked end to end.

I still had several questions surrounding the CVE and responsible disclosure ecosystem that I wanted to answer for myself. These were:

  • How do you even discover a CVE?
  • How do you perform “responsible disclosure”?
  • How do you request a new CVE?
  • How are CVE’s classified?
  • How are CVE’s issued + credited?

Inspired in part by an excellent article from Joe Helle, I decided to give the whole process a go and learn for myself how CVE’s are found, requested and issued. I also thought this would be a good opportunity to attempt a responsible disclosure, and assist in improving the security of an open source project along the way.

Apperta OpenEyes

The first step was to find an open source project to test. After some searching, I came across a project called OpenEyes, maintained by the Apperta Foundation. This one stuck out to me as its aim was to provide an application for use in the field of Ophthalmology, which is something that has personal meaning to me. OpenEyes has been implemented in a number of healthcare providers around the world, including the NHS Scotland and Eye Research Australia.

The OpenEyes homepage

To install the application, I spun up a new Azure instance with a public IP address and then used the docker container provided on their GitHub page to install the application. After editing the firewall rules to allow for my own IP address to make connections over HTTP, I was able to successfully connect to the OpenEyes application and begin some security testing.

Configuring an Azure VM to install OpenEyes on
Running OpenEyes via Docker from the Command Line
Accessing OpenEyes successfully via the browser

Discovery of 2 Vulnerabilities

With Burp Suite configured, I began interacting with the application and getting a feel for how it worked, how requests were handled and what responses looked like. This is a pretty standard approach when starting out testing, as it’s important to get a feel for the application first before you start digging deeper.

Eventually, I began testing input fields to see how user inputs were being handled by the application. I found that though the application was sanitizing user inputs in the majority of fields, this control had not been applied completely across the all the possible input locations. In particular, I found that the “Address1” field did not correctly sanitize user inputs, and after a few different XSS payloads, I was able to get one to trigger successfully.

The Address1 field not properly sanitizing user inputs…
…resulting in a XSS PoC payload triggering

Once I had the XSS vulnerability confirmed, I decided to test for access control issues, which are my favourite class of web application vulnerability. After creating a couple of different accounts with differing levels of permissions, I started to test the app from these two different user perspectives. As this application was related to medical records, I was looking to see if a low-level user could gain access to information about other patients.

When manually browsing to a patient page without this low level of access, I noticed that rather than returning typical 401 or 403 errors, the application responded with a standard HTTP 200 status and the page was loaded with a custom error message rendered on the page itself.

The “Forbidden” error message loading in a page that returns with status code 200

This got me wondering if the information that would typically be on the page was also still being loaded, so I did what any good hacker would do and viewed source. Sure enough, all the information was still found within the page itself, but was being covered over with the custom error message. This was a form of information disclosure, as it would allow for low privileged users to view the information of other patients without having the required level of access within the OpenEyes application.

Viewing the source of the page would load all the information, regardless of the users level of privilege
The section of code that would render the error message seen in the browser, despite the information still being contained in the response

Just for reference, here is what the page would have looked like if accessed by a highly privileged user. Note that the information is all rendered correctly when accessed with a sufficient level of privilege and there is no error message returned.

The patient page when loaded with sufficient permissions

So with that, I had discovered two vulnerabilities in an open source product that had never been identified before, which potentially qualified these as new CVE’s. That answered the question of “how do you even find CVE’s?” – the answer being fairly obvious – just go find some new vulnerabilities!

The Responsible Disclosure Process

After finding these two vulnerabilities, I needed to actually report them to the people responsible for building and maintaining the application. This was easier said than done, as the first email address I found listed on GitHub was out of commission.

In the end, I simply sent an introductory email to the generic email listed on the Apperta website, notifying them of the two vulnerabilities I intended to disclose. Only a few hours later, I received a reply saying the email had been forwarded on to the relevant technical team. About a week after that, I received an email from a member of said technical team, thanking me for conducting the testing and asking for further details and reproduction steps. I sent through detailed reproduction steps as well as video demonstrations to make sure they had all the information they would need.

After a bit of extra back and forth, Apperta were happy for me to disclose these vulnerabilities formally as CVE’s and confirmed a fix had been applied for both issues, again thanking me for disclosing the issues to them.

Below is a screenshot of the final piece of communication we had, which I personally think was a really nice way to close things out.

Further thanks from Apperta and confirmation that the issues have now been remediated

Apperta were absolutely fantastic to work with throughout this responsible disclosure process, and I can’t speak highly enough of how they conducted themselves when presented with these two vulnerabilities. It was satisfying to receive the thanks from them, and I was even happier to hear the two security vulnerabilities have now been remediated.

While I’m sure the responsible disclosure process may vary depending on the vendor, organisation or individual, I found this to be an excellent first experience. The answer to “How do you perform “responsible disclosure”?” may seem fairly straightforward, but I think being open, honest and providing as much detail as possible is the best way to promote a positive experience for the parties on both sides of the disclosure process.

The CVE Request Process

This is the main area where I was completely inexperienced. How to actually go about transforming an identified vulnerability into a formal CVE.

I had to start with my first question: “How do you request a new CVE?“. Turns out, there is a pretty simple web form that you can fill out to submit a new CVE request. The web form has a range of fields that require you to input basic information such as the Vendor, Product and Version you are requesting a CVE for.

What was more interesting, was that this form also included fields for all the relevant information for the vulnerability itself. I was prompted to describe the impact, classify the vulnerability type and even provide a title and description. This made requesting the CVE’s a lot more time consuming than I had initially expected, as I wanted to take my time ensuring all the details were correct to the best of my knowledge.

Essentially, the information within a CVE is almost entirely self-submitted by the person who made the initial request. By stepping through this process myself, I answered my question of “How are CVE’s classified?” – I was the one who ultimately classified these two CVE’s!

The first section of the CVE request form
Section of the CVE request form where you input information about the CVE you are requesting

Once the CVE requests had been submitted, I received an email back informing me that two CVE ID’s had been reserved for these vulnerabilities. The next step was to then make public references to these vulnerabilities, and send this information back to Mitre. A member of the Mitre vulnerability team would then cross-reference the CVE request form with the public reference to ensure the CVE request was legitimate, and all the fields contained correct information.

Full information on these CVE’s, along with corresponding video demonstrations can be found on my GitHub profile:

CVE-2021-40375 – Broken Access Control in OpenEyes 3.5.1

CVE-2021-40374 – Stored Cross-site Scripting in OpenEyes 3.5.1

The first section of the vulnerability write up on my GitHub, hopefully many more to come!

This whole process actually took a very long time in my experience, and I even had to prompt the team at Mitre for an update after not hearing anything for over 3 months. Eventually though, the two requests were formally processed and the records are now available for anyone to view. In terms of formal credit, the only real method is to reference the CVE’s in a public location such as a GitHub profile, then ensure this is linked in the references of the CVE. There was a section in the request form to list the name of the discoverer, but this information was not reflected in the CVE record once published.

The two CVE ID’s have now been formally published, as 2021-40374 and 2021-40375. This completed the CVE request process and answered my final question of “How are CVE’s issued + credited?“.

The CVE record as seen on cve.mitre.org

Lessons Learned

Overall, I learned a lot from this process. In particular, seeing the way CVE’s work and are assigned has been great for my understanding of vulnerabilities and their life cycle. I was also able to get started identifying some vulnerabilities for myself, and worked with an organisation to disclose these vulnerabilities and recommend changes that could be made to make their application more secure.

I personally think identifying CVE’s in this way is a fantastic way to keep your skills sharp through active testing of various products, while also making a meaningful contribution to the cyber security industry by disclosing these responsibly. Ultimately, the end goal should always be to help keep users of these products as secure as possible.

This was a rewarding exercise in a number of ways, and I’m stoked to officially have 2 CVE’s under my belt. Discovering vulnerabilities in this manner is something that I intend to do more of in the future, hopefully testing a range of different products and identifying a range of different vulnerability classes.

Cheers,

Kento.

Leave a comment