12 min read

Source code revelation

Normally, requesting a file with a .php extension will cause mod_php to execute the PHP code contained within the file and then return the resulting web page to the user. If the web server is misconfigured (for example if mod_php is not loaded) then the .php file will be sent by the server without interpretation, and this can be a security problem. If the source code contains credentials used to connect to an SQL database then that opens up an avenue for attack, and of course the source code being available will allow a potential attacker to scrutinize the code for vulnerabilities.

Preventing source code revelation is easy. With response body access on in ModSecurity, simply add a rule to detect the opening PHP tag:

Prevent PHP source code from being disclosed
SecRule RESPONSE_BODY "<?" "deny,msg:'PHP source code 
disclosure blocked'"

Preventing Perl and JSP source code from being disclosed works in a similar manner:

# Prevent Perl source code from being disclosed
SecRule RESPONSE_BODY "#!/usr/bin/perl" "deny,msg:'Perl 
source code disclosure blocked'"

# Prevent JSP source code from being disclosed
SecRule RESPONSE_BODY “<%” “deny,msg:’JSP source code
disclosure blocked'”


Directory traversal attacks

Normally, all web servers should be configured to reject attempts to access any document that is not under the web server’s root directory. For example, if your web server root is /home/www, then attempting to retrieve /home/joan/.bashrc should not be possible since this file is not located under the /home/www web server root. The obvious attempt to access the /home/joan directory is, of course, easy for the web server to block, however there is a more subtle way to access this directory which still allows the path to start with /home/www, and that is to make use of the .. symbolic directory link which links to the parent directory in any given directory.

Even though most web servers are hardened against this sort of attack, web applications that accept input from users may still not be checking it properly, potentially allowing users to get access to files they shouldn’t be able to view via simple directory traversal attacks. This alone is reason to implement protection against this sort of attack using ModSecurity rules. Furthermore, keeping with the principle of Defense in Depth, having multiple protections against this vulnerability can be beneficial in case the web server should contain a flaw that allows this kind of attack in certain circumstances.

There is more than one way to validly represent the .. link to the parent directory. URL encoding of .. yields % 2e% 2e, and adding the final slash at the end we end up with % 2e% 2e% 2f(please ignore the space).

Here, then is a list of what needs to be blocked:

  • ../
  • ..% 2f
  • .% 2e/
  • %  2e%  2e% 2f
  • % 2e% 2e/
  • % 2e./

Fortunately, we can use the ModSecurity transformation t:urlDecode. This function does all the URL decoding for us, and will allow us to ignore the percent-encoded values, and thus only one rule is needed to block these attacks:

SecRule REQUEST_URI "../" "t:urlDecode,deny"

Blog spam

The rise of weblogs, or blogs, as a new way to present information, share thoughts, and keep an online journal has made way for a new phenomenon: blog comments designed to advertise a product or drive traffic to a website.

Blog spam isn’t a security problem per se, but it can be annoying and cost a lot of time when you have to manually remove spam comments (or delete them from the approval queue, if comments have to be approved before being posted on the blog).

Blog spam can be mitigated by collecting a list of the most common spam phrases, and using the ability of ModSecurity to scan POST data. Any attempted blog comment that contains one of the offending phrases can then be blocked.

From both a performance and maintainability perspective, using the @pmFromFile operator is the best choice when dealing with large word lists such as spam phrases. To create the list of phrases to be blocked, simply insert them into a text file, for example, /usr/local/spamlist.txt:

viagra
v1agra
auto insurance
rx medications
cheap medications
...

Then create ModSecurity rules to block those phrases when they are used in locations such as the page that creates new blog comments:

#
# Prevent blog spam by checking comment against known spam
# phrases in file /usr/local/spamlist.txt
#
<Location /blog/comment.php>
SecRule ARGS "@pmFromFile /usr/local/spamlist.txt" "t:
lowercase,deny,msg:'Blog spam blocked'"
</Location>

