forked from viper-framework/viper
-
Notifications
You must be signed in to change notification settings - Fork 0
/
viper-web
executable file
·796 lines (716 loc) · 27.8 KB
/
viper-web
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of Viper - https://github.com/viper-framework/viper
# See the file 'LICENSE' for copying permission.
import os
import re
import sys
import bottle
import shutil
try:
from subprocess import getoutput
except ImportError:
from commands import getoutput
try:
from scandir import walk
except ImportError:
from os import walk
import requests
import argparse
import tempfile
import contextlib
import tarfile
from zipfile import ZipFile
from gzip import GzipFile
from bz2 import BZ2File
from bottle import request, response, get, template, static_file, redirect
from viper.core.session import __sessions__
from viper.core.plugins import __modules__
from viper.core.project import __project__
from viper.common.objects import File
from viper.core.storage import store_sample, get_sample_path
from viper.core.database import Database
from viper.common import network
from viper.core.ui.commands import Commands
from viper.common.constants import VIPER_ROOT
from viper.core.config import Config
###
# Needed for bottle (and correct uwsgi handling)
###
app = application = bottle.default_app()
###
# Viper Global Config
###
cfg = Config()
web_port = cfg.api.port
cuckoo_api = cfg.cuckoo.cuckoo_host
cuckoo_web = cfg.cuckoo.cuckoo_web
##
# End Viper Global Config
##
web_root = None
for folder in ['/usr/share/viper/web/', os.path.join(VIPER_ROOT, 'data/web')]:
if os.path.exists(folder):
web_root = folder
bottle.TEMPLATE_PATH.insert(0, folder)
break
# Module Dicts
mod_dicts = {}
mod_dicts['apk'] = {'help':'-h','info':'-i', 'perm':'-p', 'list':'-f', 'all':'-a', 'dump':'-d'}
mod_dicts['clamav'] = {'run':''}
mod_dicts['debup'] = {'info':'', 'extract':'-s'}
mod_dicts['editdistance'] = {'run':''}
mod_dicts['elf'] = {'sections':'--sections', 'segments':'--segments', 'symbols':'--symbols', 'interp':'--interpreter', 'dynamic':'--dynamic'}
mod_dicts['email'] = {'envelope': '-e', 'attach': '-f', 'header':'-r', 'trace':'-t', 'traceall':'-T', 'spoof':'-s', 'all':'-a'}
mod_dicts['exif'] = {'run':''}
mod_dicts['fuzzy'] = {'run':''}
mod_dicts['html'] = {'scripts':'-s', 'links':'-l', 'iframe':'-f', 'embed':'-e', 'images':'-i', 'dump':'-d'}
mod_dicts['idx'] = {'run':''}
mod_dicts['image'] = {'ghiro':'--ghiro'}
mod_dicts['jar'] = {'run':''}
mod_dicts['office'] = {'meta':'-m', 'oleid':'-o', 'streams':'-s', 'export':'-e'}
mod_dicts['pdf'] = {'id':'id', 'streams':'streams'}
mod_dicts['pe'] = {'imports':'imports', 'exports':'exports', 'res':'resources', 'imp':'imphash', 'compile':'compiletime', 'peid':'peid', 'security':'security', 'language':'language', 'sections':'sections', 'pehash':'pehash'}
mod_dicts['rat'] = {'auto':'-a', 'list': '-l'}
mod_dicts['reports'] = {'malwr':'--malwr', 'threat':'--threat', 'joe':'--joe', 'meta':'--meta'}
mod_dicts['shellcode'] = {'run':''}
mod_dicts['strings'] = {'all':'-a', 'hosts':'-H'}
mod_dicts['swf'] = {'decom':'decompress'}
mod_dicts['virustotal'] = {'scan':'', 'submit':'-s'}
mod_dicts['xor'] = {'xor':'', 'rot':'-r', 'all':'-a', 'export':'-o'}
mod_dicts['yara'] = {'scan':'scan -t', 'all': 'scan -a -t'}
##
# Helper Functions
#
def parse(data):
root = ''
args = []
# Split words by white space.
words = data.split()
# First word is the root command.
root = words[0]
# If there are more words, populate the arguments list.
if len(words) > 1:
args = words[1:]
return (root, args)
def print_output(output):
if not output:
return '<p class="text-danger">! The command Generated no Output</p>'
return_html = ''
for entry in output:
# Skip lines that say seesion opened
if 'Session opened on' in entry['data']:
continue
if entry['type'] == 'info':
return_html += '<p class="text-primary">{0}</p>'.format(entry['data'])
#self.log('info', entry['data'])
elif entry['type'] == 'item':
return_html += '<li class="text-primary">{0}</li>'.format(entry['data'])
elif entry['type'] == 'warning':
return_html += '<p class="text-warning">{0}</p>'.format(entry['data'])
elif entry['type'] == 'error':
return_html += '<p class="text-danger">{0}</p>'.format(entry['data'])
elif entry['type'] == 'success':
return_html += '<p class="text-success">{0}</p>'.format(entry['data'])
elif entry['type'] == 'table':
# set the table
return_html += '<table class="table table-bordered">'
# Column Titles
return_html += '<tr>'
for column in entry['data']['header']:
return_html += '<th>{0}</th>'.format(column)
return_html += '</tr>'
# Rows
for row in entry['data']['rows']:
return_html += '<tr>'
for cell in row:
return_html += u'<td>{0}</td>'.format(cell)
return_html += '</tr>'
# Close table
return_html += '</table>'
else:
return_html += '<p>{0}</p>'.format(entry['data'])
return return_html
def parse_text(module_text):
# String to hold the new text
set_text = ''
# Split in to lines.
for line in module_text.split('\n'):
# Remove the colour codes
line = re.sub('\[(\d)+m', '', line.replace('\x1b', ''))
# Ignore the line that says we opened a session
if 'Session opened on' in line:
continue
# add text the string
set_text += u'{0}\n'.format(line)
return set_text
def project_list():
# Get a list of projects
projects_path = __project__.get_projects_path()
p_list = []
if os.path.exists(projects_path):
for project in os.listdir(projects_path):
project_path = os.path.join(projects_path, project)
if os.path.isdir(project_path):
p_list.append(project)
return p_list
# this will allow complex command line parameters to be passed in via the web gui
def module_cmdline(cmd_line, file_hash):
html = ""
cmd = Commands()
split_commands = cmd_line.split(';')
for split_command in split_commands:
split_command = split_command.strip()
if not split_command:
continue
root, args = parse(split_command)
try:
if root in cmd.commands:
cmd.commands[root]['obj'](*args)
html += print_output(cmd.output)
del(cmd.output[:])
elif root in __modules__:
# if prev commands did not open a session open one on the current file
if file_hash:
path = get_sample_path(file_hash)
__sessions__.new(path)
module = __modules__[root]['obj']()
module.set_commandline(args)
module.run()
html += print_output(module.output)
del(module.output[:])
else:
html += '<p class="text-danger">{0} is not a valid command</p>'.format(cmd_line)
except:
html += '<p class="text-danger">We were unable to complete the command {0}</p>'.format(cmd_line)
__sessions__.close()
return html
def module_text(cmd_string, file_hash):
# A lot of commands rely on an open session
# open a session on the file hash
path = get_sample_path(file_hash)
__sessions__.new(path)
# Run the Module with args
if __sessions__.is_set():
root, args = parse(cmd_string)
if root in __modules__:
module = __modules__[root]['obj']()
module.set_commandline(args)
module.run()
html = print_output(module.output)
del(module.output[:])
else:
html = '<p class="text-danger">{0} is not a valid command</p>'.format(cmd_string)
else:
'<p class="text-danger">! There is no open session</p>'
# close the session
__sessions__.close()
return html
# context manager for file uploader
@contextlib.contextmanager
def upload_temp():
temp_dir = tempfile.mkdtemp()
yield temp_dir
shutil.rmtree(temp_dir)
##
# Pages
#
#Returns Static files e.g. CSS / JS
@get('/static/:path#.+#')
def server_static(path):
return static_file(path, root=os.path.join(web_root, 'static'))
# Index Page
@app.route("/")
@app.route("/project/<p>")
def landing(p=False):
contents = {}
if p in project_list():
__project__.open(p)
contents['p'] = p
else:
__project__.open("default")
contents['p'] = 'default'
db = Database()
# Pagination
# 25 per page
value = 25
offset = 0
contents['count'] = db.get_sample_count()
page = request.query.page
if not page:
page = 0
offset = int(page) * int(value)
contents['act_page'] = page
contents['latest'] = db.find('latest', value=value, offset=offset)
# return the Template
return template('index.tpl', **contents)
# create Project
@app.route("/create", method="POST")
def add_project():
project_name = request.forms.get('project').strip()
__project__.open(project_name)
redirect('/project/{0}'.format(project_name))
# Info Page for File
@app.route("/file/<file_hash>", method="GET")
@app.route("/file/<project>/<file_hash>", method="GET")
def file_info(file_hash, project=False):
contents = {}
if project in project_list():
__project__.open(project)
contents['project'] = project
else:
__project__.open('default')
contents['project'] = 'default'
# Open the Database
db = Database()
# Open a session
try:
path = get_sample_path(file_hash)
__sessions__.new(path)
except:
return template('error.tpl', error="{0} Does not match any hash in the Database".format(file_hash))
# Get the file info
contents['file_info'] = [
__sessions__.current.file.name,
__sessions__.current.file.tags,
__sessions__.current.file.path,
__sessions__.current.file.size,
__sessions__.current.file.type,
__sessions__.current.file.mime,
__sessions__.current.file.md5,
__sessions__.current.file.sha1,
__sessions__.current.file.sha256,
__sessions__.current.file.sha512,
__sessions__.current.file.ssdeep,
__sessions__.current.file.crc32
]
# Get Any Notes
note_list = []
malware = db.find(key='sha256', value=file_hash)
if malware:
notes = malware[0].note
if notes:
for note in notes:
note_list.append([note.title, note.body, note.id])
contents['notes'] = note_list
# Close the session
__sessions__.close()
# Return the page
return template('file.tpl', **contents)
# Add New File
# Uses Context Manager to Remove Temp files
@app.route('/add', method='POST')
def add_file():
tags = request.forms.get('tag_list')
uploads = request.files.getlist('file')
# Set Project
project = request.forms.get('project')
if project in project_list():
__project__.open(project)
else:
__project__.open('default')
project = 'default'
db = Database()
file_list = []
# Write temp file to disk
with upload_temp() as temp_dir:
for upload in uploads:
file_path = os.path.join(temp_dir, upload.filename)
with open(file_path, 'w') as tmp_file:
tmp_file.write(upload.file.read())
# Zip Files
if request.forms.get('compression') == 'zip':
zip_pass = request.forms.get('zip_pass')
try:
with ZipFile(file_path) as zf:
zf.extractall(temp_dir, pwd=zip_pass)
for root, dirs, files in walk(temp_dir, topdown=False):
for name in files:
if not name == upload.filename:
file_list.append(os.path.join(root, name))
except Exception as e:
return template('error.tpl', error="Error with zipfile - {0}".format(e))
# GZip Files
elif request.forms.get('compression') == 'gz':
try:
gzf = GzipFile(file_path, 'rb')
decompress = gzf.read()
gzf.close()
with open(file_path[:-3],"wb") as df:
df.write(decompress)
file_list.append(file_path[:-3])
except Exception as e:
return template('error.tpl', error="Error with gzipfile - {0}".format(e))
# BZip2 Files
elif request.forms.get('compression') == 'bz2':
try:
bz2f = BZ2File(file_path, 'rb')
decompress = bz2f.read()
bz2f.close()
with open(file_path[:-3],"wb") as df:
df.write(decompress)
file_list.append(file_path[:-3])
except Exception as e:
return template('error.tpl', error="Error with bzip2file - {0}".format(e))
# Tar Files (any, including tar.gz tar.bz2)
elif request.forms.get('compression') == 'tar':
try:
if not tarfile.is_tarfile(file_path):
return template('error.tpl', error="This is not a tar file")
with tarfile.open(file_path,'r:*') as tarf:
tarf.extractall(temp_dir)
for root, dirs, files in walk(temp_dir, topdown=False):
for name in files:
if not name == upload.filename:
file_list.append(os.path.join(root, name))
except Exception as e:
return template('error.tpl', error="Error with tarfile - {0}".format(e))
# Non zip files
elif request.forms.get('compression') == 'none':
file_list.append(file_path)
# Add each file
for new_file in file_list:
print new_file
obj = File(new_file)
new_path = store_sample(obj)
print new_path
success = True
if new_path:
# Add file to the database.
success = db.add(obj=obj, tags=tags)
if not success:
return template('error.tpl', error="Unable to Store The File: {0}".format(upload.filename))
redirect("/project/{0}".format(project))
#add file from url
@app.route('/URLDownload', method='POST')
def url_download():
url = request.forms.get('url')
tags = request.forms.get('tag_list')
tags = "url,"+tags
if request.forms.get("tor"):
upload = network.download(url,tor=True)
else:
upload = network.download(url,tor=False)
if upload == None:
return template('error.tpl', error="server can't download from URL")
# Set Project
tf = tempfile.NamedTemporaryFile()
tf.write(upload)
if tf == None:
return template('error.tpl', error="server can't download from URL")
tf.flush()
tf_obj = File(tf.name)
tf_obj.name = tf_obj.sha256
new_path = store_sample(tf_obj)
success = False
if new_path:
# Add file to the database.
db = Database()
success = db.add(obj=tf_obj, tags=tags)
if success:
redirect("/file/default/"+tf_obj.sha256)
else:
return template('error.tpl', error="Unable to Store The File,already in database")
# File Download
@app.route("/get/<file_hash>", method="GET")
@app.route("/get/<project>/<file_hash>", method="GET")
def file_download(file_hash, project=False):
if project in project_list():
__project__.open(project)
else:
__project__.open('default')
project = 'default'
# Open the Database
db = Database()
# Open a session
rows = db.find(key='sha256', value=file_hash)
if not rows:
return template('error.tpl', error="{0} Does not match any hash in the Database".format(file_hash))
path = get_sample_path(rows[0].sha256)
if not path:
return template('error.tpl', error="File not found on disk")
response.content_length = os.path.getsize(path)
response.content_type = 'application/octet-stream; charset=UTF-8'
data = ''
for chunk in File(path).get_chunks():
data += chunk
return data
# Search
@app.route('/search', method='POST')
def find_file():
key = request.forms.get('key')
value = request.forms.get('term').lower()
project_search = request.forms.get('project')
curr_project = request.forms.get('curr_project')
results = {}
projects = []
if project_search:
# Get list of project paths
projects_path = __project__.get_projects_path()
if os.path.exists(projects_path):
for name in os.listdir(projects_path):
projects.append(name)
projects.append('default')
else:
# If not searching all projects what are we searching
if curr_project == 'default':
projects.append('default')
else:
projects.append(curr_project)
# Search each Project in the list
for project in projects:
__project__.open(project)
# Init DB
db = Database()
#get results
proj_results = []
rows = db.find(key=key, value=value)
for row in rows:
if project == 'default':
project = 'default'
proj_results.append([row.name, row.sha256])
results[project] = proj_results
return template('search.tpl', results=results)
# Tags
@app.route('/tags', method='GET')
@app.route('/tags/<tag_action>', method='POST')
def tags(tag_action=False):
# Set DB
db = Database()
# Search or Delete
if request.method == 'GET':
action = request.query.action
value = request.query.value.strip()
if value:
if action == 'search':
# This will search all projects
# Get project list
projects = project_list()
# Add Main db to list.
projects.append('default')
# Search All projects
p_list = []
results = {}
for project in projects:
__project__.open(project)
# Init DB
db = Database()
#get results
proj_results = []
rows = db.find(key='tag', value=value)
for row in rows:
if project == 'default':
project = 'default'
proj_results.append([row.name, row.sha256])
results[project] = proj_results
p_list.append(project)
# Return the search template
return template('search.tpl', projects=p_list, results=results)
else:
return template('error.tpl', error="'{0}' Is not a valid tag action".format(action))
# Add / Delete
if request.method == 'POST':
file_hash = request.forms.get('sha256')
project = request.forms.get('project')
tag_name = request.forms.get('tag')
if tag_action == 'add':
if file_hash and project:
tags = request.forms.get('tags')
db.add_tags(file_hash, tags)
if tag_action == 'del':
if file_hash and tag_name:
db.delete_tag(tag_name, file_hash)
redirect('/file/{0}/{1}'.format(project, file_hash))
# Notes Add, Update, Delete
@app.route('/file/notes', method='POST')
def file_notes():
db = Database()
update = request.forms.get('update')
new = request.forms.get('new')
delete = request.forms.get('delete')
note_title = request.forms.get('noteTitle')
note_body = request.forms.get('noteBody')
note_id = request.forms.get('id')
note_sha = request.forms.get('sha256')
project = request.forms.get('project')
# Delete Note
if delete and note_id:
db.delete_note(note_id)
# Update an existing note
if update and note_id:
db.edit_note(note_id, note_body)
if new and note_sha and note_title and note_body:
db.add_note(note_sha, note_title, note_body)
redirect('/file/{0}/{1}#notes'.format(project, note_sha))
# Return Output from Module.
@app.route('/file/module', method='POST')
def run_module():
# Get the hash of the file we want to run a command against
file_hash = request.forms.get('file_hash')
if len(file_hash) != 64:
file_hash = False
# Lot of logic here to decide what command you entered.
module_name = request.forms.get('module')
command = request.forms.get('command')
cmd_line = request.forms.get('cmdline')
# cmd_line will override any other requests.
if cmd_line:
module_results = module_cmdline(cmd_line, file_hash)
elif command:
cmd_string = '{0} {1}'.format(module_name, mod_dicts[module_name][command])
try:
module_results = module_text(cmd_string, file_hash)
except Exception as e:
module_results = "The Command '{0}' generated an error. \n{1}".format(cmd_string, e)
else:
module_results = "You Didn't Enter A Command!"
return '<pre>{0}</pre>'.format(str(parse_text(module_results).encode('utf8')))
# Yara Rules
@app.route('/yara', method='GET')
@app.route('/yara', method='POST')
def yara_rules():
# Get list of Rules
rule_path = os.path.join(VIPER_ROOT, 'data/yara')
rule_list = os.listdir(rule_path)
# GET is for listing Rules
if request.method == 'GET':
action = request.query.action
rule = request.query.rule
if action == 'list':
# Return a list of rules.
return template('yara.tpl', rule_list=rule_list, rule_text=False)
if action == 'display' and rule:
# Display Rule Contents
rule_file = os.path.join(rule_path, rule)
if os.path.isfile(rule_file):
# Only allow .yar or .yara files to be read
file_name, file_ext = os.path.splitext(rule_file)
if file_ext in ['.yar', '.yara']:
rule_text = open(rule_file, 'r').read()
return template('yara.tpl', rule_text=rule_text, rule_name=rule, rule_list=rule_list)
else:
return template('error.tpl', error="Unable to read file {0}".format(rule_file))
# delete Rules
if action == 'delete':
rule_name = request.query.rulename
if rule_name.split('.')[-1] in ['yar', 'yara']:
os.remove(os.path.join(rule_path, rule_name))
redirect('/yara?action=list')
# POST is for adding / updating Rules
if request.method == 'POST':
rule_name = request.forms.get('rule_name')
rule_text = request.forms.get('rule_text')
rule_file = os.path.join(rule_path, rule_name)
# Prevent storing files in a relative path or with a non yar extension
rule_test = rule_name.split('.')
if len(rule_test) == 2 and rule_test[-1] in ['yar', 'yara']:
# if file exists overwrite
with open(rule_file, 'w') as rule_edit:
rule_edit.write(rule_text)
redirect('/yara?action=display&rule={0}'.format(rule_name))
else:
return template('error.tpl', error="The File Name did not match the style 'name.yar'")
# Cuckoo Functions
@app.route('/cuckoo/submit', method='GET')
def cuckoo_submit():
# Get Query Strings
project = request.query.project
file_hash = request.query.hash
if project in project_list():
__project__.open(project)
else:
__project__.open('default')
project = 'default'
# Open a session
try:
path = get_sample_path(file_hash)
__sessions__.new(path)
except:
return '<span class="alert alert-danger">Invalid Submission</span>'
try:
# Return URI For Existing Entry
check_uri = '{0}/files/view/sha256/{1}'.format(cuckoo_api, file_hash)
check_file = requests.get(check_uri)
if check_file.status_code == 200:
check_result = dict(check_file.json())
cuckoo_id = check_result['sample']['id']
return '<a href="{0}/submit/status/{1}" target="_blank"> Link To Cukoo Report</a>'.format(cuckoo_web, str(cuckoo_id))
except:
return '<span class="alert alert-danger">Error Connecting To Cuckoo</span>'
# If it doesn't exist, submit it.
# Get the file data from viper
file_data = open(__sessions__.current.file.path, 'rb').read()
file_name = __sessions__.current.file.name
if file_data:
# Submit file data to cuckoo
uri = '{0}{1}'.format(cuckoo_api, '/tasks/create/file')
options = {'file': (file_name, file_data)}
cuckoo_response = requests.post(uri, files=options)
if cuckoo_response.status_code == 200:
cuckoo_id = dict(cuckoo_response.json())['task_id']
return '<a href="{0}/submit/status/{1}" target="_blank"> Link To Cukoo Report</a>'.format(cuckoo_web, str(cuckoo_id))
else:
return '<span class="alert alert-danger">Unable to Submit File</span>'
# Hex Viewer
@app.route('/hex', method='POST')
def hex_viewer():
# get post data
file_hash = request.forms.get('file_hash')
try:
hex_offset = int(request.forms.get('hex_start'))
except:
return '<p class="text-danger">Error Generating Request</p>'
hex_length = 256
# get file path
hex_path = get_sample_path(file_hash)
# create the command string
hex_cmd = 'hexdump -C -s {0} -n {1} {2}'.format(hex_offset, hex_length, hex_path)
# get the output
hex_string = getoutput(hex_cmd)
# Format the data
html_string = ''
hex_rows = hex_string.split('\n')
for row in hex_rows:
if len(row) > 9:
off_str = row[0:8]
hex_str = row[9:58]
asc_str = row[58:78]
asc_str = asc_str.replace('"', '"')
asc_str = asc_str.replace('<', '<')
asc_str = asc_str.replace('>', '>')
html_string += '<div class="row"><span class="text-primary mono">{0}</span> <span class="text-muted mono">{1}</span> <span class="text-success mono">{2}</span></div>'.format(off_str, hex_str, asc_str)
# return the data
return html_string
# Cli Commands
@app.route('/cli')
def cli_viewer():
return template('cli.tpl')
# VirusTotal Download
@app.route('/virustotal', method='POST')
def vt_download():
vt_hash = request.forms.get('vt_hash')
project = request.forms.get('project')
tags = request.forms.get('tag_list')
cmd_line = 'virustotal --search {0} -d; virustotal -don {0};store; tags -a {1}'.format(vt_hash, tags)
module_results = module_cmdline(cmd_line, False)
if 'Stored' in module_results:
redirect('/file/{0}/{1}'.format(project, vt_hash))
else:
return template('error.tpl', error="Unable to download file {0}".format(module_results))
# Run The web Server
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-H', '--host', help='Host to bind the API server on', default='localhost', action='store', required=False)
parser.add_argument('-p', '--port', help='Port to bind the API server on', default=9090, action='store', required=False)
args = parser.parse_args()
if args.port:
web_port = args.port
bv = bottle.__version__.split('.')[1]
if int(bv) < 12:
print "Please Upgrade Bottle to the latest version 'sudo pip install --upgrade bottle'"
sys.exit()
if not os.path.exists('projects'):
os.mkdir('projects')
# Set template dir
bottle.TEMPLATE_PATH.insert(0,os.path.join(VIPER_ROOT, 'data/web'))
app.run(host=args.host, port=web_port, reloader=True)