Giter Club home page Giter Club logo

wtfis's Introduction

wtfis

Tests PyPI

Passive hostname, domain and IP lookup tool for non-robots

WTF is it?

wtfis is a commandline tool that gathers information about a domain, FQDN or IP address using various OSINT services. Unlike other tools of its kind, it's built specifically for human consumption, providing results that are pretty (YMMV) and easy to read and understand.

This tool assumes that you are using free tier / community level accounts, and so makes as few API calls as possible to minimize hitting quotas and rate limits.

The project name is a play on "whois".

Data Sources

Service Used in lookup Required Free Tier
Virustotal All Yes Yes
Passivetotal All No Yes
IP2Whois Domain/FQDN No Yes
IPWhois IP address No Yes (no signup)
Shodan IP address No No
Greynoise IP address No Yes
URLhaus All No Yes (no signup)
AbuseIPDB IP address No Yes

Virustotal

The primary source of information. Retrieves:

  • Hostname (FQDN), domain or IP
    • Latest analysis stats with vendor detail
    • Reputation score (based on VT community votes)
    • Popularity ranks (Alexa, Cisco Umbrella, etc.) (FQDN and domain only)
    • Categories (assigned by different vendors)
  • Resolutions (FQDN and domain only)
    • Last n IP addresses (default: 3, max: 10)
    • Latest analysis stats of each IP above
  • Whois
    • Fallback only: if Passivetotal creds are not available
    • Various whois data about the domain itself

Passivetotal (RiskIQ)

Optionally used if creds are provided. Retrieves:

  • Whois
    • Various whois data about the domain itself

Passivetotal is recommended over Virustotal for whois data for a couple of reasons:

  • VT whois data format is less consistent
  • PT whois data tends to be of better quality than VT. Also, VT's registrant data is apparently anonymized.
  • You can save one VT API call by offloading to PT

IP2Whois

Optionally used if creds are provided and Passivetotal creds are not supplied. (i.e. second in line for Whois information)

  • Whois
    • Various whois data about the domain itself

As above, IP2Whois is recommended over Virustotal if a Passivetotal account cannot be obtained.

IPWhois

Default Geolocation and ASN lookup source for IP addresses. Retrieves:

  • ASN, Org, ISP and Geolocation

IPWhois should not be confused with IP2Whois, which provides domain Whois data.

Shodan

GETs data from the /shodan/host/{ip} endpoint (see doc). For each IP, retrieves:

  • List of open ports and services
  • Operating system (if available)
  • Tags (assigned by Shodan)

Greynoise

Using Greynoise's community API, wtfis will show whether an IP is in one of Greynoise's datasets:

  • Noise: IP has been seen regularly scanning the Internet
  • RIOT: IP belongs to a common business application (e.g. Microsoft O365, Google Workspace, Slack)

More information about the datasets here.

In addition, the API also returns Greynoise's classification of an IP (if available). Possible values are benign, malicious, and unknown.

URLhaus

URLhaus is a crowd-sourced database of reported malicious URLs. This enrichment provides insight on whether the queried hostname or IP is being or was used for malware distribution via HTTP or HTTPS. Data that is provided include:

  • Count of currently online and total malware URLs
  • Whether the hostname or IP is currently in the DNSBL and SURBL public blocklists
  • All tags that have been assigned to the URL throughout its history in the URLhaus database

AbuseIPDB

AbuseIPDB is a crowd-sourced database of reported malicious IP addresses. Through its API wtfis shows:

  • Abuse confidence score (0-100)
  • Number of reports

Install

$ pip install wtfis

To install via conda (from conda-forge), see wtfis-feedstock.

To install via brew:

brew install wtfis

Setup

wtfis uses these environment variables:

  • VT_API_KEY (required) - Virustotal API key
  • PT_API_KEY (optional) - Passivetotal API key
  • PT_API_USER (optional) - Passivetotal API user
  • IP2WHOIS_API_KEY (optional) - IP2WHOIS API key
  • SHODAN_API_KEY (optional) - Shodan API key
  • GREYNOISE_API_KEY (optional) - Greynoise API key
  • ABUSEIPDB_API_KEY (optional) - AbuseIPDB API key
  • WTFIS_DEFAULTS (optional) - Default arguments

