apullo 0.1.5 → 0.2.0

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
2
  SHA256:
3
- metadata.gz: 96eb9917a6d5fcef270fe520a2312f7447896b2d1e6f0bfc61f966d554e8f9c6
4
- data.tar.gz: ca4807677d7519969aae72fcc647cad01f3799e04cea1d3aee3433efbb4e77bc
3
+ metadata.gz: 1e817b368f4df9a10f01670a5319264d9fd1378f16e1a22590e587c8f75aba52
4
+ data.tar.gz: 52aa9310b175ee65cd82b1c9d800382edbf58081031bb2b16f88e63c9eba237b
5
5
  SHA512:
6
- metadata.gz: 8d32ee78a83177d0dbca9a7d418f5e5e8af4894f516d05ed705a1ab8b082601663353f69e30571663015ddf0fbe9e91b19e6ac0b83431369a7866f51b147b6f7
7
- data.tar.gz: 5e846ede99ffd718f5e75944eb1ef79b97be719f6cbbf62a43ebf05891e405142a1781d6676ec7e8fabdfe5d0530b7fddeedf7ef0573ca07165110828644a098
6
+ metadata.gz: a005b2e6d73cbac89b060e08e38147c27cc5d633eb6526e804f652ea813d9395236445c8674e8c41eec32a643f4c9cf1d550ffa79a4cc10244068e41cf3661f6
7
+ data.tar.gz: 9d8cb76e97fe4a7adc43618abbce2419e8c2b0549abe894611159e54486110a47c88be51bfd13eb86fb4bad52866ee99d7e048bececefb14c3cd92a2b9cf654f
data/README.md CHANGED
@@ -50,34 +50,43 @@ $ apullo check https://example.com
50
50
  },
51
51
  "cert": {
52
52
  "md5": "3510c21c66bd62010fc547d3cd3f0ce6",
53
- "serial": "21020869104500376438182461249190639870",
53
+ "serial": 21020869104500376438182461249190639870,
54
54
  "sha1": "7bb698386970363d2919cc5772846984ffd4a889",
55
55
  "sha256": "9250711c54de546f4370e0c3d3a3ec45bc96092a25a4a71a1afa396af7047eb8"
56
56
  },
57
57
  "favicon": {
58
58
  },
59
59
  "headers": {
60
- "accept-ranges": "bytes",
61
60
  "cache-control": "max-age=604800",
62
61
  "content-type": "text/html; charset=UTF-8",
63
- "date": "Thu, 07 Nov 2019 23:21:43 GMT",
62
+ "date": "Sat, 11 Jan 2020 10:47:09 GMT",
64
63
  "etag": "\"3147526947+gzip\"",
65
- "expires": "Thu, 14 Nov 2019 23:21:43 GMT",
64
+ "expires": "Sat, 18 Jan 2020 10:47:09 GMT",
66
65
  "last-modified": "Thu, 17 Oct 2019 07:18:26 GMT",
67
- "server": "ECS (sec/9739)",
66
+ "server": "ECS (oxr/830F)",
68
67
  "vary": "Accept-Encoding",
69
68
  "x-cache": "HIT",
70
69
  "content-length": "648"
71
70
  },
72
71
  "meta": {
73
- "url": "https://example.com"
72
+ "url": "https://example.com",
73
+ "links": {
74
+ "shodan": {
75
+ "body": "https://www.shodan.io/search?query=http.html_hash%3A-2087618365",
76
+ "cert": "https://www.shodan.io/search?q=ssl.cert.serial%3A21020869104500376438182461249190639870"
77
+ },
78
+ "censys": {
79
+ "body": "https://censys.io/ipv4?q=ea8fac7c65fb589b0d53560f5251f74f9e9b243478dcb6b3ea79b5e36449c8d9",
80
+ "cert": "https://censys.io/ipv4?q=9250711c54de546f4370e0c3d3a3ec45bc96092a25a4a71a1afa396af7047eb8"
81
+ }
82
+ }
74
83
  }
75
84
  },
76
85
  "domain": {
77
86
  "dns": {
78
87
  "ns": [
79
- "b.iana-servers.net",
80
- "a.iana-servers.net"
88
+ "a.iana-servers.net",
89
+ "b.iana-servers.net"
81
90
  ],
82
91
  "cname": [
83
92
 
@@ -122,6 +131,11 @@ $ apullo check https://example.com
122
131
  "technical_contacts": [
123
132
 
124
133
  ]
134
+ },
135
+ "meta": {
136
+ "links": {
137
+ "securitytrails": "https://securitytrails.com/domain/example.com/dns"
138
+ }
125
139
  }
126
140
  },
127
141
  "ssh": {
@@ -130,6 +144,115 @@ $ apullo check https://example.com
130
144
  "target": "https://example.com"
131
145
  }
132
146
  }
