Skip to content

Commit faee2c7

Browse files
committed
Land rapid7#3492 - sqlmap plugin for sqlmap API
2 parents 3e30453 + d62b247 commit faee2c7

File tree

3 files changed

+385
-0
lines changed

3 files changed

+385
-0
lines changed

lib/sqlmap/sqlmap_manager.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
require 'json'
2+
3+
module Sqlmap
4+
class Manager
5+
def initialize(session)
6+
@session = session
7+
end
8+
9+
def new_task
10+
res = @session.get('/task/new')
11+
return JSON.parse(res.body)
12+
end
13+
14+
def delete_task(task_id)
15+
res = @session.get('/task/' + task_id + '/delete')
16+
return JSON.parse(res.body)
17+
end
18+
19+
def set_option(task_id, key, value)
20+
post = { key => value }
21+
res = @session.post('/option/' + task_id + '/set', nil, post.to_json, {'ctype' => 'application/json'})
22+
return JSON.parse(res.body)
23+
end
24+
25+
def get_options(task_id)
26+
res = @session.get('/option/' + task_id + '/list')
27+
return JSON.parse(res.body)
28+
end
29+
30+
def start_task(task_id, options = {})
31+
res = @session.post('/scan/' + task_id + '/start' , nil, options.to_json, {'ctype' => 'application/json'})
32+
return JSON.parse(res.body)
33+
end
34+
35+
def get_task_status(task_id)
36+
res = @session.get('/scan/' + task_id + '/status')
37+
return JSON.parse(res.body)
38+
end
39+
40+
def get_task_log(task_id)
41+
res = @session.get('/scan/' + task_id + '/log')
42+
return JSON.parse(res.body)
43+
end
44+
45+
def get_task_data(task_id)
46+
res = @session.get('/scan/' + task_id + '/data')
47+
return JSON.parse(res.body)
48+
end
49+
end
50+
end

lib/sqlmap/sqlmap_session.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module Sqlmap
2+
class Session
3+
def initialize(host, port = 8775)
4+
@host = host
5+
@port = port
6+
end
7+
8+
def get(uri, headers = nil, params = nil)
9+
c = Rex::Proto::Http::Client.new(@host, @port)
10+
args = {
11+
'uri' => uri
12+
}
13+
14+
args['headers'] = headers if headers
15+
args['vars_get'] = params if params
16+
res = c.request_cgi(args)
17+
res = c.send_recv(res)
18+
return res
19+
end
20+
21+
def post(uri, headers = nil, data = nil, originator_args = nil)
22+
c = Rex::Proto::Http::Client.new(@host, @port)
23+
args = {
24+
'uri' => uri,
25+
'method' => 'POST'
26+
}
27+
28+
args.merge!(originator_args) if originator_args
29+
30+
args['headers'] = headers if headers
31+
args['data'] = data if data
32+
res = c.request_cgi(args)
33+
res = c.send_recv(res)
34+
return res
35+
end
36+
end
37+
end

