The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .gitignore
├── .malwapi.conf
├── LICENSE
├── README.md
├── malwoverview
    ├── __init__.py
    ├── malwoverview.py
    ├── modules
    │   ├── __init__.py
    │   ├── alienvault.py
    │   ├── android.py
    │   ├── bazaar.py
    │   ├── bgpview.py
    │   ├── configvars.py
    │   ├── hybrid.py
    │   ├── inquest.py
    │   ├── ipinfo.py
    │   ├── malpedia.py
    │   ├── malshare.py
    │   ├── multipleip.py
    │   ├── polyswarm.py
    │   ├── threatfox.py
    │   ├── triage.py
    │   ├── urlhaus.py
    │   ├── virusexchange.py
    │   └── virustotal.py
    └── utils
    │   ├── __init__.py
    │   ├── colors.py
    │   ├── hash.py
    │   ├── peinfo.py
    │   └── utils.py
├── pictures
    ├── picture_1.jpg
    ├── picture_10.jpg
    ├── picture_11.jpg
    ├── picture_12.jpg
    ├── picture_13.jpg
    ├── picture_14.jpg
    ├── picture_15.jpg
    ├── picture_16.jpg
    ├── picture_17.jpg
    ├── picture_18.jpg
    ├── picture_19.jpg
    ├── picture_2.jpg
    ├── picture_20.jpg
    ├── picture_21.jpg
    ├── picture_22.jpg
    ├── picture_23.jpg
    ├── picture_24.jpg
    ├── picture_25.jpg
    ├── picture_26.jpg
    ├── picture_27.jpg
    ├── picture_28.jpg
    ├── picture_29.jpg
    ├── picture_3.jpg
    ├── picture_30.jpg
    ├── picture_31.jpg
    ├── picture_32.jpg
    ├── picture_33.jpg
    ├── picture_34.jpg
    ├── picture_35.jpg
    ├── picture_36.jpg
    ├── picture_37.jpg
    ├── picture_38.jpg
    ├── picture_39.jpg
    ├── picture_4.jpg
    ├── picture_40.jpg
    ├── picture_41.jpg
    ├── picture_42.jpg
    ├── picture_43.jpg
    ├── picture_44.jpg
    ├── picture_45.jpg
    ├── picture_46.jpg
    ├── picture_47.jpg
    ├── picture_48.jpg
    ├── picture_49.jpg
    ├── picture_5.jpg
    ├── picture_50.jpg
    ├── picture_6.jpg
    ├── picture_7.jpg
    ├── picture_8.jpg
    └── picture_9.jpg
├── requirements.txt
└── setup.py


/.gitignore:
--------------------------------------------------------------------------------
 1 | # Byte-compiled / optimized / DLL files
 2 | __pycache__/
 3 | *.py[cod]
 4 | *$py.class
 5 | 
 6 | # C extensions
 7 | *.so
 8 | 
 9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST


--------------------------------------------------------------------------------
/.malwapi.conf:
--------------------------------------------------------------------------------
 1 | [VIRUSTOTAL]
 2 | VTAPI = 
 3 | 
 4 | [HYBRID-ANALYSIS]
 5 | HAAPI = 
 6 | 
 7 | [MALSHARE]
 8 | MALSHAREAPI = 
 9 | 
10 | [HAUSSUBMIT]
11 | HAUSSUBMITAPI = 
12 | 
13 | [POLYSWARM]
14 | POLYAPI = 
15 | 
16 | [ALIENVAULT]
17 | ALIENAPI = 
18 | 
19 | [MALPEDIA]
20 | MALPEDIAAPI = 
21 | 
22 | [TRIAGE]
23 | TRIAGEAPI = 
24 | 
25 | [INQUEST]
26 | INQUESTAPI = 
27 | 
28 | [VIRUSEXCHANGE]
29 | VXAPI = 
30 | 
31 | [IPINFO]
32 | IPINFOAPI =


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
  1 |                     GNU GENERAL PUBLIC LICENSE
  2 |                        Version 3, 29 June 2007
  3 | 
  4 |  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
  5 |  Everyone is permitted to copy and distribute verbatim copies
  6 |  of this license document, but changing it is not allowed.
  7 | 
  8 |                             Preamble
  9 | 
 10 |   The GNU General Public License is a free, copyleft license for
 11 | software and other kinds of works.
 12 | 
 13 |   The licenses for most software and other practical works are designed
 14 | to take away your freedom to share and change the works.  By contrast,
 15 | the GNU General Public License is intended to guarantee your freedom to
 16 | share and change all versions of a program--to make sure it remains free
 17 | software for all its users.  We, the Free Software Foundation, use the
 18 | GNU General Public License for most of our software; it applies also to
 19 | any other work released this way by its authors.  You can apply it to
 20 | your programs, too.
 21 | 
 22 |   When we speak of free software, we are referring to freedom, not
 23 | price.  Our General Public Licenses are designed to make sure that you
 24 | have the freedom to distribute copies of free software (and charge for
 25 | them if you wish), that you receive source code or can get it if you
 26 | want it, that you can change the software or use pieces of it in new
 27 | free programs, and that you know you can do these things.
 28 | 
 29 |   To protect your rights, we need to prevent others from denying you
 30 | these rights or asking you to surrender the rights.  Therefore, you have
 31 | certain responsibilities if you distribute copies of the software, or if
 32 | you modify it: responsibilities to respect the freedom of others.
 33 | 
 34 |   For example, if you distribute copies of such a program, whether
 35 | gratis or for a fee, you must pass on to the recipients the same
 36 | freedoms that you received. You must make sure that they, too, receive
 37 | or can get the source code. And you must show them these terms so they
 38 | know their rights.
 39 | 
 40 |   Developers that use the GNU GPL protect your rights with two steps:
 41 | (1) assert copyright on the software, and (2) offer you this License
 42 | giving you legal permission to copy, distribute and/or modify it.
 43 | 
 44 |   For the developers' and authors' protection, the GPL clearly explains
 45 | that there is no warranty for this free software.  For both users' and
 46 | authors' sake, the GPL requires that modified versions be marked as
 47 | changed, so that their problems will not be attributed erroneously to
 48 | authors of previous versions.
 49 | 
 50 |   Some devices are designed to deny users access to install or run
 51 | modified versions of the software inside them, although the manufacturer
 52 | can do so.  This is fundamentally incompatible with the aim of
 53 | protecting users' freedom to change the software.  The systematic
 54 | pattern of such abuse occurs in the area of products for individuals to
 55 | use, which is precisely where it is most unacceptable.  Therefore, we
 56 | have designed this version of the GPL to prohibit the practice for those
 57 | products.  If such problems arise substantially in other domains, we
 58 | stand ready to extend this provision to those domains in future versions
 59 | of the GPL, as needed to protect the freedom of users.
 60 | 
 61 |   Finally, every program is threatened constantly by software patents.
 62 | States should not allow patents to restrict development and use of
 63 | software on general-purpose computers, but in those that do, we wish to
 64 | avoid the special danger that patents applied to a free program could
 65 | make it effectively proprietary.  To prevent this, the GPL assures that
 66 | patents cannot be used to render the program non-free.
 67 | 
 68 |   The precise terms and conditions for copying, distribution and
 69 | modification follow.
 70 | 
 71 |                        TERMS AND CONDITIONS
 72 | 
 73 |   0. Definitions.
 74 | 
 75 |   "This License" refers to version 3 of the GNU General Public License.
 76 | 
 77 |   "Copyright" also means copyright-like laws that apply to other kinds of
 78 | works, such as semiconductor masks.
 79 | 
 80 |   "The Program" refers to any copyrightable work licensed under this
 81 | License.  Each licensee is addressed as "you".  "Licensees" and
 82 | "recipients" may be individuals or organizations.
 83 | 
 84 |   To "modify" a work means to copy from or adapt all or part of the work
 85 | in a fashion requiring copyright permission, other than the making of an
 86 | exact copy.  The resulting work is called a "modified version" of the
 87 | earlier work or a work "based on" the earlier work.
 88 | 
 89 |   A "covered work" means either the unmodified Program or a work based
 90 | on the Program.
 91 | 
 92 |   To "propagate" a work means to do anything with it that, without
 93 | permission, would make you directly or secondarily liable for
 94 | infringement under applicable copyright law, except executing it on a
 95 | computer or modifying a private copy.  Propagation includes copying,
 96 | distribution (with or without modification), making available to the
 97 | public, and in some countries other activities as well.
 98 | 
 99 |   To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies.  Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 | 
