Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New module to query ipinfo.io to gather additional information on an IP address #603

Merged
merged 5 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj
* [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.
Expand Down
25 changes: 25 additions & 0 deletions documentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,31 @@ 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>
Expand Down
Binary file added documentation/logos/ipinfo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions documentation/mkdocs/expansion.md
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,31 @@ 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>
Expand Down
13 changes: 13 additions & 0 deletions documentation/website/expansion/ipinfo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"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)."
}
105 changes: 105 additions & 0 deletions misp_modules/modules/expansion/ipinfo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
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