fofa 0.3.18 → 0.4.22
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.
- checksums.yaml +5 -5
- data/Gemfile.lock +2 -2
- data/bin/fofacli +67 -60
- data/lib/fofa/version.rb +13 -1
- data/lib/fofa.rb +68 -10
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e19e36559d8749989cb0c93e56bbcd33ce8c28c5cf1da6923e48206c12b897c3
|
|
4
|
+
data.tar.gz: a94b322821b82a56ac996701aa199dfd3ebbeb90165fd57226244ac0148be626
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1de683c6f0e5a8c9752c4a14ad70dee53da5824ded7e36f32155f2b57e381789038f51a6d661225dfb230573413b03b815a7fc441acfb5d445a0f6489ddb18cc
|
|
7
|
+
data.tar.gz: 5e23cc12d117ea88456fe1ea15a1f6c177ef780370ffd8420c859af10007fa544c36b7fb07ef26b3fb914082f108e5795d4be00eef98f362982a29df6f2d1e60
|
data/Gemfile.lock
CHANGED
data/bin/fofacli
CHANGED
|
@@ -19,9 +19,7 @@ options = {
|
|
|
19
19
|
post: false,
|
|
20
20
|
query: nil,
|
|
21
21
|
format: 'csv',
|
|
22
|
-
|
|
23
|
-
check_app_application: nil,
|
|
24
|
-
check_app_all: false
|
|
22
|
+
task_id: nil
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
ARGV.options do |opts|
|
|
@@ -42,12 +40,16 @@ ARGV.options do |opts|
|
|
|
42
40
|
options[:page] = val.to_i
|
|
43
41
|
end
|
|
44
42
|
|
|
45
|
-
opts.on('-m', '--mode=MODE', String, 'Mode, default to [search], -m should be specified when [import_service, query_ip_list,
|
|
43
|
+
opts.on('-m', '--mode=MODE', String, 'Mode, default to [search], -m should be specified when [import_service, query_ip_list, stats, ip_tags] ') do |val|
|
|
46
44
|
options[:mode] = val.to_sym
|
|
47
45
|
end
|
|
48
46
|
|
|
49
|
-
opts.on('-f', '--file=FILE', String, 'Used at [import_service,
|
|
47
|
+
opts.on('-f', '--file=FILE', String, 'Used at [import_service, ip_tags] mode') do |val|
|
|
50
48
|
options[:file] = val
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
opts.on('-t', '--task_id=ID', Integer, 'Used at [ip_tags] mode') do |val|
|
|
52
|
+
options[:task_id] = val.to_i
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
opts.on('-s', '--limit=SIZE', Integer, "Limit size to fetch, default to #{options[:limit]}") do |val|
|
|
@@ -72,16 +74,6 @@ ARGV.options do |opts|
|
|
|
72
74
|
options[:post] = true
|
|
73
75
|
end
|
|
74
76
|
|
|
75
|
-
opts.on('-l', "--application=APPLICATION", String, "Application to check, only used in check_app mode." ) do |val|
|
|
76
|
-
options[:check_app_application] = val
|
|
77
|
-
options[:mode] = :check_app
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
opts.on('-g', "--category=CATEGORY", String, "Category to check, only used in check_app mode." ) do |val|
|
|
81
|
-
options[:check_app_category] = val
|
|
82
|
-
options[:mode] = :check_app
|
|
83
|
-
end
|
|
84
|
-
|
|
85
77
|
opts.separator "Common Options:"
|
|
86
78
|
|
|
87
79
|
opts.on( nil, "--post", "POST query mode." ) do
|
|
@@ -97,12 +89,11 @@ ARGV.options do |opts|
|
|
|
97
89
|
options[:verbose] = true
|
|
98
90
|
end
|
|
99
91
|
|
|
100
|
-
opts.on(
|
|
101
|
-
|
|
92
|
+
opts.on( "-V", "--version", "Display version" ) do
|
|
93
|
+
puts "Version: #{Fofa::VERSION}"
|
|
94
|
+
exit(0)
|
|
102
95
|
end
|
|
103
96
|
|
|
104
|
-
|
|
105
|
-
|
|
106
97
|
opts.separator ""
|
|
107
98
|
opts.separator "For example:"
|
|
108
99
|
opts.separator "\t fofacli -e aaa@bbb.com -a xxx domain=\"baidu.com\""
|
|
@@ -110,7 +101,9 @@ ARGV.options do |opts|
|
|
|
110
101
|
opts.separator "\t fofacli -e aaa@bbb.com -a xxx --query_file /tmp/fofaquery"
|
|
111
102
|
opts.separator "\t fofacli -e aaa@bbb.com -a xxx -d ip,domain,title,port,protocol -m query_ip_list -f ip.txt"
|
|
112
103
|
opts.separator %Q@\t cat vapps.txt | xargs -I{} -n 1 sh -c 'fofacli "app=$1" -d host,ip,port,country,province,city -s 1000 -o json -v > "$1.txt"' -- {}@
|
|
113
|
-
opts.separator "\t fofacli -m
|
|
104
|
+
opts.separator "\t fofacli -m stats --fields domain app=\"drupal\" | jq"
|
|
105
|
+
opts.separator "\t fofacli -e aaa@bbb.com -a xxx -m ip_tags -f ./ip.txt"
|
|
106
|
+
opts.separator "\t fofacli -e aaa@bbb.com -a xxx -m ip_tags -t 1"
|
|
114
107
|
|
|
115
108
|
begin
|
|
116
109
|
opts.parse!
|
|
@@ -140,17 +133,17 @@ def log_record(options, r)
|
|
|
140
133
|
case options[:format]
|
|
141
134
|
when 'json'
|
|
142
135
|
if r.kind_of?(Array)
|
|
143
|
-
puts Hash[options[:fields].split(',').zip r].to_json
|
|
136
|
+
STDOUT.puts Hash[options[:fields].split(',').zip r].to_json
|
|
144
137
|
elsif r.kind_of?(Hash)
|
|
145
|
-
puts r.to_json
|
|
138
|
+
STDOUT.puts r.to_json
|
|
146
139
|
else
|
|
147
|
-
puts %Q|{"id":"#{r}"}|
|
|
140
|
+
STDOUT.puts %Q|{"id":"#{r}"}|
|
|
148
141
|
end
|
|
149
142
|
when 'csv'
|
|
150
143
|
if r.kind_of?(Array)
|
|
151
|
-
|
|
144
|
+
STDOUT.puts r.map{|v| v}.join("\t")
|
|
152
145
|
else
|
|
153
|
-
|
|
146
|
+
STDOUT.puts r
|
|
154
147
|
end
|
|
155
148
|
|
|
156
149
|
end
|
|
@@ -164,7 +157,7 @@ case options[:mode]
|
|
|
164
157
|
exit -1
|
|
165
158
|
end
|
|
166
159
|
query = options[:query] || ARGV.join(' ')
|
|
167
|
-
puts "Query: '#{query}'"
|
|
160
|
+
STDERR.puts "Query: '#{query}'"
|
|
168
161
|
if options[:page] #search one page
|
|
169
162
|
Fofa::API.new(options[:email], options[:apikey], {debug:options[:verbose]})
|
|
170
163
|
.search(query, {
|
|
@@ -206,7 +199,7 @@ case options[:mode]
|
|
|
206
199
|
require 'concurrent'
|
|
207
200
|
semaphore = Concurrent::Semaphore.new(1)
|
|
208
201
|
pool = Concurrent::FixedThreadPool.new(10)
|
|
209
|
-
$
|
|
202
|
+
$STDOUT.sync = true
|
|
210
203
|
File.open(options[:file]){|f|
|
|
211
204
|
fields = options[:fields].split(',')
|
|
212
205
|
f.each_line{|line|
|
|
@@ -247,46 +240,60 @@ case options[:mode]
|
|
|
247
240
|
} #File
|
|
248
241
|
pool.shutdown
|
|
249
242
|
pool.wait_for_termination
|
|
250
|
-
when :
|
|
243
|
+
when :stats
|
|
251
244
|
query = options[:query] || ARGV.join(' ')
|
|
252
245
|
fofa = Fofa::API.new(options[:email], options[:apikey], {debug:options[:verbose]})
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
query = line.strip
|
|
257
|
-
res = fofa.check_app(query, {
|
|
258
|
-
category:options[:check_app_category],
|
|
259
|
-
application:options[:check_app_application],
|
|
260
|
-
all:options[:all],
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
if res && res.size>0
|
|
264
|
-
res.each{|r|
|
|
265
|
-
log_record(options, [query, r])
|
|
266
|
-
}
|
|
267
|
-
else
|
|
268
|
-
log_record(options, [query, nil])
|
|
269
|
-
end
|
|
246
|
+
res = fofa.stats(query, {
|
|
247
|
+
fields: options[:fields],
|
|
248
|
+
})
|
|
270
249
|
|
|
271
|
-
|
|
272
|
-
|
|
250
|
+
if res && res.size>0
|
|
251
|
+
$stdout.puts Hash[res].to_json
|
|
252
|
+
else
|
|
253
|
+
$stderr.puts "[WARNING] No result!"
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
when :ip_tags
|
|
257
|
+
fofa = Fofa::API.new(options[:email], options[:apikey], {debug:options[:verbose]})
|
|
258
|
+
if options[:task_id]
|
|
259
|
+
task_id = options[:task_id]
|
|
273
260
|
else
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
else
|
|
285
|
-
$stderr.puts "[WARNING] No result!"
|
|
261
|
+
unless options[:file]
|
|
262
|
+
puts "File not specified.".red
|
|
263
|
+
puts ARGV.options
|
|
264
|
+
exit -1
|
|
265
|
+
end
|
|
266
|
+
puts "Import ip and get tags from '#{options[:file]}'"
|
|
267
|
+
result = fofa.ip_tags(options[:file])
|
|
268
|
+
if result['error']
|
|
269
|
+
puts result
|
|
270
|
+
exit -1
|
|
286
271
|
end
|
|
287
|
-
|
|
272
|
+
puts result
|
|
273
|
+
task_id = result['task_id']
|
|
288
274
|
end
|
|
289
275
|
|
|
276
|
+
if task_id
|
|
277
|
+
get_info = -> (fofa, id) { fofa.tag_info(id) }
|
|
278
|
+
puts "Checking progress!"
|
|
279
|
+
sleep(2)
|
|
280
|
+
|
|
281
|
+
loop do
|
|
282
|
+
info = get_info.call(fofa, task_id)
|
|
283
|
+
if info['error']
|
|
284
|
+
puts info
|
|
285
|
+
break
|
|
286
|
+
elsif info['progress'].to_i == 100
|
|
287
|
+
puts "state: #{info['state']}; progress: #{info['progress']}."
|
|
288
|
+
puts info['download_url']
|
|
289
|
+
break
|
|
290
|
+
else
|
|
291
|
+
puts "state: #{info['state']}; progress: #{info['progress']}."
|
|
292
|
+
sleep(10)
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
end
|
|
290
297
|
|
|
291
298
|
end
|
|
292
299
|
|
data/lib/fofa/version.rb
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# 0.4.22
|
|
2
|
+
# add state api, remove chech_app api
|
|
3
|
+
#
|
|
4
|
+
# 0.4.21
|
|
5
|
+
# split result to stdout
|
|
6
|
+
#
|
|
7
|
+
# 0.4.20
|
|
8
|
+
# add -V option, and ip_tags method need to upgrade major version
|
|
9
|
+
#
|
|
10
|
+
# 0.3.19
|
|
11
|
+
# add feature: ip_tags, tag_info
|
|
12
|
+
#
|
|
1
13
|
# 0.3.18
|
|
2
14
|
# add feature: check applications from file
|
|
3
15
|
#
|
|
@@ -31,5 +43,5 @@
|
|
|
31
43
|
# 0.3.6
|
|
32
44
|
# add fields
|
|
33
45
|
module Fofa
|
|
34
|
-
VERSION = "0.
|
|
46
|
+
VERSION = "0.4.22"
|
|
35
47
|
end
|
data/lib/fofa.rb
CHANGED
|
@@ -132,28 +132,86 @@ module Fofa
|
|
|
132
132
|
{"error"=>"Error: #{e.to_s}"}
|
|
133
133
|
end
|
|
134
134
|
|
|
135
|
-
#
|
|
135
|
+
# Fetch fids from query
|
|
136
136
|
#
|
|
137
137
|
# Example:
|
|
138
|
-
# >> Fofa::API.new(email,apikey).
|
|
138
|
+
# >> Fofa::API.new(email,apikey).stats("domain=baidu.com", fields:"fid")
|
|
139
139
|
# => ["Coremail"]
|
|
140
140
|
#
|
|
141
141
|
# Arguments:
|
|
142
|
-
#
|
|
143
|
-
# options: (Hash)
|
|
144
|
-
def
|
|
145
|
-
options = {
|
|
146
|
-
url = "#{@api_server}/api/v1/search/
|
|
147
|
-
url += "&
|
|
148
|
-
url
|
|
149
|
-
|
|
142
|
+
# query: (String) fofa query string
|
|
143
|
+
# options: (Hash) fields: can be return multiple fields, like fid,host and so on
|
|
144
|
+
def stats(query, options={})
|
|
145
|
+
options = {fields:"fid"}.merge(options)
|
|
146
|
+
url = "#{@api_server}/api/v1/search/stats?key=#{@apikey}&email=#{@email}&qbase64=#{Base64.encode64(query)}"
|
|
147
|
+
url += "&fields=#{options[:fields]}" if options[:fields]
|
|
148
|
+
puts url if @options[:debug]
|
|
149
|
+
uri = URI.parse(url)
|
|
150
|
+
http = http_new(uri)
|
|
151
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
|
152
|
+
resp = http.request(req)
|
|
153
|
+
JSON.parse(resp.body)
|
|
154
|
+
rescue => e
|
|
155
|
+
{"error"=>"Error: #{e.to_s}"}
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Import ip and get tags
|
|
159
|
+
#
|
|
160
|
+
# Example:
|
|
161
|
+
# >> Fofa::API.new(email,apikey).ip_tags('./ips.txt')
|
|
162
|
+
# => {"task_id"=>1}
|
|
163
|
+
#
|
|
164
|
+
# Arguments:
|
|
165
|
+
# file: (String) ip file
|
|
166
|
+
def ip_tags(file)
|
|
167
|
+
url = "#{@api_server}/api/v1/ip_tags?key=#{@apikey}&email=#{@email}"
|
|
168
|
+
puts url if @options[:debug]
|
|
169
|
+
|
|
170
|
+
# Token used to terminate the file in the post body. Make sure it is not
|
|
171
|
+
# present in the file you're uploading.
|
|
172
|
+
# You might want to use `SecureRandom` class to generate this random strings
|
|
173
|
+
boundary = "AaB03x"
|
|
174
|
+
uri = URI.parse(url)
|
|
175
|
+
|
|
176
|
+
post_body = []
|
|
177
|
+
post_body << "--#{boundary}\r\n"
|
|
178
|
+
post_body << "Content-Disposition: form-data; name=\"file\"; filename=\"#{File.basename(file)}\"\r\n"
|
|
179
|
+
post_body << "Content-Type: text/plain\r\n"
|
|
180
|
+
post_body << "\r\n"
|
|
181
|
+
post_body << File.read(file)
|
|
182
|
+
post_body << "\r\n--#{boundary}--\r\n"
|
|
183
|
+
|
|
184
|
+
http = http_new(uri)
|
|
185
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
|
186
|
+
req.body = post_body.join
|
|
187
|
+
req["Content-Type"] = "multipart/form-data, boundary=#{boundary}"
|
|
188
|
+
|
|
189
|
+
resp = http.request(req)
|
|
190
|
+
puts resp if @options[:debug]
|
|
191
|
+
JSON.parse(resp.body)
|
|
192
|
+
rescue => e
|
|
193
|
+
{"error"=>"Error: #{e.to_s}"}
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Check ip_tags's task state, progress, and download_url
|
|
197
|
+
#
|
|
198
|
+
# Example:
|
|
199
|
+
# >> Fofa::API.new(email,apikey).tag_info(1)
|
|
200
|
+
# => {"state"=>"success", "progress"=>100, "download_url"=>"https://host/xxx/56af409c1c55762a7b9e9a39a801f80d.json"}
|
|
201
|
+
#
|
|
202
|
+
# Arguments:
|
|
203
|
+
# task_id: (Integer) task id
|
|
204
|
+
def tag_info(task_id)
|
|
205
|
+
url = "#{@api_server}/api/v1/ip_tags/info?key=#{@apikey}&email=#{@email}&task_id=#{task_id}"
|
|
150
206
|
puts url if @options[:debug]
|
|
151
207
|
uri = URI.parse(url)
|
|
152
208
|
http = http_new(uri)
|
|
153
209
|
req = Net::HTTP::Get.new(uri.request_uri)
|
|
210
|
+
|
|
154
211
|
resp = http.request(req)
|
|
155
212
|
JSON.parse(resp.body)
|
|
156
213
|
rescue => e
|
|
214
|
+
$stderr.puts "[WARNING]: #{e.to_s}"
|
|
157
215
|
{"error"=>"Error: #{e.to_s}"}
|
|
158
216
|
end
|
|
159
217
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fofa
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.22
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- fofa
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-02-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -106,7 +106,7 @@ homepage: https://fofa.so
|
|
|
106
106
|
licenses:
|
|
107
107
|
- MIT
|
|
108
108
|
metadata: {}
|
|
109
|
-
post_install_message:
|
|
109
|
+
post_install_message:
|
|
110
110
|
rdoc_options: []
|
|
111
111
|
require_paths:
|
|
112
112
|
- lib
|
|
@@ -121,9 +121,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
121
121
|
- !ruby/object:Gem::Version
|
|
122
122
|
version: '0'
|
|
123
123
|
requirements: []
|
|
124
|
-
rubyforge_project:
|
|
125
|
-
rubygems_version: 2.
|
|
126
|
-
signing_key:
|
|
124
|
+
rubyforge_project:
|
|
125
|
+
rubygems_version: 2.7.7
|
|
126
|
+
signing_key:
|
|
127
127
|
specification_version: 4
|
|
128
128
|
summary: A Ruby library to interact with the FOFA API. https://fofa.so
|
|
129
129
|
test_files: []
|