Clickjacking: Help, I Was Framed!

Security researchers discovered and disclosed the Clickjacking attack (also known as a “UI Redress Attack”) back in 2008. All major browsers were affected. Flash even had an interesting vulnerability that allowed control of a user’s microphone and webcam. Yet, here we are 7 years later still citing this issue on nearly every security assessment of web applications that we do. During our report delivery, development teams typically have one of the following responses: “What’s Clickjacking?”, “What can someone really do with this?”, or “So what?”.

I’d like to take a minute to explain a little bit about this exploit, give a quick example, and talk about a few ways to mitigate this issue.

The Exploit

Clickjacking involves hosting a form from the application in an iframe and tricking the user into activating the form. A common way to do this is to set the opacity of the iframe to 0 (rendering it invisible) and placing a link over a button on the application form.

clickjacking_hidden1

Don’t see anything dangerous? Let’s turn the opacity on the iframe to 1.

clickjacking_visible1

By clicking on the “Get My Free Trip!” link, we just purchased a few things off the attacker’s dream list.

The Defense

You’re probably wondering how you can protect yourself against clickjacking? It’s really pretty simple. All we have to do is add the X-Frame-Options response header and set it to “DENY” framing, or only allow it to come from the “SAMEORIGIN” (aka our site). This can be done by setting the response header in your code, changing the web server configuration, or simply using the OWASP Secure Headers tools, such as SHIM for .NET or Headlines for Java. No matter which route you take, you should see this response header from your server:

HTTP/1.1 200 OK
Server: nginx
X-FRAME-OPTIONS: DENY
Date: Tue, 17 Mar 2015 20:54:56 GMT
Content-Length: 29553

One thing to note is that while this solution works for all MODERN browser versions, it does not work for older versions. Below is a list of browsers and the version support was added for the  X-FRAME-OPTIONS header:

Chrome: 4.1.249.1042, Firefox: 3.6.9, IE: 8.0, Opera: 10.50, Safari: 4.0

So, what do we do for legacy browsers? It’s pretty simple too. OWASP recommends a style tag that hides the body, and a piece of JavaScript that removes the tag if the page is not framed, or breaks out of the frame if it is. We add a style tag with an id of “antiClickjack” that hides the body.

<style id=”antiClickjack”>body{display:none !important;}</style>

We follow this with a javascript that removes the style tag if we are the “top” (aka not in an iframe) or break out of the iframe by setting our location to the top.

