@burtthecoder/mcp-shodan 1.0.12 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -39
- package/build/index.js +263 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Shodan MCP Server
|
|
2
2
|
|
|
3
|
-
A Model Context Protocol (MCP) server for querying the [Shodan API](https://shodan.io) and [Shodan CVEDB](https://cvedb.shodan.io). This server provides
|
|
3
|
+
A Model Context Protocol (MCP) server for querying the [Shodan API](https://shodan.io) and [Shodan CVEDB](https://cvedb.shodan.io). This server provides comprehensive access to Shodan's network intelligence and security services, including IP reconnaissance, DNS operations, vulnerability tracking, and device discovery. All tools provide structured, formatted output for easy analysis and integration.
|
|
4
4
|
|
|
5
5
|
## Quick Start (Recommended)
|
|
6
6
|
|
|
@@ -58,45 +58,78 @@ npm run build
|
|
|
58
58
|
|
|
59
59
|
## Features
|
|
60
60
|
|
|
61
|
-
- **
|
|
62
|
-
- **
|
|
63
|
-
- **
|
|
64
|
-
- **
|
|
65
|
-
- **CPE Lookup**: Search for Common Platform Enumeration (CPE) entries by product name
|
|
66
|
-
- **CVEs by Product**: Search for all CVEs affecting a specific product or CPE
|
|
67
|
-
- **DNS Lookup**: Resolve hostnames to IP addresses
|
|
61
|
+
- **Network Reconnaissance**: Query detailed information about IP addresses, including open ports, services, and vulnerabilities
|
|
62
|
+
- **DNS Operations**: Forward and reverse DNS lookups for domains and IP addresses
|
|
63
|
+
- **Vulnerability Intelligence**: Access to Shodan's CVEDB for detailed vulnerability information, CPE lookups, and product-specific CVE tracking
|
|
64
|
+
- **Device Discovery**: Search Shodan's database of internet-connected devices with advanced filtering
|
|
68
65
|
|
|
69
66
|
## Tools
|
|
70
67
|
|
|
71
68
|
### 1. IP Lookup Tool
|
|
72
69
|
- Name: `ip_lookup`
|
|
73
|
-
- Description: Retrieve
|
|
70
|
+
- Description: Retrieve comprehensive information about an IP address, including geolocation, open ports, running services, SSL certificates, hostnames, and cloud provider details if available
|
|
74
71
|
- Parameters:
|
|
75
72
|
* `ip` (required): IP address to lookup
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
- Returns:
|
|
74
|
+
* IP Information (address, organization, ISP, ASN)
|
|
75
|
+
* Location (country, city, coordinates)
|
|
76
|
+
* Services (ports, protocols, banners)
|
|
77
|
+
* Cloud Provider details (if available)
|
|
78
|
+
* Associated hostnames and domains
|
|
79
|
+
* Tags
|
|
80
|
+
|
|
81
|
+
### 2. Shodan Search Tool
|
|
82
|
+
- Name: `shodan_search`
|
|
83
|
+
- Description: Search Shodan's database of internet-connected devices
|
|
80
84
|
- Parameters:
|
|
81
85
|
* `query` (required): Shodan search query
|
|
82
86
|
* `max_results` (optional, default: 10): Number of results to return
|
|
87
|
+
- Returns:
|
|
88
|
+
* Search summary with total results
|
|
89
|
+
* Country-based distribution statistics
|
|
90
|
+
* Detailed device information including:
|
|
91
|
+
- Basic information (IP, organization, ISP)
|
|
92
|
+
- Location data
|
|
93
|
+
- Service details
|
|
94
|
+
- Web server information
|
|
95
|
+
- Associated hostnames and domains
|
|
83
96
|
|
|
84
97
|
### 3. CVE Lookup Tool
|
|
85
98
|
- Name: `cve_lookup`
|
|
86
|
-
- Description:
|
|
99
|
+
- Description: Query detailed vulnerability information from Shodan's CVEDB
|
|
87
100
|
- Parameters:
|
|
88
101
|
* `cve` (required): CVE identifier in format CVE-YYYY-NNNNN (e.g., CVE-2021-44228)
|
|
89
102
|
- Returns:
|
|
90
|
-
*
|
|
91
|
-
|
|
92
|
-
-
|
|
103
|
+
* Basic Information (ID, published date, summary)
|
|
104
|
+
* Severity Scores:
|
|
105
|
+
- CVSS v2 and v3 with severity levels
|
|
106
|
+
- EPSS probability and ranking
|
|
107
|
+
* Impact Assessment:
|
|
93
108
|
- KEV status
|
|
94
|
-
- Proposed
|
|
95
|
-
- Ransomware
|
|
96
|
-
|
|
97
|
-
|
|
109
|
+
- Proposed mitigations
|
|
110
|
+
- Ransomware associations
|
|
111
|
+
* Affected products (CPEs)
|
|
112
|
+
* References
|
|
113
|
+
|
|
114
|
+
### 4. DNS Lookup Tool
|
|
115
|
+
- Name: `dns_lookup`
|
|
116
|
+
- Description: Resolve domain names to IP addresses using Shodan's DNS service
|
|
117
|
+
- Parameters:
|
|
118
|
+
* `hostnames` (required): Array of hostnames to resolve
|
|
119
|
+
- Returns:
|
|
120
|
+
* DNS resolutions mapping hostnames to IPs
|
|
121
|
+
* Summary of total lookups and queried hostnames
|
|
98
122
|
|
|
99
|
-
###
|
|
123
|
+
### 5. Reverse DNS Lookup Tool
|
|
124
|
+
- Name: `reverse_dns_lookup`
|
|
125
|
+
- Description: Perform reverse DNS lookups to find hostnames associated with IP addresses
|
|
126
|
+
- Parameters:
|
|
127
|
+
* `ips` (required): Array of IP addresses to lookup
|
|
128
|
+
- Returns:
|
|
129
|
+
* Reverse DNS resolutions mapping IPs to hostnames
|
|
130
|
+
* Summary of total lookups and results
|
|
131
|
+
|
|
132
|
+
### 6. CPE Lookup Tool
|
|
100
133
|
- Name: `cpe_lookup`
|
|
101
134
|
- Description: Search for Common Platform Enumeration (CPE) entries by product name
|
|
102
135
|
- Parameters:
|
|
@@ -108,9 +141,9 @@ npm run build
|
|
|
108
141
|
* When count is true: Total number of matching CPEs
|
|
109
142
|
* When count is false: List of CPEs with pagination details
|
|
110
143
|
|
|
111
|
-
###
|
|
144
|
+
### 7. CVEs by Product Tool
|
|
112
145
|
- Name: `cves_by_product`
|
|
113
|
-
- Description: Search for
|
|
146
|
+
- Description: Search for vulnerabilities affecting specific products or CPEs
|
|
114
147
|
- Parameters:
|
|
115
148
|
* `cpe23` (optional): CPE 2.3 identifier (format: cpe:2.3:part:vendor:product:version)
|
|
116
149
|
* `product` (optional): Name of the product to search for CVEs
|
|
@@ -125,14 +158,13 @@ npm run build
|
|
|
125
158
|
* Must provide either cpe23 or product, but not both
|
|
126
159
|
* Date filtering uses published time of CVEs
|
|
127
160
|
- Returns:
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
* `hostnames` (required): Array of hostnames to resolve
|
|
161
|
+
* Query information
|
|
162
|
+
* Results summary with pagination details
|
|
163
|
+
* Detailed vulnerability information including:
|
|
164
|
+
- Basic information
|
|
165
|
+
- Severity scores
|
|
166
|
+
- Impact assessments
|
|
167
|
+
- References
|
|
136
168
|
|
|
137
169
|
## Requirements
|
|
138
170
|
|
|
@@ -143,15 +175,44 @@ npm run build
|
|
|
143
175
|
|
|
144
176
|
### API Key Issues
|
|
145
177
|
|
|
146
|
-
If you see API key related errors:
|
|
178
|
+
If you see API key related errors (e.g., "Request failed with status code 401"):
|
|
147
179
|
|
|
148
180
|
1. Verify your API key:
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
181
|
+
- Must be a valid Shodan API key from your [account settings](https://account.shodan.io/)
|
|
182
|
+
- Ensure the key has sufficient credits/permissions for the operation
|
|
183
|
+
- Check for extra spaces or quotes around the key in the configuration
|
|
184
|
+
- Verify the key is correctly set in the SHODAN_API_KEY environment variable
|
|
185
|
+
|
|
186
|
+
2. Common Error Codes:
|
|
187
|
+
- 401 Unauthorized: Invalid API key or missing authentication
|
|
188
|
+
- 402 Payment Required: Out of query credits
|
|
189
|
+
- 429 Too Many Requests: Rate limit exceeded
|
|
190
|
+
|
|
191
|
+
3. Configuration Steps:
|
|
192
|
+
a. Get your API key from [Shodan Account](https://account.shodan.io/)
|
|
193
|
+
b. Add it to your configuration file:
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"mcpServers": {
|
|
197
|
+
"shodan": {
|
|
198
|
+
"command": "mcp-shodan",
|
|
199
|
+
"env": {
|
|
200
|
+
"SHODAN_API_KEY": "your-actual-api-key-here"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
c. Save the config file
|
|
207
|
+
d. Restart Claude Desktop
|
|
208
|
+
|
|
209
|
+
4. Testing Your Key:
|
|
210
|
+
- Try a simple query first (e.g., dns_lookup for "google.com")
|
|
211
|
+
- Check your [Shodan account dashboard](https://account.shodan.io/) for credit status
|
|
212
|
+
- Verify the key works directly with curl:
|
|
213
|
+
```bash
|
|
214
|
+
curl "https://api.shodan.io/dns/resolve?hostnames=google.com&key=your-api-key"
|
|
215
|
+
```
|
|
155
216
|
|
|
156
217
|
### Module Loading Issues
|
|
157
218
|
|
|
@@ -180,6 +241,7 @@ The server includes comprehensive error handling for:
|
|
|
180
241
|
|
|
181
242
|
## Version History
|
|
182
243
|
|
|
244
|
+
- v1.0.12: Added reverse DNS lookup and improved output formatting
|
|
183
245
|
- v1.0.7: Added CVEs by Product search functionality and renamed vulnerabilities tool to cve_lookup
|
|
184
246
|
- v1.0.6: Added CVEDB integration for enhanced CVE lookups and CPE search functionality
|
|
185
247
|
- v1.0.0: Initial release with core functionality
|
package/build/index.js
CHANGED
|
@@ -33,7 +33,7 @@ function logToFile(message) {
|
|
|
33
33
|
const IpLookupArgsSchema = z.object({
|
|
34
34
|
ip: z.string().describe("The IP address to query."),
|
|
35
35
|
});
|
|
36
|
-
const
|
|
36
|
+
const ShodanSearchArgsSchema = z.object({
|
|
37
37
|
query: z.string().describe("Search query for Shodan."),
|
|
38
38
|
max_results: z
|
|
39
39
|
.number()
|
|
@@ -49,6 +49,9 @@ const CVELookupArgsSchema = z.object({
|
|
|
49
49
|
const DnsLookupArgsSchema = z.object({
|
|
50
50
|
hostnames: z.array(z.string()).describe("List of hostnames to resolve."),
|
|
51
51
|
});
|
|
52
|
+
const ReverseDnsLookupArgsSchema = z.object({
|
|
53
|
+
ips: z.array(z.string()).describe("List of IP addresses to perform reverse DNS lookup on."),
|
|
54
|
+
});
|
|
52
55
|
const CpeLookupArgsSchema = z.object({
|
|
53
56
|
product: z.string().describe("The name of the product to search for CPEs."),
|
|
54
57
|
count: z.boolean().optional().default(false).describe("If true, returns only the count of matching CPEs."),
|
|
@@ -151,7 +154,14 @@ server.setRequestHandler(InitializeRequestSchema, async (request) => {
|
|
|
151
154
|
name: "shodan-mcp",
|
|
152
155
|
version: "1.0.0",
|
|
153
156
|
},
|
|
154
|
-
instructions:
|
|
157
|
+
instructions: `This MCP server provides comprehensive access to Shodan's network intelligence and security services:
|
|
158
|
+
|
|
159
|
+
- Network Reconnaissance: Query detailed information about IP addresses, including open ports, services, and vulnerabilities
|
|
160
|
+
- DNS Operations: Forward and reverse DNS lookups for domains and IP addresses
|
|
161
|
+
- Vulnerability Intelligence: Access to Shodan's CVEDB for detailed vulnerability information, CPE lookups, and product-specific CVE tracking
|
|
162
|
+
- Device Discovery: Search Shodan's database of internet-connected devices with advanced filtering
|
|
163
|
+
|
|
164
|
+
Each tool provides structured, formatted output for easy analysis and integration.`,
|
|
155
165
|
};
|
|
156
166
|
});
|
|
157
167
|
// Register Tools
|
|
@@ -159,34 +169,39 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
159
169
|
const tools = [
|
|
160
170
|
{
|
|
161
171
|
name: "ip_lookup",
|
|
162
|
-
description: "Retrieve information about an IP address.",
|
|
172
|
+
description: "Retrieve comprehensive information about an IP address, including geolocation, open ports, running services, SSL certificates, hostnames, and cloud provider details if available. Returns service banners and HTTP server information when present.",
|
|
163
173
|
inputSchema: zodToJsonSchema(IpLookupArgsSchema),
|
|
164
174
|
},
|
|
165
175
|
{
|
|
166
|
-
name: "
|
|
167
|
-
description: "Search
|
|
168
|
-
inputSchema: zodToJsonSchema(
|
|
176
|
+
name: "shodan_search",
|
|
177
|
+
description: "Search Shodan's database of internet-connected devices. Returns detailed information about matching devices including services, vulnerabilities, and geographic distribution. Supports advanced search filters and returns country-based statistics.",
|
|
178
|
+
inputSchema: zodToJsonSchema(ShodanSearchArgsSchema),
|
|
169
179
|
},
|
|
170
180
|
{
|
|
171
181
|
name: "cve_lookup",
|
|
172
|
-
description: "
|
|
182
|
+
description: "Query detailed vulnerability information from Shodan's CVEDB. Returns comprehensive CVE details including CVSS scores (v2/v3), EPSS probability and ranking, KEV status, proposed mitigations, ransomware associations, and affected products (CPEs).",
|
|
173
183
|
inputSchema: zodToJsonSchema(CVELookupArgsSchema),
|
|
174
184
|
},
|
|
175
185
|
{
|
|
176
186
|
name: "dns_lookup",
|
|
177
|
-
description: "
|
|
187
|
+
description: "Resolve domain names to IP addresses using Shodan's DNS service. Supports batch resolution of multiple hostnames in a single query. Returns IP addresses mapped to their corresponding hostnames.",
|
|
178
188
|
inputSchema: zodToJsonSchema(DnsLookupArgsSchema),
|
|
179
189
|
},
|
|
180
190
|
{
|
|
181
191
|
name: "cpe_lookup",
|
|
182
|
-
description: "Search for Common Platform Enumeration (CPE) entries by product name.",
|
|
192
|
+
description: "Search for Common Platform Enumeration (CPE) entries by product name in Shodan's CVEDB. Supports pagination and can return either full CPE details or just the total count. Useful for identifying specific versions and configurations of software and hardware.",
|
|
183
193
|
inputSchema: zodToJsonSchema(CpeLookupArgsSchema),
|
|
184
194
|
},
|
|
185
195
|
{
|
|
186
196
|
name: "cves_by_product",
|
|
187
|
-
description: "Search for
|
|
197
|
+
description: "Search for vulnerabilities affecting specific products or CPEs. Supports filtering by KEV status, sorting by EPSS score, date ranges, and pagination. Can search by product name or CPE 2.3 identifier. Returns detailed vulnerability information including severity scores and impact assessments.",
|
|
188
198
|
inputSchema: zodToJsonSchema(CVEsByProductArgsSchema),
|
|
189
199
|
},
|
|
200
|
+
{
|
|
201
|
+
name: "reverse_dns_lookup",
|
|
202
|
+
description: "Perform reverse DNS lookups to find hostnames associated with IP addresses. Supports batch lookups of multiple IP addresses in a single query. Returns all known hostnames for each IP address, with clear indication when no hostnames are found.",
|
|
203
|
+
inputSchema: zodToJsonSchema(ReverseDnsLookupArgsSchema),
|
|
204
|
+
},
|
|
190
205
|
];
|
|
191
206
|
logToFile("Registered tools.");
|
|
192
207
|
return { tools };
|
|
@@ -203,17 +218,55 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
203
218
|
throw new Error("Invalid ip_lookup arguments");
|
|
204
219
|
}
|
|
205
220
|
const result = await queryShodan(`/shodan/host/${parsedIpArgs.data.ip}`, {});
|
|
221
|
+
// Format the response in a user-friendly way
|
|
222
|
+
const formattedResult = {
|
|
223
|
+
"IP Information": {
|
|
224
|
+
"IP Address": result.ip_str,
|
|
225
|
+
"Organization": result.org,
|
|
226
|
+
"ISP": result.isp,
|
|
227
|
+
"ASN": result.asn,
|
|
228
|
+
"Last Update": result.last_update
|
|
229
|
+
},
|
|
230
|
+
"Location": {
|
|
231
|
+
"Country": result.country_name,
|
|
232
|
+
"City": result.city,
|
|
233
|
+
"Coordinates": `${result.latitude}, ${result.longitude}`,
|
|
234
|
+
"Region": result.region_code
|
|
235
|
+
},
|
|
236
|
+
"Services": result.ports.map((port) => {
|
|
237
|
+
const service = result.data.find((d) => d.port === port);
|
|
238
|
+
return {
|
|
239
|
+
"Port": port,
|
|
240
|
+
"Protocol": service?.transport || "unknown",
|
|
241
|
+
"Service": service?.data?.trim() || "No banner",
|
|
242
|
+
...(service?.http ? {
|
|
243
|
+
"HTTP": {
|
|
244
|
+
"Server": service.http.server,
|
|
245
|
+
"Title": service.http.title,
|
|
246
|
+
}
|
|
247
|
+
} : {})
|
|
248
|
+
};
|
|
249
|
+
}),
|
|
250
|
+
"Cloud Provider": result.data[0]?.cloud ? {
|
|
251
|
+
"Provider": result.data[0].cloud.provider,
|
|
252
|
+
"Service": result.data[0].cloud.service,
|
|
253
|
+
"Region": result.data[0].cloud.region
|
|
254
|
+
} : "Not detected",
|
|
255
|
+
"Hostnames": result.hostnames || [],
|
|
256
|
+
"Domains": result.domains || [],
|
|
257
|
+
"Tags": result.tags || []
|
|
258
|
+
};
|
|
206
259
|
return {
|
|
207
260
|
content: [
|
|
208
261
|
{
|
|
209
262
|
type: "text",
|
|
210
|
-
text: JSON.stringify(
|
|
263
|
+
text: JSON.stringify(formattedResult, null, 2),
|
|
211
264
|
},
|
|
212
265
|
],
|
|
213
266
|
};
|
|
214
267
|
}
|
|
215
|
-
case "
|
|
216
|
-
const parsedSearchArgs =
|
|
268
|
+
case "shodan_search": {
|
|
269
|
+
const parsedSearchArgs = ShodanSearchArgsSchema.safeParse(args);
|
|
217
270
|
if (!parsedSearchArgs.success) {
|
|
218
271
|
throw new Error("Invalid search arguments");
|
|
219
272
|
}
|
|
@@ -221,11 +274,54 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
221
274
|
query: parsedSearchArgs.data.query,
|
|
222
275
|
limit: parsedSearchArgs.data.max_results,
|
|
223
276
|
});
|
|
277
|
+
// Format the response in a user-friendly way
|
|
278
|
+
const formattedResult = {
|
|
279
|
+
"Search Summary": {
|
|
280
|
+
"Query": parsedSearchArgs.data.query,
|
|
281
|
+
"Total Results": result.total,
|
|
282
|
+
"Results Returned": result.matches.length
|
|
283
|
+
},
|
|
284
|
+
"Country Distribution": result.facets?.country?.map(country => ({
|
|
285
|
+
"Country": country.value,
|
|
286
|
+
"Count": country.count,
|
|
287
|
+
"Percentage": `${((country.count / result.total) * 100).toFixed(2)}%`
|
|
288
|
+
})) || [],
|
|
289
|
+
"Matches": result.matches.map(match => ({
|
|
290
|
+
"Basic Information": {
|
|
291
|
+
"IP Address": match.ip_str,
|
|
292
|
+
"Organization": match.org,
|
|
293
|
+
"ISP": match.isp,
|
|
294
|
+
"ASN": match.asn,
|
|
295
|
+
"Last Update": match.timestamp
|
|
296
|
+
},
|
|
297
|
+
"Location": {
|
|
298
|
+
"Country": match.location.country_name,
|
|
299
|
+
"City": match.location.city || "Unknown",
|
|
300
|
+
"Region": match.location.region_code || "Unknown",
|
|
301
|
+
"Coordinates": `${match.location.latitude}, ${match.location.longitude}`
|
|
302
|
+
},
|
|
303
|
+
"Service Details": {
|
|
304
|
+
"Port": match.port,
|
|
305
|
+
"Transport": match.transport,
|
|
306
|
+
"Product": match.product || "Unknown",
|
|
307
|
+
"Version": match.version || "Unknown",
|
|
308
|
+
"CPE": match.cpe || []
|
|
309
|
+
},
|
|
310
|
+
"Web Information": match.http ? {
|
|
311
|
+
"Server": match.http.server,
|
|
312
|
+
"Title": match.http.title,
|
|
313
|
+
"Robots.txt": match.http.robots ? "Present" : "Not found",
|
|
314
|
+
"Sitemap": match.http.sitemap ? "Present" : "Not found"
|
|
315
|
+
} : "No HTTP information",
|
|
316
|
+
"Hostnames": match.hostnames,
|
|
317
|
+
"Domains": match.domains
|
|
318
|
+
}))
|
|
319
|
+
};
|
|
224
320
|
return {
|
|
225
321
|
content: [
|
|
226
322
|
{
|
|
227
323
|
type: "text",
|
|
228
|
-
text: JSON.stringify(
|
|
324
|
+
text: JSON.stringify(formattedResult, null, 2),
|
|
229
325
|
},
|
|
230
326
|
],
|
|
231
327
|
};
|
|
@@ -239,24 +335,54 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
239
335
|
logToFile(`Looking up CVE: ${cveId}`);
|
|
240
336
|
try {
|
|
241
337
|
const result = await queryCVEDB(cveId);
|
|
338
|
+
// Helper function to format CVSS score severity
|
|
339
|
+
const getCvssSeverity = (score) => {
|
|
340
|
+
if (score >= 9.0)
|
|
341
|
+
return "Critical";
|
|
342
|
+
if (score >= 7.0)
|
|
343
|
+
return "High";
|
|
344
|
+
if (score >= 4.0)
|
|
345
|
+
return "Medium";
|
|
346
|
+
if (score >= 0.1)
|
|
347
|
+
return "Low";
|
|
348
|
+
return "None";
|
|
349
|
+
};
|
|
350
|
+
// Format the response in a user-friendly way
|
|
351
|
+
const formattedResult = {
|
|
352
|
+
"Basic Information": {
|
|
353
|
+
"CVE ID": result.cve_id,
|
|
354
|
+
"Published": new Date(result.published_time).toLocaleString(),
|
|
355
|
+
"Summary": result.summary
|
|
356
|
+
},
|
|
357
|
+
"Severity Scores": {
|
|
358
|
+
"CVSS v3": result.cvss_v3 ? {
|
|
359
|
+
"Score": result.cvss_v3,
|
|
360
|
+
"Severity": getCvssSeverity(result.cvss_v3)
|
|
361
|
+
} : "Not available",
|
|
362
|
+
"CVSS v2": result.cvss_v2 ? {
|
|
363
|
+
"Score": result.cvss_v2,
|
|
364
|
+
"Severity": getCvssSeverity(result.cvss_v2)
|
|
365
|
+
} : "Not available",
|
|
366
|
+
"EPSS": result.epss ? {
|
|
367
|
+
"Score": `${(result.epss * 100).toFixed(2)}%`,
|
|
368
|
+
"Ranking": `Top ${(result.ranking_epss * 100).toFixed(2)}%`
|
|
369
|
+
} : "Not available"
|
|
370
|
+
},
|
|
371
|
+
"Impact Assessment": {
|
|
372
|
+
"Known Exploited Vulnerability": result.kev ? "Yes" : "No",
|
|
373
|
+
"Proposed Action": result.propose_action || "No specific action proposed",
|
|
374
|
+
"Ransomware Campaign": result.ransomware_campaign || "No known ransomware campaigns"
|
|
375
|
+
},
|
|
376
|
+
"Affected Products": result.cpes?.length > 0 ? result.cpes : ["No specific products listed"],
|
|
377
|
+
"Additional Information": {
|
|
378
|
+
"References": result.references?.length > 0 ? result.references : ["No references provided"]
|
|
379
|
+
}
|
|
380
|
+
};
|
|
242
381
|
return {
|
|
243
382
|
content: [
|
|
244
383
|
{
|
|
245
384
|
type: "text",
|
|
246
|
-
text: JSON.stringify(
|
|
247
|
-
cve_id: result.cve_id,
|
|
248
|
-
summary: result.summary,
|
|
249
|
-
cvss_v3: result.cvss_v3,
|
|
250
|
-
cvss_v2: result.cvss_v2,
|
|
251
|
-
epss: result.epss,
|
|
252
|
-
ranking_epss: result.ranking_epss,
|
|
253
|
-
kev: result.kev,
|
|
254
|
-
propose_action: result.propose_action,
|
|
255
|
-
ransomware_campaign: result.ransomware_campaign,
|
|
256
|
-
published: result.published_time,
|
|
257
|
-
references: result.references,
|
|
258
|
-
affected_products: result.cpes
|
|
259
|
-
}, null, 2),
|
|
385
|
+
text: JSON.stringify(formattedResult, null, 2),
|
|
260
386
|
},
|
|
261
387
|
],
|
|
262
388
|
};
|
|
@@ -278,20 +404,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
278
404
|
if (!parsedDnsArgs.success) {
|
|
279
405
|
throw new Error("Invalid dns_lookup arguments");
|
|
280
406
|
}
|
|
281
|
-
//
|
|
407
|
+
// Join hostnames with commas for the API request
|
|
282
408
|
const hostnamesString = parsedDnsArgs.data.hostnames.join(",");
|
|
283
|
-
// Log the request parameters for debugging
|
|
284
|
-
logToFile(`DNS lookup request parameters: ${JSON.stringify({ hostnames: hostnamesString })}`);
|
|
285
409
|
const result = await queryShodan("/dns/resolve", {
|
|
286
410
|
hostnames: hostnamesString
|
|
287
411
|
});
|
|
288
|
-
//
|
|
289
|
-
|
|
412
|
+
// Format the response in a user-friendly way
|
|
413
|
+
const formattedResult = {
|
|
414
|
+
"DNS Resolutions": Object.entries(result).map(([hostname, ip]) => ({
|
|
415
|
+
"Hostname": hostname,
|
|
416
|
+
"IP Address": ip
|
|
417
|
+
})),
|
|
418
|
+
"Summary": {
|
|
419
|
+
"Total Lookups": Object.keys(result).length,
|
|
420
|
+
"Queried Hostnames": parsedDnsArgs.data.hostnames
|
|
421
|
+
}
|
|
422
|
+
};
|
|
290
423
|
return {
|
|
291
424
|
content: [
|
|
292
425
|
{
|
|
293
426
|
type: "text",
|
|
294
|
-
text: JSON.stringify(
|
|
427
|
+
text: JSON.stringify(formattedResult, null, 2)
|
|
295
428
|
},
|
|
296
429
|
],
|
|
297
430
|
};
|
|
@@ -355,22 +488,74 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
355
488
|
start_date: parsedArgs.data.start_date,
|
|
356
489
|
end_date: parsedArgs.data.end_date
|
|
357
490
|
});
|
|
491
|
+
// Helper function to format CVSS score severity
|
|
492
|
+
const getCvssSeverity = (score) => {
|
|
493
|
+
if (score >= 9.0)
|
|
494
|
+
return "Critical";
|
|
495
|
+
if (score >= 7.0)
|
|
496
|
+
return "High";
|
|
497
|
+
if (score >= 4.0)
|
|
498
|
+
return "Medium";
|
|
499
|
+
if (score >= 0.1)
|
|
500
|
+
return "Low";
|
|
501
|
+
return "None";
|
|
502
|
+
};
|
|
358
503
|
// Format the response based on whether it's a count request or full CVE list
|
|
359
504
|
const formattedResult = parsedArgs.data.count
|
|
360
|
-
? {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
is_kev: parsedArgs.data.is_kev,
|
|
370
|
-
sort_by_epss: parsedArgs.data.sort_by_epss,
|
|
371
|
-
start_date: parsedArgs.data.start_date,
|
|
372
|
-
end_date: parsedArgs.data.end_date
|
|
505
|
+
? {
|
|
506
|
+
"Query Information": {
|
|
507
|
+
"Product": parsedArgs.data.product || "N/A",
|
|
508
|
+
"CPE 2.3": parsedArgs.data.cpe23 || "N/A",
|
|
509
|
+
"KEV Only": parsedArgs.data.is_kev ? "Yes" : "No",
|
|
510
|
+
"Sort by EPSS": parsedArgs.data.sort_by_epss ? "Yes" : "No"
|
|
511
|
+
},
|
|
512
|
+
"Results": {
|
|
513
|
+
"Total CVEs Found": result.total
|
|
373
514
|
}
|
|
515
|
+
}
|
|
516
|
+
: {
|
|
517
|
+
"Query Information": {
|
|
518
|
+
"Product": parsedArgs.data.product || "N/A",
|
|
519
|
+
"CPE 2.3": parsedArgs.data.cpe23 || "N/A",
|
|
520
|
+
"KEV Only": parsedArgs.data.is_kev ? "Yes" : "No",
|
|
521
|
+
"Sort by EPSS": parsedArgs.data.sort_by_epss ? "Yes" : "No",
|
|
522
|
+
"Date Range": parsedArgs.data.start_date ?
|
|
523
|
+
`${parsedArgs.data.start_date} to ${parsedArgs.data.end_date || 'now'}` :
|
|
524
|
+
"All dates"
|
|
525
|
+
},
|
|
526
|
+
"Results Summary": {
|
|
527
|
+
"Total CVEs Found": result.total,
|
|
528
|
+
"CVEs Returned": result.cves.length,
|
|
529
|
+
"Page": `${Math.floor(parsedArgs.data.skip / parsedArgs.data.limit) + 1}`,
|
|
530
|
+
"CVEs per Page": parsedArgs.data.limit
|
|
531
|
+
},
|
|
532
|
+
"Vulnerabilities": result.cves.map((cve) => ({
|
|
533
|
+
"Basic Information": {
|
|
534
|
+
"CVE ID": cve.cve_id,
|
|
535
|
+
"Published": new Date(cve.published_time).toLocaleString(),
|
|
536
|
+
"Summary": cve.summary
|
|
537
|
+
},
|
|
538
|
+
"Severity Scores": {
|
|
539
|
+
"CVSS v3": cve.cvss_v3 ? {
|
|
540
|
+
"Score": cve.cvss_v3,
|
|
541
|
+
"Severity": getCvssSeverity(cve.cvss_v3)
|
|
542
|
+
} : "Not available",
|
|
543
|
+
"CVSS v2": cve.cvss_v2 ? {
|
|
544
|
+
"Score": cve.cvss_v2,
|
|
545
|
+
"Severity": getCvssSeverity(cve.cvss_v2)
|
|
546
|
+
} : "Not available",
|
|
547
|
+
"EPSS": cve.epss ? {
|
|
548
|
+
"Score": `${(cve.epss * 100).toFixed(2)}%`,
|
|
549
|
+
"Ranking": `Top ${(cve.ranking_epss * 100).toFixed(2)}%`
|
|
550
|
+
} : "Not available"
|
|
551
|
+
},
|
|
552
|
+
"Impact Assessment": {
|
|
553
|
+
"Known Exploited Vulnerability": cve.kev ? "Yes" : "No",
|
|
554
|
+
"Proposed Action": cve.propose_action || "No specific action proposed",
|
|
555
|
+
"Ransomware Campaign": cve.ransomware_campaign || "No known ransomware campaigns"
|
|
556
|
+
},
|
|
557
|
+
"References": cve.references?.length > 0 ? cve.references : ["No references provided"]
|
|
558
|
+
}))
|
|
374
559
|
};
|
|
375
560
|
return {
|
|
376
561
|
content: [
|
|
@@ -393,6 +578,37 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
393
578
|
};
|
|
394
579
|
}
|
|
395
580
|
}
|
|
581
|
+
case "reverse_dns_lookup": {
|
|
582
|
+
const parsedArgs = ReverseDnsLookupArgsSchema.safeParse(args);
|
|
583
|
+
if (!parsedArgs.success) {
|
|
584
|
+
throw new Error("Invalid reverse_dns_lookup arguments");
|
|
585
|
+
}
|
|
586
|
+
// Join IPs with commas for the API request
|
|
587
|
+
const ipsString = parsedArgs.data.ips.join(",");
|
|
588
|
+
const result = await queryShodan("/dns/reverse", {
|
|
589
|
+
ips: ipsString
|
|
590
|
+
});
|
|
591
|
+
// Format the response in a user-friendly way
|
|
592
|
+
const formattedResult = {
|
|
593
|
+
"Reverse DNS Resolutions": Object.entries(result).map(([ip, hostnames]) => ({
|
|
594
|
+
"IP Address": ip,
|
|
595
|
+
"Hostnames": hostnames.length > 0 ? hostnames : ["No hostnames found"]
|
|
596
|
+
})),
|
|
597
|
+
"Summary": {
|
|
598
|
+
"Total IPs Queried": parsedArgs.data.ips.length,
|
|
599
|
+
"IPs with Results": Object.keys(result).length,
|
|
600
|
+
"Queried IP Addresses": parsedArgs.data.ips
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
return {
|
|
604
|
+
content: [
|
|
605
|
+
{
|
|
606
|
+
type: "text",
|
|
607
|
+
text: JSON.stringify(formattedResult, null, 2)
|
|
608
|
+
},
|
|
609
|
+
],
|
|
610
|
+
};
|
|
611
|
+
}
|
|
396
612
|
default:
|
|
397
613
|
throw new Error(`Unknown tool: ${name}`);
|
|
398
614
|
}
|