Hacking Open Source Software for Fun and Non-Profit
«Hacking Open Source Software for Fun and Non-Profit» is the title of blog post by fellow security researcher @addelindh. Testing Open Source Software (OSS) is something that has been on my to-do list for a while, because I am convinced that a certain amount of time should be dedicated to it. The reason for this is simple: I firmly believe in giving back to a community that I rely on on a daily basis. This blog post is about an ongoing audit of the Ampache media streaming server, the vulnerabilities found and lessons learned.
From the Ampache website:
A web based audio/video streaming application and file manager allowing you to access your music & videos from anywhere, using almost any internet enabled device.
Goals
The three main goals were:
- Give something back to the community
- Learn more about code auditing
- Try out RIPS
I had been curious about RIPS, for a while, partially because of their Advent of PHP Application Vulnerabilities. When I wanted to purchase an on-demand license, they were so generous and offered me a free trial, once they heard what I wanted to use it for. Thanks, guys!
Lessons
Unquoted SQL Injections
Most of my previously conducted audits for my employers I had to conduct without code – searching manually for vulnerabilities in a deployed instance. Working directly with code made me a bit overeager and I reported a vulnerability that I thought was especially cool: unquoted SQL injections. I did so in a hurry and without verification and reported a false positive. Unquoted SQL injections are seldom covered. If you haven’t read about unquoted SQL injections, I highly recommend reading the article The Unexpected SQL Injection. An article discussing the exploitation of an unquoted SQL injection can be found on the RIPS blog, titled Guest Post: Vtiger 6.5.0 - SQL Injection.
Bypassing quote()
I went back to check what went wrong and learned something else interesting. There was a method named escape
in dba.class.php, that I had overlooked
132 public static function escape($var)
133 {
134 $dbh = self::dbh();
135 if (!$dbh) {
136 debug_event('Dba', 'Wrong dbh.', 1);
137 exit;
138 }
139 $var = $dbh->quote($var);
140 // This is slightly less ugly than it was, but still ugly
141 return substr($var, 1, -1);
142 }
If I had looked once, I would have noticed that things got quoted and therefore should have been fine, skipping the nuisance for the developers. The interesting part is on the second look in line 141. substr
can break a properly quoted string. While in this case it doesn’t, it is quite nice that RIPS knows - it is worth not just assuming that the method is safe. RIPS also offers configuration options where one can set not automatically discovered validators and sanitizers.
Find Vulnerabilities in Non-Active Code
One of the pitfalls in manual-only testing is that you won’t see functionality that is not configured. Ampache has a function to log successful logins of users. In that function it also logs the user agent.
911 $agent = Dba::escape($_SERVER['HTTP_USER_AGENT']);
This code is never reached, if the default setting of track_user_ip
is not changed to true
. While this instance is safe, there is a lot more functionality, like connecting to other services among other features, which I haven’t yet investigated. They are turned off by default. It seems there is more.
Logic Errors Are Difficult to Find with Static Code Analysis
During the quick manual checks in the beginning, I noticed the app is missing a CSRF token for changing passwords and it also doesn’t ask for the old password. Since I hadn’t worked much with static code analyzers before, their shortcomings were not obvious to me. However, finding them is impossible with this sort of approach due to the fact that they’re logical application errors. Just as a side note, when I wanted to report the change password issues, I noticed, that it had already been reported as issue 539, but this hasn’t been addressed yet.
Same PHP Object Instantiation but Different
My favorite issue, maybe because I haven’t cracked it yet, is a PHP Object Instantiation, something that could be considering a sub-class of PHP Object Injections (POI). Usually when there is talk of a POI vulnerability, one thinks of unserialize and magic functions. This one is different.
Quick excursion: Ampache has multi-user support for playlists. While the playlist is playing, other users can vote for songs or suggest songs. Depending on those votes, the next song will be chosen. This feature is called when choosing democratic as type of playback in the streaming. The following vulnerability is located in the voting feature.
First, the GET parameter type
is received in line 43 of democratic.ajax.php and passed to the add_vote
method.
43 $democratic->add_vote(array(array('object_type' => $_REQUEST['type'], 'object_id' => $_REQUEST['object_id'])));
The method is defined in democratic.class.php.
348 public function add_vote($items)
349 {
350 /* Iterate through the objects if no vote, add to playlist and vote */
351 foreach ($items as $element) {
352 $type = array_shift($element);
353 $object_id = array_shift($element);
354 if (!$this->has_vote($object_id, $type)) {
355 $this->_add_vote($object_id, $type);
356 }
357 } // end foreach
358 } // vote
In line 355 type
parameter is passed to _add_vote
.
396 private function _add_vote($object_id, $object_type = 'song')
397 {
398 if (!$this->tmp_playlist) {
399 return false;
400 }
401
402 $media = new $object_type($object_id);
403 ...
Finally in line 402 the object is instantiated.
When looking for this kind of POI, there is little information on it. I had found this security exchange post and @floyd_ch was so kind to point me to probably the best article on this subject: PHP Object Instantiation. If someone takes a shot at it, I’d be very curious to know if they find a chain that makes this exploitable.
RIPS also found various authenticated reflected XSS (issues 1533, 1532 and 1541) and also authenticated SQLi (issue 1536). The best documentation of those is always right in the tickets themselves. I’ll leave possible exploit chains up to the reader’s imagination.
Organizational Issues
Regarding the organizational aspects of auditing OSS, how do you report a vulnerability to an open source project that doesn’t have a dedicated e-mail address set up? In this case I asked in the mailing list beforehand and they said to just file an issue. It would be really nice to be able to report an issue as “private” so only the reporter and the admins can see the issue. This would be a great feature for Github to implement.
Conclusion
First of all, I’m not done yet, and if something else pops up worth discussing, I will write another blog post.
Mapping a detected vulnerability in the browser to the code, if you are not very familiar with the code base is an interesting activity. Also doing the inverse was fun. Finding something manually and then trying to find the matching finding of RIPS.
It is worth pointing out, that OSS is hard. This project is done completely by volunteers without doing this on their job time. Some of the vulnerabilities were closed quickly. Others are still open. I also have not reported everything yet, giving the developers a chance to close one by one without sending them too many reports at once. It seems like they’ll soon release a last version including the patches and then try to port the project to the Laravel framework.
Thanks to RIPStech who let me play with their code analysis solution to contribute to open source! It is easy to use and has a comfortable GUI, that lets you explore the code very comfortably. It is noticeable that the solution was developed by people who use it themselves - providing features tailored to bug hunters. Last but not least an appeal to security researchers, pentesters, white hats or whatever you call yourselves: If you have some spare time, consider hacking open source software. Let’s do our part and contribute.
Addendum 17/6/19
Since I was told, that some of my points could create confusion to whether RIPS didn’t perform properly - let me clarify: RIPS did an outstanding job finding a total of 165 vulnerabilities and other security issues in 3m26s in 109039 lines of code spread over 531 files. That is after configuring it, so without false positives. Some vulnerabilities have up to seven levels from source to sink. I’m impressed and I thought it was clear, that I’d recommend it to hunt for bugs in PHP.