From 68d1246f6a650d1f0ce333ecd158a94e3b630354 Mon Sep 17 00:00:00 2001 From: duong22 Date: Thu, 7 May 2026 15:07:40 -0700 Subject: [PATCH 1/3] shodan pack ver1.0.0 --- actions/dns_resolve.py | 14 +++++++++++++ actions/dns_resolve.yaml | 16 +++++++++++++++ actions/dns_reverse.py | 15 ++++++++++++++ actions/dns_reverse.yaml | 16 +++++++++++++++ actions/host_info.py | 12 +++++++++++ actions/host_info.yaml | 24 ++++++++++++++++++++++ actions/host_ports.py | 10 +++++++++ actions/host_ports.yaml | 14 +++++++++++++ actions/lib/base.py | 16 +++++++++++++++ actions/search.py | 43 +++++++++++++++++++++++++++++++++++++++ actions/search.yaml | 33 ++++++++++++++++++++++++++++++ config.schema.yaml | 5 +++++ icon.png | Bin 0 -> 5748 bytes pack.yaml | 17 ++++++++++++++++ requirements.txt | 1 + 15 files changed, 236 insertions(+) create mode 100644 actions/dns_resolve.py create mode 100644 actions/dns_resolve.yaml create mode 100644 actions/dns_reverse.py create mode 100644 actions/dns_reverse.yaml create mode 100644 actions/host_info.py create mode 100644 actions/host_info.yaml create mode 100644 actions/host_ports.py create mode 100644 actions/host_ports.yaml create mode 100644 actions/lib/base.py create mode 100644 actions/search.py create mode 100644 actions/search.yaml create mode 100644 config.schema.yaml create mode 100644 icon.png create mode 100644 pack.yaml create mode 100644 requirements.txt diff --git a/actions/dns_resolve.py b/actions/dns_resolve.py new file mode 100644 index 0000000..c7db923 --- /dev/null +++ b/actions/dns_resolve.py @@ -0,0 +1,14 @@ +from lib.base import ShodanBaseAction + +class DnsResolveAction(ShodanBaseAction): + def run(self, hostnames): + try: + if isinstance(hostnames, list): + hostnames_str = ",".join(hostnames) + else: + hostnames_str = hostnames + args = {"hostnames": hostnames_str} + result = self.client._request('/dns/resolve', args) + return True, result + except Exception as e: + return False, str(e) \ No newline at end of file diff --git a/actions/dns_resolve.yaml b/actions/dns_resolve.yaml new file mode 100644 index 0000000..608c2fc --- /dev/null +++ b/actions/dns_resolve.yaml @@ -0,0 +1,16 @@ +--- +name: dns_resolve +pack: shodan +runner_type: python-script +description: > + Look up the IP addresses for one or more hostnames. Uses Shodan's DNS + resolution endpoint. +enabled: true +entry_point: dns_resolve.py +parameters: + hostnames: + type: array + description: List of hostnames to resolve (e.g. ["google.com", "example.com"]). + required: true + items: + type: string \ No newline at end of file diff --git a/actions/dns_reverse.py b/actions/dns_reverse.py new file mode 100644 index 0000000..8bf2988 --- /dev/null +++ b/actions/dns_reverse.py @@ -0,0 +1,15 @@ +from lib.base import ShodanBaseAction + +class DnsReverseAction(ShodanBaseAction): + def run(self, ips): + try: + if isinstance(ips, list): + ips_str = ",".join(ips) + else: + ips_str = ips + args = {"ips": ips_str} + result = self.client._request('/dns/reverse', args) + + return True, result + except Exception as e: + return False, str(e) \ No newline at end of file diff --git a/actions/dns_reverse.yaml b/actions/dns_reverse.yaml new file mode 100644 index 0000000..9b87c25 --- /dev/null +++ b/actions/dns_reverse.yaml @@ -0,0 +1,16 @@ +--- +name: dns_reverse +pack: shodan +runner_type: python-script +description: > + Perform reverse DNS lookups on one or more IP addresses. Returns the + hostnames that point to each IP. +enabled: true +entry_point: dns_reverse.py +parameters: + ips: + type: array + description: List of IPv4 addresses to reverse-lookup (e.g. ["8.8.8.8", "1.1.1.1"]). + required: true + items: + type: string \ No newline at end of file diff --git a/actions/host_info.py b/actions/host_info.py new file mode 100644 index 0000000..a456c87 --- /dev/null +++ b/actions/host_info.py @@ -0,0 +1,12 @@ +"""Return all available Shodan data for a given IP address.""" + +from lib.base import ShodanBaseAction + + +class HostInfoAction(ShodanBaseAction): + def run(self, ip, history=False, minify=False): + try: + result = self.client.host(ip, history=history, minify=minify) + return True, result + except Exception as e: + return False, str(e) diff --git a/actions/host_info.yaml b/actions/host_info.yaml new file mode 100644 index 0000000..bd8ca8c --- /dev/null +++ b/actions/host_info.yaml @@ -0,0 +1,24 @@ +--- +name: host_info +pack: shodan +runner_type: python-script +description: > + Returns all services that have been found on the given host IP. Includes open + ports, banners, vulnerabilities (if any), geolocation, hostnames, and more. +enabled: true +entry_point: host_info.py +parameters: + ip: + type: string + description: IPv4 address of the host to query. + required: true + history: + type: boolean + description: Include all historical banners for the host. + required: false + default: false + minify: + type: boolean + description: Return only the minimal set of host information (faster). + required: false + default: false \ No newline at end of file diff --git a/actions/host_ports.py b/actions/host_ports.py new file mode 100644 index 0000000..b729d28 --- /dev/null +++ b/actions/host_ports.py @@ -0,0 +1,10 @@ +from lib.base import ShodanBaseAction + +class HostPortsAction(ShodanBaseAction): + def run(self, ip): + try: + result = self.client.host(ip, minify=True) + ports = result.get("ports", []) + return True, {"ip": ip, "ports": sorted(ports)} + except Exception as e: + return False, str(e) \ No newline at end of file diff --git a/actions/host_ports.yaml b/actions/host_ports.yaml new file mode 100644 index 0000000..9639512 --- /dev/null +++ b/actions/host_ports.yaml @@ -0,0 +1,14 @@ +--- +name: host_ports +pack: shodan +runner_type: python-script +description: > + Returns the list of open ports Shodan has observed on a host. Faster than + host_info as it fetches only minimal data. +enabled: true +entry_point: host_ports.py +parameters: + ip: + type: string + description: IPv4 address of the host. + required: true \ No newline at end of file diff --git a/actions/lib/base.py b/actions/lib/base.py new file mode 100644 index 0000000..bb2ac12 --- /dev/null +++ b/actions/lib/base.py @@ -0,0 +1,16 @@ +import shodan +from st2common.runners.base_action import Action + + +class ShodanBaseAction(Action): + def __init__(self, config): + super().__init__(config) + api_key = self.config.get("shodan_api_key") + if not api_key: + raise ValueError( + "shodan_api_key is not configured. " + "Please set it via: st2 pack config shodan" + ) + timeout = self.config.get("request_timeout", 30) + self.client = shodan.Shodan(api_key) + self.client.timeout = timeout \ No newline at end of file diff --git a/actions/search.py b/actions/search.py new file mode 100644 index 0000000..20323e8 --- /dev/null +++ b/actions/search.py @@ -0,0 +1,43 @@ +from lib.base import ShodanBaseAction + +MATCH_FIELDS = ( + "ip_str", "port", "transport", "hostnames", "domains", + "org", "isp", "product", "version", "os", "cpe", "cpe23", + "asn", "vulns", "tags", "timestamp", "location", +) + +class SearchAction(ShodanBaseAction): + def run(self, query, facets=None, page=1, minify=True): + try: + kwargs = {"page": page, "minify": minify} + if facets: + kwargs["facets"] = facets + + result = self.client.search(query, **kwargs) + matches = [self._trim(m) for m in result.get("matches", [])] + + return True, { + "total": result.get("total", 0), + "page": page, + "count": len(matches), + "matches": matches, + "facets": result.get("facets", {}), + } + except Exception as e: + return False, str(e) + + def _trim(self, match): + trimmed = {k: match[k] for k in MATCH_FIELDS if k in match} + + loc = trimmed.pop("location", {}) or {} + trimmed["country_code"] = loc.get("country_code", "") + trimmed["country_name"] = loc.get("country_name", "") + trimmed["city"] = loc.get("city", "") + + vulns = trimmed.get("vulns") + if isinstance(vulns, dict): + trimmed["vulns"] = { + cve: data.get("summary", "") for cve, data in vulns.items() + } + + return trimmed \ No newline at end of file diff --git a/actions/search.yaml b/actions/search.yaml new file mode 100644 index 0000000..3597f56 --- /dev/null +++ b/actions/search.yaml @@ -0,0 +1,33 @@ +--- +name: search +pack: shodan +runner_type: python-script +description: > + Search Shodan using the same query syntax as the website. Returns a list of + matching hosts with their banners, open ports, and metadata. +enabled: true +entry_point: search.py +parameters: + query: + type: string + description: > + Shodan search query (e.g. "apache country:US port:8080", + "product:nginx vuln:CVE-2021-44228"). + required: true + facets: + type: string + description: > + Comma-separated list of properties to get summary info on, e.g. + "country,org,port". Requires a paid API plan. + required: false + default: null + page: + type: integer + description: Page number of results (100 results per page). Starts at 1. + required: false + default: 1 + minify: + type: boolean + description: Return minimal host information per result to reduce payload size. + required: false + default: true \ No newline at end of file diff --git a/config.schema.yaml b/config.schema.yaml new file mode 100644 index 0000000..9252783 --- /dev/null +++ b/config.schema.yaml @@ -0,0 +1,5 @@ +shodan_api_key: + description: Shodan API key. Get one at https://account.shodan.io/ + type: string + secret: true + required: true \ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b39eaba88ca3b100def6181408559b0a4c8cc6bb GIT binary patch literal 5748 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 m=%bH5`skyNKKkf?UjGMS0ddKYo@<%_0000" + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..859a8a2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +shodan \ No newline at end of file From 5f5a1e8f21f2865d2a658c23566641b4f873fc89 Mon Sep 17 00:00:00 2001 From: duong22 <46995186+duong22@users.noreply.github.com> Date: Fri, 8 May 2026 00:20:27 +0200 Subject: [PATCH 2/3] Create LICENSE --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 08300bfe9ccec69c9eaec0509edbbe895852c641 Mon Sep 17 00:00:00 2001 From: duong22 Date: Thu, 7 May 2026 15:22:44 -0700 Subject: [PATCH 3/3] Introduction Shodan Pack --- README.md | 84 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 09ce213..93afdab 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,80 @@ -# StackStorm Exchange Incubator +# Shodan StackStorm Pack -### What is this? +StackStorm integration pack for [Shodan](https://www.shodan.io/) — the search engine for Internet-connected devices. -This repository is a very special place where user-submitted packs get reviewed, perfected, approved, and finally transferred to the Exchange. +## Requirements -If you want to submit your pack, it's simple! +- A Shodan API key — get one at [account.shodan.io](https://account.shodan.io/) -1. **Fork this repo,** -2. **create a subdirectory with your pack,** -3. **and open a Pull Request.** +## Installation -We'll take it from here. Even if your pack is work-in-progress, you can still submit it to get advice and early feedback from our engineers! Or ping us [on Slack](https://stackstorm.com/community-signup), which is generally the best place to get advice from the StackStorm Community. +```shell +st2 pack install shodan +``` -Before you submit a pack, make sure to read the [Create and Contribute a Pack](https://docs.stackstorm.com/reference/packs.html) section of our documentation. +## Configuration -Here's N.E.P.T.R. the StackStorm Exchange Governor, giving you a thumbs-up: +```shell +st2 pack config shodan +``` -![](http://i.imgur.com/3bqVAh0.gif) +Or create the config manually: -## Contributors License Agreement +```yaml +# /opt/stackstorm/configs/shodan.yaml +shodan_api_key: "YOUR_API_KEY_HERE" +request_timeout: 30 +``` -By contributing you agree that these contributions are your own (or approved by your employer) and -you grant a full, complete, irrevocable copyright license to all users and developers of the -project, present and future, pursuant to the license of the project. +Then reload: + +```shell +st2ctl reload --register-configs +``` + +## Actions + +| Action | Description | +|--------|-------------| +| `shodan.host_info` | Returns all Shodan data for a host — ports, banners, hostnames, geolocation, org, CVEs | +| `shodan.host_ports` | Returns open ports only for a host (faster than `host_info`) | +| `shodan.search` | Search Shodan using filter syntax | +| `shodan.dns_resolve` | Resolve one or more hostnames to IP addresses | +| `shodan.dns_reverse` | Reverse DNS lookup for one or more IP addresses | + +## Usage + +```shell +# Full host lookup +st2 run shodan.host_info ip=8.8.8.8 + +# Open ports only +st2 run shodan.host_ports ip=8.8.8.8 + +# Search +st2 run shodan.search query="product:redis country:VN" +st2 run shodan.search query="port:22 has:vuln" page=2 + +# DNS +st2 run shodan.dns_resolve hostnames='["google.com", "cloudflare.com"]' +st2 run shodan.dns_reverse ips='["8.8.8.8", "1.1.1.1"]' +``` + +## Shodan Query Filter Reference + +| Filter | Example | +|--------|---------| +| `port:` | `port:22` | +| `country:` | `country:VN` | +| `org:` | `org:"Amazon"` | +| `product:` | `product:nginx` | +| `os:` | `os:windows` | +| `asn:` | `asn:AS13335` | +| `vuln:` | `vuln:CVE-2021-44228` | +| `has:vuln` | `has:vuln` | + +Full filter reference: https://www.shodan.io/search/filters ## License -All packs must be licensed under the Apache 2.0 license. We will not accept your new pack -until we see a LICENSE file in your pull request with the Apache 2.0 license in it. +Apache 2.0 \ No newline at end of file