An exploration in threat intel mapping using ASN and Company data

In the case study, we are going to learn how to conduct threat intelligence mapping that stems from a specific ASN and examine company metadata information. This tutorial will make use of Business tier API service data, which includes:

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 :slight_smile:

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

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
target_route = ip_data['asn']['route']
target_route
'200.52.65.0/24'
target_asn_address = ip_data['asn']['asn']
target_asn_address
'AS14178'
target_company = ip_data['company']
target_company
{'name': 'Help Desk Solution de Mexico',
 'domain': 'e-net.com.mx',
 'type': 'business'}
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.

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

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.

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.

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.

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'])
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.


# 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.

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 for this. This will return a list.

netaddr.cidr_merge(target_sister_ips)
[IPNetwork('200.52.65.0/26')]

Making this information more friendly

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 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!