├── lib ├── __init__.py ├── config.py ├── maltego_qradio.py ├── qradio.py └── helpers.py ├── stations ├── __init__.py ├── cymon │ ├── __init__.py │ └── station.py ├── malwr │ ├── __init__.py │ └── station.py ├── blacklists │ ├── __init__.py │ └── station.py ├── fortiguard │ ├── __init__.py │ └── station.py ├── hostsfile │ ├── __init__.py │ └── station.py ├── ibmxforceex │ ├── __init__.py │ └── station.py ├── metascan │ ├── __init__.py │ └── station.py ├── threatcrowd │ ├── __init__.py │ └── station.py ├── threatexpert │ ├── __init__.py │ └── station.py ├── totalhash │ ├── __init__.py │ └── station.py ├── virustotal │ ├── __init__.py │ └── station.py └── station_tamplate.py ├── requirements.txt ├── tramatego ├── .canari ├── MANIFEST.in ├── src │ └── tramatego │ │ ├── resources │ │ ├── etc │ │ │ ├── tramatego.conf │ │ │ └── __init__.py │ │ ├── external │ │ │ └── __init__.py │ │ ├── images │ │ │ └── __init__.py │ │ ├── maltego │ │ │ └── __init__.py │ │ └── __init__.py │ │ ├── transforms │ │ ├── common │ │ │ ├── config.py │ │ │ ├── __init__.py │ │ │ ├── launchers.py │ │ │ └── entities.py │ │ ├── __init__.py │ │ ├── domain_to_hash.py │ │ ├── domain_to_score.py │ │ ├── ipv4_to_hash.py │ │ ├── domain_to_url.py │ │ ├── domain_to_blacklist.py │ │ ├── ipv4_to_blacklist.py │ │ ├── hash_to_score.py │ │ ├── hash_to_domain.py │ │ ├── hash_to_ipv4.py │ │ ├── hash_to_url.py │ │ ├── ipv4_to_score.py │ │ ├── domain_to_ipv4.py │ │ └── ipv4_to_domain.py │ │ └── __init__.py ├── setup.py └── README.md ├── .gitignore ├── README.md ├── LICENSE └── cli_qradio.py /lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stations/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /stations/cymon/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stations/malwr/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stations/blacklists/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stations/fortiguard/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stations/hostsfile/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stations/ibmxforceex/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stations/metascan/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stations/threatcrowd/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stations/threatexpert/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stations/totalhash/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stations/virustotal/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | argparse>=1.2.1 2 | requests>=2.9.1 3 | xmltodict>=0.10.1 4 | beautifulsoup4>=4.4.1 -------------------------------------------------------------------------------- /tramatego/.canari: -------------------------------------------------------------------------------- 1 | [metadata] 2 | 3 | author = Zappus 4 | project = Tramatego 5 | maintainer = Zappus 6 | email = 7 | -------------------------------------------------------------------------------- /tramatego/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.md 2 | recursive-include src *.py *.conf *.gif *.png *.mtz *.machine 3 | recursive-include maltego *.mtz 4 | -------------------------------------------------------------------------------- /tramatego/src/tramatego/resources/etc/tramatego.conf: -------------------------------------------------------------------------------- 1 | [section1] 2 | 3 | option1 = this, is, a, list 4 | 5 | option2 = this\, is\, a\, comma-separated string 6 | 7 | 8 | [section2] 9 | 10 | etc = etc -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/common/config.py: -------------------------------------------------------------------------------- 1 | class Paths(object): 2 | 3 | qradio_path = '' 4 | python_path = '' 5 | 6 | def __init__(self): 7 | qradio_path = 'path_to_cli_qradio.py' 8 | python_path = '' 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tramatego/src/tramatego/resources/etc/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __author__ = 'Zappus' 4 | __copyright__ = 'Copyright 2016, Tramatego Project' 5 | __credits__ = [] 6 | 7 | __license__ = 'GPL' 8 | __version__ = '0.1' 9 | __maintainer__ = 'Zappus' 10 | __email__ = '' 11 | __status__ = 'Development' -------------------------------------------------------------------------------- /tramatego/src/tramatego/resources/external/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __author__ = 'Zappus' 4 | __copyright__ = 'Copyright 2016, Tramatego Project' 5 | __credits__ = [] 6 | 7 | __license__ = 'GPL' 8 | __version__ = '0.1' 9 | __maintainer__ = 'Zappus' 10 | __email__ = '' 11 | __status__ = 'Development' -------------------------------------------------------------------------------- /tramatego/src/tramatego/resources/images/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __author__ = 'Zappus' 4 | __copyright__ = 'Copyright 2016, Tramatego Project' 5 | __credits__ = [] 6 | 7 | __license__ = 'GPL' 8 | __version__ = '0.1' 9 | __maintainer__ = 'Zappus' 10 | __email__ = '' 11 | __status__ = 'Development' -------------------------------------------------------------------------------- /tramatego/src/tramatego/resources/maltego/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __author__ = 'Zappus' 4 | __copyright__ = 'Copyright 2016, Tramatego Project' 5 | __credits__ = [] 6 | 7 | __license__ = 'GPL' 8 | __version__ = '0.1' 9 | __maintainer__ = 'Zappus' 10 | __email__ = '' 11 | __status__ = 'Development' -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/common/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __author__ = 'Zappus' 4 | __copyright__ = 'Copyright 2016, Tramatego Project' 5 | __credits__ = [] 6 | 7 | __license__ = 'GPL' 8 | __version__ = '0.1' 9 | __maintainer__ = 'Zappus' 10 | __email__ = '' 11 | __status__ = 'Development' 12 | 13 | __all__ = [ 14 | 'entities' 15 | ] -------------------------------------------------------------------------------- /tramatego/src/tramatego/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __author__ = 'Zappus' 4 | __copyright__ = 'Copyright 2016, Tramatego Project' 5 | __credits__ = [] 6 | 7 | __license__ = 'GPL' 8 | __version__ = '0.1' 9 | __maintainer__ = 'Zappus' 10 | __email__ = '' 11 | __status__ = 'Development' 12 | 13 | __all__ = [ 14 | 'resources', 15 | 'transforms' 16 | ] -------------------------------------------------------------------------------- /tramatego/src/tramatego/resources/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __author__ = 'Zappus' 4 | __copyright__ = 'Copyright 2016, Tramatego Project' 5 | __credits__ = [] 6 | 7 | __license__ = 'GPL' 8 | __version__ = '0.1' 9 | __maintainer__ = 'Zappus' 10 | __email__ = '' 11 | __status__ = 'Development' 12 | 13 | __all__ = [ 14 | 'etc', 15 | 'images', 16 | 'maltego', 17 | 'external' 18 | ] -------------------------------------------------------------------------------- /tramatego/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='tramatego', 5 | author='Zappus', 6 | version='1.0', 7 | author_email='', 8 | description='Transforms for Maltego - Powered by QRadio.', 9 | license='GPL', 10 | packages=find_packages('src'), 11 | package_dir={ '' : 'src' }, 12 | zip_safe=False, 13 | package_data={ 14 | '' : [ '*.gif', '*.png', '*.conf', '*.mtz', '*.machine' ] # list of resources 15 | }, 16 | install_requires=[ 17 | 'canari' 18 | ], 19 | dependency_links=[ 20 | # custom links for the install_requires 21 | ] 22 | ) 23 | -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __author__ = 'Zappus' 4 | __copyright__ = 'Copyright 2016, Tramatego Project' 5 | __credits__ = [] 6 | 7 | __license__ = 'GPL' 8 | __version__ = '0.1' 9 | __maintainer__ = 'Zappus' 10 | __email__ = '' 11 | __status__ = 'Development' 12 | 13 | __all__ = [ 14 | 'ipv4_to_hash', 15 | 'ipv4_to_blacklist', 16 | 'ipv4_to_score', 17 | 'ipv4_to_domain', 18 | 'hash_to_url', 19 | 'hash_to_score', 20 | 'hash_to_ipv4', 21 | 'hash_to_domain', 22 | 'domain_to_url', 23 | 'domain_to_score', 24 | 'domain_to_ipv4', 25 | 'domain_to_hash', 26 | 'domain_to_blacklist', 27 | 'common' 28 | ] -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/common/launchers.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | 4 | def get_qradio_data(command, result_column = 0 ): 5 | python_path = 'C:\Python27\python.exe' 6 | qradio_path = 'C:\MaltegoTransforms\QRadio\cli_qradio.py' 7 | error = "lol" 8 | output = [] 9 | 10 | command_final = python_path + ' ' + qradio_path + ' ' + command 11 | p = subprocess.Popen(command_final, stdout=subprocess.PIPE, shell=True) 12 | out, err = p.communicate() 13 | try: 14 | result = out.split('\n') 15 | del result[0] 16 | except: 17 | error = "yay..." 18 | 19 | for lin in result: 20 | try: 21 | final_out = lin.split(',')[result_column].strip("\n").strip("\r") 22 | if final_out != "" and final_out != " ": 23 | output.append(final_out) 24 | except: 25 | error = 'oh no...' 26 | return output 27 | -------------------------------------------------------------------------------- /lib/config.py: -------------------------------------------------------------------------------- 1 | # This file contains API keys that are required in order to pull data from sources 2 | # You can register for those platforms by provided links. 3 | 4 | # Cymon 5 | # Registration: https://cymon.io/user/signup 6 | cymon_api_key = '' 7 | 8 | # VirusTotal 9 | # Registration: https://www.virustotal.com/ 10 | virustotal_api_key = '' 11 | 12 | # Malwr 13 | # Registration: https://malwr.com/account/signup/ 14 | malwr_login = '' 15 | malwr_passwd = '' 16 | malw_api_key = '' 17 | 18 | # Totalhash 19 | # Registration: https://totalhash.cymru.com/contact-us/ 20 | totalhash_uid = '' 21 | totalhash_api_key = '' 22 | 23 | # MetaScan 24 | # Registration: https://accounts.opswat.com/auth/module.php/opswauth/register.php 25 | metascan_api_key = '' 26 | 27 | # PassiveTotal 28 | # Registration: https://www.passivetotal.org/register 29 | passivetotal_api_key = '' 30 | 31 | # IBM X-Force Exchange 32 | # Registration: https://www.ibm.com/account/profile/us?page=reg 33 | ibmxforce_token = None -------------------------------------------------------------------------------- /.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 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | .hypothesis/ 46 | 47 | # Translations 48 | *.mo 49 | *.pot 50 | 51 | # Django stuff: 52 | *.log 53 | 54 | # Sphinx documentation 55 | docs/_build/ 56 | 57 | # PyBuilder 58 | target/ 59 | 60 | #Ipython Notebook 61 | .ipynb_checkpoints 62 | .idea 63 | *.pyc 64 | error_log.txt -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/domain_to_hash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import Domain, Phrase 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | #'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='Domain to Hash', 28 | description='', 29 | uuids=[ 'TramaTego.v1.DomainToHash' ], 30 | inputs=[ ( 'TramaTego', Domain ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--domain_to_hash " + request.value 35 | qradio_output = get_qradio_data(command, 2) 36 | for entry in qradio_output: 37 | response += Phrase(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/domain_to_score.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import Domain, Phrase 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | #'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='Domain to Score', 28 | description='', 29 | uuids=[ 'TramaTego.v1.DomainToScore' ], 30 | inputs=[ ( 'TramaTego', Domain ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--domain_to_score " + request.value 35 | qradio_output = get_qradio_data(command, 3) 36 | for entry in qradio_output: 37 | response += Phrase(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/ipv4_to_hash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import IPv4Address, Phrase 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | #'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='IPv4 to Hash', 28 | description='', 29 | uuids=[ 'TramaTego.v1.IPv4ToHash' ], 30 | inputs=[ ( 'TramaTego', IPv4Address ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--ipv4_to_hash " + request.value 35 | qradio_output = get_qradio_data(command, 2) 36 | for entry in qradio_output: 37 | response += Phrase(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/domain_to_url.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import Domain, Phrase 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | #'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='Domain to Source URL', 28 | description='', 29 | uuids=[ 'TramaTego.v1.DomainToSourceURL' ], 30 | inputs=[ ( 'TramaTego', Domain ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--domain_to_url " + request.value 35 | qradio_output = get_qradio_data(command, 4) 36 | for entry in qradio_output: 37 | response += Phrase(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/domain_to_blacklist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import Domain, Phrase 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | #'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='Domain to Blacklist', 28 | description='', 29 | uuids=[ 'TramaTego.v1.DomainToBlacklist' ], 30 | inputs=[ ( 'TramaTego', Domain ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--domain_to_blacklist " + request.value 35 | qradio_output = get_qradio_data(command, 5) 36 | for entry in qradio_output: 37 | response += Phrase(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/ipv4_to_blacklist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import IPv4Address, Phrase 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | 'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='IPv4 to Blacklist', 28 | description='', 29 | uuids=[ 'TramaTego.v1.IPv4ToBlacklist' ], 30 | inputs=[ ( 'TramaTego', IPv4Address ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--ipv4_to_blacklist " + request.value 35 | qradio_output = get_qradio_data(command, 5) 36 | for entry in qradio_output: 37 | response += Phrase(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/hash_to_score.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import Phrase 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | #'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='Hash to Score', 28 | description='Converts Hash into Score using QRadio.', 29 | uuids=[ 'TramaTego.v1.HashToScore' ], 30 | inputs=[ ( 'TramaTego', Phrase ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--hash_to_score " + request.value 35 | qradio_output = get_qradio_data(command, 3) 36 | for entry in qradio_output: 37 | response += Phrase(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/hash_to_domain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import Phrase, Domain 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | #'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='Hash to Domain', 28 | description='Converts Hash into Domain using QRadio.', 29 | uuids=[ 'TramaTego.v1.HashToDomain' ], 30 | inputs=[ ( 'TramaTego', Phrase ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--hash_to_domain " + request.value 35 | qradio_output = get_qradio_data(command, 0) 36 | for entry in qradio_output: 37 | response += Domain(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/hash_to_ipv4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import Phrase, IPv4Address 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | #'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='Hash to IPv4', 28 | description='Converts Hash into IPv4 using QRadio.', 29 | uuids=[ 'TramaTego.v1.HashToIPv4' ], 30 | inputs=[ ( 'TramaTego', Phrase ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--hash_to_ipv4 " + request.value 35 | qradio_output = get_qradio_data(command, 1) 36 | for entry in qradio_output: 37 | response += IPv4Address(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/hash_to_url.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import Phrase 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | #'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='Hash to Source URL', 28 | description='Converts Hash into Source URL using QRadio.', 29 | uuids=[ 'TramaTego.v1.HashToSourceURL' ], 30 | inputs=[ ( 'TramaTego', Phrase ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--hash_to_url " + request.value 35 | qradio_output = get_qradio_data(command, 4) 36 | for entry in qradio_output: 37 | response += Phrase(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/ipv4_to_score.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import IPv4Address, Phrase 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | #'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='IPv4 to Score', 28 | description='Converts IPv4 into Score using QRadio.', 29 | uuids=[ 'TramaTego.v1.IPv4ToScore' ], 30 | inputs=[ ( 'TramaTego', IPv4Address ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--ipv4_to_score " + request.value 35 | qradio_output = get_qradio_data(command, 3) 36 | for entry in qradio_output: 37 | response += Phrase(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/domain_to_ipv4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import Domain, IPv4Address 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | #'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='Domain to IPv4', 28 | description='Converts Domain into IPv4 using QRadio.', 29 | uuids=[ 'TramaTego.v1.DomainToIPv4' ], 30 | inputs=[ ( 'TramaTego', Domain ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--domain_to_ipv4 " + request.value 35 | qradio_output = get_qradio_data(command, 1) 36 | for entry in qradio_output: 37 | response += IPv4Address(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/ipv4_to_domain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.utils import debug, progress 4 | from canari.framework import configure #, superuser 5 | from canari.maltego.entities import IPv4Address, Domain 6 | from common.launchers import get_qradio_data 7 | 8 | 9 | __author__ = 'Zappus' 10 | __copyright__ = 'Copyright 2016, TramaTego Project' 11 | __credits__ = [] 12 | 13 | __license__ = 'GPL' 14 | __version__ = '0.1' 15 | __maintainer__ = 'Zappus' 16 | __email__ = 'zappus@protonmail.com' 17 | __status__ = 'Development' 18 | 19 | __all__ = [ 20 | 'dotransform', 21 | 'onterminate' # comment out this line if you don't need this function. 22 | ] 23 | 24 | 25 | #@superuser 26 | @configure( 27 | label='IPv4 to Domain', 28 | description='Converts IPv4 into Domain using QRadio.', 29 | uuids=[ 'TramaTego.v1.IPv4ToDomain' ], 30 | inputs=[ ( 'TramaTego', IPv4Address ) ], 31 | debug=True 32 | ) 33 | def dotransform(request, response, config): 34 | command = "--ipv4_to_domain " + request.value 35 | qradio_output = get_qradio_data(command, 0) 36 | for entry in qradio_output: 37 | response += Domain(entry) 38 | return response 39 | 40 | 41 | def onterminate(): 42 | """ 43 | TODO: Write your cleanup logic below or delete the onterminate function and remove it from the __all__ variable 44 | """ 45 | pass -------------------------------------------------------------------------------- /stations/station_tamplate.py: -------------------------------------------------------------------------------- 1 | from lib import config, helpers 2 | import re 3 | 4 | class Station_name(object): 5 | 6 | def __init__(self): 7 | # lists of values that can be returned 8 | self.ip_list = [] 9 | self.domain_list = [] 10 | self.hash_list = [] 11 | self.url_list = [] 12 | self.score_list = [] 13 | self.imphash_list = [] 14 | 15 | # get helping functions 16 | self.api = helpers.Common() 17 | self.error_log = helpers.IO() 18 | 19 | # static station settings 20 | self.station_name = 'Station_name' 21 | self.endpoint = 'www.station_endpoint.com/api/index.php' 22 | self.path = '' 23 | self.parameters = {} 24 | self.headers = {} 25 | self.user_agent = {} 26 | self.response_format = '' 27 | 28 | ### Station tunes 29 | 30 | def domain_to_ipv4(self, data): 31 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 32 | data_to_send=None, url_path=self.path, parameters=self.parameters, 33 | headers=self.headers, user_agent=self.user_agent, 34 | response_format=self.response_format) 35 | 36 | if self.response_format == 'bs' and response: 37 | table = response.findAll('class', {'table': 'ip'}) 38 | for cell in table: 39 | match = re.search(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', cell.text) 40 | if match: 41 | self.ip_list.append(match.group()) 42 | return self.ip_list 43 | 44 | elif self.response_format == 'json' and response: 45 | for key in response['ip']: 46 | self.ip_list.append(key) 47 | return self.ip_list -------------------------------------------------------------------------------- /tramatego/README.md: -------------------------------------------------------------------------------- 1 | # README - Tramatego 2 | 3 | Welcome to Canari. You might be wondering what all these files are about. Before you can use the power of 4 | `canari install-package` you needed to create a transform package and that's exactly what you did here! I've given you a 5 | directory structure to use in the following manner: 6 | 7 | * `src/tramatego` directory is where all your stuff goes in terms of auxiliary modules that you may need for your 8 | modules 9 | * `src/tramatego/transforms` directory is where all your transform modules should be placed. An example 10 | `helloworld` transform is there for your viewing pleasure. 11 | * `src/tramatego/transforms/common` directory is where you can put some common code for your transforms like result 12 | parsing, entities, etc. 13 | * `src/tramatego/transforms/common/entities.py` is where you define your custom entities. Take a look at the 14 | examples provided if you want to play around with custom entities. 15 | * `maltego/` is where you can store your Maltego entity exports. 16 | * `src/tramatego/resources/maltego` directory is where your `entities.mtz` and `*.machine` files can be stored for auto 17 | install and uninstall. 18 | * `src/tramatego/resources/external` directory is where you can place non-Python transforms written in other languages. 19 | 20 | If you're going to add a new transform in the transforms directory, remember to update the `__all__` variable in 21 | `src/tramatego/transforms/__init__.py`. Otherwise, `canari install-package` won't attempt to install the transform. 22 | Alternatively, `canari create-transform ` can be used within the `src/tramatego/transforms` directory 23 | to generate a transform module and have it automatically added to the `__init__.py` file, like so: 24 | 25 | To test your transform, simply `cd` into the src directory and run `canari debug-transform`, like so: 26 | 27 | ```bash 28 | $ canari debug-transform Tramatego.transforms.helloworld Phil 29 | %50 30 | D:This was pointless! 31 | %100 32 | `- MaltegoTransformResponseMessage: 33 | `- Entities: 34 | `- Entity: {'Type': 'test.MyTestEntity'} 35 | `- Value: Hello Phil! 36 | `- Weight: 1 37 | `- AdditionalFields: 38 | `- Field: 2 {'DisplayName': 'Field 1', 'Name': 'test.field1', 'MatchingRule': 'strict'} 39 | `- Field: test {'DisplayName': 'Field N', 'Name': 'test.fieldN', 'MatchingRule': 'strict'} 40 | ``` 41 | 42 | Cool right? If you have any further questions don't hesitate to drop us a line;) 43 | 44 | Have fun! -------------------------------------------------------------------------------- /stations/fortiguard/station.py: -------------------------------------------------------------------------------- 1 | ################################################################# 2 | # Fortiguard station for QRadio # 3 | # ~ Tune In # 4 | # Tuned to: # 5 | # http://www.fortiguard.com/iprep/index.php # 6 | # # 7 | # Author: 10TOHH # 8 | # # 9 | # Tunes: # 10 | # domain_to_ipv4 - Resolves IP to # 11 | ################################################################# 12 | 13 | from lib import helpers 14 | 15 | class Fortiguard(object): 16 | 17 | def __init__(self): 18 | # lists of values that can be returned 19 | self.ip_list = [] 20 | self.domain_list = [] 21 | self.hash_list = [] 22 | self.url_list = [] 23 | self.score_list = [] 24 | self.imphash_list = [] 25 | 26 | # get helping functions 27 | self.api = helpers.Common() 28 | 29 | # static station settings 30 | self.station_name = 'Foriguard' 31 | self.endpoint = 'http://www.fortiguard.com/iprep/index.php' 32 | self.url_path = '' 33 | self.parameters = {} 34 | self.headers = {} 35 | self.user_agent = {} 36 | self.response_format = 'bs' 37 | 38 | ### Station tunes 39 | 40 | def domain_to_ipv4(self, domain_name): 41 | self.parameters = {'data': domain_name} 42 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 43 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 44 | headers=self.headers, user_agent=self.user_agent, 45 | response_format=self.response_format) 46 | if response: 47 | try: 48 | table = response.findAll('table', {'class': 'large'}) 49 | if len(table) != 0: 50 | self.ip_list.extend(table[1].text.split()) # first element of dict, .text = values, .split = make a list 51 | return self.ip_list 52 | except: 53 | return self.ip_list 54 | else: 55 | return self.ip_list 56 | 57 | ##### MAIN ##### 58 | if __name__ == '__main__': 59 | f = Fortiguard() 60 | ##print f.domain_to_ipv4('alfred.1gb.ru') -------------------------------------------------------------------------------- /tramatego/src/tramatego/transforms/common/entities.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from canari.maltego.message import Entity, EntityField, EntityFieldType, MatchingRule 4 | 5 | __author__ = 'Zappus' 6 | __copyright__ = 'Copyright 2016, Tramatego Project' 7 | __credits__ = [] 8 | 9 | __license__ = 'GPL' 10 | __version__ = '0.1' 11 | __maintainer__ = 'Zappus' 12 | __email__ = '' 13 | __status__ = 'Development' 14 | 15 | __all__ = [ 16 | 'TramategoEntity', 17 | 'MyTramategoEntity' 18 | ] 19 | 20 | """ 21 | DO NOT EDIT: 22 | The following entity is the base entity type from which all your entities will inherit from. This provides you with the 23 | default namespace that all your entities will use for their unique entity type in Maltego. For example, MyTramategoEntity will 24 | have an entity type name of tramatego.MyTramategoEntity. When adding a new entity in Maltego, you will have to specify this 25 | name (tramatego.MyTramategoEntity) in the 'Unique entity type' field. 26 | """ 27 | class TramategoEntity(Entity): 28 | _namespace_ = 'tramatego' 29 | 30 | 31 | """ 32 | You can specify as many entity fields as you want by just adding an extra @EntityField() decorator to your entities. The 33 | @EntityField() decorator takes the following parameters: 34 | - name: the name of the field without spaces or special characters except for dots ('.') (required) 35 | - propname: the name of the object's property used to get and set the value of the field (required, if name contains dots) 36 | - displayname: the name of the entity as it appears in Maltego (optional) 37 | - type: the data type of the field (optional, default: EntityFieldType.String) 38 | - required: whether or not the field's value must be set before sending back the message (optional, default: False) 39 | - choices: a list of acceptable field values for this field (optional) 40 | - matchingrule: whether or not the field should be loosely or strictly matched (optional, default: MatchingRule.Strict) 41 | - decorator: a function that is invoked each and everytime the field's value is set or changed. 42 | - is_value: a boolean value that determines whether the field is also the default value of the entity object. 43 | TODO: define as many custom fields and entity types as you wish:) 44 | """ 45 | @EntityField(name='tramatego.fieldN', propname='fieldN', displayname='Field N', matchingrule=MatchingRule.Loose) 46 | @EntityField(name='tramatego.field1', propname='field1', displayname='Field 1', type=EntityFieldType.Integer) 47 | class MyTramategoEntity(TramategoEntity): 48 | """ 49 | Uncomment the line below and comment out the pass if you wish to define a ridiculous entity type name like 50 | 'my.fancy.EntityType' 51 | """ 52 | # _name_ = 'my.fancy.EntityType' 53 | pass -------------------------------------------------------------------------------- /stations/hostsfile/station.py: -------------------------------------------------------------------------------- 1 | ################################################################# 2 | # Hostsfile station for QRadio # 3 | # ~ Tune In # 4 | # Tuned to: # 5 | # http://hosts-file.net/default.asp # 6 | # # 7 | # Author: 10TOHH # 8 | # # 9 | # Tunes: # 10 | # domain_to_ipv4 - Resolves IP to # 11 | # ipv4_to_domain - Resolves Domain to # 12 | ################################################################# 13 | 14 | from lib import helpers 15 | 16 | class Hostsfile(object): 17 | 18 | def __init__(self): 19 | # lists of values that can be returned 20 | self.ip_list = [] 21 | self.domain_list = [] 22 | self.hash_list = [] 23 | self.url_list = [] 24 | self.score_list = [] 25 | self.imphash_list = [] 26 | 27 | # get helping functions 28 | self.api = helpers.Common() 29 | 30 | # static station settings 31 | self.station_name = 'HostsFile' 32 | self.endpoint = 'http://hosts-file.net/' 33 | self.url_path = '' 34 | self.parameters = {} 35 | self.headers = {} 36 | self.user_agent = {} 37 | self.response_format = 'bs' 38 | 39 | ### Station tunes 40 | 41 | def domain_to_ipv4(self, data): 42 | self.parameters = {'s': data, 43 | 'view': 'history'} 44 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 45 | data_to_send=None, parameters=self.parameters, headers=self.headers, 46 | response_format=self.response_format) 47 | if response: 48 | table = response.findAll('td', {'width': '15%'}) 49 | if len(table) > 1: 50 | for cell in table[1:]: 51 | ip = ''.join(c for c in cell.text if c not in '\t\n\r') 52 | self.ip_list.append(ip) 53 | return self.ip_list 54 | 55 | def ipv4_to_domain(self, data): 56 | self.parameters = {'s': data, 'view': 'history'} # s = search 57 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 58 | data_to_send=None, parameters=self.parameters, headers=self.headers, 59 | response_format=self.response_format) 60 | if response: 61 | table = response.findAll('a', {'class': 'main_normal_noborder'}) 62 | for cell in table: 63 | if cell.text: 64 | self.domain_list.append(cell.text) 65 | return self.domain_list[9:] 66 | 67 | ################################################################################################ 68 | 69 | ### MAIN #### 70 | if __name__ == '__main__': 71 | 72 | h = Hostsfile() 73 | ##print h.domain_to_ipv4('google.com') 74 | ##print h.ipv4_to_domain('74.125.53.100') -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | ________ \\ || // 3 | / __ \ \\ || // 4 | | / \ | ______ _____ ______ __ _____ 5 | | | _ | | | _ \ / _ \ | _ \ |__| / _ \ 6 | | | / \| | | |_) ) | / \ | | | \ \ __ | / \ | 7 | | | \ | | | / | |_| | | | ) ) | | | ( ) | 8 | | \_\ | | |\ \ | _ | | |_/ / | | | \_/ | 9 | \_______ \ |__| \__\ |__| |__| |______/ |__| \_____/ 10 | \__\ 11 | ~ Tune In 12 | ``` 13 | 14 | # QRadio 15 | QRadio is a tool/framework designed to consolidate cyber threats intelligence sources. 16 | The goal of the project is to establish a robust modular framework for extraction of intelligence data from vetted sources. 17 | 18 | It uses multiple threat intelligence sources for searching supplied data. Currently we crawl the following: 19 | 20 | **You can search by the following data types:** 21 | - Domain 22 | - IPv4 23 | - Hash 24 | - Imphash 25 | - Mutex 26 | 27 | ##### Threat Info databases: 28 | - [ThreatCrowd](https://www.threatcrowd.org/) 29 | - [Virustotal](https://virustotal.com/) 30 | - [Cymon](https://cymon.io/) 31 | - [IBM X-Force Exchange](https://exchange.xforce.ibmcloud.com/) 32 | - [Metadefender](https://www.metadefender.com/) 33 | - [#totalhash](https://totalhash.cymru.com/) 34 | 35 | ##### Sandboxes: 36 | - [Malwr](https://malwr.com/) 37 | - [Threatexpert](http://www.threatexpert.com/) 38 | 39 | ##### Blacklists: 40 | - [ASPROX Tracker](http://atrack.h3x.eu/) 41 | - [Feodot Tacker](http://feodotracker.abuse.ch/) 42 | - [Zeus Tracker](http://zeustracker.abuse.ch/) 43 | - [malc0de](http://malc0de.com/bl/) 44 | - [McAfee](http://www.siteadvisor.com/sites/) 45 | 46 | ##### Other: 47 | - [FortiGuard](http://fortiguard.com/iprep) 48 | - [hpHosts](http://hosts-file.net/) 49 | 50 | ### Credentials for sources 51 | 52 | - ```/lib/config.py``` 53 | 54 | ## Usage 55 | `python cli_qradio.py` 56 | 57 | ### Options 58 | 59 | #### Output verbosity: 60 | __Return CSV if not specified__ 61 | ``` 62 | -v, --verbose - Show verbose output 63 | ``` 64 | #### From Domain 65 | ``` 66 | -100, --sonar_domain - SONAR to IPv4, Hash, Score, URL, Blacklist 67 | -102, --domain_to_ipv4 - Resolve IPv4 to 68 | -103, --domain_to_hash - Search Hash for 69 | -104, --domain_to_score - Detection score for 70 | -105, --domain_to_url - URL to analysis for 71 | -106, --domain_to_blacklist - Search in blacklists 72 | ``` 73 | #### From IPv4 74 | ``` 75 | -200, --sonar_ipv4 - SONAR to Domain, Hash, Score, Blacklist 76 | -201, --ipv4_to_domain - Resolve Domain to 77 | -203, --ipv4_to_hash - Search Hash for 78 | -204, --ipv4_to_score - Detection score for 79 | -206, --ipv4_to_blacklist - Search in blacklists 80 | ``` 81 | #### From Hash 82 | ``` 83 | -300, --sonar_hash - SONAR to Domain, IPv4, Score, URL, Imphash 84 | -301, --hash_to_domain - Search Domain for 85 | -302, --hash_to_ipv4 - Search IP for 86 | -304, --hash_to_score - Detection score for 87 | -305, --hash_to_url - URL to analysis for 88 | -307, --hash_to_imphash - Search Imphash for 89 | ``` 90 | #### Miscellaneous 91 | ``` 92 | -401, --imphash_to_hash - Search Hash with 93 | -402, --mutex_to_hash - Search Hash with 94 | ``` 95 | -------------------------------------------------------------------------------- /lib/maltego_qradio.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from helpers import api_qradio as q 4 | from helpers import MaltegoTransform 5 | 6 | 7 | ############################################################## 8 | ## ENRICH Section 9 | def ipv4_enrich(mt, ip_address): 10 | enrich_list = q.ipv4_enrich(ip_address) 11 | for domain in enrich_list['domains']: 12 | mt.addEntity("maltego.Domain", domain) 13 | for hash in enrich_list['hash']: 14 | mt.addEntity("maltego.Hash", hash) 15 | for score in enrich_list['score']: 16 | mt.addEntity("maltego.Score", score) 17 | mt.addEntity("maltego.Blacklist", str(enrich_list['blacklist'])) 18 | return mt 19 | 20 | 21 | def domain_enrich(mt, domain_name): 22 | enrich_list = q.domain_enrich(domain_name) 23 | for ip_address in enrich_list['ip_address']: 24 | mt.addEntity("maltego.IPv4Address", ip_address) 25 | for hash in enrich_list['hash']: 26 | mt.addEntity("maltego.Hash", hash) 27 | for score in enrich_list['score']: 28 | mt.addEntity("maltego.Score", score) 29 | return mt 30 | 31 | 32 | def hash_enrich(mt, hash_value): 33 | enrich_list = q.hash_enrich(hash_value) 34 | for score in enrich_list: 35 | mt.addEntity("maltego.Score", score['score']) 36 | for ip_address in enrich_list: 37 | mt.addEntity("maltego.IPv4Address", ip_address['ip_address']) 38 | for imphash in enrich_list: 39 | mt.addEntity("maltego.Imphash", imphash['imphash']) 40 | for uri in enrich_list: 41 | mt.addEntity("maltego.URI", uri['uri']) 42 | return mt 43 | 44 | 45 | 46 | ############################################################## 47 | ## IP section 48 | def ipv4_to_domain(mt, ip_address): 49 | domain_list = q.ipv4_to_domain(ip_address) 50 | for domain in domain_list: 51 | mt.addEntity("maltego.Domain", domain) 52 | return mt 53 | 54 | def ipv4_to_hash(mt, ip_address): 55 | hash_list = q.ipv4_to_hash(ip_address) 56 | for hash in hash_list: 57 | mt.addEntity("maltego.Hash", hash) 58 | return mt 59 | 60 | def ipv4_to_blacklist(mt, ip_address): 61 | blacklisted = q.ipv4_to_blacklist(ip_address) 62 | mt.addEntity("maltego.Blacklist", blacklisted) 63 | return mt 64 | 65 | def ipv4_to_score(mt, ip_address): 66 | score_list = q.ipv4_to_score(ip_address) 67 | for score in score_list: 68 | mt.addEntity("maltego.Score", score) 69 | return mt 70 | 71 | ############################################################## 72 | ## Domain section 73 | def domain_to_ipv4(mt, domain_name): 74 | ip_list = q.domain_to_ipv4(domain_name) 75 | for ip_address in ip_list: 76 | mt.addEntity("maltego.IPv4Address", ip_address) 77 | return mt 78 | 79 | def domain_to_hash(mt, domain_name): 80 | hash_list = q.domain_to_hash(domain_name) 81 | for hash in hash_list: 82 | mt.addEntity("maltego.Hash", hash) 83 | return mt 84 | 85 | def domain_to_score(mt, domain_name): 86 | score_list = q.domain_to_score(domain_name) 87 | for score in score_list: 88 | mt.addEntity("maltego.Score", score) 89 | return mt 90 | 91 | ############################################################## 92 | ## Hash section 93 | def hash_to_score(mt, hash_valuse): 94 | score_list = q.hash_to_score(hash_valuse) 95 | for score in score_list: 96 | mt.addEntity("maltego.Score", score) 97 | return mt 98 | 99 | def hash_to_imphash(mt, hash_valuse): 100 | imphash_list = q.hash_to_imphash(hash_valuse) 101 | for imphash in imphash_list: 102 | mt.addEntity("maltego.Imphash", imphash) 103 | return mt 104 | 105 | def hash_to_ipv4(mt, hash_valuse): 106 | ip_list = q.hash_to_ipv4(hash_valuse) 107 | for ip_address in ip_list: 108 | mt.addEntity("maltego.IPv4Address", ip_address) 109 | return mt 110 | 111 | def hash_to_uri(mt, hash_valuse): 112 | uri_list = q.hash_to_uri(hash_valuse) 113 | for uri in uri_list: 114 | mt.addEntity("maltego.URI", uri) 115 | return mt 116 | 117 | ############################################################## 118 | ## Imphash section 119 | def imphash_to_hash(mt, imphash): 120 | hash_list = q.imphash_to_hash(imphash) 121 | for hash in hash_list: 122 | mt.addEntity("maltego.Hash", hash) 123 | return mt 124 | 125 | ############################################################## 126 | 127 | functions = { 128 | 'ipv4_enrich': ipv4_enrich, 129 | 'domain_enrich': domain_enrich, 130 | 'hash_enrich': hash_enrich, 131 | 132 | 'ipv4_to_domain': ipv4_to_domain, 133 | 'ipv4_to_hash': ipv4_to_hash, 134 | 'ipv4_to_blacklist': ipv4_to_blacklist, 135 | 'ipv4_to_score': ipv4_to_score, 136 | 137 | 'domain_to_ipv4': domain_to_ipv4, 138 | 'domain_to_hash': domain_to_hash, 139 | 'domain_to_score': domain_to_score, 140 | 141 | 'hash_to_score': hash_to_score, 142 | 'hash_to_imphash': hash_to_imphash, 143 | 'hash_to_ipv4': hash_to_ipv4, 144 | 'hash_to_uri': hash_to_uri, 145 | 146 | 'imphash_to_hash': imphash_to_hash, 147 | } 148 | 149 | ##### MAIN ##### 150 | if __name__ == '__main__': 151 | transform = sys.argv[1] 152 | data = sys.argv[2] 153 | 154 | mt = MaltegoTransform() 155 | 156 | result = functions[transform](mt, data) 157 | result.returnOutput() -------------------------------------------------------------------------------- /stations/metascan/station.py: -------------------------------------------------------------------------------- 1 | ################################################################# 2 | # Metascan station for QRadio # 3 | # ~ Tune In # 4 | # Tuned to: # 5 | # https://www.metascan-online.com # 6 | # # 7 | # API Documentation: # 8 | # https://www.metascan-online.com/public-api#!/ # 9 | # # 10 | # Author: 10TOHH # 11 | # # 12 | # Tunes: # 13 | # ipv4_to_score - Return Score to # 14 | # # 15 | # hash_to_imphash - Return Imphash associated with # 16 | # hash_to_score - Return Score to given # 17 | ################################################################# 18 | 19 | from lib import config, helpers 20 | 21 | class Metascan(object): 22 | 23 | def __init__(self): 24 | # lists of values that can be returned 25 | self.ip_list = [] 26 | self.domain_list = [] 27 | self.hash_list = [] 28 | self.url_list = [] 29 | self.score_list = [] 30 | self.imphash_list = [] 31 | 32 | # get helping functions 33 | self.api = helpers.Common() 34 | 35 | # static station settings 36 | self.station_name = 'Metascan' 37 | self.endpoint = 'https://metascan-online.com' # different subdomains for search 38 | # hashlookup and ipscan 39 | self.url_path = '' 40 | self.parameters = {} 41 | self.headers = {'content-type': 'application/json', 42 | 'accept': 'application/json', 43 | 'file_metadata': 1} 44 | self.user_agent = {} 45 | self.response_format = 'json' 46 | 47 | if config.metascan_api_key: 48 | self.headers.update({'apikey': config.metascan_api_key}) 49 | else: 50 | msg = 'API Key NOT provided' 51 | helpers.IO().error_log(msg,self.station_name) 52 | return 53 | 54 | ####################### 55 | 56 | def hash_enrich(self, hash_value): 57 | self.endpoint = 'https://hashlookup.metascan-online.com' 58 | self.url_path = '/v2/hash/' + hash_value 59 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 60 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 61 | headers=self.headers, user_agent=self.user_agent, 62 | response_format=self.response_format) 63 | return response 64 | 65 | def ip_enrich(self, ip_address): 66 | self.endpoint = 'https://ipscan.metascan-online.com' 67 | self.url_path = '/v1/scan/' + ip_address 68 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 69 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 70 | headers=self.headers, user_agent=self.user_agent, 71 | response_format=self.response_format) 72 | return response 73 | 74 | 75 | ######################### 76 | 77 | ### Station tunes 78 | 79 | def ipv4_to_score(self, ip_address): 80 | enriched = Metascan().ip_enrich(ip_address) 81 | if enriched: 82 | self.score_list.append(str(enriched['detected_by'])+'/12') # 12 - number of Metascan sources 83 | return self.score_list 84 | 85 | def hash_to_score(self, hash_value): 86 | infected = 0 87 | enriched = Metascan().hash_enrich(hash_value) 88 | if len(enriched) > 1: 89 | if 'scan_results' in enriched: 90 | if 'total_avs' in enriched['scan_results']: 91 | total_avs = enriched['scan_results']['total_avs'] 92 | if 'scan_details' in enriched['scan_results']: 93 | for key in enriched['scan_results']['scan_details'].values(): 94 | # https://www.metascan-online.com/public-api#!/definitions 95 | # 1 = Infected/Known, 2 = Suspicious, 3 = Failed To Scan 96 | # 6 = Quarantined, 8 = Skipped Dirty, 9 = Exceeded Archive Depth, 12 = Encrypted 97 | if key['scan_result_i'] == 1 or \ 98 | key['scan_result_i'] == 2 or \ 99 | key['scan_result_i'] == 3 or \ 100 | key['scan_result_i'] == 6 or \ 101 | key['scan_result_i'] == 8 or \ 102 | key['scan_result_i'] == 9 or \ 103 | key['scan_result_i'] == 12: 104 | infected += 1 105 | self.score_list.append(str(infected)+'/'+str(total_avs)) # Infected\Number_of_AV 106 | return self.score_list 107 | 108 | 109 | def hash_to_imphash(self, hash_value): 110 | response = Metascan().hash_enrich(hash_value) 111 | if len(response) > 1: 112 | if 'file_info' in response: 113 | if 'pe_info' in response['file_info']: 114 | if 'imphash' in response['file_info']['pe_info']: 115 | self.imphash_list.append(response['file_info']['pe_info']['imphash']) 116 | return self.imphash_list 117 | 118 | #if __name__ == '__main__': 119 | ##print Metascan().ipv4_to_score('8.8.8.8') 120 | ##print Metascan().hash_to_score('7a29752205392b2a952d3f8bf2a886b3') 121 | ##print Metascan().hash_to_imphash('7a29752205392b2a952d3f8bf2a886b3') -------------------------------------------------------------------------------- /stations/cymon/station.py: -------------------------------------------------------------------------------- 1 | ################################################################# 2 | # Cymon station for QRadio # 3 | # ~ Tune In # 4 | # Tuned to: # 5 | # https://cymon.io # 6 | # # 7 | # API Documentation: # 8 | # http://docs.cymon.io/ # 9 | # # 10 | # Author: 10TOHH # 11 | # # 12 | # Tunes: # 13 | # domain_to_ipv4 - Resolves IP to # 14 | # ipv4_to_domain - Resolves Domain to # 15 | # ipv4_to_hash - Return Hash associated with # 16 | # ipv4_to_url - Return URL to report for given # 17 | # hash_to_url - Return URL to report for given # 18 | ################################################################# 19 | 20 | from lib import config, helpers 21 | 22 | class Cymon(object): 23 | 24 | def __init__(self): 25 | # lists of values that can be returned 26 | self.ip_list = [] 27 | self.domain_list = [] 28 | self.hash_list = [] 29 | self.url_list = [] 30 | self.score_list = [] 31 | self.imphash_list = [] 32 | 33 | # get helping functions 34 | self.api = helpers.Common() 35 | self.error_log = helpers.IO() 36 | 37 | # static station settings 38 | self.station_name = 'Cymon' 39 | self.endpoint = 'https://cymon.io/api/nexus/v1/' 40 | self.url_path = '' 41 | self.parameters = {'limit': '1000'} 42 | self.headers = {'content-type': 'application/json', 43 | 'accept': 'application/json', 44 | } 45 | self.user_agent = {} 46 | self.return_format = 'json' 47 | 48 | # Check for api key 49 | if config.cymon_api_key: 50 | self.headers.update({'Authorization': 'Token %s' %config.cymon_api_key}) 51 | else: 52 | error_msg = 'API Key NOT provided' 53 | self.error_log.error_log(error_msg, self.station_name) 54 | 55 | ### Station tunes 56 | 57 | def domain_to_ipv4(self, domain_name): 58 | self.url_path = '/domain/' + domain_name 59 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 60 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 61 | headers=self.headers, user_agent=self.user_agent, 62 | response_format=self.return_format) 63 | if response: 64 | for key in response['ips']: 65 | self.ip_list.append(key.split('/')[-1]) 66 | return self.ip_list 67 | 68 | def ipv4_to_domain(self, ip_address): 69 | self.url_path = '/ip/' + ip_address + '/domains' 70 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 71 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 72 | headers=self.headers, user_agent=self.user_agent, 73 | response_format=self.return_format) 74 | if response: 75 | for name in response['results']: 76 | self.domain_list.append(name['name']) 77 | return self.domain_list 78 | 79 | 80 | def ipv4_to_hash(self, ip_address): 81 | self.url_path = '/ip/' + ip_address + '/malware/' 82 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 83 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 84 | headers=self.headers, user_agent=self.user_agent, 85 | response_format=self.return_format) 86 | if response: 87 | for key in response['results']: 88 | if key['hash_type'] != 'SSDEEP': # Exclude SSDEEP 89 | self.hash_list.append(key['hash_value']) 90 | return self.hash_list 91 | 92 | def ipv4_to_url(self, ip_address): 93 | self.url_path = '/ip/' + ip_address + '/events' 94 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 95 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 96 | headers=self.headers, user_agent=self.user_agent, 97 | response_format=self.return_format) 98 | if response: 99 | for key in response['results']: 100 | if key['details_url']: 101 | self.url_list.append(key['details_url']) 102 | return list(set(self.url_list)) 103 | 104 | def hash_to_url(self, hash_value): 105 | self.url_path = '/malware/' + hash_value + '/events/' 106 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 107 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 108 | headers=self.headers, user_agent=self.user_agent, 109 | response_format=self.return_format) 110 | if response: 111 | for key in response['results']: 112 | self.url_list.append(key['details_url']) 113 | return list(set(self.url_list)) 114 | 115 | ### MAIN ### 116 | if __name__ == '__main__': 117 | 118 | c = Cymon() 119 | ##print c.domain_to_ipv4('google.com') 120 | ##print c.ipv4_to_domain('216.58.219.14') 121 | ##print c.ipv4_to_hash('216.58.219.14') 122 | ##print c.ipv4_to_url('216.58.219.14') 123 | ##print c.hash_to_url('c1bed909e40f97a923eda3b738c58a6a8238bd3b') 124 | 125 | 126 | -------------------------------------------------------------------------------- /stations/virustotal/station.py: -------------------------------------------------------------------------------- 1 | ################################################################# 2 | # Virustotal station for QRadio # 3 | # ~ Tune In # 4 | # Tuned to: # 5 | # https://www.virustotal.com/ API v2 # 6 | # # 7 | # API Documentation: # 8 | # https://www.virustotal.com/en/documentation/public-api/ # 9 | # # 10 | # Author: 10TOHH # 11 | # # 12 | # # 13 | # Tunes: # 14 | # domain_to_ipv4 - Resolves IP to # 15 | # # 16 | # ipv4_to_domain - Resolves Domain to # 17 | # ipv4_to_hash - Return Hash associated with # 18 | # # 19 | # hash_to_score - Return Score to given # 20 | # hash_to_url - Return URL to report for given # 21 | ################################################################# 22 | 23 | from lib import config, helpers 24 | 25 | 26 | class Virustotal(object): 27 | 28 | def __init__(self): 29 | # lists of values that can be returned 30 | self.ip_list = [] 31 | self.domain_list = [] 32 | self.hash_list = [] 33 | self.url_list = [] 34 | self.score_list = [] 35 | self.imphash_list = [] 36 | 37 | # get helping functions 38 | self.api = helpers.Common() 39 | 40 | # static station settings 41 | self.station_name = 'Virustotal' 42 | self.endpoint = 'https://www.virustotal.com/vtapi/v2' 43 | self.path = '' 44 | self.parameters = {} 45 | self.headers = {} 46 | self.user_agent = {} 47 | self.response_format = 'json' 48 | 49 | if config.virustotal_api_key: 50 | self.parameters = {'apikey': config.virustotal_api_key} 51 | else: 52 | msg = 'API key is missing' 53 | helpers.IO().error_log(msg, self.station_name) 54 | return 55 | 56 | ################# 57 | # helpers 58 | 59 | def ip_response(self, ip_address): 60 | self.parameters.update({'ip': ip_address}) 61 | self.path = '/ip-address/report' 62 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 63 | data_to_send=None, url_path=self.path, parameters=self.parameters, 64 | headers=self.headers, user_agent=self.user_agent, 65 | response_format=self.response_format) 66 | if response: 67 | return response 68 | 69 | def domain_response(self, ip_address): 70 | self.parameters.update({'domain': ip_address}) 71 | self.path = '/domain/report' 72 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 73 | data_to_send=None, url_path=self.path, parameters=self.parameters, 74 | headers=self.headers, user_agent=self.user_agent, 75 | response_format=self.response_format) 76 | if response: 77 | return response 78 | 79 | 80 | def hash_response(self, ip_address): 81 | self.parameters.update({'resource': ip_address}) 82 | self.path = '/file/report' 83 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 84 | data_to_send=None, url_path=self.path, parameters=self.parameters, 85 | headers=self.headers, user_agent=self.user_agent, 86 | response_format=self.response_format) 87 | if response: 88 | return response 89 | 90 | ### Station tunes 91 | 92 | def domain_to_ipv4(self, domain_name): 93 | response = self.domain_response(domain_name) 94 | if response: 95 | if 'resolutions' in response: 96 | for key in response['resolutions']: 97 | self.ip_list.append(key['ip_address']) 98 | return self.ip_list 99 | 100 | 101 | def ipv4_to_domain(self, ip_address): 102 | response = self.ip_response(ip_address) 103 | if response: 104 | if 'resolutions' in response: 105 | for key in response['resolutions']: 106 | self.domain_list.append(key['hostname']) 107 | return self.domain_list 108 | 109 | def ipv4_to_hash(self, ip_address): 110 | response = self.ip_response(ip_address) 111 | if response: 112 | if 'detected_communicating_samples' in response: 113 | for urls in response['detected_communicating_samples']: ## DETECTED 114 | if 'sha256' in urls: 115 | self.hash_list.append(urls['sha256']) 116 | if 'undetected_communicating_samples' in response: 117 | for urls in response['undetected_communicating_samples']: ## UNDETECTED 118 | if 'sha256' in urls: 119 | self.hash_list.append(urls['sha256']) 120 | return self.hash_list 121 | 122 | def hash_to_score(self, hash_value): 123 | response = self.hash_response(hash_value) 124 | if response: 125 | if 'positives' in response: 126 | self.score_list.append(str(response['positives'])+'/'+str(response['total'])) 127 | return self.score_list 128 | 129 | def hash_to_url(self, hash_value): 130 | response = self.hash_response(hash_value) 131 | if response: 132 | if 'permalink' in response: 133 | self.url_list.append(response['permalink']) 134 | return self.url_list 135 | 136 | if __name__ == '__main__': 137 | 138 | test_domain = 'pastebin.com' 139 | test_ip = '104.20.64.56' 140 | test_hash = 'f99d5c59d082636fb97d71d7340e3ecd8d041bacde46b4d30841f14953945e2f' 141 | 142 | #print 'domain_to_ipv4', Virustotal().domain_to_ipv4(test_domain) 143 | #print 'ipv4_to_domain', Virustotal().ipv4_to_domain(test_ip) 144 | #print 'ipv4_to_hash', Virustotal().ipv4_to_hash(test_ip) 145 | #print 'hash_to_score', Virustotal().hash_to_score(test_hash) 146 | #print 'hash_to_url', Virustotal().hash_to_url(test_hash) -------------------------------------------------------------------------------- /stations/blacklists/station.py: -------------------------------------------------------------------------------- 1 | ################################################################# 2 | # # 3 | # Blacklist stations for QRadio # 4 | # ~ Tune In # 5 | # Tuned to: # 6 | # Asprox # 7 | # Feodo # 8 | # Malc0de # 9 | # Zeustracker # 10 | # Mcafee # 11 | # # 12 | # Author: 10TOHH # 13 | # # 14 | # Tunes: # 15 | # ipv4_to_blacklist - Return True/False # 16 | # domain_to_blacklist - Return True/False # 17 | # # 18 | ################################################################# 19 | 20 | from lib import helpers 21 | import re 22 | 23 | # Use this class for global search 24 | class Blacklist(object): 25 | 26 | def __init__(self): 27 | # List of all current blacklists tunes 28 | self.a = Asprox() 29 | self.f = Feodo() 30 | self.m = Malc0de() 31 | self.z = Zeustracker() 32 | self.ma = Mcafee() 33 | 34 | # Check IPv4 in blacklist tunes 35 | def ipv4_blacklist_check(self, ip_address): 36 | if self.a.ipv4_to_blacklist(ip_address): 37 | return True 38 | elif self.f.ipv4_to_blacklist(ip_address): 39 | return True 40 | elif self.m.ipv4_to_blacklist(ip_address): 41 | return True 42 | elif self.z.ipv4_to_blacklist(ip_address): 43 | return True 44 | elif self.ma.ipv4_to_blacklist(ip_address): 45 | return True 46 | else: 47 | return False 48 | # Check Domain in blacklist tunes 49 | def domain_blacklist_check(self, domain_name): 50 | if self.ma.domain_to_blacklist(domain_name): 51 | return True 52 | else: 53 | return False 54 | 55 | ######################################################### 56 | # Station tunes 57 | 58 | class Asprox(object): 59 | 60 | def __init__(self): 61 | # get helping functions 62 | self.api = helpers.Common() 63 | 64 | # static station settings 65 | self.station_name = 'Asprox' 66 | self.endpoint = 'http://atrack.h3x.eu/c2' 67 | self.response_format = 'bs' 68 | 69 | def ipv4_to_blacklist(self, ip_address): 70 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 71 | response_format=self.response_format) 72 | 73 | if response: 74 | table = response.findAll('div', {'class': 'code'}) 75 | if table: 76 | for key in table: 77 | match = re.search(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', str(key.text)) 78 | if match.group() == ip_address: 79 | return True 80 | else: 81 | return False 82 | 83 | ############################ 84 | 85 | class Feodo(object): 86 | 87 | def __init__(self): 88 | # get helping functions 89 | self.api = helpers.Common() 90 | 91 | # static station settings 92 | self.station_name = 'Feodo' 93 | self.endpoint = 'https://feodotracker.abuse.ch/blocklist/?download=ipblocklist' 94 | 95 | def ipv4_to_blacklist(self, ip_address): 96 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get') 97 | 98 | 99 | match = re.findall(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', response.text) 100 | for i in match: 101 | if ip_address == str(i): 102 | return True 103 | return False 104 | 105 | ############################ 106 | 107 | class Malc0de(object): 108 | 109 | def __init__(self): 110 | # get helping functions 111 | self.api = helpers.Common() 112 | 113 | # static station settings 114 | self.station_name = 'Malc0de' 115 | self.endpoint = 'http://malc0de.com/bl/IP_Blacklist.txt' 116 | 117 | def ipv4_to_blacklist(self, ip_address): 118 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get') 119 | 120 | 121 | match = re.findall(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', response.text) 122 | for i in match: 123 | if ip_address == str(i): 124 | return True 125 | return False 126 | 127 | ############################ 128 | 129 | class Zeustracker(object): 130 | 131 | def __init__(self): 132 | # get helping functions 133 | self.api = helpers.Common() 134 | 135 | # static station settings 136 | self.station_name = 'Zeustracker' 137 | self.endpoint = 'https://zeustracker.abuse.ch/blocklist.php?download=ipblocklist' 138 | 139 | def ipv4_to_blacklist(self, ip_address): 140 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get') 141 | 142 | 143 | match = re.findall(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', response.text) 144 | for i in match: 145 | if ip_address == str(i): 146 | return True 147 | return False 148 | 149 | class Mcafee(object): 150 | 151 | def __init__(self): 152 | # get helping functions 153 | self.api = helpers.Common() 154 | 155 | # static station settings 156 | self.station_name = 'McAfee' 157 | self.endpoint = 'http://www.siteadvisor.com/sites/' 158 | self.response_format = 'bs' 159 | 160 | def to_blacklist(self, search_value): 161 | url_path = search_value 162 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 163 | url_path=url_path, response_format=self.response_format) 164 | 165 | 166 | if response: 167 | table = response.findAll('p', {'class': 'intro'}) 168 | if table: 169 | for key in table: 170 | match = re.search(r'dangerous', str(key.text)) 171 | match_2 = re.search(r'suspicious', str(key.text)) 172 | 173 | if match or match_2: 174 | return True 175 | else: 176 | return False 177 | 178 | def ipv4_to_blacklist(self, ip_address): 179 | return self.to_blacklist(ip_address) 180 | 181 | def domain_to_blacklist(self, domain_name): 182 | return self.to_blacklist(domain_name) 183 | 184 | if __name__ == '__main__': 185 | test_ip = '8.8.8.8' 186 | test_domain = 'abc.com' 187 | #print 'ipv4', Blacklist().ipv4_blacklist_check(test_ip) 188 | #print 'domain', Blacklist().domain_blacklist_check(test_domain) -------------------------------------------------------------------------------- /stations/threatcrowd/station.py: -------------------------------------------------------------------------------- 1 | ################################################################# 2 | # Threatcrowd station for QRadio # 3 | # ~ Tune In # 4 | # Tuned to: # 5 | # http://www.threatcrowd.org/ API v2 # 6 | # # 7 | # API Documentation: # 8 | # https://github.com/threatcrowd/ApiV2 # 9 | # # 10 | # Author: 10TOHH # 11 | # # 12 | # Tunes: # 13 | # domain_to_ipv4 - Resolves IP to # 14 | # domain_to_hash - Return Hash to # 15 | # ipv4_to_domain - Resolves Domain to # 16 | # ipv4_to_hash - Return Hash associated with # 17 | # hash_to_ipv4 - Return IPv4 associated with # 18 | # hash_to_domain - Return Domain associated with # 19 | # hash_to_url - Return URL to report for given # 20 | ################################################################# 21 | 22 | from lib import helpers 23 | 24 | class Threatcrowd(object): 25 | 26 | def __init__(self): 27 | # lists of values that can be returned 28 | self.ip_list = [] 29 | self.domain_list = [] 30 | self.hash_list = [] 31 | self.url_list = [] 32 | self.score_list = [] 33 | self.imphash_list = [] 34 | 35 | # get helping functions 36 | self.api = helpers.Common() 37 | 38 | # static station settings 39 | self.station_name = 'Threatcrowd' 40 | self.endpoint = 'http://www.threatcrowd.org/searchApi/v2' 41 | self.path = '' 42 | self.parameters = {} 43 | self.headers = {'content-type': 'application/json', 44 | 'accept': 'application/json'} 45 | self.user_agent = {} 46 | self.response_format = 'json' 47 | 48 | ### Station tunes 49 | 50 | def domain_to_ipv4(self, domain_name): 51 | self.path = '/domain/report/' 52 | self.parameters = {'domain': domain_name } 53 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 54 | data_to_send=None, url_path=self.path, parameters=self.parameters, 55 | headers=self.headers, user_agent=self.user_agent, 56 | response_format=self.response_format) 57 | if response: 58 | if 'resolutions' in response: 59 | for key in response['resolutions']: 60 | if key['ip_address'] != '-' and key['ip_address'] !='0.0.0.0': 61 | self.ip_list.append(key['ip_address']) 62 | return self.ip_list 63 | 64 | 65 | def domain_to_hash(self, domain_name): 66 | self.path = '/domain/report/' 67 | self.parameters = {'domain': domain_name } 68 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 69 | data_to_send=None, url_path=self.path, parameters=self.parameters, 70 | headers=self.headers, user_agent=self.user_agent, 71 | response_format=self.response_format) 72 | if response: 73 | if 'hashes' in response: 74 | for key in response['hashes']: 75 | self.hash_list.append(key) 76 | 77 | return self.hash_list 78 | 79 | def ipv4_to_domain(self, ip_address): 80 | self.path = '/ip/report/' 81 | self.parameters = {'ip': ip_address } 82 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 83 | data_to_send=None, url_path=self.path, parameters=self.parameters, 84 | headers=self.headers, user_agent=self.user_agent, 85 | response_format=self.response_format) 86 | if response: 87 | if 'resolutions' in response: 88 | for key in response['resolutions']: 89 | if key['domain'] != '-': 90 | self.domain_list.append(key['domain']) 91 | return self.domain_list 92 | 93 | def ipv4_to_hash(self, ip_address): 94 | self.path = '/ip/report/' 95 | self.parameters = {'ip': ip_address } 96 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 97 | data_to_send=None, url_path=self.path, parameters=self.parameters, 98 | headers=self.headers, user_agent=self.user_agent, 99 | response_format=self.response_format) 100 | if response: 101 | if 'hashes' in response: 102 | for key in response['hashes']: 103 | self.hash_list.append(key) 104 | 105 | return self.hash_list 106 | 107 | 108 | def hash_to_ipv4(self, hash_value): 109 | self.path = '/file/report/' 110 | self.parameters = {'resource': hash_value } 111 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 112 | data_to_send=None, url_path=self.path, parameters=self.parameters, 113 | headers=self.headers, user_agent=self.user_agent, 114 | response_format=self.response_format) 115 | if response: 116 | if 'ips' in response: 117 | for key in response['ips']: 118 | if key != '0.0.0.0': 119 | self.ip_list.append(key) 120 | return self.ip_list 121 | 122 | def hash_to_domain(self, hash_value): 123 | self.path = '/file/report/' 124 | self.parameters = {'resource': hash_value } 125 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 126 | data_to_send=None, url_path=self.path, parameters=self.parameters, 127 | headers=self.headers, user_agent=self.user_agent, 128 | response_format=self.response_format) 129 | if response: 130 | if 'domains' in response: 131 | for key in response['domains']: 132 | if key: 133 | self.domain_list.append(key) 134 | return self.domain_list 135 | 136 | def hash_to_url(self, hash_value): 137 | self.path = '/file/report/' 138 | self.parameters = {'resource': hash_value } 139 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 140 | data_to_send=None, url_path=self.path, parameters=self.parameters, 141 | headers=self.headers, user_agent=self.user_agent, 142 | response_format=self.response_format) 143 | if response: 144 | if 'permalink' in response: 145 | self.url_list.append(response['permalink']) 146 | return self.url_list 147 | 148 | 149 | if __name__ == '__main__': 150 | test_domain = 'trivika.com' 151 | test_ip = '198.57.201.75' 152 | test_hash = '7F0E135F76CEEB2EAF64E0B76186A8E2' 153 | ##print Threatcrowd().domain_to_ipv4(test_domain) 154 | ##print Threatcrowd().domain_to_hash(test_domain) 155 | ##print Threatcrowd().ipv4_to_domain(test_ip) 156 | ##print Threatcrowd().ipv4_to_hash(test_ip) 157 | ##print Threatcrowd().hash_to_ipv4(test_hash) 158 | ##print Threatcrowd().hash_to_domain(test_hash) 159 | ##print Threatcrowd().hash_to_url(test_hash) -------------------------------------------------------------------------------- /stations/threatexpert/station.py: -------------------------------------------------------------------------------- 1 | ################################################################# 2 | # Threatexpert station for QRadio # 3 | # ~ Tune In # 4 | # Tuned to: # 5 | # http://www.threatexpert.com/reports.aspx?find= # 6 | # # 7 | # Author: 10TOHH # 8 | # # 9 | # # 10 | # Tunes: # 11 | # domain_to_hash - Return Hash to # 12 | # # 13 | # ipv4_to_hash - Return Hash associated with # 14 | # # 15 | # hash_to_ipv4 - Return IP associated with # 16 | # hash_to_domain - Return Domain associated with # 17 | # hash_to_url - Return URL to report for given # 18 | # hash_to_score - Return Score to given # 19 | # # 20 | # imphash_to_hash - Return Hash associated with # 21 | # - Not in QRadio main # 22 | # mutex_to_hash - Return Hash associated with # 23 | # - Not in QRadio main # 24 | ################################################################# 25 | 26 | from lib import helpers 27 | import re 28 | 29 | class Threatexpert(object): 30 | 31 | def __init__(self): 32 | # lists of values that can be returned 33 | self.ip_list = [] 34 | self.domain_list = [] 35 | self.hash_list = [] 36 | self.url_list = [] 37 | self.score_list = [] 38 | self.imphash_list = [] 39 | 40 | # get helping functions 41 | self.api = helpers.Common() 42 | 43 | # static station settings 44 | self.station_name = 'Threatexpert' 45 | self.endpoint = 'http://www.threatexpert.com/reports.aspx' 46 | self.path = '' 47 | self.parameters = {} 48 | self.headers = {} 49 | self.user_agent = {} 50 | self.response_format = 'bs' 51 | 52 | ############################################ 53 | 54 | def splitter(self, url_list): 55 | output = [] 56 | if url_list: 57 | for key in url_list: 58 | output.append(key.split('=')[1]) 59 | return output 60 | 61 | def url_parser(self, response): 62 | url_list = [] 63 | # search for href of reports with no base 64 | lines = response.findAll('a', {'target': '_blank'}) # Search for results 65 | for link in lines: 66 | url_list.append('http://www.threatexpert.com/' + link.get('href')) 67 | # return URL to analyse 68 | return url_list 69 | 70 | 71 | def single_search(self, search_value, params=None): 72 | if search_value: 73 | parameters = {'find': search_value} 74 | if params: 75 | parameters.update(params) 76 | 77 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 78 | data_to_send=None, url_path=self.path, parameters=parameters, 79 | headers=self.headers, user_agent=self.user_agent, 80 | response_format=self.response_format) 81 | if response: 82 | parsed_url = self.url_parser(response) 83 | return parsed_url 84 | else: 85 | return [] 86 | 87 | def main_search(self, search_value): 88 | url_list = [] 89 | if search_value: 90 | self.parameters = {'find': search_value} 91 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 92 | data_to_send=None, url_path=self.path, parameters=self.parameters, 93 | headers=self.headers, user_agent=self.user_agent, 94 | response_format=self.response_format) 95 | 96 | if response: 97 | if response.findAll('table', {'align': 'center'}): # More than 1 page 98 | pages = response.findAll('td', {'class': 'page_btn'}) # find all other pages 99 | if pages: 100 | for number in pages[:-1]: # Exclude 'Next' from page numbers 101 | params = {'page': number.text} # set page number as parameter 102 | sub_response = self.single_search(search_value, params=params) 103 | if sub_response: 104 | url_list.extend(sub_response) 105 | return url_list 106 | else: 107 | return self.single_search(search_value) 108 | else: 109 | return url_list 110 | 111 | ### Station tunes 112 | 113 | def hash_to_url(self, hash_value): 114 | self.url_list.extend(self.main_search(hash_value)) 115 | return self.url_list 116 | 117 | def ipv4_to_hash(self, ip_address): 118 | url_list = self.main_search(ip_address) 119 | hash_list = self.splitter(url_list) 120 | hash_list.extend(hash_list) 121 | return hash_list 122 | 123 | def domain_to_hash(self, domain_name): 124 | url_list = self.main_search(domain_name) 125 | splited_list = self.splitter(url_list) 126 | return splited_list 127 | 128 | def imphash_to_hash(self, imphash): 129 | url_list = self.main_search(imphash) 130 | splited_list = self.splitter(url_list) 131 | return splited_list 132 | 133 | def mutex_to_hash(self, imphash): 134 | url_list = self.main_search(imphash) 135 | splited_list = self.splitter(url_list) 136 | return splited_list 137 | 138 | 139 | def hash_to_ipv4(self, hash_value): 140 | bs_result_list = [] 141 | url_list = self.main_search(hash_value) 142 | if url_list: 143 | for request_url in url_list: 144 | response = self.api.session_helper(station_name=self.station_name, method_type='get', endpoint=self.endpoint, 145 | response_format=self.response_format, go_to_url=request_url) 146 | if response: 147 | bs_result_list.append(response) 148 | if bs_result_list: 149 | for li in bs_result_list: 150 | line = li.findAll('li') # Search for list items 151 | for i in line: 152 | match = re.findall(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', i.text) # IP address 153 | if match: 154 | self.ip_list.extend(match) 155 | for elements in bs_result_list: 156 | tables = elements.findAll('table', {'class': 'tbl'}) # Search all tables with data 157 | match = re.findall(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', str(tables)) # IP address 158 | if match: 159 | self.ip_list.extend(match) 160 | 161 | return list(set(self.ip_list)) 162 | 163 | def hash_to_domain(self, hash_value): 164 | bs_result_list = [] 165 | table_list = [] 166 | url_list = self.main_search(hash_value) 167 | if url_list: 168 | for request_url in url_list: 169 | response = self.api.session_helper(station_name=self.station_name, method_type='get', endpoint=self.endpoint, 170 | response_format=self.response_format, go_to_url=request_url) 171 | if response: 172 | bs_result_list.append(response) 173 | 174 | 175 | for elements in bs_result_list: 176 | tables = elements.findAll('table', {'class': 'tbl'}) # Search all tables with data 177 | table_list.extend(tables) # get tables in the list 178 | 179 | for elem in table_list: 180 | host = elem.findAll('td', {'class': 'cell_1'}) # Search all cells 181 | for i in host: 182 | match = re.search(r'(([a-z0-9]+\.)*[a-z0-9]+\.[a-z]+[0-9]+)', i.text) # Search for domains 183 | if match: 184 | dom = match.group().strip('1234567890.') # Remove port number from domain name string 185 | self.domain_list.append(dom) # Create list of domains with ports in the end 186 | return list(set(self.domain_list)) 187 | 188 | if __name__ == '__main__': 189 | test_ip = '88.198.69.43' 190 | test_domain = 'google.com' 191 | test_hash = '171c4c62ab2001c2f2394c3ec021dfa3' 192 | test_imphash = '' 193 | test_mutex = '' 194 | #print 'hash_to_url', Threatexpert().hash_to_url(test_hash) - Not in qradio 195 | ##print 'ipv4_to_hash', Threatexpert().ipv4_to_hash(test_ip) 196 | ##print 'domain_to_hash', Threatexpert().domain_to_hash(test_domain) 197 | #print 'imphash_to_hash', Threatexpert().imphash_to_hash(test_imphash) - Not in qradio 198 | #print 'mutex_to_hash', Threatexpert().mutex_to_hash(test_mutex) - Not in qradio 199 | ##print 'hash_to_ipv4', Threatexpert().hash_to_ipv4(test_hash) 200 | ##print 'hash_to_domain', Threatexpert().hash_to_domain(test_hash) 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /stations/malwr/station.py: -------------------------------------------------------------------------------- 1 | ################################################################# 2 | # Malwr station for QRadio # 3 | # ~ Tune In # 4 | # Tuned to: # 5 | # https://malwr.com/ # 6 | # # 7 | # Cloned from: # 8 | # https://github.com/PaulSec/API-malwr.com # 9 | # # 10 | # Author: 10TOHH # 11 | # # 12 | # Note: Does NOT use /lib/helpers.py.Common().session_helper # 13 | # # 14 | # Tunes: # 15 | # domain_to_hash - Return Hash to # 16 | # domain_to_score - Return Score to # 17 | # domain_to_url - Return URL to report for given # 18 | # # 19 | # ipv4_to_score - Return Score to # 20 | # ipv4_to_hash - Return Hash associated with # 21 | # ipv4_to_url - Return URL to report for given # 22 | # # 23 | # hash_to_ipv4 - Return IP associated with # 24 | # hash_to_imphash - Return Imphash associated with # 25 | # hash_to_url - Return URL to report for given # 26 | # hash_to_score - Return Score to given # 27 | ################################################################# 28 | 29 | from lib import config, helpers 30 | 31 | import requests 32 | from bs4 import BeautifulSoup 33 | 34 | 35 | class Malwr(object): 36 | logged = False 37 | 38 | url = "https://malwr.com" 39 | headers = { 40 | 'User-Agent': "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:41.0) " + 41 | "Gecko/20100101 Firefox/41.0" 42 | } 43 | 44 | def __init__(self): 45 | # lists of values that can be returned 46 | self.ip_list = [] 47 | self.domain_list = [] 48 | self.hash_list = [] 49 | self.url_list = [] 50 | self.score_list = [] 51 | self.imphash_list = [] 52 | self.drop_hash = [] 53 | 54 | self.error_log = helpers.IO() 55 | self.station_name = 'Malwr' 56 | 57 | self.session = requests.session() 58 | 59 | # Authenticate and store the session 60 | if config.malwr_login and config.malwr_passwd: 61 | soup = self.request_to_soup(self.url + '/account/login') 62 | if soup: 63 | csrf_input = soup.find(attrs=dict(name='csrfmiddlewaretoken')) 64 | if csrf_input: 65 | if 'value' in csrf_input: 66 | csrf_token = csrf_input['value'] 67 | payload = { 68 | 'csrfmiddlewaretoken': csrf_token, 69 | 'username': u'{0}'.format(config.malwr_login), 70 | 'password': u'{0}'.format(config.malwr_passwd) 71 | } 72 | else: 73 | return 74 | try: 75 | login_request = self.session.post("https://malwr.com/account/login/", 76 | data=payload, headers=self.headers) 77 | 78 | if login_request.status_code == 200: 79 | self.logged = True 80 | except: 81 | return 82 | else: 83 | error_msg = 'Login and Password NOT provided' 84 | self.error_log.error_log(error_msg, self.station_name) 85 | return 86 | 87 | 88 | def request_to_soup(self, url=None): 89 | 90 | if url: 91 | try: 92 | req = self.session.get(url, headers=self.headers) 93 | soup = BeautifulSoup(req.content, "html.parser") 94 | return soup 95 | except: 96 | return 97 | 98 | def search(self, search_word): 99 | # Do nothing if not logged in 100 | if not self.logged: 101 | return [] 102 | 103 | search_url = self.url + '/analysis/search/' 104 | c = self.request_to_soup(search_url) 105 | 106 | csrf_input = c.find(attrs=dict(name='csrfmiddlewaretoken')) 107 | csrf_token = csrf_input['value'] 108 | payload = { 109 | 'csrfmiddlewaretoken': csrf_token, 110 | 'search': u'{}'.format(search_word) 111 | } 112 | sc = self.session.post(search_url, data=payload, headers=self.headers) 113 | ssc = BeautifulSoup(sc.content, "html.parser") 114 | 115 | res = [] 116 | submissions = ssc.findAll('div', {'class': 'box-content'}) 117 | if len(submissions) > 0: 118 | submissions = submissions[0] 119 | sub = submissions.findAll('tbody')[0] 120 | for submission in sub.findAll('tr'): 121 | infos = submission.findAll('td') 122 | infos_to_add = { 123 | 'submission_time': infos[0].string, # Date 124 | 'hash': infos[1].find('a').string, # MD5 Hash 125 | 'submission_url': self.url+infos[1].find('a')['href'], # URI 126 | 'score': infos[4].string, #Score 127 | } 128 | res.append(infos_to_add) 129 | return res 130 | #################################################################################### 131 | def ip_enrich(self, ip_address): 132 | res = Malwr().search('ip:'+ip_address) 133 | return res 134 | 135 | def domain_enrich(self, domain_name): 136 | res = Malwr().search('domain:'+domain_name) 137 | return res 138 | 139 | ################################################################################################ 140 | 141 | def ipv4_to_hash(self, ip_address): 142 | res = Malwr().ip_enrich(ip_address) 143 | for element in res: 144 | self.hash_list.append(element['hash']) 145 | return self.hash_list 146 | 147 | 148 | def ipv4_to_url(self, ip_address): 149 | res = Malwr().ip_enrich(ip_address) 150 | for element in res: 151 | self.url_list.append(element['submission_url']) 152 | return self.url_list 153 | 154 | def ipv4_to_score(self, ip_address): 155 | res = Malwr().ip_enrich(ip_address) 156 | for element in res: 157 | if element['score'] != 'n/a': 158 | self.score_list.append(element['score']) 159 | return self.score_list 160 | 161 | ################################################################################################ 162 | 163 | def domain_to_hash(self, domain_name): 164 | res = Malwr().domain_enrich(domain_name) 165 | if res: 166 | for element in res: 167 | self.hash_list.append(element['hash']) 168 | return self.hash_list 169 | 170 | def domain_to_url(self, domain_name): 171 | res = Malwr().domain_enrich(domain_name) 172 | if res: 173 | for element in res: 174 | self.url_list.append(element['submission_url']) 175 | return self.url_list 176 | 177 | def domain_to_score(self, domain_name): 178 | res = Malwr().domain_enrich(domain_name) 179 | if res: 180 | for element in res: 181 | if element['score'] != 'n/a': 182 | self.score_list.append(element['score']) 183 | return self.score_list 184 | 185 | ################################################################################################ 186 | 187 | def hash_to_score(self, hash_value): 188 | res = Malwr().search(hash_value) 189 | if res: 190 | for element in res: 191 | if element['score'] != 'n/a': 192 | self.score_list.append(element['score']) 193 | return self.score_list 194 | 195 | def hash_to_url(self, hash_value): 196 | res = Malwr().search(hash_value) 197 | if res: 198 | for element in res: 199 | self.url_list.append(element['submission_url']) 200 | return self.url_list 201 | 202 | 203 | def hash_to_ipv4(self, hash_value): 204 | uri = [] 205 | res = Malwr().search(hash_value) 206 | if res: 207 | for element in res: 208 | uri.append(element['submission_url']) 209 | for value in uri: 210 | res = Malwr().request_to_soup(value) 211 | submissions = res.findAll('section', {'id': 'hosts'})[0] 212 | for elements in submissions.findAll('td'): 213 | self.ip_list.append(elements.string) 214 | return list(set(self.ip_list)) 215 | 216 | 217 | def hash_to_imphash(self, hash_value): 218 | uri = [] 219 | res = Malwr().search(hash_value) 220 | imphash_value = '' 221 | if res: 222 | for element in res: 223 | uri.append(element['submission_url']) 224 | for value in uri: 225 | results = Malwr().request_to_soup(value) 226 | submissions = results.findAll('section', {'id': 'static_analysis'}) 227 | if submissions: 228 | submissions = submissions[0] 229 | imphash_value = submissions.findAll('div', {'class': 'well'}) 230 | if imphash_value: 231 | imphash_value = imphash_value[0].string 232 | self.imphash_list.append(hash_value) 233 | return list(set(self.imphash_list)) 234 | 235 | 236 | if __name__ == '__main__': 237 | 238 | test_ip = '146.255.37.1' 239 | test_domain = 'yandex.ru' 240 | ##print Malwr().ipv4_to_score(test_ip) 241 | ##print Malwr().ipv4_to_hash(test_ip) 242 | ##print Malwr().ipv4_to_url(test_ip) 243 | 244 | ##print Malwr().domain_to_hash(test_domain) 245 | ##print Malwr().domain_to_score(test_domain) 246 | ##print Malwr().domain_to_url(test_domain) 247 | 248 | ##print Malwr().hash_to_ipv4('a871e4d2dd2aa51da4ca863739b51ccb') 249 | ##print Malwr().hash_to_imphash('a871e4d2dd2aa51da4ca863739b51ccb') 250 | ##print Malwr().hash_to_url('a871e4d2dd2aa51da4ca863739b51ccb') 251 | ##print Malwr().hash_to_score('a871e4d2dd2aa51da4ca863739b51ccb') 252 | -------------------------------------------------------------------------------- /stations/totalhash/station.py: -------------------------------------------------------------------------------- 1 | ################################################################# 2 | # Totalhash station for QRadio # 3 | # ~ Tune In # 4 | # Tuned to: # 5 | # https://totalhash.cymru.com/ # 6 | # # 7 | # API Documentation: # 8 | # https://totalhash.cymru.com/wp-content/uploads/2015/06/ # 9 | # Totalhash-API-Documentation.pdf # 10 | # # 11 | # Author: 10TOHH # 12 | # # 13 | # # 14 | # Tunes: # 15 | # domain_to_hash - Return Hash to # 16 | # domain_to_url - Return URL to report for given # 17 | # # 18 | # ipv4_to_hash - Return Hash associated with # 19 | # ipv4_to_url - Return URL to report for given # 20 | # # 21 | # hash_to_ipv4 - Return IP associated with # 22 | # hash_to_imphash - Return Imphash associated with # 23 | # hash_to_url - Return URL to report for given # 24 | # hash_to_domain - Return Domain associated with # 25 | # # 26 | # imphash_to_hash - Return Hash associated with # 27 | # mutex_to_hash - Return Hash associated with # 28 | ################################################################# 29 | 30 | import hmac 31 | import hashlib 32 | import xmltodict 33 | import re 34 | 35 | from lib import config, helpers 36 | 37 | class Totalhash(object): 38 | 39 | def __init__(self): 40 | # lists of values that can be returned 41 | self.ip_list = [] 42 | self.domain_list = [] 43 | self.hash_list = [] 44 | self.url_list = [] 45 | self.score_list = [] 46 | self.imphash_list = [] 47 | 48 | # get helping functions 49 | self.api = helpers.Common() 50 | self.error_log = helpers.IO() 51 | 52 | # static station settings 53 | self.station_name = 'Totalhash' 54 | self.endpoint = 'https://api.totalhash.com/search/' 55 | self.path = '' 56 | self.parameters = {} 57 | self.headers = {} 58 | self.user_agent = {} 59 | self.response_format = '' 60 | 61 | def signature(self, searching_query): 62 | if config.totalhash_api_key: 63 | api_key = config.totalhash_api_key 64 | else: 65 | error_msg = 'API Key NOT provided' 66 | self.error_log.error_log(error_msg, self.station_name) 67 | return 68 | 69 | sign = hmac.new(api_key, searching_query, hashlib.sha256).hexdigest() 70 | return sign 71 | 72 | def search(self, searching_query): 73 | if config.totalhash_uid: 74 | uid = config.totalhash_uid 75 | self.path = searching_query+'&id='+uid+'&sign='+self.signature(searching_query) 76 | else: 77 | error_msg = 'User Name NOT provided' 78 | self.error_log.error_log(error_msg, self.station_name) 79 | return 80 | 81 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 82 | data_to_send=None, url_path=self.path, parameters=self.parameters, 83 | headers=self.headers, user_agent=self.user_agent, 84 | response_format=self.response_format) 85 | if response: 86 | return response 87 | 88 | def parser(self, response): 89 | if response: 90 | match = re.findall(r'API limit', response.text) # 91 | if match: 92 | self.error_log.error_log('Sorry API Limit reached (300 q/month). Get new: https://totalhash.cymru.com/contact-us/', 93 | self.station_name) 94 | return [] 95 | 96 | xml_dict = xmltodict.parse(response.text) 97 | if 'response' in xml_dict: 98 | if 'result' in xml_dict['response']: 99 | if 'doc' in xml_dict['response']['result']: 100 | try: 101 | response = xml_dict['response']['result'] # Unpacking xmltodict 102 | for records in response['doc']: 103 | self.hash_list.append(records['str']['#text']) # Append hash_values 104 | except: pass 105 | return self.hash_list # Return empty 106 | 107 | 108 | def combinator(self, searching_query): 109 | hash_list = [] 110 | hash_list.extend(self.parser(self.search(searching_query))) 111 | return hash_list 112 | 113 | ### Station tunes 114 | 115 | def ipv4_to_hash(self, ip_address): 116 | searching_query = 'ip:' + ip_address 117 | self.hash_list.extend(self.parser(self.search(searching_query))) 118 | return self.hash_list 119 | 120 | def ipv4_to_url(self, ip_address): 121 | hash = self.ipv4_to_hash(ip_address) 122 | if len(hash) > 0: 123 | for i in hash: 124 | self.url_list.append('https://totalhash.cymru.com/analysis/?'+i) 125 | return self.url_list 126 | ################### 127 | def domain_to_hash(self, domain_name): 128 | searching_query = 'dnsrr:' + domain_name 129 | self.hash_list.extend(self.parser(self.search(searching_query))) 130 | return self.hash_list 131 | 132 | def domain_to_url(self, domain_name): 133 | hash = self.domain_to_hash(domain_name) 134 | if len(hash) > 0: 135 | for i in hash: 136 | self.url_list.append('https://totalhash.cymru.com/analysis/?'+i) 137 | return self.url_list 138 | 139 | ################### 140 | 141 | def imphash_to_hash(self, imphash): 142 | searching_query = 'hash:' + imphash 143 | self.hash_list.extend(self.parser(self.search(searching_query))) 144 | return self.hash_list 145 | 146 | def mutex_to_hash(self, mutex): 147 | searching_query = 'mutex:' + mutex 148 | self.hash_list.extend(self.parser(self.search(searching_query))) 149 | return self.hash_list 150 | 151 | ################### 152 | 153 | def hash_to_ipv4(self, hash_value): 154 | request_url = 'https://totalhash.cymru.com/analysis/?' + hash_value 155 | response = self.api.session_helper(station_name=self.station_name, method_type='get', endpoint=self.endpoint, 156 | response_format='bs', go_to_url=request_url) 157 | 158 | if response: 159 | table = response.findAll('a') 160 | for cell in table: 161 | match = re.search(r'ip:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', str(cell)) 162 | if match: 163 | ip_address = match.group().split(':')[1] 164 | self.ip_list.append(ip_address) 165 | return list(set(self.ip_list)) 166 | 167 | 168 | def hash_to_url(self, hash_value): 169 | request_url = 'https://totalhash.cymru.com/analysis/?' + hash_value 170 | response = self.api.session_helper(station_name=self.station_name, method_type='get', endpoint=self.endpoint, 171 | response_format='bs', go_to_url=request_url) 172 | 173 | if response: 174 | table = response.findAll('div', {'class': 'entry-content'}) 175 | if table: 176 | match = re.search(r'Sorry something went wrong', table[0].text) 177 | if not match: 178 | self.url_list.append(request_url) 179 | return self.url_list 180 | 181 | def hash_to_domain(self, hash_value): 182 | request_url = 'https://totalhash.cymru.com/analysis/?' + hash_value 183 | response = self.api.session_helper(station_name=self.station_name, method_type='get', endpoint=self.endpoint, 184 | response_format='bs', go_to_url=request_url) 185 | 186 | if response: 187 | table = response.findAll('a') 188 | for cell in table: 189 | match = re.search(r'dnsrr:.*\"\>', str(cell)) 190 | if match: 191 | split_1 = match.group().split(':')[1] 192 | split_2 = split_1.split('"')[0] 193 | split_3 = split_2.split('.')[-1] 194 | if not split_3.isdigit(): 195 | self.domain_list.append(split_2) 196 | return list(set(self.domain_list)) 197 | 198 | def hash_to_imphash(self, hash_value): 199 | request_url = 'https://totalhash.cymru.com/analysis/?' + hash_value 200 | response = self.api.session_helper(station_name=self.station_name, method_type='get', endpoint=self.endpoint, 201 | response_format='bs', go_to_url=request_url) 202 | 203 | if response: 204 | table = response.findAll('span', {'class': 'fixed'}) 205 | for cell in table: 206 | match = re.search(r'hash:.*\"\>', str(cell)) 207 | if match: 208 | split_1 = match.group().split(':')[1] 209 | split_2 = split_1.split('"')[0] 210 | if len(split_2) == 32: 211 | self.imphash_list.append(split_2) 212 | return self.imphash_list 213 | 214 | 215 | if __name__ == '__main__': 216 | test_ip = '64.182.208.185' 217 | test_domain = 'yaplakal.com' 218 | test_imphash = '111e1a29238f230f1857b5836aa533d6' 219 | test_mutex = 'WininetConnectionMutex' 220 | test_hash = 'f4c029619efd51dfc51f39e5cad4dfb39147c851' 221 | #print 'ipv4_to_hash', Totalhash().ipv4_to_hash(test_ip) 222 | #print 'ipv4_to_url', Totalhash().ipv4_to_url(test_ip) 223 | 224 | #print 'domain_to_hash', Totalhash().domain_to_hash(test_domain) 225 | #print 'domain_to_url', Totalhash().domain_to_url(test_domain) 226 | 227 | #print 'imphash_to_hash', Totalhash().imphash_to_hash(test_imphash) 228 | #print 'mutex_to_hash', Totalhash().mutex_to_hash(test_mutex) 229 | 230 | #print 'hash_to_ipv4', Totalhash().hash_to_ipv4(test_hash) 231 | #print 'hash_to_imphash', Totalhash().hash_to_imphash(test_hash) 232 | #print 'hash_to_domain', Totalhash().hash_to_domain(test_hash) 233 | #print 'hash_to_url', Totalhash().hash_to_url(test_hash) 234 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /stations/ibmxforceex/station.py: -------------------------------------------------------------------------------- 1 | ################################################################# 2 | # Ibmxforce station for QRadio # 3 | # ~ Tune In # 4 | # Tuned to: # 5 | # https://exchange.xforce.ibmcloud.com # 6 | # # 7 | # API Documentation: # 8 | # https://xforce-api.mybluemix.net/doc/ # 9 | # # 10 | # Author: 10TOHH # 11 | # # 12 | # Tunes: # 13 | # domain_to_ipv4 - Resolves IP to # 14 | # domain_to_hash - Return Hash to # 15 | # domain_to_score - Return Score to # 16 | # # 17 | # ipv4_to_domain - Resolves Domain to # 18 | # ipv4_to_score - Return Score to # 19 | # ipv4_to_hash - Return Hash associated with # 20 | # # 21 | # hash_to_ipv4 - Return IP associated with # 22 | # hash_to_score - Return Score to given # 23 | ################################################################# 24 | 25 | from lib import config, helpers 26 | 27 | 28 | class Ibmxforce(object): 29 | 30 | def __init__(self): 31 | # lists of values that can be returned 32 | self.ipv4_list = [] 33 | self.ipv6_list = [] 34 | self.domain_list = [] 35 | self.hash_list = [] 36 | self.url_list = [] 37 | self.score_list = [] 38 | self.imphash_list = [] 39 | 40 | # get helping functions 41 | self.api = helpers.Common() 42 | 43 | # static station settings 44 | self.station_name = 'IBM X-Force' 45 | self.endpoint = 'https://xforce-api.mybluemix.net:443' 46 | self.url_path = '' 47 | self.parameters = {} 48 | self.headers = {'Accept': 'application/json'} 49 | self.user_agent = {} 50 | self.response_format = 'json' 51 | 52 | if config.ibmxforce_token: 53 | self.headers.update({'Authorization': 'Bearer ' + config.ibmxforce_token}) 54 | else: 55 | self.token = self.get_token() 56 | 57 | # return anonymous authenication token 58 | def get_token(self): 59 | self.url_path = '/auth/anonymousToken' 60 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 61 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 62 | headers=self.headers, user_agent=self.user_agent, 63 | response_format=self.response_format) 64 | if response: 65 | return self.headers.update({'Authorization': 'Bearer ' + response['token']}) 66 | 67 | # Resolve IP or Domain names 68 | def resolver(self, ip_address): 69 | domain_list = [] 70 | ipv4_list = [] 71 | 72 | self.url_path = '/resolve/' + ip_address 73 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 74 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 75 | headers=self.headers, user_agent=self.user_agent, 76 | response_format=self.response_format) 77 | # Domain 78 | if response: 79 | if 'Passive' in response: 80 | if 'records' in response['Passive']: 81 | for key in response['Passive']['records']: 82 | domain_list.append(key['value']) 83 | # IP 84 | elif 'A' in response: 85 | ipv4_list.extend(response['A']) 86 | 87 | return domain_list or ipv4_list 88 | 89 | def malware_hash_parser(self, json_input): 90 | ipv4_list = [] 91 | 92 | ## Find IP to hash 93 | if json_input: 94 | if 'malware' in json_input: 95 | if 'origins' in json_input['malware']: 96 | if 'subjects' in json_input['malware']['origins']: 97 | if 'rows' in json_input['malware']['origins']['subjects']: 98 | for key in json_input['malware']['origins']['subjects']['rows']: 99 | ipv4_list.extend(key['ips']) 100 | if 'CnCServers' in json_input['malware']['origins']: 101 | if 'rows' in json_input['malware']['origins']['CnCServers']: 102 | for key in json_input['malware']['origins']['CnCServers']['rows']: 103 | ipv4_list.append(key['ip']) 104 | if 'downloadServers' in json_input['malware']['origins']: 105 | if 'rows' in json_input['malware']['origins']['downloadServers']: 106 | for key in json_input['malware']['origins']['downloadServers']['rows']: 107 | ipv4_list.append(key['ip']) 108 | if 'emails' in json_input['malware']['origins']: 109 | if 'rows' in json_input['malware']['origins']['emails']: 110 | for key in json_input['malware']['origins']['emails']['rows']: 111 | ipv4_list.append(key['ip']) 112 | return ipv4_list 113 | 114 | 115 | def score_json_parser(self,json_input): 116 | score_list = [] 117 | if json_input: 118 | if 'malware' in json_input: 119 | if 'origins' in json_input['malware']: 120 | if 'external' in json_input['malware']['origins']: 121 | if 'detectionCoverage' in json_input['malware']['origins']['external']: 122 | hash_score = str(round(json_input['malware']['origins']['external']['detectionCoverage'], -1))[0] +'/10' 123 | score_list.append(hash_score) 124 | elif 'result' in json_input: 125 | if 'score' in json_input['result']: 126 | score = str(json_input['result']['score'])+'/10' 127 | score_list.append(score) 128 | return score_list 129 | 130 | ### Station tunes 131 | 132 | def domain_to_ipv4(self, domain_name): 133 | ipv4_list = list(self.resolver(domain_name)) 134 | return ipv4_list 135 | 136 | def domain_to_hash(self, domain_name): 137 | self.url_path = '/url/malware/' + domain_name 138 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 139 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 140 | headers=self.headers, user_agent=self.user_agent, 141 | response_format=self.response_format) 142 | # Find Hash 143 | if response: 144 | if response['count'] != 0: 145 | for key in response['malware']: 146 | self.hash_list.append(key['md5']) 147 | return self.hash_list 148 | 149 | 150 | def domain_to_score(self, domain_name): 151 | score_list = [] 152 | self.url_path = '/url/' + domain_name 153 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 154 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 155 | headers=self.headers, user_agent=self.user_agent, 156 | response_format=self.response_format) 157 | if response: 158 | score_list.extend(self.score_json_parser(response)) 159 | return score_list 160 | 161 | 162 | 163 | ################################# 164 | def ipv4_to_domain(self, ip_address): 165 | self.domain_list.extend(self.resolver(ip_address)) 166 | return self.domain_list 167 | 168 | 169 | def ipv4_to_hash(self, ip_address): 170 | self.url_path = '/ipr/malware/' + ip_address 171 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 172 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 173 | headers=self.headers, user_agent=self.user_agent, 174 | response_format=self.response_format) 175 | if response: 176 | self.hash_list.extend(self.malware_hash_parser(response)) 177 | return self.hash_list 178 | 179 | def ipv4_to_score(self, ip_address): 180 | score_list = [] 181 | self.url_path = '/ipr/' + ip_address 182 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 183 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 184 | headers=self.headers, user_agent=self.user_agent, 185 | response_format=self.response_format) 186 | if response: 187 | if 'score' in response: 188 | score_list.append(str(response['score'])+'/10') 189 | return score_list 190 | 191 | 192 | ################################# 193 | 194 | 195 | def hash_to_ipv4(self, hash_value): 196 | ipv4_list = [] 197 | self.url_path = '/malware/' + hash_value 198 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 199 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 200 | headers=self.headers, user_agent=self.user_agent, 201 | response_format=self.response_format) 202 | if response: 203 | ipv4_list.extend(self.malware_hash_parser(response)) 204 | return ipv4_list 205 | 206 | def hash_to_score(self, hash_value): 207 | score_list = [] 208 | self.url_path = '/malware/' + hash_value 209 | response = self.api.session_helper(station_name=self.station_name, endpoint=self.endpoint, method_type='get', 210 | data_to_send=None, url_path=self.url_path, parameters=self.parameters, 211 | headers=self.headers, user_agent=self.user_agent, 212 | response_format=self.response_format) 213 | if response: 214 | score_list.extend(self.score_json_parser(response)) 215 | return score_list 216 | 217 | 218 | ##### MAIN ##### 219 | if __name__ == '__main__': 220 | ibm = Ibmxforce() 221 | test_ip = '198.57.201.75' 222 | test_domain = 'trivika.com' 223 | test_hash = '474B9CCF5AB9D72CA8A333889BBB34F0' 224 | 225 | ##print 'dom-to-ip', ibm.domain_to_ipv4(test_domain) 226 | ##print 'dom-to-hash', ibm.domain_to_hash(test_domain) 227 | ##print 'dom-to-score', ibm.domain_to_score(test_domain) 228 | 229 | ##print 'ip-to-dom', ibm.ipv4_to_domain(test_ip) 230 | ##print 'ip-to-has', ibm.ipv4_to_hash(test_ip) 231 | ##print 'ip-to-score', ibm.ipv4_to_score(test_ip) 232 | 233 | print 'hash-to-ip', ibm.hash_to_ipv4(test_hash) 234 | ##print 'hash-to-score', ibm.hash_to_score(test_hash) 235 | -------------------------------------------------------------------------------- /lib/qradio.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys, os 3 | sys.path.append(os.path.abspath(os.path.join('..'))) 4 | # Import all stations 5 | from stations.blacklists.station import * 6 | from stations.cymon.station import * 7 | from stations.fortiguard.station import * 8 | from stations.hostsfile.station import * 9 | from stations.ibmxforceex.station import * 10 | from stations.malwr.station import * 11 | from stations.metascan.station import * 12 | from stations.threatcrowd.station import * 13 | from stations.threatexpert.station import * 14 | from stations.totalhash.station import * 15 | from stations.virustotal.station import * 16 | import helpers 17 | 18 | 19 | ##### DOMAIN SECTION ###### 20 | class From_domain(object): 21 | def __init__(self, verbose=False): 22 | self.verbose = verbose 23 | self.ip_dict = {} 24 | self.hash_dict = {} 25 | self.score_dict = {} 26 | self.url_dict = {} 27 | self.blacklist_dict = {} 28 | 29 | ######################################## 30 | # Searches to IP 31 | def to_ipv4(self, search_value): 32 | if search_value: 33 | self.ip_dict.update({'Cymon': Cymon().domain_to_ipv4(search_value)}) 34 | self.ip_dict.update({'FortiGuard': Fortiguard().domain_to_ipv4(search_value)}) 35 | self.ip_dict.update({'Hostsfile': Hostsfile().domain_to_ipv4(search_value)}) 36 | self.ip_dict.update({'IBM': Ibmxforce().domain_to_ipv4(search_value)}) 37 | self.ip_dict.update({'Threatcrowd': Threatcrowd().domain_to_ipv4(search_value)}) 38 | self.ip_dict.update({'Virustotal': Virustotal().domain_to_ipv4(search_value)}) 39 | 40 | if self.verbose: 41 | return helpers.Common().verbose_output(search_value, From_domain.__name__, self.to_ipv4.__name__, 42 | self.ip_dict) 43 | else: 44 | return helpers.Common().nonverbose_output(search_value, From_domain.__name__, self.to_ipv4.__name__, 45 | self.ip_dict) 46 | 47 | ######################################## 48 | # Searches to HASH 49 | def to_hash(self, search_value): 50 | if search_value: 51 | self.hash_dict.update({'Malwr': Malwr().domain_to_hash(search_value)}) 52 | self.hash_dict.update({'IBM': Ibmxforce().domain_to_hash(search_value)}) 53 | self.hash_dict.update({'Threatexpert': Threatexpert().domain_to_hash(search_value)}) 54 | self.hash_dict.update({'Totalhash': Totalhash().domain_to_hash(search_value)}) 55 | self.hash_dict.update({'Threatcrowd': Threatcrowd().domain_to_hash(search_value)}) 56 | 57 | if self.verbose: 58 | return helpers.Common().verbose_output(search_value, From_domain.__name__, self.to_hash.__name__, 59 | self.hash_dict) 60 | else: 61 | return helpers.Common().nonverbose_output(search_value, From_domain.__name__, self.to_hash.__name__, 62 | self.hash_dict) 63 | 64 | ######################################## 65 | # Searches to SCORE 66 | def to_score(self, search_value): 67 | if search_value: 68 | self.score_dict.update({'Malwr': Malwr().domain_to_score(search_value)}) 69 | self.score_dict.update({'IBM': Ibmxforce().domain_to_score(search_value)}) 70 | 71 | if self.verbose: 72 | return helpers.Common().verbose_output(search_value, From_domain.__name__, self.to_score.__name__, 73 | self.score_dict) 74 | else: 75 | return helpers.Common().nonverbose_output(search_value, From_domain.__name__, self.to_score.__name__, 76 | self.score_dict) 77 | 78 | ######################################## 79 | # Searches to URL 80 | def to_url(self, search_value): 81 | if search_value: 82 | self.url_dict.update({'Malwr': Malwr().domain_to_url(search_value)}) 83 | self.url_dict.update({'Totalhash': Totalhash().domain_to_url(search_value)}) 84 | 85 | if self.verbose: 86 | return helpers.Common().verbose_output(search_value, From_domain.__name__, self.to_url.__name__, 87 | self.url_dict) 88 | else: 89 | return helpers.Common().nonverbose_output(search_value, From_domain.__name__, self.to_url.__name__, 90 | self.url_dict) 91 | 92 | ######################################## 93 | # Searches to BLACKLISTS 94 | def to_blacklist(self, search_value): 95 | if search_value: 96 | if self.verbose: 97 | self.blacklist_dict.update({'McAfee': Mcafee().domain_to_blacklist(search_value)}) 98 | 99 | return helpers.Common().verbose_output(search_value, From_domain.__name__, self.to_blacklist.__name__, 100 | self.blacklist_dict) 101 | else: 102 | return helpers.Common().nonverbose_output(search_value, From_domain.__name__, self.to_blacklist.__name__, 103 | str(Blacklist().domain_blacklist_check(search_value))) 104 | 105 | 106 | 107 | ##### IP SECTION ###### 108 | class From_ipv4(object): 109 | def __init__(self, verbose=False): 110 | self.verbose = verbose 111 | self.domain_dict = {} 112 | self.hash_dict = {} 113 | self.score_dict = {} 114 | self.url_dict = {} 115 | self.blacklist_dict = {} 116 | 117 | ######################################## 118 | # Searches to DOMAIN 119 | def to_domain(self, search_value): 120 | if search_value: 121 | self.domain_dict.update({'Cymon': Cymon().ipv4_to_domain(search_value)}) 122 | self.domain_dict.update({'Hostsfile': Hostsfile().ipv4_to_domain(search_value)}) 123 | self.domain_dict.update({'IBM': Ibmxforce().ipv4_to_domain(search_value)}) 124 | self.domain_dict.update({'Threatcrowd': Threatcrowd().ipv4_to_domain(search_value)}) 125 | self.domain_dict.update({'Virustotal': Virustotal().ipv4_to_domain(search_value)}) 126 | 127 | if self.verbose: 128 | return helpers.Common().verbose_output(search_value, From_ipv4.__name__, self.to_domain.__name__, 129 | self.domain_dict) 130 | else: 131 | return helpers.Common().nonverbose_output(search_value, From_ipv4.__name__, self.to_domain.__name__, 132 | self.domain_dict) 133 | 134 | ######################################## 135 | # Searches to HASH 136 | def to_hash(self, search_value): 137 | if search_value: 138 | self.hash_dict.update({'Cymon': Cymon().ipv4_to_hash(search_value)}) 139 | self.hash_dict.update({'Malwr': Malwr().ipv4_to_hash(search_value)}) 140 | self.hash_dict.update({'IBM': Ibmxforce().ipv4_to_hash(search_value)}) 141 | self.hash_dict.update({'Threatcrowd': Threatcrowd().ipv4_to_hash(search_value)}) 142 | self.hash_dict.update({'Threatexpert': Threatexpert().ipv4_to_hash(search_value)}) 143 | self.hash_dict.update({'Totalhash': Totalhash().ipv4_to_hash(search_value)}) 144 | self.hash_dict.update({'Virustotal': Virustotal().ipv4_to_hash(search_value)}) 145 | 146 | if self.verbose: 147 | return helpers.Common().verbose_output(search_value, From_ipv4.__name__, self.to_hash.__name__, 148 | self.hash_dict) 149 | else: 150 | return helpers.Common().nonverbose_output(search_value, From_ipv4.__name__, self.to_hash.__name__, 151 | self.hash_dict) 152 | 153 | ######################################## 154 | # Searches to SCORE 155 | def to_score(self, search_value): 156 | if search_value: 157 | self.score_dict.update({'Malwr': Malwr().ipv4_to_score(search_value)}) 158 | self.score_dict.update({'Metascan': Metascan().ipv4_to_score(search_value)}) 159 | self.score_dict.update({'IBM': Ibmxforce().ipv4_to_score(search_value)}) 160 | 161 | if self.verbose: 162 | return helpers.Common().verbose_output(search_value, From_ipv4.__name__, self.to_score.__name__, 163 | self.score_dict) 164 | else: 165 | return helpers.Common().nonverbose_output(search_value, From_ipv4.__name__, self.to_score.__name__, 166 | self.score_dict) 167 | 168 | ######################################## 169 | # Searches to URL 170 | def to_url(self, search_value): 171 | if search_value: 172 | self.url_dict.update({'Cymon': Cymon().ipv4_to_url(search_value)}) 173 | self.url_dict.update({'Malwr': Malwr().ipv4_to_url(search_value)}) 174 | self.url_dict.update({'Totalhash': Totalhash().ipv4_to_url(search_value)}) 175 | 176 | if self.verbose: 177 | return helpers.Common().verbose_output(search_value, From_ipv4.__name__, self.to_url.__name__, 178 | self.url_dict) 179 | else: 180 | return helpers.Common().nonverbose_output(search_value, From_ipv4.__name__, self.to_url.__name__, 181 | self.url_dict) 182 | 183 | ######################################## 184 | # Searches to BLACKLISTS 185 | def to_blacklist(self, search_value): 186 | if search_value: 187 | if self.verbose: 188 | self.blacklist_dict.update({'Asprox': Asprox().ipv4_to_blacklist(search_value)}) 189 | self.blacklist_dict.update({'Feodo': Feodo().ipv4_to_blacklist(search_value)}) 190 | self.blacklist_dict.update({'Malc0de': Malc0de().ipv4_to_blacklist(search_value)}) 191 | self.blacklist_dict.update({'Zeustracker': Zeustracker().ipv4_to_blacklist(search_value)}) 192 | self.blacklist_dict.update({'McAfee': Mcafee().ipv4_to_blacklist(search_value)}) 193 | 194 | return helpers.Common().verbose_output(search_value, From_ipv4.__name__, self.to_blacklist.__name__, 195 | self.blacklist_dict) 196 | else: 197 | return helpers.Common().nonverbose_output(search_value, From_ipv4.__name__, self.to_blacklist.__name__, 198 | str(Blacklist().ipv4_blacklist_check(search_value))) 199 | 200 | 201 | ##### HASH SECTION ###### 202 | class From_hash(object): 203 | def __init__(self, verbose=False): 204 | self.verbose = verbose 205 | self.ip_dict = {} 206 | self.score_dict = {} 207 | self.imphash_dict = {} 208 | self.url_dict = {} 209 | self.domain_dict = {} 210 | 211 | ######################################## 212 | # Searches to IP 213 | def to_ipv4(self, search_value): 214 | if search_value: 215 | self.ip_dict.update({'IBM': Ibmxforce().hash_to_ipv4(search_value)}) 216 | self.ip_dict.update({'Malwr': Malwr().hash_to_ipv4(search_value)}) 217 | self.ip_dict.update({'Threatexpert': Threatexpert().hash_to_ipv4(search_value)}) 218 | self.ip_dict.update({'Totalhash': Totalhash().hash_to_ipv4(search_value)}) 219 | self.ip_dict.update({'Threatcrowd': Threatcrowd().hash_to_ipv4(search_value)}) 220 | 221 | if self.verbose: 222 | return helpers.Common().verbose_output(search_value, From_hash.__name__, self.to_ipv4.__name__, 223 | self.ip_dict) 224 | else: 225 | return helpers.Common().nonverbose_output(search_value, From_hash.__name__, self.to_ipv4.__name__, 226 | self.ip_dict) 227 | 228 | ######################################## 229 | # Searches to DOMAIN 230 | def to_domain(self, search_value): 231 | if search_value: 232 | self.domain_dict.update({'Threatexpert': Threatexpert().hash_to_domain(search_value)}) 233 | self.domain_dict.update({'Totalhash': Totalhash().hash_to_domain(search_value)}) 234 | self.domain_dict.update({'Threatcrowd': Threatcrowd().hash_to_domain(search_value)}) 235 | 236 | 237 | if self.verbose: 238 | return helpers.Common().verbose_output(search_value, From_hash.__name__, self.to_domain.__name__, 239 | self.domain_dict) 240 | else: 241 | return helpers.Common().nonverbose_output(search_value, From_hash.__name__, self.to_domain.__name__, 242 | self.domain_dict) 243 | 244 | ######################################## 245 | # Searches to SCORE 246 | def to_score(self, search_value): 247 | if search_value: 248 | self.score_dict.update({'Malwr': Malwr().hash_to_score(search_value)}) 249 | self.score_dict.update({'Metascan': Metascan().hash_to_score(search_value)}) 250 | self.score_dict.update({'IBM': Ibmxforce().hash_to_score(search_value)}) 251 | self.score_dict.update({'Virustotal': Virustotal().hash_to_score(search_value)}) 252 | 253 | if self.verbose: 254 | return helpers.Common().verbose_output(search_value, From_hash.__name__, self.to_score.__name__, 255 | self.score_dict) 256 | else: 257 | return helpers.Common().nonverbose_output(search_value, From_hash.__name__, self.to_score.__name__, 258 | self.score_dict) 259 | 260 | ######################################## 261 | # Searches to URL 262 | def to_url(self, search_value): 263 | if search_value: 264 | self.url_dict.update({'Cymon': Cymon().hash_to_url(search_value)}) 265 | self.url_dict.update({'Malwr': Malwr().hash_to_url(search_value)}) 266 | self.url_dict.update({'Threatexpert': Threatexpert().hash_to_url(search_value)}) 267 | self.url_dict.update({'Totalhash': Totalhash().hash_to_url(search_value)}) 268 | self.url_dict.update({'Virustotal': Virustotal().hash_to_url(search_value)}) 269 | self.url_dict.update({'Threatcrowd': Threatcrowd().hash_to_url(search_value)}) 270 | 271 | if self.verbose: 272 | return helpers.Common().verbose_output(search_value, From_hash.__name__, self.to_url.__name__, 273 | self.url_dict) 274 | else: 275 | return helpers.Common().nonverbose_output(search_value, From_hash.__name__, self.to_url.__name__, 276 | self.url_dict) 277 | 278 | ######################################## 279 | # Searches to IMPHASH 280 | def to_imphash(self, search_value): 281 | if search_value: 282 | self.imphash_dict.update({'Malwr': Malwr().hash_to_imphash(search_value)}) 283 | self.imphash_dict.update({'Metascan': Metascan().hash_to_imphash(search_value)}) 284 | self.imphash_dict.update({'Totalhash': Totalhash().hash_to_imphash(search_value)}) 285 | 286 | if self.verbose: 287 | return helpers.Common().verbose_output(search_value, From_hash.__name__, self.to_imphash.__name__, 288 | self.imphash_dict) 289 | else: 290 | return helpers.Common().nonverbose_output(search_value, From_hash.__name__, self.to_imphash.__name__, 291 | self.imphash_dict) 292 | 293 | 294 | 295 | ##### IMPHASH SECTION ###### 296 | class From_imphash(object): 297 | def __init__(self, verbose=False): 298 | self.verbose = verbose 299 | self.hash_dict = {} 300 | 301 | ######################################## 302 | # Searches to HASH 303 | def to_hash(self, search_value): 304 | if search_value: 305 | self.hash_dict.update({'Threatexpert': Threatexpert().imphash_to_hash(search_value)}) 306 | self.hash_dict.update({'Totalhash': Totalhash().imphash_to_hash(search_value)}) 307 | 308 | if self.verbose: 309 | return helpers.Common().verbose_output(search_value, From_imphash.__name__, self.to_hash.__name__, 310 | self.hash_dict) 311 | else: 312 | return helpers.Common().nonverbose_output(search_value, From_imphash.__name__, self.to_hash.__name__, 313 | self.hash_dict) 314 | ##### MUTEX SECTION ###### 315 | class From_mutex(object): 316 | def __init__(self, verbose=False): 317 | self.verbose = verbose 318 | self.hash_dict = {} 319 | 320 | ######################################## 321 | # Searches to HASH 322 | def to_hash(self, search_value): 323 | if search_value: 324 | self.hash_dict.update({'Threatexpert': Threatexpert().mutex_to_hash(search_value)}) 325 | self.hash_dict.update({'Totalhash': Totalhash().mutex_to_hash(search_value)}) 326 | 327 | if self.verbose: 328 | return helpers.Common().verbose_output(search_value, From_mutex.__name__, self.to_hash.__name__, 329 | self.hash_dict) 330 | else: 331 | return helpers.Common().nonverbose_output(search_value, From_mutex.__name__, self.to_hash.__name__, 332 | self.hash_dict) 333 | -------------------------------------------------------------------------------- /cli_qradio.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from argparse import ArgumentParser, FileType, RawTextHelpFormatter 3 | from lib.qradio import * 4 | import time 5 | 6 | # Parses arguments, search data and produce output 7 | class Command_line_parser(object): 8 | 9 | def __init__(self): 10 | 11 | self.parser = ArgumentParser(description='QRadio - "Firegun" Edition', add_help=True) 12 | 13 | 14 | # QRadio version 15 | self.version = '1.4.1' 16 | 17 | # Provide description and parameters for arguments 18 | self.parser = ArgumentParser( 19 | description='' 20 | 'QRadio can search and match different types of cyber threat information\n' 21 | '\t Info types: Domain, IPv4, Hash, Imphash, Mutex\n' 22 | '', 23 | # Append message in the end of help 24 | epilog='Thanks for listening! ;)\n' 25 | 'QRadio v. {}'.format(self.version), 26 | 27 | usage='%(prog)s domain|IPv4|hash [imphash|mutex] [station name|station#]\n', 28 | # Show '-h, --help' as addition option 29 | add_help=False, 30 | # Ability use \n \t... 31 | formatter_class=RawTextHelpFormatter) 32 | 33 | # Create group 'Searching' for help message 34 | 35 | self.search_group = self.parser.add_argument_group('Searching') 36 | # Add positional argument 'search_value' to store input values 37 | self.search_group.add_argument('search_value', 38 | # Accept zero or more input values 39 | nargs='*', 40 | # Append all input values into a list as string ['val','val2','val3',...] 41 | action='append', 42 | # Create help message description 43 | # Data types: 44 | # 1 = Domain 45 | # 2 = IPv4 46 | # 3 = Hash 47 | # 4 = Score 48 | # 5 = URL to Analysis 49 | # 6 = Blacklist 50 | # 7 = Imphash 51 | # 8 = Mutex 52 | help= 'search values: domain | IPv4 | hash | imphash | mutex\n' 53 | 'SONAR - 100 | 200 | 300 is used if no station supplied') # Search value(s) go first 54 | 55 | # Create group for help message 56 | self.output_group = self.parser.add_argument_group('Output verbosity') 57 | # Add optional argument for verbosity 58 | # Note: (--)Long_name, (-)Short_name 59 | self.output_group.add_argument('-v', '--verbose', required=False, 60 | # If specified returns True 61 | action='store_true', 62 | # Create help message description 63 | help='Show verbose output') 64 | 65 | # Create group for help message 66 | self.domain_group = self.parser.add_argument_group('DNS Tunes') 67 | # Add optional arguments for different searches in group. Return True if used 68 | # 100 - SONAR - Search to all available data types. 69 | # 102 - From Domain to IPv4 70 | # 103 - From Domain to Hash 71 | # 104 - From Domain to Score 72 | # 105 - From Domain to URL 73 | # 106 - Domain to Blacklist 74 | self.domain_group.add_argument('-100', 75 | '--sonar_domain', action='store_true', required=False, 76 | help='SONAR to IPv4, Hash, Score, URL, Blacklist\n ') 77 | 78 | self.domain_group.add_argument('-102', 79 | '--domain_to_ipv4', action='store_true', required=False, 80 | help='Resolve IPv4 to ') 81 | 82 | self.domain_group.add_argument('-103', 83 | '--domain_to_hash', action='store_true', required=False, 84 | help='Search Hash for ') 85 | 86 | self.domain_group.add_argument('-104', 87 | '--domain_to_score', action='store_true', required=False, 88 | help='Detection score for ') 89 | 90 | self.domain_group.add_argument('-105', 91 | '--domain_to_url', action='store_true', required=False, 92 | help='URL to analysis for ') 93 | 94 | self.domain_group.add_argument('-106', 95 | '--domain_to_blacklist', action='store_true', required=False, 96 | help='Search in blacklists') 97 | 98 | # Add optional arguments for different searches in group. Return True if used 99 | # 200 - SONAR - Search to all available data types. 100 | # 201 - From IPv4 to Domain 101 | # 203 - From IPv4 to Hash 102 | # 204 - From IPv4 to Score 103 | # 206 - IPv4 to Blacklist 104 | self.ip_group = self.parser.add_argument_group('IPv4 Tunes') 105 | self.ip_group.add_argument('-200', 106 | '--sonar_ipv4', action='store_true', required=False, 107 | help='SONAR to Domain, Hash, Score, Blacklist\n ') 108 | self.ip_group.add_argument('-201', 109 | '--ipv4_to_domain', action='store_true', required=False, 110 | help='Resolve Domain to ') 111 | self.ip_group.add_argument('-203', 112 | '--ipv4_to_hash', action='store_true', required=False, 113 | help='Search Hash for ') 114 | self.ip_group.add_argument('-204', 115 | '--ipv4_to_score', action='store_true', required=False, 116 | help='Detection score for ') 117 | self.ip_group.add_argument('-206', 118 | '--ipv4_to_blacklist', action='store_true', required=False, 119 | help='Search in blacklists') 120 | 121 | # Add optional arguments for different searches in group. Return True if used 122 | # 300 - SONAR - Search to all available data types. 123 | # 301 - From Hash to Domain 124 | # 302 - From Hash to IPv4 125 | # 304 - From Hash to Score 126 | # 305 - Hash to URL 127 | # 307 - Hash to Imphash 128 | self.hash_group = self.parser.add_argument_group('Hash Tunes') 129 | self.hash_group.add_argument('-300', 130 | '--sonar_hash', action='store_true', required=False, 131 | help='SONAR to Domain, IPv4, Score, URL, Imphash\n ') 132 | self.hash_group.add_argument('-301', 133 | '--hash_to_domain', action='store_true', required=False 134 | , help='Search Domain for ') 135 | self.hash_group.add_argument('-302', 136 | '--hash_to_ipv4', action='store_true', required=False, 137 | help='Search IP for ') 138 | self.hash_group.add_argument('-304', 139 | '--hash_to_score', action='store_true', required=False, 140 | help='Detection score for ') 141 | self.hash_group.add_argument('-305', 142 | '--hash_to_url', action='store_true', required=False, 143 | help='URL to analysis for ') 144 | self.hash_group.add_argument('-307', 145 | '--hash_to_imphash', action='store_true', required=False, 146 | help='Search Imphash for ') 147 | 148 | # Add optional arguments for different searches in group. Return True if used 149 | # 401 - From Imphash to Hash 150 | # 402 - From Mutex to Hash 151 | self.misc_group = self.parser.add_argument_group('Miscellaneous Tunes') # 400 152 | self.misc_group.add_argument('-401', 153 | '--imphash_to_hash', action='store_true', required=False, 154 | help='Search Hash with ') 155 | self.misc_group.add_argument('-402', 156 | '--mutex_to_hash', action='store_true', required=False, 157 | help='Search Hash with ') 158 | 159 | #self.file_group = self.parser.add_argument_group('File') 160 | #self.file_group.add_argument('-r', '--read_file', required=False, type=FileType('r'), help='Read values from file') 161 | #self.file_group.add_argument('-w', '--write_file', required=False, type=FileType('w'), help='Write results to file') 162 | 163 | # Store all created arguments 164 | self.arguments = self.parser.parse_args() 165 | 166 | # No search value provided 167 | if not self.arguments.search_value: 168 | # Print help message 169 | self.parser.print_help() 170 | return 171 | 172 | # If input values is provided 173 | if len(self.arguments.search_value[0]) != 0: 174 | 175 | # s_value = 'search_value' from list of values 176 | for s_value in self.arguments.search_value[0]: 177 | # Store search value in self. 178 | self.value_to_search = s_value 179 | # Validate and categorize the input 180 | # Can be: is_ip, is_domain, is_hash, is_invalid 181 | self.value_type = helpers.IO().input_validator(self.value_to_search) 182 | # If search value valid 183 | #if self.value_type != 'is_invalid': 184 | 185 | ################################## 186 | # Run search with flags and values 187 | self.form_header() 188 | self.flag_checker() 189 | self.searcher() 190 | 191 | #else: 192 | ## If search value is invalid show help message 193 | #self.parser.print_help() 194 | #return 195 | 196 | # Form output output header according to verbosity 197 | # TODO: Move to helpers.py 198 | def form_header(self): 199 | # Set verbose flag 200 | self.verbose = False 201 | 202 | # If -v, --verbose = True 203 | # Create verbose output header 204 | if self.arguments.verbose: 205 | self.verbose = True 206 | 207 | # If the script runs with multiple search values or multiple times put only one header 208 | try: 209 | # Check is the variable exist 210 | len(self.header_output) 211 | # If it exist we remove header 212 | self.header_output = '' 213 | 214 | # If the script runs first time, create according header 215 | except AttributeError: 216 | # Verbose header 217 | self.header_output = ''' 218 | # ________ \\\\ || // 219 | # / __ \ \\\\ || // 220 | # | / \ | .______ _____ ______ __ _____ 221 | # | | _ | | | _ \ / _ \ | _ \ |__| / _ \\ 222 | # | | / \| | | |_) ) | / \ | | | \ \ __ | / \ | 223 | # | | \ | | | / | |_| | | | ) ) | | | ( ) | 224 | # | \_\ | | |\ \ | _ | | |_/ / | | | \_/ | 225 | # \_______ \ |__| \__\ |__| |__| |______/ |__| \_____/ 226 | # \__\\ 227 | # ~ Tune In\n''' 228 | 229 | time_stamp = '#DATE ' + time.asctime(time.localtime(time.time())) + '\n' 230 | self.header_output += time_stamp 231 | # If -v, --verbose = False 232 | # Create CSV header 233 | else: 234 | # If the script runs with multiple search values or multiple times put only one header 235 | try: 236 | # Check is the variable exist 237 | len(self.header_output) 238 | # If it exist we remove header 239 | self.header_output = '' 240 | 241 | # If the script runs first time, create according header 242 | except AttributeError: 243 | # CSV Header 244 | # Domain , IPv4 , Hash , Score , URL , Blacklist , Imphash , Mutex 245 | # [1] [2] [3] [4] [5] [6] [7] [8] 246 | self.header_output = 'Domain,IPv4,Hash,Score,URL,Blacklist,Imphash,Mutex' +'\n' 247 | 248 | 249 | def flag_checker(self): 250 | # Flag check 251 | self.flag_cheked = False 252 | 253 | # Convert command line arguments in to dict to able access the values 254 | arguments_dict = vars(self.arguments) 255 | 256 | # i = key, argumets_dict[i] = value 257 | for i in arguments_dict: 258 | a = arguments_dict[i] 259 | # Find if any arguments are set 260 | if isinstance(arguments_dict[i], bool): 261 | # Skip 'verbose' argument 262 | if arguments_dict[i] and i != 'verbose': 263 | self.flag_cheked = True 264 | 265 | # If no arguments specified run SONAR according to value type provided 266 | if not self.flag_cheked: 267 | if self.value_type == 'is_domain': 268 | arguments_dict['sonar_domain'] = True 269 | elif self.value_type == 'is_ip': 270 | arguments_dict['sonar_ipv4'] = True 271 | elif self.value_type == 'is_hash': 272 | arguments_dict['sonar_hash'] = True 273 | 274 | 275 | # Search according to arguments 276 | def searcher(self): 277 | self.output_former_result = '' 278 | 279 | # SONAR for Domain 280 | if self.arguments.sonar_domain: 281 | self.output_former_result += str(From_domain(verbose=self.verbose).to_ipv4(self.value_to_search)) 282 | self.output_former_result += str(From_domain(verbose=self.verbose).to_hash(self.value_to_search)) 283 | self.output_former_result += str(From_domain(verbose=self.verbose).to_score(self.value_to_search)) 284 | self.output_former_result += str(From_domain(verbose=self.verbose).to_url(self.value_to_search)) 285 | self.output_former_result += str(From_domain(verbose=self.verbose).to_blacklist(self.value_to_search)) 286 | 287 | # From Domain to IPv4 288 | if self.arguments.domain_to_ipv4: 289 | self.output_former_result += str(From_domain(verbose=self.verbose).to_ipv4(self.value_to_search)) 290 | 291 | # From Domain to Hash 292 | if self.arguments.domain_to_hash: 293 | self.output_former_result += str(From_domain(verbose=self.verbose).to_hash(self.value_to_search)) 294 | 295 | # From Domain to Score 296 | if self.arguments.domain_to_score: 297 | self.output_former_result += str(From_domain(verbose=self.verbose).to_score(self.value_to_search)) 298 | 299 | # From Domain to URL 300 | if self.arguments.domain_to_url: 301 | self.output_former_result += str(From_domain(verbose=self.verbose).to_url(self.value_to_search)) 302 | 303 | # Domain to Blacklist 304 | if self.arguments.domain_to_blacklist: 305 | self.output_former_result += str(From_domain(verbose=self.verbose).to_blacklist(self.value_to_search)) 306 | 307 | 308 | 309 | # SONAR for IPv4 310 | if self.arguments.sonar_ipv4: 311 | self.output_former_result += str(From_ipv4(verbose=self.verbose).to_domain(self.value_to_search)) 312 | self.output_former_result += str(From_ipv4(verbose=self.verbose).to_hash(self.value_to_search)) 313 | self.output_former_result += str(From_ipv4(verbose=self.verbose).to_score(self.value_to_search)) 314 | self.output_former_result += str(From_ipv4(verbose=self.verbose).to_blacklist(self.value_to_search)) 315 | 316 | # From IPv4 to Domain 317 | if self.arguments.ipv4_to_domain: 318 | self.output_former_result += str(From_ipv4(verbose=self.verbose).to_domain(self.value_to_search)) 319 | 320 | # From IPv4 to Hash 321 | if self.arguments.ipv4_to_hash: 322 | self.output_former_result += str(From_ipv4(verbose=self.verbose).to_hash(self.value_to_search)) 323 | 324 | # From IPv4 to Score 325 | if self.arguments.ipv4_to_score: 326 | self.output_former_result += str(From_ipv4(verbose=self.verbose).to_score(self.value_to_search)) 327 | 328 | # IPv4 to Blacklist 329 | if self.arguments.ipv4_to_blacklist: 330 | self.output_former_result += str(From_ipv4(verbose=self.verbose).to_blacklist(self.value_to_search)) 331 | 332 | 333 | 334 | # SONAR for Hash 335 | if self.arguments.sonar_hash: 336 | self.output_former_result += str(From_hash(verbose=self.verbose).to_domain(self.value_to_search)) 337 | self.output_former_result += str(From_hash(verbose=self.verbose).to_ipv4(self.value_to_search)) 338 | self.output_former_result += str(From_hash(verbose=self.verbose).to_score(self.value_to_search)) 339 | self.output_former_result += str(From_hash(verbose=self.verbose).to_url(self.value_to_search)) 340 | self.output_former_result += str(From_hash(verbose=self.verbose).to_imphash(self.value_to_search)) 341 | 342 | # From Hash to Domain 343 | if self.arguments.hash_to_domain: 344 | self.output_former_result += str(From_hash(verbose=self.verbose).to_domain(self.value_to_search)) 345 | 346 | # From Hash to IPv4 347 | if self.arguments.hash_to_ipv4: 348 | self.output_former_result += str(From_hash(verbose=self.verbose).to_ipv4(self.value_to_search)) 349 | 350 | # From Hash to Score 351 | if self.arguments.hash_to_score: 352 | self.output_former_result += str(From_hash(verbose=self.verbose).to_score(self.value_to_search)) 353 | 354 | # Hash to URL 355 | if self.arguments.hash_to_url: 356 | self.output_former_result += str(From_hash(verbose=self.verbose).to_url(self.value_to_search)) 357 | 358 | # Hash to Imphash 359 | if self.arguments.hash_to_imphash: 360 | self.output_former_result += str(From_hash(verbose=self.verbose).to_imphash(self.value_to_search)) 361 | 362 | 363 | # Imphashto Hash 364 | if self.arguments.imphash_to_hash: 365 | self.output_former_result += str(From_imphash(verbose=self.verbose).to_hash(self.value_to_search)) 366 | 367 | 368 | # Mutex to Hash 369 | if self.arguments.mutex_to_hash: 370 | self.output_former_result += str(From_mutex(verbose=self.verbose).to_hash(self.value_to_search)) 371 | 372 | 373 | # TODO: add File input/output 374 | # IO 375 | #if self.arguments.read_file: 376 | # output_former_result += str(From_domain(verbose=self.verbose).to_hash(self.arguments.search_value)) 377 | 378 | #if self.arguments.write_file: 379 | # output_former_result += str(From_domain(verbose=self.verbose).to_hash(self.arguments.search_value)) 380 | 381 | 382 | # Print the results with a header for csv or verbose 383 | if self.output_former_result: 384 | print self.header_output+self.output_former_result 385 | 386 | if __name__ == '__main__': 387 | if len(sys.argv) > 1: 388 | Command_line_parser() 389 | else: 390 | Command_line_parser().parser.print_help() 391 | -------------------------------------------------------------------------------- /lib/helpers.py: -------------------------------------------------------------------------------- 1 | import time 2 | import os.path, sys 3 | import re 4 | import requests 5 | from bs4 import BeautifulSoup 6 | 7 | 8 | class Common(object): 9 | 10 | # Help to make a requests to websites 11 | def session_helper(self, station_name=None, # station_name - REQUIRE - 'Station_name' 12 | endpoint=None, # 'https://station.com/api/search/index.php' 13 | method_type=None, # GET/POST 14 | data_to_send=None, # Data to sent in POST 15 | url_path=None, # '/api/search/google.com' 16 | parameters=None, # {'limit': '1000'} 17 | headers=None, # {'api_key': 'api_key_value'} 18 | user_agent=None, # {'User-agent': 'VxStream Sandbox'} 19 | response_format=None, # json or bs(BeautifulSoup) 20 | go_to_url=None): # https://www.station.com/index.php?a=somethins&d=something 21 | 22 | self.session = requests.Session() 23 | self.station_name = station_name 24 | 25 | # change default user_agent to: 26 | if user_agent: 27 | self.user_agent = user_agent 28 | else: 29 | # Firefox is default User-Agent 30 | self.session.headers = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:41.0) ' + 31 | 'Gecko/20100101 Firefox/41.0'} 32 | 33 | # append custom values. e.g. {'search': 'this'} 34 | if headers: 35 | self.session.headers.update(headers) 36 | 37 | # Path additionally to endpoint 38 | # e.g. /api/search/domain/google.com 39 | if url_path: 40 | self.url_path = url_path 41 | else: 42 | self.url_path = '' 43 | 44 | # e.g. ...?search=domain_name&value=full_history 45 | if parameters: 46 | self.parameters = parameters 47 | else: 48 | self.parameters = '' 49 | 50 | # Make request only if endpoint, station and get/post specified 51 | if station_name and endpoint and method_type: 52 | if go_to_url: 53 | # If URL provided 54 | url = go_to_url 55 | else: 56 | # Create URL for request 57 | url = endpoint + str(self.url_path) 58 | try: 59 | # Disable warning for SSL key trust 60 | requests.packages.urllib3.disable_warnings() 61 | 62 | # Make request with GET 63 | if method_type == 'get': 64 | get_request = self.session.get(url, params=self.parameters, verify=False) 65 | #if get_request.status_code == 200 or get_request.status_code == 400 or get_request.status_code == 404: 66 | self.html_results = get_request 67 | 68 | # Make request with POST 69 | elif method_type == 'post': 70 | post_request = self.session.post(url, params=self.parameters, data=data_to_send) 71 | # Check if return code is 200 72 | if post_request.status_code == 200: 73 | self.html_results = post_request 74 | 75 | # Return BeautifulSoup, JSON or raw output 76 | if response_format == 'bs' and self.html_results: 77 | return BeautifulSoup(self.html_results.text, 'html.parser') 78 | elif response_format == 'json' and self.html_results: 79 | return self.html_results.json() 80 | else: 81 | return self.html_results 82 | 83 | # Catch errors 84 | except requests.ConnectionError as e: 85 | # network problem (e.g. DNS failure, refused connection, etc) in e 86 | IO().error_log(e, self.station_name) 87 | return 88 | except requests.exceptions.Timeout as e: 89 | #Request Time out in e 90 | IO().error_log(e, self.station_name) 91 | return 92 | except requests.exceptions.TooManyRedirects as e: 93 | #Invalid URL in e 94 | IO().error_log(e, self.station_name) 95 | return 96 | except requests.exceptions.RequestException as e: 97 | # Return the other errors in e 98 | IO().error_log(e, self.station_name) 99 | return 100 | except: 101 | return None 102 | # endpoint, station or get/post NOT specified 103 | else: 104 | return None 105 | 106 | 107 | 108 | # Return output for every station with its result 109 | def verbose_output(self, search_value, search_from, search_to, dictionary): 110 | #time_stamp = time.asctime(time.localtime(time.time())) 111 | #time_stamb_output = '#'+'DATE: ' + time_stamp 112 | 113 | # e.g. #FROM_HASH_TO_DOMAIN 114 | header_output = ('#'+search_from+'_'+search_to).upper() + '\n' 115 | 116 | # e.g. #SEARCH: 8.8.8.8 117 | search_output = '#SEARCH: ' + search_value 118 | full_results = '' 119 | unique_values = '' 120 | 121 | for packed_values in dictionary: 122 | if dictionary is None: 123 | return 124 | 125 | # Search values, but not Blacklist 126 | elif not isinstance(dictionary[packed_values], bool): 127 | try: 128 | # Unpack list of values from dictionary and return as list. e.g. from [[...]] to [...] 129 | unpack = [item for sublist in dictionary.values() if sublist for item in sublist] 130 | # Make unique list 131 | unique_list = list(set(unpack)) 132 | except: 133 | unique_list = [] 134 | 135 | 136 | # Blacklist has bool as value 137 | if isinstance(dictionary[packed_values], bool): 138 | unique_values = '' 139 | 140 | # Unique values 141 | elif len(unique_list) > 0: 142 | unique_values = '\n#UNIQUE: ' + str(len(unique_list)) + '\n ' + ' '.join(unique_list) 143 | # No result from station 144 | else: 145 | unique_values = '\n#NO RESULTS' 146 | 147 | 148 | for station in dictionary: 149 | station_info_output = '' 150 | results = '' 151 | # 152 | if isinstance(dictionary[station], list): 153 | if dictionary[station].__len__() != 0: 154 | station_info_output = ('#'+station+' FIND: '+str(dictionary[station].__len__())).upper()+'\n ' 155 | results += '\n '.join(dictionary[station])+'\n' 156 | 157 | # Blacklist has bool as value 158 | elif isinstance(dictionary[station], bool): 159 | station_info_output = '#'+station+': '+str(dictionary[station])+'\n' 160 | 161 | full_results += station_info_output+results 162 | 163 | footer_output = '##############################\n' 164 | 165 | verbose_output_results = '%s%s%s\n%s%s' % (header_output, search_output, unique_values, full_results, footer_output) 166 | return verbose_output_results 167 | 168 | 169 | 170 | # Return CSV from station results 171 | def nonverbose_output(self, search_value, search_from, search_to, dictionary): 172 | 173 | if isinstance(dictionary, str): 174 | unique_list = dictionary 175 | else: 176 | for packed_values in dictionary: 177 | if dictionary is None: 178 | return 179 | 180 | # Search values, but not Blacklist 181 | elif not isinstance(dictionary[packed_values], bool): 182 | try: 183 | # Unpack list of values from dictionary and return as list. e.g. from [[...]] to [...] 184 | unpack = [item for sublist in dictionary.values() if sublist for item in sublist] 185 | # Make unique list 186 | unique_list = list(set(unpack)) 187 | except: 188 | unique_list = [] 189 | 190 | # Domain | IP | Hash | Score | URL | Blacklist | Imphash | Mutex 191 | # [1] [2] [3] [4] [5] [6] [7] [8] 192 | 193 | msg = '' 194 | 195 | ########################################################################## 196 | # Domain 197 | if search_from == 'From_domain': # [1] 198 | if search_to == 'to_ipv4': # [2] 199 | for i in unique_list: 200 | msg += search_value + ',' + i + ',,,,,,' + '\n' 201 | 202 | if search_to == 'to_hash': # [3] 203 | for i in unique_list: 204 | msg += search_value + ',,' + i + ',,,,,' + '\n' 205 | 206 | if search_to == 'to_score': # [4] 207 | for i in unique_list: 208 | msg += search_value + ',,,' + i + ',' + ',,,\n' 209 | 210 | if search_to == 'to_url': # [5] 211 | for i in unique_list: 212 | msg += search_value + ',,,,' + i + ',,,\n' 213 | 214 | if search_to == 'to_blacklist': # [6] 215 | msg += search_value + ',,,,,' + str(unique_list) + ',,\n' 216 | 217 | ########################################################################## 218 | # IP 219 | if search_from == 'From_ipv4': # [2] 220 | if search_to == 'to_domain': # [1] 221 | for i in unique_list: 222 | msg += i + ',' + search_value + ',,,,,,\n' 223 | 224 | if search_to == 'to_hash': # [3] 225 | for i in unique_list: 226 | msg += ',' + search_value + ',' + i + ',,,,,\n' 227 | 228 | if search_to == 'to_score': # [4] 229 | for i in unique_list: 230 | msg += ',' + search_value + ',,' + i + ',,,,\n' 231 | 232 | if search_to == 'to_blacklist': # [6] 233 | msg += ',' + search_value + ',,,,' + str(unique_list) + ',,\n' 234 | 235 | ########################################################################## 236 | # Hash 237 | if search_from == 'From_hash': # [3] 238 | if search_to == 'to_domain': # [1] 239 | for i in unique_list: 240 | msg += i + ',,' + search_value + ',,,,,\n' 241 | 242 | if search_to == 'to_ipv4': # [2] 243 | for i in unique_list: 244 | msg += ',' + i + ',' + search_value + ',,,,,\n' 245 | 246 | if search_to == 'to_score': # [4] 247 | for i in unique_list: 248 | msg += ',,' + search_value + ',' + i + ',,,,\n' 249 | 250 | if search_to == 'to_url': # [5] 251 | for i in unique_list: 252 | msg += ',,' + search_value + ',,' + i + ',,,\n' 253 | 254 | if search_to == 'to_imphash': # [7] 255 | for i in unique_list: 256 | msg += ',,' + search_value + ',,,,' + i + ',\n' 257 | 258 | 259 | ########################################################################## 260 | # Miscelanous 261 | if search_from == 'From_imphash': # [7] 262 | if search_to == 'to_hash': # [3] 263 | for i in unique_list: 264 | msg += ',,' + i + ',,,,' + search_value + ',\n' 265 | 266 | if search_from == 'From_mutex': # [8] 267 | if search_to == 'to_hash': # [3] 268 | for i in unique_list: 269 | msg += ',,' + i + ',,,,,' + search_value + '\n' 270 | 271 | return msg 272 | 273 | 274 | 275 | class IO(object): 276 | 277 | 278 | def error_log(self, error, station_name=None): 279 | if os.path.isfile('error_log.txt'): 280 | file = open('error_log.txt', 'a') 281 | else: 282 | file = open('error_log.txt', 'w') 283 | 284 | if not station_name: 285 | station_name = 'NO-STATION-NAME' 286 | 287 | 288 | # Create time stamp 289 | time_stamp = time.asctime(time.localtime(time.time())) 290 | 291 | # Form error message 'Station == Time == Error' 292 | error_msg = str(station_name) + '\t' + time_stamp + '\t' + str(error) + '\n' 293 | file.write(error_msg) 294 | file.close() 295 | 296 | def process_file(self, file_path): 297 | try: 298 | log_file = open(file_path, 'r') 299 | log_data = log_file.readlines() 300 | for item in log_data: 301 | print(item.strip('\n')) 302 | except: 303 | print('failed to open file') 304 | return False 305 | return True 306 | 307 | 308 | # Validate input 309 | def input_validator(self,input_value): 310 | is_ip = re.match(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', input_value) 311 | is_domain = re.match(r'[a-zA-Z0-9]*\.[a-zA-Z0-9]*', input_value) 312 | is_hash = re.match(r'^\w{32,}$', input_value) 313 | 314 | if is_ip: # If input is IP address 315 | return 'is_ip' 316 | elif is_domain: # If input is domain 317 | return 'is_domain' 318 | elif is_hash and len(input_value) >= 32: # Is input is hash 319 | return 'is_hash' 320 | else: 321 | return 'is_invalid' 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | class MaltegoEntity(object): 334 | 335 | ####################################################### 336 | # Maltego Python Local Transform Helper # 337 | # Version 0.2 # 338 | # # 339 | # Local transform specification can be found at: # 340 | # http://ctas.paterva.com/view/Specification # 341 | # # 342 | # For more help and other local transforms # 343 | # try the forum or mail me: # 344 | # # 345 | # http://www.paterva.com/forum # 346 | # # 347 | # Andrew MacPherson [ andrew <> Paterva.com ] # 348 | # # 349 | ####################################################### 350 | 351 | BOOKMARK_COLOR_NONE="-1" 352 | BOOKMARK_COLOR_BLUE="0" 353 | BOOKMARK_COLOR_GREEN="1" 354 | BOOKMARK_COLOR_YELLOW="2" 355 | BOOKMARK_COLOR_ORANGE="3" 356 | BOOKMARK_COLOR_RED="4" 357 | 358 | LINK_STYLE_NORMAL="0" 359 | LINK_STYLE_DASHED="1" 360 | LINK_STYLE_DOTTED="2" 361 | LINK_STYLE_DASHDOT="3" 362 | 363 | UIM_FATAL='FatalError' 364 | UIM_PARTIAL='PartialError' 365 | UIM_INFORM='Inform' 366 | UIM_DEBUG='Debug' 367 | 368 | value = "" 369 | weight = 100 370 | displayInformation = None 371 | additionalFields = [] 372 | iconURL = "" 373 | entityType = "Phrase" 374 | 375 | def __init__(self,eT=None,v=None): 376 | if (eT is not None): 377 | self.entityType = eT 378 | if (v is not None): 379 | self.value = MaltegoTransform().sanitise(v) 380 | self.additionalFields = [] 381 | self.displayInformation = None 382 | 383 | def setType(self,eT=None): 384 | if (eT is not None): 385 | self.entityType = eT 386 | 387 | def setValue(self,eV=None): 388 | if (eV is not None): 389 | self.value = MaltegoTransform().sanitise(eV) 390 | 391 | def setWeight(self,w=None): 392 | if (w is not None): 393 | self.weight = w 394 | 395 | def setDisplayInformation(self,di=None): 396 | if (di is not None): 397 | self.displayInformation = di 398 | 399 | def addAdditionalFields(self,fieldName=None,displayName=None,matchingRule='',value=None): 400 | self.additionalFields.append([MaltegoTransform().sanitise(fieldName),MaltegoTransform().sanitise(displayName),matchingRule,MaltegoTransform().sanitise(value)]) 401 | 402 | def setIconURL(self,iU=None): 403 | if (iU is not None): 404 | self.iconURL = iU 405 | 406 | def setLinkColor(self,color): 407 | self.addAdditionalFields('link#maltego.link.color','LinkColor','',color) 408 | 409 | def setLinkStyle(self,style): 410 | self.addAdditionalFields('link#maltego.link.style','LinkStyle','',style) 411 | 412 | def setLinkThickness(self,thick): 413 | self.addAdditionalFields('link#maltego.link.thickness','Thickness','',str(thick)) 414 | 415 | def setLinkLabel(self,label): 416 | self.addAdditionalFields('link#maltego.link.label','Label','',label) 417 | 418 | def setBookmark(self,bookmark): 419 | self.addAdditionalFields('bookmark#','Bookmark','',bookmark) 420 | 421 | def setNote(self,note): 422 | self.addAdditionalFields('notes#','Notes','',note) 423 | 424 | def returnEntity(self): 425 | print "" 426 | print "" + str(self.value) + "" 427 | print "" + str(self.weight) + "" 428 | if (self.displayInformation is not None): 429 | print "" 430 | if (len(self.additionalFields) > 0): 431 | print "" 432 | for i in range(len(self.additionalFields)): 433 | if (str(self.additionalFields[i][2]) <> "strict"): 434 | print "" + str(self.additionalFields[i][3]) + "" 435 | else: 436 | print "" + str(self.additionalFields[i][3]) + "" 437 | print "" 438 | if (len(self.iconURL) > 0): 439 | print "" + self.iconURL + "" 440 | print "" 441 | 442 | class MaltegoTransform(object): 443 | ####################################################### 444 | # Maltego Python Local Transform Helper # 445 | # Version 0.2 # 446 | # # 447 | # Local transform specification can be found at: # 448 | # http://ctas.paterva.com/view/Specification # 449 | # # 450 | # For more help and other local transforms # 451 | # try the forum or mail me: # 452 | # # 453 | # http://www.paterva.com/forum # 454 | # # 455 | # Andrew MacPherson [ andrew <> Paterva.com ] # 456 | # # 457 | ####################################################### 458 | 459 | entities = [] 460 | exceptions = [] 461 | UIMessages = [] 462 | values = {} 463 | 464 | def __init__(self): 465 | values = {} 466 | value = None 467 | 468 | def parseArguments(self,argv): 469 | if (argv[1] is not None): 470 | self.value = argv[1] 471 | 472 | if (len(argv) > 2): 473 | if (argv[2] is not None): 474 | vars = argv[2].split('#') 475 | for x in range(0,len(vars)): 476 | vars_values = vars[x].split('=') 477 | if (len(vars_values) == 2): 478 | self.values[vars_values[0]] = vars_values[1] 479 | 480 | def getValue(self): 481 | if (self.value is not None): 482 | return self.value 483 | 484 | def getVar(self,varName): 485 | if (varName in self.values.keys()): 486 | if (self.values[varName] is not None): 487 | return self.values[varName] 488 | 489 | def addEntity(self,enType,enValue): 490 | me = MaltegoEntity(enType,enValue) 491 | self.addEntityToMessage(me) 492 | return self.entities[len(self.entities)-1] 493 | 494 | def addEntityToMessage(self,maltegoEntity): 495 | self.entities.append(maltegoEntity) 496 | 497 | def addUIMessage(self,message,messageType="Inform"): 498 | self.UIMessages.append([messageType,message]) 499 | 500 | def addException(self,exceptionString): 501 | self.exceptions.append(exceptionString) 502 | 503 | def throwExceptions(self): 504 | print "" 505 | print "" 506 | print "" 507 | 508 | for i in range(len(self.exceptions)): 509 | print "" + self.exceptions[i] + "" 510 | print "" 511 | print "" 512 | print "" 513 | exit() 514 | 515 | def returnOutput(self): 516 | print "" 517 | print "" 518 | 519 | print "" 520 | for i in range(len(self.entities)): 521 | self.entities[i].returnEntity() 522 | print "" 523 | 524 | print "" 525 | for i in range(len(self.UIMessages)): 526 | print "" + self.UIMessages[i][1] + "" 527 | print "" 528 | 529 | print "" 530 | print "" 531 | 532 | def writeSTDERR(self,msg): 533 | sys.stderr.write(str(msg)) 534 | 535 | def heartbeat(self): 536 | self.writeSTDERR("+") 537 | 538 | def progress(self,percent): 539 | self.writeSTDERR("%" + str(percent)) 540 | 541 | def debug(self,msg): 542 | self.writeSTDERR("D:" + str(msg)) 543 | 544 | def sanitise(self, value): 545 | replace_these = ["&",">","<"] 546 | replace_with = ["&",">","<"] 547 | tempvalue = value 548 | for i in range(0,len(replace_these)): 549 | tempvalue = tempvalue.replace(replace_these[i],replace_with[i]) 550 | return tempvalue --------------------------------------------------------------------------------