mirror of https://github.com/MISP/misp-modules
Compare commits
7 Commits
Author | SHA1 | Date |
---|---|---|
Alexandre Dulaunoy | 0a01b382f4 | |
Andreas Muehlemann | 85af573a74 | |
Alexandre Dulaunoy | 53d4cb3860 | |
Alexandre Dulaunoy | 1c963d3482 | |
Andreas Muehlemann | 8d240e3541 | |
Steve Clement | af1739cec5 | |
Steve Clement | 70543820eb |
|
@ -27,7 +27,6 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj
|
|||
* [CIRCL Passive DNS](misp_modules/modules/expansion/circl_passivedns.py) - a hover and expansion module to expand hostname and IP addresses with passive DNS information.
|
||||
* [CIRCL Passive SSL](misp_modules/modules/expansion/circl_passivessl.py) - a hover and expansion module to expand IP addresses with the X.509 certificate(s) seen.
|
||||
* [countrycode](misp_modules/modules/expansion/countrycode.py) - a hover module to tell you what country a URL belongs to.
|
||||
* [CrowdSec](misp_modules/modules/expansion/crowdsec.py) - a hover module to expand using CrowdSec's CTI API.
|
||||
* [CrowdStrike Falcon](misp_modules/modules/expansion/crowdstrike_falcon.py) - an expansion module to expand using CrowdStrike Falcon Intel Indicator API.
|
||||
* [CPE](misp_modules/modules/expansion/cpe.py) - An expansion module to query the CVE Search API with a cpe code, to get its related vulnerabilities.
|
||||
* [CVE](misp_modules/modules/expansion/cve.py) - a hover module to give more information about a vulnerability (CVE).
|
||||
|
@ -44,14 +43,13 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj
|
|||
* [GeoIP](misp_modules/modules/expansion/geoip_country.py) - a hover and expansion module to get GeoIP information from geolite/maxmind.
|
||||
* [GeoIP_City](misp_modules/modules/expansion/geoip_city.py) - a hover and expansion module to get GeoIP City information from geolite/maxmind.
|
||||
* [GeoIP_ASN](misp_modules/modules/expansion/geoip_asn.py) - a hover and expansion module to get GeoIP ASN information from geolite/maxmind.
|
||||
* [GreyNoise](misp_modules/modules/expansion/greynoise.py) - a hover and expansion module to get IP and CVE information from GreyNoise.
|
||||
* [Greynoise](misp_modules/modules/expansion/greynoise.py) - a hover to get information from greynoise.
|
||||
* [hashdd](misp_modules/modules/expansion/hashdd.py) - a hover module to check file hashes against [hashdd.com](http://www.hashdd.com) including NSLR dataset.
|
||||
* [hibp](misp_modules/modules/expansion/hibp.py) - a hover module to lookup against Have I Been Pwned?
|
||||
* [html_to_markdown](misp_modules/modules/expansion/html_to_markdown.py) - Simple HTML to markdown converter
|
||||
* [HYAS Insight](misp_modules/modules/expansion/hyasinsight.py) - a hover and expansion module to get information from [HYAS Insight](https://www.hyas.com/hyas-insight).
|
||||
* [intel471](misp_modules/modules/expansion/intel471.py) - an expansion module to get info from [Intel471](https://intel471.com).
|
||||
* [IPASN](misp_modules/modules/expansion/ipasn.py) - a hover and expansion to get the BGP ASN of an IP address.
|
||||
* [ipinfo.io](misp_modules/modules/expansion/ipinfo.py) - an expansion module to get additional information on an IP address using the ipinfo.io API
|
||||
* [iprep](misp_modules/modules/expansion/iprep.py) - an expansion module to get IP reputation from packetmail.net.
|
||||
* [Joe Sandbox submit](misp_modules/modules/expansion/joesandbox_submit.py) - Submit files and URLs to Joe Sandbox.
|
||||
* [Joe Sandbox query](misp_modules/modules/expansion/joesandbox_query.py) - Query Joe Sandbox with the link of an analysis and get the parsed data.
|
||||
|
|
265
REQUIREMENTS
265
REQUIREMENTS
|
@ -1,183 +1,112 @@
|
|||
-i https://pypi.org/simple
|
||||
aiohttp==3.8.3
|
||||
aiosignal==1.2.0 ; python_version >= '3.6'
|
||||
antlr4-python3-runtime==4.9.3
|
||||
anyio==3.6.1 ; python_full_version >= '3.6.2'
|
||||
-e .
|
||||
-e git+https://github.com/D4-project/BGP-Ranking.git/@fd9c0e03af9b61d4bf0b67ac73c7208a55178a54#egg=pybgpranking&subdirectory=client
|
||||
-e git+https://github.com/D4-project/IPASN-History.git/@fc5e48608afc113e101ca6421bf693b7b9753f9e#egg=pyipasnhistory&subdirectory=client
|
||||
-e git+https://github.com/MISP/PyIntel471.git@0df8d51f1c1425de66714b3a5a45edb69b8cc2fc#egg=pyintel471
|
||||
-e git+https://github.com/MISP/PyMISP.git@b5b40ae2c5225a4b349c26294cfc012309a61352#egg=pymisp[fileobjects,openioc,virustotal,pdfexport]
|
||||
-e git+https://github.com/Rafiot/uwhoisd.git@411572840eba4c72dc321c549b36a54ed5cea9de#egg=uwhois&subdirectory=client
|
||||
-e git+https://github.com/cartertemm/ODTReader.git/@49d6938693f6faa3ff09998f86dba551ae3a996b#egg=odtreader
|
||||
-e git+https://github.com/sebdraven/pydnstrails@48c1f740025c51289f43a24863d1845ff12fd21a#egg=pydnstrails
|
||||
-e git+https://github.com/sebdraven/pyonyphe@1ce15581beebb13e841193a08a2eb6f967855fcb#egg=pyonyphe
|
||||
-e git+https://github.com/stricaud/faup.git#egg=pyfaup&subdirectory=src/lib/bindings/python
|
||||
aiohttp==3.4.4
|
||||
antlr4-python3-runtime==4.8 ; python_version >= '3'
|
||||
apiosintds==1.8.3
|
||||
appdirs==1.4.4
|
||||
argparse==1.4.0
|
||||
assemblyline-client==4.5.0
|
||||
async-timeout==4.0.2 ; python_version >= '3.6'
|
||||
asynctest==0.13.0 ; python_version < '3.8'
|
||||
attrs==22.1.0 ; python_version >= '3.5'
|
||||
backoff==2.1.2 ; python_version >= '3.7' and python_version < '4.0'
|
||||
backports.zoneinfo==0.2.1 ; python_version < '3.9'
|
||||
assemblyline-client==3.7.3
|
||||
async-timeout==3.0.1
|
||||
attrs==19.3.0
|
||||
backscatter==0.2.4
|
||||
beautifulsoup4==4.11.1
|
||||
bidict==0.22.0 ; python_version >= '3.7'
|
||||
beautifulsoup4==4.8.2
|
||||
blockchain==1.4.4
|
||||
censys==2.1.8
|
||||
certifi==2022.9.24 ; python_version >= '3.6'
|
||||
cffi==1.15.1
|
||||
chardet==5.0.0
|
||||
charset-normalizer==2.1.1 ; python_full_version >= '3.6.0'
|
||||
clamd==1.0.2
|
||||
click==8.1.3 ; python_version >= '3.7'
|
||||
censys==0.0.8
|
||||
certifi==2019.11.28
|
||||
cffi==1.14.0
|
||||
chardet==3.0.4
|
||||
click-plugins==1.1.1
|
||||
colorama==0.4.5 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||
colorclass==2.2.2 ; python_version >= '2.6'
|
||||
commonmark==0.9.1
|
||||
compressed-rtf==1.0.6
|
||||
configparser==5.3.0 ; python_version >= '3.7'
|
||||
crowdstrike-falconpy==1.2.2
|
||||
cryptography==38.0.1 ; python_version >= '3.6'
|
||||
dateparser==1.1.1 ; python_version >= '3.5'
|
||||
decorator==5.1.1 ; python_version >= '3.5'
|
||||
deprecated==1.2.13 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
dnsdb2==1.1.4
|
||||
dnspython==2.2.1
|
||||
domaintools-api==1.0.1
|
||||
easygui==0.98.3
|
||||
ebcdic==1.1.1
|
||||
click==7.1.1
|
||||
colorama==0.4.3
|
||||
cryptography==2.8
|
||||
decorator==4.4.2
|
||||
deprecated==1.2.7
|
||||
dnspython==1.16.0
|
||||
domaintools-api==0.3.3
|
||||
enum-compat==0.0.3
|
||||
et-xmlfile==1.1.0 ; python_version >= '3.6'
|
||||
extract-msg==0.36.3
|
||||
ez-setup==0.9
|
||||
ezodf==0.3.2
|
||||
filelock==3.8.0 ; python_version >= '3.7'
|
||||
frozenlist==1.3.1 ; python_version >= '3.7'
|
||||
future==0.18.2 ; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
geoip2==4.6.0
|
||||
greynoise==2.0.0
|
||||
h11==0.12.0 ; python_version >= '3.6'
|
||||
httpcore==0.15.0 ; python_version >= '3.7'
|
||||
httplib2==0.20.4 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
httpx==0.23.0 ; python_version >= '3.7'
|
||||
idna==3.4 ; python_version >= '3.5'
|
||||
imapclient==2.3.1
|
||||
importlib-metadata==4.12.0 ; python_version < '3.8'
|
||||
importlib-resources==5.9.0 ; python_version < '3.9'
|
||||
isodate==0.6.1
|
||||
itsdangerous==2.1.2 ; python_version >= '3.7'
|
||||
jaraco.classes==3.2.3 ; python_version >= '3.7'
|
||||
jbxapi==3.18.0
|
||||
jeepney==0.8.0 ; sys_platform == 'linux'
|
||||
jinja2==3.1.2
|
||||
json-log-formatter==0.5.1
|
||||
jsonschema==4.16.0 ; python_version >= '3.7'
|
||||
keyring==23.9.3 ; python_version >= '3.7'
|
||||
lark-parser==0.12.0
|
||||
lief==0.12.1
|
||||
lxml==4.9.1
|
||||
future==0.18.2
|
||||
futures==3.1.1
|
||||
geoip2==3.0.0
|
||||
httplib2==0.17.0
|
||||
idna-ssl==1.1.0 ; python_version < '3.7'
|
||||
idna==2.9
|
||||
importlib-metadata==1.6.0 ; python_version < '3.8'
|
||||
isodate==0.6.0
|
||||
jbxapi==3.4.0
|
||||
jsonschema==3.2.0
|
||||
lief==0.10.1
|
||||
lxml==4.6.4
|
||||
maclookup==1.0.3
|
||||
markdownify==0.5.3
|
||||
markupsafe==2.1.1 ; python_version >= '3.7'
|
||||
mattermostdriver==7.3.2
|
||||
maxminddb==2.2.0 ; python_version >= '3.6'
|
||||
.
|
||||
more-itertools==8.14.0 ; python_version >= '3.5'
|
||||
msoffcrypto-tool==5.0.0 ; python_version >= '3' and platform_python_implementation != 'PyPy' or (platform_system != 'Windows' and platform_system != 'Darwin')
|
||||
multidict==6.0.2 ; python_version >= '3.7'
|
||||
mwdblib==4.3.1
|
||||
ndjson==0.3.1
|
||||
maxminddb==1.5.2
|
||||
multidict==4.7.5
|
||||
np==1.0.2
|
||||
numpy==1.21.6 ; python_version < '3.10' and platform_machine == 'aarch64'
|
||||
numpy==1.21.4
|
||||
oauth2==1.9.0.post1
|
||||
git+https://github.com/cartertemm/ODTReader.git/@49d6938693f6faa3ff09998f86dba551ae3a996b#egg=odtreader
|
||||
olefile==0.46 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
oletools==0.60.1
|
||||
opencv-python==4.6.0.66
|
||||
openpyxl==3.0.10
|
||||
packaging==21.3 ; python_version >= '3.6'
|
||||
pandas==1.3.5
|
||||
pandas-ods-reader==0.1.2
|
||||
passivetotal==2.5.9
|
||||
pcodedmp==1.2.6
|
||||
pdftotext==2.2.2
|
||||
pillow==9.2.0
|
||||
pkgutil-resolve-name==1.3.10 ; python_version < '3.9'
|
||||
progressbar2==4.0.0 ; python_full_version >= '3.7.0'
|
||||
psutil
|
||||
publicsuffixlist==0.8.0 ; python_version >= '2.6'
|
||||
git+https://github.com/D4-project/BGP-Ranking.git/@68de39f6c5196f796055c1ac34504054d688aa59#egg=pybgpranking&subdirectory=client
|
||||
pycparser==2.21
|
||||
pycryptodome==3.15.0 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||
pycryptodomex==3.15.0 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||
pydeep2==0.5.1
|
||||
git+https://github.com/sebdraven/pydnstrails@48c1f740025c51289f43a24863d1845ff12fd21a#egg=pydnstrails
|
||||
pyeupi==1.1
|
||||
pyfaup==1.2
|
||||
opencv-python==4.2.0.32
|
||||
pandas-ods-reader==0.1.4
|
||||
pandas==1.3.4
|
||||
passivetotal==1.0.31
|
||||
pdftotext==2.1.4
|
||||
pillow==7.0.0
|
||||
progressbar2==3.50.1
|
||||
psutil==5.7.0
|
||||
pycparser==2.20
|
||||
pycryptodome==3.9.7
|
||||
pycryptodomex==3.9.7
|
||||
pydeep==0.4
|
||||
pyeupi==1.0
|
||||
pygeoip==0.3.2
|
||||
pycountry==22.3.5
|
||||
pygments==2.13.0 ; python_version >= '3.6'
|
||||
git+https://github.com/MISP/PyIntel471.git@917272fafa8e12102329faca52173e90c5256968#egg=pyintel471
|
||||
git+https://github.com/D4-project/IPASN-History.git/@a2853c39265cecdd0c0d16850bd34621c0551b87#egg=pyipasnhistory&subdirectory=client
|
||||
pymisp[email,fileobjects,openioc,pdfexport,url]==2.4.162
|
||||
git+https://github.com/sebdraven/pyonyphe@d1d6741f8ea4475f3bb77ff20c876f08839cabd1#egg=pyonyphe
|
||||
pyparsing==2.4.7 ; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
pypdns==1.5.2
|
||||
pypssl==2.2
|
||||
pyrsistent==0.18.1 ; python_version >= '3.7'
|
||||
pytesseract==0.3.10
|
||||
python-baseconv==1.2.2
|
||||
python-dateutil==2.8.2 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
python-docx==0.8.11
|
||||
python-engineio==4.3.4 ; python_version >= '3.6'
|
||||
python-magic==0.4.27
|
||||
python-pptx==0.6.21
|
||||
python-socketio[client]==5.7.1 ; python_version >= '3.6'
|
||||
python-utils==3.3.3 ; python_version >= '3.7'
|
||||
pytz==2019.3
|
||||
pytz-deprecation-shim==0.1.0.post0 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'
|
||||
pyyaml==6.0 ; python_version >= '3.6'
|
||||
pyzbar==0.1.9
|
||||
pyzipper==0.3.6 ; python_version >= '3.5'
|
||||
rdflib==6.2.0 ; python_version >= '3.7'
|
||||
redis==4.3.4 ; python_version >= '3.6'
|
||||
regex==2022.3.2 ; python_version >= '3.6'
|
||||
reportlab==3.6.11
|
||||
requests==2.28.1
|
||||
requests-cache==0.6.4 ; python_version >= '3.6'
|
||||
requests-file==1.5.1
|
||||
rfc3986[idna2008]==1.5.0
|
||||
rich==12.5.1 ; python_full_version >= '3.6.3' and python_full_version < '4.0.0'
|
||||
rtfde==0.0.2
|
||||
secretstorage==3.3.3 ; sys_platform == 'linux'
|
||||
setuptools==65.4.0 ; python_version >= '3.7'
|
||||
shodan==1.28.0
|
||||
sigmatools==0.19.1
|
||||
simplejson==3.17.6 ; python_version >= '2.5' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
six==1.16.0 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
sniffio==1.3.0 ; python_version >= '3.7'
|
||||
socialscan==1.4.2
|
||||
socketio-client==0.5.7.4
|
||||
soupsieve==2.3.2.post1 ; python_version >= '3.6'
|
||||
sparqlwrapper==2.0.0
|
||||
stix2==3.0.1
|
||||
stix2-patterns==2.0.0
|
||||
tabulate==0.8.10 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||
tau-clients==0.2.9
|
||||
taxii2-client==2.3.0
|
||||
tldextract==3.3.1 ; python_version >= '3.7'
|
||||
tornado==6.2 ; python_version >= '3.7'
|
||||
tqdm==4.64.1 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
git+https://github.com/SteveClement/trustar-python.git@6954eae38e0c77eaeef26084b6c5fd033925c1c7#egg=trustar
|
||||
typing-extensions==4.3.0 ; python_version < '3.8'
|
||||
tzdata==2022.4 ; python_version >= '3.6'
|
||||
tzlocal==4.2 ; python_version >= '3.6'
|
||||
unicodecsv==0.14.1
|
||||
url-normalize==1.4.3 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'
|
||||
pyopenssl==19.1.0
|
||||
pyparsing==2.4.6
|
||||
pypdns==1.5.1
|
||||
pypssl==2.1
|
||||
pyrsistent==0.16.0
|
||||
pytesseract==0.3.3
|
||||
python-dateutil==2.8.2
|
||||
python-docx==0.8.10
|
||||
python-magic==0.4.15
|
||||
python-pptx==0.6.18
|
||||
python-utils==2.4.0
|
||||
pytz==2021.3
|
||||
pyyaml==5.3.1
|
||||
pyzbar==0.1.8
|
||||
pyzipper==0.3.1 ; python_version >= '3.5'
|
||||
rdflib==4.2.2
|
||||
redis==3.4.1
|
||||
reportlab==3.5.42
|
||||
requests-cache==0.5.2
|
||||
requests[security]==2.23.0
|
||||
shodan==1.22.0
|
||||
sigmatools==0.16.0
|
||||
six==1.16.0
|
||||
socketio-client==0.5.6
|
||||
soupsieve==2.0
|
||||
sparqlwrapper==1.8.5
|
||||
stix2-patterns==1.3.0
|
||||
tabulate==0.8.7
|
||||
tornado==6.0.4
|
||||
trustar==0.3.28
|
||||
url-normalize==1.4.1
|
||||
urlarchiver==0.2
|
||||
urllib3==1.26.12 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'
|
||||
urllib3==1.25.8
|
||||
validators==0.14.0
|
||||
vt-graph-api==2.2.0
|
||||
vt-py==0.17.1
|
||||
vulners==2.0.4
|
||||
wand==0.6.10
|
||||
websocket-client==1.4.1 ; python_version >= '3.7'
|
||||
websockets==10.3 ; python_version >= '3.7'
|
||||
wrapt==1.14.1 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||
xlrd==2.0.1
|
||||
xlsxwriter==3.0.3 ; python_version >= '3.4'
|
||||
vt-graph-api==1.0.1
|
||||
vulners==1.5.5
|
||||
wand==0.5.9
|
||||
websocket-client==0.57.0
|
||||
wrapt==1.12.1
|
||||
xlrd==1.2.0
|
||||
xlsxwriter==1.2.8
|
||||
yara-python==3.8.1
|
||||
yarl==1.8.1 ; python_version >= '3.7'
|
||||
zipp==3.8.1 ; python_version >= '3.7'
|
||||
yarl==1.4.2
|
||||
zipp==3.1.0
|
||||
|
|
|
@ -776,31 +776,6 @@ Module to query an IP ASN history service (https://github.com/D4-project/IPASN-H
|
|||
|
||||
-----
|
||||
|
||||
#### [ipinfo](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/ipinfo.py)
|
||||
|
||||
<img src=logos/ipinfo.png height=60>
|
||||
|
||||
An expansion module to query ipinfo.io to gather more information on a given IP address.
|
||||
- **features**:
|
||||
>The module takes an IP address attribute as input and queries the ipinfo.io API.
|
||||
>The geolocation information on the IP address is always returned.
|
||||
>
|
||||
>Depending on the subscription plan, the API returns different pieces of information then:
|
||||
>- With a basic plan (free) you get the AS number and the AS organisation name concatenated in the `org` field.
|
||||
>- With a paid subscription, the AS information is returned in the `asn` field with additional AS information, and depending on which plan the user has, you can also get information on the privacy method used to protect the IP address, the related domains, or the point of contact related to the IP address in case of an abuse.
|
||||
>
|
||||
>More information on the responses content is available in the [documentation](https://ipinfo.io/developers).
|
||||
- **input**:
|
||||
>IP address attribute.
|
||||
- **output**:
|
||||
>Additional information on the IP address, like its geolocation, the autonomous system it is included in, and the related domain(s).
|
||||
- **references**:
|
||||
>https://ipinfo.io/developers
|
||||
- **requirements**:
|
||||
>An ipinfo.io token
|
||||
|
||||
-----
|
||||
|
||||
#### [ipqs_fraud_and_risk_scoring](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py)
|
||||
|
||||
<img src=logos/ipqualityscore.png height=60>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 114 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.8 KiB |
|
@ -773,31 +773,6 @@ Module to query an IP ASN history service (https://github.com/D4-project/IPASN-H
|
|||
|
||||
-----
|
||||
|
||||
#### [ipinfo](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/ipinfo.py)
|
||||
|
||||
<img src=../logos/ipinfo.png height=60>
|
||||
|
||||
An expansion module to query ipinfo.io to gather more information on a given IP address.
|
||||
- **features**:
|
||||
>The module takes an IP address attribute as input and queries the ipinfo.io API.
|
||||
>The geolocation information on the IP address is always returned.
|
||||
>
|
||||
>Depending on the subscription plan, the API returns different pieces of information then:
|
||||
>- With a basic plan (free) you get the AS number and the AS organisation name concatenated in the `org` field.
|
||||
>- With a paid subscription, the AS information is returned in the `asn` field with additional AS information, and depending on which plan the user has, you can also get information on the privacy method used to protect the IP address, the related domains, or the point of contact related to the IP address in case of an abuse.
|
||||
>
|
||||
>More information on the responses content is available in the [documentation](https://ipinfo.io/developers).
|
||||
- **input**:
|
||||
>IP address attribute.
|
||||
- **output**:
|
||||
>Additional information on the IP address, like its geolocation, the autonomous system it is included in, and the related domain(s).
|
||||
- **references**:
|
||||
>https://ipinfo.io/developers
|
||||
- **requirements**:
|
||||
>An ipinfo.io token
|
||||
|
||||
-----
|
||||
|
||||
#### [ipqs_fraud_and_risk_scoring](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py)
|
||||
|
||||
<img src=../logos/ipqualityscore.png height=60>
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"description": "Hover module to lookup an IP in CrowdSec's CTI",
|
||||
"logo": "crowdsec.png",
|
||||
"requirements": [
|
||||
"A CrowdSec CTI API key. Get yours by following https://docs.crowdsec.net/docs/cti_api/getting_started/#getting-an-api-key"
|
||||
],
|
||||
"input": "An IP address.",
|
||||
"output": "IP Lookup information from CrowdSec CTI API",
|
||||
"references": [
|
||||
"https://www.crowdsec.net/",
|
||||
"https://docs.crowdsec.net/docs/cti_api/getting_started",
|
||||
"https://app.crowdsec.net/"
|
||||
],
|
||||
"features": "This module enables IP lookup from CrowdSec CTI API. It provides information about the IP, such as what kind of attacks it has been participant of as seen by CrowdSec's network. It also includes enrichment by CrowdSec like background noise score, aggressivity over time etc."
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"description": "An expansion module to query ipinfo.io to gather more information on a given IP address.",
|
||||
"logo": "ipinfo.png",
|
||||
"requirements": [
|
||||
"An ipinfo.io token"
|
||||
],
|
||||
"input": "IP address attribute.",
|
||||
"output": "Additional information on the IP address, like its geolocation, the autonomous system it is included in, and the related domain(s).",
|
||||
"references": [
|
||||
"https://ipinfo.io/developers"
|
||||
],
|
||||
"features": "The module takes an IP address attribute as input and queries the ipinfo.io API. \nThe geolocation information on the IP address is always returned.\n\nDepending on the subscription plan, the API returns different pieces of information then:\n- With a basic plan (free) you get the AS number and the AS organisation name concatenated in the `org` field.\n- With a paid subscription, the AS information is returned in the `asn` field with additional AS information, and depending on which plan the user has, you can also get information on the privacy method used to protect the IP address, the related domains, or the point of contact related to the IP address in case of an abuse.\n\nMore information on the responses content is available in the [documentation](https://ipinfo.io/developers)."
|
||||
}
|
|
@ -19,7 +19,7 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c
|
|||
'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich',
|
||||
'trustar_enrich', 'recordedfuture', 'html_to_markdown', 'socialscan', 'passive-ssh',
|
||||
'qintel_qsentry', 'mwdb', 'hashlookup', 'mmdb_lookup', 'ipqs_fraud_and_risk_scoring',
|
||||
'clamav', 'jinja_template_rendering','hyasinsight', 'variotdbs', 'crowdsec', 'extract_url_components']
|
||||
'clamav', 'jinja_template_rendering','hyasinsight', 'variotdbs']
|
||||
|
||||
|
||||
minimum_required_fields = ('type', 'uuid', 'value')
|
||||
|
|
|
@ -1,158 +0,0 @@
|
|||
import json
|
||||
|
||||
from pymisp import MISPEvent, MISPObject
|
||||
import pycountry
|
||||
import requests
|
||||
|
||||
mispattributes = {"input": ["ip-dst", "ip-src"], "output": ["text"]}
|
||||
moduleinfo = {
|
||||
"version": "1.0",
|
||||
"author": "Shivam Sandbhor <shivam@crowdsec.net>",
|
||||
"description": "Module to access CrowdSec CTI API.",
|
||||
"module-type": ["hover"],
|
||||
}
|
||||
moduleconfig = ["api_key", "api_version"]
|
||||
|
||||
|
||||
def handler(q=False):
|
||||
if q is False:
|
||||
return False
|
||||
|
||||
request = json.loads(q)
|
||||
if not request.get("config"):
|
||||
return {"error": "Missing CrowdSec Config"}
|
||||
|
||||
if not request["config"].get("api_key"):
|
||||
return {"error": "Missing CrowdSec API key"}
|
||||
|
||||
if not request["config"].get("api_version"):
|
||||
return {"error": "Missing CrowdSec API version parameter"}
|
||||
|
||||
if request["config"]["api_version"] == "v2":
|
||||
return _handler_v2(request)
|
||||
return {"error": f'API version {request["config"]["api_version"]} not supported'}
|
||||
|
||||
|
||||
def _handler_v2(request_data):
|
||||
if request_data.get("ip-dst"):
|
||||
ip = request_data.get("ip-dst")
|
||||
elif request_data.get("ip-src"):
|
||||
ip = request_data.get("ip-src")
|
||||
|
||||
crowdsec_cti = requests.get(
|
||||
f"https://cti.api.crowdsec.net/v2/smoke/{ip}",
|
||||
headers={
|
||||
"x-api-key": request_data["config"]["api_key"],
|
||||
"User-Agent": "crowdsec-misp/v1.0.0",
|
||||
},
|
||||
)
|
||||
crowdsec_cti.raise_for_status()
|
||||
crowdsec_cti = crowdsec_cti.json()
|
||||
|
||||
misp_event = MISPEvent()
|
||||
crowdsec_context_object = MISPObject("crowdsec-ip-context")
|
||||
crowdsec_context_object.add_attribute("IP Address", **{"type": "text", "value": ip})
|
||||
crowdsec_context_object.add_attribute(
|
||||
"IP Range", **{"type": "text", "value": crowdsec_cti["ip_range"]}
|
||||
)
|
||||
crowdsec_context_object.add_attribute(
|
||||
"IP Range Score", **{"type": "text", "value": crowdsec_cti["ip_range_score"]}
|
||||
)
|
||||
crowdsec_context_object.add_attribute(
|
||||
"Country",
|
||||
**{
|
||||
"type": "text",
|
||||
"value": get_country_name_from_alpha_2(crowdsec_cti["location"]["country"]),
|
||||
},
|
||||
)
|
||||
if crowdsec_cti["location"]["city"]:
|
||||
crowdsec_context_object.add_attribute(
|
||||
"City", **{"type": "text", "value": crowdsec_cti["location"]["city"]}
|
||||
)
|
||||
|
||||
crowdsec_context_object.add_attribute(
|
||||
"Latitude", **{"type": "float", "value": crowdsec_cti["location"]["latitude"]}
|
||||
)
|
||||
crowdsec_context_object.add_attribute(
|
||||
"Longitude", **{"type": "float", "value": crowdsec_cti["location"]["longitude"]}
|
||||
)
|
||||
|
||||
crowdsec_context_object.add_attribute(
|
||||
"AS Name", **{"type": "text", "value": crowdsec_cti["as_name"]}
|
||||
)
|
||||
|
||||
crowdsec_context_object.add_attribute(
|
||||
"AS Number", **{"type": "AS", "value": crowdsec_cti["as_num"]}
|
||||
)
|
||||
|
||||
crowdsec_context_object.add_attribute(
|
||||
"Reverse DNS", **{"type": "domain", "value": crowdsec_cti["reverse_dns"]}
|
||||
)
|
||||
|
||||
crowdsec_context_object.add_attribute(
|
||||
"Attack Categories",
|
||||
**{
|
||||
"type": "text",
|
||||
"value": ",".join(
|
||||
[attack_category["label"] for attack_category in crowdsec_cti["behaviors"]]
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
crowdsec_context_object.add_attribute(
|
||||
"Triggered Scenarios",
|
||||
**{
|
||||
"type": "text",
|
||||
"value": ",".join([scenario["name"] for scenario in crowdsec_cti["attack_details"]]),
|
||||
},
|
||||
)
|
||||
|
||||
crowdsec_context_object.add_attribute(
|
||||
"Top 10 Target Countries",
|
||||
**{
|
||||
"type": "float",
|
||||
"value": ",".join(
|
||||
map(get_country_name_from_alpha_2, crowdsec_cti["target_countries"].keys())
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
crowdsec_context_object.add_attribute(
|
||||
"Trust", **{"type": "float", "value": crowdsec_cti["scores"]["overall"]["trust"]}
|
||||
)
|
||||
|
||||
crowdsec_context_object.add_attribute(
|
||||
"First Seen", **{"type": "datetime", "value": crowdsec_cti["history"]["first_seen"]}
|
||||
)
|
||||
|
||||
crowdsec_context_object.add_attribute(
|
||||
"Last Seen", **{"type": "datetime", "value": crowdsec_cti["history"]["last_seen"]}
|
||||
)
|
||||
|
||||
for time_period, indicators in crowdsec_cti["scores"].items():
|
||||
tp = " ".join(map(str.capitalize, time_period.split("_")))
|
||||
|
||||
for indicator_type, indicator_value in indicators.items():
|
||||
crowdsec_context_object.add_attribute(
|
||||
f"{tp} {indicator_type.capitalize()}", **{"type": "float", "value": indicator_value}
|
||||
)
|
||||
|
||||
misp_event.add_object(crowdsec_context_object)
|
||||
|
||||
event = json.loads(misp_event.to_json())
|
||||
results = {key: event[key] for key in ("Attribute", "Object") if (key in event and event[key])}
|
||||
return {"results": results}
|
||||
|
||||
|
||||
def get_country_name_from_alpha_2(alpha_2):
|
||||
country_info = pycountry.countries.get(alpha_2=alpha_2)
|
||||
return country_info.name
|
||||
|
||||
|
||||
def introspection():
|
||||
return mispattributes
|
||||
|
||||
|
||||
def version():
|
||||
moduleinfo["config"] = moduleconfig
|
||||
return moduleinfo
|
|
@ -2,10 +2,7 @@ import json
|
|||
import sys
|
||||
|
||||
try:
|
||||
original_path = sys.path
|
||||
sys.path = original_path[1:]
|
||||
import dns.resolver
|
||||
sys.path = original_path
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.timeout = 0.2
|
||||
resolver.lifetime = 0.2
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
import json
|
||||
from pymisp import MISPEvent, MISPObject
|
||||
from . import check_input_attribute, standard_error_message
|
||||
from pyfaup.faup import Faup
|
||||
|
||||
misperrors = {'error': 'Error'}
|
||||
mispattributes = {'input': ['url'], 'format': 'misp_standard'}
|
||||
moduleinfo = {'version': '1', 'author': 'MISP Team',
|
||||
'description': "Extract URL components",
|
||||
'module-type': ['expansion', 'hover']}
|
||||
moduleconfig = []
|
||||
|
||||
|
||||
def createObjectFromURL(url):
|
||||
f = Faup()
|
||||
f.decode(url)
|
||||
parsed = f.get()
|
||||
obj = MISPObject('url')
|
||||
obj.add_attribute('url', type='url', value=url)
|
||||
if parsed['tld'] is not None:
|
||||
obj.add_attribute('tld', type='text', value=parsed['tld'])
|
||||
if parsed['subdomain'] is not None:
|
||||
obj.add_attribute('subdomain', type='text', value=parsed['subdomain'])
|
||||
obj.add_attribute('scheme', type='text', value=parsed['scheme'])
|
||||
obj.add_attribute('resource_path', type='text', value=parsed['resource_path'])
|
||||
obj.add_attribute('query_string', type='text', value=parsed['query_string'])
|
||||
obj.add_attribute('port', type='port', value=parsed['port'])
|
||||
obj.add_attribute('host', type='hostname', value=parsed['host'])
|
||||
if parsed['fragment'] is not None:
|
||||
obj.add_attribute('fragment', type='text', value=parsed['fragment'])
|
||||
obj.add_attribute('domain_without_tld', type='text', value=parsed['domain_without_tld'])
|
||||
obj.add_attribute('domain', type='domain', value=parsed['domain'])
|
||||
return obj
|
||||
|
||||
|
||||
def createEvent(urlObject, attributeUUID, urlAttribute):
|
||||
mispEvent = MISPEvent()
|
||||
mispEvent.add_attribute(**urlAttribute)
|
||||
urlObject.add_reference(attributeUUID, 'generated-from')
|
||||
mispEvent.add_object(urlObject)
|
||||
return mispEvent
|
||||
|
||||
def handler(q=False):
|
||||
if q is False:
|
||||
return False
|
||||
request = json.loads(q)
|
||||
if not request.get('attribute') or not check_input_attribute(request['attribute']):
|
||||
return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'}
|
||||
attribute = request['attribute']
|
||||
|
||||
if attribute['type'] not in mispattributes['input']:
|
||||
return {'error': 'Bad attribute type'}
|
||||
|
||||
url = attribute['value']
|
||||
urlObject = createObjectFromURL(url)
|
||||
|
||||
event = createEvent(urlObject, attribute['uuid'], attribute)
|
||||
event = json.loads(event.to_json())
|
||||
|
||||
result = {'results': {'Object': event['Object']}}
|
||||
return result
|
||||
|
||||
|
||||
def introspection():
|
||||
return mispattributes
|
||||
|
||||
|
||||
def version():
|
||||
moduleinfo['config'] = moduleconfig
|
||||
return moduleinfo
|
|
@ -1,333 +1,254 @@
|
|||
import ipaddress
|
||||
import json
|
||||
import logging
|
||||
|
||||
|
||||
try:
|
||||
from greynoise import GreyNoise
|
||||
except ImportError:
|
||||
print("greynoise module not installed.")
|
||||
from pymisp import MISPAttribute, MISPEvent, MISPObject
|
||||
|
||||
from . import check_input_attribute, standard_error_message
|
||||
|
||||
logger = logging.getLogger("greynoise")
|
||||
logger.setLevel(logging.INFO)
|
||||
import requests
|
||||
from pymisp import MISPEvent, MISPObject
|
||||
|
||||
misperrors = {"error": "Error"}
|
||||
mispattributes = {"input": ["ip-src", "ip-dst", "vulnerability"], "format": "misp_standard"}
|
||||
mispattributes = {"input": ["ip-dst", "ip-src", "vulnerability"], "output": ["text"]}
|
||||
moduleinfo = {
|
||||
"version": "1.2",
|
||||
"version": "1.1",
|
||||
"author": "Brad Chiappetta <brad@greynoise.io>",
|
||||
"description": "Used to query IP and CVE intel from GreyNoise",
|
||||
"module-type": ["expansion", "hover"],
|
||||
"description": "Module to access GreyNoise.io API.",
|
||||
"module-type": ["hover"],
|
||||
}
|
||||
moduleconfig = ["api_key", "api_type"]
|
||||
codes_mapping = {
|
||||
"0x00": "The IP has never been observed scanning the Internet",
|
||||
"0x01": "The IP has been observed by the GreyNoise sensor network",
|
||||
"0x02": "The IP has been observed scanning the GreyNoise sensor network, "
|
||||
"but has not completed a full connection, meaning this can be spoofed",
|
||||
"0x03": "The IP is adjacent to another host that has been directly observed by the GreyNoise sensor network",
|
||||
"0x04": "Reserved",
|
||||
"0x05": "This IP is commonly spoofed in Internet-scan activity",
|
||||
"0x06": "This IP has been observed as noise, but this host belongs to a cloud provider where IPs can be "
|
||||
"cycled frequently",
|
||||
"0x07": "This IP is invalid",
|
||||
"0x08": "This IP was classified as noise, but has not been observed engaging in Internet-wide scans or "
|
||||
"attacks in over 90 days",
|
||||
"0x09": "IP was found in RIOT",
|
||||
"0x10": "IP has been observed by the GreyNoise sensor network and is in RIOT",
|
||||
}
|
||||
vulnerability_mapping = {
|
||||
"id": ("vulnerability", "CVE #"),
|
||||
"details": ("text", "Details"),
|
||||
"count": ("text", "Total Scanner Count"),
|
||||
}
|
||||
enterprise_context_basic_mapping = {"ip": ("text", "IP Address"), "code_message": ("text", "Code Message")}
|
||||
enterprise_context_advanced_mapping = {
|
||||
"noise": ("text", "Is Internet Background Noise"),
|
||||
"link": ("link", "Visualizer Link"),
|
||||
"classification": ("text", "Classification"),
|
||||
"actor": ("text", "Actor"),
|
||||
"tags": ("text", "Tags"),
|
||||
"cve": ("text", "CVEs"),
|
||||
"first_seen": ("text", "First Seen Scanning"),
|
||||
"last_seen": ("text", "Last Seen Scanning"),
|
||||
"vpn": ("text", "Known VPN Service"),
|
||||
"vpn_service": ("text", "VPN Service Name"),
|
||||
"bot": ("text", "Known BOT"),
|
||||
}
|
||||
enterprise_context_advanced_metadata_mapping = {
|
||||
"asn": ("text", "ASN"),
|
||||
"rdns": ("text", "rDNS"),
|
||||
"category": ("text", "Category"),
|
||||
"tor": ("text", "Known Tor Exit Node"),
|
||||
"region": ("text", "Region"),
|
||||
"city": ("text", "City"),
|
||||
"country": ("text", "Country"),
|
||||
"country_code": ("text", "Country Code"),
|
||||
"organization": ("text", "Organization"),
|
||||
}
|
||||
enterprise_riot_mapping = {
|
||||
"riot": ("text", "Is Common Business Service"),
|
||||
"link": ("link", "Visualizer Link"),
|
||||
"category": ("text", "RIOT Category"),
|
||||
"name": ("text", "Provider Name"),
|
||||
"trust_level": ("text", "RIOT Trust Level"),
|
||||
"last_updated": ("text", "Last Updated"),
|
||||
}
|
||||
community_found_mapping = {
|
||||
"ip": ("text", "IP Address"),
|
||||
"noise": ("text", "Is Internet Background Noise"),
|
||||
"riot": ("text", "Is Common Business Service"),
|
||||
"classification": ("text", "Classification"),
|
||||
"last_seen": ("text", "Last Seen"),
|
||||
"name": ("text", "Name"),
|
||||
"link": ("link", "Visualizer Link"),
|
||||
}
|
||||
community_not_found_mapping = {
|
||||
"ip": ("text", "IP Address"),
|
||||
"noise": ("text", "Is Internet Background Noise"),
|
||||
"riot": ("text", "Is Common Business Service"),
|
||||
"message": ("text", "Message"),
|
||||
}
|
||||
misp_event = MISPEvent()
|
||||
|
||||
|
||||
class GreyNoiseParser:
|
||||
def __init__(self, attribute):
|
||||
self.misp_event = MISPEvent()
|
||||
self.attribute = MISPAttribute()
|
||||
self.attribute.from_dict(**attribute)
|
||||
self.misp_event.add_attribute(**self.attribute)
|
||||
self.ip_address_enrich_mapping = {
|
||||
"noise": {"type": "boolean", "object_relation": "noise"},
|
||||
"riot": {"type": "boolean", "object_relation": "riot"},
|
||||
"classification": {"type": "text", "object_relation": "classification"},
|
||||
"actor": {"type": "text", "object_relation": "actor"},
|
||||
"trust_level": {"type": "text", "object_relation": "trust-level"},
|
||||
"name": {"type": "text", "object_relation": "provider"},
|
||||
"first_seen": {"type": "datetime", "object_relation": "first-seen"},
|
||||
"last_seen": {"type": "datetime", "object_relation": "last-seen"},
|
||||
"link": {"type": "url", "object_relation": "link"},
|
||||
"last_updated": {"type": "datetime", "object_relation": "last-seen"},
|
||||
}
|
||||
self.ip_address_hover_mapping = {
|
||||
"noise": {"type": "boolean", "object_relation": "noise"},
|
||||
"riot": {"type": "boolean", "object_relation": "riot"},
|
||||
"classification": {"type": "text", "object_relation": "classification"},
|
||||
"actor": {"type": "text", "object_relation": "actor"},
|
||||
"tags": {"type": "text", "object_relation": "tags"},
|
||||
"cve": {"type": "text", "object_relation": "cve"},
|
||||
"vpn": {"type": "text", "object_relation": "vpn"},
|
||||
"vpn_service": {"type": "text", "object_relation": "vpn_service"},
|
||||
"bot": {"type": "text", "object_relation": "bot"},
|
||||
"first_seen": {"type": "datetime", "object_relation": "first-seen"},
|
||||
"last_seen": {"type": "datetime", "object_relation": "last-seen"},
|
||||
"spoofable": {"type": "datetime", "object_relation": "spoofable"},
|
||||
"link": {"type": "url", "object_relation": "link"},
|
||||
"category": {"type": "text", "object_relation": "category"},
|
||||
"name": {"type": "text", "object_relation": "provider"},
|
||||
"trust_level": {"type": "text", "object_relation": "trust-level"},
|
||||
"last_updated": {"type": "datetime", "object_relation": "last_updated"},
|
||||
}
|
||||
self.ip_address_metadata_mapping = {
|
||||
"tor": {"type": "text", "object_relation": "tor"},
|
||||
"asn": {"type": "AS", "object_relation": "asn"},
|
||||
"city": {"type": "text", "object_relation": "city"},
|
||||
"country_code": {"type": "text", "object_relation": "country-code"},
|
||||
"country": {"type": "text", "object_relation": "country"},
|
||||
"organization": {"type": "text", "object_relation": "organization"},
|
||||
"destination_country_codes": {"type": "text", "object_relation": "destination-country-codes"},
|
||||
"destination_countries": {"type": "text", "object_relation": "destination-countries"},
|
||||
"category": {"type": "text", "object_relation": "category"},
|
||||
"rdns": {"type": "text", "object_relation": "rdns"},
|
||||
}
|
||||
self.vulnerability_mapping = {
|
||||
"id": {"type": "text", "object_relation": "id"},
|
||||
"details": {"type": "text", "object_relation": "details"},
|
||||
"count": {"type": "text", "object_relation": "total-count"},
|
||||
"benign": {"type": "text", "object_relation": "benign-count"},
|
||||
"malicious": {"type": "text", "object_relation": "malicious-count"},
|
||||
"unknown": {"type": "text", "object_relation": "unknown-count"},
|
||||
}
|
||||
def handler(q=False): # noqa: C901
|
||||
if q is False:
|
||||
return False
|
||||
request = json.loads(q)
|
||||
if not request.get("config") or not request["config"].get("api_key"):
|
||||
return {"error": "Missing Greynoise API key."}
|
||||
|
||||
def query_greynoise_ip_hover(self, api_key, api_type):
|
||||
if api_type == "enterprise":
|
||||
logger.info(f"Starting hover enrichment for: {self.attribute.value} via GreyNoise ENT API")
|
||||
integration_name = "greynoise-misp-module-{}".format(moduleinfo["version"])
|
||||
session = GreyNoise(api_key=api_key, integration_name=integration_name)
|
||||
quick_response = session.quick(self.attribute.value)
|
||||
if len(quick_response) != 1:
|
||||
misperrors["error"] = "Quick IP lookup returned unexpected response"
|
||||
return misperrors
|
||||
else:
|
||||
quick_response = quick_response[0]
|
||||
context_response = session.ip(self.attribute.value)
|
||||
riot_response = session.riot(self.attribute.value)
|
||||
headers = {
|
||||
"Accept": "application/json",
|
||||
"key": request["config"]["api_key"],
|
||||
"User-Agent": "greynoise-misp-module-{}".format(moduleinfo["version"]),
|
||||
}
|
||||
|
||||
if riot_response and "trust_level" in riot_response:
|
||||
if riot_response["trust_level"] == "1":
|
||||
riot_response["trust_level"] = "1 - Reasonably Ignore"
|
||||
if riot_response["trust_level"] == "2":
|
||||
riot_response["trust_level"] = "2 - Commonly Seen"
|
||||
if not (request.get("vulnerability") or request.get("ip-dst") or request.get("ip-src")):
|
||||
misperrors["error"] = "Vulnerability id missing"
|
||||
return misperrors
|
||||
|
||||
if context_response and riot_response:
|
||||
response = context_response.copy()
|
||||
response.update(riot_response)
|
||||
response.update(quick_response)
|
||||
elif context_response:
|
||||
response = context_response.copy()
|
||||
response.update(quick_response)
|
||||
elif riot_response:
|
||||
response = riot_response.copy()
|
||||
response.update(quick_response)
|
||||
ip = ""
|
||||
vulnerability = ""
|
||||
|
||||
response["link"] = "https://viz.greynoise.io/ip/" + self.attribute.value
|
||||
if request.get("ip-dst"):
|
||||
ip = request.get("ip-dst")
|
||||
elif request.get("ip-src"):
|
||||
ip = request.get("ip-src")
|
||||
else:
|
||||
vulnerability = request.get("vulnerability")
|
||||
|
||||
ip_address_attributes = []
|
||||
for feature, mapping in self.ip_address_hover_mapping.items():
|
||||
logger.debug(f"Checking feature {feature}")
|
||||
if response.get(feature):
|
||||
if feature in ["cve", "tags"]:
|
||||
response[feature] = ", ".join(response[feature])
|
||||
if feature == "vpn_service" and response[feature] == "N/A":
|
||||
continue
|
||||
if feature == "actor" and response[feature] == "unknown":
|
||||
continue
|
||||
attribute = {"value": response[feature]}
|
||||
logger.debug(f"Adding Feature: {feature}, Attribute: {attribute}")
|
||||
attribute.update(mapping)
|
||||
ip_address_attributes.append(attribute)
|
||||
if "metadata" in context_response:
|
||||
for feature, mapping in self.ip_address_metadata_mapping.items():
|
||||
logger.debug(f"Checking metadata feature {feature}")
|
||||
if response["metadata"].get(feature):
|
||||
if feature in ["destination_countries", "destination_country_codes"]:
|
||||
response["metadata"][feature] = ", ".join(response["metadata"][feature])
|
||||
attribute = {"value": response["metadata"][feature]}
|
||||
logger.debug(f"Adding Feature: {feature}, Attribute: {attribute}")
|
||||
attribute.update(mapping)
|
||||
ip_address_attributes.append(attribute)
|
||||
if ip_address_attributes:
|
||||
logger.debug("creating greynoise ip object")
|
||||
gn_ip_object = MISPObject("greynoise-ip-details")
|
||||
for attribute in ip_address_attributes:
|
||||
logger.debug(f"adding attribute {attribute}")
|
||||
gn_ip_object.add_attribute(**attribute)
|
||||
logger.debug(f"attribute id: {self.attribute.uuid}")
|
||||
gn_ip_object.add_reference(self.attribute.uuid, "describes")
|
||||
self.misp_event.add_object(gn_ip_object)
|
||||
if ip:
|
||||
if request["config"]["api_type"] and request["config"]["api_type"] == "enterprise":
|
||||
greynoise_api_url = "https://api.greynoise.io/v2/noise/quick/"
|
||||
else:
|
||||
logger.info(f"Starting hover enrichment for: {self.attribute.value} via GreyNoise Community API")
|
||||
integration_name = "greynoise-community-misp-module-{}".format(moduleinfo["version"])
|
||||
session = GreyNoise(api_key=api_key, integration_name=integration_name, offering="community")
|
||||
community_response = session.ip(self.attribute.value)
|
||||
greynoise_api_url = "https://api.greynoise.io/v3/community/"
|
||||
|
||||
if "noise" in community_response and community_response["noise"]:
|
||||
community_response["actor"] = community_response["name"]
|
||||
community_response.pop("name")
|
||||
response = requests.get(f"{greynoise_api_url}{ip}", headers=headers) # Real request for IP Query
|
||||
if response.status_code == 200:
|
||||
if request["config"]["api_type"] == "enterprise":
|
||||
response = response.json()
|
||||
enterprise_context_object = MISPObject("greynoise-ip-context")
|
||||
for feature in ("ip", "code_message"):
|
||||
if feature == "code_message":
|
||||
value = codes_mapping[response.get("code")]
|
||||
else:
|
||||
value = response.get(feature)
|
||||
if value:
|
||||
attribute_type, relation = enterprise_context_basic_mapping[feature]
|
||||
enterprise_context_object.add_attribute(relation, **{"type": attribute_type, "value": value})
|
||||
if response["noise"]:
|
||||
greynoise_api_url = "https://api.greynoise.io/v2/noise/context/"
|
||||
context_response = requests.get(f"{greynoise_api_url}{ip}", headers=headers)
|
||||
context_response = context_response.json()
|
||||
context_response["link"] = "https://www.greynoise.io/viz/ip/" + ip
|
||||
if "tags" in context_response:
|
||||
context_response["tags"] = ",".join(context_response["tags"])
|
||||
if "cve" in context_response:
|
||||
context_response["cve"] = ",".join(context_response["cve"])
|
||||
for feature in enterprise_context_advanced_mapping.keys():
|
||||
value = context_response.get(feature)
|
||||
if value:
|
||||
attribute_type, relation = enterprise_context_advanced_mapping[feature]
|
||||
enterprise_context_object.add_attribute(
|
||||
relation, **{"type": attribute_type, "value": value}
|
||||
)
|
||||
for feature in enterprise_context_advanced_metadata_mapping.keys():
|
||||
value = context_response["metadata"].get(feature)
|
||||
if value:
|
||||
attribute_type, relation = enterprise_context_advanced_metadata_mapping[feature]
|
||||
enterprise_context_object.add_attribute(
|
||||
relation, **{"type": attribute_type, "value": value}
|
||||
)
|
||||
|
||||
ip_address_attributes = []
|
||||
for feature, mapping in self.ip_address_hover_mapping.items():
|
||||
if community_response.get(feature):
|
||||
if feature == "actor" and community_response[feature] == "unknown":
|
||||
continue
|
||||
attribute = {"value": community_response[feature]}
|
||||
attribute.update(mapping)
|
||||
ip_address_attributes.append(attribute)
|
||||
if ip_address_attributes:
|
||||
ip_address_object = MISPObject("greynoise-ip-details")
|
||||
for attribute in ip_address_attributes:
|
||||
ip_address_object.add_attribute(**attribute)
|
||||
ip_address_object.add_reference(self.attribute.uuid, "describes")
|
||||
self.misp_event.add_object(ip_address_object)
|
||||
|
||||
def query_greynoise_ip_expansion(self, api_key, api_type):
|
||||
if api_type == "enterprise":
|
||||
logger.info(f"Starting expansion enrichment for: {self.attribute.value} via GreyNoise ENT API")
|
||||
integration_name = "greynoise-misp-module-{}".format(moduleinfo["version"])
|
||||
session = GreyNoise(api_key=api_key, integration_name=integration_name)
|
||||
quick_response = session.quick(self.attribute.value)
|
||||
if len(quick_response) != 1:
|
||||
misperrors["error"] = "Quick IP lookup returned unexpected response"
|
||||
return misperrors
|
||||
if response["riot"]:
|
||||
greynoise_api_url = "https://api.greynoise.io/v2/riot/"
|
||||
riot_response = requests.get(f"{greynoise_api_url}{ip}", headers=headers)
|
||||
riot_response = riot_response.json()
|
||||
riot_response["link"] = "https://www.greynoise.io/viz/riot/" + ip
|
||||
for feature in enterprise_riot_mapping.keys():
|
||||
value = riot_response.get(feature)
|
||||
if value:
|
||||
attribute_type, relation = enterprise_riot_mapping[feature]
|
||||
enterprise_context_object.add_attribute(
|
||||
relation, **{"type": attribute_type, "value": value}
|
||||
)
|
||||
misp_event.add_object(enterprise_context_object)
|
||||
event = json.loads(misp_event.to_json())
|
||||
results = {key: event[key] for key in ("Attribute", "Object") if (key in event and event[key])}
|
||||
return {"results": results}
|
||||
else:
|
||||
quick_response = quick_response[0]
|
||||
context_response = session.ip(self.attribute.value)
|
||||
riot_response = session.riot(self.attribute.value)
|
||||
response = response.json()
|
||||
community_context_object = MISPObject("greynoise-community-ip-context")
|
||||
for feature in community_found_mapping.keys():
|
||||
value = response.get(feature)
|
||||
if value:
|
||||
attribute_type, relation = community_found_mapping[feature]
|
||||
community_context_object.add_attribute(relation, **{"type": attribute_type, "value": value})
|
||||
misp_event.add_object(community_context_object)
|
||||
event = json.loads(misp_event.to_json())
|
||||
results = {key: event[key] for key in ("Attribute", "Object") if (key in event and event[key])}
|
||||
return {"results": results}
|
||||
if response.status_code == 404 and request["config"]["api_type"] != "enterprise":
|
||||
response = response.json()
|
||||
community_context_object = MISPObject("greynoise-community-ip-context")
|
||||
for feature in community_not_found_mapping.keys():
|
||||
value = response.get(feature)
|
||||
if value:
|
||||
attribute_type, relation = community_not_found_mapping[feature]
|
||||
community_context_object.add_attribute(relation, **{"type": attribute_type, "value": value})
|
||||
misp_event.add_object(community_context_object)
|
||||
event = json.loads(misp_event.to_json())
|
||||
results = {key: event[key] for key in ("Attribute", "Object") if (key in event and event[key])}
|
||||
return {"results": results}
|
||||
|
||||
if riot_response and "trust_level" in riot_response:
|
||||
if riot_response["trust_level"] == "1":
|
||||
riot_response["trust_level"] = "1 - Reasonably Ignore"
|
||||
if riot_response["trust_level"] == "2":
|
||||
riot_response["trust_level"] = "2 - Commonly Seen"
|
||||
|
||||
if context_response and riot_response:
|
||||
response = context_response.copy()
|
||||
response.update(riot_response)
|
||||
response.update(quick_response)
|
||||
elif context_response:
|
||||
response = context_response.copy()
|
||||
response.update(quick_response)
|
||||
elif riot_response:
|
||||
response = riot_response.copy()
|
||||
response.update(quick_response)
|
||||
|
||||
response["link"] = "https://viz.greynoise.io/ip/" + self.attribute.value
|
||||
|
||||
ip_address_attributes = []
|
||||
for feature, mapping in self.ip_address_enrich_mapping.items():
|
||||
logger.debug(f"Checking feature {feature}")
|
||||
if response.get(feature):
|
||||
if feature == "actor" and response[feature] == "unknown":
|
||||
continue
|
||||
attribute = {"value": response[feature]}
|
||||
logger.debug(f"Adding Feature: {feature}, Attribute: {attribute}")
|
||||
attribute.update(mapping)
|
||||
ip_address_attributes.append(attribute)
|
||||
if ip_address_attributes:
|
||||
logger.debug("creating greynoise ip object")
|
||||
gn_ip_object = MISPObject("greynoise-ip")
|
||||
for attribute in ip_address_attributes:
|
||||
logger.debug(f"adding attribute {attribute}")
|
||||
gn_ip_object.add_attribute(**attribute)
|
||||
logger.debug(f"attribute id: {self.attribute.uuid}")
|
||||
gn_ip_object.add_reference(self.attribute.uuid, "describes")
|
||||
self.misp_event.add_object(gn_ip_object)
|
||||
else:
|
||||
logger.info(f"Starting expansion enrichment for: {self.attribute.value} via GreyNoise Community API")
|
||||
integration_name = "greynoise-community-misp-module-{}".format(moduleinfo["version"])
|
||||
session = GreyNoise(api_key=api_key, integration_name=integration_name, offering="community")
|
||||
community_response = session.ip(self.attribute.value)
|
||||
|
||||
if "noise" in community_response and community_response["noise"]:
|
||||
community_response["actor"] = community_response["name"]
|
||||
community_response.pop("name")
|
||||
|
||||
ip_address_attributes = []
|
||||
for feature, mapping in self.ip_address_enrich_mapping.items():
|
||||
if community_response.get(feature):
|
||||
if feature == "actor" and community_response[feature] == "unknown":
|
||||
continue
|
||||
attribute = {"value": community_response[feature]}
|
||||
attribute.update(mapping)
|
||||
ip_address_attributes.append(attribute)
|
||||
if ip_address_attributes:
|
||||
ip_address_object = MISPObject("greynoise-ip")
|
||||
for attribute in ip_address_attributes:
|
||||
ip_address_object.add_attribute(**attribute)
|
||||
ip_address_object.add_reference(self.attribute.uuid, "describes")
|
||||
self.misp_event.add_object(ip_address_object)
|
||||
|
||||
def query_greynoise_vulnerability(self, api_key, api_type):
|
||||
if api_type == "enterprise":
|
||||
logger.info(f"Starting expansion enrichment for: {self.attribute.value} via GreyNoise ENT API")
|
||||
integration_name = "greynoise-misp-module-{}".format(moduleinfo["version"])
|
||||
session = GreyNoise(api_key=api_key, integration_name=integration_name)
|
||||
querystring = f"last_seen:1w cve:{self.attribute.value}"
|
||||
if vulnerability:
|
||||
if request["config"]["api_type"] and request["config"]["api_type"] == "enterprise":
|
||||
greynoise_api_url = "https://api.greynoise.io/v2/experimental/gnql/stats"
|
||||
querystring = {"query": f"last_seen:1w cve:{vulnerability}"}
|
||||
else:
|
||||
misperrors["error"] = "Vulnerability Not Supported with Community API Key"
|
||||
return misperrors
|
||||
|
||||
response = session.stats(querystring)
|
||||
response = requests.get(f"{greynoise_api_url}", headers=headers, params=querystring) # Real request
|
||||
|
||||
if "stats" in response:
|
||||
if response.status_code == 200:
|
||||
response = response.json()
|
||||
vulnerability_object = MISPObject("greynoise-vuln-info")
|
||||
response["details"] = (
|
||||
"The IP count below reflects the number of IPs seen "
|
||||
"by GreyNoise in the last 7 days scanning for this CVE."
|
||||
)
|
||||
response["id"] = self.attribute.value
|
||||
response["id"] = vulnerability
|
||||
for feature in ("id", "details", "count"):
|
||||
value = response.get(feature)
|
||||
if value:
|
||||
attribute_type, relation = vulnerability_mapping[feature]
|
||||
vulnerability_object.add_attribute(relation, **{"type": attribute_type, "value": value})
|
||||
classifications = response["stats"].get("classifications")
|
||||
for item in classifications:
|
||||
if item["classification"] == "benign":
|
||||
value = item["count"]
|
||||
response["benign"] = value
|
||||
attribute_type, relation = ("text", "Benign Scanner Count")
|
||||
vulnerability_object.add_attribute(relation, **{"type": attribute_type, "value": value})
|
||||
if item["classification"] == "unknown":
|
||||
value = item["count"]
|
||||
response["unknown"] = value
|
||||
attribute_type, relation = ("text", "Unknown Scanner Count")
|
||||
vulnerability_object.add_attribute(relation, **{"type": attribute_type, "value": value})
|
||||
if item["classification"] == "malicious":
|
||||
value = item["count"]
|
||||
response["malicious"] = value
|
||||
vulnerability_attributes = []
|
||||
for feature, mapping in self.vulnerability_mapping.items():
|
||||
if response.get(feature):
|
||||
attribute = {"value": response[feature]}
|
||||
attribute.update(mapping)
|
||||
vulnerability_attributes.append(attribute)
|
||||
if vulnerability_attributes:
|
||||
vulnerability_object = MISPObject("greynoise-vuln-info")
|
||||
for attribute in vulnerability_attributes:
|
||||
vulnerability_object.add_attribute(**attribute)
|
||||
vulnerability_object.add_reference(self.attribute.uuid, "describes")
|
||||
self.misp_event.add_object(vulnerability_object)
|
||||
attribute_type, relation = ("text", "Malicious Scanner Count")
|
||||
vulnerability_object.add_attribute(relation, **{"type": attribute_type, "value": value})
|
||||
misp_event.add_object(vulnerability_object)
|
||||
event = json.loads(misp_event.to_json())
|
||||
results = {key: event[key] for key in ("Attribute", "Object") if (key in event and event[key])}
|
||||
return {"results": results}
|
||||
|
||||
def get_result(self):
|
||||
event = json.loads(self.misp_event.to_json())
|
||||
results = {key: event[key] for key in ("Attribute", "Object") if (key in event and event[key])}
|
||||
return {"results": results}
|
||||
|
||||
|
||||
def handler(q=False):
|
||||
if q is False:
|
||||
return False
|
||||
request = json.loads(q)
|
||||
if not request.get("config", {}).get("api_key"):
|
||||
return {"error": "GreyNoise API Key required, but missing"}
|
||||
if not request.get("config", {}).get("api_type"):
|
||||
return {"error": "GreyNoise API type of enterprise or community required, but missing"}
|
||||
if not request.get("attribute") or not check_input_attribute(request["attribute"]):
|
||||
return {"error": f"{standard_error_message}, which should contain at least a type, a value and an uuid."}
|
||||
attribute = request["attribute"]
|
||||
if attribute["type"] not in mispattributes["input"]:
|
||||
return {"error": "Unsupported attribute type."}
|
||||
greynoise_parser = GreyNoiseParser(attribute)
|
||||
|
||||
if attribute["type"] in ["ip-dst", "ip-src"]:
|
||||
try:
|
||||
ipaddress.IPv4Address(attribute["value"])
|
||||
if "persistent" in request:
|
||||
greynoise_parser.query_greynoise_ip_hover(request["config"]["api_key"], request["config"]["api_type"])
|
||||
else:
|
||||
greynoise_parser.query_greynoise_ip_expansion(request["config"]["api_key"], request["config"]["api_type"])
|
||||
except ValueError:
|
||||
return {"error": "Not a valid IPv4 address"}
|
||||
|
||||
if attribute["type"] == "vulnerability":
|
||||
greynoise_parser.query_greynoise_vulnerability(request["config"]["api_key"], request["config"]["api_type"])
|
||||
|
||||
return greynoise_parser.get_result()
|
||||
# There is an error
|
||||
errors = {
|
||||
400: "Bad request.",
|
||||
404: "IP not observed scanning the internet or contained in RIOT data set.",
|
||||
401: "Unauthorized. Please check your API key.",
|
||||
429: "Too many requests. You've hit the rate-limit.",
|
||||
}
|
||||
try:
|
||||
misperrors["error"] = errors[response.status_code]
|
||||
except KeyError:
|
||||
misperrors["error"] = f"GreyNoise API not accessible (HTTP {response.status_code})"
|
||||
return misperrors
|
||||
|
||||
|
||||
def introspection():
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
import json
|
||||
import requests
|
||||
from . import check_input_attribute, standard_error_message
|
||||
from pymisp import MISPAttribute, MISPEvent, MISPObject
|
||||
|
||||
mispattributes = {
|
||||
'input': ['ip-src', 'ip-dst'],
|
||||
'format': 'misp_standard'
|
||||
}
|
||||
moduleinfo = {
|
||||
'version': 1,
|
||||
'author': 'Christian Studer',
|
||||
'description': 'An expansion module to query ipinfo.io for additional information on an IP address',
|
||||
'module-type': ['expansion', 'hover']
|
||||
}
|
||||
moduleconfig = ['token']
|
||||
|
||||
_GEOLOCATION_OBJECT_MAPPING = {
|
||||
'city': 'city',
|
||||
'postal': 'zipcode',
|
||||
'region': 'region',
|
||||
'country': 'countrycode'
|
||||
}
|
||||
|
||||
|
||||
def handler(q=False):
|
||||
# Input checks
|
||||
if q is False:
|
||||
return False
|
||||
request = json.loads(q)
|
||||
if not request.get('attribute') or not check_input_attribute(request['attribute']):
|
||||
return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'}
|
||||
attribute = request['attribute']
|
||||
if attribute.get('type') not in mispattributes['input']:
|
||||
return {'error': 'Wrong input attribute type.'}
|
||||
if not request.get('config'):
|
||||
return {'error': 'Missing ipinfo config.'}
|
||||
if not request['config'].get('token'):
|
||||
return {'error': 'Missing ipinfo token.'}
|
||||
|
||||
# Query ipinfo.io
|
||||
query = requests.get(
|
||||
f"https://ipinfo.io/{attribute['value']}/json?token={request['config']['token']}"
|
||||
)
|
||||
if query.status_code != 200:
|
||||
return {'error': f'Error while querying ipinfo.io - {query.status_code}: {query.reason}'}
|
||||
ipinfo = query.json()
|
||||
|
||||
# Check if the IP address is not reserved for special use
|
||||
if ipinfo.get('bogon', False):
|
||||
return {'error': 'The IP address is reserved for special use'}
|
||||
|
||||
# Initiate the MISP data structures
|
||||
misp_event = MISPEvent()
|
||||
input_attribute = MISPAttribute()
|
||||
input_attribute.from_dict(**attribute)
|
||||
misp_event.add_attribute(**input_attribute)
|
||||
|
||||
# Parse the geolocation information related to the IP address
|
||||
geolocation = MISPObject('geolocation')
|
||||
for field, relation in _GEOLOCATION_OBJECT_MAPPING.items():
|
||||
geolocation.add_attribute(relation, ipinfo[field])
|
||||
for relation, value in zip(('latitude', 'longitude'), ipinfo['loc'].split(',')):
|
||||
geolocation.add_attribute(relation, value)
|
||||
geolocation.add_reference(input_attribute.uuid, 'locates')
|
||||
misp_event.add_object(geolocation)
|
||||
|
||||
# Parse the domain information
|
||||
domain_ip = misp_event.add_object(name='domain-ip')
|
||||
for feature in ('hostname', 'ip'):
|
||||
domain_ip.add_attribute(feature, ipinfo[feature])
|
||||
domain_ip.add_reference(input_attribute.uuid, 'resolves')
|
||||
if ipinfo.get('domain') is not None:
|
||||
for domain in ipinfo['domain']['domains']:
|
||||
domain_ip.add_attribute('domain', domain)
|
||||
|
||||
# Parse the AS information
|
||||
asn = MISPObject('asn')
|
||||
asn.add_reference(input_attribute.uuid, 'includes')
|
||||
if ipinfo.get('asn') is not None:
|
||||
asn_info = ipinfo['asn']
|
||||
asn.add_attribute('asn', asn_info['asn'])
|
||||
asn.add_attribute('description', asn_info['name'])
|
||||
misp_event.add_object(asn)
|
||||
elif ipinfo.get('org'):
|
||||
as_value, *description = ipinfo['org'].split(' ')
|
||||
asn.add_attribute('asn', as_value)
|
||||
asn.add_attribute('description', ' '.join(description))
|
||||
misp_event.add_object(asn)
|
||||
|
||||
|
||||
# Return the results in MISP format
|
||||
event = json.loads(misp_event.to_json())
|
||||
return {
|
||||
'results': {key: event[key] for key in ('Attribute', 'Object')}
|
||||
}
|
||||
|
||||
|
||||
def introspection():
|
||||
return mispattributes
|
||||
|
||||
|
||||
def version():
|
||||
moduleinfo['config'] = moduleconfig
|
||||
return moduleinfo
|
|
@ -2,10 +2,10 @@ import json
|
|||
from dns import reversename, resolver, exception
|
||||
|
||||
misperrors = {'error': 'Error'}
|
||||
mispattributes = {'input': ['ip-src', 'ip-dst', 'domain|ip'], 'output': ['hostname']}
|
||||
mispattributes = {'input': ['ip-src', 'ip-dst', 'domain|ip', 'ip-src|port', 'ip-dst|port'], 'output': ['hostname']}
|
||||
|
||||
# possible module-types: 'expansion', 'hover' or both
|
||||
moduleinfo = {'version': '0.1', 'author': 'Andreas Muehlemann',
|
||||
moduleinfo = {'version': '0.3', 'author': 'Andreas Muehlemann',
|
||||
'description': 'Simple Reverse DNS expansion service to resolve reverse DNS from MISP attributes',
|
||||
'module-type': ['expansion', 'hover']}
|
||||
|
||||
|
@ -23,6 +23,10 @@ def handler(q=False):
|
|||
toquery = request['ip-src']
|
||||
elif request.get('domain|ip'):
|
||||
toquery = request['domain|ip'].split('|')[1]
|
||||
elif request.get('ip-src|port'):
|
||||
toquery = request['ip-src|port'].split('|')[0]
|
||||
elif request.get('ip-dst|port'):
|
||||
toquery = request['ip-dst|port'].split('|')[0]
|
||||
else:
|
||||
return False
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import json
|
|||
|
||||
misperrors = {"error": "Error"}
|
||||
|
||||
types_to_use = ['sha256', 'sha1', 'md5', 'domain', 'ip', 'url']
|
||||
types_to_use = ['sha1', 'md5', 'domain', 'ip', 'url']
|
||||
|
||||
userConfig = {
|
||||
|
||||
|
@ -20,17 +20,11 @@ inputSource = ['event']
|
|||
outputFileExtension = 'kql'
|
||||
responseType = 'application/txt'
|
||||
|
||||
moduleinfo = {'version': '1.1', 'author': 'Julien Bachmann, Hacknowledge, Maik Wuerth',
|
||||
moduleinfo = {'version': '1.0', 'author': 'Julien Bachmann, Hacknowledge',
|
||||
'description': 'Defender for Endpoint KQL hunting query export module',
|
||||
'module-type': ['export']}
|
||||
|
||||
|
||||
def handle_sha256(value, period):
|
||||
query = f"""find in (DeviceAlertEvents, DeviceFileEvents, DeviceImageLoadEvents, DeviceProcessEvents)
|
||||
where SHA256 == '{value}' or InitiatingProcessSHA1 == '{value}'"""
|
||||
return query.replace('\n', ' ')
|
||||
|
||||
|
||||
def handle_sha1(value, period):
|
||||
query = f"""find in (DeviceAlertEvents, DeviceFileEvents, DeviceImageLoadEvents, DeviceProcessEvents)
|
||||
where SHA1 == '{value}' or InitiatingProcessSHA1 == '{value}'"""
|
||||
|
@ -62,7 +56,6 @@ def handle_url(value, period):
|
|||
|
||||
|
||||
handlers = {
|
||||
'sha256': handle_sha256,
|
||||
'sha1': handle_sha1,
|
||||
'md5': handle_md5,
|
||||
'domain': handle_domain,
|
||||
|
@ -82,10 +75,6 @@ def handler(q=False):
|
|||
for attribute in event["Attribute"]:
|
||||
if attribute['type'] in types_to_use:
|
||||
output = output + handlers[attribute['type']](attribute['value'], config['Period']) + '\n'
|
||||
for obj in event["Object"]:
|
||||
for attribute in obj["Attribute"]:
|
||||
if attribute['type'] in types_to_use:
|
||||
output = output + handlers[attribute['type']](attribute['value'], config['Period']) + '\n'
|
||||
r = {"response": [], "data": str(base64.b64encode(bytes(output, 'utf-8')), 'utf-8')}
|
||||
return r
|
||||
|
||||
|
|
|
@ -15,6 +15,5 @@ __all__ = [
|
|||
'csvimport',
|
||||
'cof2misp',
|
||||
'joe_import',
|
||||
'taxii21',
|
||||
'url_import'
|
||||
'taxii21'
|
||||
]
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
import json
|
||||
import base64
|
||||
from pymisp import MISPEvent, MISPObject, MISPAttribute
|
||||
|
||||
misperrors = {'error': 'Error'}
|
||||
userConfig = {
|
||||
'number1': {
|
||||
'type': 'Integer',
|
||||
'regex': '/^[0-4]$/i',
|
||||
'errorMessage': 'Expected a number in range [0-4]',
|
||||
'message': 'Column number used for value'
|
||||
},
|
||||
'some_string': {
|
||||
'type': 'String',
|
||||
'message': 'A text field'
|
||||
},
|
||||
'boolean_field': {
|
||||
'type': 'Boolean',
|
||||
'message': 'Boolean field test'
|
||||
},
|
||||
'comment': {
|
||||
'type': 'Integer',
|
||||
'message': 'Column number used for comment'
|
||||
}
|
||||
}
|
||||
|
||||
mispattributes = {
|
||||
'inputSource': ['file', 'paste'],
|
||||
'output': ['MISP Format'],
|
||||
'format': 'misp_standard'
|
||||
}
|
||||
|
||||
|
||||
moduleinfo = {'version': '0.1', 'author': 'Sami Mokaddem',
|
||||
'description': 'Generic blueprint to be copy-pasted to quickly boostrap creation of import module.',
|
||||
'module-type': ['import']}
|
||||
|
||||
moduleconfig = []
|
||||
|
||||
|
||||
|
||||
def generateData(event, data, config):
|
||||
# attr = MISPAttribute()
|
||||
# attr.from_dict(**{
|
||||
# 'type': 'ip-src',
|
||||
# 'value': '8.8.8.8',
|
||||
# 'distribution': 2
|
||||
# })
|
||||
# event.add_attribute(attr)
|
||||
pass
|
||||
|
||||
|
||||
def handler(q=False):
|
||||
if q is False:
|
||||
return False
|
||||
request = json.loads(q)
|
||||
data = getUploadedData(request)
|
||||
config = getPassedConfig(request)
|
||||
event = MISPEvent()
|
||||
generateData(event, data, config)
|
||||
return {"results": json.loads(event.to_json())}
|
||||
|
||||
|
||||
def getUploadedData(request):
|
||||
return base64.b64decode(request['data']).decode('utf8')
|
||||
|
||||
|
||||
def getPassedConfig(request):
|
||||
return request['config']
|
||||
|
||||
|
||||
def introspection():
|
||||
modulesetup = mispattributes
|
||||
try:
|
||||
userConfig
|
||||
modulesetup['userConfig'] = userConfig
|
||||
except NameError:
|
||||
pass
|
||||
return modulesetup
|
||||
|
||||
|
||||
def version():
|
||||
moduleinfo['config'] = moduleconfig
|
||||
return moduleinfo
|
|
@ -1,86 +0,0 @@
|
|||
import json
|
||||
import base64
|
||||
from pymisp import MISPEvent, MISPObject, MISPAttribute
|
||||
from pyfaup.faup import Faup
|
||||
|
||||
misperrors = {'error': 'Error'}
|
||||
userConfig = {
|
||||
'include_scheme': {
|
||||
'type': 'Boolean',
|
||||
'message': 'Include scheme'
|
||||
},
|
||||
}
|
||||
|
||||
mispattributes = {
|
||||
'inputSource': ['file', 'paste'],
|
||||
'output': ['MISP Format'],
|
||||
'format': 'misp_standard'
|
||||
}
|
||||
|
||||
|
||||
moduleinfo = {'version': '0.1', 'author': 'Sami Mokaddem',
|
||||
'description': 'Generic blueprint to be copy-pasted to quickly boostrap creation of import module.',
|
||||
'module-type': ['import']}
|
||||
|
||||
moduleconfig = []
|
||||
|
||||
fp = Faup()
|
||||
|
||||
def generateData(event, data, config):
|
||||
for url in data.splitlines():
|
||||
fp.decode(url)
|
||||
parsed = fp.get()
|
||||
obj = MISPObject('url')
|
||||
obj.add_attribute('url', type='url', value=url)
|
||||
if parsed['tld'] is not None:
|
||||
obj.add_attribute('tld', type='text', value=parsed['tld'])
|
||||
if parsed['subdomain'] is not None:
|
||||
obj.add_attribute('subdomain', type='text', value=parsed['subdomain'])
|
||||
if config['include_scheme'] is True:
|
||||
obj.add_attribute('scheme', type='text', value=parsed['scheme'])
|
||||
obj.add_attribute('resource_path', type='text', value=parsed['resource_path'])
|
||||
obj.add_attribute('query_string', type='text', value=parsed['query_string'])
|
||||
obj.add_attribute('port', type='port', value=parsed['port'])
|
||||
obj.add_attribute('host', type='hostname', value=parsed['host'])
|
||||
if parsed['fragment'] is not None:
|
||||
obj.add_attribute('fragment', type='text', value=parsed['fragment'])
|
||||
obj.add_attribute('domain_without_tld', type='text', value=parsed['domain_without_tld'])
|
||||
obj.add_attribute('domain', type='domain', value=parsed['domain'])
|
||||
event.objects.append(obj)
|
||||
|
||||
|
||||
def handler(q=False):
|
||||
if q is False:
|
||||
return False
|
||||
request = json.loads(q)
|
||||
data = getUploadedData(request)
|
||||
config = getPassedConfig(request)
|
||||
event = MISPEvent()
|
||||
generateData(event, data, config)
|
||||
return {"results": json.loads(event.to_json())}
|
||||
|
||||
|
||||
def getUploadedData(request):
|
||||
return base64.b64decode(request['data']).decode('utf8')
|
||||
|
||||
|
||||
def getPassedConfig(request):
|
||||
for k, v in userConfig.items():
|
||||
if v['type'] == 'Boolean':
|
||||
request['config'][k] = True if request['config'][k] == '1' else False
|
||||
return request['config']
|
||||
|
||||
|
||||
def introspection():
|
||||
modulesetup = mispattributes
|
||||
try:
|
||||
userConfig
|
||||
modulesetup['userConfig'] = userConfig
|
||||
except NameError:
|
||||
pass
|
||||
return modulesetup
|
||||
|
||||
|
||||
def version():
|
||||
moduleinfo['config'] = moduleconfig
|
||||
return moduleinfo
|
|
@ -16,7 +16,7 @@ edit_uri: ""
|
|||
use_directory_urls: true
|
||||
|
||||
# Copyright
|
||||
copyright: "Copyright © 2019-2023 MISP Project"
|
||||
copyright: "Copyright © 2019-2022 MISP Project"
|
||||
|
||||
# Options
|
||||
extra:
|
||||
|
|
|
@ -195,7 +195,7 @@ class TestExpansions(unittest.TestCase):
|
|||
query = {"module": "dbl_spamhaus", "domain": "totalmateria.net"}
|
||||
response = self.misp_modules_post(query)
|
||||
try:
|
||||
self.assertEqual(self.get_values(response), 'totalmateria.net - spam test domain')
|
||||
self.assertEqual(self.get_values(response), 'totalmateria.net - spam domain')
|
||||
except Exception:
|
||||
try:
|
||||
self.assertTrue(self.get_values(response).startswith('None of DNS query names exist:'))
|
||||
|
@ -263,7 +263,7 @@ class TestExpansions(unittest.TestCase):
|
|||
self.assertEqual(to_check, 'OK (Not Found)', response)
|
||||
else:
|
||||
self.assertEqual(self.get_errors(response), 'Have I Been Pwned authentication is incomplete (no API key)')
|
||||
|
||||
|
||||
def test_hyasinsight(self):
|
||||
module_name = "hyasinsight"
|
||||
query = {"module": module_name,
|
||||
|
@ -297,7 +297,7 @@ class TestExpansions(unittest.TestCase):
|
|||
)
|
||||
else:
|
||||
response = self.misp_modules_post(query)
|
||||
self.assertEqual(self.get_errors(response), 'GreyNoise API Key required, but missing')
|
||||
self.assertEqual(self.get_errors(response), 'Missing Greynoise API key.')
|
||||
|
||||
@unittest.skip("Service doesn't work")
|
||||
def test_ipasn(self):
|
||||
|
@ -432,7 +432,7 @@ class TestExpansions(unittest.TestCase):
|
|||
encoded = b64encode(f.read()).decode()
|
||||
query = {"module": "pdf_enrich", "attachment": filename, "data": encoded}
|
||||
response = self.misp_modules_post(query)
|
||||
self.assertRegex(self.get_values(response), r'^Pdf test')
|
||||
self.assertEqual(self.get_values(response), 'Pdf test')
|
||||
|
||||
def test_pptx(self):
|
||||
filename = 'test.pptx'
|
||||
|
|
Loading…
Reference in New Issue