fofa 0.3.12 → 0.3.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile.lock +2 -2
  3. data/bin/fofacli +121 -7
  4. data/lib/fofa.rb +100 -11
  5. data/lib/fofa/version.rb +19 -1
  6. metadata +6 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 87d0a961aecc981843e098178b7c3fc2c70c14f7
4
- data.tar.gz: 10df9f621ee857e19eb9a31ea5c488fe71895135
2
+ SHA256:
3
+ metadata.gz: bfc764bb2b5c3d56f6dfd68ac14a74478a7d19c4e14c72bec35b1d3f547fb7aa
4
+ data.tar.gz: 8c52a5488290a45f3c074fcdccca99bef75685e91d42fe554268eb886cad36f3
5
5
  SHA512:
6
- metadata.gz: 4c2eaf481b20f662ecac532f950c866ca7fc11d966e4a8b067ad67faca035e08d305923c6ad3a5c85025f3da7d3afc247b91461d4cbb2590cc126f26f28034b2
7
- data.tar.gz: f639fd08740c4df9bcece0443a4b802d9d8df01de17c5136abe4c9fdf4b1a5b6bb09e7c6f04c3c4bd9ddd6127df39ff5aa67e98de72d550f7cf4aefc81193028
6
+ metadata.gz: 67ba297c2bba208e41d01700549cb396de980fb3937e932e01699ee90ce0185d2e9f6daac48fd9366ac919adbc883cd3377686ec5b5fff15e5b44c2ff594e381
7
+ data.tar.gz: c998095f14e85ceeb262b4f6b3b02c102d3a75dd0d2c0088c6b0402b7e8b968e0311a950261c91c45719d80fb02d3e5910da3106d1c8470dd1ab04eba3eaf03a
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fofa (0.3.11)
4
+ fofa (0.3.19)
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.14.6
41
+ 1.16.4
@@ -18,7 +18,11 @@ options = {
18
18
  fields: 'host',
19
19
  post: false,
20
20
  query: nil,
21
- format: 'csv'
21
+ format: 'csv',
22
+ check_app_category: nil,
23
+ check_app_application: nil,
24
+ check_app_all: false,
25
+ task_id: nil
22
26
  }
23
27
 
24
28
  ARGV.options do |opts|
@@ -39,12 +43,16 @@ ARGV.options do |opts|
39
43
  options[:page] = val.to_i
40
44
  end
41
45
 
42
- opts.on('-m', '--mode=MODE', String, 'Mode, default to [search], -m should be specified when [import_service, query_ip_list] ') do |val|
46
+ opts.on('-m', '--mode=MODE', String, 'Mode, default to [search], -m should be specified when [import_service, query_ip_list, check_app, ip_tags] ') do |val|
43
47
  options[:mode] = val.to_sym
44
48
  end
45
49
 
46
- opts.on('-f', '--file=FILE', String, 'Used at [import_service] mode') do |val|
50
+ opts.on('-f', '--file=FILE', String, 'Used at [import_service, check_app, ip_tags] mode') do |val|
47
51
  options[:file] = val
52
+ end
53
+
54
+ opts.on('-t', '--task_id=ID', Integer, 'Used at [ip_tags] mode') do |val|
55
+ options[:task_id] = val.to_i
48
56
  end
49
57
 
50
58
  opts.on('-s', '--limit=SIZE', Integer, "Limit size to fetch, default to #{options[:limit]}") do |val|
@@ -67,7 +75,17 @@ ARGV.options do |opts|
67
75
  raise "File not exist of #{val}" unless File.exists? val
68
76
  options[:query] = File.read(val).strip
69
77
  options[:post] = true
70
- end
78
+ end
79
+
80
+ opts.on('-l', "--application=APPLICATION", String, "Application to check, only used in check_app mode." ) do |val|
81
+ options[:check_app_application] = val
82
+ options[:mode] = :check_app
83
+ end
84
+
85
+ opts.on('-g', "--category=CATEGORY", String, "Category to check, only used in check_app mode." ) do |val|
86
+ options[:check_app_category] = val
87
+ options[:mode] = :check_app
88
+ end
71
89
 
