1. Abusing Predictable Identifiers
1.1. Problem
Unique identification provides the means to look up user data.
Yet if these unique identifiers are easily predictable, it may be
possible for a malicious user to adjust his unique identifier to match
another’s, thus enabling him to view another user’s personal
information. This recipe shows you how to find and use predictable
identifiers.
1.2. Solution
This next example has affected just about every major blog
publishing platform as blog platforms have matured. For the time being,
let’s pick on WordPress; they have long since fixed this particular
problem.
WordPress allows multiple users to post to group-run blogs, but
also allows those users to mark individual posts as private. These
private posts should not be accessible by the public nor by other group
members; they are essentially diary entries.
One particular page was used to craft posts and to re-edit posts
once they had been saved. This page used the following navigational
structure:
post.php?action=edit&post=[post ID]
Each post ID was sequential in nature. If you created a post and
it was assigned ID 503, the next post would be assigned ID 504. These
are easily predictable identifiers. By starting from 1 and iterating
through each post ID, one could view and edit all posts in the order
they were written, no matter which user originally crafted it.
Unfortunately, this also let the user view private posts belonging to
other users.
For instance, user Abe wrote a private post that was assigned the
post ID 123. User Biff browses through all the available posts,
eventually reaching post ID 123.
post.php?action=edit&post=100
post.php?action=edit&post=101
post.php?action=edit&post=102
...
post.php?action=edit&post=123
At this point, user Biff notices that the “Private” flag is marked
for this post and savors the discovery of a secret. Ultimately
unsatisfied, Biff uses the edit capabilities granted via browsing in
this manner and changes the private entry to a public, published post.
User Abe thus has his deepest secrets exposed to the Web—a
confidentiality breach!
The solution in this case is not to randomly assign post IDs. In
this case, predictability was not the problem. Instead, the problem was
that the authorization and access controls were lacking. User Biff
should not have been able to view or edit user Abe’s private posts, no
matter how he arrived at the particular URL.
1.3. Discussion
This is a real issue that resurfaces occasionally with all sorts
of document management software, of which blogs are only a small
example. Private data stored on a publicly accessible service needs to
be well-protected.
While blogging may appear a trivial example, it is common for
systems to assign sequential IDs, yet not explicitly verify the
permissions for a particular document or record. Often developers will
assume that if no link is presented to a user, then the user cannot find
a particular protected record. Finding these protected or confidential
records can be as simple as incrementing IDs repeatedly. Private blog
posts may seem low risk, but this vulnerability has led to leaked
internal memos and other corporate information. Often, IDs will be
encoded so that they might appear random.
This WordPress example was a real bug. We are grateful to the
folks at WordPress who see the benefit of publishing bugs to the public.
You can check this bug report yourself at http://trac.wordpress.org/ticket/568.
This technique might seem trivial, too. Surely the expensive,
professional, automated web testing software would check something like
this, right? Again, the answer may surprise you. Straightforward testing
like incrementing identifiers is trivial to do with a tool like cURL,
but is not done routinely by the automated scanners. Adding it to your
manual or automated tests will improve your security coverage, even if
you routinely use a commercial security scanner.
2. Predicting Credentials
2.1. Problem
Many systems assign user credentials, such as usernames,
passwords, or status, rather than allowing the user to specify their
own. While this is often a security measure to ensure the strength of
user credentials, it can backfire if those credentials are easily
predictable. Learn how to avoid predictable credentials so that your
software does not fall prey to the same trap.
2.2. Solution
This recipe only applies if your application automatically assigns
initial passwords or contains batch scripts to do so during initial
deployment.
Identify how usernames, passwords, or other credentials will be
established. Will usernames be publicly displayed? If so, is it at the
discretion of the user or via a directory?
If usernames are generally accessible, understand that they may be
harvested via a script. An attacker will be able to attain a partial or
complete list of users if the usernames are displayed at the user’s
discretion or in a directory, respectively.
Are passwords assigned in bulk? How are they generated?
If passwords are assigned by incrementing a value or are generated
using the username itself, there is a high chance that the password will
be easily guessable.
Table 1 shows credentials
being issued in bulk. Each email address and password is generated and
sent to the corresponding user.
Table 1. Default passwords based on email
Email
address | Generated
password |
---|
Alice.Bailey@example.com
| exampleAB |
Chad.Daily@example.com
| exampleCD |
Elise.Franken@example.com
| exampleEF |
George.Hart@example.com
| exampleGH |
Although Table 1 shows
that distinct passwords are assigned to each user, anyone who has been
issued a password can see the implicit pattern. If George could infer
Alice’s email address and if he knew she had not changed her password
yet, he could easily take over her account.
2.3. Discussion
While the example given seems rather basic, it is vitally
important not to fall prey to this vulnerability. Predictable user IDs
allow attackers to gain a foothold on your application. Even though the
administrator’s account may not be at risk this way, gaining any legitimate account is often a very
significant first step for an attacker.
Another important point is to consider how easily a person can
request and receive multiple accounts. Many web applications require a
distinct email address for each account. A user who owns her own domain,
however, can typically have an infinite number of email addresses in her
domain. Thus, she could request many different accounts and receive many
different initial passwords, in order to infer the pattern.
One thing we’ve learned is that every time you assume a bug or
vulnerability is just common sense or that nobody would make that
mistake, it will pop up again. So while the above example might seem
like a trivial test, be sure to double-check. For instance, a company we
worked with was celebrating a successful merger. They modified a
shopping cart system to allow each and every employee to select one of
many free gifts. To ensure that each employee only signed up for one
free gift, accounts were created and assigned in bulk. Every single
account was created and mailed out with “password” as the
password.
3. Finding Random Numbers in Your Application
3.1. Problem
Many aspects of an application’s security will depend on the
fact that an adversary cannot reasonably guess certain values in the
system. You probably depend on encryption keys, session IDs, and
possibly nonces. In this recipe we just try to identify where random
values are being used in your application.
3.2. Solution
If you have surveyed your application by spidering it , then you have a starting
inventory of pages to look at. Rather than examine those pages one by
one, you should also consider which actions in your application are most
important to perform correctly (e.g., those involving money, data
access, system integrity). Look at the parameters exposed in the body of
the page, in the cookie, and in the URL. In particular, look at:
In the body of the page
Session state like ASP.NET’s __VIEWSTATE in hidden form fields
(e.g.,<input type="hidden" name="__VIEWSTATE"
value="AAA...">).
Unique identifiers, like customer IDs, guest IDs, etc.,
also in hidden form fields.
Checksum values in hidden form fields or in JavaScript
variables.
JavaScript variables and functions that control behavior
of the application, such as setUserID(215);.
In the cookie
Session IDs. They almost always have some variation or abbreviation of
the word “session.”
Unique identifiers for the visitor, their account, or
other resources.
Representations of things like roles, groups, or
privileges (e.g., groupid=8).
Indicators of workflow. Things like status=5 or state=6 or next=225.
In the URL
Session IDs, as in the cookie.
Unique identifiers, also as you might find in the
cookie.
Representations of things like resources (e.g., msgid=83342).
Indicators of workflow. Things like authorized=1.
Remember that many of these things will be encoded with Base 64,
URL encoding, or both.
3.3. Discussion
Many applications that use randomness don’t really rely on the
unpredictability of that randomness. Most uses of randomness do not have
the same impact as they might in an online poker game, for instance. If
your online chat program randomly picks an avatar for participants, does
it really matter that a certain avatar is chosen with a little more
likelihood than another?
Once you have found the random numbers in your application, you
need to ask questions to help you determine whether the randomness and
unpredictability is vital to your application. You might ask:
How much damage could a user do if he knew how to predict this
random number?
How upset would one user be if she found out that someone
could predict the next outcome?
How bad would it be if two documents (users, resources, links,
etc.) were assigned the same value?
In some cases, there won’t be a major security failure. In other
cases, failure will be catastrophic. Confidential data will be leaked,
users will see each other’s sessions, or resources might be modified in
unexpected ways.