Author Archives: Johannes Ullrich

The Day the World Will End

5
Filed under Uncategorized

With a new movie coming out about how the world will end with the (supposed) end of the Mayan calender, I figured it would be nice to get a list of software related “end of calender” issues:

Dec. 31st 1999, 23:59:59 GMT
The famous Y2k issue. We made it… (so far ;-) )

Jan. 10th, 2010, 10:10:00 GMT
“Binary Armageddon Day”. The binary representation, 1010011010 translates to ‘666′. Also, the date only includes 1s and 0s (other then the ‘2′ in the year). (thx Gadi. Also see Gadi’s facebook group about this issue ). If you are on facebook, you can find the group here: http://www.facebook.com/pages/Binary-Armageddon-Day

Dec. 21, 2012
end of Mayan calendar. Just listed here because everybody is talking about it. Should not affect software (other then the fact that the world will end that day).

Feb. 7th 2036, 6:28:16 GMT
The last date that can be expressed using “ntp”. ntp is a protocol used to synchronize clocks on the internet. The ntp date starts on Jan 1st 1900 and is expressed in 64 bits. The first 32 bits are used to indicate the number of seconds since Jan 1st 1900, the remaining bits are used as fractional seconds.

Jan. 19th 2038, 03:14:07 GMT
The end of the Unix epoch. Unix uses a 32 it signed number to express time. ‘0′ is January 1st 1970. The last date that can be expressed using unix time is Jan 19th 2038. After that… who knows? This can already be a problem. Imagine you are a bank and handing out 30 year mortgages?

Dec. 31st 9999, 23:59:59 GMT
The end of 4 digit years. Well, we got a while until that will happen.

Got any other dates of note? Let us know!

Go Google Yourself

0
Filed under defense

Regular spidering should be part of a web applications maintenance regiment. Of course, there are plenty of free and commercial tools to do it for you. Vulnerability scanners will typically come with a powerful spider function. On the other hand, public search engines like Google already do most of the work for you. In particular Google does provide you with a nice insider view into your web application. All you need to do is register. In order to register, you first need to sign up at http://www.google.com/webmasters/tools/ . Next, you need to show that the site is actually yours. You may do so by adding a special file to the site, or by adding a meta header with a specific code. The process usually only takes a couple minutes.

But why should you bother?

First of all, Google webmaster tools will provide you with more insight into how Google indexes your page. The section I am looking at first is “crawl errors”. It will tell you if any pages were not found, or if pages timed out. You can also check how robots.txt limited the crawl.

The “HTML Suggestions” section will tell you about search related errors in your HTML code. For example, inefficient use of META tags.

A common question we get at the Internet Storm Center is how to remove a URL from Google’s index. Webmaster tools will again help with that. The feature is a little bit hidden, but if you select “Site Configuration” and “Crawler Access”, you will see the “Remove URL” feature. This is probably the fastest way to remove a URL from Google.

Finally, the “Labs” section: One feature Google is currently experimenting with is malware detection. If Google found malware on your site, it will tell you about it. Sure, this is not the ideal way to find out about malware on your site. But better late then never.

I think Google’s webmaster tool is a “must have” for everybody running a website. While not strictly a security tool, it does help with security (and of course search engine optimization).

Response: Pentesting Coverage.

1
Filed under Pentest

The person I had the IM discussion with was Daniel Miessler. He responded in his own blog, and sent me the excerpt below as a response. Thanks for the offline and online comments to far. Certainly an interesting topic to discus!

Read More »

Pentesting: Do you need “coverage” ?

3
Filed under Pentest, Uncategorized

Last week, I had a discussion via instant messenger. The discussion essentially evolved around the need for coverage in a pentest. It has always been my conviction that a pentest should find as many problems as possible. In my opinion, the pentest is not over once I got root on the system. In many ways, this is where it starts.