<script type=”text/javascript”>
if (self === top) {
var antiClickjack = document.getElementById(“antiClickjack”);
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
</script>

Adding these solutions to your project globally can help you avoid an incident without taxing the server or adding much to the development cycle.

Happy (secure) coding!

References:
[1] http://ha.ckers.org/blog/20081007/clickjacking-details/
[2] http://www.sectheory.com/clickjacking.htm
[3] https://github.com/sourceclear/headlines
[4] http://shim.codeplex.com/
[5] https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet

About the Author
Aaron Cure is a senior security consultant at Cypress Data Defense, and an instructor and contributing author for the DEV544: Secure Coding in .NET course. After ten years in the U.S. Army as a Russian Linguist and a Satellite Repair Technician, he worked as a database administrator and programmer on the Iridium project, with subsequent positions as a telecommunications consultant, senior programmer, and security consultant. Other experience includes developing security tools, secure code review, vulnerability assessment, penetration testing, risk assessment, static source code analysis, and security research. Aaron holds the GIAC GSSP-.NET, GWAPT, GMOB, and CISSP certifications and is located in Arvada, CO.

Demystifying Cross-Site Request Forgery

Continuously ranked in the OWASP Top Ten, a large majority of the development community still doesn’t understand Cross-Site Request Forgery (CSRF). After years of penetration tests and code reviews, my experiences show that a high percentage of applications, especially new applications, do not have proper CSRF protections in place. This post provides a refresher on CSRF and provides a common defense for this issue.

The Exploit

CSRF occurs when an application trusts that all requests originating from the user’s browser are user-directed actions. Imagine that you are logged into your bank’s online portal. The application requires users to authenticate and passes a session cookie back to the browser. Subsequent requests made to the banking site must contain the session cookie, allowing the site to identify the user and perform the requested action.

What if an attacker could send a fake request using the victim’s browser?

Suppose an attacker wants to transfer money from a victim’s account to their own account. In an unprotected site, the attacker simply makes a valid transfer between accounts in the web site and captures the request.

The following snippet shows a hypothetical request to transfer funds:

GET /transfer.jsp?from=attacker&to=attacker&amount=1 HTTP/1.1
Host: bank.com
Cookie: JSESSIONID=aa968b1d26a891d451e02c631553d2f8f56446bd;

The attacker could then generate a malicious request, such as the src property shown in the image tag below:

<img src=”http://bank.com/transfer.jsp?from=victim&to=attacker&amount=1000000″ />

To execute this attack, our attacker uses a little social engineering and sends our victim two phishing emails. The first tells the victim about some malicious activity on their banking account. The second adversities a free vacation, and contains a link to an evil web page containing the image shown above.

The victim views the emails and quickly signs in to the banking web site. After verifying everything looks ok, the victim clicks the link for a free vacation. As long as the victim is still authenticated to the banking site, the browser will conveniently include the active banking session id in the request:

GET /transfer.jsp?from=victim&to=attacker&amount=1000000 HTTP/1.1
Host: bank.com
Cookie: JSESSIONID=2a63ad7a0ce1ab65ecc293f622587c224e8d4e21;

Notice that this looks identical to the original request, except the session id from the victim’s browser has been automatically included in the request. From the server’s point of view, this looks like a valid, user-directed request and the transfer succeeds!

The Defense

CSRF defenses commonly use synchronization tokens. This solution adds a random token to each request that modifies data or performs a transaction. As the request is processed, the application verifies that the value received in the request matches the value generated on the server. The following snippet shows what the transfer request protected with a CSRF token could look like. Notice that we have switched the GET request to a POST request. It is a best practice to make all data modifications and transactions using a POST request, rather than a GET request:

POST /transfer.jsp HTTP/1.1
Host: bank.com
Cookie: JSESSIONID=2a63ad7a0ce1ab65ecc293f622587c224e8d4e21;

from=victim&to=attacker&amount=1000000&token=444a6130-9d2a-11e4-bd06-0800200c9a66

The new token parameter has been added to the POST parameters. As long as the value is sufficiently random, the attacker will be unable to guess the victim’s current value as the attack is taking place. If the application properly verifies the token before processing the transfer, the attack will fail!

OWASP’s CSRFGuard is a popular Java EE filter that can be configured to protect an entire web site from CSRF attacks by automatically adding and verifying CSRF tokens.

To learn more about protecting your Java and web-based applications from CSRF using OWASP CSRFGuard, sign up for DEV541: Secure Coding in Java!

References:
https://www.owasp.org/index.php/Category:OWASP_CSRFGuard_Project

About the Author
Steve Kosten is a security consultant at Cypress Data Defense and the Denver Chapter President of the Open Web Application Security Project (OWASP) that focuses on information security education related to software applications. He was the co-organizer of AppSec USA 2014. He is an application security specialist who reviews software applications for top 100 firms across multiple industries including the financial, defense, identity management and more. He has a Masters degree in Information Security, and is CISSP and CISM certified.

The Google Cross-Site Scripting Challenge

If you didn’t know already, Google takes its application security seriously, especially when it comes to Cross-Site Scripting. They already have a Vulnerability Rewards Program and XSS Learning Documentation posted on their application security site. A few weeks ago, I saw some chatter on Twitter about a new approach for teaching folks about Cross-Site Scripting: The XSS Game! Wait a second, teach people about XSS by playing a game? It sounds like an app I would download on my tablet for my daughter to play with. Brilliant! Where do I sign up?

The Welcome screen contains some background information about XSS, Google’s vulnerability rewards program, and a link that takes you into the 1st of 6 missions. The goal of each mission is to get a JavaScript alert box to popup in the embedded browser.

Each level has a few hints, and a link to view the Python, HTML, and JavaScript source code running in the embedded browser. For those that do web development or code review regularly, this is a perfect way to quickly spot the vulnerability on each level. Sounds easy enough, right?

As the game progresses, you will find that reflected, persisted, and DOM XSS attacks are all covered. The various levels also require writing different types of payloads to exploit XSS in HTML, JavaScript, HTML Attribute, and URL contexts. Other vulnerabilities such as remote JavaScript file inclusion and weak data validation also come into play as you work through the levels. And of course, cake is your prize for completing all 6 levels!

Overall, I’d say this game does a fantastic job of challenging developers to think about the various ways that Cross-Site Scripting can be introduced into an application. It combines two very important skill sets for those working in application security: code review analysis and dynamic testing, both of which are needed to fully assess the security of a web application.

Are you up for the challenge? https://xss-game.appspot.com/

About the Author
Eric Johnson is a security consultant at Cypress Data Defense, and an instructor and contributing author for the SANS DEV544 Secure Coding in .NET course. He previously spent six years performing web application security assessments for a large financial institution, and another four years focusing on ASP .NET web development. Other experience includes developing security tools, secure code review, vulnerability assessment, penetration testing, risk assessment, static source code analysis, and security research. Eric completed a bachelor of science in computer engineering and a master of science in information assurance at Iowa State University. Eric currently holds the CISSP, GWAPT, and GSSP-.NET, certifications and is located in West Des Moines, IA. Follow Eric on Twitter @emjohn20.

LinkedIn OAuth Open Redirect Disclosure

During a recent mobile security engagement, I discovered an Insecure Redirect vulnerability in the LinkedIn OAuth 1.0 implementation that could allow an attacker to conduct phishing attacks against LinkedIn members. This vulnerability could be used to compromise LinkedIn user accounts, and gather sensitive information from those accounts (e.g. personal information and credit card numbers). The following describes this security vulnerability in detail and how I discovered it.

The Vulnerability
Section 4.7 of the OAuth 1.0 specification (RFC 5849) warns of possible phishing attacks, depending on the implementation. A vulnerable OAuth implementation could enable phishing attacks via user-agent redirection. The stated emphasis, further supported by OAuth 2.0 (RFC 6749 via “redirect_uri” parameter), is intended to raise awareness of open-redirection as a security vulnerability that should be avoided.

LinkedIn’s implementation lacked validation between the value of its “redirect_uri” parameter and the origin (base) of a registered client’s endpoint. In other words, if a redirect_uri (although optional) was entered during establishment of the OAuth integration, that redirect_uri value was not validated to ensure it belonged to a valid client. Manipulating this parameter to an unexpected location resulted in the application redirecting the user away from the intended web site.

The Attack
According to LinkedIn API documentation, the steps involved in obtaining an OAuth authorization token are as follows (prior to the fix recently put in place by LinkedIn):

First, a GET request specifying access permission is requested:

https://www.linkedin.com/uas/oauth2/authorization?response_type=code&client_id=you_client_id&scope=r_basicprofile,rw_nus,r_network&state=some_random_string_of_your_choice &redirect_uri=any_url

Upon successful authorization, a code is generated by LinkedIn and the end user’s browser (user-agent) is redirected to the URL specified in the redirect_uri parameter. Notice that the domain specified in the “redirect_uri” parameter in the above screenshot is the payload specified by our hypothetical attacker that redirects the end user to the malicious site.

Next, the browser redirects the user to an impersonated LinkedIn login page via the “redirect_uri” parameter, which attempts to gather the LinkedIn user’s credentials.

Finally, the victim then enters their credentials, trusting that the site is the official LinkedIn web application.

The proof-of-concept demonstrated the ability to phish for user’s credentials, but could have a larger impact if used against LinkedIn Premium members with credit card numbers stored with their personal account information.

LinkedIn Remediation
I contacted LinkedIn about the vulnerability in October 2013, and it was a pleasant experience working with the LinkedIn Information Security team members. They confirmed the vulnerability within 2 days. From confirmation of the vulnerability to deployment of its remediation, LinkedIn spent approximately seven months (from October 2013 to May 2014) developing the patch. LinkedIn took advantage of this opportunity to upgrade its OAuth implementation to v2.0, while still supporting its legacy API (v1.0) for compatibility with existing applications. Based on the information provided by LinkedIn, such remediation required deep collaboration with several partners on how OAuth tokens are handled.

Disclaimer
At the time of writing of this document, a patch for the disclosed vulnerability had already been deployed. For a detailed disclosure, please visit http://www.apt-sec.com/blogs/post/LinkedIn-OAuth-Vulnerability to obtain a free electronic copy (PDF).

About the Author
Phillip Pham is a security engineer with a strong passion for Information Security. Phillip has worked in various sectors and for organizations of various sizes, ranging from small start-ups to Fortune 500 companies. Prior to transitioning into an Information Security career, Phillip spent ten years in software engineering. His last five years of Information Security experience have been focused on Application Security. Some of his application security assessment strengths (both onpremise and cloudbased) include web applications, web services, APIs, Windows client applications, and mobile applications. He currently holds a GCIH certification and is cofounder of APT Security Solutions. Follow Phillip on Twitter @philaptsec.

Spot the Vuln – Boundaries – SQL Injection

Details

Affected Software: My Calendar WordPress Plugin

Fixed in Version: >1.7.2

Issue Type: SQL Injection

Original Code: Found Here

Details

This week’s bug was a subtle mistake in the usage of an escaping routine. It seems the developer understood the dangers of SQL injection and therefore used an escaping routine to sanitize user controlled input before using that input to build a SQL statement. Unfortunately, the developer overlooked a crucial characteristic and used the wrong escaping routine. Looking at the vulnerable line, we see the following:
[sourcecode lang=”PHP”]
$sql = “SELECT * FROM ” . WP_CALENDAR_CATEGORIES_TABLE . ” WHERE category_id=”.mysql_escape_string($_GET[‘category_id’]);
[/sourcecode]
As you can clearly see, the developer chose to utilize the mysql_escape_string() function to escape $_GET[‘category_id] before using category_id to build a SQL statement. Looking at the documentation (http://php.net/manual/en/function.mysql-escape-string.php) for mysql_escape_string(), we see that the specific characters escaped are: null byte (0), newline (\n), carriage return (\r), backslash (\), single quote (‘), double quote (“) and substiture (SUB, or 32). In this case, none of these characters are required in order for SQL injection to be successful. The user controlled $_GET[‘category_id’] is not enclosed in quotes, so there is no need to break out of quotes for SQL injection. For example, the attacker can pass the following:

http://path-to-server/calendar.php? category_id=1%20union%20select%201,2,3,4,5,6%20from%20users;

This would result in the following SQL statement:
SELECT * FROM WP_CALENDAR_CATEGORIES_TABLE WHERE category_id=1 union select 1,2,3,4,5,6 from users;

As you can see, the attacker can craft a valid SQL injection without using any of the characters escaped by mysql_escape_string(). The developers addressed this issue by casting the $_GET[‘category_id’] to an int before using it in a SQL statement.

If you look closely… you’ll see other, unpatched SQL injections with the same symptom littered throughout the code…

Vulnerable Code

[sourcecode lang=”PHP” highlight=”28″]
…snip…

get_results($sql);
echo ”

“.__(‘Category added successfully’,’calendar’).”

“;
}
else if (isset($_GET[‘mode’]) && isset($_GET[‘category_id’]) && $_GET[‘mode’] == ‘delete’)
{
$sql = “DELETE FROM ” . WP_CALENDAR_CATEGORIES_TABLE . ” WHERE category_id=”.mysql_escape_string($_GET[‘category_id’]);
$wpdb->get_results($sql);
$sql = “UPDATE ” . WP_CALENDAR_TABLE . ” SET event_category=1 WHERE event_category=”.mysql_escape_string($_GET[‘category_id’]);
$wpdb->get_results($sql);
echo ”

“.__(‘Category deleted successfully’,’calendar’).”

“;
}
else if (isset($_GET[‘mode’]) && isset($_GET[‘category_id’]) && $_GET[‘mode’] == ‘edit’ && !isset($_POST[‘mode’]))
{
$sql = “SELECT * FROM ” . WP_CALENDAR_CATEGORIES_TABLE . ” WHERE category_id=”.mysql_escape_string($_GET[‘category_id’]);
$cur_cat = $wpdb->get_row($sql);
?>

<form name=”catform” id=”catform” class=”wrap” method=”post” action=”/wp-admin/admin.php?page=calendar-categories”>

<input type=”hidden” name=”category_id” value=”category_id) ?>” />

: <input type=”text” name=”category_name” class=”input” size=”30″ maxlength=”30″ value=”category_name) ?>” />
: <input type=”text” name=”category_colour” class=”input” size=”10″ maxlength=”7″ value=”category_colour) ?>” />