Set these using your own method.

Alternatively, create a file in your home directory ~/.env.wtfis with the above declarations. See .env.wtfis.example for a template. NOTE: Don't forget to chmod 400 the file!

Usage

usage: wtfis [-h] [-m N] [-s] [-g] [-a] [-u] [-n] [-1] [-V] entity

positional arguments:
  entity                Hostname, domain or IP

optional arguments:
  -h, --help            show this help message and exit
  -m N, --max-resolutions N
                        Maximum number of resolutions to show (default: 3)
  -s, --use-shodan      Use Shodan to enrich IPs
  -g, --use-greynoise   Enable Greynoise for IPs
  -a, --use-abuseipdb   Enable AbuseIPDB for IPs
  -u, --use-urlhaus     Enable URLhaus for IPs and domains
  -n, --no-color        Show output without colors
  -1, --one-column      Display results in one column
  -V, --version         Print version number

Basically:

$ wtfis FQDN_OR_DOMAIN_OR_IP

and you will get results organized by panel, similar to the image above.

Defanged input is accepted (e.g. api[.]google[.]com).

If supported by the terminal, the Analysis field and (if using PT) headings in the whois panel are clickable hyperlinks that point to the appropriate pages on the VT or PT website.

Shodan

Shodan can be used to show an IP's open ports or services, and OS in some results. Invoke with the -s or --use-shodan flag.

If supported by the terminal, the Services field is a clickable hyperlink that takes you to the Shodan web interface.

Greynoise

To enable Greynoise, invoke with the -g or --use-greynoise flag. Because the API quota is quite low (50 requests per week as of March 2023), this lookup is off by default.

The GreyNoise field name is also a hyperlink (if terminal-supported) that points to the IP entry in the Greynoise web interface, where more context is shown.

URLhaus

Use the -u or --use-urlhaus flag to enable URLhaus enrichment for hostnames, domains and IPs.

The Malware URLs field name is a hyperlink (if terminal-supported) that takes you to the specific URLhaus database page for your query.

AbuseIPDB

Use the -a or --use-abuseipdb flag to enable AbuseIPDB enrichment for hostnames, domains and IPs.

The AbuseIPDB field name is a hyperlink (if terminal-supported) that takes you to the specific AbuseIPDB database page for your query.

Display options

For FQDN and domain lookups, you can increase or decrease the maximum number of displayed IP resolutions with -m NUMBER or --max-resolutions=NUMBER. The upper limit is 10. If you don't need resolutions at all, set the number to 0.

To show all panels in one column, use the -1 or --one-column flag.

Panels can be displayed with no color with -n or --no-color.

Defaults

Default arguments can be defined by setting the WTFIS_DEFAULTS environment variable. For example, to use shodan and display results in one column by default:

WTFIS_DEFAULTS=-s -1

If an argument is in WTFIS_DEFAULTS, then specifying the same argument during command invocation negates that argument. So in the example above, if you then run:

$ wtfis example.com -s

then Shodan will NOT be used.

Note that maximum resolutions (-m N, --max-resolutions N) cannot be defined in defaults at the moment.

Docker

wtfis can be run from a Docker image. First, build the image (using the included Dockerfile) by running:

$ make docker-image

The image will have the latest tagged version (not necessarily from the latest commit) wtfis. This ensures that you are getting a stable release.

Two ways you can run the image:

Ensure .env.wtfis is in your home directory and set with the necessary envvars. Then simply run:

$ make docker-run

This is an alias to

$ docker run --env-file=${HOME}/.env.wtfis -it wtfis

Note that each definition must NOT have any spaces before and after the equal sign (FOO=bar, not FOO = bar).

Altenatively, you can set the environment variables yourself, then run, e.g.:

$ docker run -e VT_API_KEY -e SHODAN_API_KEY -it wtfis

wtfis's People

Contributors

p-linnane avatar pirxthepilot avatar zbalkan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wtfis's Issues

Set default command-line parameters

