# An exploration in threat intel mapping using ASN and Company data

**URL:** <https://community.ipinfo.io/t/an-exploration-in-threat-intel-mapping-using-asn-and-company-data/5548>\
**Category:** General\
**Tags:** asn, ip-company\
**Created:** [March 9, 2024, 3:01am UTC](https://community.ipinfo.io/t/an-exploration-in-threat-intel-mapping-using-asn-and-company-data/5548 "2024-03-09T03:01:10Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Abdullah](https://yyz1.discourse-cdn.com/flex035/user_avatar/community.ipinfo.io/abdullah/32/4973_2.png) [@Abdullah](https://community.ipinfo.io/u/Abdullah)\
**Post date:** [March 9, 2024, 3:01am UTC](https://community.ipinfo.io/t/an-exploration-in-threat-intel-mapping-using-asn-and-company-data/5548/1 "2024-03-09T03:01:11Z")

</div>

In the case study, we are going to learn how to conduct threat intelligence mapping that stems from a specific [ASN](https://ipinfo.io/products/asn-api) and examine [company metadata](https://ipinfo.io/products/ip-company-api) information. This tutorial will make use of [Business tier API service data](https://ipinfo.io/developers/responses#business-plan), which includes:

- [IP to ASN](https://ipinfo.io/products/asn-api)
- [IP to Company data](https://ipinfo.io/products/ip-company-api)

```python
import requests # For communicating to the IPinfo API
import pandas as pd # for dataframe stuff
import duckdb as db # easier to write SQL than pandas code sometimes
import netaddr # aggregating IPs to cidr/range

```

The following IP address is something I found from honeypot 🙂

```python
target_ip_address = '200.52.65.31'
url = f'https://ipinfo.io/{target_ip_address}?token={token}'
ip_data = requests.get(url).json()

```

Getting a closer look at the target IP address

```python
ip_data

```

```
{'ip': '200.52.65.31',
 'hostname': 'service-static-52.65.31.mcm-telecom.com.mx',
 'city': 'Mexico City',
 'region': 'Mexico City',
 'country': 'MX',
 'loc': '19.4285,-99.1277',
 'postal': '03020',
 'timezone': 'America/Mexico_City',
 'asn': {'asn': 'AS14178',
  'name': 'Megacable Comunicaciones de Mexico, S.A. de C.V.',
  'domain': 'mcmtelecom.com',
  'route': '200.52.65.0/24',
  'type': 'isp'},
 'company': {'name': 'Help Desk Solution de Mexico',
  'domain': 'e-net.com.mx',
  'type': 'business'},
 'privacy': {'vpn': False,
  'proxy': False,
  'tor': False,
  'relay': False,
  'hosting': False,
  'service': ''},
 'abuse': {'address': 'Help Desk Solution de Mexico Juan Escutia 32, Col. Condesa Mexico City, DF, 111,, 00000 - Ciudad - ME',
  'country': 'MX',
  'email': 'msalas@e-net.com.mx',
  'name': 'Miguel Salas',
  'network': '200.52.65.0/26',
  'phone': '+52 5252111444'},
 'domains': {'page': 0, 'total': 0, 'domains': []}}

```

As IPinfo’s IP-company metadata comes from WHOIS records, it is not clear who actually owns the IP address. But we can take a closer look:

- Check the ASN (Optional)
- Check the ASN route information
- Identify other organizations with the route

```python
target_route = ip_data['asn']['route']
target_route

```

```
'200.52.65.0/24'

```

```python
target_asn_address = ip_data['asn']['asn']
target_asn_address

```

```
'AS14178'

```

```python
target_company = ip_data['company']
target_company

```

```
{'name': 'Help Desk Solution de Mexico',
 'domain': 'e-net.com.mx',
 'type': 'business'}

```

```python
url = f'https://ipinfo.io/{target_asn_address}?token={token}'
asn_data = requests.get(url).json()

```

Preview the IPv4 prefixes owned by the target ASN.

```python
as_prefixes = [prefix['netblock'] for prefix in asn_data['prefixes']]
print("\n".join(as_prefixes[:5]))

```

```
131.161.56.0/22
131.161.56.0/23
131.161.56.0/24
131.161.57.0/24
131.161.58.0/23

```

Getting the very high level netblock information from the ASN data

```python
for prefix in asn_data['prefixes']:
    if prefix['netblock'] == target_route:
        as_prefix_data = prefix
        # There is only going to be one match. There is no need to continue checking.
        break
# Preview data
as_prefix_data

```

```
{'netblock': '200.52.65.0/24',
 'id': 'IPA',
 'name': 'Megacable Comunicaciones de Mexico, S.A. de C.V.',
 'country': 'MX',
 'size': '256',
 'status': 'REALLOCATED',
 'domain': 'mcm.net.mx'}

```

If you notice something curious, it is that the information shown above does not match the information we have on the company payload.

```python
target_company

```

```
{'name': 'Help Desk Solution de Mexico',
 'domain': 'e-net.com.mx',
 'type': 'business'}

```

Prefix level information only points to a combination of data present in the WHOIS records. The entire network is not necessarily operated by ‘Megacable Comunicaciones de Mexico, S.A. de C.V.’ as shown. The entire ASN prefix can be shared by multiple different organizations.

The prefix information shown on the ASN API is based on accurate range ownership data, and organizational metadata comes from WHOIS records. Instead of listing all the organizations on the prefix, IPinfo picks one based on internal methods.

To properly conduct threat intelligence mapping of a specific route/prefix/netblock/iprange, we need to perform bulk enrichment and analyze data at the company level.

* * *

Here I am going to [user our CLI](https://github.com/ipinfo/cli).

```bash
ipinfo bulk 200.52.65.0/24 -c > route_data.csv

```

This will result in `route_data.csv` which will contain all the necessary IP information for each individual IP address.

```python
dtypes = {"ip": str, "hostname": str, "bogon": bool, "anycast": bool, "city": str, "region": str, "country": str, "country_name": str, "country_flag_emoji": str, "country_flag_unicode": str, "country_flag_url": str, "country_currency_code": str, "country_currency_symbol": str, "continent_code": str, "continent_name": str, "isEU": str, "loc": str, "org": str, "postal": str, "timezone": str, "asn_id": str, "asn_asn": str, "asn_domain": str, "asn_route": str, "asn_type": str, "company_name": str, "company_domain": str, "company_type": str, "carrier_name": str, "carrier_mcc": str, "carrier_mnc": str, "privacy_vpn": bool, "privacy_proxy": bool, "privacy_tor": bool, "privacy_relay": bool, "privacy_hosting": bool, "privacy_service": str, "abuse_address": str, "abuse_country": str, "abuse_country_name": str, "abuse_email": str, "abuse_name": str, "abuse_network": str, "abuse_phone": str, "domains_total": str}
# We don't need all the columns
usecols = ['ip', 'hostname', 'city', 'region', 'country', 'loc', 'org', 'postal', 'timezone', 'asn_id', 'asn_asn', 'asn_domain', 'asn_route', 'asn_type', 'company_name', 'company_domain', 'company_type', 'carrier_name', 'carrier_mcc', 'carrier_mnc', 'privacy_vpn', 'privacy_proxy', 'privacy_tor', 'privacy_relay', 'privacy_hosting', 'privacy_service', 'abuse_country', 'abuse_country_name', 'abuse_email', 'abuse_name', 'abuse_network', 'domains_total']
route_data = pd.read_csv('route_data.csv', usecols=usecols, dtype=dtypes, true_values=['true'], false_values=['false'])

```

```python
route_data

```

| | ip | hostname | city | region | country | loc | org | postal | timezone | asn\_id | ... | privacy\_tor | privacy\_relay | privacy\_hosting | privacy\_service | abuse\_country | abuse\_country\_name | abuse\_email | abuse\_name | abuse\_network | domains\_total |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 0 | 200.52.65.26 | service-static-52.65.26.mcm-telecom.com.mx | Mexico City | Mexico City | MX | 19.4285,-99.1277 | NaN | 03020 | America/Mexico\_City | AS14178 | ... | False | False | False | NaN | MX | Mexico | msalas@e-net.com.mx | Miguel Salas | 200.52.65.0/26 | 0 |
| 1 | 200.52.65.73 | service-static-52.65.73.mcm-telecom.com.mx | Cuauhtémoc | Mexico City | MX | 19.4318,-99.1449 | NaN | 06693 | America/Mexico\_City | AS14178 | ... | False | False | False | NaN | MX | Mexico | colivera@toshiba.com.mx | Carlos Olivera | 200.52.65.64/26 | 0 |
| 2 | 200.52.65.218 | service-static-52.65.218.mcm-telecom.com.mx | Miguel Hidalgo | Mexico City | MX | 19.4389,-99.2100 | NaN | 11200 | America/Mexico\_City | AS14178 | ... | False | False | False | NaN | MX | Mexico | ipmaster@MCMTELECOM.COM.MX | IPMASTER ADMINISTRATOR | 200.52.65.0/24 | 0 |
| 3 | 200.52.65.100 | service-static-52.65.100.mcm-telecom.com.mx | Cuauhtémoc | Mexico City | MX | 19.4318,-99.1449 | NaN | 06693 | America/Mexico\_City | AS14178 | ... | False | False | False | NaN | MX | Mexico | colivera@toshiba.com.mx | Carlos Olivera | 200.52.65.64/26 | 0 |
| 4 | 200.52.65.44 | service-static-52.65.44.mcm-telecom.com.mx | Mexico City | Mexico City | MX | 19.4285,-99.1277 | NaN | 03020 | America/Mexico\_City | AS14178 | ... | False | False | False | NaN | MX | Mexico | msalas@e-net.com.mx | Miguel Salas | 200.52.65.0/26 | 0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 251 | 200.52.65.203 | service-static-52.65.203.mcm-telecom.com.mx | Miguel Hidalgo | Mexico City | MX | 19.4389,-99.2100 | NaN | 11200 | America/Mexico\_City | AS14178 | ... | False | False | False | NaN | MX | Mexico | ipmaster@MCMTELECOM.COM.MX | IPMASTER ADMINISTRATOR | 200.52.65.0/24 | 0 |
| 252 | 200.52.65.7 | service-static-52.65.7.mcm-telecom.com.mx | Mexico City | Mexico City | MX | 19.4285,-99.1277 | NaN | 03020 | America/Mexico\_City | AS14178 | ... | False | False | False | NaN | MX | Mexico | msalas@e-net.com.mx | Miguel Salas | 200.52.65.0/26 | 0 |
| 253 | 200.52.65.135 | service-static-52.65.135.mcm-telecom.com.mx | Miguel Hidalgo | Mexico City | MX | 19.4389,-99.2100 | NaN | 11200 | America/Mexico\_City | AS14178 | ... | False | False | False | NaN | MX | Mexico | raul.ocampo@reuters.com | Raul Ocampo | 200.52.65.128/26 | 0 |
| 254 | 200.52.65.211 | service-static-52.65.211.mcm-telecom.com.mx | Miguel Hidalgo | Mexico City | MX | 19.4389,-99.2100 | NaN | 11200 | America/Mexico\_City | AS14178 | ... | False | False | False | NaN | MX | Mexico | ipmaster@MCMTELECOM.COM.MX | IPMASTER ADMINISTRATOR | 200.52.65.0/24 | 0 |
| 255 | 200.52.65.147 | service-static-52.65.147.mcm-telecom.com.mx | Miguel Hidalgo | Mexico City | MX | 19.4389,-99.2100 | NaN | 11200 | America/Mexico\_City | AS14178 | ... | False | False | False | NaN | MX | Mexico | raul.ocampo@reuters.com | Raul Ocampo | 200.52.65.128/26 | 0 |

256 rows × 32 columns

Now that we have the IP address metadata loaded for the entire range. Lets take a closer look.

* * *

```python
# Identify the companies and domain count
db.query('''
SELECT company_name, company_domain, count(*) ip_count
FROM route_data
GROUP BY company_name, company_domain
ORDER BY ip_count DESC
''')

```

```
┌──────────────────────────────────────────────────┬────────────────┬──────────┐
│ company_name │ company_domain │ ip_count │
│ varchar │ varchar │ int64 │
├──────────────────────────────────────────────────┼────────────────┼──────────┤
│ Toshiba de Mexico, S.A. de C.V. │ toshiba.com.mx │ 64 │
│ Help Desk Solution de Mexico │ e-net.com.mx │ 64 │
│ Reuters de Mexico, SA de CV │ reuters.com │ 64 │
│ Megacable Comunicaciones de Mexico, S.A. de C.V. │ mcm.net.mx │ 64 │
└──────────────────────────────────────────────────┴────────────────┴──────────┘

```

From the beginning, we can see that the route (200.52.65.0/24) is equally shared between 4 companies:

- The ASN prefix level company is “Megacable Comunicaciones de Mexico, S.A. de C.V. (mcm.net.mx)”, which is an ISP.
- Our target company behind the target IP address is “Help Desk Solution de Mexico (e-net.com.mx)”, which I have no clue what it is. Maybe a business of some sort.
- Then we have two other companies: a multinational news agency and a multinational electronics manufacturing organization.

So, for our threat intelligence mapping, pointing to the entire `/24` range as malicious should not be a good thing to do. We have to be more specific and target the company behind the IP address. So, let’s get all their IP addresses. We are going to use the `company_domain` field to identify the target IP addresses and sister domains within the netblock.

```python
target_sister_ips = db.query('''
SELECT ip
FROM route_data
WHERE company_domain = 'e-net.com.mx'
''').to_df()['ip'].to_list()
# Preview data
target_sister_ips[:5]

```

```
['200.52.65.26', '200.52.65.44', '200.52.65.5', '200.52.65.60', '200.52.65.45']

```

From here, all we have to do is merge the IP address into CIDR. We are going to use the third-party library [netaddr](https://pypi.org/project/netaddr/) for this. This will return a list.

```python
netaddr.cidr_merge(target_sister_ips)

```

```
[IPNetwork('200.52.65.0/26')]

```

Making this information more friendly

```python
route_to_ban = str(netaddr.cidr_merge(target_sister_ips)[0])
route_to_ban

```

```
'200.52.65.0/26'

```

That’s about it. That is how you can perform threat intelligence mapping using our standard products. If you want more control and granularity, you can use [WHOIS databases](https://ipinfo.io/products/whois-database) to delve into finer details. However, in most cases, using a combination of ASN and company data will get the job done. Feel free to leave a comment or let me know what you think!