<input type=”submit” name=”save” class=”button bold” value=” »” />

get_results($sql);
echo ”

“.__(‘Category edited successfully’,’calendar’).”

“;
}

$get_mode = 0;
$post_mode = 0;
if (isset($_GET[‘mode’])) {
if ($_GET[‘mode’] == ‘edit’) {
$get_mode = 1;
}
}
if (isset($_POST[‘mode’])) {
if ($_POST[‘mode’] == ‘edit’) {
$post_mode = 1;
}
}
if ($get_mode != 1 || $post_mode == 1)
{
?>

<form name=”catform” id=”catform” class=”wrap” method=”post” action=”/wp-admin/admin.php?page=calendar-categories”>

:
:

<input type=”submit” name=”save” class=”button bold” value=” »” />

[/sourcecode]

Spot the Vuln – Boundaries

I like pushing boundaries.
Lady Gaga

Spot the Vuln uses code snippets from open source applications to demonstrate vulnerabilities in real world web applications. Every Monday morning a vulnerable code snippet is posted. Take a look at the vulnerable code and try to identify where the security vulnerability is. Every Friday, a solution is posted so you can check your answers. Each exercise is designed to last between 5 and 10 minutes. Do it while you drink your morning coffee and you will be on your way to writing more secure applications.

[sourcecode language="php"]
...snip...
     
</style>
<?php
  // We do some checking to see what we're doing
  if (isset($_POST['mode']) && $_POST['mode'] == 'add')
    {
      // Proceed with the save  
      $sql = "INSERT INTO " . WP_CALENDAR_CATEGORIES_TABLE . " SET category_name='".mysql_escape_string($_POST['category_name'])."', category_colour='".mysql_escape_string($_POST['category_colour'])."'";
      $wpdb->get_results($sql);
      echo "<div class=\"updated\"><p><strong>".__('Category added successfully','calendar')."</strong></p></div>";
    }
  else if (isset($_GET['mode']) && isset($_GET['category_id']) && $_GET['mode'] == 'delete')
    {
      $sql = "DELETE FROM " . WP_CALENDAR_CATEGORIES_TABLE . " WHERE category_id=".mysql_escape_string($_GET['category_id']);
      $wpdb->get_results($sql);
      $sql = "UPDATE " . WP_CALENDAR_TABLE . " SET event_category=1 WHERE event_category=".mysql_escape_string($_GET['category_id']);
      $wpdb->get_results($sql);
      echo "<div class=\"updated\"><p><strong>".__('Category deleted successfully','calendar')."</strong></p></div>";
    }
  else if (isset($_GET['mode']) && isset($_GET['category_id']) && $_GET['mode'] == 'edit' && !isset($_POST['mode']))
    {
      $sql = "SELECT * FROM " . WP_CALENDAR_CATEGORIES_TABLE . " WHERE category_id=".mysql_escape_string($_GET['category_id']);
      $cur_cat = $wpdb->get_row($sql);
      ?>
<div class="wrap">
   <h2><?php _e('Edit Category','calendar'); ?></h2>
    <form name="catform" id="catform" class="wrap" method="post" action="<?php echo bloginfo('wpurl'); ?>/wp-admin/admin.php?page=calendar-categories">
                <input type="hidden" name="mode" value="edit" />
                <input type="hidden" name="category_id" value="<?php echo stripslashes($cur_cat->category_id) ?>" />
                <div id="linkadvanceddiv" class="postbox">
                        <div style="float: left; width: 98%; clear: both;" class="inside">
				<table cellpadding="5" cellspacing="5">
                                <tr>
				<td><legend><?php _e('Category Name','calendar'); ?>:</legend></td>
                                <td><input type="text" name="category_name" class="input" size="30" maxlength="30" value="<?php echo stripslashes($cur_cat->category_name) ?>" /></td>
				</tr>
                                <tr>
				<td><legend><?php _e('Category Colour (Hex format)','calendar'); ?>:</legend></td>
                                <td><input type="text" name="category_colour" class="input" size="10" maxlength="7" value="<?php echo stripslashes($cur_cat->category_colour) ?>" /></td>
                                </tr>
                                </table>
                        </div>
                        <div style="clear:both; height:1px;">&nbsp;</div>
                </div>
                <input type="submit" name="save" class="button bold" value="<?php _e('Save','calendar'); ?> &raquo;" />
    </form>
</div>
      <?php
    }
  else if (isset($_POST['mode']) && isset($_POST['category_id']) && isset($_POST['category_name']) && isset($_POST['category_colour']) && $_POST['mode'] == 'edit')
    {
      // Proceed with the save
      $sql = "UPDATE " . WP_CALENDAR_CATEGORIES_TABLE . " SET category_name='".mysql_escape_string($_POST['category_name'])."', category_colour='".mysql_escape_string($_POST['category_colour'])."' WHERE category_id=".mysql_escape_string($_POST['category_id']);
      $wpdb->get_results($sql);
      echo "<div class=\"updated\"><p><strong>".__('Category edited successfully','calendar')."</strong></p></div>";
    }

  $get_mode = 0;
  $post_mode = 0;
  if (isset($_GET['mode'])) {
    if ($_GET['mode'] == 'edit') {
      $get_mode = 1;
    }
  }
  if (isset($_POST['mode'])) {
    if ($_POST['mode'] == 'edit') {
      $post_mode = 1;
    }
  }
  if ($get_mode != 1 || $post_mode == 1)
    {
?>

  <div class="wrap">
    <h2><?php _e('Add Category','calendar'); ?></h2>
    <form name="catform" id="catform" class="wrap" method="post" action="<?php echo bloginfo('wpurl'); ?>/wp-admin/admin.php?page=calendar-categories">
                <input type="hidden" name="mode" value="add" />
                <input type="hidden" name="category_id" value="">
                <div id="linkadvanceddiv" class="postbox">
                        <div style="float: left; width: 98%; clear: both;" class="inside">
       				<table cellspacing="5" cellpadding="5">
                                <tr>
                                <td><legend><?php _e('Category Name','calendar'); ?>:</legend></td>
                                <td><input type="text" name="category_name" class="input" size="30" maxlength="30" value="" /></td>
                                </tr>
                                <tr>
                                <td><legend><?php _e('Category Colour (Hex format)','calendar'); ?>:</legend></td>
                                <td><input type="text" name="category_colour" class="input" size="10" maxlength="7" value="" /></td>
                                </tr>
                                </table>
                        </div>
		        <div style="clear:both; height:1px;">&nbsp;</div>
                </div>
                <input type="submit" name="save" class="button bold" value="<?php _e('Save','calendar'); ?> &raquo;" />
    </form>
    <h2><?php _e('Manage Categories','calendar'); ?></h2>
...snip...
[/sourcecode]

About the Authors:
Brett Hardin and Billy Rios run spotthevuln.com, a website dedicated to helping developers understand secure coding practices. You can find out more about the authors by visiting http://spotthevuln.com/about-spot-the-vuln/

Spot the Vuln – Floods – SQL Injection

Details

Affected Software: Corpse C&C

Fixed in Version: ?

Issue Type: SQL Injection

Original Code: Found Here

Details

This week’s bug is in Corpse C&C. SpotTheVuln reader Christina hits it right on the head, line 32 contains a ridiculous amount of SQL injection. Most of the parameters passed to the INSERT statement results in SQL injection. $id, $info, and $user are all set directly from $_GET or $_POST and are used in the SQL statement without any sanitization. Despite its name, $real_ip is also completely attacker controlled and can be used for SQL injection. Getenv(“HTTP_X_FORWARDED_FOR”) doesn’t sanitize the user controlled value in any way. For some reason, many developers assume the X-Forwarded-For header will only specify an IP address or domain name. X-Forwarded-For can contain any characters (including angle brackets, single quotes, and double quotes).

[sourcecode lang=”PHP” highlight=”32″]
<?php

$use_mysql = 1;

if ($use_mysql == 1) {
require_once('./mysqllog.php');
require_once('./geoipcity.inc');
}

$ip = getenv("REMOTE_ADDR");
$real_ip = getenv("HTTP_X_FORWARDED_FOR");

if (isset($_GET['id'])) {
$id = $_GET['id'];
} else {
$id = $_POST['id'];
}

$info = $_POST['info'];
$user = $_POST['user'];

if ($use_mysql == 1) {
//———————————–
$gi = geoip_open('./GeoIPCity.dat', GEOIP_STANDARD);
$record = geoip_record_by_addr($gi, $ip);
geoip_close($gi);
//———————————–
$info = decode_string($info);
if(@!mysql_connect($mysql_host,$mysql_login,$mysql_pass)) {echo '<p class="err"> Error. Cant connect to mysql server </p>'; }
if(@!mysql_selectdb($mysql_db)) {echo '<p class="err"> Error. Cant connect to DB</p>'; }
$query = 'INSERT INTO pass (add_date,id,uidlog,ip_real,ip,pass,country,city,zip)
VALUES (now(), "'. $id . '", "'. $user .'", "'. $real_ip . '", "'. $ip .'", "'. $info .'", "'. $record->country_name .'", "'. $record->city .'", "'. $record->postal_code .'")';
if(@!mysql_query($query)) {echo '<p class="err"> Error. Cant execute query</p>'; }
}
else {
$date = date("Y-m-d");
$time=date("H:i:s");

list($year, $month, $day) = explode('-', $date);
$filename = "pass.$day.$month.txt";
$log = "$info@@@@@$user@@@@@$id@@@@@$real_ip@@@@@$ip@@@@@$date@@@@@$time\n";
$fh = fopen("logs/$filename", "a+");
fputs($fh, $log);
fclose($fh);
}

function decode_string($string) {
$bindata = '';
for ($i=0;$i<strlen($string);$i+=2) {
$bindata.=chr(hexdec(substr($string,$i,2)));
}
return addslashes($bindata);
}
?>
[/sourcecode]

Spot the Vuln – Floods

The moment we begin to fear the opinions of others and hesitate to tell the truth that is in us, and from motives of policy are silent when we should speak, the divine floods of light and life no longer flow into our souls.
Elizabeth Cady Stanton

Spot the Vuln uses code snippets from open source applications to demonstrate vulnerabilities in real world web applications. Every Monday morning a vulnerable code snippet is posted. Take a look at the vulnerable code and try to identify where the security vulnerability is. Every Friday, a solution is posted so you can check your answers. Each exercise is designed to last between 5 and 10 minutes. Do it while you drink your morning coffee and you will be on your way to writing more secure applications.

[sourcecode language="php"]
<?php

$use_mysql = 1;

if ($use_mysql == 1) {
	require_once('./mysqllog.php');
	require_once('./geoipcity.inc');
}

$ip = getenv("REMOTE_ADDR");
$real_ip = getenv("HTTP_X_FORWARDED_FOR");

if (isset($_GET['id'])) {
	$id = $_GET['id']; 
} else {
	$id = $_POST['id'];
}

$info = $_POST['info'];
$user = $_POST['user'];

if ($use_mysql == 1) {
	//-----------------------------------
	$gi = geoip_open('./GeoIPCity.dat', GEOIP_STANDARD);
	$record = geoip_record_by_addr($gi, $ip);
	geoip_close($gi);
	//-----------------------------------
	$info = decode_string($info);
	if(@!mysql_connect($mysql_host,$mysql_login,$mysql_pass)) {echo '<p class="err"> Error. Cant connect to mysql server </p>'; }
	if(@!mysql_selectdb($mysql_db)) {echo '<p class="err"> Error. Cant connect to DB</p>'; }
	$query = 'INSERT INTO pass (add_date,id,uidlog,ip_real,ip,pass,country,city,zip)
			  VALUES (now(), "'. $id . '", "'. $user .'", "'. $real_ip . '", "'. $ip .'", "'. $info .'", "'. $record->country_name .'", "'. $record->city .'", "'. $record->postal_code .'")';
	if(@!mysql_query($query)) {echo '<p class="err"> Error. Cant execute query</p>';  }
}
else {
	$date = date("Y-m-d");
	$time=date("H:i:s");
	
	list($year, $month, $day) = explode('-', $date);
	$filename = "pass.$day.$month.txt";
	$log = "$info@@@@@$user@@@@@$id@@@@@$real_ip@@@@@$ip@@@@@$date@@@@@$time\n";
	$fh = fopen("logs/$filename", "a+");
	fputs($fh, $log);		
	fclose($fh);
}

function decode_string($string) {
    $bindata = '';
    for ($i=0;$i<strlen($string);$i+=2) {
        $bindata.=chr(hexdec(substr($string,$i,2)));
    }
    return addslashes($bindata);
}
?>
[/sourcecode]

About the Authors:
Brett Hardin and Billy Rios run spotthevuln.com, a website dedicated to helping developers understand secure coding practices. You can find out more about the authors by visiting http://spotthevuln.com/about-spot-the-vuln/

Spot the Vuln – Grammys – Cross Site Scripting

Details

Affected Software: Corpse C&C

Fixed in Version: ?

Issue Type: XSS

Original Code: Found Here

Details

Fairly straightforward XSS bug here. This week’s bug can be found in the index.php file for the Corpse C&C. Specifically, the index file located at Corpse/info/socks/index.php. Buried deep within the print statement starting on line 30 are two unsanitized, unescaped variables ($states and $countrys). Both $states and $countrys are taken directly from $_POST parameters and assigned to php variables. Those php variables are then used to build HTML markup. Buried within a large print statement, a little difficult to spot, but this bug is classic XSS.

[sourcecode lang=”PHP” highlight=”6,7,36″]
<?php

include_once('geoipcity.inc');
include_once('../mysqllog.php');

$countrys = $_POST['countrys'];
$states = $_POST['states'];

if ($countrys == "") {
$countrys = "all";
}
if ($states == "") {
$states = "all";
}

$date = date("m-d");
list($month, $day) = explode('-', $date);

print "<STYLE><!– a:link{color:#404040;text-decoration:none} a:visited{color:#909090;text-decoration:none} a:active{color:#000000;text-decoration:none} a:hover{color:#000000;text-decoration:none} input{BACKGROUND-COLOR:#66CF96;BORDER-BOTTOM:#ffffff 1px solid;BORDER-LEFT:#ffffff 1px solid;BORDER-RIGHT:#ffffff 1px solid;BORDER-TOP:#ffffff 1px solid;COLOR:#000000;FONT-FAMILY:Tahoma,sans-serif;FONT-SIZE:12px} –></STYLE>
<BODY bgcolor=#DDDDDD text=#505050 marginwidth=0><table align=center border=1><TD><form action=index.php method=post><B>Select by country</B><TD><select name=countrys><option value=all>All countries";

$j = 1;
while ($GEOIP_COUNTRY_CODES[$j] != "") {
print "<option value=$GEOIP_COUNTRY_CODES[$j]>$GEOIP_COUNTRY_NAMES[$j]\r\n";
$j++;
}

print "</SELECT><TD><BR><INPUT type=submit value=submit></FORM><TR><TD>
<FORM action=index.php method=post><B>Select by state</B><TD><select name=states><option value=all>all<option value=AK>AK<option value=AL>AL<option value=AR>AR<option value=AS>AS<option value=AZ>AZ<option value=CA>CA<option value=CO>CO
<option value=CT>CT<option value=DC>DC<option value=DE>DE<option value=FL>FL<option value=GA>GA<option value=HI>HI<option value=IA>IA<option value=ID>ID<option value=IL>IL<option value=IN>IN
<option value=KS>KS<option value=KY>KY<option value=LA>LA<option value=MA>MA<option value=MD>MD<option value=ME>ME<option value=MI>MI<option value=MN>MN<option value=MO>MO<option value=MP>MP
<option value=MS>MS<option value=MT>MT<option value=NC>NC<option value=ND>ND<option value=NE>NE<option value=NH>NH<option value=NJ>NJ<option value=NM>NM<option value=NV>NU<option value=NY>NY
<option value=OH>OH<option value=OK>OK<option value=OR>OR<option value=PA>PA<option value=PR>PR<option value=RI>RI<option value=SC>SC<option value=SD>SD<option value=TN>TN<option value=TX>TX
<option value=UT>UT<option value=VA>VA<option value=VI>VI<option value=VT>VT<option value=WA>WA<option value=WI>WI<option value=WV>WV<option value=WY>WY</select>
<TD><BR><input type=submit value=submit></form></table><B><CENTER><BR>Current country selected:$countrys<BR>Current state selected:$states</CENTER></B><BR>
<table width=100% cellspacing=0><tr><td><table width=100% bgcolor=#FFFFFF cellspacing=1><tr><td align=center bgcolor=#66CF96><b>List</b></td></tr></table></td></tr>
<tr><td><table width=100% bgcolor=#FFFFFF cellspacing=1><tr><td align=center bgcolor=#66CF96>IP</td><td align=center bgcolor=#66CF96>UPDATE</td><td align=center bgcolor=#66CF96>ID</td>
<td align=center bgcolor=#66CF96>COUNTRY</td>
<td align=center bgcolor=#66CF96>CITY</td>
<td align=center bgcolor=#66CF96>STATE</td>
<td align=center bgcolor=#66CF96>UPTIME</td></tr>";

$stime = mktime();
$stime = $stime – 86400;
$link = mysql_connect($mysql_host, $mysql_login, $mysql_pass) or die("Could not connect:" . mysql_error());
mysql_select_db($mysql_db, $link) or die("Could not select:" . mysql_error());
$query = 'SELECT * FROM `socks` WHERE `update` >' . $stime . ' ORDER BY `update` DESC';
$result = mysql_query($query, $link) or die("Could not execute:" . mysql_error());

$tot = 0;
while ($row = mysql_fetch_assoc($result)) {
$prms[0] = $row['ip'];
$prms[1] = $row['hport'];
$prms[2] = $row['sport'];
$prms[3] = $row['update'];
$prms[4] = $row['uptime'];
$prms[5] = $row['uid'];
$prms[6] = $row['used'];
if ($prms[0] != "") {
printent($prms,$tot,$countrys,$states);
$tot++;
}
}
mysql_close($link);

print "</table><table width=100% bgcolor=#FFFFFF cellspacing=1><tr><td align=right bgcolor=#66CF96>Total:<b>$tot</b></tr></td></table></table></table></tr></td></TABLE></BODY></HTML>";

function printent($prms,$tot,$countrys,$states){
if(!($tot%2)) {
$bcolor="#D6D6D6";
} else {
$bcolor="#98E8E1";
}

$tid = $prms[5];
$tid = chop($tid);

$gi = geoip_open("../GeoIPCity.dat",GEOIP_STANDARD);
$record = geoip_record_by_addr($gi,$prms[0]);
geoip_close($gi);

if (($countrys == "all") & ($states == "all")) {
echo "<tr>\r\n";
echo "<td align=left bgcolor=$bcolor onClick=\"window.open('check.php?ip=$prms[0]&port=$prms[2]&hport=$prms[1]','child','scrollbars=no,width=250,height=100');\" onmouseover=\"this.style.background='#000D2A';\" onmouseout=\"this.style.background='$bcolor';\"><font face='Fixedsys' color=#707070><INPUT type=button value=\"Copy IP\" onclick=window.clipboardData.setData(\"Text\",\"$prms[0]\")>$prms[0]</font></td>\r\n";
echo "<td align=center bgcolor=$bcolor><font face='Fixedsys' color=#707070>" . date("H:i:s d.m.y", $prms[3]) ."</font></td>\r\n";//socks
echo "<td align=center bgcolor=$bcolor><font face='Fixedsys' color=#707070><INPUT type=button value=\"Copy ID\" onclick=window.clipboardData.setData(\"Text\",\"$tid\")>&nbsp;$tid</font></td>\r\n";//socks

// Show flag
if ($record->country_code == "") {
$record->country_code = "-";
$record->country_name = "";
}

$c_code = strtolower($record->country_code);

$flag = "<IMG src=../flags/$c_code.gif>&nbsp;$record->country_name.<BR>";

echo "<td align=left bgcolor=$bcolor><font face='Fixedsys' color=#707070>$flag</font></td>\r\n";
[/sourcecode]

Spot the Vuln – Grammys

The last thing I want is to walk into my house after a long day and see all the Grammys and awards. It would make me feel weird.
Alicia Keys

Spot the Vuln uses code snippets from open source applications to demonstrate vulnerabilities in real world web applications. Every Monday morning a vulnerable code snippet is posted. Take a look at the vulnerable code and try to identify where the security vulnerability is. Every Friday, a solution is posted so you can check your answers. Each exercise is designed to last between 5 and 10 minutes. Do it while you drink your morning coffee and you will be on your way to writing more secure applications.

[sourcecode language="php"]
<?php

include_once('geoipcity.inc');
include_once('../mysqllog.php');

$countrys = $_POST['countrys'];
$states = $_POST['states'];

if ($countrys == "") {
	$countrys = "all";
}
if ($states == "") {
	$states = "all";
}

$date = date("m-d");
list($month, $day) = explode('-', $date);


print "<STYLE><!-- a:link{color:#404040;text-decoration:none;}  a:visited {color:#909090;text-decoration:none;}  a:active {color:#000000;text-decoration:none;}  a:hover {color:#000000;text-decoration:none;}  input {BACKGROUND-COLOR: #66CF96; BORDER-BOTTOM: #ffffff 1px solid; BORDER-LEFT: #ffffff 1px solid; BORDER-RIGHT: #ffffff 1px solid; BORDER-TOP: #ffffff 1px solid; COLOR: #000000; FONT-FAMILY: Tahoma,sans-serif; FONT-SIZE: 12px;}  --></STYLE>
		<BODY bgcolor=#DDDDDD text=#505050 marginwidth=0><table align=center border=1><TD><form action=index.php method=post><B>Select by country</B><TD><select name=countrys><option value=all>All countries";

$j = 1;
while ($GEOIP_COUNTRY_CODES[$j] != "") {
	print "<option value=$GEOIP_COUNTRY_CODES[$j]>$GEOIP_COUNTRY_NAMES[$j]\r\n";
	$j++;
}

print "</SELECT><TD><BR><INPUT type=submit value=submit></FORM><TR><TD>
		<FORM action=index.php method=post><B>Select by state</B><TD><select name=states><option value=all>all<option value=AK>AK<option value=AL>AL<option value=AR>AR<option value=AS>AS<option value=AZ>AZ<option value=CA>CA<option value=CO>CO
		<option value=CT>CT<option value=DC>DC<option value=DE>DE<option value=FL>FL<option value=GA>GA<option value=HI>HI<option value=IA>IA<option value=ID>ID<option value=IL>IL<option value=IN>IN
		<option value=KS>KS<option value=KY>KY<option value=LA>LA<option value=MA>MA<option value=MD>MD<option value=ME>ME<option value=MI>MI<option value=MN>MN<option value=MO>MO<option value=MP>MP
		<option value=MS>MS<option value=MT>MT<option value=NC>NC<option value=ND>ND<option value=NE>NE<option value=NH>NH<option value=NJ>NJ<option value=NM>NM<option value=NV>NU<option value=NY>NY
		<option value=OH>OH<option value=OK>OK<option value=OR>OR<option value=PA>PA<option value=PR>PR<option value=RI>RI<option value=SC>SC<option value=SD>SD<option value=TN>TN<option value=TX>TX
		<option value=UT>UT<option value=VA>VA<option value=VI>VI<option value=VT>VT<option value=WA>WA<option value=WI>WI<option value=WV>WV<option value=WY>WY</select>
		<TD><BR><input type=submit value=submit></form></table><B><CENTER><BR>Current country selected: $countrys<BR>Current state selected: $states</CENTER></B><BR>
		<table width=100% cellspacing=0><tr><td><table width=100% bgcolor=#FFFFFF cellspacing=1><tr><td align=center bgcolor=#66CF96><b>List</b></td></tr></table></td></tr>
		<tr><td><table width=100% bgcolor=#FFFFFF cellspacing=1><tr><td align=center bgcolor=#66CF96>IP</td><td align=center bgcolor=#66CF96>UPDATE</td><td align=center bgcolor=#66CF96>ID</td>
		<td align=center bgcolor=#66CF96>COUNTRY</td>
		<td align=center bgcolor=#66CF96>CITY</td>
		<td align=center bgcolor=#66CF96>STATE</td>
		<td align=center bgcolor=#66CF96>UPTIME</td></tr>";
     

$stime = mktime();
$stime = $stime - 86400;
$link = mysql_connect($mysql_host, $mysql_login, $mysql_pass) or die("Could not connect: " . mysql_error());
mysql_select_db($mysql_db, $link) or die("Could not select : " . mysql_error());
$query = 'SELECT * FROM `socks` WHERE `update` > ' . $stime . ' ORDER BY `update` DESC';
$result = mysql_query($query, $link) or die("Could not execute: " . mysql_error());

$tot = 0;
while ($row = mysql_fetch_assoc($result)) {
	$prms[0] = $row['ip'];
	$prms[1] = $row['hport'];
	$prms[2] = $row['sport'];
	$prms[3] = $row['update'];
	$prms[4] = $row['uptime'];
	$prms[5] = $row['uid'];
	$prms[6] = $row['used'];
	if ($prms[0] != "") {
		printent($prms,$tot,$countrys,$states);
		$tot++;
	}
}
mysql_close($link);

print "</table><table width=100% bgcolor=#FFFFFF cellspacing=1><tr><td align=right bgcolor=#66CF96>Total: <b>$tot</b></tr></td></table></table></table></tr></td></TABLE></BODY></HTML>";

function printent($prms,$tot,$countrys,$states){
	if(!($tot%2)) {
		$bcolor="#D6D6D6";
	} else {
		$bcolor="#98E8E1";
	}
	
	$tid = $prms[5];
	$tid = chop($tid);
	
	$gi = geoip_open("../GeoIPCity.dat",GEOIP_STANDARD);
	$record = geoip_record_by_addr($gi,$prms[0]);
	geoip_close($gi);

	if (($countrys == "all") & ($states == "all")) {
		echo "<tr>\r\n";
		echo "<td align=left bgcolor=$bcolor onClick=\"window.open('check.php?ip=$prms[0]&port=$prms[2]&hport=$prms[1]','child','scrollbars=no,width=250,height=100');\" onmouseover=\"this.style.background='#000D2A';\" onmouseout=\"this.style.background='$bcolor';\"><font face='Fixedsys' color=#707070><INPUT type=button value=\"Copy IP\" onclick=window.clipboardData.setData(\"Text\",\"$prms[0]\")>$prms[0]</font></td>\r\n";
		echo "<td align=center bgcolor=$bcolor><font face='Fixedsys' color=#707070>" . date("H:i:s d.m.y", $prms[3]) ."</font></td>\r\n";//socks
		echo "<td align=center bgcolor=$bcolor><font face='Fixedsys' color=#707070><INPUT type=button value=\"Copy ID\" onclick=window.clipboardData.setData(\"Text\",\"$tid\")>&nbsp;$tid</font></td>\r\n";//socks

		// Show flag
		if ($record->country_code == "") {
			$record->country_code = "-";
			$record->country_name = "";
		}

		$c_code = strtolower($record->country_code);
		
		$flag = "<IMG src=../flags/$c_code.gif>&nbsp;$record->country_name.<BR>";
		
		echo "<td align=left bgcolor=$bcolor><font face='Fixedsys' color=#707070>$flag</font></td>\r\n";
[/sourcecode]

About the Authors:
Brett Hardin and Billy Rios run spotthevuln.com, a website dedicated to helping developers understand secure coding practices. You can find out more about the authors by visiting http://spotthevuln.com/about-spot-the-vuln/