Skip to content

Commit 0521ffe

Browse files
am0o0hmac
authored andcommitted
v1.4 correct dirs uppercase issue
1 parent 0e343e5 commit 0521ffe

File tree

6 files changed

+187
-0
lines changed

6 files changed

+187
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!DOCTYPE qhelp SYSTEM "qhelp.dtd">
2+
<qhelp>
3+
<overview>
4+
<p>
5+
Processing an unvalidated user input can allow an attacker to execute arbitrary code in your application.
6+
Unsafe deserializing the malicious serialized yaml document through the Psych (YAML) library, making it possible to execute some code or execute arbitrary code with the help of a complete gadget chain.
7+
</p>
8+
</overview>
9+
<recommendation>
10+
<p>
11+
After Psych(YAML) 4.0.0, the load method is same as safe_load method.
12+
This vulnerability can be prevented by using YAML.load (same as <code>YAML.safe_load</code>), <code>YAML.load_file</code> (same as <code>YAML.safe_load_file</code>) instead of <code>YAML.unsafe_*</code> methods.
13+
Be careful that <code>YAML.load_stream</code> don't use safe_load method, Also Be careful the <code>to_ruby</code> method of Psych get called on a trusted parsed (<code>YAML.parse*</code>) yaml document.
14+
</p>
15+
</recommendation>
16+
<example>
17+
<p>In the example below, you can see safe and unsafe methods get called by a remote user input. You can give correct authorization to users, or you can use safe methods for loading yaml documents.</p>
18+
<sample src="YAMLUnsafeYamlDeserialization.rb" />
19+
</example>
20+
</qhelp>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* @name Deserialization of user-controlled data by YAML
3+
* @description Deserializing user-controlled data may allow attackers to
4+
* execute arbitrary code.
5+
* @kind path-problem
6+
* @problem.severity warning
7+
* @security-severity 9.8
8+
* @precision high
9+
* @id rb/YAML-unsafe-deserialization
10+
* @tags security
11+
* experimental
12+
* external/cwe/cwe-502
13+
*/
14+
15+
import codeql.ruby.ApiGraphs
16+
import codeql.ruby.DataFlow
17+
import codeql.ruby.TaintTracking
18+
import DataFlow::PathGraph
19+
import codeql.ruby.security.UnsafeDeserializationCustomizations
20+
21+
abstract class YamlSink extends DataFlow::Node { }
22+
23+
class YamlUnsafeLoadArgument extends YamlSink {
24+
YamlUnsafeLoadArgument() {
25+
this =
26+
API::getTopLevelMember(["YAML", "Psych"])
27+
.getAMethodCall(["unsafe_load_file", "unsafe_load", "load_stream"])
28+
.getArgument(0)
29+
or
30+
this =
31+
API::getTopLevelMember(["YAML", "Psych"])
32+
.getAMethodCall(["unsafe_load", "load_stream"])
33+
.getKeywordArgument("yaml")
34+
or
35+
this =
36+
API::getTopLevelMember(["YAML", "Psych"])
37+
.getAMethodCall("unsafe_load_file")
38+
.getKeywordArgument("filename")
39+
or
40+
this =
41+
API::getTopLevelMember(["YAML", "Psych"])
42+
.getAMethodCall(["parse", "parse_stream", "parse_file"])
43+
.getAMethodCall("to_ruby")
44+
}
45+
}
46+
47+
class Configuration extends TaintTracking::Configuration {
48+
Configuration() { this = "UnsafeYAMLDeserialization" }
49+
50+
override predicate isSource(DataFlow::Node source) {
51+
// to detect CVE-2022-32224, we should uncomment following line instead of current UnsafeDeserialization::Source
52+
// source instanceof DataFlow::LocalSourceNode
53+
source instanceof UnsafeDeserialization::Source
54+
}
55+
56+
override predicate isSink(DataFlow::Node sink) {
57+
// after changing the isSource for detecting CVE-2022-32224
58+
// uncomment the following line only see the CVE sink not other files similar sinks
59+
// sink.getLocation().getFile().toString().matches("%yaml_column%") and
60+
sink instanceof YamlSink
61+
}
62+
63+
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
64+
exists(DataFlow::CallNode yaml_parser_methods |
65+
yaml_parser_methods =
66+
API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall(["parse", "parse_stream"]) and
67+
(
68+
nodeFrom = yaml_parser_methods.getArgument(0) or
69+
nodeFrom = yaml_parser_methods.getKeywordArgument("yaml")
70+
) and
71+
nodeTo = yaml_parser_methods.getAMethodCall("to_ruby")
72+
)
73+
or
74+
exists(DataFlow::CallNode yaml_parser_methods |
75+
yaml_parser_methods = API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall("parse_file") and
76+
(
77+
nodeFrom = yaml_parser_methods.getArgument(0) or
78+
nodeFrom = yaml_parser_methods.getKeywordArgument("filename")
79+
) and
80+
nodeTo = yaml_parser_methods.getAMethodCall("to_ruby")
81+
)
82+
}
83+
}
84+
85+
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
86+
where config.hasFlowPath(source, sink)
87+
select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(),
88+
"potentially untrusted source"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
require 'yaml'
2+
class UsersController < ActionController::Base
3+
def example
4+
# safe
5+
Psych.load(params[:yaml_string])
6+
Psych.load_file(params[:yaml_file])
7+
Psych.parse_stream(params[:yaml_string])
8+
Psych.parse(params[:yaml_string])
9+
Psych.parse_file(params[:yaml_file])
10+
# unsafe
11+
Psych.unsafe_load(params[:yaml_string])
12+
Psych.unsafe_load_file(params[:yaml_file])
13+
Psych.load_stream(params[:yaml_string])
14+
parse_output = Psych.parse_stream(params[:yaml_string])
15+
parse_output.to_ruby
16+
Psych.parse(params[:yaml_string]).to_ruby
17+
Psych.parse_file(params[:yaml_file]).to_ruby
18+
19+
end
20+
end
21+
22+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
edges
2+
| YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] |
3+
| YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params : | YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] |
4+
| YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] |
5+
| YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params : | YAMLUnsafeYamlDeserialization.rb:14:39:14:58 | ...[...] : |
6+
| YAMLUnsafeYamlDeserialization.rb:14:39:14:58 | ...[...] : | YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby |
7+
| YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params : | YAMLUnsafeYamlDeserialization.rb:16:17:16:36 | ...[...] : |
8+
| YAMLUnsafeYamlDeserialization.rb:16:17:16:36 | ...[...] : | YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby |
9+
| YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params : | YAMLUnsafeYamlDeserialization.rb:17:22:17:39 | ...[...] : |
10+
| YAMLUnsafeYamlDeserialization.rb:17:22:17:39 | ...[...] : | YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby |
11+
nodes
12+
| YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params : | semmle.label | call to params : |
13+
| YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | semmle.label | ...[...] |
14+
| YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params : | semmle.label | call to params : |
15+
| YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | semmle.label | ...[...] |
16+
| YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params : | semmle.label | call to params : |
17+
| YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | semmle.label | ...[...] |
18+
| YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params : | semmle.label | call to params : |
19+
| YAMLUnsafeYamlDeserialization.rb:14:39:14:58 | ...[...] : | semmle.label | ...[...] : |
20+
| YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | semmle.label | call to to_ruby |
21+
| YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | semmle.label | call to to_ruby |
22+
| YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params : | semmle.label | call to params : |
23+
| YAMLUnsafeYamlDeserialization.rb:16:17:16:36 | ...[...] : | semmle.label | ...[...] : |
24+
| YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | semmle.label | call to to_ruby |
25+
| YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params : | semmle.label | call to params : |
26+
| YAMLUnsafeYamlDeserialization.rb:17:22:17:39 | ...[...] : | semmle.label | ...[...] : |
27+
subpaths
28+
#select
29+
| YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params | potentially untrusted source |
30+
| YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params : | YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params | potentially untrusted source |
31+
| YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params | potentially untrusted source |
32+
| YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params : | YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params | potentially untrusted source |
33+
| YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params : | YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params | potentially untrusted source |
34+
| YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params : | YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params | potentially untrusted source |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/CWE-502/YAMLUnsafeYamlDeserialization.ql
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
require 'yaml'
2+
class UsersController < ActionController::Base
3+
def example
4+
# safe
5+
Psych.load(params[:yaml_string])
6+
Psych.load_file(params[:yaml_file])
7+
Psych.parse_stream(params[:yaml_string])
8+
Psych.parse(params[:yaml_string])
9+
Psych.parse_file(params[:yaml_file])
10+
# unsafe
11+
Psych.unsafe_load(params[:yaml_string])
12+
Psych.unsafe_load_file(params[:yaml_file])
13+
Psych.load_stream(params[:yaml_string])
14+
parse_output = Psych.parse_stream(params[:yaml_string])
15+
parse_output.to_ruby
16+
Psych.parse(params[:yaml_string]).to_ruby
17+
Psych.parse_file(params[:yaml_file]).to_ruby
18+
19+
end
20+
end
21+
22+

0 commit comments

Comments
 (0)