2019-05-08 13:47:52 +03:00
#!/usr/bin/env python
2010-09-15 17:28:56 +04:00
"""
2020-01-01 15:25:15 +03:00
Copyright ( c ) 2006 - 2020 sqlmap developers ( http : / / 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
"""
2019-06-04 13:15:39 +03:00
from __future__ import division
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
2019-05-09 17:38:44 +03:00
from lib . core . common import clearColors
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-05-03 14:20:15 +03:00
from lib . core . compat import xrange
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-01-08 01:49:45 +03:00
2019-05-03 14:20:15 +03:00
_rand = 0
2016-09-19 16:51:28 +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 ' " , ) ) ,
( " -u <url> --flush-session --wizard --check-internet " , ( " Please choose: " , " back-end DBMS: SQLite " , " current user is DBA: True " , " banner: ' 3. " , " ~no connection detected " ) ) ,
2020-01-07 13:48:02 +03:00
( u " -c <config> --flush-session --sql-query= \" SELECT ' \u0161 u \u0107 uraj ' \" --technique=U " , ( u " : ' \u0161 u \u0107 uraj ' " , ) ) ,
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 ' " , ) ) ,
( " --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 " ) ) ,
2019-11-28 01:26:39 +03:00
( " --list-tampers " , ( " between " , " MySQL " , " xforwardedfor " ) ) ,
2019-11-30 00:03:16 +03:00
( " -r <request> --flush-session -v 5 " , ( " CloudFlare " , " possible DBMS: ' SQLite ' " , " User-agent: foobar " ) ) ,
2019-12-10 15:54:29 +03:00
( " -l <log> --flush-session --keep-alive --skip-waf -v 5 --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] " ) ) ,
( " -u <url> --flush-session --encoding=ascii --forms --crawl=2 --threads=2 --banner " , ( " total of 2 targets " , " might be injectable " , " Type: UNION query " , " banner: ' 3. " ) ) ,
( " -u <url> --flush-session --data= ' { \" id \" : 1} ' --banner " , ( " might be injectable " , " 3 columns " , " Payload: { \" id \" " , " Type: boolean-based blind " , " Type: time-based blind " , " Type: UNION query " , " banner: ' 3. " ) ) ,
2019-11-30 00:03:16 +03:00
( " -u <url> --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 " ) ) ,
2019-11-17 02:21:33 +03:00
( " -u <url> --flush-session --method=PUT --data= ' a=1&b=2&c=3&id=1 ' --skip-static --dump -T users --start=1 --stop=2 " , ( " might be injectable " , " Parameter: id (PUT) " , " Type: boolean-based blind " , " Type: time-based blind " , " Type: UNION query " , " 2 entries " ) ) ,
( " -u <url> --flush-session -H ' id: 1* ' --tables " , ( " might be injectable " , " Parameter: id #1* ((custom) HEADER) " , " Type: boolean-based blind " , " Type: time-based blind " , " Type: UNION query " , " users " ) ) ,
2019-12-10 15:54:29 +03:00
( " -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 " ) ) ,
2019-11-28 02:29:42 +03:00
( " -u <url> --flush-session --null-connection --technique=B --tamper=between,randomcase --banner " , ( " NULL connection is supported with HEAD method " , " banner: ' 3. " ) ) ,
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-07 13:48:02 +03:00
( " -u <url> --technique=U --fresh-queries --force-partial --dump -T users --answers= \" crack=n \" -v 3 " , ( " performed 6 queries " , " nameisnull " , " ~using default dictionary " ) ) ,
2019-11-18 00:08:18 +03:00
( " -u <url> --flush-session --all " , ( " 5 entries " , " Type: boolean-based blind " , " Type: time-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 " ) ) ,
2019-11-18 14:08:26 +03:00
( " -u ' <url>&echo=foobar* ' --flush-session " , ( " might be vulnerable to cross-site scripting " , ) ) ,
2019-11-28 02:29:42 +03:00
( " -u ' <url>&query=* ' --flush-session --technique=Q --banner " , ( " Title: SQLite inline queries " , " banner: ' 3. " ) ) ,
2019-11-17 02:36:39 +03:00
( " -d <direct> --flush-session --dump -T users --binary-fields=name --where \" id=3 \" " , ( " 7775 " , " 179ad45c6ce2cb97cf1029e212046e81 (testpass) " , ) ) ,
2019-11-28 02:29:42 +03:00
( " -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
2019-04-29 15:19:56 +03:00
address , port = " 127.0.0.10 " , random . randint ( 1025 , 65535 )
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
thread = threading . Thread ( target = _thread )
thread . daemon = True
thread . start ( )
2019-11-14 03:29:51 +03:00
while True :
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
try :
s . connect ( ( address , port ) )
break
except :
time . sleep ( 1 )
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 )
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 )
2019-11-28 01:26:39 +03:00
open ( request , " w+ " ) . write ( content )
open ( log , " w+ " ) . write ( ' <port> %d </port><request base64= " true " ><![CDATA[ %s ]]></request> ' % ( port , encodeBase64 ( content , binary = False ) ) )
2019-11-17 18:04:22 +03:00
2019-11-17 02:21:33 +03:00
url = " http:// %s : %d /?id=1 " % ( address , port )
2019-11-17 18:04:22 +03:00
direct = " sqlite3:// %s " % database
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 )
open ( config , " w+ " ) . write ( content )
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 ) )
2020-01-07 13:48:02 +03:00
cmd = " %s %s %s --batch " % ( sys . executable , os . path . abspath ( os . path . join ( os . path . dirname ( __file__ ) , " .. " , " .. " , " sqlmap.py " ) ) , options . replace ( " <url> " , url ) . replace ( " <direct> " , direct ) . replace ( " <request> " , request ) . replace ( " <log> " , log ) . replace ( " <config> " , config ) )
2019-05-09 17:38:44 +03:00
output = shellExec ( cmd )
2019-04-30 14:20:31 +03:00
2019-11-28 01:26:39 +03:00
if not all ( ( check in output if not check . startswith ( ' ~ ' ) else check [ 1 : ] not in output ) for check in checks ) :
2019-05-09 17:38:44 +03:00
dataToStdout ( " --- \n \n $ %s \n " % cmd )
dataToStdout ( " %s --- \n " % clearColors ( output ) )
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
2020-01-08 01:49:45 +03:00
def fuzzTest ( ) :
count = 0
address , port = " 127.0.0.10 " , random . randint ( 1025 , 65535 )
def _thread ( ) :
vulnserver . init ( quiet = True )
vulnserver . run ( address = address , port = port )
thread = threading . Thread ( target = _thread )
thread . daemon = True
thread . start ( )
while True :
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
try :
s . connect ( ( address , port ) )
break
except :
time . sleep ( 1 )
handle , config = tempfile . mkstemp ( suffix = " .conf " )
os . close ( handle )
url = " http:// %s : %d /?id=1 " % ( address , port )
content = open ( os . path . abspath ( os . path . join ( os . path . dirname ( __file__ ) , " .. " , " .. " , " sqlmap.conf " ) ) ) . read ( ) . replace ( " url = " , " url = %s " % url )
open ( config , " w+ " ) . write ( content )
while True :
lines = content . split ( " \n " )
for i in xrange ( 20 ) :
j = random . randint ( 0 , len ( lines ) - 1 )
2020-01-08 12:43:11 +03:00
if any ( _ in lines [ j ] for _ in ( " googleDork " , ) ) :
continue
2020-01-08 01:49:45 +03:00
if lines [ j ] . strip ( ) . endswith ( ' = ' ) :
lines [ j ] + = random . sample ( ( " True " , " False " , randomStr ( ) , str ( randomInt ( ) ) ) , 1 ) [ 0 ]
k = random . randint ( 0 , len ( lines ) - 1 )
if ' = ' in lines [ k ] :
lines [ k ] + = chr ( random . randint ( 0 , 255 ) )
open ( config , " w+ " ) . write ( " \n " . join ( lines ) )
2020-01-08 12:43:11 +03:00
cmd = " %s %s -c %s --non-interactive --answers= ' Github=n ' --flush-session --technique= %s --banner " % ( sys . executable , os . path . abspath ( os . path . join ( os . path . dirname ( __file__ ) , " .. " , " .. " , " sqlmap.py " ) ) , config , random . sample ( " BEUQ " , 1 ) [ 0 ] )
2020-01-08 01:49:45 +03:00
output = shellExec ( cmd )
if " Traceback " in output :
dataToStdout ( " --- \n \n $ %s \n " % cmd )
dataToStdout ( " %s --- \n " % clearColors ( output ) )
2020-01-08 02:01:15 +03:00
2020-01-08 01:49:45 +03:00
handle , config = tempfile . mkstemp ( prefix = " sqlmapcrash " , suffix = " .conf " )
os . close ( handle )
open ( config , " w+ " ) . write ( " \n " . join ( lines ) )
2020-01-08 02:01:15 +03:00
else :
2020-01-08 01:49:45 +03:00
dataToStdout ( " \r %d \r " % count )
count + = 1
2019-05-03 14:20:15 +03:00
def dirtyPatchRandom ( ) :
"""
Unifying random generated data across different Python versions
"""
def _lcg ( ) :
global _rand
a = 1140671485
c = 128201163
m = 2 * * 24
_rand = ( a * _rand + c ) % m
return _rand
def _randint ( a , b ) :
_ = a + ( _lcg ( ) % ( b - a + 1 ) )
return _
def _choice ( seq ) :
return seq [ _randint ( 0 , len ( seq ) - 1 ) ]
def _sample ( population , k ) :
return [ _choice ( population ) for _ in xrange ( k ) ]
def _seed ( seed ) :
global _rand
_rand = seed
random . choice = _choice
random . randint = _randint
random . sample = _sample
random . seed = _seed
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
2019-05-03 14:20:15 +03:00
dirtyPatchRandom ( )
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 ) :
if any ( _ in root for _ in ( " thirdparty " , " extra " ) ) :
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 ) :
if any ( _ in root for _ in ( " thirdparty " , " extra " ) ) :
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