plugins/sqlmap.rb

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
require 'sqlmap/sqlmap_session'
2+
require 'sqlmap/sqlmap_manager'
3+
require 'json'
4+
5+
module Msf
6+
class Plugin::Sqlmap < Msf::Plugin
7+
class SqlmapCommandDispatcher
8+
include Msf::Ui::Console::CommandDispatcher
9+
10+
def name
11+
"Sqlmap"
12+
end
13+
14+
def commands
15+
{
16+
'sqlmap_new_task' => 'It\'s a task!',
17+
'sqlmap_connect' => 'sqlmap_connect <host> [<port>]',
18+
'sqlmap_list_tasks' => 'List the knows tasks. Not stored in a DB, so lives as long as the console does',
19+
'sqlmap_get_option' => 'Get an option for a task',
20+
'sqlmap_set_option' => 'Set an option for a task',
21+
'sqlmap_start_task' => 'Start the task',
22+
'sqlmap_get_status' => 'Get the status of a task',
23+
'sqlmap_get_log' => 'Get the running log of a task',
24+
'sqlmap_get_data' => 'Get the resulting data of the task',
25+
'sqlmap_save_data' => 'Save the resulting data as web_vulns'
26+
}
27+
end
28+
29+
def cmd_sqlmap_connect(*args)
30+
if args.length == 0
31+
print_error('Need a host, and optionally a port')
32+
return
33+
end
34+
35+
host = args[0]
36+
port = args.length == 2 ? args[1] : nil
37+
38+
if !port
39+
@manager = Sqlmap::Manager.new(Sqlmap::Session.new(host))
40+
else
41+
@manager = Sqlmap::Manager.new(Sqlmap::Session.new(host, port))
42+
end
43+
44+
print_good('Set connection settings for host ' + host + (port ? ' on port ' + port : ''))
45+
end
46+
47+
def cmd_sqlmap_set_option(*args)
48+
unless args.length == 3
49+
print_error('Usage:')
50+
print_error('\tsqlmap_set_option <taskid> <option_name> <option_value>')
51+
return
52+
end
53+
54+
unless @manager
55+
print_error('Please run sqlmap_connect <host> first.')
56+
return
57+
end
58+
59+
val = args[2]
60+
if args[2] =~ /^\d+$/
61+
val = val.to_i
62+
end
63+
64+
res = @manager.set_option(@hid_tasks[args[0]], args[1], val)
65+
print_status('Success: ' + res['success'].to_s)
66+
end
67+
68+
def cmd_sqlmap_start_task(*args)
69+
if args.length == 0
70+
print_error('Usage:')
71+
print_error('\tsqlmap_start_task <taskid> [<url>]')
72+
return
73+
end
74+
75+
options = {}
76+
77+
if args.length == 2
78+
options['url'] = args[1]
79+
end
80+
81+
if !options['url'] && @tasks[@hid_tasks[args[0]]]['url'] == ''
82+
print_error('You need to specify a URL either as an argument to sqlmap_start_task or sqlmap_set_option')
83+
return
84+
end
85+
86+
unless @manager
87+
print_error('Please run sqlmap_connect <host> first.')
88+
return
89+
end
90+
91+
res = @manager.start_task(@hid_tasks[args[0]], options)
92+
print_status('Started task: ' + res['success'].to_s)
93+
end
94+
95+
def cmd_sqlmap_get_log(*args)
96+
unless args.length == 1
97+
print_error('Usage:')
98+
print_error('\tsqlmap_get_log <taskid>')
99+
return
100+
end
101+
102+
unless @manager
103+
print_error('Please run sqlmap_connect <host> first.')
104+
return
105+
end
106+
107+
res = @manager.get_task_log(@hid_tasks[args[0]])
108+
109+
res['log'].each do |message|
110+
print_status("[#{message["time"]}] #{message["level"]}: #{message["message"]}")
111+
end
112+
end
113+
114+
def cmd_sqlmap_get_status(*args)
115+
unless args.length == 1
116+
print_error('Usage:')
117+
print_error('\tsqlmap_get_status <taskid>')
118+
return
119+
end
120+
121+
unless @manager
122+
print_error('Please run sqlmap_connect <host> first.')
123+
return
124+
end
125+
126+
res = @manager.get_task_status(@hid_tasks[args[0]])
127+
128+
print_status('Status: ' + res['status'])
129+
end
130+
131+
def cmd_sqlmap_get_data(*args)
132+
unless args.length == 1
133+
print_error('Usage:')
134+
print_error('\tsqlmap_get_data <taskid>')
135+
return
136+
end
137+
138+
@hid_tasks ||= {}
139+
@tasks ||= {}
140+
141+
unless @manager
142+
print_error('Please run sqlmap_connect <host> first.')
143+
return
144+
end
145+
146+
@tasks[@hid_tasks[args[0]]] = @manager.get_options(@hid_tasks[args[0]])['options']
147+
148+
print_line
149+
print_status('URL: ' + @tasks[@hid_tasks[args[0]]]['url'])
150+
151+
res = @manager.get_task_data(@hid_tasks[args[0]])
152+
153+
tbl = Rex::Ui::Text::Table.new(
154+
'Columns' => ['Title','Payload'])
155+
156+
res['data'].each do |d|
157+
d['value'].each do |v|
158+
v['data'].each do |i|
159+
title = i[1]['title'].split('-')[0]
160+
payload = i[1]['payload']
161+
tbl << [title, payload]
162+
end
163+
end
164+
end
165+
166+
print_line
167+
print_line tbl.to_s
168+
print_line
169+
end
170+
171+
def cmd_sqlmap_save_data(*args)
172+
unless args.length == 1
173+
print_error('Usage:')
174+
print_error('\tsqlmap_save_data <taskid>')
175+
return
176+
end
177+
178+
unless framework.db && framework.db.usable
179+
print_error('No database is connected or usable')
180+
return
181+
end
182+
183+
@hid_tasks ||= {}
184+
@tasks ||= {}
185+
186+
unless @manager
187+
print_error('Please run sqlmap_connect <host> first.')
188+
return
189+
end
190+
191+
@tasks[@hid_tasks[args[0]]] = @manager.get_options(@hid_tasks[args[0]])['options']
192+
193+
print_line
194+
print_status('URL: ' + @tasks[@hid_tasks[args[0]]]['url'])
195+
196+
res = @manager.get_task_data(@hid_tasks[args[0]])
197+
web_vuln_info = {}
198+
url = @tasks[@hid_tasks[args[0]]]['url']
199+
proto = url.split(':')[0]
200+
host = url.split('/')[2]
201+
port = 80
202+
port = host.split(':')[1] if host.index(':')
203+
host = host.split(':')[0] if host.index(':')
204+
path = '/' + (url.split('/')[3..(url.split('/').length - 1)].join('/'))
205+
query = url.split('?')[1]
206+
web_vuln_info[:web_site] = url
207+
web_vuln_info[:path] = path
208+
web_vuln_info[:query] = query
209+
web_vuln_info[:host] = host
210+
web_vuln_info[:port] = port
211+
web_vuln_info[:ssl] = (proto =~ /https/)
212+
web_vuln_info[:category] = 'imported from sqlmap'
213+
res['data'].each do |d|
214+
d['value'].each do |v|
215+
web_vuln_info[:pname] = v['parameter']
216+
web_vuln_info[:method] = v['place']
217+
web_vuln_info[:payload] = v['suffix']
218+
v['data'].each do |k,i|
219+
web_vuln_info[:name] = i['title']
220+
web_vuln_info[:description] = res.to_json
221+
web_vuln_info[:proof] = i['payload']
222+
framework.db.report_web_vuln(web_vuln_info)
223+
end
224+
end
225+
end
226+
print_good('Saved vulnerabilities to database.')
227+
end
228+
229+
def cmd_sqlmap_get_option(*args)
230+
@hid_tasks ||= {}
231+
@tasks ||= {}
232+
233+
unless args.length == 2
234+
print_error('Usage:')
235+
print_error('\tsqlmap_get_option <taskid> <option_name>')
236+
end
237+
238+
unless @manager
239+
print_error('Please run sqlmap_connect <host> first.')
240+
return
241+
end
242+
243+
task_options = @manager.get_options(@hid_tasks[args[0]])
244+
@tasks[@hid_tasks[args[0]]] = task_options['options']
245+
246+
if @tasks[@hid_tasks[args[0]]]
247+
print_good(args[1] + ': ' + @tasks[@hid_tasks[args[0]]][args[1]].to_s)
248+
else
249+
print_error('Option ' + args[0] + ' doesn\'t exist')
250+
end
251+
end
252+
253+
def cmd_sqlmap_new_task(*args)
254+
@hid_tasks ||= {}
255+
@tasks ||= {}
256+
257+
unless @manager
258+
print_error('Please run sqlmap_connect <host> first.')
259+
return
260+
end
261+
262+
taskid = @manager.new_task['taskid']
263+
@hid_tasks[(@hid_tasks.length+1).to_s] = taskid
264+
task_options = @manager.get_options(taskid)
265+
@tasks[@hid_tasks[@hid_tasks.length]] = task_options['options']
266+
print_good('Created task: ' + @hid_tasks.length.to_s)
267+
end
268+
269+
def cmd_sqlmap_list_tasks(*args)
270+
@hid_tasks ||= {}
271+
@tasks ||= {}
272+
@hid_tasks.each do |task, options|
273+
print_good('Task ID: ' + task.to_s)
274+
end
275+
end
276+
end
277+
278+
def initialize(framework, opts)
279+
super
280+
281+
add_console_dispatcher(SqlmapCommandDispatcher)
282+
283+
print_status('Sqlmap plugin loaded')
284+
end
285+
286+
def cleanup
287+
remove_console_dispatcher('Sqlmap')
288+
end
289+
290+
def name
291+
'Sqlmap'
292+
end
293+
294+
def desc
295+
'Use Sqlmap, yo!'
296+
end
297+
end
298+
end

0 commit comments

Comments
 (0)