I think it is a matter of pentesting philosophy. I see myself primarily as a coder. A pentest is for me part of my software testing regimen. Like my other tests, code coverage is of utmost importance. I don’t consider my application working until it has been fully and thoroughly tested. A pentest is just another, very special, test I subject my code to. As a result, I am not a big fan of “black box” testing, or “early dawn raids” as I call them. They may find a problem, but the tester will typically waste a lot of time finding and exploiting vulnerabilities, which may have been trivial to exploit with a bit of source code, or after talking to the developer. I rather have the pentester spend their precious time on going after yet another possible vulnerability.

I do understand that a pentest does not find all bugs, but the aim should be to do just that. I strongly believe that a pentester should be held to the same standards as a developer. Would you accept software that works once? Code that only has one bug fixed? Just because it is hard, doesn’t mean that we shouldn’t attempt it. In many ways, this is what I enjoy about being a developer: Hard problems.

Is it possible to come close to the goal? I think it is. All of this is based on a good framework and to apply this framework thoroughly. You do your recognicance, and don’t stop at the first web application you find, unless the scope of the project limits you. Next, you map out the web application, and try to find not just all URLs or “pages”, but all features. A feature you miss in the mapping phase will be a feature you miss in your test. Next, you discover vulnerabilities. And finally, you try to exploit them. Once you manage to exploit a vulnerability, you are likely to find more content and your process starts all over again.

Sounds boring and tedious? Yes it is! If you don’t know how to script. I say this a lot: If you don’t script, you will soon be replaced by a script. If you want to distinguish yourself as a pentester, you will have to understand your tools well enough to extend them and build upon them.

Various PHP and MySQL pitfalls

2
Filed under Uncategorized

This is a short post, to summarize some of the issues I see with PHP code and the use of MySQL. Not too many people know about these pitfalls and they are given rise to some of the more subtle security issues:

1 – “SQL Overflow”

If a value you insert into a column is too large, it is truncated silently. This can lead to security issues if you don’t validate that the submitted string is of the right length.

2 – “Trailing White Space Ambiguity”

Trailing white spaces are removed by MySQL silently. For example, these two queries retrieve the same result:

select role from user where username='Admin';
select role from user where username='Admin   ';   (note the space at the end).

3 – Unbalanced comments

