Skip to content

Commit 887bf2c

Browse files
committed
use facets for shodan
1 parent 2116cea commit 887bf2c

File tree

2 files changed

+115
-87
lines changed

2 files changed

+115
-87
lines changed

Gemfile.lock

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -158,32 +158,33 @@ GEM
158158
rspec-expectations (~> 3.12)
159159
arel-helpers (2.16.0)
160160
activerecord (>= 3.1.0, < 8.1)
161-
ast (2.4.2)
161+
ast (2.4.3)
162162
aws-eventstream (1.3.2)
163-
aws-partitions (1.1065.0)
164-
aws-sdk-core (3.220.1)
163+
aws-partitions (1.1084.0)
164+
aws-sdk-core (3.222.1)
165165
aws-eventstream (~> 1, >= 1.3.0)
166166
aws-partitions (~> 1, >= 1.992.0)
167167
aws-sigv4 (~> 1.9)
168168
base64
169169
jmespath (~> 1, >= 1.6.1)
170-
aws-sdk-ec2 (1.511.0)
170+
logger
171+
aws-sdk-ec2 (1.515.0)
171172
aws-sdk-core (~> 3, >= 3.216.0)
172173
aws-sigv4 (~> 1.5)
173174
aws-sdk-ec2instanceconnect (1.55.0)
174175
aws-sdk-core (~> 3, >= 3.216.0)
175176
aws-sigv4 (~> 1.5)
176-
aws-sdk-iam (1.119.0)
177+
aws-sdk-iam (1.120.0)
177178
aws-sdk-core (~> 3, >= 3.216.0)
178179
aws-sigv4 (~> 1.5)
179180
aws-sdk-kms (1.99.0)
180181
aws-sdk-core (~> 3, >= 3.216.0)
181182
aws-sigv4 (~> 1.5)
182-
aws-sdk-s3 (1.182.0)
183+
aws-sdk-s3 (1.183.0)
183184
aws-sdk-core (~> 3, >= 3.216.0)
184185
aws-sdk-kms (~> 1)
185186
aws-sigv4 (~> 1.5)
186-
aws-sdk-ssm (1.191.0)
187+
aws-sdk-ssm (1.192.0)
187188
aws-sdk-core (~> 3, >= 3.216.0)
188189
aws-sigv4 (~> 1.5)
189190
aws-sigv4 (1.11.0)
@@ -198,19 +199,19 @@ GEM
198199
msgpack (~> 1.2)
199200
bson (5.0.2)
200201
builder (3.3.0)
201-
byebug (11.1.3)
202+
byebug (12.0.0)
202203
chunky_png (1.4.0)
203204
coderay (1.1.3)
204205
concurrent-ruby (1.3.4)
205206
cookiejar (0.3.4)
206207
crass (1.0.6)
207-
csv (3.3.2)
208+
csv (3.3.3)
208209
daemons (1.4.1)
209210
date (3.4.1)
210211
debug (1.8.0)
211212
irb (>= 1.5.0)
212213
reline (>= 0.3.1)
213-
diff-lcs (1.6.0)
214+
diff-lcs (1.6.1)
214215
dnsruby (1.72.4)
215216
base64 (~> 0.2.0)
216217
logger (~> 1.6.5)
@@ -244,7 +245,7 @@ GEM
244245
faraday-net_http (>= 2.0, < 3.1)
245246
ruby2_keywords (>= 0.0.4)
246247
faraday-net_http (3.0.2)
247-
faraday-retry (2.2.1)
248+
faraday-retry (2.3.1)
248249
faraday (~> 2.0)
249250
faye-websocket (0.11.3)
250251
eventmachine (>= 0.12.0)
@@ -291,9 +292,11 @@ GEM
291292
nokogiri (>= 1.12.0)
292293
memory_profiler (1.1.0)
293294
metasm (1.0.5)
294-
metasploit-concern (5.0.4)
295+
metasploit-concern (5.0.5)
295296
activemodel (~> 7.0)
296297
activesupport (~> 7.0)
298+
drb
299+
mutex_m
297300
railties (~> 7.0)
298301
zeitwerk
299302
metasploit-credential (6.0.14)
@@ -323,10 +326,10 @@ GEM
323326
webrick
324327
metasploit_payloads-mettle (1.0.35)
325328
method_source (1.1.0)
326-
mime-types (3.6.0)
329+
mime-types (3.6.2)
327330
logger
328331
mime-types-data (~> 3.2015)
329-
mime-types-data (3.2025.0304)
332+
mime-types-data (3.2025.0408)
330333
mini_portile2 (2.8.8)
331334
minitest (5.25.5)
332335
mqtt (0.6.0)
@@ -350,7 +353,7 @@ GEM
350353
network_interface (0.0.4)
351354
nexpose (7.3.0)
352355
nio4r (2.7.4)
353-
nokogiri (1.18.3)
356+
nokogiri (1.18.7)
354357
mini_portile2 (~> 2.8.2)
355358
racc (~> 1.4)
356359
nori (2.7.1)
@@ -365,7 +368,7 @@ GEM
365368
packetfu (2.0.0)
366369
pcaprub (~> 0.13.1)
367370
parallel (1.26.3)
368-
parser (3.3.7.1)
371+
parser (3.3.7.4)
369372
ast (~> 2.4.1)
370373
racc
371374
patch_finder (1.0.2)
@@ -377,12 +380,13 @@ GEM
377380
ruby-rc4
378381
ttfunk
379382
pg (1.5.9)
380-
pry (0.14.2)
383+
prism (1.4.0)
384+
pry (0.15.2)
381385
coderay (~> 1.1)
382386
method_source (~> 1.0)
383-
pry-byebug (3.10.1)
384-
byebug (~> 11.0)
385-
pry (>= 0.13, < 0.15)
387+
pry-byebug (3.11.0)
388+
byebug (~> 12.0)
389+
pry (>= 0.13, < 0.16)
386390
public_suffix (6.0.1)
387391
puma (6.6.0)
388392
nio4r (~> 2.0)
@@ -412,11 +416,11 @@ GEM
412416
rasn1 (0.14.0)
413417
strptime (~> 0.2.5)
414418
rb-readline (0.5.5)
415-
recog (3.1.14)
419+
recog (3.1.16)
416420
nokogiri
417421
redcarpet (3.6.1)
418422
regexp_parser (2.10.0)
419-
reline (0.6.0)
423+
reline (0.6.1)
420424
io-console (~> 0.5)
421425
require_all (3.0.0)
422426
rex-arch (0.1.18)
@@ -427,7 +431,7 @@ GEM
427431
rex-core
428432
rex-struct2
429433
rex-text
430-
rex-core (0.1.32)
434+
rex-core (0.1.33)
431435
rex-encoder (0.1.8)
432436
metasm
433437
rex-arch
@@ -457,15 +461,16 @@ GEM
457461
metasm
458462
rex-core
459463
rex-text
460-
rex-socket (0.1.59)
464+
rex-socket (0.1.60)
461465
dnsruby
462466
rex-core
463467
rex-sslscan (0.1.11)
464468
rex-core
465469
rex-socket
466470
rex-text
467471
rex-struct2 (0.1.5)
468-
rex-text (0.2.60)
472+
rex-text (0.2.61)
473+
bigdecimal
469474
rex-zip (0.1.6)
470475
rex-text
471476
rexml (3.4.1)
@@ -507,8 +512,9 @@ GEM
507512
rubocop-ast (>= 1.32.2, < 2.0)
508513
ruby-progressbar (~> 1.7)
509514
unicode-display_width (>= 2.4.0, < 3.0)
510-
rubocop-ast (1.38.1)
511-
parser (>= 3.3.1.0)
515+
rubocop-ast (1.44.0)
516+
parser (>= 3.3.7.2)
517+
prism (~> 1.4)
512518
ruby-macho (4.1.0)
513519
ruby-mysql (4.2.0)
514520
ruby-prof (1.4.2)
@@ -555,7 +561,7 @@ GEM
555561
bigdecimal (~> 3.1)
556562
tzinfo (2.0.6)
557563
concurrent-ruby (~> 1.0)
558-
tzinfo-data (1.2025.1)
564+
tzinfo-data (1.2025.2)
559565
tzinfo (>= 1.0.0)
560566
unicode-display_width (2.6.0)
561567
unix-crypt (1.3.1)

