| hash | 18 |name | 19 |brand | 20 |version | 21 |
|---|---|---|---|
| {{ firmware.hash }} | 27 |{{ firmware.name }} | 28 |{{ firmware.brand }} | 29 |{{ firmware.version }} | 30 |
Status : {{ upload.status }}
10 |Hash : {{ upload.hash }}
11 | View results 12 || hash | 18 |name | 19 |brand | 20 |version | 21 |
|---|---|---|---|
| {{ firmware.hash }} | 27 |{{ firmware.name }} | 28 |{{ firmware.brand }} | 29 |{{ firmware.version }} | 30 |
Search by constructor or model name
10 | 19 || hash | 28 |name | 29 |brand | 30 |version | 31 |
|---|---|---|---|
| {{ firmware.hash }} | 37 |{{ firmware.name }} | 38 |{{ firmware.brand }} | 39 |{{ firmware.version }} | 40 |
{{ firmware.description }}
11 | 12 |Informations
37 |Upload a firmware archive and start analyzing it
10 | 44 |" + data["type"] + "
"; 14 | var is_image = /\.(gif|png|jpg|jpeg|bmp)$/ig.test(data["filename"]); 15 | if (is_image){ 16 | infos += "" + htmlEntities(data["informations"]) + ""; 36 | } 37 | 38 | //Content 39 | if (data["content"] != undefined) { 40 | infos += "
" + htmlEntities(data["content"]) + ""; 41 | } 42 | 43 | // Loots 44 | if (data["loots"] != undefined && data["loots"].length > 0) { 45 | infos += "
";
47 | for(i=0; i < data["loots"].length; i++) {
48 | infos += data["loots"][i]["type"] + ": " + data["loots"][i]["info"] + "\n";
49 | }
50 | infos += "";
51 | }
52 |
53 | // Imports
54 | if (data["imports"] != undefined) {
55 | infos += "" + htmlEntities(data["imports"]) + ""; 56 | } 57 | 58 | $("#file_infos").html(infos); 59 | }); 60 | scrollToAnchor('content'); 61 | } 62 | 63 | function scrollToAnchor(aid){ 64 | var aTag = $("a[id='"+ aid +"']"); 65 | $('html,body').animate({scrollTop: aTag.offset().top},'slow'); 66 | } 67 | 68 | function htmlEntities(str) { 69 | return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); 70 | } 71 | 72 | function create_graph(hash){ 73 | $("#gen_graph").html("
");
74 | $.get( "/api/file/"+hash+"?graph", function( data ) {
75 | if (data["graph"] != undefined && data["graph"] != false){
76 | $("#gen_graph").html("{}'
154 | ''.format(content))
155 |
156 | return response
157 |
158 |
159 | # Static files (CSS, JavaScript, Images)
160 | # https://docs.djangoproject.com/en/1.9/howto/static-files/
161 |
162 | STATIC_URL = '/static/'
163 | STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)
164 |
165 | # Rats static code analysis : https://security.web.cern.ch/security/recommendations/en/codetools/rats.shtml
166 | RATS_BINARY = "/bin/rats"
167 |
168 | FIRMWARES_FOLDER = '/tmp/firmflaws/'
169 | LOOTS_FILENAMES = {"certificate": ['*.pem','*.crt','*p7b','*p12','*.cer'],
170 | "configuration": ['*.conf','*.cfg','*.ini', '*.xml', "*.sql"],
171 | "RSA": ['id_rsa','*id_rsa*','id_dsa','*id_dsa*','*.pub'],
172 | "databases": ['*.db','*.sqlite'],
173 | "passwords": ['passwd','shadow','*.psk']}
174 |
175 | LOOTS_GREP = {"interesting greps": ["passwd", "password"],
176 | "ipv4": ["(?:[0-9]{1,3}\.){3}[0-9]{1,3}"],
177 | "email": ["[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+"]}
178 |
179 | INSECURE_FUNCTIONS = ['strcpy', 'strcat', 'sprintf', 'vsprintf', 'gets', 'strlen', 'scanf', 'fscanf', 'sscanf', 'vscanf', 'vsscanf', 'vfscanf', 'realpath', 'getopt', 'getpass', 'streadd', 'strecpy', 'strtrns', 'getwd']
180 |
--------------------------------------------------------------------------------
/api/management/commands/process_firmware.py:
--------------------------------------------------------------------------------
1 | from django.core.management.base import BaseCommand, CommandError
2 | from api.models import FirmwareModel, FileModel, LootModel, LootTypeModel
3 | from lib.extract import Extractor
4 | from django.conf import settings
5 | from lib.parseELF import insecure_imports, is_elf, binary_informations, open_pipe
6 | from lib.rats import is_parsable, parse
7 | from lib.cert import is_cert, check_cert
8 | import magic
9 | import re
10 | import os
11 | import fnmatch
12 | import shutil
13 | import uuid
14 | import hashlib
15 | from pathlib import Path
16 |
17 |
18 | class Command(BaseCommand):
19 | help = 'process firmware'
20 |
21 | def handle(self, *args, **options):
22 | process_lock = os.path.join(settings.FIRMWARES_FOLDER, ".process_lock")
23 |
24 | if not os.path.isfile(process_lock):
25 | Path(process_lock).touch()
26 | while True:
27 | try:
28 | self.firmware = FirmwareModel.objects.filter(status="waiting")[0]
29 | try:
30 | self.run()
31 | except:
32 | self.firmware.status = "failed"
33 | self.firmware.save()
34 | except IndexError:
35 | self.stdout.write("No waiting firmwares")
36 | break
37 | os.remove(process_lock)
38 |
39 | def run(self):
40 | self.workspace = self.firmware.filepath.replace("firmware", "")
41 | self.set_status("0")
42 | extractor = Extractor(self.workspace, self.firmware.filepath)
43 | self.extracted_path = extractor.extract()
44 | self.set_status("50")
45 | self.register_files()
46 | self.set_status("done")
47 |
48 | def register_files(self):
49 | print("Start registering files")
50 | for root, dirs, files in os.walk(self.extracted_path):
51 | for file in files:
52 | full_path = os.path.join(root, file)
53 | if not os.path.isfile(full_path):
54 | continue
55 | path = full_path.replace(self.extracted_path, "")
56 | content = ""
57 | hash = ""
58 | with open(full_path, "rb") as fd:
59 | content = fd.read()
60 | hash_content = "%s:%s" % (file, content)
61 | hash = hashlib.md5(hash_content.encode('utf-8')).hexdigest()
62 | try:
63 | file_obj = FileModel.objects.get(hash=hash)
64 | file_obj.firmware.add(self.firmware)
65 | file_obj.save()
66 | except FileModel.DoesNotExist:
67 | try:
68 | file_obj = FileModel()
69 | file_obj.filepath = os.path.join(root, file)
70 | file_obj.hash = hash
71 | file_obj.filesize = len(content)
72 | file_obj.filename = path
73 | file_obj.save()
74 | file_obj.firmware.add(self.firmware)
75 | file_obj.file_type = magic.from_file(os.path.join(root,
76 | file))
77 | file_obj.save()
78 | self.find_loots(file_obj)
79 | # Performance tweak
80 | file_obj.nb_loots = file_obj.loots.all().count()
81 | except:
82 | file_obj.file_type = "unknown"
83 |
84 | print("Files registered")
85 |
86 | def find_loots(self, file):
87 | # Find loots based on filenames
88 | loots_refs = settings.LOOTS_FILENAMES
89 | for type, values in loots_refs.items():
90 | try:
91 | loot_type = LootTypeModel.objects.get(name=type)
92 | loot_type.save()
93 | except LootTypeModel.DoesNotExist:
94 | loot_type = LootTypeModel()
95 | loot_type.name = type
96 | loot_type.save()
97 | for value in values:
98 | if fnmatch.fnmatch(file.filename, value):
99 | loot = LootModel()
100 | loot.file = file
101 | loot.type = loot_type
102 | loot.info = "Filename looks interesting"
103 | loot.save()
104 |
105 | # Find greppable loots
106 | loots_refs = settings.LOOTS_GREP
107 |
108 | with open(file.filepath, "rb") as fd:
109 | content = fd.read()
110 |
111 | for type, values in loots_refs.items():
112 | try:
113 | loot_type = LootTypeModel.objects.get(name=type)
114 | except LootTypeModel.DoesNotExist:
115 | loot_type = LootTypeModel()
116 | loot_type.name = type
117 | loot_type.save()
118 | for value in values:
119 | matchs = re.findall(str.encode(value),
120 | content,
121 | re.IGNORECASE | re.MULTILINE)
122 | if matchs:
123 | for match in matchs:
124 | try:
125 | loot = LootModel.objects.get(type=loot_type,
126 | file=file,
127 | info=match.decode("utf-8"))
128 | continue
129 | except LootModel.DoesNotExist:
130 | loot = LootModel()
131 | loot.file = file
132 | loot.type = loot_type
133 | loot.info = match.decode("utf-8")
134 | loot.save()
135 |
136 | if is_elf(file):
137 | handle = open_pipe(file)
138 | insecure_imports(file, handle)
139 | binary_informations(file, handle)
140 |
141 | if is_parsable(file.filepath):
142 | type = "static source analysis"
143 | try:
144 | loot_type = LootTypeModel.objects.get(name=type)
145 | except LootTypeModel.DoesNotExist:
146 | loot_type = LootTypeModel()
147 | loot_type.name = type
148 | loot_type.save()
149 | for msg in parse(file.filepath):
150 | loot = LootModel()
151 | loot.file = file
152 | loot.type = loot_type
153 | loot.info = msg
154 | loot.save()
155 |
156 | if is_cert(file.filepath):
157 | type = "certificate"
158 | try:
159 | loot_type = LootTypeModel.objects.get(name=type)
160 | except LootTypeModel.DoesNotExist:
161 | loot_type = LootTypeModel()
162 | loot_type.name = type
163 | loot_type.save()
164 | try:
165 | for msg in check_cert(file.filepath):
166 | loot = LootModel()
167 | loot.file = file
168 | loot.type = loot_type
169 | loot.info = msg
170 | loot.save()
171 | except:
172 | pass
173 |
174 | def set_status(self, status):
175 | self.firmware.status = status
176 | self.firmware.save()
177 |
--------------------------------------------------------------------------------
/static/css/custom.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | background-color: #f0f0f0;
4 | width: 100%;
5 | height: 100%;
6 | font-family: 'Open Sans','Helvetica Neue',Arial,sans-serif;
7 | }
8 |
9 | a {
10 | color: yellowgreen;
11 | -webkit-transition: all .35s;
12 | -moz-transition: all .35s;
13 | transition: all .35s;
14 | }
15 |
16 | a:hover,
17 | a:focus {
18 | color: forestgreen;
19 | }
20 |
21 | p {
22 | font-size: 16px;
23 | line-height: 1.5;
24 | }
25 |
26 | header {
27 | position: relative;
28 | width: 100%;
29 | min-height: auto;
30 | text-align: center;
31 | color: #fff;
32 | background-image: url('../images/header.jpg');
33 | background-position: center;
34 | -webkit-background-size: cover;
35 | -moz-background-size: cover;
36 | background-size: cover;
37 | -o-background-size: cover;
38 | }
39 |
40 | header .header-content {
41 | position: relative;
42 | width: 100%;
43 | padding: 100px 15px 70px;
44 | text-align: center;
45 | }
46 |
47 | header .header-content .header-content-inner h1 {
48 | margin-top: 0;
49 | margin-bottom: 20px;
50 | font-size: 50px;
51 | font-weight: 300;
52 | }
53 |
54 | header .header-content .header-content-inner p {
55 | margin-bottom: 50px;
56 | font-size: 16px;
57 | font-weight: 300;
58 | color: rgba(255,255,255,.7);
59 | }
60 |
61 | @media(min-width:768px) {
62 | header {
63 | min-height: 100%;
64 | }
65 |
66 | header .header-content {
67 | position: absolute;
68 | top: 50%;
69 | padding: 0 50px;
70 | -webkit-transform: translateY(-50%);
71 | -ms-transform: translateY(-50%);
72 | transform: translateY(-50%);
73 | }
74 |
75 | header .header-content .header-content-inner {
76 | margin-right: auto;
77 | margin-left: auto;
78 | max-width: 1000px;
79 | }
80 |
81 | header .header-content .header-content-inner h1 {
82 | font-size: 100px;
83 | word-wrap: break-word;
84 | }
85 |
86 | header .header-content .header-content-inner p {
87 | margin-right: auto;
88 | margin-left: auto;
89 | max-width: 80%;
90 | font-size: 18px;
91 | }
92 | }
93 |
94 | .section-heading {
95 | margin-top: 0;
96 | margin-bottom: 20px;
97 | }
98 |
99 | .intro {
100 | color: #fff;
101 | background-color: yellowgreen;
102 | padding: 70px 0;
103 | text-align: center;
104 | }
105 |
106 | .content {
107 | background-color: #f0f0f0;
108 | padding: 100px 0;
109 | }
110 |
111 | .content-2 {
112 | color: #fff;
113 | background-color: #222;
114 | }
115 |
116 | .content-3 {
117 | padding: 20px 0 40px;
118 | text-align: center;
119 | }
120 |
121 | .promo,
122 | .promo h3,
123 | .promo a:link,
124 | .promo a:visited,
125 | .promo a:hover,
126 | .promo a:active {
127 | color: white;
128 | text-shadow: 0px 0px 40px black;
129 | text-decoration: none;
130 | }
131 |
132 | .promo-item {
133 | height: 200px;
134 | line-height: 180px;
135 | text-align: center;
136 | }
137 |
138 | .promo-item:hover {
139 | background-size: 110%;
140 | border: 10px solid rgba(255,255,255,0.3);
141 | line-height: 160px;
142 | }
143 |
144 | .promo-item h3 {
145 | font-size: 40px;
146 | display: inline-block;
147 | vertical-align: middle;
148 | }
149 |
150 | .item-1 {
151 | background: url('../images/writing.jpg');
152 | }
153 |
154 | .item-2 {
155 | background: url('../images/concert.jpg');
156 | }
157 |
158 | .item-3 {
159 | background: url('../images/pencil_sharpener.jpg');
160 | }
161 |
162 | .item-1,
163 | .item-2,
164 | .item-3 {
165 | background-size: cover;
166 | background-position: 50% 50%;
167 | }
168 |
169 | .page-footer {
170 | background-color: #ffffff;
171 | text-align: center;
172 | }
173 |
174 | .page-footer .contact {
175 | padding: 100px 0;
176 | background-color: yellowgreen;
177 | color: #fff;
178 | }
179 |
180 | .page-footer .contact p {
181 | font-size: 22px;
182 | font-weight: 300;
183 | }
184 |
185 | .content-3 .glyphicon,
186 | .page-footer .contact .glyphicon {
187 | font-size: 32px;
188 | font-weight: 700;
189 | }
190 |
191 | .page-footer .small-print {
192 | padding: 50px 0 40px;
193 | font-weight: 300;
194 | }
195 |
196 | .text-light {
197 | color: rgba(255,255,255,.7);
198 | }
199 |
200 | .navbar-default {
201 | border-color: rgba(34,34,34,.05);
202 | background-color: #fff;
203 | -webkit-transition: all .35s;
204 | -moz-transition: all .35s;
205 | transition: all .35s;
206 | }
207 |
208 | .navbar-default .navbar-header .navbar-brand {
209 | color: yellowgreen;
210 | }
211 |
212 | .navbar-default .navbar-header .navbar-brand:hover,
213 | .navbar-default .navbar-header .navbar-brand:focus {
214 | color: #eb3812;
215 | }
216 |
217 | .navbar-default .nav > li>a,
218 | .navbar-default .nav>li>a:focus {
219 | color: #222;
220 | }
221 |
222 | .navbar-default .nav > li>a:hover,
223 | .navbar-default .nav>li>a:focus:hover {
224 | color: yellowgreen;
225 | }
226 |
227 | .navbar-default .nav > li.active>a,
228 | .navbar-default .nav>li.active>a:focus {
229 | color: yellowgreen!important;
230 | background-color: transparent;
231 | }
232 |
233 | .navbar-default .nav > li.active>a:hover,
234 | .navbar-default .nav>li.active>a:focus:hover {
235 | background-color: transparent;
236 | }
237 |
238 | @media(min-width:768px) {
239 | .navbar-default {
240 | border-color: rgba(255,255,255,.3);
241 | background-color: transparent;
242 | }
243 |
244 | .navbar-default .navbar-header .navbar-brand {
245 | color: rgba(255,255,255,.7);
246 | letter-spacing: 0.5em;
247 | }
248 |
249 | .navbar-default .navbar-header .navbar-brand:hover,
250 | .navbar-default .navbar-header .navbar-brand:focus {
251 | color: #fff;
252 | }
253 |
254 | .navbar-default .nav > li>a,
255 | .navbar-default .nav>li>a:focus {
256 | color: rgba(255,255,255,.7);
257 | }
258 |
259 | .navbar-default .nav > li>a:hover,
260 | .navbar-default .nav>li>a:focus:hover {
261 | color: #fff;
262 | }
263 |
264 | .navbar-default.affix {
265 | border-color: #fff;
266 | background-color: #fff;
267 | box-shadow: 0px 7px 20px 0px rgba(0,0,0,0.1);
268 | }
269 |
270 | .navbar-default.affix .navbar-header .navbar-brand {
271 | letter-spacing: 0;
272 | color: yellowgreen;
273 | }
274 |
275 | .navbar-default.affix .navbar-header .navbar-brand:hover,
276 | .navbar-default.affix .navbar-header .navbar-brand:focus {
277 | color: #eb3812;
278 | }
279 |
280 | .navbar-default.affix .nav > li>a,
281 | .navbar-default.affix .nav>li>a:focus {
282 | color: #222;
283 | }
284 |
285 | .navbar-default.affix .nav > li>a:hover,
286 | .navbar-default.affix .nav>li>a:focus:hover {
287 | color: yellowgreen;
288 | }
289 | }
290 |
291 | .btn-default {
292 | border-color: #fff;
293 | color: #222;
294 | background-color: #fff;
295 | -webkit-transition: all .35s;
296 | -moz-transition: all .35s;
297 | transition: all .35s;
298 | }
299 |
300 | .btn-default:hover,
301 | .btn-default:focus,
302 | .btn-default.focus,
303 | .btn-default:active,
304 | .btn-default.active,
305 | .open > .dropdown-toggle.btn-default {
306 | border-color: #eee;
307 | color: #222;
308 | background-color: #eee;
309 | }
310 |
311 | .btn-default:active,
312 | .btn-default.active,
313 | .open > .dropdown-toggle.btn-default {
314 | background-image: none;
315 | }
316 |
317 | .btn-default.disabled,
318 | .btn-default[disabled],
319 | fieldset[disabled] .btn-default,
320 | .btn-default.disabled:hover,
321 | .btn-default[disabled]:hover,
322 | fieldset[disabled] .btn-default:hover,
323 | .btn-default.disabled:focus,
324 | .btn-default[disabled]:focus,
325 | fieldset[disabled] .btn-default:focus,
326 | .btn-default.disabled.focus,
327 | .btn-default[disabled].focus,
328 | fieldset[disabled] .btn-default.focus,
329 | .btn-default.disabled:active,
330 | .btn-default[disabled]:active,
331 | fieldset[disabled] .btn-default:active,
332 | .btn-default.disabled.active,
333 | .btn-default[disabled].active,
334 | fieldset[disabled] .btn-default.active {
335 | border-color: #fff;
336 | background-color: #fff;
337 | }
338 |
339 | .btn-default .badge {
340 | color: #fff;
341 | background-color: #222;
342 | }
343 |
344 | .btn-primary {
345 | border-color: yellowgreen;
346 | color: #fff;
347 | background-color: yellowgreen;
348 | -webkit-transition: all .35s;
349 | -moz-transition: all .35s;
350 | transition: all .35s;
351 | }
352 |
353 | .btn-primary:hover,
354 | .btn-primary:focus,
355 | .btn-primary.focus,
356 | .btn-primary:active,
357 | .btn-primary.active,
358 | .open > .dropdown-toggle.btn-primary {
359 | border-color: limegreen;
360 | color: #fff;
361 | background-color: limegreen;
362 | }
363 |
364 | .btn-primary:active,
365 | .btn-primary.active,
366 | .open > .dropdown-toggle.btn-primary {
367 | background-image: none;
368 | }
369 |
370 | .btn-primary.disabled,
371 | .btn-primary[disabled],
372 | fieldset[disabled] .btn-primary,
373 | .btn-primary.disabled:hover,
374 | .btn-primary[disabled]:hover,
375 | fieldset[disabled] .btn-primary:hover,
376 | .btn-primary.disabled:focus,
377 | .btn-primary[disabled]:focus,
378 | fieldset[disabled] .btn-primary:focus,
379 | .btn-primary.disabled.focus,
380 | .btn-primary[disabled].focus,
381 | fieldset[disabled] .btn-primary.focus,
382 | .btn-primary.disabled:active,
383 | .btn-primary[disabled]:active,
384 | fieldset[disabled] .btn-primary:active,
385 | .btn-primary.disabled.active,
386 | .btn-primary[disabled].active,
387 | fieldset[disabled] .btn-primary.active {
388 | border-color: yellowgreen;
389 | background-color: yellowgreen;
390 | }
391 |
392 | .btn-primary .badge {
393 | color: yellowgreen;
394 | background-color: #fff;
395 | }
396 |
397 |
398 | .btn {
399 | border-radius: 300px;
400 | text-transform: uppercase;
401 | }
402 |
403 | .btn-lg {
404 | padding: 15px 30px;
405 | }
406 |
407 | ::-moz-selection {
408 | text-shadow: none;
409 | color: #fff;
410 | background: #222;
411 | }
412 |
413 | ::selection {
414 | text-shadow: none;
415 | color: #fff;
416 | background: #222;
417 | }
418 |
419 | img::selection {
420 | color: #fff;
421 | background: 0 0;
422 | }
423 |
424 | img::-moz-selection {
425 | color: #fff;
426 | background: 0 0;
427 | }
428 |
429 | .text-primary {
430 | color: yellowgreen;
431 | }
432 |
433 | .bg-primary {
434 | background-color: yellowgreen;
435 | }
436 |
437 |
438 | .table {
439 | background-color: #ffffff;
440 | }
441 |
--------------------------------------------------------------------------------
/api/views.py:
--------------------------------------------------------------------------------
1 | from django.views.decorators.csrf import csrf_exempt
2 | from django.http import JsonResponse, HttpResponse
3 | from django.db import IntegrityError
4 | from django.conf import settings
5 | from django.db.models import Q
6 | from api.management.commands.process_firmware_thread import start_process_thread
7 | from api.models import FirmwareModel, FileModel, LootModel, BrandModel, LootTypeModel
8 | from lib.parseELF import is_elf, parse_elf
9 | from lib.util import parseFilesToHierarchy
10 | import hashlib
11 | import os
12 |
13 | @csrf_exempt
14 | def api_upload(request):
15 | """ Upload firmware to firmflaws
16 | """
17 | if not request.method == 'POST':
18 | return JsonResponse({"error": "POST only"})
19 |
20 | if not 'file' in request.FILES:
21 | return JsonResponse({"error": "No file"})
22 |
23 | description = request.POST['description']
24 | brand = request.POST['brand']
25 | version = request.POST['version']
26 | model = request.POST['model']
27 | firmware = request.FILES['file']
28 |
29 | brand_obj = BrandModel()
30 | brand_obj.name = brand
31 | try:
32 | brand_obj.save()
33 | except IntegrityError:
34 | brand_obj = BrandModel.objects.get(name=brand)
35 |
36 | content = firmware.read()
37 | hash = hashlib.md5(content).hexdigest()
38 | directory = os.path.join(settings.FIRMWARES_FOLDER, hash)
39 |
40 | if not os.path.exists(directory):
41 | os.makedirs(directory)
42 |
43 | path = os.path.join(directory, "firmware")
44 | with open(path, "wb") as fd:
45 | fd.write(content)
46 |
47 | firmware_obj = FirmwareModel()
48 | firmware_obj.brand = brand_obj
49 | firmware_obj.description = description
50 | firmware_obj.version = version
51 | firmware_obj.model = model
52 | firmware_obj.name = firmware.name
53 | firmware_obj.filesize = firmware.size
54 | firmware_obj.hash = hash
55 | firmware_obj.filepath = path
56 |
57 | try:
58 | firmware_obj.save()
59 | #start_process_thread()
60 | from django.core.management import call_command
61 | call_command('process_firmware')
62 | return JsonResponse({"status": "new", "hash": firmware_obj.hash})
63 | except IntegrityError:
64 | return JsonResponse({"status": "repost", "hash": firmware_obj.hash})
65 |
66 | def api_get_firmware(request, hash):
67 | """ Return firmware informations
68 | """
69 | try:
70 | firmware = FirmwareModel.objects.get(hash=hash)
71 |
72 | # Direct download
73 | if 'raw' in request.GET.keys():
74 | content = ""
75 | content_type = "application/octet-stream"
76 | with open(firmware.filepath, "rb") as fd:
77 | content = fd.read()
78 | response = HttpResponse(content, content_type=content_type)
79 | content_disposition = "attachment; filename=%s.img" % firmware.hash
80 | response["Content-Disposition"] = content_disposition
81 | return response
82 |
83 | files = []
84 | for file in firmware.files.all():
85 | files.append({"filename": file.filename,
86 | "size": file.filesize,
87 | "type": file.file_type,
88 | "hash": file.hash,
89 | "nb_loots": file.nb_loots})
90 |
91 | loots = {}
92 | loots_types = [_.name for _ in LootTypeModel.objects.all()]
93 | for type in loots_types:
94 | result = LootModel.objects.filter(type__name=type, file__firmware=firmware).count()
95 | loots[type] = result
96 |
97 | return JsonResponse({"name": firmware.name,
98 | "hash": firmware.hash,
99 | "model": firmware.model,
100 | "version": firmware.version,
101 | "status": firmware.status,
102 | "loots": loots,
103 | "created_at": firmware.created_at,
104 | "files": files,
105 | "filesize": firmware.filesize,
106 | "brand": firmware.brand.name,
107 | "description": firmware.description})
108 |
109 | except FirmwareModel.DoesNotExist:
110 | return JsonResponse({"error": "firmware not found", "hash": hash})
111 |
112 | def api_get_firmware_summary(request, hash):
113 | """ Return firmware informations
114 | """
115 | try:
116 | firmware = FirmwareModel.objects.get(hash=hash)
117 |
118 | loots = {}
119 | loots_types = [_.name for _ in LootTypeModel.objects.all()]
120 | for type in loots_types:
121 | result = LootModel.objects.filter(type__name=type, file__firmware=firmware).count()
122 | loots[type] = result
123 |
124 | return JsonResponse({"name": firmware.name,
125 | "hash": firmware.hash,
126 | "model": firmware.model,
127 | "version": firmware.version,
128 | "status": firmware.status,
129 | "loots": loots,
130 | "created_at": firmware.created_at,
131 | "filesize": firmware.filesize,
132 | "brand": firmware.brand.name,
133 | "description": firmware.description})
134 |
135 | except FirmwareModel.DoesNotExist:
136 | return JsonResponse({"error": "firmware not found", "hash": hash})
137 |
138 |
139 | def api_get_hierarchy(request, hash):
140 | try:
141 | firmware = FirmwareModel.objects.get(hash=hash)
142 |
143 | files = []
144 | for file in firmware.files.all():
145 | nb_loots = LootModel.objects.filter(file=file).count()
146 | files.append({"filename": file.filename,
147 | "size": file.filesize,
148 | "type": file.file_type,
149 | "hash": file.hash,
150 | "nb_loots": nb_loots})
151 |
152 | return JsonResponse({'files': files})
153 | except FirmwareModel.DoesNotExist:
154 | return JsonResponse({"error": "firmware not found", "hash": hash})
155 |
156 |
157 | def api_get_file(request, hash):
158 | """ Return file from given hash
159 | """
160 | try:
161 | file = FileModel.objects.get(hash=hash)
162 | content = ""
163 | with open(file.filepath, "rb") as fd:
164 | content = fd.read()
165 | # Direct download
166 | if 'raw' in request.GET.keys():
167 | content = ""
168 | with open(file.filepath, "rb") as fd:
169 | content = fd.read()
170 | content_type = "application/octet-stream"
171 | response = HttpResponse(content, content_type=content_type)
172 | content_disposition = "attachment; filename=%s" % file.filename
173 | response["Content-Disposition"] = content_disposition
174 | return response
175 |
176 | # Graph download
177 | if 'graph' in request.GET.keys():
178 | if file.graph_file != '' and file.graph_file != False:
179 | content = ""
180 | with open(file.graph_file, "rb") as fd:
181 | content = fd.read()
182 |
183 | content_type = "image/png"
184 | response = HttpResponse(content, content_type=content_type)
185 | return response
186 | elif file.graph_file == False:
187 | return HttpResponse("no graph")
188 | elif file.graph_file == '':
189 | try:
190 | workspace = file.firmware.all()[0].filepath.replace("firmware",
191 | "")
192 | parse_elf(workspace, file)
193 | with open(file.graph_file, "rb") as fd:
194 | content = fd.read()
195 |
196 | content_type = "image/png"
197 | response = HttpResponse(content, content_type=content_type)
198 | except NotImplementedError:
199 | file.graph_file = False
200 | file.save()
201 | return HttpResponse("no graph")
202 |
203 | loots = []
204 | for loot in file.loots.all():
205 | loots.append({"type": loot.type.name, "info": loot.info})
206 |
207 | response = {"loots": loots,
208 | "hash": file.hash,
209 | "type": file.file_type,
210 | "filename": file.filename,
211 | "filesize": file.filesize}
212 |
213 | if is_elf(file):
214 | response["imports"] = file.imports
215 | response["informations"] = file.informations
216 |
217 | if file.graph_file == "":
218 | response["graph"] = ''
219 | else:
220 | if file.graph_file == False:
221 | response["graph"] = False
222 | else:
223 | response["graph"] = True
224 |
225 | if "text" in file.file_type:
226 | content = ""
227 | with open(file.filepath, "r") as fd:
228 | content = fd.read()
229 | response["content"] = content
230 |
231 | return JsonResponse(response)
232 | except FileModel.DoesNotExist:
233 | return JsonResponse({"error": "file not found", "hash": hash})
234 |
235 | def api_get_latest(request):
236 | """ Return the 10 last firmwares
237 | """
238 | try:
239 | firmwares = FirmwareModel.objects.filter(status="done").order_by('-id')[:10]
240 | response = []
241 | for firmware in firmwares:
242 | response.append({"name": firmware.name,
243 | "hash": firmware.hash,
244 | "version": firmware.version,
245 | "status": firmware.status,
246 | "filesize": firmware.filesize,
247 | "brand": firmware.brand.name,
248 | "description": firmware.description,
249 | "model": firmware.model})
250 | return JsonResponse({"firmwares": response})
251 | except:
252 | return JsonResponse({"error": "unknown error"})
253 |
254 | def api_get_stats(request):
255 | """ Return global stats
256 | """
257 | try:
258 | response = {"total": LootModel.objects.all().count()}
259 | loots_types = [_.name for _ in LootTypeModel.objects.all()]
260 | for type in loots_types:
261 | result = LootModel.objects.filter(type__name=type).count()
262 | response[type] = result
263 |
264 | nb_firmwares_done = FirmwareModel.objects.filter(status="done").count()
265 | nb_firmwares_total = FirmwareModel.objects.all().count()
266 |
267 | return JsonResponse({"firmwares_done": nb_firmwares_done,
268 | "firmwares_total": nb_firmwares_total,
269 | "stats_loots": response})
270 | except:
271 | return JsonResponse({"error": "unknown error"})
272 |
273 | def api_search(request):
274 | try:
275 | k = request.GET.get('keyword', False)
276 | if k is False:
277 | return JsonResponse({"Error": "missing keyword argument"})
278 |
279 | firmwares = FirmwareModel.objects.filter(Q(name__icontains=k) |
280 | Q(description__icontains=k) |
281 | Q(brand__name__icontains=k)).values("hash", "description", "brand__name", "name")
282 | response = []
283 | for firmware in firmwares:
284 | response.append({"hash": firmware["hash"],
285 | "brand": firmware["brand__name"],
286 | "name": firmware["name"],
287 | "description": firmware["description"]})
288 | return JsonResponse({"results": response})
289 |
290 | except NotImplementedError:
291 | return JsonResponse({"Error": "unknown error"})
292 |
--------------------------------------------------------------------------------
/lib/extractor.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | """
4 | Module that performs extraction. For usage, refer to documentation for the class
5 | 'Extractor'. This module can also be executed directly,
6 | e.g. 'extractor.py