apullo 0.1.3 → 0.1.4

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
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