72
90
  opts.separator "Common Options:"
73
91
 
@@ -84,13 +102,23 @@ ARGV.options do |opts|
84
102
  options[:verbose] = true
85
103
  end
86
104
 
105
+ opts.on( nil, "--check_app_all", "Check all applications, only used in check_app mode." ) do
106
+ options[:check_app_all] = true
107
+ end
108
+
109
+
110
+
87
111
  opts.separator ""
88
112
  opts.separator "For example:"
89
113
  opts.separator "\t fofacli -e aaa@bbb.com -a xxx domain=\"baidu.com\""
90
114
  opts.separator "\t fofacli -e aaa@bbb.com -a xxx -s 100 domain=\"baidu.com\""
91
115
  opts.separator "\t fofacli -e aaa@bbb.com -a xxx --query_file /tmp/fofaquery"
92
116
  opts.separator "\t fofacli -e aaa@bbb.com -a xxx -d ip,domain,title,port,protocol -m query_ip_list -f ip.txt"
93
-
117
+ 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"' -- {}@
118
+ opts.separator "\t fofacli -m check_app --app \"Coremail\" mail.tsinghua.edu.cn"
119
+ opts.separator "\t fofacli -e aaa@bbb.com -a xxx -m ip_tags -f ./ip.txt"
120
+ opts.separator "\t fofacli -e aaa@bbb.com -a xxx -m ip_tags -t 1"
121
+
94
122
  begin
95
123
  opts.parse!
96
124
  rescue OptionParser::InvalidOption => e
@@ -125,8 +153,13 @@ def log_record(options, r)
125
153
  else
126
154
  puts %Q|{"id":"#{r}"}|
127
155
  end
128
- when 'csv'
129
- puts r.map{|v| v}.join("\t")
156
+ when 'csv'
157
+ if r.kind_of?(Array)
158
+ puts r.map{|v| v}.join("\t")
159
+ else
160
+ puts r
161
+ end
162
+
130
163
  end
131
164
  end
132
165
 
@@ -221,5 +254,86 @@ case options[:mode]
221
254
  } #File
222
255
  pool.shutdown
223
256
  pool.wait_for_termination
257
+ when :check_app
258
+ query = options[:query] || ARGV.join(' ')
259
+ fofa = Fofa::API.new(options[:email], options[:apikey], {debug:options[:verbose]})
260
+ if options[:file]
261
+ File.open(options[:file]){|f|
262
+ f.each_line{|line|
263
+ query = line.strip
264
+ res = fofa.check_app(query, {
265
+ category:options[:check_app_category],
266
+ application:options[:check_app_application],
267
+ all:options[:all],
268
+ })
269
+
270
+ if res && res.size>0
271
+ res.each{|r|
272
+ log_record(options, [query, r])
273
+ }
274
+ else
275
+ log_record(options, [query, nil])
276
+ end
277
+
278
+ }
279
+ }
280
+ else
281
+ res = fofa.check_app(query, {
282
+ category:options[:check_app_category],
283
+ application:options[:check_app_application],
284
+ all:options[:all],
285
+ })
286
+
287
+ if res && res.size>0
288
+ res.each{|r|
289
+ log_record(options, r)
290
+ }
291
+ else
292
+ $stderr.puts "[WARNING] No result!"
293
+ end
294
+
295
+ end
296
+ when :ip_tags
297
+ fofa = Fofa::API.new(options[:email], options[:apikey], {debug:options[:verbose]})
298
+ if options[:task_id]
299
+ task_id = options[:task_id]
300
+ else
301
+ unless options[:file]
302
+ puts "File not specified.".red
303
+ puts ARGV.options
304
+ exit -1
305
+ end
306
+ puts "Import ip and get tags from '#{options[:file]}'"
307
+ result = fofa.ip_tags(options[:file])
308
+ if result['error']
309
+ puts result
310
+ exit -1
311
+ end
312
+ puts result
313
+ task_id = result['task_id']
314
+ end
315
+
316
+ if task_id
317
+ get_info = -> (fofa, id) { fofa.tag_info(id) }
318
+ puts "Checking progress!"
319
+ sleep(2)
320
+
321
+ loop do
322
+ info = get_info.call(fofa, task_id)
323
+ if info['error']
324
+ puts info
325
+ break
326
+ elsif info['progress'].to_i == 100
327
+ puts "state: #{info['state']}; progress: #{info['progress']}."
328
+ puts info['download_url']
329
+ break
330
+ else
331
+ puts "state: #{info['state']}; progress: #{info['progress']}."
332
+ sleep(10)
333
+ end
334
+ end
335
+
336
+ end
337
+
224
338
  end
