2019-05-08 13:47:52 +03:00
#!/usr/bin/env python
2010-09-15 17:28:56 +04:00
"""
2023-01-03 01:24:59 +03:00
Copyright ( c ) 2006 - 2023 sqlmap developers ( https : / / sqlmap . org / )
2017-10-11 15:50:46 +03:00
See the file ' LICENSE ' for copying permission
2010-09-15 17:28:56 +04:00
"""
2010-09-27 17:26:46 +04:00
import doctest
2019-05-06 12:41:19 +03:00
import logging
2010-09-15 17:28:56 +04:00
import os
2019-04-29 15:19:56 +03:00
import random
2010-09-26 18:02:13 +04:00
import re
2019-11-14 03:29:51 +03:00
import socket
2019-11-17 02:21:33 +03:00
import sqlite3
2010-09-15 17:28:56 +04:00
import sys
2010-09-26 18:02:13 +04:00
import tempfile
2019-04-19 14:28:11 +03:00
import threading
2010-09-26 14:47:04 +04:00
import time
2010-09-15 17:28:56 +04:00
2019-04-19 14:28:11 +03:00
from extra . vulnserver import vulnserver
2010-11-24 00:00:42 +03:00
from lib . core . common import clearConsoleLine
2010-09-26 14:47:04 +04:00
from lib . core . common import dataToStdout
2020-01-08 01:49:45 +03:00
from lib . core . common import randomInt
from lib . core . common import randomStr
2019-04-19 14:28:11 +03:00
from lib . core . common import shellExec
2019-05-03 00:51:54 +03:00
from lib . core . compat import round
2019-11-28 01:26:39 +03:00
from lib . core . convert import encodeBase64
2019-05-06 12:41:19 +03:00
from lib . core . data import kb
2010-09-15 17:28:56 +04:00
from lib . core . data import logger
from lib . core . data import paths
2019-06-05 11:37:11 +03:00
from lib . core . data import queries
2020-02-07 00:15:31 +03:00
from lib . core . patch import unisonRandom
2021-09-29 21:59:10 +03:00
from lib . core . settings import IS_WIN
2020-01-08 01:49:45 +03:00
2019-04-19 14:28:11 +03:00
def vulnTest ( ) :
"""
Runs the testing against ' vulnserver '
"""
2019-11-06 13:52:50 +03:00
TESTS = (
2020-01-09 13:25:09 +03:00
( " -h " , ( " to see full list of options run with ' -hh ' " , ) ) ,
2022-06-22 12:58:09 +03:00
( " --dependencies " , ( " sqlmap requires " , " third-party library " ) ) ,
2021-09-29 21:29:36 +03:00
( " -u <url> --data= \" reflect=1 \" --flush-session --wizard --disable-coloring " , ( " Please choose: " , " back-end DBMS: SQLite " , " current user is DBA: True " , " banner: ' 3. " ) ) ,
( " -u <url> --data= \" code=1 \" --code=200 --technique=B --banner --no-cast --flush-session " , ( " back-end DBMS: SQLite " , " banner: ' 3. " , " ~COALESCE(CAST( " ) ) ,
2021-03-11 12:55:09 +03:00
( u " -c <config> --flush-session --output-dir= \" <tmpdir> \" --smart --roles --statements --hostname --privileges --sql-query= \" SELECT ' \u0161 u \u0107 uraj ' \" --technique=U " , ( u " : ' \u0161 u \u0107 uraj ' " , " on SQLite it is not possible " , " as the output directory " ) ) ,
2020-01-09 13:25:09 +03:00
( u " -u <url> --flush-session --sql-query= \" SELECT ' \u0161 u \u0107 uraj ' \" --technique=B --no-escape --string=luther --unstable " , ( u " : ' \u0161 u \u0107 uraj ' " , ) ) ,
2021-09-29 16:15:31 +03:00
( " -m <multiple> --flush-session --technique=B --banner " , ( " /3] URL: " , " back-end DBMS: SQLite " , " banner: ' 3. " ) ) ,
2020-01-09 13:25:09 +03:00
( " --dummy " , ( " all tested parameters do not appear to be injectable " , " does not seem to be injectable " , " there is not at least one " , " ~might be injectable " ) ) ,
2021-09-29 21:29:36 +03:00
( " -u \" <url>&id2=1 \" -p id2 -v 5 --flush-session --level=5 --text-only --test-filter= \" AND boolean-based blind - WHERE or HAVING clause (MySQL comment) \" " , ( " ~1AND " , ) ) ,
2019-11-28 01:26:39 +03:00
( " --list-tampers " , ( " between " , " MySQL " , " xforwardedfor " ) ) ,
2021-09-29 21:29:36 +03:00
( " -r <request> --flush-session -v 5 --test-skip= \" heavy \" --save=<config> " , ( " CloudFlare " , " web application technology: Express " , " possible DBMS: ' SQLite ' " , " User-agent: foobar " , " ~Type: time-based blind " , " saved command line options to the configuration file " ) ) ,
2021-01-12 18:21:43 +03:00
( " -c <config> " , ( " CloudFlare " , " possible DBMS: ' SQLite ' " , " User-agent: foobar " , " ~Type: time-based blind " ) ) ,
2021-02-05 17:15:58 +03:00
( " -l <log> --flush-session --keep-alive --skip-waf -vvvvv --technique=U --union-from=users --banner --parse-errors " , ( " banner: ' 3. " , " ORDER BY term out of range " , " ~xp_cmdshell " , " Connection: keep-alive " ) ) ,
2019-11-28 02:29:42 +03:00
( " -l <log> --offline --banner -v 5 " , ( " banner: ' 3. " , " ~[TRAFFIC OUT] " ) ) ,
2021-09-29 21:29:36 +03:00
( " -u <base> --flush-session --data= \" id=1&_=Eewef6oh \" --chunked --randomize=_ --random-agent --banner " , ( " fetched random HTTP User-Agent header value " , " Parameter: id (POST) " , " Type: boolean-based blind " , " Type: time-based blind " , " Type: UNION query " , " banner: ' 3. " ) ) ,
( " -u <base64> -p id --base64=id --data= \" base64=true \" --flush-session --banner --technique=B " , ( " banner: ' 3. " , ) ) ,
( " -u <base64> -p id --base64=id --data= \" base64=true \" --flush-session --tables --technique=U " , ( " users " , ) ) ,
( " -u <url> --flush-session --banner --technique=B --disable-precon --not-string \" no results \" " , ( " banner: ' 3. " , ) ) ,
2021-02-05 14:44:39 +03:00
( " -u <url> --flush-session --encoding=gbk --banner --technique=B --first=1 --last=2 " , ( " banner: ' 3. ' " , ) ) ,
2019-11-28 02:29:42 +03:00
( " -u <url> --flush-session --encoding=ascii --forms --crawl=2 --threads=2 --banner " , ( " total of 2 targets " , " might be injectable " , " Type: UNION query " , " banner: ' 3. " ) ) ,
2022-12-23 18:24:41 +03:00
( " -u <base> --flush-session --technique=BU --data= \" { \\ \" id \\ \" : 1} \" --banner " , ( " might be injectable " , " 3 columns " , " Payload: { \" id \" " , " Type: boolean-based blind " , " Type: UNION query " , " banner: ' 3. " ) ) ,
2021-09-29 21:29:36 +03:00
( " -u <base> --flush-session -H \" Foo: Bar \" -H \" Sna: Fu \" --data= \" <root><param name= \\ \" id \\ \" value= \\ \" 1* \\ \" /></root> \" --union-char=1 --mobile --answers= \" smartphone=3 \" --banner --smart -v 5 " , ( " might be injectable " , " Payload: <root><param name= \" id \" value= \" 1 " , " Type: boolean-based blind " , " Type: time-based blind " , " Type: UNION query " , " banner: ' 3. " , " Nexus " , " Sna: Fu " , " Foo: Bar " ) ) ,
2022-12-23 18:24:41 +03:00
( " -u <base> --flush-session --technique=BU --method=PUT --data= \" a=1;id=1;b=2 \" --param-del= \" ; \" --skip-static --har=<tmpfile> --dump -T users --start=1 --stop=2 " , ( " might be injectable " , " Parameter: id (PUT) " , " Type: boolean-based blind " , " Type: UNION query " , " 2 entries " ) ) ,
2021-09-29 21:29:36 +03:00
( " -u <url> --flush-session -H \" id: 1* \" --tables -t <tmpfile> " , ( " might be injectable " , " Parameter: id #1* ((custom) HEADER) " , " Type: boolean-based blind " , " Type: time-based blind " , " Type: UNION query " , " users " ) ) ,
( " -u <url> --flush-session --banner --invalid-logical --technique=B --predict-output --test-filter= \" OR boolean \" --tamper=space2dash " , ( " banner: ' 3. " , " LIKE " ) ) ,
2019-11-17 02:21:33 +03:00
( " -u <url> --flush-session --cookie= \" PHPSESSID=d41d8cd98f00b204e9800998ecf8427e; id=1*; id2=2 \" --tables --union-cols=3 " , ( " might be injectable " , " Cookie #1* ((custom) HEADER) " , " Type: boolean-based blind " , " Type: time-based blind " , " Type: UNION query " , " users " ) ) ,
2021-02-25 13:39:08 +03:00
( " -u <url> --flush-session --null-connection --technique=B --tamper=between,randomcase --banner --count -T users " , ( " NULL connection is supported with HEAD method " , " banner: ' 3. " , " users | 5 " ) ) ,
2022-02-11 00:30:17 +03:00
( " -u <base> --data= \" aWQ9MQ== \" --flush-session --base64=POST -v 6 " , ( " aWQ9MTtXQUlURk9SIERFTEFZICcwOjA " , ) ) ,
2019-11-26 01:47:29 +03:00
( " -u <url> --flush-session --parse-errors --test-filter= \" subquery \" --eval= \" import hashlib; id2=2; id3=hashlib.md5(id.encode()).hexdigest() \" --referer= \" localhost \" " , ( " might be injectable " , " : syntax error " , " back-end DBMS: SQLite " , " WHERE or HAVING clause (subquery " ) ) ,
2019-11-28 02:29:42 +03:00
( " -u <url> --banner --schema --dump -T users --binary-fields=surname --where \" id>3 \" " , ( " banner: ' 3. " , " INTEGER " , " TEXT " , " id " , " name " , " surname " , " 2 entries " , " 6E616D6569736E756C6C " ) ) ,
2020-01-27 14:20:29 +03:00
( " -u <url> --technique=U --fresh-queries --force-partial --dump -T users --dump-format=HTML --answers= \" crack=n \" -v 3 " , ( " performed 6 queries " , " nameisnull " , " ~using default dictionary " , " dumped to HTML file " ) ) ,
2022-12-23 18:24:41 +03:00
( " -u <url> --flush-session --technique=BU --all " , ( " 5 entries " , " Type: boolean-based blind " , " Type: UNION query " , " luther " , " blisset " , " fluffy " , " 179ad45c6ce2cb97cf1029e212046e81 " , " NULL " , " nameisnull " , " testpass " ) ) ,
2019-11-17 20:54:33 +03:00
( " -u <url> -z \" tec=B \" --hex --fresh-queries --threads=4 --sql-query= \" SELECT * FROM users \" " , ( " SELECT * FROM users [5] " , " nameisnull " ) ) ,
2021-09-29 21:29:36 +03:00
( " -u \" <url>&echo=foobar* \" --flush-session " , ( " might be vulnerable to cross-site scripting " , ) ) ,
( " -u \" <url>&query=* \" --flush-session --technique=Q --banner " , ( " Title: SQLite inline queries " , " banner: ' 3. " ) ) ,
2021-09-29 21:59:10 +03:00
( " -d \" <direct> \" --flush-session --dump -T users --dump-format=SQLITE --binary-fields=name --where \" id=3 \" " , ( " 7775 " , " 179ad45c6ce2cb97cf1029e212046e81 (testpass) " , " dumped to SQLITE database " ) ) ,
( " -d \" <direct> \" --flush-session --banner --schema --sql-query= \" UPDATE users SET name= ' foobar ' WHERE id=5; SELECT * FROM users; SELECT 987654321 \" " , ( " banner: ' 3. " , " INTEGER " , " TEXT " , " id " , " name " , " surname " , " 5, foobar, nameisnull " , " ' 987654321 ' " , ) ) ,
2020-01-09 13:25:09 +03:00
( " --purge -v 3 " , ( " ~ERROR " , " ~CRITICAL " , " deleting the whole directory tree " ) ) ,
2019-11-06 13:52:50 +03:00
)
2019-04-19 14:28:11 +03:00
retVal = True
2019-11-06 13:52:50 +03:00
count = 0
2021-09-30 09:36:07 +03:00
while True :
address , port = " 127.0.0.1 " , random . randint ( 10000 , 65535 )
try :
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
if s . connect_ex ( ( address , port ) ) :
break
2021-09-30 09:36:59 +03:00
else :
time . sleep ( 1 )
2021-09-30 09:36:07 +03:00
finally :
s . close ( )
2019-04-19 14:28:11 +03:00
def _thread ( ) :
vulnserver . init ( quiet = True )
2019-04-29 15:19:56 +03:00
vulnserver . run ( address = address , port = port )
2019-04-19 14:28:11 +03:00
2021-10-01 10:11:57 +03:00
vulnserver . _alive = True
2019-04-19 14:28:11 +03:00
thread = threading . Thread ( target = _thread )
thread . daemon = True
thread . start ( )
2021-10-01 10:11:57 +03:00
while vulnserver . _alive :
2019-11-14 03:29:51 +03:00
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
try :
s . connect ( ( address , port ) )
2021-10-07 01:39:54 +03:00
s . sendall ( b " GET / HTTP/1.1 \r \n \r \n " )
result = b " "
while True :
current = s . recv ( 1024 )
if not current :
break
else :
result + = current
if b " vulnserver " in result :
2020-05-27 18:57:38 +03:00
break
2019-11-14 03:29:51 +03:00
except :
2021-10-01 10:05:06 +03:00
pass
2020-05-27 18:57:38 +03:00
finally :
s . close ( )
2021-10-01 10:11:57 +03:00
time . sleep ( 1 )
2019-11-14 03:29:51 +03:00
2021-10-01 10:11:57 +03:00
if not vulnserver . _alive :
logger . error ( " problem occurred in vulnserver instantiation (address: ' http:// %s : %s ' ) " % ( address , port ) )
2021-10-01 10:02:44 +03:00
return False
else :
logger . info ( " vulnserver running at ' http:// %s : %s ' ... " % ( address , port ) )
2020-01-07 13:48:02 +03:00
handle , config = tempfile . mkstemp ( suffix = " .conf " )
os . close ( handle )
2019-11-17 18:04:22 +03:00
handle , database = tempfile . mkstemp ( suffix = " .sqlite " )
2019-11-17 02:21:33 +03:00
os . close ( handle )
2019-11-17 18:04:22 +03:00
with sqlite3 . connect ( database ) as conn :
2019-11-17 02:21:33 +03:00
c = conn . cursor ( )
c . executescript ( vulnserver . SCHEMA )
2019-11-17 18:04:22 +03:00
handle , request = tempfile . mkstemp ( suffix = " .req " )
os . close ( handle )
2019-11-28 01:26:39 +03:00
handle , log = tempfile . mkstemp ( suffix = " .log " )
os . close ( handle )
2021-02-11 14:30:21 +03:00
handle , multiple = tempfile . mkstemp ( suffix = " .lst " )
os . close ( handle )
2019-11-30 00:03:16 +03:00
content = " POST / HTTP/1.0 \n User-agent: foobar \n Host: %s : %s \n \n id=1 \n " % ( address , port )
2021-09-29 21:05:07 +03:00
with open ( request , " w+ " ) as f :
f . write ( content )
f . flush ( )
2019-11-28 01:26:39 +03:00
2021-09-29 21:05:07 +03:00
content = ' <port> %d </port><request base64= " true " ><![CDATA[ %s ]]></request> ' % ( port , encodeBase64 ( content , binary = False ) )
with open ( log , " w+ " ) as f :
f . write ( content )
f . flush ( )
2019-11-17 18:04:22 +03:00
2021-02-03 13:52:50 +03:00
base = " http:// %s : %d / " % ( address , port )
url = " %s ?id=1 " % base
2019-11-17 18:04:22 +03:00
direct = " sqlite3:// %s " % database
2021-02-25 13:39:08 +03:00
tmpdir = tempfile . mkdtemp ( )
2019-11-17 02:21:33 +03:00
2020-01-07 13:48:02 +03:00
content = open ( os . path . abspath ( os . path . join ( os . path . dirname ( __file__ ) , " .. " , " .. " , " sqlmap.conf " ) ) ) . read ( ) . replace ( " url = " , " url = %s " % url )
2021-09-29 21:05:07 +03:00
with open ( config , " w+ " ) as f :
f . write ( content )
f . flush ( )
content = " %s ? %s = %d \n %s ? %s = %d \n %s & %s =1 " % ( base , randomStr ( ) , randomInt ( ) , base , randomStr ( ) , randomInt ( ) , url , randomStr ( ) )
with open ( multiple , " w+ " ) as f :
f . write ( content )
f . flush ( )
2021-02-11 14:30:21 +03:00
2019-11-06 13:52:50 +03:00
for options , checks in TESTS :
2019-11-11 13:20:12 +03:00
status = ' %d / %d ( %d %% ) ' % ( count , len ( TESTS ) , round ( 100.0 * count / len ( TESTS ) ) )
dataToStdout ( " \r [ %s ] [INFO] complete: %s " % ( time . strftime ( " %X " ) , status ) )
2021-09-29 21:59:10 +03:00
if IS_WIN and " uraj " in options :
options = options . replace ( u " \u0161 u \u0107 uraj " , " sucuraj " )
checks = [ check . replace ( u " \u0161 u \u0107 uraj " , " sucuraj " ) for check in checks ]
2021-02-25 13:39:08 +03:00
for tag , value in ( ( " <url> " , url ) , ( " <base> " , base ) , ( " <direct> " , direct ) , ( " <tmpdir> " , tmpdir ) , ( " <request> " , request ) , ( " <log> " , log ) , ( " <multiple> " , multiple ) , ( " <config> " , config ) , ( " <base64> " , url . replace ( " id=1 " , " id=MZ= %3d " ) ) ) :
2020-09-04 13:45:33 +03:00
options = options . replace ( tag , value )
2021-09-30 22:23:40 +03:00
cmd = " %s \" %s \" %s --batch --non-interactive --debug --time-sec=1 " % ( sys . executable if ' ' not in sys . executable else ' " %s " ' % sys . executable , os . path . abspath ( os . path . join ( os . path . dirname ( __file__ ) , " .. " , " .. " , " sqlmap.py " ) ) , options )
2020-03-05 00:43:50 +03:00
2021-03-11 13:00:38 +03:00
if " <tmpfile> " in cmd :
2020-03-05 00:43:50 +03:00
handle , tmp = tempfile . mkstemp ( )
os . close ( handle )
2021-03-11 13:00:38 +03:00
cmd = cmd . replace ( " <tmpfile> " , tmp )
2020-03-05 00:43:50 +03:00
2019-05-09 17:38:44 +03:00
output = shellExec ( cmd )
2019-04-30 14:20:31 +03:00
2020-03-05 00:43:50 +03:00
if not all ( ( check in output if not check . startswith ( ' ~ ' ) else check [ 1 : ] not in output ) for check in checks ) or " unhandled exception " in output :
2019-05-09 17:38:44 +03:00
dataToStdout ( " --- \n \n $ %s \n " % cmd )
2020-03-06 14:21:26 +03:00
dataToStdout ( " %s --- \n " % output , coloring = False )
2019-04-19 14:28:11 +03:00
retVal = False
count + = 1
clearConsoleLine ( )
if retVal :
logger . info ( " vuln test final result: PASSED " )
else :
logger . error ( " vuln test final result: FAILED " )
2019-04-19 15:36:23 +03:00
return retVal
2010-09-15 17:28:56 +04:00
def smokeTest ( ) :
"""
2013-03-14 00:57:09 +04:00
Runs the basic smoke testing of a program
2010-09-15 17:28:56 +04:00
"""
2013-03-14 00:57:09 +04:00
2020-02-07 00:15:31 +03:00
unisonRandom ( )
2019-05-03 14:20:15 +03:00
2020-01-03 15:46:12 +03:00
content = open ( paths . ERRORS_XML , " r " ) . read ( )
for regex in re . findall ( r ' <error regexp= " (.+?) " /> ' , content ) :
try :
re . compile ( regex )
except re . error :
errMsg = " smoke test failed at compiling ' %s ' " % regex
logger . error ( errMsg )
return False
2010-09-15 17:28:56 +04:00
retVal = True
2010-09-26 14:47:04 +04:00
count , length = 0 , 0
2010-09-27 15:20:48 +04:00
2019-01-06 02:37:30 +03:00
for root , _ , files in os . walk ( paths . SQLMAP_ROOT_PATH ) :
2020-05-06 14:18:19 +03:00
if any ( _ in root for _ in ( " thirdparty " , " extra " , " interbase " ) ) :
2019-01-06 02:37:30 +03:00
continue
for filename in files :
if os . path . splitext ( filename ) [ 1 ] . lower ( ) == " .py " and filename != " __init__.py " :
length + = 1
for root , _ , files in os . walk ( paths . SQLMAP_ROOT_PATH ) :
2020-05-06 14:18:19 +03:00
if any ( _ in root for _ in ( " thirdparty " , " extra " , " interbase " ) ) :
2019-01-06 02:37:30 +03:00
continue
for filename in files :
2019-12-06 00:20:00 +03:00
if os . path . splitext ( filename ) [ 1 ] . lower ( ) == " .py " and filename not in ( " __init__.py " , " gui.py " ) :
2019-01-06 02:37:30 +03:00
path = os . path . join ( root , os . path . splitext ( filename ) [ 0 ] )
path = path . replace ( paths . SQLMAP_ROOT_PATH , ' . ' )
path = path . replace ( os . sep , ' . ' ) . lstrip ( ' . ' )
try :
__import__ ( path )
module = sys . modules [ path ]
2019-01-22 03:20:27 +03:00
except Exception as ex :
2019-01-06 02:37:30 +03:00
retVal = False
dataToStdout ( " \r " )
2019-01-22 03:20:27 +03:00
errMsg = " smoke test failed at importing module ' %s ' ( %s ): \n %s " % ( path , os . path . join ( root , filename ) , ex )
2019-01-06 02:37:30 +03:00
logger . error ( errMsg )
else :
2019-05-06 12:41:19 +03:00
logger . setLevel ( logging . CRITICAL )
kb . smokeMode = True
2019-05-29 17:42:04 +03:00
( failure_count , _ ) = doctest . testmod ( module )
2019-05-06 12:41:19 +03:00
kb . smokeMode = False
logger . setLevel ( logging . INFO )
2019-01-06 02:37:30 +03:00
if failure_count > 0 :
2010-09-15 17:28:56 +04:00
retVal = False
2019-01-06 02:37:30 +03:00
count + = 1
status = ' %d / %d ( %d %% ) ' % ( count , length , round ( 100.0 * count / length ) )
dataToStdout ( " \r [ %s ] [INFO] complete: %s " % ( time . strftime ( " %X " ) , status ) )
2010-09-26 14:47:04 +04:00
2019-06-05 11:37:11 +03:00
def _ ( node ) :
for __ in dir ( node ) :
if not __ . startswith ( ' _ ' ) :
candidate = getattr ( node , __ )
if isinstance ( candidate , str ) :
if ' \\ ' in candidate :
try :
re . compile ( candidate )
except :
errMsg = " smoke test failed at compiling ' %s ' " % candidate
logger . error ( errMsg )
raise
else :
_ ( candidate )
for dbms in queries :
try :
_ ( queries [ dbms ] )
except :
retVal = False
2010-11-24 00:00:42 +03:00
clearConsoleLine ( )
2010-09-15 17:28:56 +04:00
if retVal :
2010-09-27 15:20:48 +04:00
logger . info ( " smoke test final result: PASSED " )
2010-09-15 17:28:56 +04:00
else :
2010-09-27 15:20:48 +04:00
logger . error ( " smoke test final result: FAILED " )
2010-09-15 17:28:56 +04:00
return retVal