2013-02-14 15:32:17 +04:00
#!/usr/bin/env python
2012-12-14 06:52:31 +04:00
"""
2013-01-18 18:07:51 +04:00
Copyright ( c ) 2006 - 2013 sqlmap developers ( http : / / sqlmap . org / )
2012-12-14 06:52:31 +04:00
See the file ' doc/COPYING ' for copying permission
"""
2013-01-25 21:11:31 +04:00
import logging
2012-12-14 06:52:31 +04:00
import os
2012-12-15 02:00:42 +04:00
import shutil
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
2013-01-07 15:10:33 +04:00
from subprocess import PIPE
2012-12-20 21:56:53 +04:00
2012-12-20 20:53:43 +04:00
from lib . core . common import unArrayizeValue
2013-01-09 16:34:45 +04:00
from lib . core . convert import base64pickle
2012-12-14 16:04:44 +04:00
from lib . core . convert import hexencode
2013-01-29 05:39:27 +04:00
from lib . core . convert import dejsonize
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:15:04 +04:00
from lib . core . data import paths
2012-12-14 16:04:44 +04:00
from lib . core . data import logger
2013-01-10 02:09:50 +04:00
from lib . core . datatype import AttribDict
2012-12-20 20:53:43 +04:00
from lib . core . defaults import _defaults
2013-02-03 15:31:05 +04:00
from lib . core . enums import CONTENT_STATUS
2013-02-05 18:43:03 +04:00
from lib . core . enums import PART_RUN_CONTENT_TYPES
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
2013-01-29 05:39:27 +04:00
from lib . core . subprocessng import Popen
2013-01-10 04:01:28 +04:00
from lib . core . subprocessng import send_all
from lib . core . subprocessng import recv_some
2012-12-22 03:34:37 +04:00
from thirdparty . bottle . bottle import abort
from thirdparty . bottle . bottle import error
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
from thirdparty . bottle . bottle import static_file
2012-12-20 18:35:02 +04:00
RESTAPI_SERVER_HOST = " 127.0.0.1 "
RESTAPI_SERVER_PORT = 8775
2012-12-14 06:52:31 +04:00
2012-12-14 17:40:25 +04:00
# Local global variables
adminid = " "
2013-01-29 05:39:27 +04:00
db = None
2013-02-04 01:39:26 +04:00
db_filepath = None
2013-01-29 05:39:27 +04:00
tasks = dict ( )
2012-12-14 06:52:31 +04:00
2013-01-29 05:39:27 +04:00
# API objects
class Database ( object ) :
2013-02-03 15:31:05 +04:00
global db_filepath
2013-01-29 05:39:27 +04:00
LOGS_TABLE = " CREATE TABLE logs(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, time TEXT, level TEXT, message TEXT) "
DATA_TABLE = " CREATE TABLE data(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, status INTEGER, content_type INTEGER, value TEXT) "
ERRORS_TABLE = " CREATE TABLE errors(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, error TEXT) "
2013-02-03 15:31:05 +04:00
def __init__ ( self , database = None ) :
if database :
self . database = database
else :
self . database = db_filepath
2013-01-29 05:39:27 +04:00
2013-02-03 15:31:05 +04:00
def connect ( self , who = " server " ) :
2013-01-29 19:38:09 +04:00
self . connection = sqlite3 . connect ( self . database , timeout = 3 , isolation_level = None )
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 ) :
self . cursor . close ( )
self . connection . close ( )
2013-02-06 21:09:43 +04:00
def commit ( self ) :
self . cursor . commit ( )
2013-01-29 05:39:27 +04:00
def execute ( self , statement , arguments = None ) :
if arguments :
self . cursor . execute ( statement , arguments )
else :
self . cursor . execute ( statement )
if statement . lstrip ( ) . upper ( ) . startswith ( " SELECT " ) :
return self . cursor . fetchall ( )
2013-02-03 15:31:05 +04:00
def init ( self ) :
2013-01-29 05:39:27 +04:00
self . execute ( self . LOGS_TABLE )
self . execute ( self . DATA_TABLE )
self . execute ( self . ERRORS_TABLE )
class Task ( object ) :
2013-02-03 15:31:05 +04:00
global db_filepath
2013-01-29 05:39:27 +04:00
def __init__ ( self , taskid ) :
self . process = None
self . output_directory = None
self . initialize_options ( taskid )
def initialize_options ( self , taskid ) :
dataype = { " boolean " : False , " string " : None , " integer " : None , " float " : None }
self . options = AttribDict ( )
for _ in optDict :
for name , type_ in optDict [ _ ] . items ( ) :
type_ = unArrayizeValue ( type_ )
self . options [ name ] = _defaults . get ( name , dataype [ type_ ] )
# Let sqlmap engine knows it is getting called by the API, the task ID and the file path of the IPC database
self . options . api = True
self . options . taskid = taskid
2013-02-03 15:31:05 +04:00
self . options . database = db_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
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
def set_output_directory ( self ) :
2013-02-07 04:05:26 +04:00
if not self . output_directory or not os . path . isdir ( self . output_directory ) :
self . output_directory = tempfile . mkdtemp ( prefix = " sqlmapoutput- " )
self . set_option ( " oDir " , self . output_directory )
2013-01-29 05:39:27 +04:00
def clean_filesystem ( self ) :
shutil . rmtree ( self . output_directory )
def engine_start ( self ) :
2013-01-29 19:38:09 +04:00
self . process = Popen ( " python sqlmap.py --pickled-options %s " % base64pickle ( self . options ) , shell = True , stdin = PIPE , close_fds = False )
2013-01-29 05:39:27 +04:00
def engine_stop ( self ) :
if self . process :
2013-01-29 19:38:09 +04:00
return self . process . terminate ( )
else :
return None
2013-01-29 05:39:27 +04:00
def engine_kill ( self ) :
if self . process :
2013-01-29 19:38:09 +04:00
return self . process . kill ( )
else :
return None
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 ) :
self . process . poll ( )
return self . process . returncode
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 ) :
encoding = " UTF-8 "
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-07 04:05:26 +04:00
insert = True
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
2013-02-07 01:32:26 +04:00
#print >>sys.__stdout__, "value: %s\nstatus: %d\ncontent_type: %d\nkb.partRun: %s\n--------------" % (value, status, content_type, kb.partRun)
2013-02-07 04:05:26 +04:00
output = conf . database_cursor . execute ( " SELECT id, status, value FROM data WHERE taskid = ? AND content_type = ? " ,
2013-02-07 01:32:26 +04:00
( self . taskid , content_type ) )
2013-02-04 02:09:33 +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 :
for index in xrange ( 0 , len ( output ) - 1 ) :
if output [ index ] [ 1 ] == CONTENT_STATUS . COMPLETE :
insert = False
else :
conf . database_cursor . execute ( " DELETE FROM data WHERE id = ? " , ( output [ index ] [ 0 ] , ) )
if insert :
conf . database_cursor . 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 :
conf . database_cursor . execute ( " INSERT INTO data VALUES(NULL, ?, ?, ?, ?) " ,
( self . taskid , status , content_type , jsonize ( value ) ) )
else :
2013-02-07 04:05:26 +04:00
new_value = " %s %s " % ( dejsonize ( output [ 0 ] [ 2 ] ) , value )
2013-02-03 15:31:05 +04:00
conf . database_cursor . execute ( " UPDATE data SET value = ? WHERE id = ? " ,
( jsonize ( new_value ) , output [ 0 ] [ 0 ] ) )
2013-01-25 21:11:31 +04:00
else :
2013-01-29 19:38:09 +04:00
conf . database_cursor . 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
"""
2013-01-29 05:39:27 +04:00
conf . database_cursor . execute ( " INSERT INTO logs VALUES(NULL, ?, ?, ?, ?) " ,
2013-01-29 21:00:28 +04:00
( 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 ( ) :
2013-01-29 05:39:27 +04:00
if hasattr ( conf , " api " ) :
2013-02-03 15:31:05 +04:00
conf . database_cursor = Database ( conf . database )
conf . database_cursor . connect ( " client " )
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
2012-12-14 17:40:25 +04:00
def is_admin ( taskid ) :
global adminid
if adminid != taskid :
2012-12-14 06:52:31 +04:00
return False
else :
return True
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 "
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-01-10 16:18:44 +04:00
@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-01-10 16:18:44 +04:00
@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-01-10 16:18:44 +04:00
@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-01-10 16:18:44 +04:00
@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 "
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
"""
2012-12-14 17:40:25 +04:00
Create new task ID
2012-12-14 06:52:31 +04:00
"""
2012-12-14 17:40:25 +04:00
global tasks
2012-12-15 02:00:42 +04:00
2013-01-29 05:39:27 +04:00
taskid = hexencode ( os . urandom ( 8 ) )
tasks [ taskid ] = Task ( taskid )
2013-01-24 16:57:24 +04:00
2013-01-29 19:38:09 +04:00
logger . debug ( " Created new task ID: %s " % taskid )
2012-12-14 17:40:25 +04:00
return jsonize ( { " 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
"""
2013-01-29 19:38:09 +04:00
Delete own task ID
2012-12-14 06:52:31 +04:00
"""
2013-01-29 05:39:27 +04:00
if taskid in tasks :
tasks [ taskid ] . clean_filesystem ( )
2012-12-14 22:13:21 +04:00
tasks . pop ( taskid )
2013-01-29 19:38:09 +04:00
logger . debug ( " Deleted task ID: %s " % taskid )
2012-12-14 16:15:04 +04:00
return jsonize ( { " success " : True } )
2012-12-14 06:52:31 +04:00
else :
2012-12-14 17:40:25 +04:00
abort ( 500 , " Invalid task ID " )
2012-12-14 06:52:31 +04:00
2013-01-29 19:38:09 +04:00
###################
# Admin functions #
###################
@get ( " /admin/<taskid>/list " )
2012-12-14 17:40:25 +04:00
def task_list ( taskid ) :
2012-12-14 06:52:31 +04:00
"""
2013-01-29 20:11:25 +04:00
List task pull
2012-12-14 06:52:31 +04:00
"""
2012-12-14 17:40:25 +04:00
if is_admin ( taskid ) :
2013-01-29 20:11:25 +04:00
logger . debug ( " Listed task pull " )
2013-01-29 19:38:09 +04:00
return jsonize ( { " tasks " : tasks , " tasks_num " : len ( tasks ) } )
2012-12-14 06:52:31 +04:00
else :
abort ( 401 )
2013-01-29 19:38:09 +04:00
@get ( " /admin/<taskid>/flush " )
2012-12-14 17:40:25 +04:00
def task_flush ( taskid ) :
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
"""
2012-12-14 17:40:25 +04:00
global tasks
2012-12-15 02:00:42 +04:00
2012-12-14 17:40:25 +04:00
if is_admin ( taskid ) :
2013-01-29 05:39:27 +04:00
for task in tasks :
tasks [ task ] . clean_filesystem ( )
2012-12-15 02:00:42 +04:00
2013-01-29 05:39:27 +04:00
tasks = dict ( )
2013-01-29 20:11:25 +04:00
logger . debug ( " Flushed task pull " )
2012-12-14 16:15:04 +04:00
return jsonize ( { " success " : True } )
2012-12-14 06:52:31 +04:00
else :
abort ( 401 )
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
"""
if taskid not in tasks :
abort ( 500 , " Invalid task ID " )
2013-01-29 19:38:09 +04:00
return jsonize ( { " options " : tasks [ taskid ] . get_options ( ) } )
2012-12-14 18:51:01 +04:00
@post ( " /option/<taskid>/get " )
def option_get ( taskid ) :
"""
Get the value of an option ( command line switch ) for a certain task ID
"""
2013-01-29 20:11:25 +04:00
global tasks
2012-12-14 18:51:01 +04:00
if taskid not in tasks :
abort ( 500 , " Invalid task ID " )
option = request . json . get ( " option " , " " )
2012-12-15 04:12:22 +04:00
if option in tasks [ taskid ] :
2013-01-29 05:39:27 +04:00
return jsonize ( { option : tasks [ taskid ] . get_option ( option ) } )
2012-12-14 18:51:01 +04:00
else :
2013-01-29 20:11:25 +04:00
return jsonize ( { option : " not set " } )
2012-12-14 18:51:01 +04:00
@post ( " /option/<taskid>/set " )
def option_set ( taskid ) :
"""
Set an option ( command line switch ) for a certain task ID
"""
2012-12-15 02:00:42 +04:00
global tasks
2012-12-14 18:51:01 +04:00
if taskid not in tasks :
abort ( 500 , " Invalid task ID " )
2013-01-29 05:39:27 +04:00
for option , value in request . json . items ( ) :
tasks [ taskid ] . set_option ( option , value )
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
"""
2012-12-15 02:00:42 +04:00
global tasks
2012-12-14 17:40:25 +04:00
if taskid not in tasks :
abort ( 500 , " Invalid task ID " )
2012-12-14 06:52:31 +04:00
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 ( ) :
tasks [ taskid ] . set_option ( option , value )
2012-12-15 02:00:42 +04:00
2013-01-25 21:11:31 +04:00
# Overwrite output directory value to a temporary directory
2013-01-29 05:39:27 +04:00
tasks [ taskid ] . set_output_directory ( )
2012-12-15 02:00:42 +04:00
2013-01-29 20:11:25 +04:00
# Launch sqlmap engine in a separate process
2013-01-29 05:39:27 +04:00
tasks [ taskid ] . engine_start ( )
2012-12-14 17:40:25 +04:00
2013-01-29 20:11:25 +04:00
logger . debug ( " Started scan for task ID %s " % taskid )
2013-01-29 19:38:09 +04:00
return jsonize ( { " success " : True , " engineid " : 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
"""
2012-12-14 22:13:21 +04:00
global tasks
2012-12-14 18:51:01 +04:00
if taskid not in tasks :
abort ( 500 , " Invalid task ID " )
2013-01-29 20:11:25 +04:00
tasks [ taskid ] . engine_stop ( )
logger . debug ( " Stopped scan for task ID %s " % taskid )
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
"""
global tasks
2013-01-25 21:11:31 +04:00
2013-01-29 05:39:27 +04:00
if taskid not in tasks :
abort ( 500 , " Invalid task ID " )
2013-01-25 21:11:31 +04:00
2013-01-29 20:11:25 +04:00
tasks [ taskid ] . engine_kill ( )
logger . debug ( " Killed scan for task ID %s " % taskid )
return jsonize ( { " success " : True } )
@get ( " /scan/<taskid>/status " )
def scan_status ( taskid ) :
"""
Returns status of a scan
"""
global tasks
if taskid not in tasks :
abort ( 500 , " Invalid task ID " )
status = " terminated " if tasks [ taskid ] . engine_has_terminated ( ) is True else " running "
logger . debug ( " Requested status of scan for task ID %s " % taskid )
return jsonize ( { " status " : status , " returncode " : tasks [ taskid ] . engine_get_returncode ( ) } )
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
"""
global db
global tasks
json_data_message = list ( )
json_errors_message = list ( )
if taskid not in tasks :
abort ( 500 , " Invalid task ID " )
# Read all data from the IPC database for the taskid
for status , content_type , value in db . execute ( " SELECT status, content_type, value FROM data WHERE taskid = ? ORDER BY id ASC " , ( taskid , ) ) :
2013-01-29 19:38:09 +04:00
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
for error in db . execute ( " SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC " , ( taskid , ) ) :
json_errors_message . append ( error )
2013-01-29 20:11:25 +04:00
logger . debug ( " Retrieved data and error messages for scan for task ID %s " % taskid )
2013-01-29 05:39:27 +04:00
return jsonize ( { " data " : json_data_message , " error " : json_errors_message } )
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
"""
2013-01-29 05:39:27 +04:00
global db
global tasks
json_log_messages = list ( )
2013-01-10 04:01:28 +04:00
if taskid not in tasks :
abort ( 500 , " Invalid task ID " )
2013-01-27 16:26:40 +04:00
if not start . isdigit ( ) or not end . isdigit ( ) or end < start :
2013-01-10 04:01:28 +04:00
abort ( 500 , " Invalid start or end value, must be digits " )
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
for time_ , level , message in db . execute ( " SELECT time, level, message FROM logs WHERE taskid = ? AND id >= ? AND id <= ? ORDER BY id ASC " , ( taskid , start , end ) ) :
json_log_messages . append ( { " time " : time_ , " level " : level , " message " : message } )
2013-01-10 04:01:28 +04:00
2013-01-29 20:11:25 +04:00
logger . debug ( " Retrieved subset of log messages for scan for task ID %s " % taskid )
2013-01-24 16:57:24 +04:00
return jsonize ( { " 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
"""
2013-01-29 05:39:27 +04:00
global db
global tasks
json_log_messages = list ( )
2013-01-10 04:01:28 +04:00
2012-12-14 21:21:19 +04:00
if taskid not in tasks :
abort ( 500 , " Invalid task ID " )
2013-01-29 05:39:27 +04:00
# Read all log messages from the IPC database
for time_ , level , message in db . execute ( " SELECT time, level, message FROM logs WHERE taskid = ? ORDER BY id ASC " , ( taskid , ) ) :
json_log_messages . append ( { " time " : time_ , " level " : level , " message " : message } )
2013-01-10 04:01:28 +04:00
2013-01-29 20:11:25 +04:00
logger . debug ( " Retrieved log messages for scan for task ID %s " % taskid )
2013-01-24 16:57:24 +04:00
return jsonize ( { " 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
"""
2012-12-14 17:40:25 +04:00
if taskid not in tasks :
abort ( 500 , " Invalid task ID " )
2012-12-14 20:18:45 +04:00
# Prevent file path traversal - the lame way
if target . startswith ( " . " ) :
abort ( 500 )
2012-12-14 16:01:13 +04:00
path = os . path . join ( paths . SQLMAP_OUTPUT_PATH , target )
2013-01-29 05:39:27 +04:00
2012-12-14 16:01:13 +04:00
if os . path . exists ( path ) :
return static_file ( filename , root = path )
else :
2013-01-29 20:11:25 +04:00
abort ( 500 , " File does not exist " )
2012-12-14 16:01:13 +04:00
2012-12-20 19:29:23 +04:00
def server ( host = " 0.0.0.0 " , port = RESTAPI_SERVER_PORT ) :
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
"""
2012-12-14 17:40:25 +04:00
global adminid
2013-01-29 05:39:27 +04:00
global db
2013-02-03 15:31:05 +04:00
global db_filepath
2012-12-15 02:00:42 +04:00
2012-12-14 18:51:01 +04:00
adminid = hexencode ( os . urandom ( 16 ) )
2013-02-04 01:39:26 +04:00
db_filepath = tempfile . mkstemp ( prefix = " sqlmapipc- " , text = False ) [ 1 ]
2013-01-29 19:38:09 +04:00
logger . info ( " Running REST-JSON API server at ' %s : %d ' .. " % ( host , port ) )
logger . info ( " Admin ID: %s " % adminid )
2013-02-03 15:31:05 +04:00
logger . debug ( " IPC database: %s " % db_filepath )
2013-01-29 19:38:09 +04:00
# Initialize IPC database
2013-01-29 05:39:27 +04:00
db = Database ( )
2013-02-03 15:31:05 +04:00
db . connect ( )
db . init ( )
2012-12-15 04:12:22 +04:00
2012-12-20 18:35:02 +04:00
# Run RESTful API
2013-01-29 19:38:09 +04:00
run ( host = host , port = port , quiet = True , debug = False )
2012-12-14 06:52:31 +04:00
2012-12-20 18:35:02 +04:00
def client ( host = RESTAPI_SERVER_HOST , port = RESTAPI_SERVER_PORT ) :
2012-12-15 04:29:35 +04:00
"""
REST - JSON API client
"""
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 )
2012-12-14 16:01:13 +04:00
2012-12-20 18:35:02 +04:00
# TODO: write a simple client with requests, for now use curl from command line
2013-01-29 19:38:09 +04:00
logger . error ( " Not yet implemented, use curl from command line instead for now, for example: " )
2012-12-20 18:35:02 +04:00
print " \n \t $ curl http:// %s : %d /task/new " % ( host , port )
print " \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 )
2013-02-06 21:45:52 +04:00
print " \t $ curl http:// %s : %d /scan/:taskid/data " % ( host , port )
2012-12-20 18:35:02 +04:00
print " \t $ curl http:// %s : %d /scan/:taskid/log \n " % ( host , port )