Please add an environment option like WTFIS_DEFAULTS="-s" to specify default command-line parameters. I would like to use Shodan every time; this could be a universal solution.

How do I upgrade?

I tried "pip install wtfis" and "pip uninstall wtfis" but always end up with v.0.2.2

RiskIQ / Passivetotal does not work anymore

RiskIQ has been bought by Microsoft, the login creation has been shut down and replaced by "Defender Threat Intelligence". Old logins are supposed to still work via API. Is there an alternative?

Please add support for pydantic-2

The package currently requires pydantic-1. Please add support for pydantic-2. FWICS it would require some code changes:

ImportError while loading conftest '/tmp/wtfis/tests/conftest.py'.
tests/conftest.py:8: in <module>
    from wtfis.models.greynoise import GreynoiseIp
wtfis/models/greynoise.py:18: in <module>
    class GreynoiseIpMap(BaseModel):
.venv/lib/python3.11/site-packages/pydantic/_internal/_model_construction.py:95: in __new__
    private_attributes = inspect_namespace(
.venv/lib/python3.11/site-packages/pydantic/_internal/_model_construction.py:279: in inspect_namespace
    raise TypeError("To define root models, use `pydantic.RootModel` rather than a field called '__root__'")
E   TypeError: To define root models, use `pydantic.RootModel` rather than a field called '__root__'

Tag the source

Could you please tag the source? This allows distributions to get the complete source from GitHub if they want.

Thanks

Whois not able to gather data

VT and Shodan are working as expected. PT is not configured, since no account is possible now. Whois output is red, "unable to gather data".

Adding IP2Location.io API into wtfis

IP2WHOIS API is now part of IP2Location.io. They are sharing the same API token in one sign up.

It will be interesting you can integrate both IP2Location.io and IP2WHOIS API into wtfis.

Multi-API Keys

I'm wondering how I can set multiple API keys in .env.wtfis

Data model validation error with v0.0.5

Issue #1 resolved a Data model validation error via an upgrade to v0.0.5. Here it is once more, for bbc.co.uk:

$ wtfis bbc.co.uk                                                                                                                                                                                                       ✓ Fetching data from Virustotal         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:01
✓ Fetching IP enrichments from IPWhois  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
⠦ Fetching domain whois from Virustotal ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% 0:00:00
Data model validation error: 6 validation errors for HistoricalWhois
data -> 0 -> attributes -> whois_map -> Domain Name
  field required (type=value_error.missing)
data -> 1 -> attributes -> whois_map -> Domain Name
  field required (type=value_error.missing)
data -> 2 -> attributes -> whois_map -> Domain Name
  field required (type=value_error.missing)
data -> 4 -> attributes -> whois_map -> Domain Name
  field required (type=value_error.missing)
data -> 5 -> attributes -> whois_map -> Domain Name
  field required (type=value_error.missing)
data -> 6 -> attributes -> whois_map -> Domain Name
  field required (type=value_error.missing)
$ wtfis --version                                                                                                                                                                                                         0.0.5

Docker Image?

Can you provide a Docker image for this tool? It could make the installation much easier.

VirusToal Quota to low, makes wtfis unusable

A quota from 1 query per day from Virustotal, just kills this project. It is also not enough, to complete -1- query with this tool.

$ wtfis adomain.com
⠙ Fetching data from Virustotal ━━━━━━━━━━━━━━━━━━━━━━━━━━╺━━━━━━━━━━━━━  66% 0:00:00
Error fetching data: 429 Client Error: Too Many Requests for url: https://www.virustotal.com/api/v3/domains/adomain.com/resolutions

It would be nice, if this can be changed in any way (skip virustotal or make only one query per wtfis call, or...)

Would it be a good idea to use a filesystem-backed persistent cache to minimize API usage?

I used this solution in my wtfis-Wazuh integration and it works smoothly.

import diskcache

def __query_with_cache(target: str, config: Config, cache_dir: str = './') -> Optional[dict]:

    # Check if private IP or not
    if is_private(target=target):
        __debug(f"The target IP is in private range: {target}")
        return None

    # Create path for cache if not exists
    if os.path.exists(cache_dir) is False:
        os.makedirs(cache_dir, 0o700)

    __debug("Opening cache")
    with diskcache.Cache(directory=cache_dir) as cache:

        # Enable stats if not enabled on the first run
        cache.stats(enable=True)
        # Expire old items first
        cache.expire()

        __debug("Checking cache")
        cache_result: Optional[str] = cache.get(target)  # type: ignore

        if cache_result:
            __debug("Found the value in cache")
            return dict(json.loads(cache_result))

        else:
            __debug("Cache miss. Querying APIs...")

            # Initiate resolver
            resolver = Resolver(target, config)

            # Fetch data
            resolver.fetch()

            # Get result
            export = resolver.export()

            if export:
                __debug("Adding the response to cache")
                cache.add(target, json.dumps(export, sort_keys=True))
            else:
                return None

To make the code above understandable, I must give some context. I had to make wtfis a library that outputs JSON results for that integration. So, the external script can just call the library methods. I, first, stripped away all the UI related code, then created a wrapper class called Resolver, which includes the generate_entity_handler method inside. Then a fetch and an export method were added as the main interface to the library.

image

In wtfis you used environment variables stored in .env.wtfis file. In order to be able to integrate smoothly, I first created a class called Config, that I can pass to Resolver. One can use any method to create this Config class. In my case, Wazuh initiates the Python script with a bash script, along with arguments. So, I read the arguments, initiate the Config class instance, and pass it to the Resover along with the target IP or domain name.

image

These two methods are the interface of the wtfis library. Everything else was moved under wtfis.internal.

The code above then reads the SQLite-backed cache. I am using the defaults for cache settings. But it is possible to customize parameters, choose a different strategy, and have a shorter lifetime for cache.

The idea is to minimize the API usage. It may help in the long term.

Feature Request: Supporting IPinfo.io (Geolocation, ASN) and Host.io (For domain information)

Requesting integration of:

  • IPinfo.io for IP geolocation and ASN information
  • Host.io for domain-level information

Both these services are accessible to users easily and comes with full accuracy and generous free tier.

IPinfo.io

  • Supports 1,000 requests/day without an access token
  • Support 50,000 requests/month with a free tier access token
  • HTTPS / SSL encryption for free tier and non-token API access
  • Highly accurate geolocation information backed by IPinfo's probe network infrastructure with 600+ servers
  • With paid tokens, users can get access to information on anonymous IP data

API request example:

curl https://ipinfo.io/123.226.51.149?token=$token
{
  "ip": "123.226.51.149",
  "hostname": "p1259149-ipngn2001niigatani.niigata.ocn.ne.jp",
  "city": "Niigata",
  "region": "Niigata",
  "country": "JP",
  "loc": "37.8864,139.0059",
  "org": "AS4713 NTT Communications Corporation",
  "postal": "950-0954",
  "timezone": "Asia/Tokyo"
}

ASN information is available on the org field.

Host.io

  • Provides detailed domain information
  • Requires a free tier access token
  • Domain information payload includes:
    • Domain information
    • Meta tag information
    • DNS records and IP addresses
    • NS and MX records
    • Server geolocation and ASN information powered by IPinfo
    • Backlinks and redirects
curl https://host.io/api/full/github.com?token=$token
{
  "domain": "github.com",
  "web": {
    "domain": "github.com",
    "rank": 30,
    "url": "https://github.com/",
    "ip": "192.30.255.113",
    "date": "2023-08-28T06:18:32.912Z",
    "length": 235688,
    "server": "GitHub.com",
    "encoding": "utf8",
    "copyright": "© 2023 GitHub, Inc.",
    "facebook": "GitHub",
    "twitter": "github",
    "title": "GitHub: Letâs build from here · GitHub",
    "description": "GitHub is where over 100 million developers shape the future of software, together. Contribute to the open source community, manage your Git repositories, review code like a pro, track bugs and features, power your CI/CD and DevOps workflows, and secure code before you commit it.",
    "links": [
      "github.blog",
      "electronjs.org",
      "github.community",
      "githubstatus.com",
      "twitter.com",
      "facebook.com",
      "linkedin.com",
      "youtube.com",
      "twitch.tv",
      "tiktok.com"
    ]
  },
  "dns": {
    "domain": "github.com",
    "a": [
      "192.30.255.112"
    ],
    "mx": [
      "1 aspmx.l.google.com.",
      "10 alt3.aspmx.l.google.com.",
      "10 alt4.aspmx.l.google.com.",
      "5 alt1.aspmx.l.google.com.",
      "5 alt2.aspmx.l.google.com."
    ],
    "ns": [
      "dns1.p08.nsone.net.",
      "dns2.p08.nsone.net.",
      "dns3.p08.nsone.net.",
      "dns4.p08.nsone.net.",
      "ns-1283.awsdns-32.org.",
      "ns-1707.awsdns-21.co.uk.",
      "ns-421.awsdns-52.com.",
      "ns-520.awsdns-01.net."
    ]
  },
  "ipinfo": {
    "192.30.255.113": {
      "city": "Seattle",
      "region": "Washington",
      "country": "US",
      "loc": "47.6062,-122.3321",
      "postal": "98101",
      "timezone": "America/Los_Angeles",
      "asn": {
        "asn": "AS36459",
        "name": "GitHub, Inc.",
        "domain": "github.com",
        "route": "192.30.255.0/24",
        "type": "isp"
      }
    },
    "192.30.255.112": {
      "city": "Seattle",
      "region": "Washington",
      "country": "US",
      "loc": "47.6062,-122.3321",
      "postal": "98101",
      "timezone": "America/Los_Angeles",
      "asn": {
        "asn": "AS36459",
        "name": "GitHub, Inc.",
        "domain": "github.com",
        "route": "192.30.255.0/24",
        "type": "isp"
      }
    }
  },
  "related": {
    "ip": [
      {
        "value": "192.30.255.113",
        "count": 9558
      },
      {
        "value": "192.30.255.112",
        "count": 9645
      }
    ],
    "asn": [
      {
        "value": "AS36459",
        "count": 55240
      }
    ],
    "ns": [
      {
        "value": "nsone.net",
        "count": 3112162
      },
      {
        "value": "awsdns-32.org",
        "count": 48035
      },
      {
        "value": "awsdns-21.co.uk",
        "count": 53472
      },
      {
        "value": "awsdns-52.com",
        "count": 54451
      },
      {
        "value": "awsdns-01.net",
        "count": 54069
      }
    ],
    "mx": [
      {
        "value": "google.com",
        "count": 16432132
      }
    ],
    "backlinks": [
      {
        "value": "github.com",
        "count": 875699
      }
    ],
    "redirects": [
      {
        "value": "github.com",
        "count": 19280
      }
    ]
  }
}

Please let me know what you think. The project currently relies on WHOIS information for geolocation information, which does not provide a reliable picture of location of IP addresses.

Maybe it would be nice, to also query RBL lists

Hi,

it would be nice, to qeury also RBL Lists. Like the one from Spamhouse ans so one...
They and others have a ip/url blocklist. We could display, if the target ip is from a dynamic address pool or used in a botnet and so on...

Data model validation error: 7 validation errors for HistoricalWhois

Experiencing whois error when querying specific domain (some of the sub-domains are also affected):

$ wtfis mymanatee.org
✓ Fetching data from Virustotal         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
✓ Fetching IP enrichments from IPWhois  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
⠙ Fetching domain whois from Virustotal ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% 0:00:00
Data model validation error: 7 validation errors for HistoricalWhois
data -> 6 -> attributes -> whois_map -> Domain Name
  field required (type=value_error.missing)
data -> 6 -> attributes -> whois_map -> Registrar
  field required (type=value_error.missing)
data -> 6 -> attributes -> whois_map -> Name Server
  field required (type=value_error.missing)
data -> 6 -> attributes -> whois_map -> Creation Date
  field required (type=value_error.missing)
data -> 6 -> attributes -> whois_map -> Registry Expiry Date
  field required (type=value_error.missing)
data -> 6 -> attributes -> whois_map -> Updated Date
  field required (type=value_error.missing)
data -> 6 -> attributes -> last_updated
  field required (type=value_error.missing)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.