Responsible Disclosure: Preventing A Possible DisasterResponsible disclosure, a method of vulnerability disclosure where all stakeholders agree to a certain window in which the vulnerability will be fixed before the details are revealed to the public. There are, for all intents and purposes, two camps when it comes to responsible disclosure: the nice guys and the extortionists. I used words that have very strong implications, but let me be clear, anyone who discloses responsibly is a nice guy. So what are the differences between the two camps? It's pretty simple, money.
The nice guys are usually not security researchers by profession. It's not their intent to make a living by finding vulnerabilities, reporting them, and getting paid for their findings. When they find a vulnerability they report it to the affected party merely because it's the right thing to do. The extortionists are the exact opposite case. These guys are usually security researchers by profession. They feed their families by finding vulnerabilities, reporting them to the affected parties, and getting paid for their time and effort. Then, once the vulnerability is patched, they disclose all of the details, who/what/where/when/why/etc. This whole process serves two purposes: first, they get to eat tonight, second, they get to eat tomorrow because their disclosure will bring in more work from companies who hire them to conduct security testing on their products/network/etc.
Hopefully you understand now, neither camp is bad. All of them are good people, some just do this for a living. In what camp do I fall? I would love to say that I fall into the extortionist camp, but I have neither the skills (I'm good, but the guys who do this for a living are magic) nor the job title to claim that I do this for a living. When I find a vulnerability, I usually just tell the affected party and that's that.
So why the homily? Recently (May 11) I found a vulnerability in the Mount Aloysius College public website which allowed anyone with a working knowledge of HTML to send emails to anyone from anyone. This may sound trivial, but imagine the consequences of this going public; an email is sent to a professor from a student verbally attacking the professor ending in expulsion of the student or an email is sent to the College's main fundraising ally from the president denouncing them and their company ending in a huge financial loss for the College. Emails are dangerous collections of 1's and 0's and can have tragic effects on people's livelihoods when used improperly.
But despite the efforts of the person who found the vulnerability, sometimes being responsible isn't good enough. Those of us who find these problems aren't interested in seeing the affected party be exploited. Dan Kaminsky didn't want to see the entire Internet be broken when he found a critical flaw in DNS and I don't want to see the College have their web server abused. But not all parties react by fixing the problem, and sometimes it's not their fault. Many times when the problem isn't quickly fixed, it's because the problem is not within the affected party's control. In the case of the College, the website is hosted offsite by a third party. Add to that the CMS being used is a COTS product and you see that there are multiple barriers to fixing this flaw. The College can't fix it because the server isn't under their control. The hosting company could fix it by modifying some code, but that means they need to have a software engineer in their employ with knowledge of the COTS product. And even if the hosting company did "fix" the flaw, the next time they update the server with the newest version of the COTS product the flaw would be back.
As I've eluded, responsible disclosure is not always easy. Sometimes the problem doesn't get fixed. Sometimes the affected party ignores the patching window and the details get published before the flaw is fixed. Sometimes this is just what needs to happen to light the fire.
The SituationThis semester marked the last for the College's current president, because of which a farewell reception was held. Being a director on the College's Alumni Association Board, I was invited. In order to RSVP for this event, I was directed to a form on their website.
Being a fan of SQL injection, anytime I see a web form I usually check out the page source in hopes I can see what that form is doing. Upon finding the <form> tag in the source, I was quite surprised by what was staring back at me.
<form method="post" action="/dotCMS/sendEmail" onsubmit="return checkForm();"> <input name="from" value="firstname.lastname@example.org" id="from" type="hidden" /> <input name="to" value="REDACTED@mtaloy.edu" id="to" type="hidden" /> <input name="cc" value="REDACTED@mtaloy.edu" id="cc" type="hidden" /> <input name="subject" value="Farewell Reception" id="subject" type="hidden" /> <input name="return" value="http://www.mtaloy.edu/about_mac/thank-you-for- registering-for-the-farewell-reception.dot" id="return" type="hidden" /> <input name="formType" value="Farewell Reception" id="formType" type="hidden" /> <input name="order" value="name,guestname,organization,email,needs,people_attending" id="order" type="hidden" />
Could it be? An email function built into the CMS software that blindly sends emails given the correct parameters? It sure looks that way. Let me be clear about something. When you have a web form that sends email, you don't do it like this... at all... ever............ ever. The proper way to make an email web form is to have items such as to/from email addresses, subjects, etc. stored in server-side code. Doing it that way not only makes it invisible to the user (it doesn't show up in the page source like you see above) but it also makes it unchangeable. And if there's one thing you don't want people to do it's changing your code. There's a rule of thumb among security professionals, if it's sent to the client it can be changed. That is exactly what happened here. Internal workings of this form were sent to the client (showing up in the browser) and as such can be changed.
The ExploitIt should be obvious that the page, as we see it, can't be changed. It's code lives on the web server thus making the only way to change it having access to the web server's filesystem. But since we have a copy of the code in our browser, we don't need to change the original. We can make our own web page using this code and run it from our own machine. The only thing we need to change to make the form work is the "action" in the <form> tag. Because it's running on the mtaloy.edu webserver, the relative path of "/dotCMS/sendEmail" will be fully resolved to "http://mtaloy.edu/dotCMS/sendEmail" when you click the "Press To Send" button. That's just how web servers work. But since we're not running our modified code on their server, we need to change that from a relative path to an absolute path. If not, when we click the button the path will resolve to "http://localhost/dotCMS/sendEmail", which will obviously not work. We need it to point back to the College's web server.
So here is our modified code that will send a test email to my email account from the webmaster.
<form method="post" action="http://mtaloy.edu/dotCMS/sendEmail"> <input name="from" value="email@example.com" id="from" type="hidden" /> <input name="to" value="REDACTED@mtaloy.edu" id="to" type="hidden" /> <input name="subject" value="B0rked" id="subject" type="hidden" /> <input name="return" value="http://www.mtaloy.edu/about_mac/thank-you-for- registering-for-the-farewell-reception.dot" id="return" type="hidden" /> <input name="formType" value="B0rked" id="formType" type="hidden" /> <input name="order" value="message" id="order" type="hidden" /> <input required="text" pretty="message" name="message" value="Does this work?" />
Note how the "action" has been changed to an absolute path, we are only sending it to one recipient (to), the subject is "B0rked", and the message is hardcoded as a value of the textbox. If the sendEmail function on the web server acts the way I think it does, I should have an email waiting for me once I click the "Press To Send" button.
The AnalysisOk, that's not good. It worked. The public website can be used by anyone to send emails. It was at this point that I gave this info the the College's IT staff, who, in turn, gave it to the third party hosting company to fix. My part's done. But I wasn't satisfied. I still had more questions; at least one, is the vulnerable function limited to this particular web server or can it be found in each and every install of whatever CMS software is being used? Let's find out.
First, we need to determine what CMS software is being used. The oldest trick in the book is to cause an error on the site in order to get the default error page. This shouldn't be possible, as the first thing you do when configuring web software is disable default error messages because they divulge way too much critical information. But, hoping for the best, let's see if it works. I'll just try to go to a page that doesn't exist by smashing the keyboard (http://mtaloy.edu/dfljjkhlsdfjhlfkdg).
You have got to be kidding me. A default error page. Ok, so we now know what software is being used on the web server, dotCMS. By clicking the "URL" link, we also get a ton of extra information which no one should ever see.
Request information Requested URL: http://mtaloy.edu/portal/404.jsp Request method: GET Request URI: /portal/404.jsp Request protocol: HTTP/1.1 Servlet path: /portal/404.jsp Path info: null Path translated: /wwwroot/mtaloy.edu/webapps/../dotCMS/portal/404.jsp Query string: null Content length: -1 Content type: null Server name: mtaloy.edu Server port: 80 Remote user: null Remote address: REDACTED Remote host: REDACTED Authorization scheme: null
After downloading and extracting the code for dotCMS, I now want to see if the "sendEmail" function is in the base code. If it is, that means that any web site running a default install of dotCMS is vulnerable. If it isn't, that means that this is probably a custom function on the College's site, which means someone made a boo-boo.
Normally, I would do a simple grep to look for the string "sendEmail" to see if it existed in any files in the dotCMS directory. However, since this package is written in Java, most of the good stuff is tucked away inside of JAR files. I ran the grep anyway and found a bunch of documentation files that tell me there are five sendEmail functions. We're on the right track. Now I just have to see if they can be used the exact way that the College's site was using them (via /dotCMS/sendEmail) to determine if it's a standard thing. To do that, I installed dotCMS on my computer and hit the same address locally, http://localhost:8080/dotCMS/sendEmail. Voila, I get the same error as I get on the College's site when I try to hit that address directly. The error is basically a Java stack trace that tells me that this function exists but I used it incorrectly. If the function didn't exist, I would have been redirected to the admin interface's login screen. Seriously?!?!
Final CommentsThere are so many things wrong with the dotCMS platform I'm not even going to try and explain them all. From being an open email relay, to default error messages, to redirecting to the admin interface... who wrote this? Seriously...