225
339
 
@@ -1,6 +1,7 @@
1
1
  require "fofa/version"
2
2
  require 'net/http'
3
3
  require 'json'
4
+ require 'base64'
4
5
 
5
6
  module Fofa
6
7
  class API
@@ -29,13 +30,10 @@ module Fofa
29
30
  options = {page:1, size:100, fields:'host'}.merge(options)
30
31
 
31
32
  url = "#{@api_server}/api/v1/search/all?key=#{@apikey}&email=#{@email}&page=#{options[:page]}&size=#{options[:size]}&fields=#{options[:fields]}"
32
- url += "&q=#{URI.escape(query)}" unless options[:post]
33
+ url += "&qbase64=#{Base64.encode64(query)}" unless options[:post]
33
34
  puts url if @options[:debug]
34
35
  uri = URI.parse(url)
35
- http = Net::HTTP.new(uri.host, uri.port)
36
- if uri.scheme == 'https'
37
- http.use_ssl = true
38
- end
36
+ http = http_new(uri)
39
37
 
40
38
  if options[:post]
41
39
  req = Net::HTTP::Post.new(uri.request_uri)
@@ -47,6 +45,7 @@ module Fofa
47
45
  resp = http.request(req)
48
46
  JSON.parse(resp.body)
49
47
  rescue => e
48
+ $stderr.puts "[WARNING]: #{e.to_s}"
50
49
  {"error"=>"Error: #{e.to_s}"}
51
50
  end
52
51
 
@@ -74,7 +73,7 @@ module Fofa
74
73
  res = search(query, {page:page}.merge(options))
75
74
  if !res['error'] && res['results'].size > 0
76
75
  all_res += res['results']
77
- yield(res['results'], page) if block_given?
76
+ yield(res['results'], page, res['size'].to_i) if block_given?
78
77
 
79
78
  if all_size && all_res.size > all_size.to_i
80
79
  all_res = all_res[0..all_size-1]
@@ -104,11 +103,7 @@ module Fofa
104
103
  url = "#{@api_server}/api/v1/import/services?key=#{@apikey}&email=#{@email}&port=#{options[:port]}"
105
104
  puts url if @options[:debug]
106
105
  uri = URI.parse(url)
107
- http = Net::HTTP.new(uri.host, uri.port)
108
- if uri.scheme == 'https'
109
- http.use_ssl = true
110
- end
111
- http.set_debug_output $stderr if @options[:debug]
106
+ http = http_new(uri)
112
107
 
113
108
  File.open(file) do |f|
114
109
  results = []
@@ -137,5 +132,99 @@ module Fofa
137
132
  {"error"=>"Error: #{e.to_s}"}
138
133
  end
139
134
 