modules/auxiliary/gather/shodan_search.rb

Lines changed: 81 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def initialize(info = {})
3636
[
3737
OptString.new('SHODAN_APIKEY', [true, 'The SHODAN API key']),
3838
OptString.new('QUERY', [true, 'Keywords you want to search for']),
39+
OptString.new('FACETS', [true, 'List of facets']),
3940
OptString.new('OUTFILE', [false, 'A filename to store the list of IPs']),
4041
OptBool.new('DATABASE', [false, 'Add search results to the database', false]),
4142
OptInt.new('MAXPAGE', [true, 'Max amount of pages to collect', 1]),
@@ -55,7 +56,7 @@ def initialize(info = {})
5556
end
5657

5758
# create our Shodan query function that performs the actual web request
58-
def shodan_query(apikey, query, page)
59+
def shodan_query(apikey, query, facets, page)
5960
# send our query to Shodan
6061
res = send_request_cgi({
6162
'method' => 'GET',
@@ -66,6 +67,7 @@ def shodan_query(apikey, query, page)
6667
'vars_get' => {
6768
'key' => apikey,
6869
'query' => query,
70+
'facets' => facets,
6971
'page' => page.to_s
7072
}
7173
})
@@ -117,12 +119,13 @@ def run
117119

118120
# create our Shodan request parameters
119121
query = datastore['QUERY']
122+
facets = datastore['FACETS']
120123
apikey = datastore['SHODAN_APIKEY']
121124
maxpage = datastore['MAXPAGE']
122125

123126
# results gets our results from shodan_query
124127
results = []
125-
results[0] = shodan_query(apikey, query, 1)
128+
results[0] = shodan_query(apikey, query, facets, 1)
126129

127130
if results[0]['total'].nil? || results[0]['total'] == 0
128131
msg = "No results."
@@ -141,68 +144,87 @@ def run
141144
end
142145
maxpage = tpages if datastore['MAXPAGE'] > tpages
143146

144-
# start printing out our query statistics
145-
print_status("Total: #{results[0]['total']} on #{tpages} " +
146-
"pages. Showing: #{maxpage} page(s)")
147-
148-
# If search results greater than 100, loop & get all results
149-
print_status('Collecting data, please wait...')
150-
151-
if results[0]['total'] > 100
152-
page = 1
153-
while page < maxpage
154-
page_result = shodan_query(apikey, query, page+1)
155-
if page_result['matches'].nil?
156-
next
147+
if facets
148+
facets_tbl = Rex::Text::Table.new(
149+
'Header' => 'Facets',
150+
'Indent' => 1,
151+
'Columns' => ['Facet', 'Name', 'Count']
152+
)
153+
print_status("Total: #{results[0]['total']} on #{tpages} " \
154+
'pages. Showing facets')
155+
facet = results[0]['facets']
156+
facet.each do |name, list|
157+
list.each do |f|
158+
facets_tbl << [name.to_s, (f['value']).to_s, (f['count']).to_s]
157159
end
158-
results[page] = page_result
159-
page += 1
160160
end
161-
end
162-
163-
# Save the results to this table
164-
tbl = Rex::Text::Table.new(
165-
'Header' => 'Search Results',
166-
'Indent' => 1,
167-
'Columns' => ['IP:Port', 'City', 'Country', 'Hostname']
168-
)
161+
else
162+
# start printing out our query statistics
163+
print_status("Total: #{results[0]['total']} on #{tpages} " +
164+
"pages. Showing: #{maxpage} page(s)")
165+
166+
# If search results greater than 100, loop & get all results
167+
print_status('Collecting data, please wait...')
168+
169+
if results[0]['total'] > 100
170+
page = 1
171+
while page < maxpage
172+
page_result = shodan_query(apikey, query, facets, page+1)
173+
if page_result['matches'].nil?
174+
next
175+
end
176+
results[page] = page_result
177+
page += 1
178+
end
179+
end
180+
# Save the results to this table
181+
tbl = Rex::Text::Table.new(
182+
'Header' => 'Search Results',
183+
'Indent' => 1,
184+
'Columns' => ['IP:Port', 'City', 'Country', 'Hostname']
185+
)
169186

170-
# Organize results and put them into the table and database
171-
regex = datastore['REGEX'] if datastore['REGEX']
172-
results.each do |page|
173-
page['matches'].each do |host|
174-
city = host['location']['city'] || 'N/A'
175-
ip = host['ip_str'] || 'N/A'
176-
port = host['port'] || ''
177-
country = host['location']['country_name'] || 'N/A'
178-
hostname = host['hostnames'][0]
179-
data = host['data']
180-
181-
report_host(:host => ip,
182-
:name => hostname,
183-
:comments => 'Added from Shodan',
184-
:info => host['info']
185-
) if datastore['DATABASE']
186-
187-
report_service(:host => ip,
188-
:port => port,
189-
:info => 'Added from Shodan'
190-
) if datastore['DATABASE']
191-
192-
if ip =~ regex ||
193-
city =~ regex ||
194-
country =~ regex ||
195-
hostname =~ regex ||
196-
data =~ regex
197-
# Unfortunately we cannot display the banner properly,
198-
# because it messes with our output format
199-
tbl << ["#{ip}:#{port}", city, country, hostname]
187+
# Organize results and put them into the table and database
188+
regex = datastore['REGEX'] if datastore['REGEX']
189+
results.each do |page|
190+
page['matches'].each do |host|
191+
city = host['location']['city'] || 'N/A'
192+
ip = host['ip_str'] || 'N/A'
193+
port = host['port'] || ''
194+
country = host['location']['country_name'] || 'N/A'
195+
hostname = host['hostnames'][0]
196+
data = host['data']
197+
198+
report_host(:host => ip,
199+
:name => hostname,
200+
:comments => 'Added from Shodan',
201+
:info => host['info']
202+
) if datastore['DATABASE']
203+
204+
report_service(:host => ip,
205+
:port => port,
206+
:info => 'Added from Shodan'
207+
) if datastore['DATABASE']
208+
209+
if ip =~ regex ||
210+
city =~ regex ||
211+
country =~ regex ||
212+
hostname =~ regex ||
213+
data =~ regex
214+
# Unfortunately we cannot display the banner properly,
215+
# because it messes with our output format
216+
tbl << ["#{ip}:#{port}", city, country, hostname]
217+
end
200218
end
201219
end
220+
#Show data and maybe save it if needed
221+
print_line()
222+
print_line("#{tbl}")
223+
save_output(tbl) if datastore['OUTFILE']
224+
end
225+
if datastore['FACETS']
226+
print_line(facets_tbl.to_s)
227+
save_output(facets_tbl) if datastore['OUTFILE']
202228
end
203-
#Show data and maybe save it if needed
204-
print_line()
205-
print_line("#{tbl}")
206-
save_output(tbl) if datastore['OUTFILE']
207229
end
208230
end

0 commit comments

Comments
 (0)