103 |   An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License.  If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 | 
112 |   1. Source Code.
113 | 
114 |   The "source code" for a work means the preferred form of the work
115 | for making modifications to it.  "Object code" means any non-source
116 | form of a work.
117 | 
118 |   A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 | 
123 |   The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form.  A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 | 
134 |   The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities.  However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work.  For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 | 
147 |   The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 | 
151 |   The Corresponding Source for a work in source code form is that
152 | same work.
153 | 
154 |   2. Basic Permissions.
155 | 
156 |   All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met.  This License explicitly affirms your unlimited
159 | permission to run the unmodified Program.  The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work.  This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 | 
164 |   You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force.  You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright.  Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 | 
175 |   Conveying under any other circumstances is permitted solely under
176 | the conditions stated below.  Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 | 
179 |   3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 | 
181 |   No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 | 
187 |   When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 | 
195 |   4. Conveying Verbatim Copies.
196 | 
197 |   You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 | 
205 |   You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 | 
208 |   5. Conveying Modified Source Versions.
209 | 
210 |   You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 | 
214 |     a) The work must carry prominent notices stating that you modified
215 |     it, and giving a relevant date.
216 | 
217 |     b) The work must carry prominent notices stating that it is
218 |     released under this License and any conditions added under section
219 |     7.  This requirement modifies the requirement in section 4 to
220 |     "keep intact all notices".
221 | 
222 |     c) You must license the entire work, as a whole, under this
223 |     License to anyone who comes into possession of a copy.  This
224 |     License will therefore apply, along with any applicable section 7
225 |     additional terms, to the whole of the work, and all its parts,
226 |     regardless of how they are packaged.  This License gives no
227 |     permission to license the work in any other way, but it does not
228 |     invalidate such permission if you have separately received it.
229 | 
230 |     d) If the work has interactive user interfaces, each must display
231 |     Appropriate Legal Notices; however, if the Program has interactive
232 |     interfaces that do not display Appropriate Legal Notices, your
233 |     work need not make them do so.
234 | 
235 |   A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit.  Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 | 
245 |   6. Conveying Non-Source Forms.
246 | 
247 |   You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 | 
252 |     a) Convey the object code in, or embodied in, a physical product
253 |     (including a physical distribution medium), accompanied by the
254 |     Corresponding Source fixed on a durable physical medium
255 |     customarily used for software interchange.
256 | 
257 |     b) Convey the object code in, or embodied in, a physical product
258 |     (including a physical distribution medium), accompanied by a
259 |     written offer, valid for at least three years and valid for as
260 |     long as you offer spare parts or customer support for that product
261 |     model, to give anyone who possesses the object code either (1) a
262 |     copy of the Corresponding Source for all the software in the
263 |     product that is covered by this License, on a durable physical
264 |     medium customarily used for software interchange, for a price no
265 |     more than your reasonable cost of physically performing this
266 |     conveying of source, or (2) access to copy the
267 |     Corresponding Source from a network server at no charge.
268 | 
269 |     c) Convey individual copies of the object code with a copy of the
270 |     written offer to provide the Corresponding Source.  This
271 |     alternative is allowed only occasionally and noncommercially, and
272 |     only if you received the object code with such an offer, in accord
273 |     with subsection 6b.
274 | 
275 |     d) Convey the object code by offering access from a designated
276 |     place (gratis or for a charge), and offer equivalent access to the
277 |     Corresponding Source in the same way through the same place at no
278 |     further charge.  You need not require recipients to copy the
279 |     Corresponding Source along with the object code.  If the place to
280 |     copy the object code is a network server, the Corresponding Source
281 |     may be on a different server (operated by you or a third party)
282 |     that supports equivalent copying facilities, provided you maintain
283 |     clear directions next to the object code saying where to find the
284 |     Corresponding Source.  Regardless of what server hosts the
285 |     Corresponding Source, you remain obligated to ensure that it is
286 |     available for as long as needed to satisfy these requirements.
287 | 
288 |     e) Convey the object code using peer-to-peer transmission, provided
289 |     you inform other peers where the object code and Corresponding
290 |     Source of the work are being offered to the general public at no
291 |     charge under subsection 6d.
292 | 
293 |   A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 | 
297 |   A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling.  In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage.  For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product.  A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 | 
310 |   "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source.  The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 | 
318 |   If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information.  But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 | 
329 |   The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed.  Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 | 
337 |   Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 | 
343 |   7. Additional Terms.
344 | 
345 |   "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law.  If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 | 
354 |   When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it.  (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.)  You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 | 
361 |   Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 | 
365 |     a) Disclaiming warranty or limiting liability differently from the
366 |     terms of sections 15 and 16 of this License; or
367 | 
368 |     b) Requiring preservation of specified reasonable legal notices or
369 |     author attributions in that material or in the Appropriate Legal
370 |     Notices displayed by works containing it; or
371 | 
372 |     c) Prohibiting misrepresentation of the origin of that material, or
373 |     requiring that modified versions of such material be marked in
374 |     reasonable ways as different from the original version; or
375 | 
376 |     d) Limiting the use for publicity purposes of names of licensors or
377 |     authors of the material; or
378 | 
379 |     e) Declining to grant rights under trademark law for use of some
380 |     trade names, trademarks, or service marks; or
381 | 
382 |     f) Requiring indemnification of licensors and authors of that
383 |     material by anyone who conveys the material (or modified versions of
384 |     it) with contractual assumptions of liability to the recipient, for
385 |     any liability that these contractual assumptions directly impose on
386 |     those licensors and authors.
387 | 
388 |   All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10.  If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term.  If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 | 
398 |   If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 | 
403 |   Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 | 
407 |   8. Termination.
408 | 
409 |   You may not propagate or modify a covered work except as expressly
410 | provided under this License.  Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 | 
415 |   However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 | 
422 |   Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 | 
429 |   Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License.  If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 | 
435 |   9. Acceptance Not Required for Having Copies.
436 | 
437 |   You are not required to accept this License in order to receive or
438 | run a copy of the Program.  Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance.  However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work.  These actions infringe copyright if you do
443 | not accept this License.  Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 | 
446 |   10. Automatic Licensing of Downstream Recipients.
447 | 
448 |   Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License.  You are not responsible
451 | for enforcing compliance by third parties with this License.
452 | 
453 |   An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations.  If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 | 
463 |   You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License.  For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 | 
471 |   11. Patents.
472 | 
473 |   A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based.  The
475 | work thus licensed is called the contributor's "contributor version".
476 | 
477 |   A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version.  For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 | 
487 |   Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 | 
492 |   In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement).  To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 | 
499 |   If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients.  "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 | 
513 |   If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 | 
521 |   A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License.  You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 | 
536 |   Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 | 
540 |   12. No Surrender of Others' Freedom.
541 | 
542 |   If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License.  If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all.  For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 | 
552 |   13. Use with the GNU Affero General Public License.
553 | 
554 |   Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work.  The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 | 
563 |   14. Revised Versions of this License.
564 | 
565 |   The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time.  Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 | 
570 |   Each version is given a distinguishing version number.  If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation.  If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 | 
579 |   If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 | 
584 |   Later license versions may give you additional or different
585 | permissions.  However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 | 
589 |   15. Disclaimer of Warranty.
590 | 
591 |   THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 | 
600 |   16. Limitation of Liability.
601 | 
602 |   IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 | 
612 |   17. Interpretation of Sections 15 and 16.
613 | 
614 |   If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 | 
621 |                      END OF TERMS AND CONDITIONS
622 | 
623 |             How to Apply These Terms to Your New Programs
624 | 
625 |   If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 | 
629 |   To do so, attach the following notices to the program.  It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 | 
634 |     <one line to give the program's name and a brief idea of what it does.>
635 |     Copyright (C) <year>  <name of author>
636 | 
637 |     This program is free software: you can redistribute it and/or modify
638 |     it under the terms of the GNU General Public License as published by
639 |     the Free Software Foundation, either version 3 of the License, or
640 |     (at your option) any later version.
641 | 
642 |     This program is distributed in the hope that it will be useful,
643 |     but WITHOUT ANY WARRANTY; without even the implied warranty of
644 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
645 |     GNU General Public License for more details.
646 | 
647 |     You should have received a copy of the GNU General Public License
648 |     along with this program.  If not, see <https://www.gnu.org/licenses/>.
649 | 
650 | Also add information on how to contact you by electronic and paper mail.
651 | 
652 |   If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 | 
655 |     <program>  Copyright (C) <year>  <name of author>
656 |     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 |     This is free software, and you are welcome to redistribute it
658 |     under certain conditions; type `show c' for details.
659 | 
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License.  Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 | 
664 |   You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | <https://www.gnu.org/licenses/>.
668 | 
669 |   The GNU General Public License does not permit incorporating your program
670 | into proprietary programs.  If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library.  If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License.  But first, please read
674 | <https://www.gnu.org/licenses/why-not-lgpl.html>.
675 | 


--------------------------------------------------------------------------------
/malwoverview/__init__.py:
--------------------------------------------------------------------------------
1 | 
2 | 


--------------------------------------------------------------------------------
/malwoverview/malwoverview.py:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/env python3
  2 | 
  3 | # Copyright (C)  2018-2025 Alexandre Borges <https://exploitreversing.com>
  4 | #
  5 | # This program is free software: you can redistribute it and/or modify
  6 | # it under the terms of the GNU General Public License as published by
  7 | # the Free Software Foundation, either version 3 of the License, or
  8 | # (at your option) any later version.
  9 | #
 10 | # This program is distributed in the hope that it will be useful,
 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13 | # GNU General Public License for more details.
 14 | #
 15 | # See GNU Public License on <http://www.gnu.org/licenses/>.
 16 | 
 17 | # CONTRIBUTORS
 18 | 
 19 | # Alexandre Borges (project owner)
 20 | # Corey Forman (https://github.com/digitalsleuth)
 21 | # Christian Clauss (https://github.com/cclauss)
 22 | # Artur Marzano (https://github.com/Macmod)
 23 | 
 24 | # Malwoverview.py: version 6.1.1
 25 | 
 26 | import os
 27 | import argparse
 28 | import configparser
 29 | import platform
 30 | import signal
 31 | from colorama import init
 32 | from pathlib import Path
 33 | from malwoverview.modules.alienvault import AlienVaultExtractor
 34 | from malwoverview.modules.android import AndroidExtractor
 35 | from malwoverview.modules.bazaar import BazaarExtractor
 36 | from malwoverview.modules.hybrid import HybridAnalysisExtractor
 37 | from malwoverview.modules.inquest import InQuestExtractor
 38 | from malwoverview.modules.malpedia import MalpediaExtractor
 39 | from malwoverview.modules.malshare import MalshareExtractor
 40 | from malwoverview.modules.polyswarm import PolyswarmExtractor
 41 | from malwoverview.modules.threatfox import ThreatFoxExtractor
 42 | from malwoverview.modules.triage import TriageExtractor
 43 | from malwoverview.modules.urlhaus import URLHausExtractor
 44 | from malwoverview.modules.virustotal import VirusTotalExtractor
 45 | from malwoverview.modules.virusexchange import VirusExchangeExtractor 
 46 | from malwoverview.modules.ipinfo import IPInfoExtractor
 47 | from malwoverview.modules.bgpview import BGPViewExtractor
 48 | from malwoverview.modules.multipleip import MultipleIPExtractor
 49 | from malwoverview.utils.colors import printr
 50 | from malwoverview.utils.hash import calchash
 51 | import malwoverview.modules.configvars as cv
 52 | 
 53 | 
 54 | # On Windows systems, it is necessary to install python-magic-bin: pip install python-magic-bin
 55 | 
 56 | __author__ = "Alexandre Borges"
 57 | __copyright__ = "Copyright 2018-2025, Alexandre Borges"
 58 | __license__ = "GNU General Public License v3.0"
 59 | __version__ = "6.1.1"
 60 | __email__ = "reverseexploit at proton.me"
 61 | 
 62 | def finish_hook(signum, frame):
 63 |     printr()
 64 |     exit(1)
 65 | 
 66 | def main():
 67 |     FINISH_SIGNALS = [signal.SIGINT, signal.SIGTERM]
 68 |     for signal_to_hook in FINISH_SIGNALS:
 69 |         signal.signal(signal_to_hook, finish_hook)
 70 | 
 71 |     cv.windows = ''
 72 |     if platform.system() == 'Windows':
 73 |         USER_HOME_DIR = str(Path.home()) + '\\'
 74 |         init(convert=True)
 75 |         cv.windows = 1
 76 |     else:
 77 |         USER_HOME_DIR = str(Path.home()) + '/'
 78 |         cv.windows = 0
 79 | 
 80 |     parser = argparse.ArgumentParser(prog=None, description="Malwoverview is a first response tool for threat hunting written by Alexandre Borges. This version is " + __version__, usage="usage: python malwoverview.py -c <API configuration file> -d <directory> -o <0|1> -v <1-13> -V <virustotal arg> -a <1-15> -w <0|1> -A <filename> -l <1-7> -L <hash> -j <1-7> -J <URLhaus argument> -p <1-8> -P <polyswarm argument> -y <1-5> -Y <file name> -n <1-5> -N <argument> -m <1-8> -M <argument> -b <1-10> -B <arg> -x <1-7> -X <arg> -i <1-13> -I <INQUEST argument> -vx <1-2> -VX <VirusExchange arg> -O <output directory> -ip <1-3> -IP <IP address>")
 81 |     parser.add_argument('-c', '--config', dest='config', type=str, metavar="CONFIG FILE", default=(USER_HOME_DIR + '.malwapi.conf'), help='Use a custom config file to specify API\'s.')
 82 |     parser.add_argument('-d', '--directory', dest='direct', type=str, default='', metavar="DIRECTORY", help='Specifies the directory containing malware samples to be checked against VIRUS TOTAL. Use the option -D to decide whether you are being using a public VT API or a Premium VT API.')
 83 |     parser.add_argument('-o', '--background', dest='backg', type=int, default=1, metavar="BACKGROUND", help='Adapts the output colors to a light background color terminal. The default is dark background color terminal.')
 84 |     parser.add_argument('-v', '--virustotal_option', dest='virustotaloption', type=int, default=0, metavar="VIRUSTOTAL", help='-v 1: given a file using -V option, it queries the VIRUS TOTAL database (API v.3) to get the report for the given file through -V option.; -v 2: it shows an antivirus report for a given file using -V option (API v.3); -v 3: equal to -v2, but the binary\'s IAT and EAT are also shown (API v.3); -v 4: it extracts the overlay; -v 5: submits an URL to VT scanning; -v 6: submits an IP address to Virus Total; -v 7: this options gets a report on the provided domain from Virus Total; -v 8: verifies a given hash against Virus Total; -v 9: submits a sample to VT (up to 32 MB). Use forward slash to specify the target file on Windows systems. Demands passing sample file with -V option; -v 10: verifies hashes from a provided file through option -V. This option uses public VT API v.3; -v 11: verifies hashes from a provided file through option -V. This option uses Premium API v.3; -v 12: it shows behaviour information of a sample given a hash through option -V. This option uses VT API v.3; -v 13: it submits LARGE files (above 32 MB) to VT using API v.3;')
 85 |     parser.add_argument('-V', '--virustotal_arg', dest='virustotalarg', type=str, default='', metavar="VIRUSTOTAL_ARG", help='Provides arguments for -v option.')
 86 |     parser.add_argument('-a', '--hybrid_option', dest='haoption', type=int, default=0, metavar="HYBRID_ANALYSIS", help='This parameter fetches reports from HYBRID ANALYSIS, download samples and submits samples to be analyzed. The possible values are: 1: gets a report for a given hash or sample from a Windows 7 32-bit environment; 2: gets a report for a given hash or sample from a Windows 7 32-bit environment (HWP Support); 3: gets a report for given hash or sample from a Windows 64-bit environment; 4: gets a report for a given hash or sample from an Android environment; 5: gets a report for a given hash or sample from a Linux 64-bit environment; 6: submits a sample to Windows 7 32-bit environment; 7. submits a sample to Windows 7 32-bit environment with HWP support environment; 8. submits a sample to Windows 7 64-bit environment ; 9. submits a sample to an Android environment ; 10. submits a sample to a Linux 64-bit environment; 11. downloads a sample from a Windows 7 32-bit environment; 12. downloads a sample from a Windows 7 32-bit HWP environment; 13. downloads a sample from a Windows 7 64-bit environment; 14. downloads a sample from an Android environment; 15. downloads a sample from a Linux 64-bit environment.')
 87 |     parser.add_argument('-A', '--ha_arg', dest='haarg', type=str, metavar="SUBMIT_HA", help='Provides an argument for -a option from HYBRID ANALYSIS.')
 88 |     parser.add_argument('-D', '--vtpubpremium', dest='vtpubpremium', type=int, default=0, metavar="VT_PUBLIC_PREMIUM", help='This option must be used with -d option. Possible values: <0> it uses the Premium VT API v3 (default); <1> it uses the Public VT API v3.')
 89 |     parser.add_argument('-l', '--malsharelist', dest='malsharelist', type=int, default=0, metavar="MALSHARE_HASHES", help='This option performs download a sample and shows hashes of a specific type from the last 24 hours from MALSHARE repository. Possible values are: 1: Download a sample; 2: PE32 (default) ; 3: ELF ; 4: Java; 5: PDF ; 6: Composite(OLE); 7: List of hashes from past 24 hours.')
 90 |     parser.add_argument('-L', '--malshare_hash', dest='malsharehash', type=str, metavar="MALSHARE_HASH_SEARCH", help='Provides a hash as argument for downloading a sample from MALSHARE repository.')
 91 |     parser.add_argument('-j', '--haus_option', dest='hausoption', type=int, default=0, metavar="HAUS_OPTION", help='This option fetches information from URLHaus depending of the value passed as argument: 1: performs download of the given sample; 2: queries information about a provided hash ; 3: searches information about a given URL; 4: searches a malicious URL by a given tag (case sensitive); 5: searches for payloads given a tag; 6: retrives a list of downloadable links to recent payloads; 7: retrives a list of recent malicious URLs.')
 92 |     parser.add_argument('-J', '--haus_arg', dest='hausarg', type=str, metavar="HAUS_ARG", help='Provides argument to -j option from URLHaus.')
 93 |     parser.add_argument('-p', '--poly_option', dest='polyoption', type=int, default=0, metavar="POLY_OPTION", help='(Only for Linux) This option is related to POLYSWARM operations: 1. searches information related to a given hash provided using -P option; 2. submits a sample provided by -P option to be analyzed by Polyswarm engine ; 3. Downloads a sample from Polyswarm by providing the hash throught option -P .Attention: Polyswarm enforces a maximum of 20 samples per month; 4. searches for similar samples given a sample file thought option -P; 5. searches for samples related to a provided IP address through option -P; 6. searches for samples related to a given domain provided by option -P; 7. searches for samples related to a provided URL throught option -P; 8. searches for samples related to a provided malware family given by option -P.')
 94 |     parser.add_argument('-P', '--poly_arg', dest='polyarg', type=str, metavar="POLYSWARM_ARG", help='(Only for Linux) Provides an argument for -p option from POLYSWARM.')
 95 |     parser.add_argument('-y', '--android_option', dest='androidoption', type=int, default=0, metavar="ANDROID_OPTION", help='This ANDROID option has multiple possible values: <1>: Check all third-party APK packages from the USB-connected Android device against Hybrid Analysis using multithreads. Notes: the Android device does not need to be rooted and the system does need to have the adb tool in the PATH environment variable; <2>: Check all third-party APK packages from the USB-connected Android device against VirusTotal using Public API (slower because of 60 seconds delay for each 4 hashes). Notes: the Android device does not need to be rooted and the system does need to have adb tool in the PATH environment variable; <3>: Check all third-party APK packages from the USB-connected Android device against VirusTotal using multithreads (only for Private Virus API). Notes: the Android device does not need to be rooted and the system needs to have adb tool in the PATH environment variable; <4> Sends an third-party APK from your USB-connected Android device to Hybrid Analysis; 5. Sends an third-party APK from your USB-connected Android device to Virus-Total.')
 96 |     parser.add_argument('-Y', '--android_arg', dest='androidarg', type=str, default='', metavar="ANDROID_ARG", help='This option provides the argument for -y from ANDROID.')
 97 |     parser.add_argument('-n', '--alienvault', dest='alienvault', type=int, default=0, metavar="ALIENVAULT", help='Checks multiple information from ALIENVAULT. The possible values are: 1: Get the subscribed pulses ; 2: Get information about an IP address; 3: Get information about a domain; 4: Get information about a hash; 5: Get information about a URL.')
 98 |     parser.add_argument('-N', '--alienvaultargs', dest='alienvaultargs', type=str, default='', metavar="ALIENVAULT_ARGS", help='Provides argument to ALIENVAULT -n option.')
 99 |     parser.add_argument('-m', '--malpedia', dest='malpedia', type=int, default=0, metavar="MALPEDIA", help='This option is related to MALPEDIA and presents different meanings depending on the chosen value. Thus, 1: List meta information for all families ; 2: List all actors ID ; 3: List all available payloads organized by family from Malpedia; 4: Get meta information from an specific actor, so it is necessary to use the -M option. Additionally, try to confirm the correct actor ID by executing malwoverview with option -m 3; 5: List all families IDs; 6: Get meta information from an specific family, so it is necessary to use the -M option. Additionally, try to confirm the correct family ID by executing malwoverview with option -m 5; 7: Get a malware sample from malpedia (zip format -- password: infected). It is necessary to specify the requested hash by using -M option; 8: Get a zip file containing Yara rules for a specific family (get the possible families using -m 5), which must be specified by using -M option.')
100 |     parser.add_argument('-M', '--malpediarg', dest='malpediaarg', type=str, default='', metavar="MALPEDIAARG", help='This option provides an argument to the -m option, which is related to MALPEDIA.')
101 |     parser.add_argument('-b', '--bazaar', dest='bazaar', type=int, default=0, metavar="BAZAAR", help='Checks multiple information from MALWARE BAZAAR and THREATFOX. The possible values are: 1: (Bazaar) Query information about a malware hash sample ; 2: (Bazaar) Get information and a list of malware samples associated and according to a specific tag; 3: (Bazaar) Get a list of malware samples according to a given imphash; 4: (Bazaar) Query latest malware samples; 5: (Bazaar) Download a malware sample from Malware Bazaar by providing a SHA256 hash. The downloaded sample is zipped using the following password: infected; 6: (ThreatFox) Get current IOC dataset from last x days given by option -B (maximum of 7 days); 7: (ThreatFox) Search for the specified IOC on ThreatFox given by option -B; 8: (ThreatFox) Search IOCs according to the specified tag given by option -B; 9: (ThreatFox) Search IOCs according to the specified malware family provided by option -B; 10. (ThreatFox) List all available malware families.')
102 |     parser.add_argument('-B', '--bazaararg', dest='bazaararg', type=str, metavar = "BAZAAR_ARG", help='Provides argument to -b MALWARE BAZAAR and THREAT FOX option. If you specified "-b 1" then the -B\'s argument must be a hash and a report about the sample will be retrieved; If you specified "-b 2" then -B\'s argument must be a malware tag and last samples matching this tag will be shown; If you specified "-b 3" then the argument must be a imphash and last samples matching this impshash will be shown; If you specified "-b 4", so the argument must be "100 or time", where "100" lists last "100 samples" and "time" lists last samples added to Malware Bazaar in the last 60 minutes; If you specified "-b 5", so the sample will be downloaded and -B\'s argument must be a SHA256 hash of the sample that you want to download from Malware Bazaar; If you specified "-b 6" then a list of IOCs will be retrieved and the -B\'s value is the number of DAYS to filter such IOCs. The maximum time is 7 (days); If you used "-b 7" so the -B\'s argument is the IOC you want to search for; If you used "-b 8", so the -B\'s argument is the IOC\'s TAG that you want search for; If you used "-b 9", so the -B argument is the malware family that you want to search for IOCs;')
103 |     parser.add_argument('-x', '--triage', dest='triage', type=int, default=0, metavar="TRIAGE", help='Provides information from TRIAGE according to the specified value: <1> this option gets sample\'s general information by providing an argument with -X option in the following possible formats: sha256:<value>, sha1:<value>, md5:<value>, family:<value>, score:<value>, tag:<value>, url:<value>, wallet:<value>, ip:<value>; <2> Get a sumary report for a given Triage ID (got from option -x 1) ; <3> Submit a sample for analysis ; <4> Submit a sample through a URL for analysis ; <5> Download sample specified by the Triage ID; <6> Download pcapng file from sample associated to given Triage ID; <7> Get a dynamic report for the given Triage ID (got from option -x 1);')
104 |     parser.add_argument('-X', '--triagearg', dest='triagearg', type=str, default='', metavar="TRIAGE_ARG", help='Provides argument for options especified by -x option. Pay attention: the format of this argument depends on provided -x value.')
105 |     parser.add_argument('-i', '--inquest', dest='inquest', type=int, default=0, metavar="INQUEST", help='Retrieves multiple information from INQUEST. The possible values are: 1: Downloads a sample; 2: Retrives information about a sample given a SHA256; 3: Retrieves information about a sample given a MD5 hash; 4: Gets the most recent list of threats. To this option, the -I argument must be "list" (lowercase and without double quotes) ; 5: Retrives threats related to a provided domain; 6. Retrieves a list of samples related to the given IP address; 7. Retrives a list of sample related to the given e-mail address; 8. Retrieves a list of samples related to the given filename; 9. Retrieves a list of samples related to a given URL; 10. Retrieves information about a specified IOC; 11. List a list of IOCs. Note: you must pass "list" (without double quotes) as argument to -I; 12. Check for a given keyword in the reputation database; 13. List artifacts in the reputation dabatabse. Note: you must pass "list" (without double quotes) as argument to -I.')
106 |     parser.add_argument('-I', '--inquestarg', dest='inquestarg', type=str, metavar="INQUEST_ARG", help='Provides argument to INQUEST -i option.')
107 |     parser.add_argument('-vx', '--vx', dest='vxoption', type=int, default=0, help='VirusExchange operations. The possible values are: 1: Gets basic metadata for a given SHA256 hash; 2: Downloads sample given a SHA256 provided in the -VX argument.')
108 |     parser.add_argument('-VX', '--VX', dest='vxarg', type=str, help='Provides argument to the -vx option from VirusExchange.')
109 |     parser.add_argument('-O', '--output-dir', dest='output_dir', type=str, default='.', help='Set output directory for all sample downloads.')
110 |     parser.add_argument('-ip', '--ip', dest='ipoption', type=int, default=0, metavar="IP", help='Get IP information from various sources. The possible values are: 1: Get details for an IP address provided with -IP from IPInfo; 2: Get details for an IP address provided with -IP from BGPView; 3: Get details for an IP address provided with -IP from all available intel services (VirusTotal/Alienvault).')
111 |     parser.add_argument('-IP', '--iparg', dest='iparg', type=str, metavar="IP_ARG", help='Provides argument for IP lookup operations specified by the -ip option.')
112 | 
113 |     args = parser.parse_args()
114 | 
115 |     config_file = configparser.ConfigParser()
116 |     config_file.read(args.config)
117 |     config_dict = config_file
118 | 
119 |     def getoption(section, name):
120 |         if config_dict.has_option(section,name):
121 |             return config_dict.get(section,name)
122 |         else:
123 |             return ''
124 | 
125 |     VTAPI = getoption('VIRUSTOTAL', 'VTAPI')
126 |     HAAPI = getoption('HYBRID-ANALYSIS', 'HAAPI')
127 |     MALSHAREAPI = getoption('MALSHARE', 'MALSHAREAPI')
128 |     HAUSSUBMITAPI = getoption('HAUSSUBMIT', 'HAUSSUBMITAPI')
129 |     POLYAPI = getoption('POLYSWARM', 'POLYAPI')
130 |     ALIENAPI = getoption('ALIENVAULT', 'ALIENAPI')
131 |     MALPEDIAAPI = getoption('MALPEDIA', 'MALPEDIAAPI')
132 |     TRIAGEAPI = getoption('TRIAGE', 'TRIAGEAPI')
133 |     INQUESTAPI = getoption('INQUEST', 'INQUESTAPI')
134 |     VXAPI = getoption('VIRUSEXCHANGE', 'VXAPI')
135 |     IPINFOAPI = getoption('IPINFO', 'IPINFOAPI')
136 | 
137 |     optval = range(2)
138 |     optval1 = range(3)
139 |     optval2 = range(5)
140 |     optval3 = range(7)
141 |     optval4 = range(4)
142 |     optval5 = range(6)
143 |     optval6 = range(9)
144 |     optval7 = range(11)
145 |     optval8 = range(8)
146 |     optval9 = range(14)
147 |     optval10 = range(16)
148 |     repo = args.direct
149 |     cv.output_dir = args.output_dir
150 |     cv.bkg = args.backg
151 |     virustotaloptionx = args.virustotaloption
152 |     haoptionx = args.haoption
153 |     haargx = args.haarg
154 |     vtpubpremiumx = args.vtpubpremium
155 |     mallist = args.malsharelist
156 |     maltype = args.malsharelist
157 |     malhash = args.malsharehash
158 |     hausoptionx = args.hausoption
159 |     hausargx = args.hausarg
160 |     polyoptionx = args.polyoption
161 |     polyargx = args.polyarg
162 |     androidoptionx = args.androidoption
163 |     androidargx = args.androidarg
164 |     alienx = args.alienvault
165 |     alienargsx = args.alienvaultargs
166 |     malpediax = args.malpedia
167 |     malpediaargx = args.malpediaarg
168 |     bazaarx = args.bazaar
169 |     bazaarargx = args.bazaararg
170 |     triagex = args.triage
171 |     triageargx = args.triagearg
172 |     virustotalargx = args.virustotalarg
173 |     inquestx = args.inquest
174 |     inquestargx = args.inquestarg
175 |     vxoptionx = args.vxoption
176 |     vxargx = args.vxarg
177 |     ipoptionx = args.ipoption
178 |     ipargx = args.iparg
179 |     config = args.config
180 | 
181 |     ffpname = ''
182 |     if (virustotaloptionx in range(1, 5)):
183 |         ffpname = virustotalargx
184 |     if (haoptionx in range(1, 6)):
185 |         ffpname = haargx
186 | 
187 |     fprovided = 0
188 |     if (os.path.isfile(ffpname)):
189 |         fprovided = 1
190 | 
191 |     INVALID_ARG_CONDITIONS = [
192 | #        (virustotaloptionx in range(1, 5) or haoptionx in range(1, 6)) and fprovided == 0,
193 |         args.haoption not in optval10,
194 |         args.alienvault not in optval5,
195 |         args.hausoption not in optval8,
196 |         args.polyoption not in optval6,
197 |         args.bazaar not in optval7,
198 |         args.malpedia not in optval6,
199 |         args.triage not in optval8,
200 |         args.inquest not in optval9,
201 |         args.backg not in optval,
202 |         args.malsharelist not in optval8,
203 |         args.virustotaloption not in optval9,
204 |         args.vtpubpremium not in optval,
205 |         args.vxoption not in optval1,
206 |         args.ipoption not in optval4,
207 |         args.androidoption not in optval5
208 |     ]
209 | 
210 |     MIN_OPTIONS = [
211 |         virustotaloptionx in range(5, 10) and virustotalargx,
212 |         virustotalargx, virustotaloptionx, args.direct, fprovided,
213 |         haargx, mallist, args.malsharehash, args.hausoption, polyoptionx, polyargx,
214 |         androidoptionx, androidargx, alienx, alienargsx, malpediaargx,
215 |         malpediax, bazaarx, bazaarargx, triagex, triageargx,
216 |         inquestx, inquestargx, vxoptionx, vxargx, ipoptionx, ipargx
217 |     ]
218 | 
219 |     # Show the help message if:
220 |     # 1 - User uses invalid arg values
221 |     # 2 - User does not specify any of the minimum options required
222 |     if any(INVALID_ARG_CONDITIONS) or not any(MIN_OPTIONS):
223 |         parser.print_help()
224 |         printr()
225 |         exit(0)
226 | 
227 |     # Module objects
228 |     polyswarm = PolyswarmExtractor(POLYAPI)
229 |     alien = AlienVaultExtractor(ALIENAPI)
230 |     bazaar = BazaarExtractor()
231 |     threatfox = ThreatFoxExtractor()
232 |     triage = TriageExtractor(TRIAGEAPI)
233 |     inquest = InQuestExtractor(INQUESTAPI)
234 |     malpedia = MalpediaExtractor(MALPEDIAAPI)
235 |     virustotal = VirusTotalExtractor(VTAPI)
236 |     hybrid = HybridAnalysisExtractor(HAAPI)
237 |     malshare = MalshareExtractor(MALSHAREAPI)
238 |     haus = URLHausExtractor(HAUSSUBMITAPI)
239 |     android = AndroidExtractor(hybrid, virustotal)
240 |     vx = VirusExchangeExtractor(VXAPI)
241 |     ipinfo = IPInfoExtractor(IPINFOAPI)
242 |     bgpview = BGPViewExtractor()
243 |     multipleip = MultipleIPExtractor(
244 |         {
245 |             #"IPInfo": ipinfo,
246 |             #"BGPView": bgpview,
247 |             "VirusTotal": virustotal,
248 |             "AlienVault": alien,
249 |             #"InQuest": inquest,
250 |             # "PolySwarm": polyswarm,
251 |         }
252 |     )
253 | 
254 |     # Special parameters for hybrid analysis module
255 |     query = haargx
256 |     if haoptionx in range(6) and haargx and os.path.isfile(haargx):
257 |         query = calchash(haargx)
258 | 
259 |     def ha_show_and_down(haargx, xx=0):
260 | #        hybrid.hashow(haargx, xx=xx)
261 |         hybrid.downhash(haargx)
262 | 
263 |     # Map from flags to actions that they specify
264 |     # and parameters to be used with each method call
265 |     OPTIONS_MAPS = [
266 |         {
267 |             'flag': polyoptionx,
268 |             'actions': {
269 |                 1: (polyswarm.polyhashsearch, [polyargx, 0]),
270 |                 2: (polyswarm.polyfile, [polyargx]),
271 |                 3: (polyswarm.polyhashsearch, [polyargx, 1]),
272 |                 4: (polyswarm.polymetasearch, [polyargx, polyoptionx]),
273 |                 5: (polyswarm.polymetasearch, [polyargx, polyoptionx]),
274 |                 6: (polyswarm.polymetasearch, [polyargx, polyoptionx]),
275 |                 7: (polyswarm.polymetasearch, [polyargx, polyoptionx]),
276 |                 8: (polyswarm.polymetasearch, [polyargx, polyoptionx])
277 |             },
278 |         },
279 |         {
280 |             'flag': alienx,
281 |             'actions': {
282 |                 1: (alien.alien_subscribed, [alienargsx]),
283 |                 2: (alien.alien_ipv4, [alienargsx]),
284 |                 3: (alien.alien_domain, [alienargsx]),
285 |                 4: (alien.alien_hash, [alienargsx]),
286 |                 5: (alien.alien_url, [alienargsx])
287 |             }
288 |         },
289 |         {
290 |             'flag': bazaarx,
291 |             'actions': {
292 |                 1: (bazaar.bazaar_hash, [bazaarargx]),
293 |                 2: (bazaar.bazaar_tag, [bazaarargx]),
294 |                 3: (bazaar.bazaar_imphash, [bazaarargx]),
295 |                 4: (bazaar.bazaar_lastsamples, [bazaarargx]),
296 |                 5: (bazaar.bazaar_download, [bazaarargx])
297 |             }
298 |         },
299 |         {
300 |             'flag': bazaarx,
301 |             'actions': {
302 |                 6: (threatfox.threatfox_listiocs, [bazaarargx]),
303 |                 7: (threatfox.threatfox_searchiocs, [bazaarargx]),
304 |                 8: (threatfox.threatfox_searchtags, [bazaarargx]),
305 |                 9: (threatfox.threatfox_searchmalware, [bazaarargx]),
306 |                 10: (threatfox.threatfox_listmalware, [])
307 |             }
308 |         },
309 |         {
310 |             'flag': triagex,
311 |             'actions': {
312 |                 1: (triage.triage_search, [triageargx]),
313 |                 2: (triage.triage_summary, [triageargx]),
314 |                 3: (triage.triage_sample_submit, [triageargx]),
315 |                 4: (triage.triage_url_sample_submit, [triageargx]),
316 |                 5: (triage.triage_download, [triageargx]),
317 |                 6: (triage.triage_download_pcap, [triageargx]),
318 |                 7: (triage.triage_dynamic, [triageargx])
319 |             }
320 |         },
321 |         {
322 |             'flag': inquestx,
323 |             'actions': {
324 |                 1: (inquest.inquest_download, [inquestargx]),
325 |                 2: (inquest.inquest_hash, [inquestargx]),
326 |                 3: (inquest.inquest_hash_md5, [inquestargx]),
327 |                 4: (inquest.inquest_list, [inquestargx]),
328 |                 5: (inquest.inquest_domain, [inquestargx]),
329 |                 6: (inquest.inquest_ip, [inquestargx]),
330 |                 7: (inquest.inquest_email, [inquestargx]),
331 |                 8: (inquest.inquest_filename, [inquestargx]),
332 |                 9: (inquest.inquest_url, [inquestargx]),
333 |                 10: (inquest.inquest_ioc_search, [inquestargx]),
334 |                 11: (inquest.inquest_ioc_list, [inquestargx]),
335 |                 12: (inquest.inquest_rep_search, [inquestargx]),
336 |                 13: (inquest.inquest_rep_list, [inquestargx])
337 |             }
338 |         },
339 |         {
340 |             'flag': malpediax,
341 |             'actions': {
342 |                 1: (malpedia.malpedia_families, []),
343 |                 2: (malpedia.malpedia_actors, []),
344 |                 3: (malpedia.malpedia_payloads, []),
345 |                 4: (malpedia.malpedia_get_actor, [malpediaargx]),
346 |                 5: (malpedia.malpedia_families, []),
347 |                 6: (malpedia.malpedia_get_family, [malpediaargx]),
348 |                 7: (malpedia.malpedia_get_sample, [malpediaargx]),
349 |                 8: (malpedia.malpedia_get_yara, [malpediaargx])
350 |             }
351 |         },
352 |         {
353 |             'flag': virustotaloptionx,
354 |             'actions': {
355 |                 1: (virustotal.filechecking_v3, [virustotalargx, 0, 0, 0]),
356 |                 2: (virustotal.filechecking_v3, [virustotalargx, 1, 0, 0]),
357 |                 3: (virustotal.filechecking_v3, [virustotalargx, 1, 1, 0]),
358 |                 4: (virustotal.filechecking_v3, [virustotalargx, 1, 0, 1]),
359 |                 5: (virustotal.vturlwork, [virustotalargx]),
360 |                 6: (virustotal.vtipwork, [virustotalargx]),
361 |                 7: (virustotal.vtdomainwork, [virustotalargx]),
362 |                 8: (virustotal.vthashwork, [virustotalargx, 1]),
363 |                 9: (virustotal.vtuploadfile, [virustotalargx]),
364 |                 10: (virustotal.vtbatchcheck, [virustotalargx, 1]),
365 |                 11: (virustotal.vtbatchcheck, [virustotalargx, 0]),
366 |                 12: (virustotal.vtbehavior, [virustotalargx]),
367 |                 13: (virustotal.vtlargefile, [virustotalargx])
368 |             }
369 |         },
370 |         {
371 |             'flag': repo,
372 |             'actions': (virustotal.vtdirchecking, [repo, vtpubpremiumx])
373 |         },
374 |         {
375 |             'flag': haoptionx,
376 |             'actions': {
377 |                 1: (hybrid.hashow, [query], {'xx': 0}),
378 |                 2: (hybrid.hashow, [query], {'xx': 1}),
379 |                 3: (hybrid.hashow, [query], {'xx': 2}),
380 |                 4: (hybrid.hashow, [query], {'xx': 3}),
381 |                 5: (hybrid.hashow, [query], {'xx': 4}),
382 |                 6: (hybrid.hafilecheck, [haargx], {'xx': 0}),
383 |                 7: (hybrid.hafilecheck, [haargx], {'xx': 1}),
384 |                 8: (hybrid.hafilecheck, [haargx], {'xx': 2}),
385 |                 9: (hybrid.hafilecheck, [haargx], {'xx': 3}),
386 |                 10: (hybrid.hafilecheck, [haargx], {'xx': 4}),
387 |                 11: (ha_show_and_down, [haargx], {'xx': 0}),
388 |                 12: (ha_show_and_down, [haargx], {'xx': 1}),
389 |                 13: (ha_show_and_down, [haargx], {'xx': 2}),
390 |                 14: (ha_show_and_down, [haargx], {'xx': 3}),
391 |                 15: (ha_show_and_down, [haargx], {'xx': 4})
392 |             }
393 |         },
394 |         {
395 |             'flag': mallist,
396 |             'actions': {
397 |                 1: (malshare.malsharedown, [malhash]),
398 |                 2: (malshare.malsharelastlist, [maltype]),
399 |                 3: (malshare.malsharelastlist, [maltype]),
400 |                 4: (malshare.malsharelastlist, [maltype]),
401 |                 5: (malshare.malsharelastlist, [maltype]),
402 |                 6: (malshare.malsharelastlist, [maltype]),
403 |                 7: (malshare.malsharelastlist, [maltype])
404 |             }
405 |         },
406 |         {
407 |             'flag': hausoptionx,
408 |             'actions': {
409 |                 1: (haus.haussample, [hausargx]),
410 |                 2: (haus.haushashsearch, [hausargx]),
411 |                 3: (haus.urlhauscheck, [hausargx]),
412 |                 4: (haus.haustagsearchroutine, [hausargx]),
413 |                 5: (haus.haussigsearchroutine, [hausargx]),
414 |                 6: (haus.hauspayloadslist, []),
415 |                 7: (haus.hausgetbatch, [])
416 |             }
417 |         },
418 |         {
419 |             'flag': androidoptionx,
420 |             'actions': {
421 |                 1: (android.checkandroid, [1]),
422 |                 2: (android.checkandroid, [2]),
423 |                 3: (android.checkandroid, [3]),
424 |                 4: (android.sendandroidha, [androidargx]),
425 |                 5: (android.sendandroidvt, [androidargx])
426 |             }
427 |         },
428 |         {
429 |             'flag': vxoptionx,
430 |             'actions': {
431 |                 1: (vx.check_hash, [vxargx]),
432 |                 2: (vx.download_sample, [vxargx])
433 | #               3: (vx.upload_sample, [vxargx])
434 |             }
435 |         },
436 |         {
437 |             'flag': ipoptionx,
438 |             'actions': {
439 |                 1: (ipinfo.get_ip_details, [ipargx]),
440 |                 2: (bgpview.get_ip_details, [ipargx]),
441 |                 3: (multipleip.get_multiple_ip_details, [ipargx])
442 |             }
443 |         }
444 |     ]
445 | 
446 |     # Dispatch the first selected action with the specified parameters
447 |     for option_map in OPTIONS_MAPS:
448 |         flag = option_map['flag']
449 |         actions = option_map['actions']
450 | 
451 |         if isinstance(actions, dict) and flag in actions:
452 |             action_obj = actions[flag]
453 |         elif isinstance(actions, tuple) and flag:
454 |             action_obj = actions
455 |         else:
456 |             continue
457 | 
458 |         # If flag conditions are met, then call the appropriate function with its parameters
459 |         if len(action_obj) == 3:
460 |             action, action_args, action_kwargs = action_obj
461 |             result = action(*action_args, **action_kwargs)
462 |         elif len(action_obj) == 2:
463 |             action, action_args = action_obj
464 |             result = action(*action_args)
465 |         elif len(action_obj) == 1:
466 |             action = action_obj[0]
467 |             result = action()
468 |         else:
469 |             continue
470 | 
471 |         printr()
472 |         status = 0
473 |         if result is False:
474 |             status = 1
475 | 
476 |         exit(status)
477 | 
478 | if __name__ == "__main__":
479 |     main()
480 | 


--------------------------------------------------------------------------------
/malwoverview/modules/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/malwoverview/modules/__init__.py


--------------------------------------------------------------------------------
/malwoverview/modules/android.py:
--------------------------------------------------------------------------------
  1 | import malwoverview.modules.configvars as cv
  2 | from malwoverview.utils.colors import mycolors, printr
  3 | from malwoverview.utils.hash import sha256hash
  4 | from malwoverview.modules.hybrid import HybridAnalysisExtractor
  5 | import requests
  6 | import subprocess
  7 | import threading
  8 | import json
  9 | import time
 10 | import os
 11 | 
 12 | # threadLimiter = threading.BoundedSemaphore(10)
 13 | 
 14 | 
 15 | class androidVTThread(threading.Thread):
 16 |     def __init__(self, key, package, extractor):
 17 |         threading.Thread.__init__(self)
 18 |         self.key = key
 19 |         self.package = package
 20 |         self.extractor = extractor
 21 | 
 22 |     def run(self):
 23 | #        threadLimiter.acquire()
 24 | #        try:
 25 |         key1 = self.key
 26 |         package1 = self.package
 27 | 
 28 |         myhash = key1
 29 |         vtfinal = self.extractor.virustotal.vtcheck(myhash, 0)
 30 | 
 31 |         if (cv.bkg == 1):
 32 |             print((mycolors.foreground.yellow + "%-70s" % package1), end=' ')
 33 |             print((mycolors.foreground.lightcyan + "%-32s" % key1), end=' ')
 34 |             print((mycolors.reset + mycolors.foreground.lightcyan + "%8s" % vtfinal + mycolors.reset))
 35 |         else:
 36 |             print((mycolors.foreground.green + "%-70s" % package1), end=' ')
 37 |             print((mycolors.foreground.cyan + "%-32s" % key1), end=' ')
 38 |             print((mycolors.reset + mycolors.foreground.red + "%8s" % vtfinal + mycolors.reset))
 39 | #        finally:
 40 | #            threadLimiter.release()
 41 | 
 42 | 
 43 | class quickHAAndroidThread(threading.Thread):
 44 |     def __init__(self, key, package, extractor):
 45 |         threading.Thread.__init__(self)
 46 |         self.key = key
 47 |         self.package = package
 48 |         self.extractor = extractor
 49 | 
 50 |     def run(self):
 51 | #        threadLimiter.acquire()
 52 | #        try:
 53 |         key1 = self.key
 54 |         package1 = self.package
 55 | 
 56 |         myhash = key1
 57 |         result = self.extractor.quickhashowAndroid(myhash)
 58 | 
 59 |         (final, verdict, avdetect, totalsignatures, threatscore, totalprocesses, networkconnections) = result
 60 | 
 61 |         if (cv.bkg == 1):
 62 |             print((mycolors.foreground.lightcyan + "%-70s" % package1), end=' ')
 63 |             print((mycolors.foreground.yellow + "%-34s" % key1), end=' ')
 64 |             print((mycolors.foreground.lightcyan + "%9s" % final), end='')
 65 |             if (avdetect == 'None'):
 66 |                 print((mycolors.foreground.lightcyan + "%7s" % avdetect), end='')
 67 |             else:
 68 |                 print((mycolors.foreground.lightcyan + "%6s%%" % avdetect), end='')
 69 |             print((mycolors.foreground.yellow + "%7s" % totalsignatures), end='')
 70 |             if (threatscore == 'None'):
 71 |                 print((mycolors.foreground.lightred + "%12s" % threatscore), end='')
 72 |             else:
 73 |                 print((mycolors.foreground.lightred + "%8s/100" % threatscore), end='')
 74 |             if (verdict == "malicious"):
 75 |                 print((mycolors.foreground.lightred + "%20s" % verdict), end='\n')
 76 |             elif (verdict == "suspicious"):
 77 |                 print((mycolors.foreground.yellow + "%20s" % verdict), end='\n')
 78 |             elif (verdict == "no specific threat"):
 79 |                 print((mycolors.foreground.lightcyan + "%20s" % verdict), end='\n')
 80 |             else:
 81 |                 verdict = 'not analyzed yet'
 82 |                 print((mycolors.reset + "%20s" % verdict), end='\n')
 83 |         else:
 84 |             print((mycolors.foreground.cyan + "%-70s" % package1), end=' ')
 85 |             print((mycolors.foreground.green + "%-34s" % key1), end=' ')
 86 |             print((mycolors.foreground.cyan + "%9s" % final), end='')
 87 |             if (avdetect == 'None'):
 88 |                 print((mycolors.foreground.purple + "%7s" % avdetect), end='')
 89 |             else:
 90 |                 print((mycolors.foreground.purple + "%6s%%" % avdetect), end='')
 91 |             print((mycolors.foreground.green + "%7s" % totalsignatures), end='')
 92 |             if (threatscore == 'None'):
 93 |                 print((mycolors.foreground.red + "%12s" % threatscore), end='')
 94 |             else:
 95 |                 print((mycolors.foreground.red + "%8s/100" % threatscore), end='')
 96 |             if (verdict == "malicious"):
 97 |                 print((mycolors.foreground.red + "%20s" % verdict), end='\n')
 98 |             elif (verdict == "suspicious"):
 99 |                 print((mycolors.foreground.cyan + "%20s" % verdict), end='\n')
100 |             elif (verdict == "no specific threat"):
101 |                 print((mycolors.foreground.green + "%20s" % verdict), end='\n')
102 |             else:
103 |                 verdict = 'not analyzed yet'
104 |                 print((mycolors.reset + "%20s" % verdict), end='\n')
105 | #        finally:
106 | #            threadLimiter.release()
107 | 
108 | 
109 | class AndroidExtractor():
110 |     def __init__(self, hybrid, virustotal):
111 |         self.hybrid = hybrid
112 |         self.virustotal = virustotal
113 | 
114 |     def quickhashowAndroid(self, filehash, user_agent='Falcon Sandbox'):
115 |         haurl = HybridAnalysisExtractor.haurl
116 | 
117 |         hatext = ''
118 |         haresponse = ''
119 |         final = 'Yes'
120 |         verdict = '-'
121 |         avdetect = '0'
122 |         totalsignatures = '-'
123 |         threatscore = '-'
124 |         totalprocesses = '-'
125 |         networkconnections = '-'
126 | 
127 |         self.hybrid.requestHAAPI()
128 | 
129 |         try:
130 |             resource = filehash
131 |             requestsession = requests.Session()
132 |             requestsession.headers.update({'user-agent': user_agent})
133 |             requestsession.headers.update({'api-key': self.hybrid.HAAPI})
134 |             requestsession.headers.update({'content-type': 'application/x-www-form-urlencoded'})
135 |             finalurl = '/'.join([haurl, 'report', 'summary'])
136 |             resource1 = resource + ":200"
137 |             datahash = {
138 |                 'hashes[0]': resource1
139 |             }
140 | 
141 |             haresponse = requestsession.post(url=finalurl, data=datahash)
142 |             hatext = json.loads(haresponse.text)
143 | 
144 |             rc = str(hatext)
145 | 
146 |             if 'message' in rc:
147 |                 final = 'Not Found'
148 |                 return (final, verdict, avdetect, totalsignatures, threatscore, totalprocesses, networkconnections)
149 | 
150 |             if 'verdict' in hatext[0]:
151 |                 verdict = str(hatext[0]['verdict'])
152 |             else:
153 |                 verdict = ''
154 | 
155 |             if 'threat_score' in hatext[0]:
156 |                 threatscore = str(hatext[0]['threat_score'])
157 |             else:
158 |                 threatscore = ''
159 | 
160 |             if 'av_detect' in hatext[0]:
161 |                 avdetect = str(hatext[0]['av_detect'])
162 |             else:
163 |                 avdetect = ''
164 | 
165 |             if 'total_signatures' in hatext[0]:
166 |                 totalsignatures = str(hatext[0]['total_signatures'])
167 |             else:
168 |                 totalsignatures = ''
169 | 
170 |             if 'total_processes' in hatext[0]:
171 |                 totalprocesses = str(hatext[0]['total_processes'])
172 |             else:
173 |                 totalprocesses = ''
174 | 
175 |             if 'total_network_connections' in hatext[0]:
176 |                 networkconnections = str(hatext[0]['total_network_connections'])
177 |             else:
178 |                 networkconnections = ''
179 | 
180 |             return (final, verdict, avdetect, totalsignatures, threatscore, totalprocesses, networkconnections)
181 | 
182 |         except ValueError as e:
183 |             print(e)
184 |             if (cv.bkg == 1):
185 |                 print((mycolors.foreground.lightred + "Error while connecting to Hybrid-Analysis!\n"))
186 |             else:
187 |                 print((mycolors.foreground.red + "Error while connecting to Hybrid-Analysis!\n"))
188 |             printr()
189 | 
190 |     def checkandroidha(self, key, package):
191 |         if len(key) == 0 or len(package) == 0:
192 |             return
193 | 
194 |         if cv.windows:
195 |             thread = quickHAAndroidThread(key, package, self)
196 |             thread.start()
197 |             thread.join()
198 |         else:
199 |             thread = quickHAAndroidThread(key, package, self)
200 |             thread.start()
201 | 
202 |     def checkandroidvt(self, key, package):
203 |         if len(key) == 0 or len(package) == 0:
204 |             return
205 | 
206 |         key1 = key
207 |         vtfinal = self.virustotal.vtcheck(key1, 0)
208 |         if (cv.bkg == 1):
209 |             print((mycolors.foreground.yellow + "%-70s" % package), end=' ')
210 |             print((mycolors.foreground.lightcyan + "%-32s" % key1), end=' ')
211 |             print((mycolors.foreground.lightred + "%8s" % vtfinal + mycolors.reset))
212 |         else:
213 |             print((mycolors.foreground.green + "%-70s" % package), end=' ')
214 |             print((mycolors.foreground.cyan + "%-32s" % key1), end=' ')
215 |             print((mycolors.reset + mycolors.foreground.red + "%8s" % vtfinal + mycolors.reset))
216 | 
217 |     def checkandroidvtx(self, key, package):
218 |         if len(key) == 0 or len(package) == 0:
219 |             return
220 | 
221 |         if (cv.windows == 1):
222 |             thread = androidVTThread(key, package, self)
223 |             thread.start()
224 |             thread.join()
225 |         else:
226 |             thread = androidVTThread(key, package, self)
227 |             thread.start()
228 | 
229 |     def checkandroid(self, engine):
230 |         adb_comm = "adb"
231 |         results = list()
232 |         results2 = list()
233 |         final1 = list()
234 |         final2 = list()
235 | 
236 |         tm1 = 0
237 | 
238 |         myconn = subprocess.run([adb_comm, "shell", "pm", "list", "packages", "-f", "-3"], capture_output=True)
239 |         myconn2 = myconn.stdout.decode()
240 | 
241 |         try:
242 |             for i in myconn2.split('\n'):
243 |                 for j in i.split("base.apk"):
244 |                     if 'package' in j:
245 |                         key, value = j.split('package:')
246 |                         _, value2 = value.split('/data/app/')
247 |                         results.append(value2[:-3])
248 |                         valuetmp = value + "base.apk"
249 |                         results2.append(valuetmp)
250 |         except AttributeError:
251 |             pass
252 | 
253 |         try:
254 |             for h in results2:
255 |                 myconn3 = subprocess.run([adb_comm, "shell", "md5sum", h], text=True, capture_output=True)
256 |                 x = myconn3.stdout.split(" ")[0]
257 |                 final1.append(x)
258 | 
259 |         except AttributeError:
260 |             pass
261 | 
262 |         try:
263 |             for n in results:
264 |                 final2.append(n)
265 |         except AttributeError:
266 |             pass
267 | 
268 |         zipAndroid = zip(final2, final1)
269 |         dictAndroid = dict(zipAndroid)
270 | 
271 |         if (engine == 1):
272 |             print(mycolors.reset + "\n")
273 |             print("Package".center(70) + "Hash".center(34) + "Found?".center(12) + "AVdet".center(10) + "Sigs".center(5) + "Score".center(14) + "Verdict".center(14))
274 |             print((162 * '-').center(81))
275 |             for key, value in dictAndroid.items():
276 |                 try:
277 |                     key1a = (key.split("==/", 1)[1])
278 |                 except IndexError:
279 |                     key1a = key
280 |                 try:
281 |                     key1b = (key1a.split("-", 1)[0])
282 |                 except IndexError:
283 |                     key1b = key1a
284 | 
285 |                 self.checkandroidha(value, key1b)
286 | 
287 |         if (engine == 2):
288 |             print(mycolors.reset + "\n")
289 |             print("Package".center(70) + "Hash".center(36) + "Virus Total".center(12))
290 |             print((118 * '-').center(59))
291 |             for key, value in dictAndroid.items():
292 |                 try:
293 |                     key1a = (key.split("==/", 1)[1])
294 |                 except IndexError:
295 |                     key1a = key
296 |                 try:
297 |                     key1b = (key1a.split("-", 1)[0])
298 |                 except IndexError:
299 |                     key1b = key1a
300 |                 tm1 = tm1 + 1
301 |                 if tm1 % 4 == 0:
302 |                     time.sleep(61)
303 |                 self.checkandroidvt(value, key1b)
304 | 
305 |         if (engine == 3):
306 |             print(mycolors.reset + "\n")
307 |             print("Package".center(70) + "Hash".center(36) + "Virus Total".center(12))
308 |             print((118 * '-').center(59))
309 |             for key, value in dictAndroid.items():
310 |                 try:
311 |                     key1a = (key.split("==/", 1)[1])
312 |                 except IndexError:
313 |                     key1a = key
314 |                 try:
315 |                     key1b = (key1a.split("-", 1)[0])
316 |                 except IndexError:
317 |                     key1b = key1a
318 |                 self.checkandroidvtx(value, key1b)
319 | 
320 |     def sendandroidha(self, package, xx=3):
321 |         adb_comm = "adb"
322 |         results = list()
323 |         results2 = list()
324 |         newname = ''
325 | 
326 |         myconn = subprocess.run([adb_comm, "shell", "pm", "list", "packages", "-f", "-3"], capture_output=True)
327 |         myconn2 = myconn.stdout.decode()
328 | 
329 |         try:
330 |             for i in myconn2.split('\n'):
331 |                 for j in i.split('base.apk'):
332 |                     if 'package' in j:
333 |                         _, value = j.split('package:')
334 |                         _, value2 = value.split('/data/app/')
335 |                         results.append(value2)
336 |                         valuetmp = value + "base.apk"
337 |                         results2.append(valuetmp)
338 | 
339 |         except AttributeError:
340 |             pass
341 | 
342 |         try:
343 |             for j in results2:
344 |                 if (package in j):
345 |                     subprocess.run([adb_comm, "pull", j], capture_output=True)
346 |                     newname = j[10:]
347 | 
348 |         except AttributeError:
349 |             pass
350 | 
351 |         try:
352 |             targetfile1 = newname.split('==/', 1)[1]
353 |             targetfile = targetfile1.split('-', 1)[0]
354 |             os.rename('base.apk', targetfile)
355 |             self.hybrid.hafilecheck(targetfile, xx=xx)
356 |         except FileNotFoundError:
357 |             if (cv.bkg == 1):
358 |                 print((mycolors.foreground.lightred + "\nFile not found on device!\n"))
359 |             else:
360 |                 print((mycolors.foreground.lightred + "\nFile not found on device!\n"))
361 |             exit(1)
362 |         finally:
363 |             if (targetfile != ".apk"):
364 |                 os.remove(targetfile)
365 | 
366 |     def sendandroidvt(self, package):
367 |         adb_comm = "adb"
368 |         results = list()
369 |         results2 = list()
370 |         newname = ''
371 | 
372 |         myconn = subprocess.run([adb_comm, "shell", "pm", "list", "packages", "-f", "-3"], capture_output=True)
373 |         myconn2 = myconn.stdout.decode()
374 | 
375 |         try:
376 |             for i in myconn2.split('\n'):
377 |                 for j in i.split('base.apk='):
378 |                     if 'package' in j:
379 |                         _, value = j.split('package:')
380 |                         _, value2 = value.split('/data/app/')
381 |                         results.append(value2)
382 |                         valuetmp = value + "base.apk"
383 |                         results2.append(valuetmp)
384 | 
385 |         except AttributeError:
386 |             pass
387 | 
388 |         try:
389 |             for j in results2:
390 |                 if (package in j):
391 |                     subprocess.run([adb_comm, "pull", j], capture_output=True)
392 |                     newname = j[10:]
393 | 
394 |         except AttributeError:
395 |             pass
396 | 
397 |         try:
398 |             targetfile1 = newname.split('==/', 1)[1]
399 |             targetfile = targetfile1.split('-', 1)[0]
400 |             os.rename(r'base.apk', targetfile)
401 |             myhash = sha256hash(targetfile)
402 |             self.virustotal.vtuploadfile(targetfile)
403 |             if (cv.bkg == 1):
404 |                 print(mycolors.foreground.yellow + "\tWaiting for 120 seconds...\n")
405 |             if (cv.bkg == 0):
406 |                 print(mycolors.foreground.purple + "\tWaiting for 120 seconds...\n")
407 |             time.sleep(120)
408 |             self.virustotal.vthashwork(myhash, 1)
409 | 
410 |         except FileNotFoundError:
411 |             if (cv.bkg == 1):
412 |                 print((mycolors.foreground.lightred + "\nFile not found on device!\n"))
413 |             else:
414 |                 print((mycolors.foreground.lightred + "\nFile not found on device!\n"))
415 |             printr()
416 |             exit(1)
417 | 
418 |         finally:
419 |             if (targetfile != ".apk"):
420 |                 os.remove(targetfile)
421 | 


--------------------------------------------------------------------------------
/malwoverview/modules/bgpview.py:
--------------------------------------------------------------------------------
 1 | import requests
 2 | import malwoverview.modules.configvars as cv
 3 | from malwoverview.utils.colors import mycolors
 4 | 
 5 | class BGPViewExtractor:
 6 |     urlbgpview = "https://api.bgpview.io/ip/"
 7 | 
 8 |     def _raw_ip_info(self, ip_address):
 9 |         url = f"{BGPViewExtractor.urlbgpview}{ip_address}"
10 |         
11 |         try:
12 |             response = requests.get(url)
13 |             data = response.json()
14 |             return data.get('data', {}) if data.get('status') == 'ok' else {}
15 |         except:
16 |             return {}
17 | 
18 |     def get_ip_details(self, ip_address):
19 |         data = self._raw_ip_info(ip_address)
20 | 
21 |         try:
22 |             print()
23 |             print((mycolors.reset + "BGPVIEW.IO REPORT".center(100)), end='')
24 |             print((mycolors.reset + "".center(28)), end='')
25 |             print("\n" + (100 * '-').center(50))
26 |             
27 |             if not data:
28 |                 print(mycolors.foreground.error(cv.bkg) + "\nNo information available\n" + mycolors.reset)
29 |                 return
30 | 
31 |             prefixes = data.get('prefixes', [{}])
32 |             if len(prefixes) == 0:
33 |                 prefixes = [{}]
34 | 
35 |             fields = {
36 |                 'IP Address': data.get('ip'),
37 |                 'PTR Record': data.get('ptr_record'),
38 |                 'Prefix': prefixes[0].get('prefix'),
39 |                 'ASN': prefixes[0].get('asn', {}).get('asn'),
40 |                 'AS Name': prefixes[0].get('asn', {}).get('name'),
41 |                 'AS Description': prefixes[0].get('asn', {}).get('description'),
42 |                 'Country Code': prefixes[0].get('asn', {}).get('country_code')
43 |             }
44 | 
45 |             COLSIZE = max(len(field) for field in fields.keys()) + 3
46 |             
47 |             for field, value in fields.items():
48 |                 print(mycolors.foreground.info(cv.bkg) + f"{field}: ".ljust(COLSIZE) + mycolors.reset + str(value))
49 | 
50 |         except Exception as e:
51 |             print(mycolors.foreground.error(cv.bkg) + f"\nError: {str(e)}" + mycolors.reset)
52 | 


--------------------------------------------------------------------------------
/malwoverview/modules/configvars.py:
--------------------------------------------------------------------------------
1 | bkg = 0
2 | windows = 0
3 | output_dir = '.'


--------------------------------------------------------------------------------
/malwoverview/modules/hybrid.py:
--------------------------------------------------------------------------------
  1 | import malwoverview.modules.configvars as cv
  2 | import requests
  3 | from colorama import Fore
  4 | import geocoder
  5 | from malwoverview.utils.colors import mycolors, printr
  6 | from malwoverview.utils.hash import sha256hash
  7 | import json
  8 | import os
  9 | 
 10 | 
 11 | class HybridAnalysisExtractor():
 12 |     haurl = 'https://www.hybrid-analysis.com/api/v2'
 13 | 
 14 |     def __init__(self, HAAPI):
 15 |         self.HAAPI = HAAPI
 16 | 
 17 |     def requestHAAPI(self):
 18 |         if (self.HAAPI == ''):
 19 |             print(mycolors.foreground.red + "\nTo be able to get/submit information from/to Hybrid Analysis, you must create the .malwapi.conf file under your user home directory (on Linux is $HOME\\.malwapi.conf and on Windows is in C:\\Users\\[username]\\.malwapi.conf) and insert the Hybrid Analysis API according to the format shown on the Github website." + mycolors.reset + "\n")
 20 |             exit(1)
 21 | 
 22 |     def downhash(self, filehash, user_agent='Falcon Sandbox'):
 23 |         haurl = HybridAnalysisExtractor.haurl
 24 | 
 25 |         hatext = ''
 26 |         haresponse = ''
 27 |         final = ''
 28 | 
 29 |         self.requestHAAPI()
 30 | 
 31 |         try:
 32 | 
 33 |             resource = filehash
 34 |             requestsession = requests.Session()
 35 |             requestsession.headers.update({'user-agent': user_agent})
 36 |             requestsession.headers.update({'api-key': self.HAAPI})
 37 |             requestsession.headers.update({'accept': 'application/gzip'})
 38 | 
 39 |             finalurl = '/'.join([haurl, 'overview', resource, 'sample'])
 40 | 
 41 |             haresponse = requestsession.get(url=finalurl, allow_redirects=True)
 42 | 
 43 |             try:
 44 | 
 45 |                 hatext = haresponse.text
 46 | 
 47 |                 rc = str(hatext)
 48 |                 if 'message' in rc:
 49 |                     final = 'Malware sample is not available to download.'
 50 |                     if (cv.bkg == 1):
 51 |                         print((mycolors.foreground.lightred + "\n" + final + "\n"))
 52 |                     else:
 53 |                         print((mycolors.foreground.red + "\n" + final + "\n"))
 54 |                     print((mycolors.reset))
 55 |                     return final
 56 | 
 57 |                 outputpath = os.path.join(cv.output_dir, f'{resource}.gz')
 58 |                 open(outputpath, 'wb').write(haresponse.content)
 59 |                 final = f'Sample downloaded to: {outputpath}'
 60 | 
 61 |                 print((mycolors.reset))
 62 |                 print((final + "\n"))
 63 |                 return final
 64 | 
 65 |             except ValueError as e:
 66 |                 print(e)
 67 |                 if (cv.bkg == 1):
 68 |                     print((mycolors.foreground.lightred + "Error while downloading Hybrid-Analysis!\n"))
 69 |                 else:
 70 |                     print((mycolors.foreground.red + "Error while downloading Hybrid-Analysis!\n"))
 71 |                 printr()
 72 | 
 73 |         except ValueError as e:
 74 |             print(e)
 75 |             if (cv.bkg == 1):
 76 |                 print((mycolors.foreground.lightred + "Error while connecting to Hybrid-Analysis!\n"))
 77 |             else:
 78 |                 print((mycolors.foreground.red + "Error while connecting to Hybrid-Analysis!\n"))
 79 |             printr()
 80 | 
 81 |     def hashow(self, filehash, xx=0, user_agent='Falcon Sandbox'):
 82 |         haurl = HybridAnalysisExtractor.haurl
 83 | 
 84 |         hatext = ''
 85 |         haresponse = ''
 86 |         final = ''
 87 | 
 88 |         self.requestHAAPI()
 89 | 
 90 |         try:
 91 |             resource = filehash
 92 |             requestsession = requests.Session()
 93 |             requestsession.headers.update({'user-agent': user_agent})
 94 |             requestsession.headers.update({'api-key': self.HAAPI})
 95 |             requestsession.headers.update({'content-type': 'application/json'})
 96 | 
 97 |             if (xx == 0):
 98 |                 finalurl = '/'.join([haurl, 'report', resource + ':100', 'summary'])
 99 |             elif (xx == 1):
100 |                 finalurl = '/'.join([haurl, 'report', resource + ':110', 'summary'])
101 |             elif (xx == 2):
102 |                 finalurl = '/'.join([haurl, 'report', resource + ':120', 'summary'])
103 |             elif (xx == 3):
104 |                 finalurl = '/'.join([haurl, 'report', resource + ':200', 'summary'])
105 |             else:
106 |                 finalurl = '/'.join([haurl, 'report', resource + ':300', 'summary'])
107 | 
108 |             haresponse = requestsession.get(url=finalurl)
109 |             hatext = json.loads(haresponse.text)
110 | 
111 |             rc = str(hatext)
112 |             if 'Failed' in rc:
113 |                 final = 'Malware sample was not found in Hybrid-Analysis repository.'
114 |                 if (cv.bkg == 1):
115 |                     print((mycolors.foreground.lightred + "\n" + final + "\n"))
116 |                 else:
117 |                     print((mycolors.foreground.red + "\n" + final + "\n"))
118 |                 return final
119 | 
120 |             if 'environment_description' in hatext:
121 |                 envdesc = str(hatext['environment_description'])
122 |             else:
123 |                 envdesc = ''
124 | 
125 |             if 'type' in hatext:
126 |                 maltype = str(hatext['type'])
127 |             else:
128 |                 maltype = ''
129 | 
130 |             if 'verdict' in hatext:
131 |                 verdict = str(hatext['verdict'])
132 |             else:
133 |                 verdict = ''
134 | 
135 |             if 'threat_level' in hatext:
136 |                 threatlevel = str(hatext['threat_level'])
137 |             else:
138 |                 threatlevel = ''
139 | 
140 |             if 'threat_score' in hatext:
141 |                 threatscore = str(hatext['threat_score'])
142 |             else:
143 |                 threatscore = ''
144 | 
145 |             if 'av_detect' in hatext:
146 |                 avdetect = str(hatext['av_detect'])
147 |             else:
148 |                 avdetect = ''
149 | 
150 |             if 'total_signatures' in hatext:
151 |                 totalsignatures = str(hatext['total_signatures'])
152 |             else:
153 |                 totalsignatures = ''
154 | 
155 |             if 'submit_name' in hatext:
156 |                 submitname = str(hatext['submit_name'])
157 |             else:
158 |                 submitname = ''
159 | 
160 |             if 'analysis_start_time' in hatext:
161 |                 analysistime = str(hatext['analysis_start_time'])
162 |             else:
163 |                 analysistime = ''
164 | 
165 |             if 'size' in hatext:
166 |                 malsize = str(hatext['size'])
167 |             else:
168 |                 malsize = ''
169 | 
170 |             if 'total_processes' in hatext:
171 |                 totalprocesses = str(hatext['total_processes'])
172 |             else:
173 |                 totalprocesses = ''
174 | 
175 |             if 'total_network_connections' in hatext:
176 |                 networkconnections = str(hatext['total_network_connections'])
177 |             else:
178 |                 networkconnections = ''
179 | 
180 |             if 'domains' in hatext:
181 |                 domains = (hatext['domains'])
182 |             else:
183 |                 domains = ''
184 | 
185 |             if 'hosts' in hatext:
186 |                 hosts = (hatext['hosts'])
187 |             else:
188 |                 hosts = ''
189 | 
190 |             if 'compromised_hosts' in hatext:
191 |                 compromised_hosts = (hatext['compromised_hosts'])
192 |             else:
193 |                 compromised_hosts = ''
194 | 
195 |             if 'vx_family' in hatext:
196 |                 vxfamily = str(hatext['vx_family'])
197 |             else:
198 |                 vxfamily = ''
199 | 
200 |             if 'type_short' in (hatext):
201 |                 typeshort = (hatext['type_short'])
202 |             else:
203 |                 typeshort = ''
204 | 
205 |             if 'tags' in hatext:
206 |                 classification = (hatext['tags'])
207 |             else:
208 |                 classification = ''
209 | 
210 |             if 'certificates' in hatext:
211 |                 certificates = hatext['certificates']
212 |             else:
213 |                 certificates = ''
214 | 
215 |             if 'mitre_attcks' in hatext:
216 |                 mitre = hatext['mitre_attcks']
217 |             else:
218 |                 mitre = ''
219 | 
220 |             printr()
221 |             print("\nHybrid-Analysis Summary Report:")
222 |             print((70 * '-').ljust(70))
223 |             if (cv.bkg == 1):
224 |                 print((mycolors.foreground.lightcyan))
225 |             else:
226 |                 print((mycolors.foreground.red))
227 |             print("Environment:".ljust(20), envdesc)
228 |             print("File Type:".ljust(20), maltype)
229 |             print("Verdict:".ljust(20), verdict)
230 |             print("Threat Level:".ljust(20), threatlevel)
231 |             print("Threat Score:".ljust(20), threatscore + '/100')
232 |             print("AV Detect".ljust(20), avdetect + '%')
233 |             print("Total Signatures:".ljust(20), totalsignatures)
234 |             if (cv.bkg == 1):
235 |                 print((mycolors.foreground.yellow))
236 |             else:
237 |                 print((mycolors.foreground.cyan))
238 |             print("Submit Name:".ljust(20), submitname)
239 |             print("Analysis Time:".ljust(20), analysistime)
240 |             print("File Size:".ljust(20), malsize)
241 |             print("Total Processes:".ljust(20), totalprocesses)
242 |             print("Network Connections:".ljust(20), networkconnections)
243 | 
244 |             print("\nDomains:")
245 |             for i in domains:
246 |                 print("".ljust(20), i)
247 | 
248 |             print("\nHosts:")
249 |             for i in hosts:
250 |                 print("".ljust(20), i, "\t", "city: " + (geocoder.ip(i).city))
251 | 
252 |             print("\nCompromised Hosts:")
253 |             for i in compromised_hosts:
254 |                 print("".ljust(20), i, "\t", "city: " + (geocoder.ip(i).city))
255 | 
256 |             if (cv.bkg == 1):
257 |                 print((mycolors.foreground.lightred))
258 |             else:
259 |                 print((mycolors.foreground.cyan))
260 | 
261 |             print("Vx Family:".ljust(20), vxfamily)
262 |             print("File Type Short:    ", end=' ')
263 |             for i in typeshort:
264 |                 print(i, end=' ')
265 | 
266 |             print("\nClassification Tags:".ljust(20), end=' ')
267 |             for i in classification:
268 |                 print(i, end=' ')
269 | 
270 |             if (cv.bkg == 1):
271 |                 print((mycolors.foreground.lightcyan))
272 |             else:
273 |                 print((mycolors.foreground.purple))
274 | 
275 |             print("\nCertificates:\n", end='\n')
276 |             for i in certificates:
277 |                 print("".ljust(20), end=' ')
278 |                 print(("owner: %s" % i['owner']))
279 |                 print("".ljust(20), end=' ')
280 |                 print(("issuer: %s" % i['issuer']))
281 |                 print("".ljust(20), end=' ')
282 |                 print(("valid_from: %s" % i['valid_from']))
283 |                 print("".ljust(20), end=' ')
284 |                 print(("valid_until: %s\n" % i['valid_until']))
285 | 
286 |             if (cv.bkg == 1):
287 |                 print(mycolors.foreground.lightcyan)
288 |             else:
289 |                 print(mycolors.foreground.purple)
290 | 
291 |             print("\nMITRE Attacks:\n")
292 |             for i in mitre:
293 |                 print("".ljust(20), end=' ')
294 |                 print(("tactic: %s" % i['tactic']))
295 |                 print("".ljust(20), end=' ')
296 |                 print(("technique: %s" % i['technique']))
297 |                 print("".ljust(20), end=' ')
298 |                 print(("attck_id: %s" % i['attck_id']))
299 |                 print("".ljust(20), end=' ')
300 |                 print(("attck_id_wiki: %s\n" % i['attck_id_wiki']))
301 | 
302 |             rc = (hatext)
303 |             if (rc == 0):
304 |                 final = 'Not Found'
305 |             printr()
306 |             return final
307 | 
308 |         except ValueError as e:
309 |             print(e)
310 |             if (cv.bkg == 1):
311 |                 print((mycolors.foreground.lightred + "Error while connecting to Hybrid-Analysis!\n"))
312 |             else:
313 |                 print((mycolors.foreground.red + "Error while connecting to Hybrid-Analysis!\n"))
314 |             printr()
315 | 
316 |     def hafilecheck(self, filenameha, xx=0, user_agent='Falcon Sandbox'):
317 |         if not os.path.isfile(filenameha):
318 |             if (cv.bkg == 1):
319 |                 print(mycolors.foreground.lightred + "\nYou didn't provide a valid file!\n")
320 |             else:
321 |                 print(mycolors.foreground.red + "\nYou didn't provide a valid file!\n")
322 |             return False
323 | 
324 |         haurl = HybridAnalysisExtractor.haurl
325 | 
326 |         hatext = ''
327 |         haresponse = ''
328 |         resource = ''
329 |         haenv = '100'
330 |         job_id = ''
331 | 
332 |         self.requestHAAPI()
333 | 
334 |         try:
335 |             if (xx == 0):
336 |                 haenv = '100'
337 |             elif (xx == 1):
338 |                 haenv = '110'
339 |             elif (xx == 2):
340 |                 haenv = '120'
341 |             elif (xx == 3):
342 |                 haenv = '200'
343 |             else:
344 |                 haenv = '300'
345 | 
346 |             resource = {'file': (os.path.basename(filenameha), open(filenameha, 'rb')), 'environment_id': (None, haenv)}
347 | 
348 |             mysha256hash = sha256hash(filenameha)
349 | 
350 |             if (cv.bkg == 1):
351 |                 print((mycolors.foreground.lightcyan + "\nSubmitted file: %s".ljust(20) % filenameha))
352 |                 print(("Submitted hash: %s".ljust(20) % mysha256hash))
353 |                 print(("Environment ID: %3s" % haenv))
354 |                 print((Fore.WHITE))
355 |             else:
356 |                 print((mycolors.foreground.purple + "\nSubmitted file: %s".ljust(20) % filenameha))
357 |                 print(("Submitted hash: %s".ljust(20) % mysha256hash))
358 |                 print(("Environment ID: %3s" % haenv))
359 |                 print((Fore.BLACK))
360 | 
361 |             requestsession = requests.Session()
362 |             requestsession.headers.update({'user-agent': user_agent})
363 |             requestsession.headers.update({'api-key': self.HAAPI})
364 |             requestsession.headers.update({'accept': 'application/json'})
365 | 
366 |             finalurl = '/'.join([haurl, 'submit', 'file'])
367 | 
368 |             haresponse = requestsession.post(url=finalurl, files=resource)
369 | 
370 |             hatext = json.loads(haresponse.text)
371 | 
372 |             rc = str(hatext)
373 | 
374 |             job_id = str(hatext['job_id'])
375 |             hash_received = str(hatext['sha256'])
376 |             environment_id = str(hatext['environment_id'])
377 | 
378 |             if (job_id) in rc:
379 |                 if (cv.bkg == 1):
380 |                     print((mycolors.foreground.yellow + "The suspicious file has been successfully submitted to Hybrid Analysis."))
381 |                     print(("\nThe job ID is: ").ljust(31), end=' ')
382 |                     print(("%s" % job_id))
383 |                     print(("The environment ID is: ").ljust(30), end=' ')
384 |                     print(("%s" % environment_id))
385 |                     print(("The received sha256 hash is: ").ljust(30), end=' ')
386 |                     print(("%s" % hash_received))
387 |                     print((mycolors.reset + "\n"))
388 |                 else:
389 |                     print((mycolors.foreground.green + "The suspicious file has been successfully submitted to Hybrid Analysis."))
390 |                     print(("\nThe job ID is: ").ljust(31), end=' ')
391 |                     print(("%s" % job_id))
392 |                     print(("The environment ID is: ").ljust(30), end=' ')
393 |                     print(("%s" % environment_id))
394 |                     print(("The received sha256 hash is: ").ljust(30), end=' ')
395 |                     print(("%s" % hash_received))
396 |                     print((mycolors.reset + "\n"))
397 |             else:
398 |                 if (cv.bkg == 1):
399 |                     print((mycolors.foreground.lightred + "\nAn error occured while sending the file!"))
400 |                     print((mycolors.reset + "\n"))
401 |                 else:
402 |                     print((mycolors.foreground.red + "\nAn error occured while sending the file!"))
403 |                     print((mycolors.reset + "\n"))
404 |         except ValueError as e:
405 |             print(e)
406 |             if (cv.bkg == 1):
407 |                 print((mycolors.foreground.lightred + "Error while connecting to Hybrid-Analysis!\n"))
408 |             else:
409 |                 print((mycolors.foreground.red + "Error while connecting to Hybrid-Analysis!\n"))
410 |             print((mycolors.reset))
411 | 
412 |     def quickhashow(self, filehash, xx=0, user_agent='Falcon Sandbox'):
413 |         haurl = HybridAnalysisExtractor.haurl
414 | 
415 |         hatext = ''
416 |         haresponse = ''
417 |         final = 'Yes'
418 |         verdict = '-'
419 |         avdetect = '0'
420 |         totalsignatures = '-'
421 |         threatscore = '-'
422 |         totalprocesses = '-'
423 |         networkconnections = '-'
424 | 
425 |         self.requestHAAPI()
426 | 
427 |         try:
428 | 
429 |             resource = filehash
430 |             requestsession = requests.Session()
431 |             requestsession.headers.update({'user-agent': user_agent})
432 |             requestsession.headers.update({'api-key': self.HAAPI})
433 |             requestsession.headers.update({'content-type': 'application/json'})
434 | 
435 |             if (xx == 0):
436 |                 finalurl = '/'.join([haurl, 'report', resource + ':100', 'summary'])
437 |             elif (xx == 1):
438 |                 finalurl = '/'.join([haurl, 'report', resource + ':110', 'summary'])
439 |             elif (xx == 2):
440 |                 finalurl = '/'.join([haurl, 'report', resource + ':120', 'summary'])
441 |             elif (xx == 3):
442 |                 finalurl = '/'.join([haurl, 'report', resource + ':200', 'summary'])
443 |             else:
444 |                 finalurl = '/'.join([haurl, 'report', resource + ':300', 'summary'])
445 | 
446 |             haresponse = requestsession.get(url=finalurl)
447 |             hatext = json.loads(haresponse.text)
448 | 
449 |             rc = str(hatext)
450 |             if 'message' in rc:
451 |                 final = 'Not Found'
452 |                 return (final, verdict, avdetect, totalsignatures, threatscore, totalprocesses, networkconnections)
453 | 
454 |             rc2 = (hatext)
455 |             if (rc2 == 0):
456 |                 final = 'Not Found'
457 |                 return (final, verdict, avdetect, totalsignatures, threatscore, totalprocesses, networkconnections)
458 | 
459 |             if 'verdict' in hatext:
460 |                 verdict = str(hatext['verdict'])
461 |             else:
462 |                 verdict = ''
463 | 
464 |             if 'threat_score' in hatext:
465 |                 threatscore = str(hatext['threat_score'])
466 |             else:
467 |                 threatscore = ''
468 | 
469 |             if 'av_detect' in hatext:
470 |                 avdetect = str(hatext['av_detect'])
471 |             else:
472 |                 avdetect = ''
473 | 
474 |             if 'total_signatures' in hatext:
475 |                 totalsignatures = str(hatext['total_signatures'])
476 |             else:
477 |                 totalsignatures = ''
478 | 
479 |             if 'total_processes' in hatext:
480 |                 totalprocesses = str(hatext['total_processes'])
481 |             else:
482 |                 totalprocesses = ''
483 | 
484 |             if 'total_network_connections' in hatext:
485 |                 networkconnections = str(hatext['total_network_connections'])
486 |             else:
487 |                 networkconnections = ''
488 | 
489 |             return (final, verdict, avdetect, totalsignatures, threatscore, totalprocesses, networkconnections)
490 | 
491 |         except ValueError as e:
492 |             print(e)
493 |             if (cv.bkg == 1):
494 |                 print((mycolors.foreground.lightred + "Error while connecting to Hybrid-Analysis!\n"))
495 |             else:
496 |                 print((mycolors.foreground.red + "Error while connecting to Hybrid-Analysis!\n"))
497 |             printr()
498 | 
499 | 
500 | """
501 | class quickHAThread(threading.Thread):
502 |     def __init__(self, key):
503 |         threading.Thread.__init__(self)
504 |         self.key = key
505 | 
506 |     def run(self):
507 |         key1 = self.key
508 | 
509 |         myhashdir = sha256hash(key1)
510 |         (final, verdict, avdetect, totalsignatures, threatscore, totalprocesses, networkconnections) =  self.hybrid.quickhashow(myhashdir)
511 | 
512 |         if (cv.bkg == 1):
513 |             print((mycolors.foreground.yellow + "%-70s" % key1), end=' ')
514 |             print((mycolors.foreground.lightcyan + "%9s" % final), end='')
515 |             print((mycolors.foreground.lightred + "%11s" % verdict), end='')
516 |             if(avdetect == 'None'):
517 |                 print((mycolors.foreground.pink + "%7s" % avdetect), end='')
518 |             else:
519 |                 print((mycolors.foreground.pink + "%6s%%" % avdetect), end='')
520 |             print((mycolors.foreground.yellow + "%7s" % totalsignatures), end='')
521 |             if(threatscore == 'None'):
522 |                 print((mycolors.foreground.lightred + "%12s" % threatscore), end='')
523 |             else:
524 |                 print((mycolors.foreground.lightred + "%8s/100" % threatscore), end='')
525 |             print((mycolors.foreground.lightcyan + "%6s" % totalprocesses), end='')
526 |             print((mycolors.foreground.lightcyan + "%6s" % networkconnections + mycolors.reset))
527 |         else:
528 |             print((mycolors.foreground.cyan + "%-70s" % key1), end=' ')
529 |             print((mycolors.foreground.cyan + "%9s" % final), end='')
530 |             print((mycolors.foreground.red + "%11s" % verdict), end='')
531 |             if (avdetect == 'None'):
532 |                 print((mycolors.foreground.purple + "%7s" % avdetect), end='')
533 |             else:
534 |                 print((mycolors.foreground.purple + "%6s%%" % avdetect), end='')
535 |             print((mycolors.foreground.green + "%7s" % totalsignatures), end='')
536 |             if(threatscore == 'None'):
537 |                 print((mycolors.foreground.red + "%12s" % threatscore), end='')
538 |             else:
539 |                 print((mycolors.foreground.red + "%8s/100" % threatscore), end='')
540 |             print((mycolors.foreground.blue + "%6s" % totalprocesses), end='')
541 |             print((mycolors.foreground.blue + "%6s" % networkconnections + mycolors.reset))
542 | """
543 | 


--------------------------------------------------------------------------------
/malwoverview/modules/ipinfo.py:
--------------------------------------------------------------------------------
 1 | import malwoverview.modules.configvars as cv
 2 | from malwoverview.utils.colors import mycolors, printc
 3 | import requests
 4 | 
 5 | class IPInfoExtractor:
 6 |     def __init__(self, IPINFOAPI):
 7 |         self.IPINFOAPI = IPINFOAPI
 8 |         
 9 |     """
10 |     IPInfo API can be used anonymously up to 1000 requests per day
11 |     def requestIPINFOAPI(self):
12 |             if self.IPINFOAPI == '':
13 |                 print(mycolors.foreground.red + "\nTo use IPInfo.io services, you must create the .malwapi.conf file under your user home directory (on Linux is $HOME\\.malwapi.conf and on Windows is in C:\\Users\\[username]\\.malwapi.conf) and insert the IPInfo API key according to the format shown on the Github website." + mycolors.reset + "\n")
14 |                 exit(1)
15 |     """
16 | 
17 |     def _raw_ip_info(self, ip_address):
18 |         url = f"https://ipinfo.io/{ip_address}?token={self.IPINFOAPI}"
19 |         
20 |         try:
21 |             response = requests.get(url)
22 |             return response.json()
23 |         except Exception as e:
24 |             return {'error': e}
25 | 
26 |     def get_ip_details(self, ip_address):
27 | #        self.requestIPINFOAPI()
28 |         
29 |         data = self._raw_ip_info(ip_address)
30 | 
31 |         try:
32 |             print()
33 |             print((mycolors.reset + "IPINFO.IO REPORT".center(100)), end='')
34 |             print((mycolors.reset + "".center(28)), end='')
35 |             print("\n" + (100 * '-').center(50))
36 |             
37 |             if 'error' in data:
38 |                 printc(f"\n{data['error']['message']}\n", mycolors.foreground.error(cv.bkg))
39 |                 return
40 |                 
41 |             fields = ['ip', 'hostname', 'org', 'country', 'region', 'city', 'loc', 'postal', 'timezone']
42 | 
43 |             COLSIZE = max(len(field) for field in fields) + 3
44 |             
45 |             for field in fields:
46 |                 if field in data:
47 |                     print(mycolors.foreground.info(cv.bkg) + f"{field.title()}: ".ljust(COLSIZE) + mycolors.reset + str(data[field]))
48 | 
49 |         except Exception as e:
50 |             printc(f"\nError: {str(e)}\n", mycolors.foreground.error(cv.bkg))
51 | 


--------------------------------------------------------------------------------
/malwoverview/modules/malpedia.py:
--------------------------------------------------------------------------------
  1 | import malwoverview.modules.configvars as cv
  2 | from malwoverview.utils.colors import mycolors, printr
  3 | import requests
  4 | import textwrap
  5 | import base64
  6 | import json
  7 | import os
  8 | 
  9 | class MalpediaExtractor():
 10 |     malpediaurl = 'https://malpedia.caad.fkie.fraunhofer.de/api'
 11 | 
 12 |     def __init__(self, MALPEDIAAPI):
 13 |         self.MALPEDIAAPI = MALPEDIAAPI
 14 | 
 15 |     def requestMALPEDIAAPI(self):
 16 |         if (self.MALPEDIAAPI == ''):
 17 |             print(mycolors.foreground.red + "\nTo be able to get information from Malpedia, you must create the .malwapi.conf file under your user home directory (on Linux is $HOME\\.malwapi.conf and on Windows is in C:\\Users\\[username]\\.malwapi.conf) and insert the Malpedia API according to the format shown on the Github website." + mycolors.reset + "\n")
 18 |             exit(1)
 19 | 
 20 |     def malpedia_actors(self):
 21 |         urlx = MalpediaExtractor.malpediaurl
 22 | 
 23 |         hatext = ''
 24 |         haresponse = ''
 25 | 
 26 |         self.requestMALPEDIAAPI()
 27 | 
 28 |         try:
 29 | 
 30 |             resource = urlx
 31 |             requestsession = requests.Session()
 32 |             requestsession.headers.update({'Content-Type': 'application/json'})
 33 |             requestsession.headers.update({'Authorization': 'apitoken ' + self.MALPEDIAAPI})
 34 |             finalurl = '/'.join([resource, 'list', 'actors'])
 35 |             haresponse = requestsession.get(url=finalurl)
 36 |             hatext = json.loads(haresponse.text)
 37 | 
 38 |             if ('200' not in str(haresponse)):
 39 |                 print(mycolors.foreground.red + "\nThe search key couldn't be found on Malpedia.\n", mycolors.reset)
 40 |                 exit(1)
 41 | 
 42 |             if (cv.bkg == 1):
 43 |                 print(mycolors.foreground.lightcyan + "\nActors:".ljust(13), end='\n'.ljust(11))
 44 |                 j = 1
 45 |                 for i in hatext:
 46 |                     if (j < 10):
 47 |                         print(mycolors.foreground.lightred + "Actor_%s:    " % j + mycolors.reset + str(i), end='\n'.ljust(11))
 48 |                     if ((j > 9) and (j < 100)):
 49 |                         print(mycolors.foreground.lightred + "Actor_%s:   " % j + mycolors.reset + str(i), end='\n'.ljust(11))
 50 |                     if (j > 99):
 51 |                         print(mycolors.foreground.lightred + "Actor_%s:  " % j + mycolors.reset + str(i), end='\n'.ljust(11))
 52 |                     j = j + 1
 53 | 
 54 |             if (cv.bkg == 0):
 55 |                 print(mycolors.foreground.green + "\nActors:".ljust(13), end='\n'.ljust(11))
 56 |                 j = 1
 57 |                 for i in hatext:
 58 |                     if (j < 10):
 59 |                         print(mycolors.foreground.red + "Actor_%s:    " % j + mycolors.reset + str(i), end='\n'.ljust(11))
 60 |                     if ((j > 9) and (j < 100)):
 61 |                         print(mycolors.foreground.red + "Actor_%s:   " % j + mycolors.reset + str(i), end='\n'.ljust(11))
 62 |                     if (j > 99):
 63 |                         print(mycolors.foreground.red + "Actor_%s:  " % j + mycolors.reset + str(i), end='\n'.ljust(11))
 64 |                     j = j + 1
 65 | 
 66 |         except ValueError as e:
 67 |             print(e)
 68 |             if (cv.bkg == 1):
 69 |                 print((mycolors.foreground.lightred + "Error while connecting to Malpedia!\n"))
 70 |             else:
 71 |                 print((mycolors.foreground.red + "Error while connecting to Malpedia!\n"))
 72 |             printr()
 73 | 
 74 |     def malpedia_payloads(self):
 75 |         urlx = MalpediaExtractor.malpediaurl
 76 | 
 77 |         hatext = ''
 78 |         haresponse = ''
 79 | 
 80 |         self.requestMALPEDIAAPI()
 81 | 
 82 |         try:
 83 | 
 84 |             resource = urlx
 85 |             requestsession = requests.Session()
 86 |             requestsession.headers.update({'Content-Type': 'application/json'})
 87 |             requestsession.headers.update({'Authorization': 'apitoken ' + self.MALPEDIAAPI})
 88 |             finalurl = '/'.join([resource, 'list', 'samples'])
 89 |             haresponse = requestsession.get(url=finalurl)
 90 |             hatext = json.loads(haresponse.text)
 91 | 
 92 |             if ('200' not in str(haresponse)):
 93 |                 print(mycolors.foreground.red + "\nThe search key couldn't be found on Malpedia.\n", mycolors.reset)
 94 |                 exit(1)
 95 | 
 96 |             if (cv.bkg == 1):
 97 |                 for key, value in hatext.items():
 98 |                     print(mycolors.foreground.yellow + "Family:".ljust(11) + mycolors.reset + key, end=' ')
 99 |                     for i in value:
100 |                         for j in i.items():
101 |                             for k in i.keys():
102 |                                 if (k == 'status'):
103 |                                     if (i['status']):
104 |                                         print(mycolors.foreground.lightcyan + "\n\nStatus:".ljust(13) + mycolors.reset + str(i['status']), end='')
105 |                                 if (k == 'sha256'):
106 |                                     if (i['sha256']):
107 |                                         print(mycolors.foreground.lightcyan + "\nHash:".ljust(12) + mycolors.reset + str(i['sha256']), end='')
108 |                                 if (k == 'version'):
109 |                                     if (i['version']):
110 |                                         print(mycolors.foreground.lightcyan + "\nVersion:".ljust(12) + mycolors.reset + str(i['version']), end=' ')
111 |                     print("\n" + '-' * 75)
112 | 
113 |             if (cv.bkg == 0):
114 |                 for key, value in hatext.items():
115 |                     print(mycolors.foreground.red + "Family:".ljust(11) + mycolors.reset + key, end=' ')
116 |                     for i in value:
117 |                         for j in i.items():
118 |                             for k in i.keys():
119 |                                 if (k == 'status'):
120 |                                     if (i['status']):
121 |                                         print(mycolors.foreground.green + "\n\nStatus:".ljust(13) + mycolors.reset + str(i['status']), end='')
122 |                                 if (k == 'sha256'):
123 |                                     if (i['sha256']):
124 |                                         print(mycolors.foreground.green + "\nHash:".ljust(12) + mycolors.reset + str(i['sha256']), end='')
125 |                                 if (k == 'version'):
126 |                                     if (i['version']):
127 |                                         print(mycolors.foreground.green + "\nVersion:".ljust(12) + mycolors.reset + str(i['version']), end=' ')
128 |                     print("\n" + '-' * 75)
129 |         except ValueError as e:
130 |             print(e)
131 |             if (cv.bkg == 1):
132 |                 print((mycolors.foreground.lightred + "Error while connecting to Malpedia!\n"))
133 |             else:
134 |                 print((mycolors.foreground.red + "Error while connecting to Malpedia!\n"))
135 |             printr()
136 | 
137 |     def malpedia_get_actor(self, arg1):
138 |         urlx = MalpediaExtractor.malpediaurl
139 | 
140 |         hatext = ''
141 |         haresponse = ''
142 |         myargs = arg1
143 |         wrapper = textwrap.TextWrapper(width=100)
144 | 
145 |         self.requestMALPEDIAAPI()
146 | 
147 |         try:
148 |             resource = urlx
149 |             requestsession = requests.Session()
150 |             requestsession.headers.update({'Content-Type': 'application/json'})
151 |             requestsession.headers.update({'Authorization': 'apitoken ' + self.MALPEDIAAPI})
152 |             finalurl = '/'.join([resource, 'get', 'actor', myargs])
153 |             haresponse = requestsession.get(url=finalurl)
154 |             hatext = json.loads(haresponse.text)
155 | 
156 |             if (cv.bkg == 1):
157 |                 if ('Not found.' in str(hatext)):
158 |                     print(mycolors.foreground.yellow + "\nInformation about this actor couldn't be found on Malpedia.\n", mycolors.reset)
159 |                     exit(1)
160 | 
161 |             if (cv.bkg == 0):
162 |                 if ('Not found.' in str(hatext)):
163 |                     print(mycolors.foreground.cyan + "\nInformation about this actor couldn't be found on Malpedia.\n", mycolors.reset)
164 |                     exit(1)
165 | 
166 |             if ('200' not in str(haresponse)):
167 |                 print(mycolors.foreground.red + "\nThe search key couldn't be found on Malpedia.\n", mycolors.reset)
168 |                 exit(1)
169 | 
170 |             if (cv.bkg == 1):
171 |                 if (hatext['value']):
172 |                     print(mycolors.foreground.yellow + "\nActor:".ljust(11) + mycolors.reset + hatext['value'], end=' ')
173 |                 if (hatext['description']):
174 |                     print(mycolors.foreground.yellow + "\n\nOverview: ".ljust(11) + mycolors.reset + ("\n".ljust(11)).join(textwrap.wrap(str(hatext['description']), width=100)), end=' ')
175 |                 for key, value in hatext.items():
176 |                     if (key == 'meta'):
177 |                         for key2, value2 in value.items():
178 |                             if (key2 == 'country'):
179 |                                 if (value['country']):
180 |                                     print(mycolors.foreground.yellow + "\n\nCountry:".ljust(12) + mycolors.reset + str(value['country']), end='\n')
181 |                             if (key2 == 'synonyms'):
182 |                                 if (value['synonyms']):
183 |                                     print(mycolors.foreground.lightcyan + "\n\nSynonyms:".ljust(11), end=' ')
184 |                                     for x in value['synonyms']:
185 |                                         print(mycolors.reset + str(x), end=' ')
186 |                             if (key2 == 'refs'):
187 |                                 if (value['refs']):
188 |                                     for x in value['refs']:
189 |                                         print(mycolors.foreground.lightcyan + "\nREFs:".ljust(11) + mycolors.reset + ("\n".ljust(11)).join(wrapper.wrap(str(x))).ljust(11), end=" ")
190 |                     if (key == 'families'):
191 |                         for key3, value3 in value.items():
192 |                             print("\n" + '-' * 112, end='')
193 |                             print(mycolors.foreground.yellow + "\nFamily: ".ljust(11) + mycolors.reset + key3)
194 |                             if 'updated' in value3.keys():
195 |                                 if (value3['updated']):
196 |                                     print(mycolors.foreground.lightcyan + "Updated: ".ljust(10) + mycolors.reset + value3['updated'])
197 |                             if 'attribution' in value3.keys():
198 |                                 if (len(value3['attribution']) > 0):
199 |                                     print(mycolors.foreground.lightcyan + "Attrib.: ".ljust(9), end=' ')
200 |                                     for y in value3['attribution']:
201 |                                         print(mycolors.reset + y, end=' ')
202 |                             if 'alt_names' in value3.keys():
203 |                                 if (len(value3['alt_names']) > 0):
204 |                                     print(mycolors.foreground.lightcyan + "\nAliases: ".ljust(10), end=' ')
205 |                                     for y in value3['alt_names']:
206 |                                         print(mycolors.reset + y, end=' ')
207 |                             if 'common_name' in value3.keys():
208 |                                 if (value3['common_name']):
209 |                                     print(mycolors.foreground.lightcyan + "\nCommon: ".ljust(11) + mycolors.reset + value3['common_name'], end=' ')
210 |                             if 'sources' in value3.keys():
211 |                                 if (len(value3['sources']) > 0):
212 |                                     print(mycolors.foreground.lightcyan + "\nSources: ".ljust(11), end=' ')
213 |                                     for y in value3['sources']:
214 |                                         print(mycolors.reset + y, end=' ')
215 |                             if 'description' in value3.keys():
216 |                                 if value3['description']:
217 |                                     print(mycolors.foreground.lightcyan + "\nDescr.: ".ljust(11) + mycolors.reset + ("\n".ljust(11)).join(textwrap.wrap(str(value3['description']), width=100)), end=' ')
218 |                             if 'urls' in value3.keys():
219 |                                 if (len(value3['urls']) > 0):
220 |                                     for y in value3['urls']:
221 |                                         print(mycolors.foreground.lightcyan + "\nURLs: ".ljust(11) + mycolors.reset + ("\n".ljust(11)).join(wrapper.wrap(str(y))).ljust(11), end=" ")
222 | 
223 |             if (cv.bkg == 0):
224 |                 if (hatext['value']):
225 |                     print(mycolors.foreground.red + "\nActor:".ljust(11) + mycolors.reset + hatext['value'], end=' ')
226 |                 if (hatext['description']):
227 |                     print(mycolors.foreground.red + "\n\nOverview: ".ljust(11) + mycolors.reset + ("\n".ljust(11)).join(textwrap.wrap(str(hatext['description']), width=100)), end=' ')
228 |                 for key, value in hatext.items():
229 |                     if (key == 'meta'):
230 |                         for key2, value2 in value.items():
231 |                             if (key2 == 'country'):
232 |                                 if (value['country']):
233 |                                     print(mycolors.foreground.red + "\n\nCountry:".ljust(12) + mycolors.reset + str(value['country']), end='\n')
234 |                             if (key2 == 'synonyms'):
235 |                                 if (value['synonyms']):
236 |                                     print(mycolors.foreground.green + "\n\nSynonyms:".ljust(11), end=' ')
237 |                                     for x in value['synonyms']:
238 |                                         print(mycolors.reset + str(x), end=' ')
239 |                             if (key2 == 'refs'):
240 |                                 if (value['refs']):
241 |                                     for x in value['refs']:
242 |                                         print(mycolors.foreground.green + "\nREFs:".ljust(11) + mycolors.reset + ("\n".ljust(11)).join(wrapper.wrap(str(x))).ljust(11), end=" ")
243 |                     if (key == 'families'):
244 |                         for key3, value3 in value.items():
245 |                             print("\n" + '-' * 112, end='')
246 |                             print(mycolors.foreground.red + "\nFamily: ".ljust(11) + mycolors.reset + key3)
247 |                             if 'updated' in value3.keys():
248 |                                 if (value3['updated']):
249 |                                     print(mycolors.foreground.green + "Updated: ".ljust(10) + mycolors.reset + value3['updated'])
250 |                             if 'attribution' in value3.keys():
251 |                                 if (len(value3['attribution']) > 0):
252 |                                     print(mycolors.foreground.green + "Attrib.: ".ljust(9), end=' ')
253 |                                     for y in value3['attribution']:
254 |                                         print(mycolors.reset + y, end=' ')
255 |                             if 'alt_names' in value3.keys():
256 |                                 if (len(value3['alt_names']) > 0):
257 |                                     print(mycolors.foreground.green + "\nAliases: ".ljust(10), end=' ')
258 |                                     for y in value3['alt_names']:
259 |                                         print(mycolors.reset + y, end=' ')
260 |                             if 'common_name' in value3.keys():
261 |                                 if (value3['common_name']):
262 |                                     print(mycolors.foreground.green + "\nCommon: ".ljust(11) + mycolors.reset + value3['common_name'], end=' ')
263 |                             if 'sources' in value3.keys():
264 |                                 if (len(value3['sources']) > 0):
265 |                                     print(mycolors.foreground.green + "\nSources: ".ljust(11), end=' ')
266 |                                     for y in value3['sources']:
267 |                                         print(mycolors.reset + y, end=' ')
268 |                             if 'description' in value3.keys():
269 |                                 if value3['description']:
270 |                                     print(mycolors.foreground.green + "\nDescr.: ".ljust(11) + mycolors.reset + ("\n".ljust(11)).join(textwrap.wrap(str(value3['description']), width=100)), end=' ')
271 |                             if 'urls' in value3.keys():
272 |                                 if (len(value3['urls']) > 0):
273 |                                     for y in value3['urls']:
274 |                                         print(mycolors.foreground.green + "\nURLs: ".ljust(11) + mycolors.reset + ("\n".ljust(11)).join(wrapper.wrap(str(y))).ljust(11), end=" ")
275 | 
276 |         except ValueError as e:
277 |             print(e)
278 |             if (cv.bkg == 1):
279 |                 print((mycolors.foreground.lightred + "Error while connecting to Malpedia!\n"))
280 |             else:
281 |                 print((mycolors.foreground.red + "Error while connecting to Malpedia!\n"))
282 |             printr()
283 | 
284 |     def malpedia_families(self):
285 |         urlx = MalpediaExtractor.malpediaurl
286 | 
287 |         hatext = ''
288 |         haresponse = ''
289 |         # wrapper = textwrap.TextWrapper(width=100)
290 | 
291 |         self.requestMALPEDIAAPI()
292 | 
293 |         try:
294 |             resource = urlx
295 |             requestsession = requests.Session()
296 |             requestsession.headers.update({'Content-Type': 'application/json'})
297 |             requestsession.headers.update({'Authorization': 'apitoken ' + self.MALPEDIAAPI})
298 |             finalurl = '/'.join([resource, 'list', 'families'])
299 |             haresponse = requestsession.get(url=finalurl)
300 |             hatext = json.loads(haresponse.text)
301 | 
302 |             if ('200' not in str(haresponse)):
303 |                 print(mycolors.foreground.red + "\nThe search key couldn't be found on Malpedia.\n", mycolors.reset)
304 |                 exit(1)
305 | 
306 |             if (cv.bkg == 1):
307 |                 print(mycolors.foreground.yellow + "\nFamilies:".ljust(13), end='\n'.ljust(11))
308 |                 j = 1
309 |                 for i in hatext:
310 |                     if (j < 10):
311 |                         print(mycolors.foreground.lightcyan + "Family_%s:     " % j + mycolors.reset + str(i), end='\n'.ljust(11))
312 |                     if ((j > 9) and (j < 100)):
313 |                         print(mycolors.foreground.lightcyan + "Family_%s:    " % j + mycolors.reset + str(i), end='\n'.ljust(11))
314 |                     if ((j > 99) and (j < 1000)):
315 |                         print(mycolors.foreground.lightcyan + "Family_%s:   " % j + mycolors.reset + str(i), end='\n'.ljust(11))
316 |                     if (j > 999):
317 |                         print(mycolors.foreground.lightcyan + "Family_%s:  " % j + mycolors.reset + str(i), end='\n'.ljust(11))
318 |                     j = j + 1
319 | 
320 |             if (cv.bkg == 0):
321 |                 print(mycolors.foreground.red + "\nFamilies:".ljust(13), end='\n'.ljust(11))
322 |                 j = 1
323 |                 for i in hatext:
324 |                     if (j < 10):
325 |                         print(mycolors.foreground.cyan + "Family_%s:     " % j + mycolors.reset + str(i), end='\n'.ljust(11))
326 |                     if ((j > 9) and (j < 100)):
327 |                         print(mycolors.foreground.cyan + "Family_%s:    " % j + mycolors.reset + str(i), end='\n'.ljust(11))
328 |                     if ((j > 99) and (j < 1000)):
329 |                         print(mycolors.foreground.cyan + "Family_%s:   " % j + mycolors.reset + str(i), end='\n'.ljust(11))
330 |                     if (j > 999):
331 |                         print(mycolors.foreground.cyan + "Family_%s:  " % j + mycolors.reset + str(i), end='\n'.ljust(11))
332 |                     j = j + 1
333 | 
334 |         except ValueError as e:
335 |             print(e)
336 |             if (cv.bkg == 1):
337 |                 print((mycolors.foreground.lightred + "Error while connecting to Malpedia!\n"))
338 |             else:
339 |                 print((mycolors.foreground.red + "Error while connecting to Malpedia!\n"))
340 |             printr()
341 | 
342 |     def malpedia_get_family(self, arg1):
343 |         urlx = MalpediaExtractor.malpediaurl
344 | 
345 |         hatext = ''
346 |         haresponse = ''
347 |         myargs = arg1
348 |         # wrapper = textwrap.TextWrapper(width=100)
349 | 
350 |         self.requestMALPEDIAAPI()
351 | 
352 |         try:
353 | 
354 |             resource = urlx
355 |             requestsession = requests.Session()
356 |             requestsession.headers.update({'Content-Type': 'application/json'})
357 |             requestsession.headers.update({'Authorization': 'apitoken ' + self.MALPEDIAAPI})
358 |             finalurl = '/'.join([resource, 'get', 'family', myargs])
359 |             haresponse = requestsession.get(url=finalurl)
360 |             hatext = json.loads(haresponse.text)
361 | 
362 |             if (cv.bkg == 1):
363 |                 if ('Not found.' in str(hatext)):
364 |                     print(mycolors.foreground.yellow + "\nInformation about this family couldn't be found on Malpedia.\n", mycolors.reset)
365 |                     exit(1)
366 | 
367 |             if (cv.bkg == 0):
368 |                 if ('Not found.' in str(hatext)):
369 |                     print(mycolors.foreground.cyan + "\nInformation about this family couldn't be found on Malpedia.\n", mycolors.reset)
370 |                     exit(1)
371 | 
372 |             if ('200' not in str(haresponse)):
373 |                 print(mycolors.foreground.red + "\nThe search key couldn't be found on Malpedia.\n", mycolors.reset)
374 |                 exit(1)
375 | 
376 |             if (cv.bkg == 1):
377 |                 print(mycolors.foreground.lightcyan + "\nFamily:".ljust(14) + mycolors.reset + myargs)
378 |                 print(mycolors.foreground.yellow + "\nUpdated:".ljust(14) + mycolors.reset + hatext['updated'], end=' ')
379 |                 if (hatext['attribution']):
380 |                     print(mycolors.foreground.yellow + "\nAttribution:".ljust(13), end=' ')
381 |                     for i in hatext['attribution']:
382 |                         print(mycolors.reset + str(i), end=' ')
383 |                 if (hatext['alt_names']):
384 |                     print(mycolors.foreground.yellow + "\nAliases:".ljust(13), end=' ')
385 |                     for i in hatext['alt_names']:
386 |                         print(mycolors.reset + i, end=' ')
387 |                 if (hatext['common_name']):
388 |                     print(mycolors.foreground.yellow + "\nCommon Name: ".ljust(13) + mycolors.reset + hatext['common_name'], end=' ')
389 |                 if (hatext['description']):
390 |                     print(mycolors.foreground.yellow + "\nDescription: ".ljust(13) + mycolors.reset + ("\n".ljust(14)).join(textwrap.wrap(hatext['description'], width=110)), end='\n')
391 | 
392 |                 if (hatext['urls']):
393 |                     j = 0
394 |                     for i in hatext['urls']:
395 |                         if (j < 10):
396 |                             print(mycolors.foreground.yellow + "\nURL_%d:".ljust(15) % j + mycolors.reset + ("\n".ljust(14)).join(textwrap.wrap(i, width=110)), end=' ')
397 |                         if (j > 9 and j < 100):
398 |                             print(mycolors.foreground.yellow + "\nURL_%d:".ljust(14) % j + mycolors.reset + ("\n".ljust(14)).join(textwrap.wrap(i, width=110)), end=' ')
399 |                         if (j > 99):
400 |                             print(mycolors.foreground.yellow + "\nURL_%d:".ljust(13) % j + mycolors.reset + ("\n".ljust(14)).join(textwrap.wrap(i, width=110)), end=' ')
401 |                         j = j + 1
402 | 
403 |             if (cv.bkg == 0):
404 |                 print(mycolors.foreground.purple + "\nFamily:".ljust(14) + mycolors.reset + myargs)
405 |                 print(mycolors.foreground.cyan + "\nUpdated:".ljust(14) + mycolors.reset + hatext['updated'], end=' ')
406 |                 if (hatext['attribution']):
407 |                     print(mycolors.foreground.cyan + "\nAttribution:".ljust(13), end=' ')
408 |                     for i in hatext['attribution']:
409 |                         print(mycolors.reset + str(i), end=' ')
410 |                 if (hatext['alt_names']):
411 |                     print(mycolors.foreground.cyan + "\nAliases:".ljust(13), end=' ')
412 |                     for i in hatext['alt_names']:
413 |                         print(mycolors.reset + i, end=' ')
414 |                 if (hatext['common_name']):
415 |                     print(mycolors.foreground.cyan + "\nCommon Name: ".ljust(13) + mycolors.reset + hatext['common_name'], end=' ')
416 |                 if (hatext['description']):
417 |                     print(mycolors.foreground.cyan + "\nDescription: ".ljust(13) + mycolors.reset + ("\n".ljust(14)).join(textwrap.wrap(hatext['description'], width=110)), end='\n')
418 | 
419 |                 if (hatext['urls']):
420 |                     j = 0
421 |                     for i in hatext['urls']:
422 |                         if (j < 10):
423 |                             print(mycolors.foreground.cyan + "\nURL_%d:".ljust(15) % j + mycolors.reset + ("\n".ljust(14)).join(textwrap.wrap(i, width=110)), end=' ')
424 |                         if (j > 9 and j < 100):
425 |                             print(mycolors.foreground.cyan + "\nURL_%d:".ljust(14) % j + mycolors.reset + ("\n".ljust(14)).join(textwrap.wrap(i, width=110)), end=' ')
426 |                         if (j > 99):
427 |                             print(mycolors.foreground.cyan + "\nURL_%d:".ljust(13) % j + mycolors.reset + ("\n".ljust(14)).join(textwrap.wrap(i, width=110)), end=' ')
428 |                         j = j + 1
429 | 
430 |         except ValueError as e:
431 |             print(e)
432 |             if (cv.bkg == 1):
433 |                 print((mycolors.foreground.lightred + "Error while connecting to Malpedia!\n"))
434 |             else:
435 |                 print((mycolors.foreground.red + "Error while connecting to Malpedia!\n"))
436 |             printr()
437 | 
438 |     def malpedia_get_sample(self, arg1):
439 |         if len(arg1) not in [32, 64]:
440 |             return False
441 | 
442 |         urlx = MalpediaExtractor.malpediaurl
443 | 
444 |         hatext = ''
445 |         haresponse = ''
446 |         myargs = arg1
447 | 
448 |         self.requestMALPEDIAAPI()
449 | 
450 |         try:
451 |             resource = urlx
452 |             requestsession = requests.Session()
453 |             requestsession.headers.update({'Content-Type': 'application/json'})
454 |             requestsession.headers.update({'Authorization': 'apitoken ' + self.MALPEDIAAPI})
455 |             finalurl = '/'.join([resource, 'get', 'sample', myargs, 'zip'])
456 |             haresponse = requestsession.get(url=finalurl)
457 |             hatext = json.loads(haresponse.text)
458 | 
459 |             if (cv.bkg == 1):
460 |                 if ('Not found.' in str(hatext)):
461 |                     print(mycolors.foreground.yellow + "\nThis sample couldn't be found on Malpedia.\n", mycolors.reset)
462 |                     exit(1)
463 | 
464 |             if (cv.bkg == 0):
465 |                 if ('Not found.' in str(hatext)):
466 |                     print(mycolors.foreground.cyan + "\nThis sample couldn't be found on Malpedia.\n", mycolors.reset)
467 |                     exit(1)
468 | 
469 |             if ('200' not in str(haresponse)):
470 |                 print(mycolors.foreground.red + "\nThe search key couldn't be found on Malpedia.\n", mycolors.reset)
471 |                 exit(1)
472 | 
473 |             if ('200' in str(haresponse)):
474 |                 outputpath = os.path.join(cv.output_dir, myargs + ".zip")
475 |                 open(outputpath, 'wb').write(base64.b64decode(hatext['zipped']))
476 |                 if (cv.bkg == 1):
477 |                     print(mycolors.foreground.lightcyan + f"\nSample downloaded to: {outputpath}\n", mycolors.reset)
478 |                 else:
479 |                     print(mycolors.foreground.green + f"\nSample downloaded to: {outputpath}\n", mycolors.reset)
480 |                 exit(0)
481 | 
482 |         except ValueError as e:
483 |             print(e)
484 |             if (cv.bkg == 1):
485 |                 print((mycolors.foreground.lightred + "Error while connecting to Malpedia!\n"))
486 |             else:
487 |                 print((mycolors.foreground.red + "Error while connecting to Malpedia!\n"))
488 |             printr()
489 | 
490 |     def malpedia_get_yara(self, arg1):
491 |         urlx = MalpediaExtractor.malpediaurl
492 | 
493 |         hatext = ''
494 |         haresponse = ''
495 |         myargs = arg1
496 | 
497 |         self.requestMALPEDIAAPI()
498 | 
499 |         try:
500 |             resource = urlx
501 |             requestsession = requests.Session()
502 |             requestsession.headers.update({'Content-Type': 'application/json'})
503 |             requestsession.headers.update({'Authorization': 'apitoken ' + self.MALPEDIAAPI})
504 |             finalurl = '/'.join([resource, 'get', 'yara', myargs, 'zip'])
505 |             haresponse = requestsession.get(url=finalurl)
506 | 
507 |             if (cv.bkg == 1):
508 |                 if ('Not found.' in str(hatext)):
509 |                     print(mycolors.foreground.yellow + "\nThe Yara rule for this family couldn't be found on Malpedia.\n", mycolors.reset)
510 |                     exit(1)
511 | 
512 |             if (cv.bkg == 0):
513 |                 if ('Not found.' in str(hatext)):
514 |                     print(mycolors.foreground.cyan + "\nThe Yara rule for this family couldn't be found on Malpedia.\n", mycolors.reset)
515 |                     exit(1)
516 | 
517 |             if ('200' not in str(haresponse)):
518 |                 print(mycolors.foreground.red + "\nThe search key couldn't be found on Malpedia.\n", mycolors.reset)
519 |                 exit(1)
520 | 
521 |             if ('200' in str(haresponse)):
522 |                 outputpath = os.path.join(cv.output_dir, myargs + ".zip")
523 |                 if (cv.bkg == 1):
524 |                     open(outputpath, 'wb').write(haresponse.content)
525 |                     print(mycolors.foreground.lightcyan + "\nA zip file named %s.zip containing Yara rules has been SUCCESSFULLY downloaded from Malpedia!\n" % myargs, mycolors.reset)
526 |                 else:
527 |                     open(outputpath, 'wb').write(haresponse.content)
528 |                     print(mycolors.foreground.green + "\nA zip file named %s.zip containing Yara rules has been SUCCESSFULLY downloaded from Malpedia!\n" % myargs, mycolors.reset)
529 |                     exit(0)
530 |         except ValueError as e:
531 |             print(e)
532 |             if (cv.bkg == 1):
533 |                 print((mycolors.foreground.lightred + "Error while connecting to Malpedia!\n"))
534 |             else:
535 |                 print((mycolors.foreground.red + "Error while connecting to Malpedia!\n"))
536 |             printr()
537 | 


--------------------------------------------------------------------------------
/malwoverview/modules/malshare.py:
--------------------------------------------------------------------------------
  1 | import malwoverview.modules.configvars as cv
  2 | from malwoverview.utils.colors import mycolors, printr
  3 | import json
  4 | import requests
  5 | import sys
  6 | import os
  7 | 
  8 | 
  9 | class MalshareExtractor():
 10 |     urlmalshare = 'https://malshare.com/api.php?api_key='
 11 | 
 12 |     def __init__(self, MALSHAREAPI):
 13 |         self.MALSHAREAPI = MALSHAREAPI
 14 | 
 15 |     def requestMALSHAREAPI(self):
 16 |         if (self.MALSHAREAPI == ''):
 17 |             print(mycolors.foreground.red + "\nTo be able to get/submit information from/to Malshare, you must create the .malwapi.conf file under your user home directory (on Linux is $HOME\\.malwapi.conf and on Windows is in C:\\Users\\[username]\\.malwapi.conf) and insert the Malshare API according to the format shown on the Github website." + mycolors.reset + "\n")
 18 |             exit(1)
 19 | 
 20 |     def malsharedown(self, filehash):
 21 |         if len(filehash) not in [32, 40, 64]:
 22 |             return False
 23 | 
 24 |         urlmalshare = MalshareExtractor.urlmalshare
 25 |         malresponse3 = ''
 26 |         resource = ''
 27 | 
 28 |         self.requestMALSHAREAPI()
 29 | 
 30 |         try:
 31 |             resource = filehash
 32 |             requestsession3 = requests.Session()
 33 |             finalurl3 = ''.join([
 34 |                 urlmalshare, self.MALSHAREAPI,
 35 |                 '&action=getfile&hash=', resource
 36 |             ])
 37 | 
 38 |             malresponse3 = requestsession3.get(
 39 |                 url=finalurl3,
 40 |                 allow_redirects=True
 41 |             )
 42 | 
 43 |             if (b'Sample not found by hash' in malresponse3.content):
 44 |                 if (cv.bkg == 1):
 45 |                     print((mycolors.foreground.lightred + "\nSample not found by the provided hash.\n"))
 46 |                 else:
 47 |                     print((mycolors.foreground.red + "\nSample not found by the provided hash.\n"))
 48 |                     print(mycolors.reset)
 49 |                     exit(1)
 50 |             else:
 51 |                 outputpath = os.path.join(cv.output_dir, resource)
 52 |                 open(outputpath, 'wb').write(malresponse3.content)
 53 | 
 54 |                 print("\n")
 55 |                 print((mycolors.reset + f"Sample downloaded to: {outputpath}"))
 56 |                 printr()
 57 |         except (BrokenPipeError, IOError):
 58 |             print(mycolors.reset, file=sys.stderr)
 59 |             exit(2)
 60 |         except ValueError as e:
 61 |             print(e)
 62 |             if (cv.bkg == 1):
 63 |                 print((mycolors.foreground.lightred + "Error while connecting to Malshare.com!\n"))
 64 |             else:
 65 |                 print((mycolors.foreground.red + "Error while connecting to Malshare.com!\n"))
 66 |             printr()
 67 | 
 68 |     def malsharelastlist(self, typex):
 69 |         urlmalshare = MalshareExtractor.urlmalshare
 70 |         maltext = ''
 71 |         malresponse = ''
 72 |         filetype = ''
 73 |         maltype = typex
 74 | 
 75 |         self.requestMALSHAREAPI()
 76 | 
 77 |         if (maltype == 2):
 78 |             filetype = 'PE32'
 79 |         elif (maltype == 3):
 80 |             filetype = 'ELF'
 81 |         elif (maltype == 4):
 82 |             filetype = 'Java'
 83 |         elif (maltype == 5 or maltype == 6):
 84 |             filetype = 'PDF'
 85 |         else:
 86 |             filetype = 'all'
 87 | 
 88 |         try:
 89 |             if (filetype != "all"):
 90 |                 print("\n")
 91 |                 print((mycolors.reset + "SHA256 hash".center(75)), end='')
 92 |                 print((mycolors.reset + "MD5 hash".center(38)), end='')
 93 |                 print((mycolors.reset + "File type".center(8)), end='')
 94 |                 print("\n" + (126 * '-').center(59))
 95 |                 print((mycolors.reset))
 96 | 
 97 |                 requestsession = requests.Session()
 98 |                 requestsession.headers.update({'accept': 'application/json'})
 99 |                 finalurl = ''.join([
100 |                     urlmalshare, self.MALSHAREAPI,
101 |                     '&action=type&type=', filetype
102 |                 ])
103 |                 malresponse = requestsession.get(url=finalurl)
104 |                 maltext = json.loads(malresponse.text)
105 |             else:
106 |                 print("\n")
107 |                 print((mycolors.reset + "SHA256 hash".center(75)), end='')
108 |                 print((mycolors.reset + "MD5 hash".center(38)), end='')
109 |                 print("\n" + (112 * '-').center(56))
110 |                 print((mycolors.reset))
111 | 
112 |                 requestsession = requests.Session( )
113 |                 requestsession.headers.update({'accept': 'application/json'})
114 |                 finalurl = ''.join([urlmalshare, self.MALSHAREAPI, '&action=getlist'])
115 |                 malresponse = requestsession.get(url=finalurl)
116 |                 maltext = json.loads(malresponse.text)
117 | 
118 |             if maltext:
119 |                 try:
120 |                     for i in range(0, len(maltext)):
121 |                         if (maltext[i].get('sha256')):
122 |                             if (cv.bkg == 1):
123 |                                 if filetype != "all":
124 |                                     print((mycolors.reset + "sha256: " + mycolors.foreground.yellow + "%s" % maltext[i]['sha256'] + mycolors.reset + "  md5: " + mycolors.foreground.lightcyan + "%s" % maltext[i]['md5'] + mycolors.reset + "  type: " + mycolors.foreground.lightred + "%s" % filetype))
125 |                                 else:
126 |                                     print((mycolors.reset + "sha256: " + mycolors.foreground.yellow + "%s" % maltext[i]['sha256'] + mycolors.reset + " md5: " + mycolors.foreground.lightcyan + "%s" % maltext[i]['md5'] + mycolors.reset))
127 |                             else:
128 |                                 if filetype != "all":
129 |                                     print((mycolors.reset + "sha256: " + mycolors.foreground.red + "%s" % maltext[i]['sha256'] + mycolors.reset + "  md5: " + mycolors.foreground.blue + "%s" % maltext[i]['md5'] + mycolors.reset + "   type: " + mycolors.foreground.purple + "%s" % filetype))
130 |                                 else:
131 |                                     print((mycolors.reset + "sha256: " + mycolors.foreground.red + "%s" % maltext[i]['sha256'] + mycolors.reset + " md5: " + mycolors.foreground.blue + "%s" % maltext[i]['md5'] + mycolors.reset))
132 |                 except KeyError:
133 |                     pass
134 |                 except (BrokenPipeError, IOError):
135 |                     print(mycolors.reset, file=sys.stderr)
136 |                     return False
137 |         except ValueError as e:
138 |             print(e)
139 |             if (cv.bkg == 1):
140 |                 print((mycolors.foreground.lightred + "Error while connecting to Malshare.com!\n"))
141 |             else:
142 |                 print((mycolors.foreground.red + "Error while connecting to Malshare.com!\n"))
143 |             printr()
144 |             return False
145 | 
146 |         return True
147 | 


--------------------------------------------------------------------------------
/malwoverview/modules/multipleip.py:
--------------------------------------------------------------------------------
  1 | from malwoverview.utils.colors import mycolors, printr, printc
  2 | import malwoverview.modules.configvars as cv
  3 | 
  4 | class MultipleIPExtractor:
  5 |     def __init__(self, extractors):
  6 |         self.extractors = extractors
  7 | 
  8 |     def get_multiple_ip_details(self, ip_address):
  9 |         if ip_address is None:
 10 |             printc("A valid IP address is required.", mycolors.foreground.error(cv.bkg))
 11 |             return
 12 | 
 13 |         for extractor in self.extractors:
 14 |             extractor_obj = self.extractors[extractor]
 15 |             if extractor == "IPInfo":
 16 |                 extractor_obj.get_ip_details(ip_address)
 17 |             elif extractor == "BGPView":
 18 |                 extractor_obj.get_ip_details(ip_address)
 19 |             elif extractor == "VirusTotal":
 20 |                 data = extractor_obj._raw_ip_info(ip_address)
 21 |                 self._get_info_virustotal(data.json())
 22 |             elif extractor == "AlienVault":
 23 |                 data = extractor_obj._raw_ip_info(ip_address)
 24 |                 self._get_info_alienvault(data.json())
 25 |             #elif extractor == "InQuest":
 26 |             #    data = extractor_obj._raw_ip_info(ip_address)
 27 |             #    self._get_info_inquest(data.json())
 28 |             # elif extractor == "PolySwarm":
 29 |             #     data = extractor_obj._raw_ip_info(ip_address)
 30 |             #     self._get_info_polyswarm(data)
 31 | 
 32 |     def _get_info_virustotal(self, data):
 33 |         try:
 34 |             attributes = data.get('data', {}).get('attributes', {})
 35 |     
 36 |             print()
 37 |             print((mycolors.reset + "VIRUSTOTAL IP REPORT".center(100)), end='')
 38 |             #print((mycolors.reset + "".center(28)), end='')
 39 |             print("\n" + (100 * '-').center(50))
 40 |     
 41 |             fields = {
 42 |                 'Reputation': attributes.get('reputation'),
 43 |                 'RIR': attributes.get('regional_internet_registry'),
 44 |                 'Network': attributes.get('network'),
 45 |                 'ASN': attributes.get('asn'),
 46 |                 'AS Owner': attributes.get('as_owner'),
 47 |                 'Country Code': attributes.get('country'),
 48 |                 'Continent': attributes.get('continent')
 49 |             }
 50 | 
 51 |             COLSIZE = max(len(field) for field in fields.keys()) + 3
 52 |     
 53 |             for field, value in fields.items():
 54 |                 print(mycolors.foreground.info(cv.bkg) + f"{field}:".ljust(COLSIZE) + "\t" + mycolors.reset + str(value))
 55 |     
 56 |             print("\nAnalysis Stats:")
 57 |             stats = attributes.get('last_analysis_stats', {})
 58 |             for stat, count in stats.items():
 59 |                 print(mycolors.foreground.error(cv.bkg) + f"{stat.title()}:".ljust(COLSIZE) + "\t" + mycolors.reset + str(count))
 60 |             
 61 |             print("\nCommunity Votes:")
 62 |             votes = attributes.get('total_votes', {})
 63 |             for vote, count in votes.items():
 64 |                 print(mycolors.foreground.error(cv.bkg) + f"{vote.title()}:".ljust(COLSIZE) + "\t" + mycolors.reset + str(count))
 65 |             
 66 |         except Exception as e:
 67 |             print(mycolors.foreground.error(cv.bkg) + f"\nError: {str(e)}\n" + mycolors.reset)
 68 | 
 69 |         print()
 70 |         print("(For the full VirusTotal report use the -v and -V options)")
 71 | 
 72 |     def _get_info_alienvault(self, data):
 73 |         try:
 74 |             print()
 75 |             print((mycolors.reset + "ALIENVAULT IP REPORT".center(100)), end='')
 76 |             # print((mycolors.reset + "".center(28)), end='')
 77 |             print("\n" + (100 * '-').center(50))
 78 |         
 79 |             COLSIZE = 13
 80 |         
 81 |             infocolor = mycolors.foreground.info(cv.bkg)
 82 |             print(infocolor + f"ASN:".ljust(COLSIZE) + "\t" + mycolors.reset + str(data.get('asn')))
 83 |             print(infocolor + f"Country:".ljust(COLSIZE) + "\t" + mycolors.reset + str(data.get('country_name')))
 84 |             print(infocolor + f"Region:".ljust(COLSIZE) + "\t" + mycolors.reset + str(data.get('region')))
 85 |             print(infocolor + f"City:".ljust(COLSIZE) + "\t" + mycolors.reset + str(data.get('city')))
 86 |             print(infocolor + f"Continent:".ljust(COLSIZE) + "\t" + mycolors.reset + str(data.get('continent_code')))
 87 |             print(infocolor + f"Latitude:".ljust(COLSIZE) + "\t" + mycolors.reset + str(data.get('latitude')))
 88 |             print(infocolor + f"Longitude:".ljust(COLSIZE) + "\t" + mycolors.reset + str(data.get('longitude')))
 89 |             print(infocolor + f"Sections:".ljust(COLSIZE) + "\t" + mycolors.reset + ', '.join(data.get('sections', [])))
 90 |             print(mycolors.foreground.error(cv.bkg) + f"Pulses Found:".ljust(COLSIZE) + "\t" + mycolors.reset + str(data.get('pulse_info', {}).get('count')))
 91 |                 
 92 |         except Exception as e:
 93 |             printc(f"\nError: {str(e)}\n", mycolors.foreground.error(cv.bkg))
 94 | 
 95 |         print()
 96 |         print("(For the full AlienVault report use the -n and -N options)")
 97 | 
 98 | """
 99 |     def _get_info_inquest(self, data):
100 |         try:
101 |             print("\n")
102 |             print((mycolors.reset + "INQUEST IP REPORT".center(100)), end='')
103 |             print((mycolors.reset + "".center(28)), end='')
104 |             print("\n" + (100 * '-').center(50))
105 | 
106 |             print(data)
107 | 
108 |         except Exception as e:
109 |             if (cv.bkg == 1):
110 |                 print(mycolors.foreground.lightred + f"\nError: {str(e)}\n" + mycolors.reset)
111 |             else:
112 |                 print(mycolors.foreground.red + f"\nError: {str(e)}\n" + mycolors.reset)
113 | 
114 |         print()
115 |         print("(For the full InQuest report use the -i and -I options)")
116 | 
117 |     def _get_info_polyswarm(self, data):
118 |         print("\n")
119 |         print((mycolors.reset + "POLYSWARM IP REPORT".center(100)), end='')
120 |         print((mycolors.reset + "".center(28)), end='')
121 |         print("\n" + (100 * '-').center(50))
122 | 
123 |         print(data)
124 | 
125 |         print()
126 |         print("(For the full PolySwarm report use the -p and -P options)")
127 | """


--------------------------------------------------------------------------------
/malwoverview/modules/polyswarm.py:
--------------------------------------------------------------------------------
  1 | import malwoverview.modules.configvars as cv
  2 | from polyswarm_api.api import PolyswarmAPI
  3 | from malwoverview.utils.colors import mycolors, printr
  4 | from malwoverview.utils.hash import sha256hash
  5 | from malwoverview.utils.peinfo import ftype
  6 | import pefile
  7 | from requests.exceptions import RetryError
  8 | import re
  9 | import os
 10 | 
 11 | 
 12 | class PolyswarmExtractor():
 13 |     def __init__(self, POLYAPI):
 14 |         self.POLYAPI = POLYAPI
 15 | 
 16 |     def requestPOLYAPI(self):
 17 |         if self.POLYAPI == '':
 18 |             print(mycolors.foreground.red + "\nTo be able to get/submit information from/to Polyswarm, you must create the .malwapi.conf file under your user home directory (on Linux is $HOME\\.malwapi.conf and on Windows is in C:\\Users\\[username]\\.malwapi.conf) and insert the Polyswarm API according to the format shown on the Github website." + mycolors.reset + "\n")
 19 |             exit(1)
 20 | 
 21 |     def polymetasearch(self, poly, metainfo):
 22 |         self.requestPOLYAPI()
 23 |         polyswarm = PolyswarmAPI(key=self.POLYAPI)
 24 | 
 25 |         if (metainfo == 4):
 26 |             targetfile = poly
 27 |             dname = str(os.path.dirname(targetfile))
 28 |             if not os.path.abspath(dname):
 29 |                 dname = os.path.abspath('.') + "/" + dname
 30 |             magictype = ftype(targetfile)
 31 | 
 32 |             try:
 33 |                 if re.match(r'^PE[0-9]{2}|^MS-DOS', magictype):
 34 |                     fmype = pefile.PE(targetfile)
 35 |                     fimph = fmype.get_imphash()
 36 |                 else:
 37 |                     if (cv.bkg == 1):
 38 |                         print(mycolors.foreground.lightred + "\nYou didn\'t provided a PE file")
 39 |                     else:
 40 |                         print(mycolors.foreground.red + "\nYou didn\'t provided a PE file")
 41 |                     printr()
 42 |                     exit(1)
 43 |             except (AttributeError, NameError):
 44 |                 if (cv.bkg == 1):
 45 |                     print((mycolors.foreground.lightred + "\nThe file %s doesn't respect some PE format rules. Exiting...\n" % targetfile))
 46 |                 else:
 47 |                     print((mycolors.foreground.red + "\nThe file %s doesn't respect some PE format rules. Exiting...\n" % targetfile))
 48 |                 printr()
 49 |                 exit(1)
 50 | 
 51 |         printr()
 52 |         print("POLYSWARM.NETWORK RESULTS")
 53 |         print('-' * 25, end="\n\n")
 54 | 
 55 |         try:
 56 |             if (metainfo == 4):
 57 |                 metaresults = polyswarm.search_by_metadata("pefile.imphash:" + fimph)
 58 |                 for x in metaresults:
 59 |                     if (cv.bkg == 1):
 60 |                         if (x.sha256):
 61 |                             print(mycolors.reset + "\nSHA256: " + mycolors.foreground.lightred + "%s" % x.sha256, end=' ')
 62 |                         else:
 63 |                             print(mycolors.reset + "\nSHA256: " + mycolors.foreground.lightred + "%s" + "None", end=' ')
 64 |                         if (x.md5):
 65 |                             print(mycolors.reset + "MD5: " + mycolors.foreground.lightcyan + "%s" % x.md5, end=' ')
 66 |                         else:
 67 |                             print(mycolors.reset + "MD5: " + mycolors.foreground.lightcyan + "%s" + "None", end=' ')
 68 |                     else:
 69 |                         if (x.sha256):
 70 |                             print(mycolors.reset + "\nSHA256: " + mycolors.foreground.red + "%s" % x.sha256, end=' ')
 71 |                         else:
 72 |                             print(mycolors.reset + "\nSHA256: " + mycolors.foreground.red + "%s" + "None", end=' ')
 73 |                         if (x.md5):
 74 |                             print(mycolors.reset + "MD5: " + mycolors.foreground.green + "%s" % x.md5, end=' ')
 75 |                         else:
 76 |                             print(mycolors.reset + "MD5: " + mycolors.foreground.green + "%s" + "None", end=' ')
 77 |                 print(mycolors.reset + "\n")
 78 |                 exit(0)
 79 | 
 80 |             try:
 81 |                 if (metainfo == 5):
 82 |                     metaresults = polyswarm.search_by_metadata("strings.ipv4:" + poly)
 83 |                 if (metainfo == 6):
 84 |                     metaresults = polyswarm.search_by_metadata("strings.domains:" + poly)
 85 |                 if (metainfo == 7):
 86 |                     poly = (r'"' + poly + r'"')
 87 |                     metaresults = polyswarm.search_by_metadata("strings.urls:" + poly)
 88 |                 if (metainfo == 8):
 89 |                     poly = ('scan.latest_scan.\*.metadata.malware_family:' + poly)
 90 |                     metaresults = polyswarm.search_by_metadata(poly)
 91 |             except Exception:
 92 |                 if (cv.bkg == 1):
 93 |                     print((mycolors.foreground.lightred + "\nInformation not found on Polyswarm.\n"))
 94 |                 else:
 95 |                     print((mycolors.foreground.red + "\nInformation not found on Polyswarm.\n"))
 96 |                 print(mycolors.reset)
 97 |                 exit(0)
 98 | 
 99 |             for y in metaresults:
100 |                 if (cv.bkg == 1):
101 |                     if (y.sha256):
102 |                         print(mycolors.reset + "\nSHA256: " + mycolors.foreground.lightcyan + "%s" % y.sha256, end=' ')
103 |                     else:
104 |                         print(mycolors.reset + "Result: " + mycolors.foreground.yellow + "Sample not found!", end=' ')
105 |                         exit(0)
106 | 
107 |                     try:
108 |                         score = next(polyswarm.search(y.sha256))
109 |                     except Exception:
110 |                         score.polyscore = "None"
111 |                         pass
112 | 
113 |                     print(mycolors.reset + "Polyscore: " + mycolors.foreground.yellow + "%20s" % score.polyscore, end=' ')
114 |                     if (str(y.scan.get('detections', {}).get('malicious'))) != 'None':
115 |                         print(mycolors.reset + "scan: " + mycolors.foreground.yellow + "%s" % y.scan.get('detections', {}).get('malicious'), end=' ')
116 |                         print("/ " + "%2s malicious" % y.scan.get('detections', {}).get('total'), end=' ')
117 |                     else:
118 |                         print(mycolors.reset + "scan: " + mycolors.foreground.pink + "not scanned yet", end=' ')
119 |                 else:
120 |                     if (y.sha256):
121 |                         print(mycolors.reset + "\nSHA256: " + mycolors.foreground.green + "%s" % y.sha256, end=' ')
122 |                     else:
123 |                         print(mycolors.reset + "scan: " + mycolors.foreground.purple + "Sample not found!", end=' ')
124 |                         exit(0)
125 | 
126 |                     try:
127 |                         score = next(polyswarm.search(y.sha256))
128 |                     except Exception:
129 |                         score.polyscore = "None"
130 |                         pass
131 | 
132 |                     print(mycolors.reset + "Polyscore: " + mycolors.foreground.red + "%20s" % score.polyscore, end=' ')
133 |                     if (str(y.scan.get('detections', {}).get('malicious'))) != 'None':
134 |                         print(mycolors.reset + "scan: " + mycolors.foreground.red + "%s" % y.scan.get('detections', {}).get('malicious'), end=' ')
135 |                         print("/ " + "%2s malicious" % y.scan.get('detections', {}).get('total'), end=' ')
136 |                     else:
137 |                         print(mycolors.reset + "Result: " + mycolors.foreground.purple + "not scanned yet", end=' ')
138 | 
139 |             printr()
140 |         except RetryError:
141 |             if (cv.bkg == 1):
142 |                 print((mycolors.foreground.lightred + "\nAn error has ocurred during Polyswarm processing. Exiting...\n"))
143 |             else:
144 |                 print((mycolors.foreground.red + "\nAn error has ocurred during Polyswarm processing. Exiting...\n"))
145 |             printr()
146 |             exit(1)
147 |         except Exception as e:
148 |             if (cv.bkg == 1):
149 |                 print((mycolors.foreground.lightred + "\nAn error has ocurred while connecting to Polyswarm.\n"))
150 |             else:
151 |                 print((mycolors.foreground.red + "\nAn error has ocurred while connecting to Polyswarm.\n"))
152 |             print(e)
153 |             printr()
154 |             exit(1)
155 | 
156 |     def polyfile(self, poly):
157 |         if not (os.path.isfile(poly)):
158 |             if (cv.bkg == 0):
159 |                 print(mycolors.foreground.red + "\nYou didn't provide a valid file.\n")
160 |                 printr()
161 |                 exit(1)
162 |             else:
163 |                 print(mycolors.foreground.yellow + "\nYou didn't provide a valid file.\n")
164 |                 printr()
165 |                 exit(1)
166 | 
167 |         sha256 = ''
168 |         filetype = ''
169 |         extended = ''
170 |         firstseen = ''
171 |         score = 0
172 | 
173 |         self.requestPOLYAPI()
174 | 
175 |         polyswarm = PolyswarmAPI(key=self.POLYAPI)
176 | 
177 |         try:
178 |             myhash = sha256hash(poly)
179 |             instance = polyswarm.submit(poly)
180 |             result = polyswarm.wait_for(instance)
181 |             printr()
182 |             print("POLYSWARM.NETWORK RESULTS")
183 |             print('-' * 25, end="\n\n")
184 |             for assertion in result.assertions:
185 |                 if (cv.bkg == 1):
186 |                     print(mycolors.reset + "Engine: " + mycolors.foreground.lightcyan + "%-25s" % assertion.author_name, end='')
187 |                     print(mycolors.reset + "\tVerdict:" + mycolors.foreground.lightred + " ", "Malicious" if assertion.verdict else "Clean")
188 |                 else:
189 |                     print(mycolors.reset + "Engine: " + mycolors.foreground.green + "%-25s" % assertion.author_name, end='')
190 |                     print(mycolors.reset + "\tVerdict:" + mycolors.foreground.red + " ", "Malicious" if assertion.verdict else "Clean")
191 | 
192 |             results = polyswarm.search(myhash)
193 |             printr()
194 |             for myhashes in results:
195 |                 if (myhashes.sha256):
196 |                     sha256 = myhashes.sha256
197 |                 if (myhashes.mimetype):
198 |                     filetype = myhashes.mimetype
199 |                 if (myhashes.extended_type):
200 |                     extended = myhashes.extended_type
201 |                 if (myhashes.first_seen):
202 |                     firstseen = myhashes.first_seen
203 |                 if (myhashes.polyscore):
204 |                     score = myhashes.polyscore
205 | 
206 |             if (cv.bkg == 1):
207 |                 if (sha256):
208 |                     print(mycolors.foreground.lightred + "\nSHA256: \t%s" % sha256)
209 |                 if (filetype):
210 |                     print(mycolors.foreground.lightred + "File Type: \t%s" % filetype)
211 |                 if (extended):
212 |                     print(mycolors.foreground.lightred + "Extended Info: \t%s" % extended)
213 |                 if (firstseen):
214 |                     print(mycolors.foreground.lightred + "First seen: \t%s" % firstseen)
215 |                 if (score is not None):
216 |                     print(mycolors.foreground.yellow + "\nPolyscore: \t%f" % score)
217 |             else:
218 |                 if (sha256):
219 |                     print(mycolors.foreground.cyan + "\nSHA256: \t%s" % sha256)
220 |                 if (filetype):
221 |                     print(mycolors.foreground.cyan + "File Type: \t%s" % filetype)
222 |                 if (extended):
223 |                     print(mycolors.foreground.cyan + "Extended Info: \t%s" % extended)
224 |                 if (firstseen):
225 |                     print(mycolors.foreground.cyan + "First seen: \t%s" % firstseen)
226 |                 if (score is not None):
227 |                     print(mycolors.foreground.red + "\nPolyscore: \t%f" % score)
228 |             printr()
229 |         except Exception:
230 |             if (cv.bkg == 1):
231 |                 print((mycolors.foreground.lightred + "\nAn error has ocurred while connecting to Polyswarm.\n"))
232 |             else:
233 |                 print((mycolors.foreground.red + "\nAn error has ocurred while connecting to Polyswarm.\n"))
234 |             printr()
235 |             exit(1)
236 | 
237 |     def polyhashsearch(self, poly, download):
238 |         if len(poly) not in [32, 40, 64]:
239 |             return False
240 | 
241 |         sha256 = ''
242 |         filetype = ''
243 |         extended = ''
244 |         firstseen = ''
245 |         score = 0
246 |         down = download
247 |         DOWN_DIR = cv.output_dir
248 | 
249 |         self.requestPOLYAPI()
250 |         polyswarm = PolyswarmAPI(key=self.POLYAPI)
251 | 
252 |         try:
253 |             results = polyswarm.search(poly)
254 | 
255 |             printr()
256 |             print("POLYSWARM.NETWORK RESULTS")
257 |             print('-' * 25, end="\n\n")
258 |             printr()
259 | 
260 |             for myhashes in results:
261 |                 if not myhashes.assertions:
262 |                     if (cv.bkg == 1):
263 |                         print(mycolors.foreground.lightred + "This sample has not been scanned on Polyswarm yet!\n" + mycolors.reset)
264 |                         exit(1)
265 |                     else:
266 |                         print(mycolors.foreground.red + "This sample has not been scanned on Polyswarmi yet!\n" + mycolors.reset)
267 |                         exit(1)
268 |                 if (myhashes.sha256):
269 |                     sha256 = myhashes.sha256
270 |                 if (myhashes.mimetype):
271 |                     filetype = myhashes.mimetype
272 |                 if (myhashes.extended_type):
273 |                     extended = myhashes.extended_type
274 |                 if (myhashes.first_seen):
275 |                     firstseen = myhashes.first_seen
276 |                 if (myhashes.polyscore):
277 |                     score = myhashes.polyscore
278 |                 results = myhashes.assertions
279 |                 for i in results:
280 |                     if (cv.bkg == 1):
281 |                         print(mycolors.foreground.lightcyan + "%s" % i)
282 |                     else:
283 |                         print(mycolors.foreground.green + "%s" % i)
284 | 
285 |             if (cv.bkg == 1):
286 |                 if (sha256):
287 |                     print(mycolors.foreground.lightred + "\nSHA256: \t%s" % sha256)
288 |                 if (filetype):
289 |                     print(mycolors.foreground.lightred + "File Type: \t%s" % filetype)
290 |                 if (extended):
291 |                     print(mycolors.foreground.lightred + "Extended Info: \t%s" % extended)
292 |                 if (firstseen):
293 |                     print(mycolors.foreground.lightred + "First seen: \t%s" % firstseen)
294 |                 if (score is not None):
295 |                     print(mycolors.foreground.yellow + "\nPolyscore: \t%f" % score)
296 |                 if (down == 1):
297 |                     polyswarm.download(DOWN_DIR, sha256)
298 |                     print(mycolors.reset + f"\n\nSample downloaded to: {DOWN_DIR}")
299 |             else:
300 |                 if (sha256):
301 |                     print(mycolors.foreground.cyan + "\nSHA256: \t%s" % sha256)
302 |                 if (filetype):
303 |                     print(mycolors.foreground.cyan + "File Type: \t%s" % filetype)
304 |                 if (extended):
305 |                     print(mycolors.foreground.cyan + "Extended Info: \t%s" % extended)
306 |                 if (firstseen):
307 |                     print(mycolors.foreground.cyan + "First seen: \t%s" % firstseen)
308 |                 if (score is not None):
309 |                     print(mycolors.foreground.red + "\nPolyscore: \t%f" % score)
310 |                 if (down == 1):
311 |                     polyswarm.download(DOWN_DIR, sha256)
312 |                     print(mycolors.reset + f"\n\nSample downloaded to: {DOWN_DIR}")
313 |             printr()
314 |         except Exception:
315 |             if (cv.bkg == 1):
316 |                 print((mycolors.foreground.yellow + "\nThis hash couldn't be found on Polyswarm.\n"))
317 |             else:
318 |                 print((mycolors.foreground.red + "\nThis hash couldn't be found Polyswarm.\n"))
319 |             printr()
320 |             exit(1)
321 | 


--------------------------------------------------------------------------------
/malwoverview/modules/virusexchange.py:
--------------------------------------------------------------------------------
  1 | import requests
  2 | import os
  3 | import malwoverview.modules.configvars as cv
  4 | from malwoverview.utils.colors import mycolors, printc
  5 | 
  6 | class VirusExchangeExtractor:
  7 |     def __init__(self, api_key):
  8 |         self.VXAPI = api_key
  9 |         self.base_url = "https://virus.exchange/api"
 10 | 
 11 |     def _requireVXAPI(self):
 12 |         if (self.VXAPI == ''):
 13 |             printc("\nTo be able to get/submit information from/to Virus Exchange, you must create the .malwapi.conf file under your user home directory (on Linux is $HOME\\.malwapi.conf and on Windows is in C:\\Users\\[username]\\.malwapi.conf) and insert the Virus Exchange API according to the format shown on the Github website.", mycolors.foreground.error(cv.bkg))
 14 |             exit(1)
 15 | 
 16 |     def _get_hash_metadata(self, sha256):
 17 |         url = f'{self.base_url}/samples/{sha256}'
 18 |         response = requests.get(url, headers={'Authorization': f'Bearer {self.VXAPI}'})
 19 |         return response
 20 | 
 21 |     def check_hash(self, sha256):
 22 |         self._requireVXAPI()
 23 | 
 24 |         metadata_to_show = [
 25 |             {"key": "md5", "name": "MD5", "colors": (mycolors.foreground.cyan, mycolors.foreground.lightcyan), "pad": 12},
 26 |             {"key": "sha1", "name": "SHA1", "colors": (mycolors.foreground.cyan, mycolors.foreground.lightcyan), "pad": 12},
 27 |             {"key": "sha256", "name": "SHA256", "colors": (mycolors.foreground.cyan, mycolors.foreground.lightcyan), "pad": 12},
 28 |             {"key": "size", "name": "Size", "colors": (mycolors.foreground.purple, mycolors.foreground.yellow), "pad": 12},
 29 |             {
 30 |                 "key": "tags", "name": "Tags",
 31 |                 "colors": (mycolors.foreground.purple, mycolors.foreground.yellow), "pad": 12,
 32 |                 "fn": lambda x: ', '.join(x) if hasattr(x, '__iter__') else ""
 33 |             },
 34 |             {"key": "type", "name": "Type", "colors": (mycolors.foreground.purple, mycolors.foreground.yellow), "pad": 12},
 35 |             {"key": "first_seen", "name": "First Seen", "colors": (mycolors.foreground.purple, mycolors.foreground.yellow), "pad": 12}
 36 |         ]
 37 | 
 38 |         try:
 39 |             response = self._get_hash_metadata(sha256)
 40 |         except Exception as e:
 41 |             printc(f"[-] Error checking hash metadata: {str(e)}", mycolors.foreground.error(cv.bkg))
 42 |             return
 43 | 
 44 |         metadata = response.json()
 45 | 
 46 |         print()
 47 |         for val in metadata_to_show:
 48 |             key = val['key']
 49 |             color = val["colors"][cv.bkg]
 50 |             name = val['name']
 51 |             pad = val['pad']
 52 |             fn = val.get('fn', None)
 53 | 
 54 |             if key in metadata:
 55 |                 printc(f'{name}:'.ljust(pad), color, end='')
 56 | 
 57 |                 val_to_print = metadata[key]
 58 |                 if fn is not None:
 59 |                     val_to_print = fn(metadata[key])
 60 | 
 61 |                 print(f'{val_to_print}')
 62 | 
 63 |     def download_sample(self, sha256):
 64 |         self._requireVXAPI()
 65 | 
 66 |         try:
 67 |             response = self._get_hash_metadata(sha256)
 68 |             metadata = response.json()
 69 | 
 70 |             if response.status_code == 200:
 71 |                 download_link = metadata.get('download_link')
 72 |                 if download_link:
 73 |                     # Download the actual sample
 74 |                     sample_response = requests.get(download_link)
 75 |                     if sample_response.status_code == 200:
 76 |                         output_path = os.path.join(cv.output_dir, sha256)
 77 |                         with open(output_path, 'wb') as f:
 78 |                             f.write(sample_response.content)
 79 |                         printc(f"Sample downloaded to: {output_path}", mycolors.foreground.success(cv.bkg))
 80 |                     else:
 81 |                         printc(f"Failed to download sample: {sample_response.status_code}", mycolors.foreground.error(cv.bkg))
 82 |                 else:
 83 |                     printc("No download link available in metadata", mycolors.foreground.error(cv.bkg))
 84 |             else:
 85 |                 error_detail = metadata.get('errors', {}).get('detail', 'Unknown error')
 86 |                 printc(f"Failed to fetch sample metadata: {error_detail}", mycolors.foreground.error(cv.bkg))
 87 |         except Exception as e:
 88 |             printc(f"Error downloading sample: {str(e)}", mycolors.foreground.error(cv.bkg))
 89 | 
 90 |     # This method is currently shown in the docs, but it's always returning 401
 91 |     # so it was removed from the official options.
 92 |     def upload_sample(self, file_path):
 93 |         self._requireVXAPI()
 94 | 
 95 |         url = f'{self.base_url}/samples/new'
 96 |         
 97 |         try:
 98 |             with open(file_path, 'rb') as f:
 99 |                 files = {'file': f}
100 |                 response = requests.post(url, headers={'Authorization': f'Bearer {self.VXAPI}'}, files=files)
101 |                 
102 |             if response.status_code == 200:
103 |                 printc("Sample uploaded successfully", mycolors.foreground.success(cv.bkg))
104 |             else:
105 |                 printc(f"Failed to upload sample: {response.status_code}", mycolors.foreground.error(cv.bkg))
106 |         except Exception as e:
107 |             printc(f"Error uploading sample: {str(e)}", mycolors.foreground.error(cv.bkg))


--------------------------------------------------------------------------------
/malwoverview/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/malwoverview/utils/__init__.py


--------------------------------------------------------------------------------
/malwoverview/utils/colors.py:
--------------------------------------------------------------------------------
 1 | class mycolors:
 2 |     reset = '\033[0m'
 3 |     reverse = '\033[07m'
 4 |     bold = '\033[01m'
 5 | 
 6 |     class foreground:
 7 |         orange = '\033[33m'
 8 |         blue = '\033[34m'
 9 |         purple = '\033[35m'
10 |         lightgreen = '\033[92m'
11 |         lightblue = '\033[94m'
12 |         pink = '\033[95m'
13 |         lightcyan = '\033[96m'
14 |         red = '\033[31m'
15 |         green = '\033[32m'
16 |         cyan = '\033[36m'
17 |         lightgrey = '\033[37m'
18 |         darkgrey = '\033[90m'
19 |         lightred = '\033[91m'
20 |         yellow = '\033[93m'
21 | 
22 |         @staticmethod
23 |         def error(bkg):
24 |             if bkg == 1:
25 |                 return mycolors.foreground.lightred
26 |             else:
27 |                 return mycolors.foreground.red
28 | 
29 |         @staticmethod
30 |         def info(bkg):
31 |             if bkg == 1:
32 |                 return mycolors.foreground.lightcyan
33 |             else:
34 |                 return mycolors.foreground.cyan
35 |         
36 |         @staticmethod
37 |         def success(bkg):
38 |             if bkg == 1:
39 |                 return mycolors.foreground.yellow
40 |             else:
41 |                 return mycolors.foreground.blue
42 | 
43 |     class background:
44 |         black = '\033[40m'
45 |         blue = '\033[44m'
46 |         cyan = '\033[46m'
47 |         lightgrey = '\033[47m'
48 |         purple = '\033[45m'
49 |         green = '\033[42m'
50 |         orange = '\033[43m'
51 |         red = '\033[41m'
52 | 
53 | 
54 | def printc(text, color, *args, **kwargs):
55 |     print(f'{color}{text}{mycolors.reset}', *args, **kwargs)
56 | 
57 | 
58 | def printr():
59 |     print(mycolors.reset)
60 | 


--------------------------------------------------------------------------------
/malwoverview/utils/hash.py:
--------------------------------------------------------------------------------
 1 | import hashlib
 2 | import os
 3 | from malwoverview.utils.colors import mycolors, printr
 4 | import malwoverview.modules.configvars as cv
 5 | 
 6 | 
 7 | def sha256hash(fname):
 8 |     BSIZE = 65536
 9 |     hnd = open(fname, 'rb')
10 |     hash256 = hashlib.sha256()
11 |     while True:
12 |         info = hnd.read(BSIZE)
13 |         if not info:
14 |             break
15 |         hash256.update(info)
16 |     return hash256.hexdigest()
17 | 
18 | 
19 | def md5hash(fname):
20 |     BSIZE = 65536
21 |     hnd = open(fname, 'rb')
22 |     hashmd5 = hashlib.md5()
23 |     while True:
24 |         info = hnd.read(BSIZE)
25 |         if not info:
26 |             break
27 |         hashmd5.update(info)
28 |     return hashmd5.hexdigest()
29 | 
30 | 
31 | def calchash(ffpname2):
32 |     targetfile = ffpname2
33 |     mysha256hash = ''
34 |     dname = str(os.path.dirname(targetfile))
35 |     if not os.path.abspath(dname):
36 |         dname = os.path.abspath('.') + "/" + dname
37 | 
38 |     print(mycolors.reset, end=' ')
39 | 
40 |     try:
41 |         mysha256hash = sha256hash(targetfile)
42 |         return mysha256hash
43 |     except Exception:
44 |         if (cv.bkg == 1):
45 |             print((mycolors.foreground.lightred + "Error while calculing the hash!\n"))
46 |         else:
47 |             print((mycolors.foreground.red + "Error while calculating the hash\n"))
48 |         printr()
49 | 


--------------------------------------------------------------------------------
/malwoverview/utils/peinfo.py:
--------------------------------------------------------------------------------
  1 | import pefile
  2 | from malwoverview.utils.colors import mycolors, printr
  3 | import malwoverview.modules.configvars as cv
  4 | import magic
  5 | import os
  6 | 
  7 | 
  8 | def ftype(filename):
  9 |     type = magic.from_file(filename)
 10 |     return type
 11 | 
 12 | 
 13 | def isoverlay(file_item):
 14 |     mype2 = pefile.PE(file_item)
 15 |     over = mype2.get_overlay_data_start_offset()
 16 |     if over is None:
 17 |         ovr = "NO"
 18 |     else:
 19 |         ovr = "YES"
 20 |     return ovr
 21 | 
 22 | 
 23 | def overextract(fname):
 24 |     with open(fname, "rb") as o:
 25 |         r = o.read()
 26 |     pe = pefile.PE(fname)
 27 |     offset = pe.get_overlay_data_start_offset()
 28 |     if offset is None:
 29 |         exit(0)
 30 | 
 31 |     outputpath = os.path.join(cv.output_dir, fname + '.overlay')
 32 |     with open(outputpath, "wb") as t:
 33 |         t.write(r[offset:])
 34 |     if (cv.bkg == 1):
 35 |         print((mycolors.foreground.yellow + "\n\nOverlay extracted:   " + mycolors.reset + outputpath))
 36 |     else:
 37 |         print((mycolors.foreground.green + "\n\nOverlay extracted:   " + mycolors.reset + outputpath))
 38 |     printr()
 39 | 
 40 | 
 41 | def listexports(fname):
 42 |     exps = []
 43 | 
 44 |     mype2 = pefile.PE(fname, fast_load=True)
 45 |     if mype2.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT']].VirtualAddress != 0:
 46 |         mype2.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT']])
 47 |         for exptab in mype2.DIRECTORY_ENTRY_EXPORT.symbols:
 48 |             x = hex(mype2.OPTIONAL_HEADER.ImageBase + exptab.address), exptab.name
 49 |             exps.append(x)
 50 | 
 51 |     return exps
 52 | 
 53 | 
 54 | def listimports(fname):
 55 |     imps = []
 56 | 
 57 |     mype2 = pefile.PE(fname, fast_load=True)
 58 |     if mype2.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']].VirtualAddress != 0:
 59 |         mype2.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']])
 60 |         if mype2.DIRECTORY_ENTRY_IMPORT is not None:
 61 |             for entry in mype2.DIRECTORY_ENTRY_IMPORT:
 62 |                 for imptab in entry.imports:
 63 |                     if imptab.name is None:
 64 |                         imptab.name = "None"
 65 |                     if imptab.address is None:
 66 |                         imptab.address = int(0)
 67 |                     x = hex(int(imptab.address)), imptab.name
 68 |                     imps.append(x)
 69 |     return imps
 70 | 
 71 | 
 72 | def list_imports_exports(targetfile):
 73 |     printr()
 74 | 
 75 |     print(("\nImported Functions".ljust(40)))
 76 |     print((110 * '-').ljust(110))
 77 |     IR = []
 78 |     IR = sorted(listimports(targetfile))
 79 |     dic = {}
 80 |     dic = dict(IR)
 81 |     d = iter(list(dic.items()))
 82 |     IX = []
 83 |     for key, value in sorted(d):
 84 |         IX.append(str(value))
 85 |     Y = iter(IX)
 86 | 
 87 |     for i in Y:
 88 |         if i is None:
 89 |             break
 90 | 
 91 |         while (i == 'None'):
 92 |             i = next(Y, None)
 93 | 
 94 |         if i is None:
 95 |             break
 96 |         if (cv.bkg == 1):
 97 |             print((mycolors.foreground.pink + "%-40s" % (i)[2:-1]), end=' ')
 98 |         else:
 99 |             print((mycolors.foreground.cyan + "%-40s" % (i)[2:-1]), end=' ')
100 |         w = next(Y, None)
101 |         if w is None:
102 |             break
103 |         if (w == 'None'):
104 |             w = next(Y, None)
105 |         if w is None:
106 |             break
107 |         if (cv.bkg == 1):
108 |             print((mycolors.foreground.lightcyan + "%-40s" % (w)[2:-1]), end=' ')
109 |         else:
110 |             print((mycolors.foreground.green + "%-40s" % (w)[2:-1]), end=' ')
111 |         t = next(Y, None)
112 |         if t is None:
113 |             break
114 |         if (t == 'None'):
115 |             t = next(Y, None)
116 |         if t is None:
117 |             break
118 |         if (cv.bkg == 1):
119 |             print((mycolors.foreground.yellow + "%-40s" % (t)[2:-1]))
120 |         else:
121 |             print((mycolors.foreground.purple + "%-40s" % (t)[2:-1]))
122 | 
123 |     printr()
124 | 
125 |     print(("\n\nExported Functions".ljust(40)))
126 |     print((110 * '-').ljust(110))
127 |     ER = []
128 |     ER = sorted(listexports(targetfile))
129 |     dic2 = {}
130 |     dic2 = dict(ER)
131 |     d2 = iter(list(dic2.items()))
132 |     EX = []
133 |     for key, value in sorted(d2):
134 |         EX.append(str(value))
135 |     Y2 = iter(EX)
136 |     for i in Y2:
137 |         if i is None:
138 |             break
139 |         while (i == 'None'):
140 |             i = next(Y2, None)
141 |         if i is None:
142 |             break
143 |         if (cv.bkg == 1):
144 |             print((mycolors.foreground.yellow + "%-40s" % (i)[2:-1]), end=' ')
145 |         else:
146 |             print((mycolors.foreground.purple + "%-40s" % (i)[2:-1]), end=' ')
147 |         w = next(Y2, None)
148 |         if w is None:
149 |             break
150 |         if (w == 'None'):
151 |             w = next(Y2, None)
152 |         if w is None:
153 |             break
154 |         if (cv.bkg == 1):
155 |             print((mycolors.foreground.lightcyan + "%-40s" % (w)[2:-1]), end=' ')
156 |         else:
157 |             print((mycolors.foreground.green + "%-40s" % (w)[2:-1]), end=' ')
158 | 
159 |         t = next(Y2, None)
160 |         if t is None:
161 |             break
162 |         if (t == 'None'):
163 |             t = next(Y2, None)
164 |         if t is None:
165 |             break
166 |         if (cv.bkg == 1):
167 |             print((mycolors.foreground.lightcyan + "%-40s" % (t)[2:-1]))
168 |         else:
169 |             print((mycolors.foreground.cyan + "%-40s" % (t)[2:-1]))
170 | 
171 |     printr()
172 | 


--------------------------------------------------------------------------------
/malwoverview/utils/utils.py:
--------------------------------------------------------------------------------
 1 | from urllib.parse import urlparse
 2 | import geocoder
 3 | import socket
 4 | 
 5 | 
 6 | def urltoip(urltarget):
 7 |     geoloc = ''
 8 |     target = ''
 9 |     finalip = ''
10 |     result = ''
11 | 
12 |     try:
13 |         target = urlparse(urltarget)
14 |         result = target.netloc
15 |         finalip = socket.gethostbyname(result)
16 |         if finalip is not None:
17 |             geoloc = geocoder.ip(finalip)
18 |             if (geoloc is not None):
19 |                 return geoloc.city
20 |             else:
21 |                 result = ''
22 |                 return result
23 |         else:
24 |             result = "Not Found"
25 |             return result
26 |     except Exception:
27 |         result = "Not Found"
28 |         return result
29 | 


--------------------------------------------------------------------------------
/pictures/picture_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_1.jpg


--------------------------------------------------------------------------------
/pictures/picture_10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_10.jpg


--------------------------------------------------------------------------------
/pictures/picture_11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_11.jpg


--------------------------------------------------------------------------------
/pictures/picture_12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_12.jpg


--------------------------------------------------------------------------------
/pictures/picture_13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_13.jpg


--------------------------------------------------------------------------------
/pictures/picture_14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_14.jpg


--------------------------------------------------------------------------------
/pictures/picture_15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_15.jpg


--------------------------------------------------------------------------------
/pictures/picture_16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_16.jpg


--------------------------------------------------------------------------------
/pictures/picture_17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_17.jpg


--------------------------------------------------------------------------------
/pictures/picture_18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_18.jpg


--------------------------------------------------------------------------------
/pictures/picture_19.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_19.jpg


--------------------------------------------------------------------------------
/pictures/picture_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_2.jpg


--------------------------------------------------------------------------------
/pictures/picture_20.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_20.jpg


--------------------------------------------------------------------------------
/pictures/picture_21.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_21.jpg


--------------------------------------------------------------------------------
/pictures/picture_22.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_22.jpg


--------------------------------------------------------------------------------
/pictures/picture_23.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_23.jpg


--------------------------------------------------------------------------------
/pictures/picture_24.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_24.jpg


--------------------------------------------------------------------------------
/pictures/picture_25.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_25.jpg


--------------------------------------------------------------------------------
/pictures/picture_26.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_26.jpg


--------------------------------------------------------------------------------
/pictures/picture_27.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_27.jpg


--------------------------------------------------------------------------------
/pictures/picture_28.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_28.jpg


--------------------------------------------------------------------------------
/pictures/picture_29.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_29.jpg


--------------------------------------------------------------------------------
/pictures/picture_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_3.jpg


--------------------------------------------------------------------------------
/pictures/picture_30.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_30.jpg


--------------------------------------------------------------------------------
/pictures/picture_31.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_31.jpg


--------------------------------------------------------------------------------
/pictures/picture_32.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_32.jpg


--------------------------------------------------------------------------------
/pictures/picture_33.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_33.jpg


--------------------------------------------------------------------------------
/pictures/picture_34.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_34.jpg


--------------------------------------------------------------------------------
/pictures/picture_35.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_35.jpg


--------------------------------------------------------------------------------
/pictures/picture_36.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_36.jpg


--------------------------------------------------------------------------------
/pictures/picture_37.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_37.jpg


--------------------------------------------------------------------------------
/pictures/picture_38.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_38.jpg


--------------------------------------------------------------------------------
/pictures/picture_39.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_39.jpg


--------------------------------------------------------------------------------
/pictures/picture_4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_4.jpg


--------------------------------------------------------------------------------
/pictures/picture_40.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_40.jpg


--------------------------------------------------------------------------------
/pictures/picture_41.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_41.jpg


--------------------------------------------------------------------------------
/pictures/picture_42.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_42.jpg


--------------------------------------------------------------------------------
/pictures/picture_43.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_43.jpg


--------------------------------------------------------------------------------
/pictures/picture_44.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_44.jpg


--------------------------------------------------------------------------------
/pictures/picture_45.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_45.jpg


--------------------------------------------------------------------------------
/pictures/picture_46.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_46.jpg


--------------------------------------------------------------------------------
/pictures/picture_47.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_47.jpg


--------------------------------------------------------------------------------
/pictures/picture_48.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_48.jpg


--------------------------------------------------------------------------------
/pictures/picture_49.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_49.jpg


--------------------------------------------------------------------------------
/pictures/picture_5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_5.jpg


--------------------------------------------------------------------------------
/pictures/picture_50.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_50.jpg


--------------------------------------------------------------------------------
/pictures/picture_6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_6.jpg


--------------------------------------------------------------------------------
/pictures/picture_7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_7.jpg


--------------------------------------------------------------------------------
/pictures/picture_8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_8.jpg


--------------------------------------------------------------------------------
/pictures/picture_9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexandreborges/malwoverview/d5fc570750951dfcf1608525282b6cb01a753a41/pictures/picture_9.jpg


--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
 1 | pefile>=2021.9.3
 2 | python-magic>=0.4.25
 3 | colorama>=0.4.4
 4 | simplejson>=3.17.6
 5 | requests>=2.26.0
 6 | validators>=0.18.2
 7 | geocoder>=1.38.1
 8 | polyswarm-api>=2.9.2
 9 | pathlib>=1.0.1
10 | configparser>=5.2.0
11 | 


--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env python3
 2 | import os
 3 | from setuptools import setup, find_packages
 4 | from pathlib import Path
 5 | import platform
 6 | 
 7 | USER_HOME_DIR = str(Path.home()) + os.sep
 8 | 
 9 | with open("README.md", encoding='utf8') as readme:
10 |     long_description = readme.read()
11 | 
12 | setup(
13 |     name="malwoverview",
14 |     version="6.1.1",
15 |     author="Alexandre Borges",
16 |     author_email="reverseexploit@proton.me",
17 |     license="GNU GPL v3.0",
18 |     url="https://github.com/alexandreborges/malwoverview",
19 |     description=("Malwoverview is a first response tool for threat hunting."),
20 |     long_description=long_description,
21 |     long_description_content_type="text/markdown",
22 |     packages=find_packages(),
23 |     classifiers=[
24 |     'Operating System :: OS Independent',
25 |     'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
26 |     'Programming Language :: Python :: 3',
27 |     ],
28 |     install_requires=[
29 |         "pefile",
30 |         "colorama",
31 |         "python-magic; platform_system == 'Linux' or platform_system == 'Darwin'",
32 |         "simplejson",
33 |         "requests",
34 |         "validators",
35 |         "geocoder",
36 |         "polyswarm-api",
37 |         "pathlib",
38 |         "configparser",
39 | 		"python-magic-bin; platform_system == 'Windows'"
40 |     ],
41 |     entry_points = {
42 |         "console_scripts": [
43 |             "malwoverview = malwoverview.malwoverview:main",
44 |         ]
45 |     },
46 |     package_data={'': ['README.md, LICENSE, .malwapi.conf']},
47 |     data_files=[(USER_HOME_DIR, ['.malwapi.conf'])],
48 | )
49 | 


--------------------------------------------------------------------------------