147
+
148
+ $ apullo check jppost-ku.com
149
+ {
150
+ "http": {
151
+ "body": {
152
+ "md5": "0728450344e6ea95107ce8c3b00f10ae",
153
+ "mmh3": 421543491,
154
+ "sha1": "6fa29d366b33d5f3c54d62c95b23aa1cce2587a3",
155
+ "sha256": "7bc86f6a3d8877bd84d9917c3661658867af3fdb44842b973be2d299fe793dc2"
156
+ },
157
+ "cert": {
158
+ },
159
+ "favicon": {
160
+ "md5": "ad184c25a1a01d97696dcb59a1ffef74",
161
+ "mmh3": 111036816,
162
+ "sha1": "cb4842a54c3e96408765290cb810793302c17f0b",
163
+ "sha256": "6949c58f841fa21a89e2e2375ae5645e1db62385f89a0218766f2b0a9c490fb8",
164
+ "meta": {
165
+ "url": "https://www.post.japanpost.jp/img/common/touch-icon.png"
166
+ }
167
+ },
168
+ "headers": {
169
+ "server": "Apache-Coyote/1.1",
170
+ "accept-ranges": "bytes",
171
+ "etag": "W/\"54423-1577193448000\"",
172
+ "last-modified": "Tue, 24 Dec 2019 13:17:28 GMT",
173
+ "content-type": "text/html",
174
+ "content-length": "54423",
175
+ "date": "Sat, 11 Jan 2020 10:48:28 GMT"
176
+ },
177
+ "meta": {
178
+ "url": "http://jppost-ku.com",
179
+ "links": {
180
+ "shodan": {
181
+ "body": "https://www.shodan.io/search?query=http.html_hash%3A421543491",
182
+ "favicon": "https://www.shodan.io/search?query=http.favicon.hash%3A111036816"
183
+ },
184
+ "censys": {
185
+ "body": "https://censys.io/ipv4?q=7bc86f6a3d8877bd84d9917c3661658867af3fdb44842b973be2d299fe793dc2"
186
+ }
187
+ }
188
+ }
189
+ },
190
+ "domain": {
191
+ "dns": {
192
+ "ns": [
193
+ "ns2.bdydns.cn",
194
+ "ns1.bdydns.cn"
195
+ ],
196
+ "cname": [
197
+
198
+ ],
199
+ "soa": [
200
+ "sa.dudns.com"
201
+ ],
202
+ "mx": [
203
+
204
+ ],
205
+ "a": [
206
+ "45.10.90.113"
207
+ ],
208
+ "aaaa": [
209
+
210
+ ]
211
+ },
212
+ "whois": {
213
+ "registrant_contacts": [
214
+
215
+ ],
216
+ "admin_contacts": [
217
+
218
+ ],
219
+ "technical_contacts": [
220
+
221
+ ]
222
+ },
223
+ "meta": {
224
+ "links": {
225
+ "securitytrails": "https://securitytrails.com/domain/jppost-ku.com/dns"
226
+ }
227
+ }
228
+ },
229
+ "ssh": {
230
+ "rsa": {
231
+ "md5": "565c74c34ca3a4a44625e8cbf732bed5",
232
+ "sha1": "2fb4d2241f7b6dd83c376548a794d5e903ce2b64",
233
+ "sha256": "e97b6fa7a9c3cb00919fbe90d862b08c2b4b1ac8c09701a0bb063e47ae764160"
234
+ },
235
+ "ecdsa-sha2-nistp256": {
236
+ "md5": "59e75650c592742fbe54a56140965af6",
237
+ "sha1": "1cddc49647d0e3cd5fefcc15e41fa036651ba903",
238
+ "sha256": "54a7bcac7ac7c2ffc501396dd1ae68b0c7f7b3a627c813c0020822b7a01e6a69"
239
+ },
240
+ "ed25519": {
241
+ "md5": "5ca62c892f4cb1c3197b245b2e1b9254",
242
+ "sha1": "9bbcfec876f80c831a9ace061dfa7ba7d207c2d2",
243
+ "sha256": "e7c2073b8ae07dea059307eb4d1f435c92d25228e5def49075e8007f5cb44765"
244
+ },
245
+ "meta": {
246
+ "links": {
247
+ "shodan": "https://www.shodan.io/search?query=port%3A22+56%3A5c%3A74%3Ac3%3A4c%3Aa3%3Aa4%3Aa4%3A46%3A25%3Ae8%3Acb%3Af7%3A32%3Abe%3Ad5",
248
+ "censys": "https://censys.io/ipv4?q=54a7bcac7ac7c2ffc501396dd1ae68b0c7f7b3a627c813c0020822b7a01e6a69"
249
+ }
250
+ }
251
+ },
252
+ "meta": {
253
+ "target": "jppost-ku.com"
254
+ }
255
+ }
133
256
  ```
134
257
 
135
258
  ## Notes
@@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
35
35
  spec.add_dependency "ipaddr", "~> 1.2"
36
36
  spec.add_dependency "mem", "~> 0.1"
37
37
  spec.add_dependency "murmurhash3", "~> 0.1"
38
- spec.add_dependency "oga", "~> 3.0"
38
+ spec.add_dependency "oga", "~> 3.2"
39
39
  spec.add_dependency "parallel", "~> 1.19"
40
40
  spec.add_dependency "public_suffix", "~> 4.0"
41
41
  spec.add_dependency "ssh_scan", "~> 0.0"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "resolv"
4
+ require "uri"
4
5
  require "whois-parser"
5
6
 
6
7
  module Apullo
@@ -21,6 +22,9 @@ module Apullo
21
22
  {
22
23
  dns: resources,
23
24
  whois: contacts,
25
+ meta: {
26
+ links: links
27
+ }
24
28
  }
25
29
  end
26
30
 
@@ -89,6 +93,16 @@ module Apullo
89
93
  technical_contacts: technical_contacts
90
94
  }
91
95
  end
96
+
97
+ def securitytrails_link
98
+ target.domain? ? "https://securitytrails.com/domain/#{target.host}/dns" : "https://securitytrails.com/list/ip/#{target.host}"
99
+ end
100
+
101
+ def links
102
+ {
103
+ securitytrails: securitytrails_link
104
+ }
105
+ end
92
106
  end
93
107
  end
94
108
  end
@@ -24,7 +24,7 @@ module Apullo
24
24
  sha1: hash.sha1,
25
25
  sha256: hash.sha256,
26
26
  meta: {
27
- url: uri.to_s
27
+ url: uri.to_s,
28
28
  }
29
29
  }
30
30
  end
@@ -1,12 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "mem"
3
4
  require "net/http"
4
5
  require "oga"
5
6
  require "openssl"
7
+ require "uri"
6
8
 
7
9
  module Apullo
8
10
  module Fingerprint
9
11
  class HTTP < Base
12
+ include Mem
13
+
10
14
  attr_writer :headers
11
15
 
12
16
  def initialize(target)
@@ -26,7 +30,8 @@ module Apullo
26
30
  favicon: favicon,
27
31
  headers: response_headers,
28
32
  meta: {
29
- url: target.url
33
+ url: target.url,
34
+ links: links
30
35
  }
31
36
  }
32
37
  end
@@ -42,6 +47,7 @@ module Apullo
42
47
  sha256: hash.sha256,
43
48
  }
44
49
  end
50
+ memoize :cert
45
51
 
46
52
  def body
47
53
  return {} unless @body
@@ -54,6 +60,7 @@ module Apullo
54
60
  sha256: hash.sha256,
55
61
  }
56
62
  end
63
+ memoize :body
57
64
 
58
65
  def favicon
59
66
  url = favicon_url
@@ -62,6 +69,7 @@ module Apullo
62
69
  favicon = Favicon.new(url)
63
70
  favicon.results
64
71
  end
72
+ memoize :favicon
65
73
 
66
74
  def response_headers
67
75
  @response_headers || {}
@@ -133,6 +141,38 @@ module Apullo
133
141
  def rebuild_target(url)
134
142
  @target = Target.new(url)
135
143
  end
144
+
145
+ def shodan_link
146
+ uri = URI("https://www.shodan.io/search")
147
+ uri.query = URI.encode_www_form(query: "http.html_hash:#{body.dig(:mmh3)}")
148
+ body_link = body.empty? ? nil : uri.to_s
149
+
150
+ uri.query = URI.encode_www_form(q: "ssl.cert.serial:#{cert.dig(:serial)}")
151
+ cert_link = cert.empty? ? nil : uri.to_s
152
+
153
+ uri.query = URI.encode_www_form(query: "http.favicon.hash:#{favicon.dig(:mmh3)}")
154
+ favicon_link = favicon.empty? ? nil : uri.to_s
155
+
156
+ { body: body_link, cert: cert_link, favicon: favicon_link }.compact
157
+ end
158
+
159
+ def censys_link
160
+ uri = URI("https://censys.io/ipv4")
161
+ uri.query = URI.encode_www_form(q: body.dig(:sha256))
162
+ body_link = body.empty? ? nil : uri.to_s
163
+
164
+ uri.query = URI.encode_www_form(q: cert.dig(:sha256))
165
+ cert_link = cert.empty? ? nil : uri.to_s
166
+
167
+ { body: body_link, cert: cert_link }.compact
168
+ end
169
+
170
+ def links
171
+ {
172
+ shodan: shodan_link,
173
+ censys: censys_link
174
+ }
175
+ end
136
176
  end
137
177
  end
138
178
  end
@@ -1,34 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "mem"
3
4
  require "ssh_scan"
5
+ require "uri"
4
6
 
5
7
  module Apullo
6
8
  module Fingerprint
7
9
  class SSH < Base
10
+ include Mem
11
+
8
12
  DEFAULT_OPTIONS = { "timeout" => 3 }.freeze
9
13
  DEFAULT_PORTS = [22, 2222].freeze
10
14
 
11
15
  private
12
16
 
13
17
  def build_results
14
- pluck_fingerprints
18
+ results = fingerprints
19
+ results = results.merge(meta: { links: links }) unless results.empty?
20
+ results
15
21
  end
16
22
 
17
- def pluck_fingerprints
23
+ def fingerprints
18
24
  result = scan
19
25
  keys = result.dig("keys") || {}
20
26
  keys.map do |cipher, data|
21
- raw = data.dig("raw")
22
27
  fingerprints = data.dig("fingerprints") || []
23
28
  normalized_fingerprints = fingerprints.map do |hash, value|
24
29
  [hash, value.delete(":")]
25
30
  end.to_h
26
31
  [
27
32
  cipher,
28
- { raw: raw, fingerprints: normalized_fingerprints }
33
+ normalized_fingerprints
29
34
  ]
30
35
  end.to_h
31
36
  end
37
+ memoize :fingerprints
32
38
 
33
39
  def _scan(target, port: 22)
34
40
  return nil unless target.host
@@ -47,6 +53,32 @@ module Apullo
47
53
  end
48
54
  {}
49
55
  end
56
+
57
+ def shodan_link
58
+ uri = URI("https://www.shodan.io/search")
59
+ fingerprint = fingerprints.dig("rsa", "md5") || fingerprints.dig("ecdsa-sha2-nistp256", "md5")
60
+ return nil unless fingerprint
61
+
62
+ fingerprint = fingerprint.to_s.chars.each_slice(2).map(&:join).join(":")
63
+ uri.query = URI.encode_www_form(query: "port:22 #{fingerprint}")
64
+ uri.to_s
65
+ end
66
+
67
+ def censys_link
68
+ uri = URI("https://censys.io/ipv4")
69
+ fingerprint = fingerprints.dig("ecdsa-sha2-nistp256", "sha256") || fingerprints.dig("rsa", "sha256")
70
+ return nil unless fingerprint
71
+
72
+ uri.query = URI.encode_www_form(q: fingerprint.to_s)
73
+ uri.to_s
74
+ end
75
+
76
+ def links
77
+ {
78
+ shodan: shodan_link,
79
+ censys: censys_link
80
+ }
81
+ end
50
82
  end
51
83
  end
52
84
  end
@@ -39,12 +39,6 @@ module Apullo
39
39
  uri && (ip? | domain?)
40
40
  end
41
41
 
42
- private
43
-
44
- def _url
45
- @_url ||= id.start_with?("http://", "https://") ? id : "http://#{id}"
46
- end
47
-
48
42
  def ip?
49
43
  IPAddr.new host
50
44
  true
@@ -58,6 +52,12 @@ module Apullo
58
52
  PublicSuffix.valid?(host, default_rule: nil)
59
53
  end
60
54
 
55
+ private
56
+
57
+ def _url
58
+ @_url ||= id.start_with?("http://", "https://") ? id : "http://#{id}"
59
+ end
60
+
61
61
  def resolve
62
62
  Resolv.getaddress uri&.host
63
63
  rescue Resolv::ResolvError => _e
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Apullo
4
- VERSION = "0.1.5"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apullo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manabu Niseki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-03 00:00:00.000000000 Z
11
+ date: 2020-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -156,14 +156,14 @@ dependencies:
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: '3.0'
159
+ version: '3.2'
160
160
  type: :runtime
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: '3.0'
166
+ version: '3.2'
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: parallel
169
169
  requirement: !ruby/object:Gem::Requirement