apullo 0.1.3 → 0.1.4

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: b0827cde956b63c66bd88b8c6a37597626f355cd677a701e6590ab5996011c21
4
- data.tar.gz: bb53dfba7e8297f85b3992a54efa8546e2d815247443430134bfbd1b15963acd
3
+ metadata.gz: 3fef54d32890b574081af9e64c6d76dc01287651779bb43eaecc046df9763e43
4
+ data.tar.gz: 07f3676d94fd54345a40a547c0a6b82ffa5d599ff34a21b7cf59270e608f2df1
5
5
  SHA512:
6
- metadata.gz: 53469022402714f62e9561da863f35e0bf5032b29d7ec2754224d04b5dc44e40615d4f3fee980d23c8e60a3a27b84388561f3f00e7b14ab816e79aec63ec7acb
7
- data.tar.gz: c67723532f368589ee043c700fa4fb3cc5a6dd500bdbbe29244f5a3f2c992a64181c08d7531c2ef2315c0b4854fa109052485ab81b64c9de09e845f369945f88
6
+ metadata.gz: 5bd0f54aebb6c6f7515fbc2979c22c3af2a3553535665f5a3ca7af5bd1358f0e5372b33a2db03c181e115d9aac87d7b819f57b49c281a8362c78860d175c2545
7
+ data.tar.gz: d5ac2f8806e242005d2bd6ee1d03de9126eed3b01b93a19583fe387e5842cce1b5a99ff05dcbfc6a3b58ba5b4d5ae38e11a48a68872953b03e47dcf2417d5e45
data/.travis.yml CHANGED
@@ -4,4 +4,4 @@ language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
6
  - 2.6
7
- before_install: gem install bundler -v 2.0.2
7
+ before_install: gem install bundler -v 2.1
data/README.md CHANGED
@@ -31,6 +31,7 @@ Commands:
31
31
  It takes basic network fingerprints of a target.
32
32
 
33
33
  - Hashes of an HTTP response body
34
+ - Headers of an HTTP response
34
35
  - Hashes of an SSL certificate
35
36
  - Hashes of a favicon image
36
37
  - Hashes of an SSH host key
@@ -54,9 +55,46 @@ $ apullo check https://example.com
54
55
  "sha256": "9250711c54de546f4370e0c3d3a3ec45bc96092a25a4a71a1afa396af7047eb8"
55
56
  },
56
57
  "favicon": {
58
+ },
59
+ "headers": {
60
+ "accept-ranges": "bytes",
61
+ "cache-control": "max-age=604800",
62
+ "content-type": "text/html; charset=UTF-8",
63
+ "date": "Thu, 07 Nov 2019 23:21:43 GMT",
64
+ "etag": "\"3147526947+gzip\"",
65
+ "expires": "Thu, 14 Nov 2019 23:21:43 GMT",
66
+ "last-modified": "Thu, 17 Oct 2019 07:18:26 GMT",
67
+ "server": "ECS (sec/9739)",
68
+ "vary": "Accept-Encoding",
69
+ "x-cache": "HIT",
70
+ "content-length": "648"
71
+ },
72
+ "meta": {
73
+ "url": "https://example.com"
57
74
  }
58
75
  },
