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 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: []