fofa 0.3.18 → 0.4.22
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|