Older versions of MySQL allow /* to be used unbalanced. For example,

select now() /* test

will work. Newer versions of MySQL require balanced comments (unbalanced was always “illegal” according to the documentation

4 – php ‘rand()’ function returns bad results

If the argument exceeds the maximum integer, you will get not-so random numbers back. This one depends a bit on the version of PHP you are using. But you will not get an error. Instead, you will get negative numbers, or numbers that are not random (e.g. only last couple of digits change).

5 – MySQL “–” comments require a white space

In order to use “–” as a comment, it has to be followed by a whitespace.

select now() –test  will fail
select now() — test will work

You don’t have to use a space. A tab will work just fine and evades some filters.

Got some to add? Use the comments ;-)

Session Attacks and PHP – Part 2

1
Filed under Sessions, php

Yes, I will talk in this article about why it is not good to leave your session files in /tmp. But first, allow me to follow Jason’s lead and talk about the attacks he discussed in Part 2 of his ASP.NET article. I will keep it short :)

Session fixation isn’t really that much of a problem as long as you stick with a few simple principles. Remember we called this blog “App Sec Streetfighter“? Its about simple and reproducible techniques that work while under attack. So lets keep it simple:

  1. Use only cookies to transport the session token. This considerably raises the bar on session fixation. The attacker now has to set a cookie which isn’t easy at all.
  2. Change the session ID whenever the users state changes (logged in vs. logged out).
  3. Change the session ID every so often. (every X pageviews, every X minutes).

In order to change the session id, PHP offers a simple comand, session_regenerate_id, just add it to your header and you will get a new session ID on every page. If that works for you: great!. If it causes performance issues, then add some logic to limit the life time of sessions or add the session_regenerate_id whenever the user logs in and out.

One important caveat for session_regenerate_id: It uses one parameter. Set it to “true“. The default is “false“, which will leave the old session intact.

Now to the part everybody appears to be waiting for: Why not /tmp ?

/tmp is a convinient location for session data. Every Unix system I have seen has a /tmp directory that is globally readable and writable. But this is just the problem for session data. The file name itself gives away the session ID. A listing of all session files will give an attacker a list of all valid sessions. In most dedicated web server scenarios, the risk of leaking /tmp file names is low. But the defense is simple enough to “just do it” ™ :

  • Create a directory which will only hold session data (let’s call it /tmp/phpsessions).
  • This directory should NOT be owned by the apache user, but by root and the apache user’s group.
  • Set permissions to 770. Sadly, 760 is not possible. Theoretically, it should work. PHP (the web server) doesn’t really need to be able to get a list of valid sessions. But sessions will fail if you set the permissions to 760.

I typically prefer to keep my sessions in a database, less for security reasons but more for scalability. Memcached sessions is an other great way to get sessions to scale.

Session Attacks and PHP

7
Filed under Sessions, php

This blog is of course inspired by Jason’s ASP .Net blog. I figured as the PHP guy in the group, I may as well cover what he did for .Net from the PHP side.

PHP’s default session mechanism is rather simple and effective. The php.ini file configures how sessions work. Many of the parameters can be overridden within your PHP code, or .htaccess files can be used to create more fine grained configurations for particular directories. The session module is part of PHP by default, but can be disabled at compile time. By default, the session data is saved in files. The directory the session data is stored in is again configured in php.ini and defaults to /tmp (not the best choice, but more about that in a later blog).

Much of the session module can be adjusted, or custom code can be used to store session data in a database.

Like Jason noted for .Net, sessions and authentication are two different things in PHP as well. It is up to the developer to use sessions to store the user’s identifier.

Let’s follow Jason’s outline, and talk about session fixation first!

Session Fixation

PHP provides a number of defenses to prevent session fixation. First of all, the lifetime of a session can be limited in php.ini, or by simply using runtime configuration directives. The one “gotcha” here is that some developers mistake the cookie lifetime for the session lifetime. The session lifetime is the part that counts. It is adjusted via gc_maxlifetime parameters. The “gc” (Garbage Collection) parameters are a bit hard to grasp for someone new to PHP. First of all, it is important to understand that nothing happens in PHP unless a page is rendered. Session data will survive indefinetly if the server is idle. Whenever a page is displayed and a session is initiated, the garbage collection functions run and clean up old session data. There are a total of three parameters that determine how this is done:

gc_probability and gc_divisor: How likely is it that the garbage collection is performed. The probability is calculated as gc_probability/gc_divisor. The default is 1/100, which may be a bit a lot probablility if you only have 100 page views per hour. But if you have 100 page views per second, this is perfectly fine.

gc_maxlifetime: This parameter defaults to 1440 seconds (15 minutes). After 15 minutes of inactivity, the session is considered for garbage collection.

So in short: Using default parameters, the session is deleted within 100 page views after the 15 minute time out expired, assuming that each page view initiates a session.

What does this mean for session fixation and reusing session data: The time window is about 15 minutes by default, which is appropriate for most applications.

PHP does allow for a “referrer check”. Sessions will only be considered if the referrer contains the string defined using the “referer_check” configuration parameter. By default, this parameter is empty. This is a very powerful way to block many session fixation attacks.

It is also rather simple to change the session ID in PHP. session_regenerate_id will create the new session and move the data. Take care to set the optional parameter to “true”. Otherwise, the old session will not be deleted. For the paranoid, it is as easy as adding “session_regenerate_id(true)” to your header file. (and wait for entropy starvation to set in ;-) ).

Now what about the attacker obtaining a valid session id? PHP allows for sessions to be delivered via the URL, or cookies. URL based sessions are disabled by default via the “use_only_cookies” parameter. The cookie itself is configured via php.ini.

Cookies can simply be configured as http_only and secure via php.ini. No need for extra code on this one.

Another item not discussed (yet?) by Jason is the session ID generation. PHP’s session ID generation is reasonably secure by default. It is possible to define the source of the entropy used to create sessions (/dev/random or /dev/urandom), how many bytes of entropy are used and which hash function is used. Plenty of ways to mix it up!

There is a lot more to talk about when it comes to PHP sessions. Let’s see what Jason is up to next! Also note that the facts above are valid for later versions of PHP (5 and later). Maybe I should also write about suhosin one of these days, an excelent PHP hardening module.

My Top 6 Honeytokens

6
Filed under Uncategorized

A few years ago, I was looking for a new developer to join our team. Of course, the hard part was to find a developer that was up to the task. I don’t believe much of what people say in their resumes, so I rather had them show me a site they coded and give me permissions to take a closer look. Many of the sites failed miserably after only some minor probing. The result was a small guide I published to summarize my experience, Web Application Auditing over Lunch. Sadly, a lot of attacks are just so simple. But then again, lets see how we can turn this around and use it against the attacker. An attacker will likely try to use simple tricks just like the once outlined in the guide. As a counter measure, I am using a set of “honeytokens”. Simple tripwires to alert me of an attacker.

  1. Don’t hand session credentials to automated clients: Whenever a browser identifies itself a “wget”, or a search engine, don’t bother setting a session cookie for them. They shouldn’t log in. Yes, it is easy to fake the user agent. But many attackers don’t bother.
  2. Add fake admin pages to robots.txt: Add a fake admin page as “Disallowed” to your robots.txt file. We all know of course that robots.txt should not be used as a security tool. But many websites still use it that way and as a result, attackers use it as a road map to attack a site. Whenever someone hits your fake “admin” page, you know they are up to no good.
  3. Add fake cookies: Add a fake “admin” cookie and set it to “FALSE”, or “No”. This is a classic mistake attackers are looking for. But you are of course not using this cookie to assign admin privileges. Instead, you detect attacks whenever the cookies value changes.
  4. Add “spider loops”: Little redirect loops to send spiders in a loop. Be nice, and add “NOFOLLOW” tags to not annoy legit search engines too much. See if anybody falls for it. It is kind of like a La Brea tarpit for web application vulnerability scanners.
  5. Add fake hidden passwords as HTML comments: On your login page, add a comment like <!– NOTE: developer test login, user: alkhewr password: 234kjw –> … Wait for someone to use it :-)
  6. “Hidden” form fields: This is different from the <input type=”hidden”> form field. Instead, add a regular form field <input type=”text”> but set the style to “display: none” . That way, the form field will not be visible to normal browsers. But vulnerability scanners will happily fill it in. Note that this can be a problem for “audio browsers” used by the blind. You may want to pre-fill the form with something like “do not change this field”.

Now isn’t that all just about hiding more serious vulnerabilities? Security through obscurity? Sure … it is… but it works. In my opinion, a good defensive technique is easy to implement but hard to bypass. These techniques only take minutes to implement, but break most automated tools and will cost hours of the attackers time.

You have to decide for yourself what to do once you detected an attacker. Maybe just hope for the attacker to go away? A more aggressive, but dangerous approach is to automatically shun attackers. It all depends on how much you are willing to lock out the wrong person. In particular the hidden URLs in robots.txt are dangerous as someone who discovers the trick my now spam this hidden URL to your customers and lock them out of the side if they click on it. If you decide to shun attacker: Have a plan ready to mass “unblock” a large number of false positives.

Got your own tricks like that? Let us know and write a comment below!

How can I tell if my password is encrypted?

7
Filed under Authentication

For most websites, we don’t have the source code available. As a user, we more or less trust the site is doing “the right thing”, or well, we just use a throw away password that we accept to be compromised.

Sometimes, it is obvious. For example the site is sending you your password in the clear. Other times, it is less obvious. Usually, if a site is imposing limits to your password, like the length or it doesn’t allow certain characters, you can guess that your password will be stored in the clear.

The reason is simple: If the password is hashed, then it doesn’t matter how long it is, or what characters it uses. It will always end up as a fixed length hex string. SHA1 creates 20 bytes (40 hex characters), MD5 creates 16 bytes (32 hex characters). The one exception may be if the code uses a database function to do the hash. For example some pseudo code:

$hash=md5($password);
update users set passwordhash='$hash'

In this case, it doesn’t matter if the password includes a single quote or whatever. However, it could matter in the following snippet:

update users set passwordhash=sha1($password)

(Needless to say, I wouldn’t use dynamic SQL like this, but this is the topic of another blog post).

The length of the password is typically limited, if stored unencrypted, because the database table was defined with a certainl length for this field. Maybe even worse: If you are not told about it, your password maybe truncated “silently”.

[digg=http://digg.com/security/How_to_tell_if_a_website_encrypts_your_password]

An Encrypted Password Experiment

9
Filed under Uncategorized

Jason’s blog post (”How Not to Do Website User Registration“) certainly attracted a lot of comments! I think articles like this exceed my expectations about this blog. Back before we had “blogs”, we had the Internet Storm Center diaries, which are still going strong. I always felt that the best diaries are the diaries that don’t have all the answers, but diaries that stimulate discussion and feedback.

With that comes the need to sometimes step back and consider that I don’t have all the answers. In particular, we have to keep in mind that we don’t secure websites just for themselves, but we secure websites so they can fulfill a function. 32 characters random passwords, which have to be changed daily, are just not the solution. I am always interested in ways to make software more secure without impacting usability. The way I sometimes put it: “Security is not about preventing a breach, security is about staying in business”.

Now how does this apply to website registration and encrypted passwords? A dilemma I always had: I hash all my passwords in the database. I use SSL to transmit the password and cookies. But I hardly ever encrypt the password itself as it is transmitted from the browser to the server. You may say: Wait! We got SSL for that! Right, but remember SSL is just protecting the password. The website still ends up processing the clear text password. What is needed is a way to encrypt the password on the browser before passing it to the server.

The Internet Storm Center is one site that I like to use for experiments like that. However, the only way I know how to encrypt passwords is to use Javascript. I also have a rule not to force users to turn on Javascript. I may use it for the usability improvements, but the site has to work with Javascript turned off. I know this is an anachronism in today’s Web 2.0 world, but you should see some of the hate mail we get just for using cookies.

HTTP actually has a decent way to encrypt passwords, called Digest authentication. But it has other problems and is not really an option for many sites.

Here is the solution I came up with: Make it optional. If Javascript is enabled, it is used to encrypt the password and the original clear text password is not sent. If Javascript is not enabled, the password is sent “as before”.You can check out the Javascript code at https://isc.sans.org/login.html .

I am using a SHA1 hash. Sadly, Javascript doesn’t have a SHA1 function natively, so I am using an implementation of the SHA1 algorithm in Javascript that appears to work pretty well (see http://isc.sans.org/js/sha1.js).  The database stores the sha1 hash of e-mail address and password (sha1(email+password)). I could just pass this hash, but I well, since I hash the password anyway I want to do it right and avoid reply/”pass the hash” style attacks. So the server sends a “nonce” first. The nonce is a random value. The client now appends the nonce to the password hash and hashes it again. So the final hash function is:

sha1(sha1(email+password)+nonce)

The server stores the nonce in a session, and can do the same calculation using the password hash stored in the database.

This way the server is only exposed to the original password during the account creation and whenever the password is changed. You may ask why anybody would care about their ISC password… well, maybe if they use the same password for their online banking, I just don’t want to touch it. Information becomes a liability pretty quickly in these cases and sometimes you have to protect the users from themselves.

So here my questions:

- Is this effort worth it? Am I fighting a real threat or am I just an obnoxious security fanatic?
- Are there other solutions?
- What did I do wrong? Any holes in this scheme? (I do consider sha1 good enough for this case)

[digg=http://digg.com/security/Javascript_Password_Encryption_for_Web_Applications]