Showing posts with label Bug. Show all posts
Showing posts with label Bug. Show all posts

Monday, December 17, 2012

Persistence is Key, Another Bug Hunt

Introduction:
There are few things I find more frustrating than looking for bugs and not finding a single one. I've seen and used buffer overflows and format string vulnerabilities in war games, I've even seen some of these bugs in real applications. Sometimes I choose a piece of software to assess and it's just rock solid.

But that's not a reason to stop.

I am specifically talking about Cerberus FTP Server which is, in my opinion, very well written - and it showed in that it provided me a great deal of difficulty in finding bugs through binary analysis like I have done in the past. I did get lucky enough to find a couple of bugs though, but only once I shifted my paradigm. One set is fairly trivial and the other is not as trivial and kind of hard to get to fire. This was all performed using Cerberus FTP Server v5.0.5.1.

Usually I'd discuss the whole process of my bug hunt, but in this case it was a long time to get so far. I started by seeking out printf/buffer overflow vulnerabilities, running some fuzzers and the like. All to no avail. An analysis of the binary showed a good indication of why:

This call to sprintf is of the "_s" variant. This means it is "security enhanced", as described here. I checked every single possibility for *printf and found very few potentially viable options. In part compilers now have warnings for these sorts of bugs, but also when the developer is aware of them too it's that much worse. Of the possibilities I did find I was later informed they exist in unused code sections which are presently unreachable. So whether or not they'll even be available is unknown much less vulnerable too, especially after mentioning them!

Moving Along:

Just to get into it, I gave up on the classics and moved to the modern world - web based attacks. This is an area of vast opportunity and means it's often fruitful. The bugs can slip in through many avenues, sometimes even shrouded under the guise of guessed safe practices.

Let's begin with the trivial bug. It is a Cross Site Scripting (XSS) scripting bug and it can be located in the "/servermanager" page of the web admin interface for Cerberus FTP Server. This interface is by default disabled. One enabled an administrative user may login at "http://localhost:10000/" Once the administrator is logged in they may find a link to the server manager page in the left menu. Or you may use "http://localhost:10000/servermanager"

It should look, something like this:
Select the "Messages" tab and you will be presented with the vulnerable page - though if the server has already been exploited, those should have already fired. The messages page looks like so:
Each of the message fields may be exploited trivially like so:
</textarea><script>alert('trivial xss');</script>

Click update to save the message, then reload the server manager page for the effect:

This as fun and all, but as one should note there is only 1 administrative user for the web interface and thus if this XSS bug is being leveraged - you probably have larger problems. None the less, this bug has been fixed and the latest version of Cerberus FTP has the corrections.

And a Little Harder:
Now for the more difficult bug. Having found a trivial XSS bug I now know that at least some fields may or may not be properly escaped. So the goal is to find other methods of interacting with the web interface that may not require authentication. Most of the options are available only to administrative users, and after exploring all the available options I finally decide to attempt to attack the "http://localhost:10000/log" page

First thing to note is the log is empty. Javascript is required for using the log page and it is on an 8 second update cycle. When log entries are shown you have approximately 2-3 seconds to review it before they are cleared. This plays a role in making this bug irritatingly difficult to fire. It should also be noted that the data transfer from the servers are in fact html encoded, hiding this bug from view in post-mortem analysis. No time like the present.

The log page is shown here, empty, waiting on it's 8 second cycle:
Normal usage will fill the log with events, but we're most curious about usage which does not require any form of authentication. To generate some "usage" traffic I write application in python (test.py):

import sys
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('192.168.0.200', 21)
sock.connect(server_address)
data = sock.recv(128)
print >>sys.stderr, '%s' % data
user = "USER testuser\r\n"
sock.sendall(user)
data = sock.recv(128)
print >>sys.stderr, '%s' % data
quit = "QUIT\r\n"
sock.sendall(quit)
sock.close()
Then I execute this from bash using a simple for loop, and I note that it returns the XSS banner we've already exploited.
for each in {1..100}
do
python test.py
done
We execute this loop and look for the results, sometimes I had to run the script multiple times to get it to show:
Obviously we have some control over what is shown in this log even though we have not authenticated in any way. The real question is, does this render in a way which might be dangerous. To test, we merely adjust our 'testuser' to be something more effective fun:

USER <div onmouseover="alert('xss');" />

And run the loop again, because we're using the onmouseover - we'll need to fire this by mousing over the areas where the divs will be. If it works it should be immediately apparent.

And a Cross Site scripting bug is discovered. As with the other one I am told this bug has been patched and is no longer an issue. Special thanks to Grant @ Cerberus FTP for the fixes and extremely timely support given, this has been the best company I've had the pleasure to deal with so far in regards to security related matters.

For reference these bugs were assigned CVE-2012-6339.

See you next time and until then, Hack Safe, Hack Legal, but most of all Hack Fun!

Monday, September 17, 2012

SQL and finding a Floating Point Error

So we have some SQL which goes out and grabs some data. Then we use that data for processing. Pretty normal. But the fun part is, sometimes we reuse that data in the same SQL. No this is not about stored SQL Injection, it's really just about a dumb bug we ran into recently, but a reuse bug would be pretty fun too.

So we have a list of people, and we've compared this list of people to another list of people using the Levenshtein Algorithm. Then we store the results of that comparison in the database so some users can check it out later, a time/memory trade off which has actually been fairly useful (especially with the speed increases we've given it over the years).

Now when a user loads this list of comparisons, we track what the current "match_score" is. This is the decimal percentage difference between the two people's information. Weighted to our liking.  So our application gets the match_score:
SELECT TOP 1 * FROM People_Comparisons P WHERE ID = 13689385
Return: 13689385 |  0.214736842105263
From the perspective of the application, and the database, ID is effectively random. We sort the results of comparisons by the match_score then by the ID, so that when doing reviews we're looking at each ID (x) in the subset of records with the same match_score (y). But the point is, the ID is sorted only within each match_score across the domain of Y values. To get the next record we run a query with the currenct ID and and match_score, N(x,y), which uses the following SQL to return the next record of interest:
SELECT TOP 1 * FROM People_Comparisons P WHERE ID= 13689385 AND MATCH_SCORE > {0}
In the above {0} is replaced with the previously retrieved match_score, in this case the value:
0.214736842105263
Now some experienced database type individuals probably already see the problem. But to less experienced individuals, this can be quite the snake-bite. Because when we run the SQL we have a somewhat unexpected result:

SELECT TOP 1 * FROM People_Comparisons P WHERE ID= 13689385 AND MATCH_SCORE > 0.214736842105263
Return: 13689385 |  0.214736842105263
Obviously there is a mistake, we've used the same value  (0.21473...) and it seems impossible that this SQL should return the same row when we're expecting a row with a match_score greater than that value! Enter Floating-Point Precision.

The quick rundown of it is this: What the database reports is a truncated version of the raw stored value. We have 15 significant figures displayed, but the database is storing much more data than this. This truncation gives us a raw binary value which is different than that which is stored - and in this case, less than what is stored. Thus, the next greater match_score, after truncation/rounding, is the same match_score! To solve this I select the value of the match score into a SQL variable then us this value to maintain the full precision of the data type and reuse this variable for comparisons.