59
76
  "domain": {
77
+ "dns": {
78
+ "ns": [
79
+ "b.iana-servers.net",
80
+ "a.iana-servers.net"
81
+ ],
82
+ "cname": [
83
+
84
+ ],
85
+ "soa": [
86
+ "noc.dns.icann.org"
87
+ ],
88
+ "mx": [
89
+
90
+ ],
91
+ "a": [
92
+ "93.184.216.34"
93
+ ],
94
+ "aaaa": [
95
+ "2606:2800:220:1:248:1893:25C8:1946"
96
+ ]
97
+ },
60
98
  "whois": {
61
99
  "registrant_contacts": [
62
100
  {
@@ -84,27 +122,6 @@ $ apullo check https://example.com
84
122
  "technical_contacts": [
85
123
 
86
124
  ]
87
- },
88
- "dns": {
89
- "ns": [
90
- "a.iana-servers.net",
91
- "b.iana-servers.net"
92
- ],
93
- "cname": [
94
-
95
- ],
96
- "soa": [
97
- "noc.dns.icann.org"
98
- ],
99
- "mx": [
100
-
101
- ],
102
- "a": [
103
- "93.184.216.34"
104
- ],
105
- "aaaa": [
106
- "2606:2800:220:1:248:1893:25C8:1946"
107
- ]
108
125
  }
109
126
  },
110
127
  "ssh": {
@@ -113,84 +130,12 @@ $ apullo check https://example.com
113
130
  "target": "https://example.com"
114
131
  }
115
132
  }
133
+ ```
116
134
 
117
- $ apullo check jppost-be.top
118
- {
119
- "http": {
120
- "body": {
121
- "md5": "74ad15c4ab3f67eee1d546e22248931f",
122
- "mmh3": -330759974,
123
- "sha1": "c0280893956852b0c07ae4da752ee5d776d248b8",
124
- "sha256": "28fa3b0beaf188d48b32557fa4df8f0aa451bd10f8e8bb26e919009d2d41b8fb"
125
- },
126
- "cert": {
127
- },
128
- "favicon": {
129
- "md5": "ad184c25a1a01d97696dcb59a1ffef74",
130
- "mmh3": 111036816,
131
- "sha1": "cb4842a54c3e96408765290cb810793302c17f0b",
132
- "sha256": "6949c58f841fa21a89e2e2375ae5645e1db62385f89a0218766f2b0a9c490fb8",
133
- "meta": {
134
- "url": "https://www.post.japanpost.jp/img/common/touch-icon.png"
135
- }
136
- }
137
- },
138
- "domain": {
139
- "whois": {
140
- "registrant_contacts": [
141
-
142
- ],
143
- "admin_contacts": [
144
-
145
- ],
146
- "technical_contacts": [
147
-
148
- ]
149
- },
150
- "dns": {
151
- "ns": [
152
- "ns1.bdydns.cn",
153
- "ns2.bdydns.cn"
154
- ],
155
- "cname": [
156
-
157
- ],
158
- "soa": [
159
- "sa.dudns.com"
160
- ],
161
- "mx": [
135
+ ## Notes
162
136
 
163
- ],
164
- "a": [
165
- "193.148.69.12"
166
- ],
167
- "aaaa": [
168
-
169
- ]
170
- }
171
- },
172
- "ssh": {
173
- "rsa": {
174
- "md5": "960bb068dbfb9aa9f9d6899c15844fca",
175
- "sha1": "d36555028decde1f931b47c90e469fc52e8f364a",
176
- "sha256": "cf3c7ea7b9442f71423f2253a9c0e448fd0d619e1abc7e519499cd789fac6e74"
177
- },
178
- "ecdsa-sha2-nistp256": {
179
- "md5": "551222e53a38c10817653a723e6caf0c",
180
- "sha1": "cd6044db29b30d35f32e26e74d66258570cd6527",
181
- "sha256": "0664dbea7580f9430da6d0ba13e7a4bba0f1efd449c895a6adcc147abc958ce6"
182
- },
183
- "ed25519": {
184
- "md5": "6da2245d9a211731c2d229ea7cce829b",
185
- "sha1": "d86afd8fca1a052249ef3a0ee26a24f6cc644485",
186
- "sha256": "7f0d4b642ea2c236eca4018a2dadff3b8a03c37745f9a9f741d9d246a420f358"
187
- }
188
- },
189
- "meta": {
190
- "target": "jppost-be.top"
191
- }
192
- }
193
- ```
137
+ - `mmh3` is a 32 bit signed int value of MurmurHash3.
138
+ - Keys of `http.headers` are downcased.
194
139
 
195
140
  ## License
196
141
 
data/apullo.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = ["lib"]
26
26
 
27
- spec.add_development_dependency "bundler", "~> 2.0"
27
+ spec.add_development_dependency "bundler", "~> 2.1"
28
28
  spec.add_development_dependency "coveralls", "~> 0.8"
29
29
  spec.add_development_dependency "rake", "~> 13.0"
30
30
  spec.add_development_dependency "rspec", "~> 3.9"
@@ -35,10 +35,11 @@ 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", "~> 2.15"
38
+ spec.add_dependency "oga", "~> 3.0"
39
+ spec.add_dependency "parallel", "~> 1.19"
39
40
  spec.add_dependency "public_suffix", "~> 4.0"
40
41
  spec.add_dependency "ssh_scan", "~> 0.0"
41
- spec.add_dependency "thor", "~> 0.20"
42
+ spec.add_dependency "thor", "~> 1.0"
42
43
  spec.add_dependency "whois", "~> 5.0"
43
44
  spec.add_dependency "whois-parser", "~> 1.2"
44
45
  end
data/lib/apullo/cli.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require "parallel"
4
5
  require "thor"
5
6
 
6
7
  module Apullo
@@ -26,7 +27,7 @@ module Apullo
26
27
  }
27
28
  end
28
29
 
29
- Apullo.fingerprints.map do |klass|
30
+ Parallel.map(Apullo.fingerprints) do |klass|
30
31
  fingerprint = klass.new(target)
31
32
  fingerprint.headers = headers if fingerprint.respond_to?(:headers=)
32
33
 
@@ -7,6 +7,7 @@ module Apullo
7
7
 
8
8
  def initialize(target)
9
9
  @target = target
10
+ @results = nil
10
11
  end
11
12
 
12
13
  def name
@@ -14,6 +15,23 @@ module Apullo
14
15
  end
15
16
 
16
17
  def results
18
+ return @results if @results
19
+
20
+ with_error_handling do
21
+ @results ||= build_results
22
+ end
23
+ @results
24
+ end
25
+
26
+ private
27
+
28
+ def with_error_handling
29
+ yield
30
+ rescue StandardError => e
31
+ @results = { error: e.to_s }
32
+ end
33
+
34
+ def build_results
17
35
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
18
36
  end
19
37
 
@@ -15,15 +15,15 @@ module Apullo
15
15
  Resolv::DNS::Resource::IN::AAAA,
