2019-05-08 13:47:52 +03:00
#!/usr/bin/env python
2013-12-14 18:44:10 +04:00
# -*- coding: utf-8 -*-
2012-12-14 06:52:31 +04:00
"""
2021-09-08 22:01:41 +03:00
Copyright ( c ) 2006 - 2021 sqlmap developers ( https : / / sqlmap . org / )
2017-10-11 15:50:46 +03:00
See the file ' LICENSE ' for copying permission
2012-12-14 06:52:31 +04:00
"""
2019-01-22 03:28:24 +03:00
from __future__ import print_function
2017-01-20 15:11:12 +03:00
import contextlib
2013-01-25 21:11:31 +04:00
import logging
2012-12-14 06:52:31 +04:00
import os
2015-09-10 16:01:30 +03:00
import re
import shlex
2015-12-09 14:20:09 +03:00
import socket
2013-01-24 16:57:24 +04:00
import sqlite3
2013-01-25 21:11:31 +04:00
import sys
2012-12-14 19:52:35 +04:00
import tempfile
2013-01-25 21:11:31 +04:00
import time
2012-12-14 16:04:44 +04:00
2015-09-10 16:01:30 +03:00
from lib . core . common import dataToStdout
2015-09-10 16:51:33 +03:00
from lib . core . common import getSafeExString
2019-05-13 12:51:47 +03:00
from lib . core . common import openFile
2017-04-10 15:50:17 +03:00
from lib . core . common import saveConfig
2021-09-09 21:27:45 +03:00
from lib . core . common import setColor
2012-12-20 20:53:43 +04:00
from lib . core . common import unArrayizeValue
2019-03-28 18:04:38 +03:00
from lib . core . compat import xrange
2019-05-03 14:20:15 +03:00
from lib . core . convert import decodeBase64
2013-01-29 05:39:27 +04:00
from lib . core . convert import dejsonize
2019-06-04 15:44:06 +03:00
from lib . core . convert import encodeBase64
from lib . core . convert import encodeHex
2020-01-16 00:47:06 +03:00
from lib . core . convert import getBytes
from lib . core . convert import getText
2013-01-10 02:09:50 +04:00
from lib . core . convert import jsonize
2013-01-25 21:11:31 +04:00
from lib . core . data import conf
2013-02-03 15:31:05 +04:00
from lib . core . data import kb
2012-12-14 16:04:44 +04:00
from lib . core . data import logger
2019-06-04 15:44:06 +03:00
from lib . core . data import paths
2013-01-10 02:09:50 +04:00
from lib . core . datatype import AttribDict
2016-10-18 21:17:51 +03:00
from lib . core . defaults import _defaults
2018-06-21 00:21:55 +03:00
from lib . core . dicts import PART_RUN_CONTENT_TYPES
2018-06-21 00:04:58 +03:00
from lib . core . enums import AUTOCOMPLETE_TYPE
2013-02-03 15:31:05 +04:00
from lib . core . enums import CONTENT_STATUS
2016-05-31 14:02:26 +03:00
from lib . core . enums import MKSTEMP_PREFIX
2015-03-10 00:01:59 +03:00
from lib . core . exception import SqlmapConnectionException
2013-01-25 21:11:31 +04:00
from lib . core . log import LOGGER_HANDLER
2012-12-20 20:53:43 +04:00
from lib . core . optiondict import optDict
2015-05-07 13:36:23 +03:00
from lib . core . settings import IS_WIN
2019-06-04 15:44:06 +03:00
from lib . core . settings import RESTAPI_DEFAULT_ADAPTER
2015-12-13 01:48:30 +03:00
from lib . core . settings import RESTAPI_DEFAULT_ADDRESS
from lib . core . settings import RESTAPI_DEFAULT_PORT
2021-01-12 17:58:40 +03:00
from lib . core . settings import RESTAPI_UNSUPPORTED_OPTIONS
2020-05-20 16:20:44 +03:00
from lib . core . settings import VERSION_STRING
2018-06-21 00:04:58 +03:00
from lib . core . shell import autoCompletion
2013-01-29 05:39:27 +04:00
from lib . core . subprocessng import Popen
2015-09-09 16:14:04 +03:00
from lib . parse . cmdline import cmdLineParser
2013-12-15 12:22:01 +04:00
from thirdparty . bottle . bottle import error as return_error
2012-12-22 03:34:37 +04:00
from thirdparty . bottle . bottle import get
from thirdparty . bottle . bottle import hook
from thirdparty . bottle . bottle import post
from thirdparty . bottle . bottle import request
from thirdparty . bottle . bottle import response
from thirdparty . bottle . bottle import run
2017-04-07 15:46:41 +03:00
from thirdparty . bottle . bottle import server_names
2020-08-10 22:26:37 +03:00
from thirdparty import six
2019-03-27 15:33:46 +03:00
from thirdparty . six . moves import http_client as _http_client
2019-05-02 01:45:44 +03:00
from thirdparty . six . moves import input as _input
2019-03-27 15:33:46 +03:00
from thirdparty . six . moves import urllib as _urllib
2012-12-20 18:35:02 +04:00
2017-08-04 14:37:49 +03:00
# Global data storage
2013-12-15 03:19:58 +04:00
class DataStore ( object ) :
2018-11-15 17:27:05 +03:00
admin_token = " "
2013-12-15 03:19:58 +04:00
current_db = None
tasks = dict ( )
2017-08-04 14:37:49 +03:00
username = None
password = None
2013-12-14 18:44:10 +04:00
2013-01-29 05:39:27 +04:00
# API objects
class Database ( object ) :
2013-12-15 03:19:58 +04:00
filepath = None
2013-01-29 05:39:27 +04:00
2013-02-03 15:31:05 +04:00
def __init__ ( self , database = None ) :
2013-12-15 03:19:58 +04:00
self . database = self . filepath if database is None else database
2013-12-15 12:16:38 +04:00
self . connection = None
self . cursor = None
2013-01-29 05:39:27 +04:00
2013-02-03 15:31:05 +04:00
def connect ( self , who = " server " ) :
2017-06-18 16:00:12 +03:00
self . connection = sqlite3 . connect ( self . database , timeout = 3 , isolation_level = None , check_same_thread = False )
2013-01-29 05:39:27 +04:00
self . cursor = self . connection . cursor ( )
2013-02-03 15:31:05 +04:00
logger . debug ( " REST-JSON API %s connected to IPC database " % who )
2013-01-29 05:39:27 +04:00
def disconnect ( self ) :
2015-03-10 00:01:59 +03:00
if self . cursor :
self . cursor . close ( )
if self . connection :
self . connection . close ( )
2013-01-29 05:39:27 +04:00
2013-02-06 21:09:43 +04:00
def commit ( self ) :
2013-12-14 18:58:09 +04:00
self . connection . commit ( )
2013-02-06 21:09:43 +04:00
2013-01-29 05:39:27 +04:00
def execute ( self , statement , arguments = None ) :
2015-04-22 17:41:20 +03:00
while True :
try :
if arguments :
self . cursor . execute ( statement , arguments )
else :
self . cursor . execute ( statement )
2019-01-22 02:40:48 +03:00
except sqlite3 . OperationalError as ex :
2018-03-13 15:45:42 +03:00
if " locked " not in getSafeExString ( ex ) :
2015-04-22 17:41:20 +03:00
raise
else :
break
2013-01-29 05:39:27 +04:00
if statement . lstrip ( ) . upper ( ) . startswith ( " SELECT " ) :
return self . cursor . fetchall ( )
2013-02-03 15:31:05 +04:00
def init ( self ) :
2018-03-13 15:45:42 +03:00
self . execute ( " CREATE TABLE logs(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, time TEXT, level TEXT, message TEXT) " )
self . execute ( " CREATE TABLE data(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, status INTEGER, content_type INTEGER, value TEXT) " )
self . execute ( " CREATE TABLE errors(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, error TEXT) " )
2013-01-29 05:39:27 +04:00
class Task ( object ) :
2015-09-15 15:37:30 +03:00
def __init__ ( self , taskid , remote_addr ) :
self . remote_addr = remote_addr
2013-01-29 05:39:27 +04:00
self . process = None
self . output_directory = None
2013-07-10 18:57:44 +04:00
self . options = None
self . _original_options = None
2013-01-29 05:39:27 +04:00
self . initialize_options ( taskid )
def initialize_options ( self , taskid ) :
2013-07-10 18:57:44 +04:00
datatype = { " boolean " : False , " string " : None , " integer " : None , " float " : None }
2013-01-29 05:39:27 +04:00
self . options = AttribDict ( )
for _ in optDict :
for name , type_ in optDict [ _ ] . items ( ) :
type_ = unArrayizeValue ( type_ )
2016-10-18 21:17:51 +03:00
self . options [ name ] = _defaults . get ( name , datatype [ type_ ] )
2013-01-29 05:39:27 +04:00
2013-12-14 18:44:10 +04:00
# Let sqlmap engine knows it is getting called by the API,
# the task ID and the file path of the IPC database
2013-01-29 05:39:27 +04:00
self . options . api = True
self . options . taskid = taskid
2013-12-15 03:19:58 +04:00
self . options . database = Database . filepath
2013-01-29 05:39:27 +04:00
2013-02-06 21:45:52 +04:00
# Enforce batch mode and disable coloring and ETA
2013-01-29 05:39:27 +04:00
self . options . batch = True
self . options . disableColoring = True
2013-02-06 21:45:52 +04:00
self . options . eta = False
2013-01-29 05:39:27 +04:00
2013-07-10 18:57:44 +04:00
self . _original_options = AttribDict ( self . options )
2013-01-29 05:39:27 +04:00
def set_option ( self , option , value ) :
self . options [ option ] = value
def get_option ( self , option ) :
return self . options [ option ]
def get_options ( self ) :
return self . options
2013-07-10 18:57:44 +04:00
def reset_options ( self ) :
self . options = AttribDict ( self . _original_options )
2013-01-29 05:39:27 +04:00
def engine_start ( self ) :
2017-04-14 14:08:51 +03:00
handle , configFile = tempfile . mkstemp ( prefix = MKSTEMP_PREFIX . CONFIG , text = True )
os . close ( handle )
2017-04-10 15:50:17 +03:00
saveConfig ( self . options , configFile )
2015-09-08 12:04:36 +03:00
if os . path . exists ( " sqlmap.py " ) :
2019-02-09 17:11:06 +03:00
self . process = Popen ( [ sys . executable or " python " , " sqlmap.py " , " --api " , " -c " , configFile ] , shell = False , close_fds = not IS_WIN )
2017-01-16 15:44:46 +03:00
elif os . path . exists ( os . path . join ( os . getcwd ( ) , " sqlmap.py " ) ) :
2019-02-09 17:11:06 +03:00
self . process = Popen ( [ sys . executable or " python " , " sqlmap.py " , " --api " , " -c " , configFile ] , shell = False , cwd = os . getcwd ( ) , close_fds = not IS_WIN )
2018-06-07 11:07:12 +03:00
elif os . path . exists ( os . path . join ( os . path . abspath ( os . path . dirname ( sys . argv [ 0 ] ) ) , " sqlmap.py " ) ) :
2019-02-09 17:11:06 +03:00
self . process = Popen ( [ sys . executable or " python " , " sqlmap.py " , " --api " , " -c " , configFile ] , shell = False , cwd = os . path . join ( os . path . abspath ( os . path . dirname ( sys . argv [ 0 ] ) ) ) , close_fds = not IS_WIN )
2015-09-08 12:04:36 +03:00
else :
2017-04-10 15:50:17 +03:00
self . process = Popen ( [ " sqlmap " , " --api " , " -c " , configFile ] , shell = False , close_fds = not IS_WIN )
2013-01-29 05:39:27 +04:00
def engine_stop ( self ) :
if self . process :
2015-08-12 11:25:33 +03:00
self . process . terminate ( )
return self . process . wait ( )
2013-01-29 19:38:09 +04:00
else :
return None
2013-01-29 05:39:27 +04:00
2014-01-02 15:15:56 +04:00
def engine_process ( self ) :
return self . process
2013-01-29 05:39:27 +04:00
def engine_kill ( self ) :
if self . process :
2016-01-27 23:25:34 +03:00
try :
self . process . kill ( )
2016-01-28 11:10:04 +03:00
return self . process . wait ( )
2016-01-27 23:25:34 +03:00
except :
pass
2016-01-28 11:10:04 +03:00
return None
2013-01-29 19:38:09 +04:00
def engine_get_id ( self ) :
if self . process :
return self . process . pid
else :
return None
2013-01-29 05:39:27 +04:00
2013-01-29 20:11:25 +04:00
def engine_get_returncode ( self ) :
2014-01-02 13:55:40 +04:00
if self . process :
self . process . poll ( )
return self . process . returncode
else :
return None
2013-01-29 20:11:25 +04:00
2013-01-29 19:38:09 +04:00
def engine_has_terminated ( self ) :
2013-01-29 20:11:25 +04:00
return isinstance ( self . engine_get_returncode ( ) , int )
2013-01-29 05:39:27 +04:00
# Wrapper functions for sqlmap engine
2013-01-25 21:11:31 +04:00
class StdDbOut ( object ) :
2013-01-29 05:39:27 +04:00
def __init__ ( self , taskid , messagetype = " stdout " ) :
2013-01-25 21:11:31 +04:00
# Overwrite system standard output and standard error to write
2013-01-29 05:39:27 +04:00
# to an IPC database
self . messagetype = messagetype
self . taskid = taskid
2013-01-25 21:11:31 +04:00
2013-01-29 05:39:27 +04:00
if self . messagetype == " stdout " :
2013-01-25 21:11:31 +04:00
sys . stdout = self
else :
sys . stderr = self
2013-02-03 15:31:05 +04:00
def write ( self , value , status = CONTENT_STATUS . IN_PROGRESS , content_type = None ) :
2013-01-29 05:39:27 +04:00
if self . messagetype == " stdout " :
2013-02-03 15:31:05 +04:00
if content_type is None :
2013-02-05 18:43:03 +04:00
if kb . partRun is not None :
content_type = PART_RUN_CONTENT_TYPES . get ( kb . partRun )
else :
# Ignore all non-relevant messages
return
2013-02-03 15:31:05 +04:00
2017-06-14 15:13:41 +03:00
output = conf . databaseCursor . execute ( " SELECT id, status, value FROM data WHERE taskid = ? AND content_type = ? " , ( self . taskid , content_type ) )
2013-02-15 04:53:05 +04:00
2013-02-06 21:09:43 +04:00
# Delete partial output from IPC database if we have got a complete output
2013-02-07 04:05:26 +04:00
if status == CONTENT_STATUS . COMPLETE :
if len ( output ) > 0 :
2013-12-15 19:59:47 +04:00
for index in xrange ( len ( output ) ) :
2017-06-14 15:13:41 +03:00
conf . databaseCursor . execute ( " DELETE FROM data WHERE id = ? " , ( output [ index ] [ 0 ] , ) )
2013-02-07 04:05:26 +04:00
2017-06-14 15:13:41 +03:00
conf . databaseCursor . execute ( " INSERT INTO data VALUES(NULL, ?, ?, ?, ?) " , ( self . taskid , status , content_type , jsonize ( value ) ) )
2013-02-06 21:45:52 +04:00
if kb . partRun :
kb . partRun = None
2013-02-07 04:05:26 +04:00
elif status == CONTENT_STATUS . IN_PROGRESS :
2013-02-03 15:31:05 +04:00
if len ( output ) == 0 :
2017-06-14 15:13:41 +03:00
conf . databaseCursor . execute ( " INSERT INTO data VALUES(NULL, ?, ?, ?, ?) " , ( self . taskid , status , content_type , jsonize ( value ) ) )
2013-02-03 15:31:05 +04:00
else :
2013-02-07 04:05:26 +04:00
new_value = " %s %s " % ( dejsonize ( output [ 0 ] [ 2 ] ) , value )
2017-06-14 15:13:41 +03:00
conf . databaseCursor . execute ( " UPDATE data SET value = ? WHERE id = ? " , ( jsonize ( new_value ) , output [ 0 ] [ 0 ] ) )
2013-01-25 21:11:31 +04:00
else :
2017-06-14 15:13:41 +03:00
conf . databaseCursor . execute ( " INSERT INTO errors VALUES(NULL, ?, ?) " , ( self . taskid , str ( value ) if value else " " ) )
2013-01-25 21:11:31 +04:00
def flush ( self ) :
pass
def close ( self ) :
pass
def seek ( self ) :
pass
class LogRecorder ( logging . StreamHandler ) :
def emit ( self , record ) :
"""
2013-01-29 05:39:27 +04:00
Record emitted events to IPC database for asynchronous I / O
2013-01-25 21:11:31 +04:00
communication with the parent process
"""
2017-06-14 15:13:41 +03:00
conf . databaseCursor . execute ( " INSERT INTO logs VALUES(NULL, ?, ?, ?, ?) " , ( conf . taskid , time . strftime ( " %X " ) , record . levelname , record . msg % record . args if record . args else record . msg ) )
2013-01-25 21:11:31 +04:00
def setRestAPILog ( ) :
2017-04-10 20:21:22 +03:00
if conf . api :
2015-03-10 00:01:59 +03:00
try :
2016-08-08 17:08:16 +03:00
conf . databaseCursor = Database ( conf . database )
conf . databaseCursor . connect ( " client " )
2019-01-22 02:40:48 +03:00
except sqlite3 . OperationalError as ex :
2018-03-13 13:13:38 +03:00
raise SqlmapConnectionException ( " %s ( ' %s ' ) " % ( ex , conf . database ) )
2013-01-25 21:11:31 +04:00
2013-01-29 05:39:27 +04:00
# Set a logging handler that writes log messages to a IPC database
2013-01-25 21:11:31 +04:00
logger . removeHandler ( LOGGER_HANDLER )
LOGGER_RECORDER = LogRecorder ( )
logger . addHandler ( LOGGER_RECORDER )
2012-12-14 06:52:31 +04:00
# Generic functions
2018-11-15 17:27:05 +03:00
def is_admin ( token ) :
return DataStore . admin_token == token
2012-12-14 06:52:31 +04:00
2017-08-04 14:37:49 +03:00
@hook ( ' before_request ' )
def check_authentication ( ) :
if not any ( ( DataStore . username , DataStore . password ) ) :
return
authorization = request . headers . get ( " Authorization " , " " )
match = re . search ( r " (?i) \ ABasic \ s+([^ \ s]+) " , authorization )
if not match :
request . environ [ " PATH_INFO " ] = " /error/401 "
try :
2019-05-03 00:51:54 +03:00
creds = decodeBase64 ( match . group ( 1 ) , binary = False )
2017-08-04 14:37:49 +03:00
except :
request . environ [ " PATH_INFO " ] = " /error/401 "
else :
if creds . count ( ' : ' ) != 1 :
request . environ [ " PATH_INFO " ] = " /error/401 "
else :
username , password = creds . split ( ' : ' )
if username . strip ( ) != ( DataStore . username or " " ) or password . strip ( ) != ( DataStore . password or " " ) :
request . environ [ " PATH_INFO " ] = " /error/401 "
2013-12-14 18:44:10 +04:00
2012-12-14 21:21:19 +04:00
@hook ( " after_request " )
2013-01-27 16:26:40 +04:00
def security_headers ( json_header = True ) :
2012-12-14 16:15:04 +04:00
"""
Set some headers across all HTTP responses
"""
response . headers [ " Server " ] = " Server "
2012-12-14 17:40:25 +04:00
response . headers [ " X-Content-Type-Options " ] = " nosniff "
response . headers [ " X-Frame-Options " ] = " DENY "
2012-12-14 16:15:04 +04:00
response . headers [ " X-XSS-Protection " ] = " 1; mode=block "
2012-12-14 17:40:25 +04:00
response . headers [ " Pragma " ] = " no-cache "
response . headers [ " Cache-Control " ] = " no-cache "
response . headers [ " Expires " ] = " 0 "
2017-08-04 14:37:49 +03:00
2013-01-27 16:26:40 +04:00
if json_header :
response . content_type = " application/json; charset=UTF-8 "
2012-12-14 16:15:04 +04:00
2012-12-14 17:40:25 +04:00
##############################
# HTTP Status Code functions #
##############################
2012-12-14 16:15:04 +04:00
2013-12-15 12:22:01 +04:00
@return_error ( 401 ) # Access Denied
2012-12-14 17:40:25 +04:00
def error401 ( error = None ) :
2013-01-27 16:26:40 +04:00
security_headers ( False )
2012-12-14 06:52:31 +04:00
return " Access denied "
2013-12-15 12:22:01 +04:00
@return_error ( 404 ) # Not Found
2012-12-14 17:40:25 +04:00
def error404 ( error = None ) :
2013-01-27 16:26:40 +04:00
security_headers ( False )
2012-12-14 06:52:31 +04:00
return " Nothing here "
2013-12-15 12:22:01 +04:00
@return_error ( 405 ) # Method Not Allowed (e.g. when requesting a POST method via GET)
2012-12-14 17:40:25 +04:00
def error405 ( error = None ) :
2013-01-27 16:26:40 +04:00
security_headers ( False )
2012-12-14 06:52:31 +04:00
return " Method not allowed "
2013-12-15 12:22:01 +04:00
@return_error ( 500 ) # Internal Server Error
2012-12-14 17:40:25 +04:00
def error500 ( error = None ) :
2013-01-27 16:26:40 +04:00
security_headers ( False )
2012-12-14 06:52:31 +04:00
return " Internal server error "
2017-08-04 14:37:49 +03:00
#############
# Auxiliary #
#############
@get ( ' /error/401 ' )
def path_401 ( ) :
response . status = 401
return response
2012-12-14 17:40:25 +04:00
#############################
# Task management functions #
#############################
2012-12-14 06:52:31 +04:00
# Users' methods
2012-12-14 17:40:25 +04:00
@get ( " /task/new " )
def task_new ( ) :
2012-12-14 06:52:31 +04:00
"""
2018-11-15 17:27:05 +03:00
Create a new task
2012-12-14 06:52:31 +04:00
"""
2019-05-03 14:20:15 +03:00
taskid = encodeHex ( os . urandom ( 8 ) , binary = False )
2015-09-15 15:37:30 +03:00
remote_addr = request . remote_addr
DataStore . tasks [ taskid ] = Task ( taskid , remote_addr )
2013-01-24 16:57:24 +04:00
2014-12-23 11:07:33 +03:00
logger . debug ( " Created new task: ' %s ' " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : True , " taskid " : taskid } )
2012-12-14 06:52:31 +04:00
2013-01-29 19:38:09 +04:00
@get ( " /task/<taskid>/delete " )
def task_delete ( taskid ) :
2012-12-14 06:52:31 +04:00
"""
2018-11-15 17:27:05 +03:00
Delete an existing task
2012-12-14 06:52:31 +04:00
"""
2013-12-15 03:19:58 +04:00
if taskid in DataStore . tasks :
DataStore . tasks . pop ( taskid )
2013-01-29 19:38:09 +04:00
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Deleted task " % taskid )
2012-12-14 16:15:04 +04:00
return jsonize ( { " success " : True } )
2012-12-14 06:52:31 +04:00
else :
2018-11-15 17:27:05 +03:00
response . status = 404
logger . warning ( " [ %s ] Non-existing task ID provided to task_delete() " % taskid )
return jsonize ( { " success " : False , " message " : " Non-existing task ID " } )
2012-12-14 06:52:31 +04:00
2013-01-29 19:38:09 +04:00
###################
# Admin functions #
###################
2018-11-15 17:27:05 +03:00
@get ( " /admin/list " )
@get ( " /admin/<token>/list " )
def task_list ( token = None ) :
2012-12-14 06:52:31 +04:00
"""
2018-11-15 17:27:05 +03:00
Pull task list
2012-12-14 06:52:31 +04:00
"""
2015-09-24 17:26:52 +03:00
tasks = { }
for key in DataStore . tasks :
2018-11-15 17:27:05 +03:00
if is_admin ( token ) or DataStore . tasks [ key ] . remote_addr == request . remote_addr :
2015-09-24 17:26:52 +03:00
tasks [ key ] = dejsonize ( scan_status ( key ) ) [ " status " ]
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Listed task pool ( %s ) " % ( token , " admin " if is_admin ( token ) else request . remote_addr ) )
2015-09-14 17:31:02 +03:00
return jsonize ( { " success " : True , " tasks " : tasks , " tasks_num " : len ( tasks ) } )
2012-12-14 06:52:31 +04:00
2018-11-15 17:27:05 +03:00
@get ( " /admin/flush " )
@get ( " /admin/<token>/flush " )
def task_flush ( token = None ) :
2012-12-14 06:52:31 +04:00
"""
2013-01-29 19:38:09 +04:00
Flush task spool ( delete all tasks )
2012-12-14 06:52:31 +04:00
"""
2016-01-27 23:25:34 +03:00
for key in list ( DataStore . tasks ) :
2018-11-15 17:27:05 +03:00
if is_admin ( token ) or DataStore . tasks [ key ] . remote_addr == request . remote_addr :
2016-01-27 23:25:34 +03:00
DataStore . tasks [ key ] . engine_kill ( )
del DataStore . tasks [ key ]
2015-09-15 15:37:30 +03:00
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Flushed task pool ( %s ) " % ( token , " admin " if is_admin ( token ) else request . remote_addr ) )
2015-09-15 15:37:30 +03:00
return jsonize ( { " success " : True } )
2012-12-14 06:52:31 +04:00
2012-12-14 17:40:25 +04:00
##################################
# sqlmap core interact functions #
##################################
2012-12-14 18:51:01 +04:00
2013-01-29 19:38:09 +04:00
# Handle task's options
2012-12-14 18:51:01 +04:00
@get ( " /option/<taskid>/list " )
def option_list ( taskid ) :
"""
List options for a certain task ID
"""
2013-12-15 03:19:58 +04:00
if taskid not in DataStore . tasks :
2013-12-15 19:59:47 +04:00
logger . warning ( " [ %s ] Invalid task ID provided to option_list() " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : False , " message " : " Invalid task ID " } )
2012-12-14 18:51:01 +04:00
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Listed task options " % taskid )
2013-12-15 03:19:58 +04:00
return jsonize ( { " success " : True , " options " : DataStore . tasks [ taskid ] . get_options ( ) } )
2012-12-14 18:51:01 +04:00
@post ( " /option/<taskid>/get " )
def option_get ( taskid ) :
"""
2018-11-15 19:13:13 +03:00
Get value of option ( s ) for a certain task ID
2012-12-14 18:51:01 +04:00
"""
2013-12-15 03:19:58 +04:00
if taskid not in DataStore . tasks :
2013-12-15 19:59:47 +04:00
logger . warning ( " [ %s ] Invalid task ID provided to option_get() " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : False , " message " : " Invalid task ID " } )
2012-12-14 18:51:01 +04:00
2018-11-15 19:13:13 +03:00
options = request . json or [ ]
results = { }
2012-12-14 18:51:01 +04:00
2018-11-15 19:13:13 +03:00
for option in options :
if option in DataStore . tasks [ taskid ] . options :
results [ option ] = DataStore . tasks [ taskid ] . options [ option ]
else :
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Requested value for unknown option ' %s ' " % ( taskid , option ) )
2018-11-15 19:13:13 +03:00
return jsonize ( { " success " : False , " message " : " Unknown option ' %s ' " % option } )
2020-01-01 15:30:20 +03:00
logger . debug ( " ( %s ) Retrieved values for option(s) ' %s ' " % ( taskid , ' , ' . join ( options ) ) )
2018-11-15 19:13:13 +03:00
return jsonize ( { " success " : True , " options " : results } )
2012-12-14 18:51:01 +04:00
@post ( " /option/<taskid>/set " )
def option_set ( taskid ) :
"""
2018-11-15 19:13:13 +03:00
Set value of option ( s ) for a certain task ID
2012-12-14 18:51:01 +04:00
"""
2017-08-04 14:37:49 +03:00
2013-12-15 03:19:58 +04:00
if taskid not in DataStore . tasks :
2013-12-15 19:59:47 +04:00
logger . warning ( " [ %s ] Invalid task ID provided to option_set() " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : False , " message " : " Invalid task ID " } )
2012-12-14 18:51:01 +04:00
2017-09-04 18:16:00 +03:00
if request . json is None :
logger . warning ( " [ %s ] Invalid JSON options provided to option_set() " % taskid )
return jsonize ( { " success " : False , " message " : " Invalid JSON options " } )
2013-01-29 05:39:27 +04:00
for option , value in request . json . items ( ) :
2013-12-15 03:19:58 +04:00
DataStore . tasks [ taskid ] . set_option ( option , value )
2012-12-14 18:51:01 +04:00
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Requested to set options " % taskid )
2012-12-14 18:51:01 +04:00
return jsonize ( { " success " : True } )
2013-01-29 19:38:09 +04:00
# Handle scans
2012-12-14 19:52:35 +04:00
@post ( " /scan/<taskid>/start " )
2012-12-15 04:29:35 +04:00
def scan_start ( taskid ) :
2012-12-14 17:40:25 +04:00
"""
2012-12-14 18:51:01 +04:00
Launch a scan
2012-12-14 17:40:25 +04:00
"""
2017-08-04 14:37:49 +03:00
2013-12-15 03:19:58 +04:00
if taskid not in DataStore . tasks :
2013-12-15 19:59:47 +04:00
logger . warning ( " [ %s ] Invalid task ID provided to scan_start() " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : False , " message " : " Invalid task ID " } )
2012-12-14 06:52:31 +04:00
2017-09-04 18:16:00 +03:00
if request . json is None :
logger . warning ( " [ %s ] Invalid JSON options provided to scan_start() " % taskid )
return jsonize ( { " success " : False , " message " : " Invalid JSON options " } )
2021-01-12 17:58:40 +03:00
for key in request . json :
if key in RESTAPI_UNSUPPORTED_OPTIONS :
logger . warning ( " [ %s ] Unsupported option ' %s ' provided to scan_start() " % ( taskid , key ) )
return jsonize ( { " success " : False , " message " : " Unsupported option ' %s ' " % key } )
2013-01-25 21:11:31 +04:00
# Initialize sqlmap engine's options with user's provided options, if any
2013-01-29 05:39:27 +04:00
for option , value in request . json . items ( ) :
2013-12-15 03:19:58 +04:00
DataStore . tasks [ taskid ] . set_option ( option , value )
2012-12-15 02:00:42 +04:00
2013-01-29 20:11:25 +04:00
# Launch sqlmap engine in a separate process
2013-12-15 03:19:58 +04:00
DataStore . tasks [ taskid ] . engine_start ( )
2012-12-14 17:40:25 +04:00
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Started scan " % taskid )
2013-12-15 03:19:58 +04:00
return jsonize ( { " success " : True , " engineid " : DataStore . tasks [ taskid ] . engine_get_id ( ) } )
2012-12-14 17:40:25 +04:00
2013-01-29 05:39:27 +04:00
@get ( " /scan/<taskid>/stop " )
def scan_stop ( taskid ) :
2012-12-14 18:51:01 +04:00
"""
2013-01-29 05:39:27 +04:00
Stop a scan
2012-12-14 18:51:01 +04:00
"""
2017-08-04 14:37:49 +03:00
2018-06-10 00:38:00 +03:00
if ( taskid not in DataStore . tasks or DataStore . tasks [ taskid ] . engine_process ( ) is None or DataStore . tasks [ taskid ] . engine_has_terminated ( ) ) :
2013-12-15 19:59:47 +04:00
logger . warning ( " [ %s ] Invalid task ID provided to scan_stop() " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : False , " message " : " Invalid task ID " } )
2012-12-14 18:51:01 +04:00
2013-12-15 03:19:58 +04:00
DataStore . tasks [ taskid ] . engine_stop ( )
2013-01-29 20:11:25 +04:00
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Stopped scan " % taskid )
2013-01-29 20:11:25 +04:00
return jsonize ( { " success " : True } )
2013-01-10 02:09:50 +04:00
2013-01-29 05:39:27 +04:00
@get ( " /scan/<taskid>/kill " )
def scan_kill ( taskid ) :
"""
Kill a scan
"""
2017-08-04 14:37:49 +03:00
2018-06-10 00:38:00 +03:00
if ( taskid not in DataStore . tasks or DataStore . tasks [ taskid ] . engine_process ( ) is None or DataStore . tasks [ taskid ] . engine_has_terminated ( ) ) :
2013-12-15 19:59:47 +04:00
logger . warning ( " [ %s ] Invalid task ID provided to scan_kill() " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : False , " message " : " Invalid task ID " } )
2013-01-25 21:11:31 +04:00
2013-12-15 03:19:58 +04:00
DataStore . tasks [ taskid ] . engine_kill ( )
2013-01-29 20:11:25 +04:00
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Killed scan " % taskid )
2013-01-29 20:11:25 +04:00
return jsonize ( { " success " : True } )
@get ( " /scan/<taskid>/status " )
def scan_status ( taskid ) :
"""
Returns status of a scan
"""
2017-08-04 14:37:49 +03:00
2013-12-15 03:19:58 +04:00
if taskid not in DataStore . tasks :
2013-12-15 19:59:47 +04:00
logger . warning ( " [ %s ] Invalid task ID provided to scan_status() " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : False , " message " : " Invalid task ID " } )
2013-01-29 20:11:25 +04:00
2014-01-02 15:15:56 +04:00
if DataStore . tasks [ taskid ] . engine_process ( ) is None :
2014-01-02 13:55:40 +04:00
status = " not running "
else :
status = " terminated " if DataStore . tasks [ taskid ] . engine_has_terminated ( ) is True else " running "
2013-01-29 20:11:25 +04:00
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Retrieved scan status " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( {
" success " : True ,
" status " : status ,
2013-12-15 03:19:58 +04:00
" returncode " : DataStore . tasks [ taskid ] . engine_get_returncode ( )
2013-12-14 19:22:30 +04:00
} )
2012-12-14 18:51:01 +04:00
2013-01-29 05:39:27 +04:00
@get ( " /scan/<taskid>/data " )
def scan_data ( taskid ) :
"""
Retrieve the data of a scan
"""
2017-08-04 14:37:49 +03:00
2013-01-29 05:39:27 +04:00
json_data_message = list ( )
json_errors_message = list ( )
2013-12-15 03:19:58 +04:00
if taskid not in DataStore . tasks :
2013-12-15 19:59:47 +04:00
logger . warning ( " [ %s ] Invalid task ID provided to scan_data() " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : False , " message " : " Invalid task ID " } )
2013-01-29 05:39:27 +04:00
# Read all data from the IPC database for the taskid
2017-06-14 15:13:41 +03:00
for status , content_type , value in DataStore . current_db . execute ( " SELECT status, content_type, value FROM data WHERE taskid = ? ORDER BY id ASC " , ( taskid , ) ) :
json_data_message . append ( { " status " : status , " type " : content_type , " value " : dejsonize ( value ) } )
2013-01-29 05:39:27 +04:00
# Read all error messages from the IPC database
2017-06-14 15:13:41 +03:00
for error in DataStore . current_db . execute ( " SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC " , ( taskid , ) ) :
2013-01-29 05:39:27 +04:00
json_errors_message . append ( error )
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Retrieved scan data and error messages " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : True , " data " : json_data_message , " error " : json_errors_message } )
2013-01-29 05:39:27 +04:00
2013-01-10 04:01:28 +04:00
# Functions to handle scans' logs
@get ( " /scan/<taskid>/log/<start>/<end> " )
def scan_log_limited ( taskid , start , end ) :
"""
2013-01-24 16:57:24 +04:00
Retrieve a subset of log messages
2013-01-10 04:01:28 +04:00
"""
2017-08-04 14:37:49 +03:00
2013-01-29 05:39:27 +04:00
json_log_messages = list ( )
2013-01-10 04:01:28 +04:00
2013-12-15 03:19:58 +04:00
if taskid not in DataStore . tasks :
2015-09-10 12:34:03 +03:00
logger . warning ( " [ %s ] Invalid task ID provided to scan_log_limited() " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : False , " message " : " Invalid task ID " } )
2013-01-10 04:01:28 +04:00
2020-12-04 13:47:09 +03:00
if not start . isdigit ( ) or not end . isdigit ( ) or int ( end ) < int ( start ) :
2013-12-15 19:59:47 +04:00
logger . warning ( " [ %s ] Invalid start or end value provided to scan_log_limited() " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : False , " message " : " Invalid start or end value, must be digits " } )
2013-01-10 04:01:28 +04:00
2013-01-24 16:57:24 +04:00
start = max ( 1 , int ( start ) )
2013-01-10 04:01:28 +04:00
end = max ( 1 , int ( end ) )
2013-01-29 05:39:27 +04:00
# Read a subset of log messages from the IPC database
2017-06-14 15:13:41 +03:00
for time_ , level , message in DataStore . current_db . execute ( " SELECT time, level, message FROM logs WHERE taskid = ? AND id >= ? AND id <= ? ORDER BY id ASC " , ( taskid , start , end ) ) :
2013-01-29 05:39:27 +04:00
json_log_messages . append ( { " time " : time_ , " level " : level , " message " : message } )
2013-01-10 04:01:28 +04:00
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Retrieved scan log messages subset " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : True , " log " : json_log_messages } )
2013-01-10 04:01:28 +04:00
2012-12-15 04:12:22 +04:00
@get ( " /scan/<taskid>/log " )
def scan_log ( taskid ) :
2012-12-14 21:21:19 +04:00
"""
2012-12-17 15:28:03 +04:00
Retrieve the log messages
2012-12-14 21:21:19 +04:00
"""
2017-08-04 14:37:49 +03:00
2013-01-29 05:39:27 +04:00
json_log_messages = list ( )
2013-01-10 04:01:28 +04:00
2013-12-15 03:19:58 +04:00
if taskid not in DataStore . tasks :
2015-09-10 12:34:03 +03:00
logger . warning ( " [ %s ] Invalid task ID provided to scan_log() " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : False , " message " : " Invalid task ID " } )
2012-12-14 21:21:19 +04:00
2013-01-29 05:39:27 +04:00
# Read all log messages from the IPC database
2017-06-14 15:13:41 +03:00
for time_ , level , message in DataStore . current_db . execute ( " SELECT time, level, message FROM logs WHERE taskid = ? ORDER BY id ASC " , ( taskid , ) ) :
2013-01-29 05:39:27 +04:00
json_log_messages . append ( { " time " : time_ , " level " : level , " message " : message } )
2013-01-10 04:01:28 +04:00
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Retrieved scan log messages " % taskid )
2013-12-14 19:22:30 +04:00
return jsonize ( { " success " : True , " log " : json_log_messages } )
2012-12-14 21:21:19 +04:00
# Function to handle files inside the output directory
2012-12-14 19:52:35 +04:00
@get ( " /download/<taskid>/<target>/<filename:path> " )
2012-12-14 17:40:25 +04:00
def download ( taskid , target , filename ) :
2012-12-14 16:01:13 +04:00
"""
Download a certain file from the file system
"""
2017-08-04 14:37:49 +03:00
2013-12-15 03:19:58 +04:00
if taskid not in DataStore . tasks :
2013-12-15 19:59:47 +04:00
logger . warning ( " [ %s ] Invalid task ID provided to download() " % taskid )
2013-12-14 19:33:17 +04:00
return jsonize ( { " success " : False , " message " : " Invalid task ID " } )
2012-12-14 17:40:25 +04:00
2016-02-26 19:10:32 +03:00
path = os . path . abspath ( os . path . join ( paths . SQLMAP_OUTPUT_PATH , target , filename ) )
# Prevent file path traversal
if not path . startswith ( paths . SQLMAP_OUTPUT_PATH ) :
2013-12-15 19:59:47 +04:00
logger . warning ( " [ %s ] Forbidden path ( %s ) " % ( taskid , target ) )
2013-12-14 19:33:17 +04:00
return jsonize ( { " success " : False , " message " : " Forbidden path " } )
2012-12-14 20:18:45 +04:00
2016-02-26 19:10:32 +03:00
if os . path . isfile ( path ) :
2018-12-17 18:08:14 +03:00
logger . debug ( " ( %s ) Retrieved content of file %s " % ( taskid , target ) )
2019-05-13 12:51:47 +03:00
content = openFile ( path , " rb " ) . read ( )
return jsonize ( { " success " : True , " file " : encodeBase64 ( content , binary = False ) } )
2012-12-14 16:01:13 +04:00
else :
2013-12-15 19:59:47 +04:00
logger . warning ( " [ %s ] File does not exist %s " % ( taskid , target ) )
2013-12-14 19:33:17 +04:00
return jsonize ( { " success " : False , " message " : " File does not exist " } )
2012-12-14 16:01:13 +04:00
2020-05-20 16:20:44 +03:00
@get ( " /version " )
def version ( token = None ) :
"""
Fetch server version
"""
logger . debug ( " Fetched version ( %s ) " % ( " admin " if is_admin ( token ) else request . remote_addr ) )
return jsonize ( { " success " : True , " version " : VERSION_STRING . split ( ' / ' ) [ - 1 ] } )
2017-08-04 14:37:49 +03:00
def server ( host = RESTAPI_DEFAULT_ADDRESS , port = RESTAPI_DEFAULT_PORT , adapter = RESTAPI_DEFAULT_ADAPTER , username = None , password = None ) :
2012-12-14 06:52:31 +04:00
"""
2012-12-20 18:35:02 +04:00
REST - JSON API server
2012-12-14 06:52:31 +04:00
"""
2017-08-04 14:37:49 +03:00
2019-05-03 14:20:15 +03:00
DataStore . admin_token = encodeHex ( os . urandom ( 16 ) , binary = False )
2017-08-04 14:37:49 +03:00
DataStore . username = username
DataStore . password = password
_ , Database . filepath = tempfile . mkstemp ( prefix = MKSTEMP_PREFIX . IPC , text = False )
os . close ( _ )
2013-01-29 19:38:09 +04:00
2017-01-20 15:11:12 +03:00
if port == 0 : # random
with contextlib . closing ( socket . socket ( socket . AF_INET , socket . SOCK_STREAM ) ) as s :
s . bind ( ( host , 0 ) )
port = s . getsockname ( ) [ 1 ]
2013-01-29 19:38:09 +04:00
logger . info ( " Running REST-JSON API server at ' %s : %d ' .. " % ( host , port ) )
2018-11-15 17:27:05 +03:00
logger . info ( " Admin (secret) token: %s " % DataStore . admin_token )
2017-04-10 15:50:17 +03:00
logger . debug ( " IPC database: ' %s ' " % Database . filepath )
2013-01-29 19:38:09 +04:00
# Initialize IPC database
2013-12-15 03:19:58 +04:00
DataStore . current_db = Database ( )
DataStore . current_db . connect ( )
DataStore . current_db . init ( )
2012-12-15 04:12:22 +04:00
2012-12-20 18:35:02 +04:00
# Run RESTful API
2015-12-09 14:20:09 +03:00
try :
2017-04-07 15:30:52 +03:00
# Supported adapters: aiohttp, auto, bjoern, cgi, cherrypy, diesel, eventlet, fapws3, flup, gae, gevent, geventSocketIO, gunicorn, meinheld, paste, rocket, tornado, twisted, waitress, wsgiref
# Reference: https://bottlepy.org/docs/dev/deployment.html || bottle.server_names
2016-01-27 12:03:30 +03:00
if adapter == " gevent " :
2016-01-22 06:33:12 +03:00
from gevent import monkey
monkey . patch_all ( )
2016-01-27 12:03:30 +03:00
elif adapter == " eventlet " :
2016-01-22 06:33:12 +03:00
import eventlet
eventlet . monkey_patch ( )
2016-01-27 12:03:30 +03:00
logger . debug ( " Using adapter ' %s ' to run bottle " % adapter )
2017-08-04 14:37:49 +03:00
run ( host = host , port = port , quiet = True , debug = True , server = adapter )
2019-01-22 02:40:48 +03:00
except socket . error as ex :
2015-12-09 14:20:09 +03:00
if " already in use " in getSafeExString ( ex ) :
logger . error ( " Address already in use ( ' %s : %s ' ) " % ( host , port ) )
else :
raise
2016-01-27 12:03:30 +03:00
except ImportError :
2017-04-07 15:46:41 +03:00
if adapter . lower ( ) not in server_names :
errMsg = " Adapter ' %s ' is unknown. " % adapter
2019-01-22 05:00:44 +03:00
errMsg + = " List of supported adapters: %s " % ' , ' . join ( sorted ( list ( server_names . keys ( ) ) ) )
2017-04-07 15:46:41 +03:00
else :
2017-04-07 15:55:25 +03:00
errMsg = " Server support for adapter ' %s ' is not installed on this system " % adapter
2021-07-19 14:58:54 +03:00
errMsg + = " (Note: you can try to install it with ' apt install python- %s ' or ' pip %s install %s ' ) " % ( adapter , ' 3 ' if six . PY3 else " " , adapter )
2016-01-27 12:03:30 +03:00
logger . critical ( errMsg )
2013-12-14 18:44:10 +04:00
2015-09-10 16:06:07 +03:00
def _client ( url , options = None ) :
2018-11-15 17:27:05 +03:00
logger . debug ( " Calling ' %s ' " % url )
2015-09-09 16:14:04 +03:00
try :
2017-08-04 14:37:49 +03:00
headers = { " Content-Type " : " application/json " }
2020-01-16 00:47:06 +03:00
if options is not None :
data = getBytes ( jsonize ( options ) )
else :
data = None
2017-08-04 14:37:49 +03:00
if DataStore . username or DataStore . password :
2019-05-03 14:20:15 +03:00
headers [ " Authorization " ] = " Basic %s " % encodeBase64 ( " %s : %s " % ( DataStore . username or " " , DataStore . password or " " ) , binary = False )
2017-08-04 14:37:49 +03:00
2019-03-27 15:33:46 +03:00
req = _urllib . request . Request ( url , data , headers )
response = _urllib . request . urlopen ( req )
2020-01-16 00:47:06 +03:00
text = getText ( response . read ( ) )
2015-09-09 16:14:04 +03:00
except :
2015-09-10 16:06:07 +03:00
if options :
2015-09-17 17:18:58 +03:00
logger . error ( " Failed to load and parse %s " % url )
2015-09-09 16:14:04 +03:00
raise
return text
2017-08-04 14:37:49 +03:00
def client ( host = RESTAPI_DEFAULT_ADDRESS , port = RESTAPI_DEFAULT_PORT , username = None , password = None ) :
2012-12-15 04:29:35 +04:00
"""
REST - JSON API client
"""
2016-02-16 10:58:18 +03:00
2017-08-04 14:37:49 +03:00
DataStore . username = username
DataStore . password = password
2016-02-16 10:58:18 +03:00
dbgMsg = " Example client access from command line: "
2019-05-08 13:28:50 +03:00
dbgMsg + = " \n \t $ taskid=$(curl http:// %s : %d /task/new 2>1 | grep -o -I ' [a-f0-9] \\ { 16 \\ } ' ) && echo $taskid " % ( host , port )
2016-02-16 10:58:18 +03:00
dbgMsg + = " \n \t $ curl -H \" Content-Type: application/json \" -X POST -d ' { \" url \" : \" http://testphp.vulnweb.com/artists.php?artist=1 \" } ' http:// %s : %d /scan/$taskid/start " % ( host , port )
dbgMsg + = " \n \t $ curl http:// %s : %d /scan/$taskid/data " % ( host , port )
dbgMsg + = " \n \t $ curl http:// %s : %d /scan/$taskid/log " % ( host , port )
logger . debug ( dbgMsg )
2012-12-14 16:01:13 +04:00
addr = " http:// %s : %d " % ( host , port )
2013-01-29 19:38:09 +04:00
logger . info ( " Starting REST-JSON API client to ' %s ' ... " % addr )
2015-09-09 16:14:04 +03:00
2015-09-10 16:01:30 +03:00
try :
_client ( addr )
2019-01-22 02:40:48 +03:00
except Exception as ex :
2019-03-27 15:33:46 +03:00
if not isinstance ( ex , _urllib . error . HTTPError ) or ex . code == _http_client . UNAUTHORIZED :
2016-01-27 12:03:30 +03:00
errMsg = " There has been a problem while connecting to the "
2015-09-10 16:01:30 +03:00
errMsg + = " REST-JSON API server at ' %s ' " % addr
2021-09-02 22:44:41 +03:00
errMsg + = " ( %s ) " % getSafeExString ( ex )
2015-09-10 16:01:30 +03:00
logger . critical ( errMsg )
return
2020-05-20 16:20:44 +03:00
commands = ( " help " , " new " , " use " , " data " , " log " , " status " , " option " , " stop " , " kill " , " list " , " flush " , " version " , " exit " , " bye " , " quit " )
2021-09-09 21:27:45 +03:00
colors = ( ' red ' , ' green ' , ' yellow ' , ' blue ' , ' magenta ' , ' cyan ' , ' lightgrey ' , ' lightred ' , ' lightgreen ' , ' lightyellow ' , ' lightblue ' , ' lightmagenta ' , ' lightcyan ' )
2018-06-21 00:04:58 +03:00
autoCompletion ( AUTOCOMPLETE_TYPE . API , commands = commands )
2015-09-10 16:01:30 +03:00
taskid = None
logger . info ( " Type ' help ' or ' ? ' for list of available commands " )
2015-09-09 16:14:04 +03:00
while True :
2015-09-10 16:01:30 +03:00
try :
2021-09-09 21:27:45 +03:00
color = colors [ int ( taskid or " 0 " , 16 ) % len ( colors ) ]
command = _input ( " api %s > " % ( " ( %s ) " % setColor ( taskid , color ) if taskid else " " ) ) . strip ( )
2016-08-08 16:55:39 +03:00
command = re . sub ( r " \ A( \ w+) " , lambda match : match . group ( 1 ) . lower ( ) , command )
2015-09-10 16:01:30 +03:00
except ( EOFError , KeyboardInterrupt ) :
2019-01-22 03:28:24 +03:00
print ( )
2015-09-10 16:01:30 +03:00
break
2015-09-16 00:15:16 +03:00
if command in ( " data " , " log " , " status " , " stop " , " kill " ) :
2015-09-10 16:01:30 +03:00
if not taskid :
logger . error ( " No task ID in use " )
2015-09-09 16:14:04 +03:00
continue
2015-09-17 17:18:58 +03:00
raw = _client ( " %s /scan/ %s / %s " % ( addr , taskid , command ) )
2015-09-10 16:01:30 +03:00
res = dejsonize ( raw )
if not res [ " success " ] :
2015-09-17 17:18:58 +03:00
logger . error ( " Failed to execute command %s " % command )
2015-09-10 16:01:30 +03:00
dataToStdout ( " %s \n " % raw )
2017-07-05 14:51:48 +03:00
2017-02-25 09:58:59 +03:00
elif command . startswith ( " option " ) :
2017-02-25 06:24:00 +03:00
if not taskid :
logger . error ( " No task ID in use " )
continue
try :
2018-11-15 19:13:13 +03:00
command , option = command . split ( " " , 1 )
2017-02-25 06:24:00 +03:00
except ValueError :
raw = _client ( " %s /option/ %s /list " % ( addr , taskid ) )
else :
2018-11-15 19:13:13 +03:00
options = re . split ( r " \ s*, \ s* " , option . strip ( ) )
2017-02-25 06:24:00 +03:00
raw = _client ( " %s /option/ %s /get " % ( addr , taskid ) , options )
res = dejsonize ( raw )
if not res [ " success " ] :
logger . error ( " Failed to execute command %s " % command )
dataToStdout ( " %s \n " % raw )
2015-09-10 16:01:30 +03:00
2015-09-16 00:15:16 +03:00
elif command . startswith ( " new " ) :
2015-09-10 16:01:30 +03:00
if ' ' not in command :
logger . error ( " Program arguments are missing " )
2015-09-09 16:14:04 +03:00
continue
2017-02-25 09:54:54 +03:00
try :
argv = [ " sqlmap.py " ] + shlex . split ( command ) [ 1 : ]
2019-01-22 02:40:48 +03:00
except Exception as ex :
2021-09-02 22:44:41 +03:00
logger . error ( " Error occurred while parsing arguments ( ' %s ' ) " % getSafeExString ( ex ) )
2017-02-25 09:54:54 +03:00
taskid = None
continue
2015-09-10 16:01:30 +03:00
2015-09-09 16:14:04 +03:00
try :
2015-09-10 16:06:07 +03:00
cmdLineOptions = cmdLineParser ( argv ) . __dict__
2015-09-09 16:14:04 +03:00
except :
2015-09-10 16:01:30 +03:00
taskid = None
continue
2015-09-15 18:23:59 +03:00
for key in list ( cmdLineOptions ) :
if cmdLineOptions [ key ] is None :
del cmdLineOptions [ key ]
2015-09-10 16:01:30 +03:00
2015-09-17 17:18:58 +03:00
raw = _client ( " %s /task/new " % addr )
2015-09-10 16:01:30 +03:00
res = dejsonize ( raw )
if not res [ " success " ] :
2021-01-12 17:58:40 +03:00
logger . error ( " Failed to create new task ( ' %s ' ) " % res . get ( " message " , " " ) )
2015-09-09 16:14:04 +03:00
continue
2015-09-10 16:01:30 +03:00
taskid = res [ " taskid " ]
logger . info ( " New task ID is ' %s ' " % taskid )
2015-09-17 17:18:58 +03:00
raw = _client ( " %s /scan/ %s /start " % ( addr , taskid ) , cmdLineOptions )
2015-09-10 16:01:30 +03:00
res = dejsonize ( raw )
if not res [ " success " ] :
2021-01-12 17:58:40 +03:00
logger . error ( " Failed to start scan ( ' %s ' ) " % res . get ( " message " , " " ) )
2015-09-09 16:14:04 +03:00
continue
logger . info ( " Scanning started " )
2015-09-10 16:01:30 +03:00
2015-09-16 00:15:16 +03:00
elif command . startswith ( " use " ) :
2015-09-10 16:01:30 +03:00
taskid = ( command . split ( ) [ 1 ] if ' ' in command else " " ) . strip ( " ' \" " )
if not taskid :
logger . error ( " Task ID is missing " )
taskid = None
continue
elif not re . search ( r " \ A[0-9a-fA-F] {16} \ Z " , taskid ) :
logger . error ( " Invalid task ID ' %s ' " % taskid )
taskid = None
continue
logger . info ( " Switching to task ID ' %s ' " % taskid )
2020-05-20 16:20:44 +03:00
elif command in ( " version " , ) :
raw = _client ( " %s / %s " % ( addr , command ) )
res = dejsonize ( raw )
if not res [ " success " ] :
logger . error ( " Failed to execute command %s " % command )
dataToStdout ( " %s \n " % raw )
2015-09-16 00:15:16 +03:00
elif command in ( " list " , " flush " ) :
2018-11-15 17:27:05 +03:00
raw = _client ( " %s /admin/ %s " % ( addr , command ) )
2015-09-14 17:31:02 +03:00
res = dejsonize ( raw )
if not res [ " success " ] :
2015-09-17 17:18:58 +03:00
logger . error ( " Failed to execute command %s " % command )
elif command == " flush " :
taskid = None
2015-09-14 17:31:02 +03:00
dataToStdout ( " %s \n " % raw )
2015-09-16 00:15:16 +03:00
elif command in ( " exit " , " bye " , " quit " , ' q ' ) :
2015-09-09 16:14:04 +03:00
return
2015-09-10 16:01:30 +03:00
2015-09-16 00:15:16 +03:00
elif command in ( " help " , " ? " ) :
2018-03-13 15:45:42 +03:00
msg = " help Show this help message \n "
2017-02-25 09:54:54 +03:00
msg + = " new ARGS Start a new scan task with provided arguments (e.g. ' new -u \" http://testphp.vulnweb.com/artists.php?artist=1 \" ' ) \n "
msg + = " use TASKID Switch current context to different task (e.g. ' use c04d8c5c7582efb4 ' ) \n "
msg + = " data Retrieve and show data for current task \n "
msg + = " log Retrieve and show log for current task \n "
msg + = " status Retrieve and show status for current task \n "
msg + = " option OPTION Retrieve and show option for current task \n "
msg + = " options Retrieve and show all options for current task \n "
msg + = " stop Stop current task \n "
msg + = " kill Kill current task \n "
msg + = " list Display all tasks \n "
2020-05-20 16:20:44 +03:00
msg + = " version Fetch server version \n "
2017-02-25 09:54:54 +03:00
msg + = " flush Flush tasks (delete all tasks) \n "
msg + = " exit Exit this client \n "
2015-09-10 16:01:30 +03:00
dataToStdout ( msg )
elif command :
logger . error ( " Unknown command ' %s ' " % command )