pwn 0.5.425 → 0.5.426

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: 30904f7cfcbec2b9dc3ae06c5cb6ef6cf1c59e711bccbc7a7302b26e21639eef
4
- data.tar.gz: 7e4c812591317c30242e558bf924d2eacd96563dbfea75ebf07161435c7c1720
3
+ metadata.gz: d6bbdb81c1c0df4969edc99d219a625265fa48d5af722628a2a77d3ead2741cb
4
+ data.tar.gz: 68e09e473021b7b574074f737c066e06cc0276171eb74f99f9c8495f34a8825d
5
5
  SHA512:
6
- metadata.gz: 2fffe14cfa05fb8960ab0551de18171f02643b4162d3ee9e06ab60adb081345f71b6c8c9409555f010762d356f419445a7358e3ccf3e501b53131cac7579b94f
7
- data.tar.gz: 6795f441e69e5ec1b7bd43831bf273aec2596565ecd8734618a293c82f0fd360a16a1378399c72d1468d89bcd42c276f4b5d217a960d5d46525cfa15a80859d9
6
+ metadata.gz: 3e279405371694e5d6c602a869662fab95ebdd18f7b207734a6023a4c0d2d98a3ddf903f03f150d966c14fbb02a7e9dda36ad8eef77850eacebf870179c5ff56
7
+ data.tar.gz: e832fd158b5427c267e6240d8c7473d766ce1a9fdc53c5ff3353e08933cc4f3c2e2cc45a93f6b270092d6b38e1ccacc2d4a437fdc7983c201c71cb68dcc978d5
data/Gemfile CHANGED
@@ -90,7 +90,7 @@ gem 'ruby-nmap', '1.0.3'
90
90
  gem 'ruby-saml', '1.18.1'
91
91
  gem 'rvm', '1.11.3.9'
92
92
  gem 'savon', '2.15.1'
93
- gem 'selenium-devtools', '0.139.0'
93
+ gem 'selenium-devtools', '0.140.0'
94
94
  # gem 'serialport', '1.3.2'
95
95
  # gem 'sinatra', '4.0.0'
96
96
  gem 'slack-ruby-client', '3.0.0'
data/README.md CHANGED
@@ -37,7 +37,7 @@ $ cd /opt/pwn
37
37
  $ ./install.sh
38
38
  $ ./install.sh ruby-gem
39
39
  $ pwn
40
- pwn[v0.5.425]:001 >>> PWN.help
40
+ pwn[v0.5.426]:001 >>> PWN.help
41
41
  ```
42
42
 
43
43
  [![Installing the pwn Security Automation Framework](https://raw.githubusercontent.com/0dayInc/pwn/master/documentation/pwn_install.png)](https://youtu.be/G7iLUY4FzsI)
@@ -52,7 +52,7 @@ $ rvm use ruby-3.4.4@pwn
52
52
  $ gem uninstall --all --executables pwn
53
53
  $ gem install --verbose pwn
54
54
  $ pwn
55
- pwn[v0.5.425]:001 >>> PWN.help
55
+ pwn[v0.5.426]:001 >>> PWN.help
56
56
  ```
57
57
 
58
58
  If you're using a multi-user install of RVM do:
@@ -62,7 +62,7 @@ $ rvm use ruby-3.4.4@pwn
62
62
  $ rvmsudo gem uninstall --all --executables pwn
63
63
  $ rvmsudo gem install --verbose pwn
64
64
  $ pwn