16
16
  ].freeze
17
17
 
18
- def results
18
+ private
19
+
20
+ def build_results
19
21
  {
20
22
  dns: resources,
21
23
  whois: contacts,
22
24
  }
23
25
  end
24
26
 
25
- private
26
-
27
27
  def dns
28
28
  @dns ||= Resolv::DNS.new
29
29
  end
@@ -10,23 +10,25 @@ module Apullo
10
10
  attr_writer :headers
11
11
 
12
12
  def initialize(target)
13
- @target = target
13
+ super target
14
+
14
15
  @headers = {}
15
16
  end
16
17
 
17
- def results
18
- @results ||= [].tap do |out|
19
- get(target.uri.path)
20
-
21
- out << {
22
- body: body,
23
- cert: cert,
24
- favicon: favicon,
25
- meta: {
26
- url: target.url
27
- }
18
+ private
19
+
20
+ def build_results
21
+ get(target.uri.path)
22
+
23
+ {
24
+ body: body,
25
+ cert: cert,
26
+ favicon: favicon,
27
+ headers: response_headers,
28
+ meta: {
29
+ url: target.url
28
30
  }
29
- end.first
31
+ }
30
32
  end
31
33
 
32
34
  def cert
@@ -61,7 +63,9 @@ module Apullo
61
63
  favicon.results
62
64
  end
63
65
 
64
- private
66
+ def response_headers
67
+ @response_headers || {}
68
+ end
65
69
 
66
70
  def headers
67
71
  @headers.compact
@@ -71,10 +75,18 @@ module Apullo
71
75
  "#{target.uri.scheme}://#{target.uri.host}:#{target.uri.port}/favicon.ico"
72
76
  end
73
77
 
78
+ def build_doc
79
+ Oga.parse_html(@body)
80
+ rescue ArgumentError, LL::ParserError => _e
81
+ nil
82
+ end
83
+
74
84
  def favicon_url
75
85
  return nil unless @body
76
86
 
77
- doc = Oga.parse_html(@body)
87
+ doc = build_doc
88
+ return nil unless doc
89
+
78
90
  icon = doc.at_css("link[rel='shortcut icon']") || doc.at_css("link[rel='icon']")
79
91
  return default_favicon_url unless icon
80
92
 
@@ -101,6 +113,7 @@ module Apullo
101
113
  else
102
114
  @peer_cert = http.peer_cert
103
115
  @body = response.body
116
+ @response_headers = response.each_header.to_h
104
117
  @path = path
105
118
  end
106
119
  rescue Errno::ECONNREFUSED, Net::HTTPError, OpenSSL::OpenSSLError, Timeout::Error => _e
@@ -8,26 +8,30 @@ module Apullo
8
8
  DEFAULT_OPTIONS = { "timeout" => 3 }.freeze
9
9
  DEFAULT_PORT = 22
10
10
 
11
- def results
12
- @results ||= pluck
13
- end
14
-
15
11
  private
16
12
 
17
- def pluck
13
+ def build_results
14
+ pluck_fingerprints
15
+ end
16
+
17
+ def pluck_fingerprints
18
18
  result = scan
19
19
  keys = result.dig("keys") || []
20
20
  keys.map do |cipher, data|
21
+ raw = data.dig("raw")
21
22
  fingerprints = data.dig("fingerprints") || []
22
- normalized = fingerprints.map do |hash, value|
23
+ normalized_fingerprints = fingerprints.map do |hash, value|
23
24
  [hash, value.delete(":")]
24
25
  end.to_h
25
- [cipher, normalized]
26
+ [
27
+ cipher,
28
+ { raw: raw, fingerprints: normalized_fingerprints }
29
+ ]
26
30
  end.to_h
27
31
  end
28
32
 
29
33
  def scan
30
- return {} unless target.ipv4
34
+ return {} unless target.host
31
35
 
32
36
  engine = SSHScan::ScanEngine.new
33
37
  dest = "#{target.host}:#{DEFAULT_PORT}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Apullo
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.4"
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.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manabu Niseki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-07 00:00:00.000000000 Z
11
+ date: 2019-12-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: '2.1'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.0'
26
+ version: '2.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: coveralls
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -156,14 +156,28 @@ dependencies:
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: '2.15'
159
+ version: '3.0'
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: '2.15'
166
+ version: '3.0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: parallel
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '1.19'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '1.19'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: public_suffix
169
183
  requirement: !ruby/object:Gem::Requirement
@@ -198,14 +212,14 @@ dependencies:
198
212
  requirements:
199
213
  - - "~>"
200
214
  - !ruby/object:Gem::Version
201
- version: '0.20'
215
+ version: '1.0'
202
216
  type: :runtime
203
217
  prerelease: false
204
218
  version_requirements: !ruby/object:Gem::Requirement
205
219
  requirements:
206
220
  - - "~>"
207
221
  - !ruby/object:Gem::Version
208
- version: '0.20'
222
+ version: '1.0'
209
223
  - !ruby/object:Gem::Dependency
210
224
  name: whois
211
225
  requirement: !ruby/object:Gem::Requirement