Keep in mind that the spam list file can contain whole sentences—not just single words—so be sure to take advantage of that fact when creating the list of known spam phrases.

SQL injection

SQL injection attacks can occur if an attacker is able to supply data to a web application that is then used in unsanitized form in an SQL query. This can cause the SQL query to do completely different things than intended by the developers of the web application. Consider an SQL query like this:

SELECT * FROM user WHERE username = '%s' AND password = '%s';

The flaw here is that if someone can provide a password that looks like ‘ OR ‘1’=’1, then the query, with username and password inserted, will become:

SELECT * FROM user WHERE username = 'anyuser' AND password = '' 
OR '1'='1';

This query will return all users in the results table, since the OR ‘1’=’1′ part at the end of the statement will make the entire statement true no matter what username and password is provided.

Standard injection attempts

Let’s take a look at some of the most common ways SQL injection attacks are performed.

Retrieving data from multiple tables with UNION

An SQL UNION statement can be used to retrieve data from two separate tables. If there is one table named cooking_recipes and another table named user_credentials, then the following SQL statement will retrieve data from both tables:

SELECT dish_name FROM recipe UNION SELECT username, password 
FROM user_credentials;

It’s easy to see how the UNION statement can allow an attacker to retrieve data from other tables in the database if he manages to sneak it into a query. A similar SQL statement is UNION ALL, which works almost the same way as UNION—the only difference is that UNION ALL will not eliminate any duplicate rows returned in the result.

Multiple queries in one call

If the SQL engine allows multiple statements in a single SQL query then seemingly harmless statements such as the following can present a problem:

SELECT * FROM products WHERE id = %d;

If an attacker is able to provide an ID parameter of 1; DROP TABLE products;, then the statement suddenly becomes:

SELECT * FROM products WHERE id = 1; DROP TABLE products;

When the SQL engine executes this, it will first perform the expected SELECT query, and then the DROP TABLE products statement, which will cause the products table to be deleted.

Reading arbitrary files

MySQL can be used to read data from arbitrary files on the system. This is done by using the LOAD_FILE() function:

SELECT LOAD_FILE("/etc/passwd");

This command returns the contents of the file /etc/passwd. This works for any file to which the MySQL process has read access.

Writing data to files

MySQL also supports the command INTO OUTFILE which can be used to write data into files. This attack illustrates how dangerous it can be to include user-supplied data in SQL commands, since with the proper syntax, an SQL command can not only affect the database, but also the underlying file system.

This simple example shows how to use MySQL to write the string some data into the file test.txt:

mysql> SELECT "some data" INTO OUTFILE "test.txt";

Preventing SQL injection attacks

There are three important steps you need to take to prevent SQL injection attacks:

  1. Use SQL prepared statements.
  2. Sanitize user data.
  3. Use ModSecurity to block SQL injection code supplied to web applications.

These are in order of importance, so the most important consideration should always be to make sure that any code querying SQL databases that relies on user input should use prepared statements. A prepared statement looks as follows:

SELECT * FROM books WHERE isbn = ? AND num_copies < ?;

This allows the SQL engine to replace the question marks with the actual data. Since the SQL engine knows exactly what is data and what SQL syntax, this prevents SQL injection from taking place.

The advantages of using prepared statements are twofold:

  1. They effectively prevent SQL injection.
  2. They speed up execution time, since the SQL engine can compile the statement once, and use the pre-compiled statement on all subsequent query invocations.

So not only will using prepared statements make your code more secure—it will also make it quicker.

The second step is to make sure that any user data used in SQL queries is sanitized. Any unsafe characters such as single quotes should be escaped. If you are using PHP, the function mysql_real_escape_string() will do this for you.

Finally, let’s take a look at strings that ModSecurity can help block to prevent SQL injection attacks.

What to block

The following table lists common SQL commands that you should consider blocking, together with a suggested regular expression for blocking. The regular expressions are in lowercase and therefore assume that the t:lowercase transformation function is used.

