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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d1266f6e972edc71cf6f3c36c8dca778832d9340
4
- data.tar.gz: 9e4c2976d711e4e43dfcd18878e6518434d5a7be
2
+ SHA256:
3
+ metadata.gz: e19e36559d8749989cb0c93e56bbcd33ce8c28c5cf1da6923e48206c12b897c3
4
+ data.tar.gz: a94b322821b82a56ac996701aa199dfd3ebbeb90165fd57226244ac0148be626
5
5
  SHA512:
6
- metadata.gz: 077e078e08a70d059fed83f8f92cd425096172c160ab0eab71702eb9026a1328bb812a7b7cb7cbf03f1eb9e239a94258b138513bcb275f26351c088b32d8b36e
7
- data.tar.gz: 892e27388b7cb3a1be9de0ec36d8b20c5c0223b1d481432218a61a6beeb86ebfcf9ddd6d464e4fb4e47fcc5994bed1ce9467981031d030ea23ce0874ec408fa9
6
+ metadata.gz: 1de683c6f0e5a8c9752c4a14ad70dee53da5824ded7e36f32155f2b57e381789038f51a6d661225dfb230573413b03b815a7fc441acfb5d445a0f6489ddb18cc
7
+ data.tar.gz: 5e23cc12d117ea88456fe1ea15a1f6c177ef780370ffd8420c859af10007fa544c36b7fb07ef26b3fb914082f108e5795d4be00eef98f362982a29df6f2d1e60
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fofa (0.3.17)
4
+ fofa (0.4.21)
5
5
  colorize
6
6
  concurrent-ruby
7
7
 
@@ -38,4 +38,4 @@ DEPENDENCIES
38
38
  rspec (~> 3.0)
39
39
 
40
40
  BUNDLED WITH
41
- 1.15.1
41
+ 1.16.4
data/bin/fofacli CHANGED
@@ -19,9 +19,7 @@ options = {
19
19
  post: false,
20
20
  query: nil,
21
21
  format: 'csv',
22
- check_app_category: nil,
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, check_app] ') do |val|
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, check_app] mode') do |val|
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( nil, "--check_app_all", "Check all applications, only used in check_app mode." ) do
101
- options[:check_app_all] = true
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 check_app --app \"Coremail\" mail.tsinghua.edu.cn"
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
- puts r.map{|v| v}.join("\t")
144
+ STDOUT.puts r.map{|v| v}.join("\t")
152
145
  else
153
- puts r
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
- $stdout.sync = true
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 :check_app
243
+ when :stats
251
244
  query = options[:query] || ARGV.join(' ')
252
245
  fofa = Fofa::API.new(options[:email], options[:apikey], {debug:options[:verbose]})
253
- if options[:file]
254
- File.open(options[:file]){|f|
255
- f.each_line{|line|
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
- res = fofa.check_app(query, {
275
- category:options[:check_app_category],
276
- application:options[:check_app_application],
277
- all:options[:all],
278
- })
279
-
280
- if res && res.size>0
281
- res.each{|r|
282
- log_record(options, r)
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.3.18"
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
- # Check applications of asset
135
+ # Fetch fids from query
136
136
  #
137
137
  # Example:
138
- # >> Fofa::API.new(email,apikey).checkapp("mail.tsinghua.edu.cn", category:"邮件系统")
138
+ # >> Fofa::API.new(email,apikey).stats("domain=baidu.com", fields:"fid")
139
139
  # => ["Coremail"]
140
140
  #
141
141
  # Arguments:
142
- # host: (String) Category name
143
- # options: (Hash) category: Category name, application: Application name, all: return all applications or break when match first
144
- def check_app(host, options={})
145
- options = {all:false}.merge(options)
146
- url = "#{@api_server}/api/v1/search/checkapp?key=#{@apikey}&email=#{@email}&host=#{host}"
147
- url += "&all=#{options[:all]}" if options[:all]
148
- url += "&application=#{URI.escape(options[:application])}" if options[:application]
149
- url += "&category=#{URI.escape(options[:category])}" if options[:category]
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.3.18
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: 2017-09-09 00:00:00.000000000 Z
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.6.12
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: []