65
- pwn[v0.5.425]:001 >>> PWN.help
65
+ pwn[v0.5.426]:001 >>> PWN.help
66
66
  ```
67
67
 
68
68
  PWN periodically upgrades to the latest version of Ruby which is reflected in `/opt/pwn/.ruby-version`. The easiest way to upgrade to the latest version of Ruby from a previous PWN installation is to run the following script:
data/bin/pwn CHANGED
@@ -14,16 +14,8 @@ OptionParser.new do |options|
14
14
  opts[:yaml_config_path] = p
15
15
  end
16
16
 
17
- options.on('-dPATH', '--decryptor=PATH', '<Optional - File Containing Decryption Key && IV>') do |d|
18
- opts[:decryption_file] = d
19
- end
20
-
21
- options.on('-kKEY', '--decryption-key=KEY', '<Optional - Decryption Key>') do |k|
22
- opts[:key] = k
23
- end
24
-
25
- options.on('-iIV', '--decryption-iv=PATH', '<Optional - Decryption IV>') do |i|
26
- opts[:iv] = i
17
+ options.on('-dPATH', '--yaml-decryptor=PATH', '<Optional - Out-of-Band YAML File with :key && :iv>') do |d|
18
+ opts[:yaml_decryptor_path] = d
27
19
  end
28
20
  end.parse!
29
21
 
data/lib/pwn/ai.rb CHANGED
@@ -9,7 +9,7 @@ module PWN
9
9
  autoload :Ollama, 'pwn/ai/ollama'
10
10
  autoload :OpenAI, 'pwn/ai/open_ai'
11
11
 
12
- # Display a List of Every PWN::Plugins Module
12
+ # Display a List of Every PWN::AI Module
13
13
 
14
14
  public_class_method def self.help
15
15
  constants.sort
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'rest-client'
5
+ require 'tty-spinner'
6
+
7
+ module PWN
8
+ module Blockchain
9
+ # This plugin interacts with BitCoin's Blockchain API.
10
+ module BTC
11
+ # Supported Method Parameters::
12
+ # btc_rest_call(
13
+ # http_method: 'optional HTTP method (defaults to GET)
14
+ # rest_call: 'required rest call to make per the schema',
15
+ # params: 'optional params passed in the URI or HTTP Headers',
16
+ # http_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST',
17
+ # timeout: 'optional timeout in seconds (defaults to 180)',
18
+ # spinner: 'optional - display spinner (defaults to false)'
19
+ # )
20
+
21
+ private_class_method def self.btc_rest_call(opts = {})
22
+ http_method = if opts[:http_method].nil?
23
+ :get
24
+ else
25
+ opts[:http_method].to_s.scrub.to_sym
26
+ end
27
+
28
+ base_uri = 'https://api.blockcypher.com/v1/btc/'
29
+ rest_call = opts[:rest_call].to_s.scrub
30
+ params = opts[:params]
31
+ headers = { content_type: 'application/json; charset=UTF-8' }
32
+
33
+ http_body = opts[:http_body]
34
+ http_body ||= {}
35
+
36
+ timeout = opts[:timeout]
37
+ timeout ||= 180
38
+
39
+ spinner = opts[:spinner] || false
40
+
41
+ browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :rest)
42
+ rest_client = browser_obj[:browser]::Request
43
+
44
+ if spinner
45
+ spin = TTY::Spinner.new(format: :dots)
46
+ spin.auto_spin
47
+ end
48
+
49
+ retries = 0
50
+ case http_method
51
+ when :delete, :get
52
+ headers[:params] = params
53
+ response = rest_client.execute(
54
+ method: http_method,
55
+ url: "#{base_uri}#{rest_call}",
56
+ headers: headers,
57
+ verify_ssl: false,
58
+ timeout: timeout
59
+ )
60
+
61
+ when :post
62
+ if http_body.key?(:multipart)
63
+ headers[:content_type] = 'multipart/form-data'
64
+
65
+ response = rest_client.execute(
66
+ method: http_method,
67
+ url: "#{base_uri}#{rest_call}",
68
+ headers: headers,
69
+ payload: http_body,
70
+ verify_ssl: false,
71
+ timeout: timeout
72
+ )
73
+ else
74
+ response = rest_client.execute(
75
+ method: http_method,
76
+ url: "#{base_uri}#{rest_call}",
77
+ headers: headers,
78
+ payload: http_body.to_json,
79
+ verify_ssl: false,
80
+ timeout: timeout
81
+ )
82
+ end
83
+
84
+ else
85
+ raise "Unsupported HTTP Method #{http_method} for #{self} Plugin"
86
+ end
87
+
88
+ response
89
+ rescue RestClient::ExceptionWithResponse => e
90
+ case e.http_code
91
+ when 400, 404
92
+ "#{e.http_code} #{e.message}: #{e.response.body}"
93
+ else
94
+ raise e
95
+ end
96
+ rescue StandardError => e
97
+ raise e
98
+ ensure
99
+ spin.stop if spinner
100
+ end
101
+
102
+ # Supported Method Parameters::
103
+ # latest_block = PWN::Blockchain::BTC.get_latest_block(
104
+ # token: 'optional - API token for higher rate limits'
105
+ # )
106
+
107
+ public_class_method def self.get_latest_block(opts = {})
108
+ params = {}
109
+ params[:token] = opts[:token] if opts[:token]
110
+
111
+ rest_call = 'main'
112
+ response = btc_rest_call(rest_call: rest_call, params: params)
113
+
114
+ JSON.parse(response.body, symbolize_names: true)
115
+ rescue StandardError => e
116
+ raise e
117
+ end
118
+
119
+ # Supported Method Parameters::
120
+ # PWN::Blockchain::BTC.get_block_details(
121
+ # height: 'required - block height number',
122
+ # token: 'optional - API token for higher rate limits'
123
+ # )
124
+ public_class_method def self.get_block_details(opts = {})
125
+ height = opts[:height]
126
+ params = {}
127
+ params[:token] = opts[:token] if opts[:token]
128
+
129
+ rest_call = "main/blocks/#{height}"
130
+ response = btc_rest_call(rest_call: rest_call, params: params)
131
+
132
+ JSON.parse(response.body, symbolize_names: true)
133
+ rescue StandardError => e
134
+ raise e
135
+ end
136
+
137
+ # Author(s):: 0day Inc. <support@0dayinc.com>
138
+
139
+ public_class_method def self.authors
140
+ "AUTHOR(S):
141
+ 0day Inc. <support@0dayinc.com>
142
+ "
143
+ end
144
+
145
+ # Display Usage for this Module
146
+
147
+ public_class_method def self.help
148
+ puts "USAGE:
149
+ latest_block = #{self}.get_latest_block
150
+
151
+ block_details = #{self}.get_block_details(
152
+ height: 'required - block height number'
153
+ )
154
+
155
+ #{self}.authors
156
+ "
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'rest-client'
5
+ require 'tty-spinner'
6
+
7
+ module PWN
8
+ module Blockchain
9
+ # This plugin interacts with BitCoin's Blockchain API.
10
+ module ETH
11
+ # Supported Method Parameters::
12
+ # eth_rest_call(
13
+ # http_method: 'optional HTTP method (defaults to GET)
14
+ # rest_call: 'required rest call to make per the schema',
15
+ # params: 'optional params passed in the URI or HTTP Headers',
16
+ # http_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST',
17
+ # timeout: 'optional timeout in seconds (defaults to 180)',
18
+ # spinner: 'optional - display spinner (defaults to false)'
19
+ # )
20
+
21
+ private_class_method def self.eth_rest_call(opts = {})
22
+ http_method = if opts[:http_method].nil?
23
+ :get
24
+ else
25
+ opts[:http_method].to_s.scrub.to_sym
26
+ end
27
+
28
+ base_uri = 'https://api.blockcypher.com/v1/eth/'
29
+ rest_call = opts[:rest_call].to_s.scrub
30
+ params = opts[:params]
31
+ headers = { content_type: 'application/json; charset=UTF-8' }
32
+
33
+ http_body = opts[:http_body]
34
+ http_body ||= {}
35
+
36
+ timeout = opts[:timeout]
37
+ timeout ||= 180
38
+
39
+ spinner = opts[:spinner] || false
40
+
41
+ browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :rest)
42
+ rest_client = browser_obj[:browser]::Request
43
+
44
+ if spinner
45
+ spin = TTY::Spinner.new(format: :dots)
46
+ spin.auto_spin
47
+ end
48
+
49
+ retries = 0
50
+ case http_method
51
+ when :delete, :get
52
+ headers[:params] = params
53
+ response = rest_client.execute(
54
+ method: http_method,
55
+ url: "#{base_uri}#{rest_call}",
56
+ headers: headers,
57
+ verify_ssl: false,
58
+ timeout: timeout
59
+ )
60
+
61
+ when :post
62
+ if http_body.key?(:multipart)
63
+ headers[:content_type] = 'multipart/form-data'
64
+
65
+ response = rest_client.execute(
66
+ method: http_method,
67
+ url: "#{base_uri}#{rest_call}",
68
+ headers: headers,
69
+ payload: http_body,
70
+ verify_ssl: false,
71
+ timeout: timeout
72
+ )
73
+ else
74
+ response = rest_client.execute(
75
+ method: http_method,
76
+ url: "#{base_uri}#{rest_call}",
77
+ headers: headers,
78
+ payload: http_body.to_json,
79
+ verify_ssl: false,
80
+ timeout: timeout
81
+ )
82
+ end
83
+
84
+ else
85
+ raise "Unsupported HTTP Method #{http_method} for #{self} Plugin"
86
+ end
87
+
88
+ response
89
+ rescue RestClient::ExceptionWithResponse => e
90
+ case e.http_code
91
+ when 400, 404
92
+ "#{e.http_code} #{e.message}: #{e.response.body}"
93
+ else
94
+ raise e
95
+ end
96
+ rescue StandardError => e
97
+ raise e
98
+ ensure
99
+ spin.stop if spinner
100
+ end
101
+
102
+ # Supported Method Parameters::
103
+ # latest_block = PWN::Blockchain::ETH.get_latest_block(
104
+ # token: 'optional - API token for higher rate limits'
105
+ # )
106
+
107
+ public_class_method def self.get_latest_block(opts = {})
108
+ params = {}
109
+ params[:token] = opts[:token] if opts[:token]
110
+
111
+ rest_call = 'main'
112
+ response = eth_rest_call(rest_call: rest_call, params: params)
113
+
114
+ JSON.parse(response.body, symbolize_names: true)
115
+ rescue StandardError => e
116
+ raise e
117
+ end
118
+
119
+ # Supported Method Parameters::
120
+ # PWN::Blockchain::ETH.get_block_details(
121
+ # height: 'required - block height number',
122
+ # token: 'optional - API token for higher rate limits'
123
+ # )
124
+ public_class_method def self.get_block_details(opts = {})
125
+ height = opts[:height]
126
+ params = {}
127
+ params[:token] = opts[:token] if opts[:token]
128
+
129
+ rest_call = "main/blocks/#{height}"
130
+ response = eth_rest_call(rest_call: rest_call, params: params)
131
+
132
+ JSON.parse(response.body, symbolize_names: true)
133
+ rescue StandardError => e
134
+ raise e
135
+ end
136
+
137
+ # Author(s):: 0day Inc. <support@0dayinc.com>
138
+
139
+ public_class_method def self.authors
140
+ "AUTHOR(S):
141
+ 0day Inc. <support@0dayinc.com>
142
+ "
143
+ end
144
+
145
+ # Display Usage for this Module
146
+
147
+ public_class_method def self.help
148
+ puts "USAGE:
149
+ latest_block = #{self}.get_latest_block
150
+
151
+ block_details = #{self}.get_block_details(
152
+ height: 'required - block height number'
153
+ )
154
+
155
+ #{self}.authors
156
+ "
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PWN
4
+ # This file, using the autoload directive loads SP plugins
5
+ # into memory only when they're needed. For more information, see:
6
+ # http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
7
+ module Blockchain
8
+ autoload :BTC, 'pwn/blockchain/btc'
9
+ autoload :ETH, 'pwn/blockchain/eth'
10
+
11
+ # Display a List of Every PWN::Blockchain Module
12
+
13
+ public_class_method def self.help
14
+ constants.sort
15
+ end
16
+ end
17
+ end
@@ -469,12 +469,13 @@ module PWN
469
469
  return
470
470
  end
471
471
 
472
- decryption_file = pi.config.decryption_file ||= "#{Dir.home}/.pwn/pwn.decryptor.yaml"
473
- unless File.exist?(decryption_file)
474
- puts "ERROR: pwn.decryptor.yaml not found: #{decryption_file}"
472
+ yaml_decryptor_path = pi.config.yaml_decryptor_path ||= "#{Dir.home}/.pwn/pwn.decryptor.yaml"
473
+ unless File.exist?(yaml_decryptor_path)
474
+ puts "ERROR: pwn.decryptor.yaml not found: #{yaml_decryptor_path}"
475
475
  return
476
476
  end
477
- decryptor = YAML.load_file(decryption_file, symbolize_names: true)
477
+
478
+ decryptor = YAML.load_file(yaml_decryptor_path, symbolize_names: true)
478
479
  key = decryptor[:key]
479
480
  iv = decryptor[:iv]
480
481
 
@@ -539,7 +540,7 @@ module PWN
539
540
  Pry.config.hooks.add_hook(:before_session, :init_opts) do |_output, _binding, pi|
540
541
  opts[:pi] = pi
541
542
  Pry.config.yaml_config_path = opts[:yaml_config_path]
542
- Pry.config.decryption_file = opts[:decryption_file]
543
+ Pry.config.yaml_decryptor_path = opts[:yaml_decryptor_path]
543
544
 
544
545
  PWN::Plugins::Vault.refresh_config_for_repl(opts)
545
546
  end
@@ -690,7 +691,7 @@ module PWN
690
691
  pwn_config_root = "#{Dir.home}/.pwn"
691
692
  FileUtils.mkdir_p(pwn_config_root)
692
693
  opts[:yaml_config_path] ||= "#{pwn_config_root}/pwn.yaml"
693
- opts[:decryption_file] ||= "#{pwn_config_root}/pwn.decryptor.yaml"
694
+ opts[:yaml_decryptor_path] ||= "#{pwn_config_root}/pwn.decryptor.yaml"
694
695
 
695
696
  add_hooks(opts)
696
697
 
@@ -238,7 +238,7 @@ module PWN
238
238
  # PWN::Plugins::Vault.refresh_config_for_repl(
239
239
  # yaml_config_path: 'required - full path to pwn.yaml file',
240
240
  # pi: 'optional - Pry instance (default: Pry)',
241
- # decryption_file: 'optional - full path to decryption YAML file'
241
+ # yaml_decryptor_path: 'optional - full path to decryption YAML file'
242
242
  # )
243
243
  public_class_method def self.refresh_config_for_repl(opts = {})
244
244
  yaml_config_path = opts[:yaml_config_path]
@@ -251,10 +251,10 @@ module PWN
251
251
 
252
252
  if is_encrypted
253
253
  # TODO: Implement "something you know, something you have, && something you are?"
254
- decryption_file = opts[:decryption_file] ||= "#{Dir.home}/pwn.decryptor.yaml"
255
- raise "ERROR: #{decryption_file} does not exist." unless File.exist?(decryption_file)
254
+ yaml_decryptor_path = opts[:yaml_decryptor_path] ||= "#{Dir.home}/.pwn/pwn.decryptor.yaml"
255
+ raise "ERROR: #{yaml_decryptor_path} does not exist." unless File.exist?(yaml_decryptor_path)
256
256
 
257
- yaml_decryptor = YAML.load_file(decryption_file, symbolize_names: true)
257
+ yaml_decryptor = YAML.load_file(yaml_decryptor_path, symbolize_names: true)
258
258
 
259
259
  key = opts[:key] ||= yaml_decryptor[:key] ||= ENV.fetch('PWN_DECRYPTOR_KEY')
260
260
  key = PWN::Plugins::AuthenticationHelper.mask_password(prompt: 'Decryption Key') if key.nil?
@@ -377,7 +377,7 @@ module PWN
377
377
  #{self}.refresh_config_for_repl(
378
378
  yaml_config_path: 'required - full path to pwn.yaml file',
379
379
  pi: 'optional - Pry instance (default: Pry)',
380
- decryption_file: 'optional - full path to decryption YAML file'
380
+ yaml_decryptor_path: 'optional - full path to decryption YAML file'
381
381
  )
382
382
 
383
383
  #{self}.authors
data/lib/pwn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PWN
4
- VERSION = '0.5.425'
4
+ VERSION = '0.5.426'
5
5
  end
data/lib/pwn.rb CHANGED
@@ -11,6 +11,7 @@ module PWN
11
11
  autoload :AI, 'pwn/ai'
12
12
  autoload :AWS, 'pwn/aws'
13
13
  autoload :Banner, 'pwn/banner'
14
+ autoload :Blockchain, 'pwn/blockchain'
14
15
  autoload :FFI, 'pwn/ffi'
15
16
  autoload :Plugins, 'pwn/plugins'
16
17
  autoload :Reports, 'pwn/reports'
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe PWN::Blockchain::BTC do
6
+ it 'should display information for authors' do
7
+ authors_response = PWN::Blockchain::BTC
8
+ expect(authors_response).to respond_to :authors
9
+ end
10
+
11
+ it 'should display information for existing help method' do
12
+ help_response = PWN::Blockchain::BTC
13
+ expect(help_response).to respond_to :help
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe PWN::Blockchain::ETH do
6
+ it 'should display information for authors' do
7
+ authors_response = PWN::Blockchain::ETH
8
+ expect(authors_response).to respond_to :authors
9
+ end
10
+
11
+ it 'should display information for existing help method' do
12
+ help_response = PWN::Blockchain::ETH
13
+ expect(help_response).to respond_to :help
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe PWN::Blockchain do
6
+ it 'should return data for help method' do
7
+ help_response = PWN::Blockchain.help
8
+ expect(help_response).not_to be_nil
9
+ end
10
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.425
4
+ version: 0.5.426
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.
@@ -1051,14 +1051,14 @@ dependencies:
1051
1051
  requirements:
1052
1052
  - - '='
1053
1053
  - !ruby/object:Gem::Version
1054
- version: 0.139.0
1054
+ version: 0.140.0
1055
1055
  type: :runtime
1056
1056
  prerelease: false
1057
1057
  version_requirements: !ruby/object:Gem::Requirement
1058
1058
  requirements:
1059
1059
  - - '='
1060
1060
  - !ruby/object:Gem::Version
1061
- version: 0.139.0
1061
+ version: 0.140.0
1062
1062
  - !ruby/object:Gem::Dependency
1063
1063
  name: slack-ruby-client
1064
1064
  requirement: !ruby/object:Gem::Requirement
@@ -1829,6 +1829,9 @@ files:
1829
1829
  - lib/pwn/banner/radare2.rb
1830
1830
  - lib/pwn/banner/radare2_ai.rb
1831
1831
  - lib/pwn/banner/white_rabbit.rb
1832
+ - lib/pwn/blockchain.rb
1833
+ - lib/pwn/blockchain/btc.rb
1834
+ - lib/pwn/blockchain/eth.rb
1832
1835
  - lib/pwn/ffi.rb
1833
1836
  - lib/pwn/ffi/stdio.rb
1834
1837
  - lib/pwn/plugins.rb
@@ -2174,6 +2177,9 @@ files:
2174
2177
  - spec/lib/pwn/banner/radare2_spec.rb
2175
2178
  - spec/lib/pwn/banner/white_rabbit_spec.rb
2176
2179
  - spec/lib/pwn/banner_spec.rb
2180
+ - spec/lib/pwn/blockchain/btc_spec.rb
2181
+ - spec/lib/pwn/blockchain/eth_spec.rb
2182
+ - spec/lib/pwn/blockchain_spec.rb
2177
2183
  - spec/lib/pwn/ffi/stdio_spec.rb
2178
2184
  - spec/lib/pwn/ffi_spec.rb
2179
2185
  - spec/lib/pwn/plugins/android_spec.rb