135
+ # Check applications of asset
136
+ #
137
+ # Example:
138
+ # >> Fofa::API.new(email,apikey).checkapp("mail.tsinghua.edu.cn", category:"邮件系统")
139
+ # => ["Coremail"]
140
+ #
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]
150
+ puts url if @options[:debug]
151
+ uri = URI.parse(url)
152
+ http = http_new(uri)
153
+ req = Net::HTTP::Get.new(uri.request_uri)
154
+ resp = http.request(req)
155
+ JSON.parse(resp.body)
156
+ rescue => e
157
+ {"error"=>"Error: #{e.to_s}"}
158
+ end
159
+
160
+ # Import ip and get tags
161
+ #
162
+ # Example:
163
+ # >> Fofa::API.new(email,apikey).ip_tags('./ips.txt')
164
+ # => {"task_id"=>1}
165
+ #
166
+ # Arguments:
167
+ # file: (String) ip file
168
+ def ip_tags(file)
169
+ url = "#{@api_server}/api/v1/ip_tags?key=#{@apikey}&email=#{@email}"
170
+ puts url if @options[:debug]
171
+
172
+ # Token used to terminate the file in the post body. Make sure it is not
173
+ # present in the file you're uploading.
174
+ # You might want to use `SecureRandom` class to generate this random strings
175
+ boundary = "AaB03x"
176
+ uri = URI.parse(url)
177
+
178
+ post_body = []
179
+ post_body << "--#{boundary}\r\n"
180
+ post_body << "Content-Disposition: form-data; name=\"file\"; filename=\"#{File.basename(file)}\"\r\n"
181
+ post_body << "Content-Type: text/plain\r\n"
182
+ post_body << "\r\n"
183
+ post_body << File.read(file)
184
+ post_body << "\r\n--#{boundary}--\r\n"
185
+
186
+ http = http_new(uri)
187
+ req = Net::HTTP::Post.new(uri.request_uri)
188
+ req.body = post_body.join
189
+ req["Content-Type"] = "multipart/form-data, boundary=#{boundary}"
190
+
191
+ resp = http.request(req)
192
+ puts resp if @options[:debug]
193
+ JSON.parse(resp.body)
194
+ rescue => e
195
+ {"error"=>"Error: #{e.to_s}"}
196
+ end
197
+
198
+ # Check ip_tags's task state, progress, and download_url
199
+ #
200
+ # Example:
201
+ # >> Fofa::API.new(email,apikey).tag_info(1)
202
+ # => {"state"=>"success", "progress"=>100, "download_url"=>"https://host/xxx/56af409c1c55762a7b9e9a39a801f80d.json"}
203
+ #
204
+ # Arguments:
205
+ # task_id: (Integer) task id
206
+ def tag_info(task_id)
207
+ url = "#{@api_server}/api/v1/ip_tags/info?key=#{@apikey}&email=#{@email}&task_id=#{task_id}"
208
+ puts url if @options[:debug]
209
+ uri = URI.parse(url)
210
+ http = http_new(uri)
211
+ req = Net::HTTP::Get.new(uri.request_uri)
212
+
213
+ resp = http.request(req)
214
+ JSON.parse(resp.body)
215
+ rescue => e
216
+ $stderr.puts "[WARNING]: #{e.to_s}"
217
+ {"error"=>"Error: #{e.to_s}"}
218
+ end
219
+
220
+ private
221
+ def http_new(uri)
222
+ http = Net::HTTP.new(uri.host, uri.port)
223
+ if uri.scheme == 'https'
224
+ http.use_ssl = true
225
+ end
226
+ http.set_debug_output $stderr if @options[:debug]
227
+ http
228
+ end
140
229
  end
141
230
  end
@@ -1,3 +1,21 @@
1
+ # 0.3.19
2
+ # add feature: ip_tags, tag_info
3
+ #
4
+ # 0.3.18
5
+ # add feature: check applications from file
6
+ #
7
+ # 0.3.17
8
+ # add checkapp api call
9
+ #
10
+ # 0.3.15
11
+ # fixed query encode bug of '&&'
12
+ #
13
+ # 0.3.14
14
+ # fixed bug of search_all yield when size reached
15
+ #
16
+ # 0.3.13
17
+ # search_all yield with all size
18
+ #
1
19
  # 0.3.12
2
20
  # fofacli add -o --output-format option, support csv(defualt) and json
3
21
  #
@@ -16,5 +34,5 @@
16
34
  # 0.3.6
17
35
  # add fields
18
36
  module Fofa
19
- VERSION = "0.3.12"
37
+ VERSION = "0.3.19"
20
38
  end
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.12
4
+ version: 0.3.19
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-05-11 00:00:00.000000000 Z
11
+ date: 2020-06-29 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,8 @@ 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.3
126
- signing_key:
124
+ rubygems_version: 3.0.6
125
+ signing_key:
127
126
  specification_version: 4
128
127
  summary: A Ruby library to interact with the FOFA API. https://fofa.so
129
128
  test_files: []