metasploit-credential 6.0.3 → 6.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/app/models/metasploit/credential/pkcs12.rb +84 -0
- data/app/models/metasploit/credential/private.rb +1 -0
- data/config/locales/en.yml +6 -1
- data/db/migrate/20221209005658_create_index_on_private_data_and_type_for_pkcs12.rb +32 -0
- data/lib/metasploit/credential/creation.rb +2 -0
- data/lib/metasploit/credential/version.rb +1 -1
- data/lib/metasploit/credential.rb +1 -0
- data/spec/dummy/config/database.yml +2 -2
- data/spec/factories/metasploit/credential/pkcs12.rb +37 -0
- data/spec/lib/metasploit/credential/creation_spec.rb +22 -2
- data/spec/models/metasploit/credential/pkcs12_spec.rb +116 -0
- data/spec/models/metasploit/credential/private_spec.rb +1 -0
- data.tar.gz.sig +0 -0
- metadata +8 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97884e997f5fe9428e4fc79f0b4a9e4a4e51a06363b372a2fa1affa0dffba6d5
|
4
|
+
data.tar.gz: 37e4b1a543e249e7facb0c6570efd104d4bf347922636b561aecb78bab0602a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23da300c2d52cd12a8aeb398010b21d2542ab4f88f59946fabde21efbb3554c30f36b32839961abef13c43528324ddacc807d68872cc9df3bbf2e47d31c66905
|
7
|
+
data.tar.gz: 90f93c7bd036e2f6b328dca97707d5e85bd40a257cb6f7dba928d0d6fd983cd295e38b3052cb6aae271c37bd730e4b5c36e71feed1e1dc561bb0b163c207d098
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
# A private Pkcs12 file.
|
5
|
+
class Metasploit::Credential::Pkcs12 < Metasploit::Credential::Private
|
6
|
+
#
|
7
|
+
# Attributes
|
8
|
+
#
|
9
|
+
|
10
|
+
# @!attribute data
|
11
|
+
# A private pkcs12 file, base64 encoded - i.e. starting with 'MIIMhgIBAzCCDFAGCSqGSIb3DQEHAaCC....'
|
12
|
+
#
|
13
|
+
# @return [String]
|
14
|
+
|
15
|
+
#
|
16
|
+
#
|
17
|
+
# Validations
|
18
|
+
#
|
19
|
+
#
|
20
|
+
|
21
|
+
#
|
22
|
+
# Attribute Validations
|
23
|
+
#
|
24
|
+
|
25
|
+
validates :data,
|
26
|
+
presence: true
|
27
|
+
#
|
28
|
+
# Method Validations
|
29
|
+
#
|
30
|
+
|
31
|
+
validate :readable
|
32
|
+
|
33
|
+
#
|
34
|
+
# Instance Methods
|
35
|
+
#
|
36
|
+
|
37
|
+
# Converts the private pkcs12 data in {#data} to an `OpenSSL::PKCS12` instance.
|
38
|
+
#
|
39
|
+
# @return [OpenSSL::PKCS12]
|
40
|
+
# @raise [ArgumentError] if {#data} cannot be loaded
|
41
|
+
def openssl_pkcs12
|
42
|
+
if data
|
43
|
+
begin
|
44
|
+
password = ''
|
45
|
+
OpenSSL::PKCS12.new(Base64.strict_decode64(data), password)
|
46
|
+
rescue OpenSSL::PKCS12::PKCS12Error => error
|
47
|
+
raise ArgumentError.new(error)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# The {#data key data}'s fingerprint, suitable for displaying to the
|
53
|
+
# user.
|
54
|
+
#
|
55
|
+
# @return [String]
|
56
|
+
def to_s
|
57
|
+
return '' unless data
|
58
|
+
|
59
|
+
cert = openssl_pkcs12.certificate
|
60
|
+
result = []
|
61
|
+
result << "subject:#{cert.subject.to_s}"
|
62
|
+
result << "issuer:#{cert.issuer.to_s}"
|
63
|
+
result.join(',')
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
#
|
69
|
+
# Validates that {#data} can be read by OpenSSL and a `OpenSSL::PKCS12` can be created from {#data}. Any exception
|
70
|
+
# raised will be reported as a validation error.
|
71
|
+
#
|
72
|
+
# @return [void]
|
73
|
+
def readable
|
74
|
+
if data
|
75
|
+
begin
|
76
|
+
openssl_pkcs12
|
77
|
+
rescue => error
|
78
|
+
errors.add(:data, "#{error.class} #{error}")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
Metasploit::Concern.run(self)
|
84
|
+
end
|
data/config/locales/en.yml
CHANGED
@@ -57,6 +57,7 @@ en:
|
|
57
57
|
metasploit/credential/ntlm_hash: "NTLM hash"
|
58
58
|
metasploit/credential/ssh_key: "SSH key"
|
59
59
|
metasploit/credential/krb_enc_key: 'Krb enc key'
|
60
|
+
metasploit/credential/pkcs12: 'Pkcs12 (pfx)'
|
60
61
|
errors:
|
61
62
|
models:
|
62
63
|
metasploit/credential/core:
|
@@ -83,10 +84,14 @@ en:
|
|
83
84
|
attributes:
|
84
85
|
data:
|
85
86
|
format: "is not in the KrbEncKey data format of 'msf_krbenckey:<ENCTYPE>:<KEY>:<SALT>', where the key and salt are in hexadecimal characters"
|
87
|
+
metasploit/credential/pkcs12:
|
88
|
+
attributes:
|
89
|
+
data:
|
90
|
+
format: "is not a Base64 encoded pkcs12 file without a password"
|
86
91
|
metasploit/credential/ssh_key:
|
87
92
|
attributes:
|
88
93
|
data:
|
89
|
-
encrypted: "is encrypted, but Metasploit::Credential::SSHKey only supports
|
94
|
+
encrypted: "is encrypted, but Metasploit::Credential::SSHKey only supports unencrypted private keys."
|
90
95
|
not_private: "is not a private key."
|
91
96
|
errors:
|
92
97
|
messages:
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class CreateIndexOnPrivateDataAndTypeForPkcs12 < ActiveRecord::Migration[6.1]
|
2
|
+
def up
|
3
|
+
# Drop the existing index created by 20161107153145_recreate_index_on_private_data_and_type.rb, and recreate it
|
4
|
+
# with Metasploit::Credential::Pkcs12 ignored
|
5
|
+
remove_index :metasploit_credential_privates, [:type, :data], if_exists: true
|
6
|
+
change_table :metasploit_credential_privates do |t|
|
7
|
+
t.index [:type, :data],
|
8
|
+
unique: true,
|
9
|
+
where: "NOT (type = 'Metasploit::Credential::SSHKey' or type = 'Metasploit::Credential::Pkcs12')"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Create a new index similar to 20161107203710_create_index_on_private_data_and_type_for_ssh_key.rb
|
13
|
+
sql = <<~EOF
|
14
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "index_metasploit_credential_privates_on_type_and_data_pkcs12" ON
|
15
|
+
"metasploit_credential_privates" ("type", decode(md5(data), 'hex'))
|
16
|
+
WHERE type in ('Metasploit::Credential::Pkcs12')
|
17
|
+
EOF
|
18
|
+
execute(sql)
|
19
|
+
end
|
20
|
+
|
21
|
+
def down
|
22
|
+
# Restore the original metasploit_credential_privates index from /Users/adfoster/Documents/code/metasploit-credential/db/migrate/20161107153145_recreate_index_on_private_data_and_type.rb
|
23
|
+
# XXX: this would crash if there are any Pkcs12 entries present, so for the simplicity of avoiding a data migration we keep the pkcs12 type ommitted from the index
|
24
|
+
remove_index :metasploit_credential_privates, [:type, :data], if_exists: true
|
25
|
+
change_table :metasploit_credential_privates do |t|
|
26
|
+
t.index [:type, :data],
|
27
|
+
unique: true,
|
28
|
+
where: "NOT (type = 'Metasploit::Credential::SSHKey' or type = 'Metasploit::Credential::Pkcs12')"
|
29
|
+
end
|
30
|
+
remove_index :metasploit_credential_privates, name: :index_metasploit_credential_privates_on_type_and_data_pkcs12, if_exists: true
|
31
|
+
end
|
32
|
+
end
|
@@ -479,6 +479,8 @@ module Metasploit::Credential::Creation
|
|
479
479
|
private_object = Metasploit::Credential::Password.where(data: private_data).first_or_create
|
480
480
|
when :ssh_key
|
481
481
|
private_object = Metasploit::Credential::SSHKey.where(data: private_data).first_or_create
|
482
|
+
when :pkcs12
|
483
|
+
private_object = Metasploit::Credential::Pkcs12.where(data: private_data).first_or_create
|
482
484
|
when :krb_enc_key
|
483
485
|
private_object = Metasploit::Credential::KrbEncKey.where(data: private_data).first_or_create
|
484
486
|
when :ntlm_hash
|
@@ -1,6 +1,6 @@
|
|
1
1
|
development: &pgsql
|
2
2
|
adapter: postgresql
|
3
|
-
database: metasploit-
|
3
|
+
database: metasploit-credential_development3
|
4
4
|
username: msf
|
5
5
|
password: pass123
|
6
6
|
host: localhost
|
@@ -10,4 +10,4 @@ development: &pgsql
|
|
10
10
|
min_messages: warning
|
11
11
|
test:
|
12
12
|
<<: *pgsql
|
13
|
-
database: metasploit-
|
13
|
+
database: metasploit-credential_test3
|
@@ -0,0 +1,37 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :metasploit_credential_pkcs12,
|
3
|
+
class: Metasploit::Credential::Pkcs12 do
|
4
|
+
transient do
|
5
|
+
# key size tuned for speed. DO NOT use for production, it is below current recommended key size of 2048
|
6
|
+
key_size { 1024 }
|
7
|
+
# signing algorithm for the pkcs12 cert
|
8
|
+
signing_algorithm { 'SHA256' }
|
9
|
+
# the cert subject
|
10
|
+
subject { '/C=BE/O=Test/OU=Test/CN=Test' }
|
11
|
+
# the cert issuer
|
12
|
+
issuer { '/C=BE/O=Test/OU=Test/CN=Test' }
|
13
|
+
end
|
14
|
+
|
15
|
+
data {
|
16
|
+
password = ''
|
17
|
+
pkcs12_name = ''
|
18
|
+
|
19
|
+
private_key = OpenSSL::PKey::RSA.new(key_size)
|
20
|
+
public_key = private_key.public_key
|
21
|
+
|
22
|
+
cert = OpenSSL::X509::Certificate.new
|
23
|
+
cert.subject = OpenSSL::X509::Name.parse(subject)
|
24
|
+
cert.issuer = OpenSSL::X509::Name.parse(issuer)
|
25
|
+
cert.not_before = Time.now
|
26
|
+
cert.not_after = Time.now + 365 * 24 * 60 * 60
|
27
|
+
cert.public_key = public_key
|
28
|
+
cert.serial = 0x0
|
29
|
+
cert.version = 2
|
30
|
+
cert.sign(private_key, OpenSSL::Digest.new(signing_algorithm))
|
31
|
+
|
32
|
+
pkcs12 = OpenSSL::PKCS12.create(password, pkcs12_name, private_key, cert)
|
33
|
+
pkcs12_base64 = Base64.strict_encode64(pkcs12.to_der)
|
34
|
+
pkcs12_base64
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
@@ -134,20 +134,30 @@ RSpec.describe Metasploit::Credential::Creation do
|
|
134
134
|
nonreplayable_hash: "Metasploit::Credential::NonreplayableHash",
|
135
135
|
ntlm_hash: "Metasploit::Credential::NTLMHash",
|
136
136
|
postgres_md5: "Metasploit::Credential::PostgresMD5",
|
137
|
-
ssh_key: "Metasploit::Credential::SSHKey"
|
137
|
+
ssh_key: "Metasploit::Credential::SSHKey",
|
138
|
+
krb_enc_key: "Metasploit::Credential::KrbEncKey",
|
139
|
+
pkcs12: "Metasploit::Credential::Pkcs12"
|
138
140
|
}.each_pair do |private_type, public_class|
|
139
141
|
context "Origin[manual], Public[Username], Private[#{private_type}]" do
|
140
142
|
let(:ssh_key) {
|
141
143
|
key_class = OpenSSL::PKey.const_get(:RSA)
|
142
144
|
key_class.generate(512).to_s
|
143
145
|
}
|
146
|
+
let(:krb_enc_key) {
|
147
|
+
FactoryBot.build(:metasploit_credential_krb_enc_key).data
|
148
|
+
}
|
149
|
+
let(:pkcs12) {
|
150
|
+
FactoryBot.build(:metasploit_credential_pkcs12).data
|
151
|
+
}
|
144
152
|
let(:private_data) { {
|
145
153
|
password: 'password',
|
146
154
|
blank_password: '',
|
147
155
|
nonreplayable_hash: '435ba65d2e46d35bc656086694868d1ab2c0f9fd',
|
148
156
|
ntlm_hash: 'aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0',
|
149
157
|
postgres_md5: 'md5ac4bbe016b808c3c0b816981f240dcae',
|
150
|
-
ssh_key: ssh_key
|
158
|
+
ssh_key: ssh_key,
|
159
|
+
krb_enc_key: krb_enc_key,
|
160
|
+
pkcs12: pkcs12
|
151
161
|
}}
|
152
162
|
let(:credential_data) {{
|
153
163
|
workspace_id: workspace.id,
|
@@ -821,6 +831,16 @@ RSpec.describe Metasploit::Credential::Creation do
|
|
821
831
|
expect{ test_object.create_credential_private(opts) }.to change{ Metasploit::Credential::KrbEncKey.count }.by(1)
|
822
832
|
end
|
823
833
|
end
|
834
|
+
|
835
|
+
context 'when :private_type is pkcs12' do
|
836
|
+
it 'creates a Metasploit::Credential::Pkcs12' do
|
837
|
+
opts = {
|
838
|
+
private_data: FactoryBot.build(:metasploit_credential_pkcs12).data,
|
839
|
+
private_type: :pkcs12
|
840
|
+
}
|
841
|
+
expect{ test_object.create_credential_private(opts) }.to change{ Metasploit::Credential::Pkcs12.count }.by(1)
|
842
|
+
end
|
843
|
+
end
|
824
844
|
end
|
825
845
|
|
826
846
|
context '#create_credential_core' do
|
@@ -0,0 +1,116 @@
|
|
1
|
+
RSpec.describe Metasploit::Credential::Pkcs12, type: :model do
|
2
|
+
it_should_behave_like 'Metasploit::Concern.run'
|
3
|
+
|
4
|
+
context 'factories' do
|
5
|
+
context 'metasploit_credential_pkcs12' do
|
6
|
+
subject(:metasploit_credential_pkcs12) do
|
7
|
+
FactoryBot.build(:metasploit_credential_pkcs12)
|
8
|
+
end
|
9
|
+
|
10
|
+
it { is_expected.to be_valid }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'validations' do
|
15
|
+
it { is_expected.to validate_presence_of :data }
|
16
|
+
|
17
|
+
context 'on #data' do
|
18
|
+
subject(:data_errors) do
|
19
|
+
pkcs12.errors[:data]
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:pkcs12) do
|
23
|
+
FactoryBot.build(:metasploit_credential_pkcs12)
|
24
|
+
end
|
25
|
+
|
26
|
+
context '#readable' do
|
27
|
+
context 'with #data' do
|
28
|
+
context 'with error' do
|
29
|
+
#
|
30
|
+
# Shared Examples
|
31
|
+
#
|
32
|
+
|
33
|
+
shared_examples_for 'exception' do
|
34
|
+
it 'includes error class' do
|
35
|
+
exception_class_name = exception.class.to_s
|
36
|
+
expect(
|
37
|
+
data_errors.any? { |error|
|
38
|
+
error.include? exception_class_name
|
39
|
+
}
|
40
|
+
).to be true
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'includes error message' do
|
44
|
+
exception_message = exception.to_s
|
45
|
+
|
46
|
+
expect(
|
47
|
+
data_errors.any? { |error|
|
48
|
+
error.include? exception_message
|
49
|
+
}
|
50
|
+
).to be true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Callbacks
|
56
|
+
#
|
57
|
+
|
58
|
+
before(:example) do
|
59
|
+
expect(pkcs12).to receive(:openssl_pkcs12).and_raise(exception)
|
60
|
+
|
61
|
+
pkcs12.valid?
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'with ArgumentError' do
|
65
|
+
let(:exception) do
|
66
|
+
ArgumentError.new("Bad Argument")
|
67
|
+
end
|
68
|
+
|
69
|
+
it_should_behave_like 'exception'
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with OpenSSL::PKCS12::PKCS12Error' do
|
73
|
+
let(:exception) do
|
74
|
+
OpenSSL::PKCS12::PKCS12Error.new('mac verify failure')
|
75
|
+
end
|
76
|
+
|
77
|
+
it_should_behave_like 'exception'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'without error' do
|
82
|
+
before(:example) do
|
83
|
+
pkcs12.valid?
|
84
|
+
end
|
85
|
+
|
86
|
+
it { is_expected.to be_empty }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'without #data' do
|
91
|
+
let(:error) do
|
92
|
+
I18n.translate!('errors.messages.blank')
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# Callbacks
|
97
|
+
#
|
98
|
+
|
99
|
+
before(:example) do
|
100
|
+
pkcs12.data = nil
|
101
|
+
|
102
|
+
pkcs12.valid?
|
103
|
+
end
|
104
|
+
|
105
|
+
it { is_expected.to include(error) }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe 'human name' do
|
112
|
+
it 'properly determines the model\'s human name' do
|
113
|
+
expect(described_class.model_name.human).to eq('Pkcs12 (pfx)')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metasploit-credential
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.0.
|
4
|
+
version: 6.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Metasploit Hackers
|
@@ -93,7 +93,7 @@ cert_chain:
|
|
93
93
|
EknWpNgVhohbot1lfVAMmIhdtOVaRVcQQixWPwprDj/ydB8ryDMDosIMcw+fkoXU
|
94
94
|
9GJsSaSRRYQ9UUkVL27b64okU8D48m8=
|
95
95
|
-----END CERTIFICATE-----
|
96
|
-
date: 2023-04-
|
96
|
+
date: 2023-04-11 00:00:00.000000000 Z
|
97
97
|
dependencies:
|
98
98
|
- !ruby/object:Gem::Dependency
|
99
99
|
name: metasploit-concern
|
@@ -255,6 +255,7 @@ files:
|
|
255
255
|
- app/models/metasploit/credential/origin/session.rb
|
256
256
|
- app/models/metasploit/credential/password.rb
|
257
257
|
- app/models/metasploit/credential/password_hash.rb
|
258
|
+
- app/models/metasploit/credential/pkcs12.rb
|
258
259
|
- app/models/metasploit/credential/postgres_md5.rb
|
259
260
|
- app/models/metasploit/credential/private.rb
|
260
261
|
- app/models/metasploit/credential/public.rb
|
@@ -288,6 +289,7 @@ files:
|
|
288
289
|
- db/migrate/20150106201450_old_creds_to_new_creds2.rb
|
289
290
|
- db/migrate/20161107153145_recreate_index_on_private_data_and_type.rb
|
290
291
|
- db/migrate/20161107203710_create_index_on_private_data_and_type_for_ssh_key.rb
|
292
|
+
- db/migrate/20221209005658_create_index_on_private_data_and_type_for_pkcs12.rb
|
291
293
|
- lib/metasploit/credential.rb
|
292
294
|
- lib/metasploit/credential/core_validations.rb
|
293
295
|
- lib/metasploit/credential/creation.rb
|
@@ -366,6 +368,7 @@ files:
|
|
366
368
|
- spec/factories/metasploit/credential/origin/sessions.rb
|
367
369
|
- spec/factories/metasploit/credential/password_hashes.rb
|
368
370
|
- spec/factories/metasploit/credential/passwords.rb
|
371
|
+
- spec/factories/metasploit/credential/pkcs12.rb
|
369
372
|
- spec/factories/metasploit/credential/postgres_md5.rb
|
370
373
|
- spec/factories/metasploit/credential/privates.rb
|
371
374
|
- spec/factories/metasploit/credential/publics.rb
|
@@ -401,6 +404,7 @@ files:
|
|
401
404
|
- spec/models/metasploit/credential/origin/session_spec.rb
|
402
405
|
- spec/models/metasploit/credential/password_hash_spec.rb
|
403
406
|
- spec/models/metasploit/credential/password_spec.rb
|
407
|
+
- spec/models/metasploit/credential/pkcs12_spec.rb
|
404
408
|
- spec/models/metasploit/credential/postgres_md5_spec.rb
|
405
409
|
- spec/models/metasploit/credential/private_spec.rb
|
406
410
|
- spec/models/metasploit/credential/public_spec.rb
|
@@ -497,6 +501,7 @@ test_files:
|
|
497
501
|
- spec/factories/metasploit/credential/origin/sessions.rb
|
498
502
|
- spec/factories/metasploit/credential/password_hashes.rb
|
499
503
|
- spec/factories/metasploit/credential/passwords.rb
|
504
|
+
- spec/factories/metasploit/credential/pkcs12.rb
|
500
505
|
- spec/factories/metasploit/credential/postgres_md5.rb
|
501
506
|
- spec/factories/metasploit/credential/privates.rb
|
502
507
|
- spec/factories/metasploit/credential/publics.rb
|
@@ -532,6 +537,7 @@ test_files:
|
|
532
537
|
- spec/models/metasploit/credential/origin/session_spec.rb
|
533
538
|
- spec/models/metasploit/credential/password_hash_spec.rb
|
534
539
|
- spec/models/metasploit/credential/password_spec.rb
|
540
|
+
- spec/models/metasploit/credential/pkcs12_spec.rb
|
535
541
|
- spec/models/metasploit/credential/postgres_md5_spec.rb
|
536
542
|
- spec/models/metasploit/credential/private_spec.rb
|
537
543
|
- spec/models/metasploit/credential/public_spec.rb
|
metadata.gz.sig
CHANGED
Binary file
|