SQL code Regular expression
UNION SELECT unions+select
UNION ALL SELECT unions+alls+select
INTO OUTFILE intos+outfile
DROP TABLE drops+table
ALTER TABLE alters+table
LOAD_FILE load_file
SELECT * selects+*

For example, a rule to detect attempts to write data into files using INTO OUTFILE looks as follows:

SecRule ARGS "intos+outfile" "t:lowercase,deny,msg:
'SQL Injection'"

The s+ regular expression syntax allows for detection of an arbitrary number of whitespace characters. This will detect evasion attempts such as INTO%20%20OUTFILE where multiple spaces are used between the SQL command words.

Website defacement

We’ve all seen the news stories: “Large Company X was yesterday hacked and their homepage was replaced with an obscene message”. This sort of thing is an everyday occurrence on the Internet.

After the company SCO initiated a lawsuit against Linux vendors citing copyright violations in the Linux source code, the SCO corporate website was hacked and an image was altered to read WE OWN ALL YOUR CODE—pay us all your money. The hack was subtle enough that the casual visitor to the SCO site would likely not be able to tell that this was not the official version of the homepage:

ModSecurity 2.5

The above image shows what the SCO homepage looked like after being defaced—quite subtle, don’t you think?

Preventing website defacement is important for a business for several reasons:

  • Potential customers will turn away when they see the hacked site
  • There will be an obvious loss of revenue if the site is used for any sort of e-commerce sales
  • Bad publicity will tarnish the company’s reputation

Defacement of a site will of course depend on a vulnerability being successfully exploited. The measures we will look at here are aimed to detect that a defacement has taken place, so that the real site can be restored as quickly as possible.

Detection of website defacement is usually done by looking for a specific token in the outgoing web pages. This token has been placed within the pages in advance specifically so that it may be used to detect defacement—if the token isn’t there then the site has likely been defaced. This can be sufficient, but it can also allow the attacker to insert the same token into his defaced page, defeating the detection mechanism. Therefore, we will go one better and create a defacement detection technology that will be difficult for the hacker to get around.

To create a dynamic token, we will be using the visitor’s IP address. The reason we use the IP address instead of the hostname is that a reverse lookup may not always be possible, whereas the IP address will always be available.

The following example code in JSP illustrates how the token is calculated and inserted into the page.

<%@ page import="java.security.*" %>
<%
 String tokenPlaintext = request.getRemoteAddr();
 String tokenHashed = "";
 String hexByte = "";
 // Hash the IP address
 MessageDigest md5 = MessageDigest.getInstance("MD5");
 md5.update(tokenPlaintext.getBytes());
 byte[] digest = md5.digest();
 for (int i = 0; i < digest.length; i++) {
 hexByte = Integer.toHexString(0xFF & digest[i]);
 if (hexByte.length() < 2) {
 hexByte = "0" + hexByte;
 }
 tokenHashed += hexByte;
 }
 // Write MD5 sum token to HTML document
 out.println(String.format("<span style='color: white'>%s</span>", 
tokenHashed));
%>

 

Assuming the background of the page is white, the <span style=”color: white”> markup will ensure it is not visible to website viewers.

Now for the ModSecurity rules to handle the defacement detection. We need to look at outgoing pages and make sure that they include the appropriate token. Since the token will be different for different users, we need to calculate the same MD5 sum token in our ModSecurity rule and make sure that this token is included in the output. If not, we block the page from being sent and sound the alert by sending an email message to the website administrator.

#
# Detect and block outgoing pages not containing our token
#
SecRule REMOTE_ADDR ".*" "phase:4,deny,chain,t:md5,t:hexEncode,
 exec:/usr/bin/emailadmin.sh"
SecRule RESPONSE_BODY "!@contains %{MATCHED_VAR}"

We are placing the rule in phase 4 since this is required when we want to inspect the response body. The exec action is used to send an email to the website administrator to let him know of the website defacement.

1 COMMENT

LEAVE A REPLY

Please enter your comment!
Please enter your name here