diff --git a/powershell/ql/lib/semmle/code/powershell/security/UnsafeDeserializationCustomizations.qll b/powershell/ql/lib/semmle/code/powershell/security/UnsafeDeserializationCustomizations.qll index 25615acd77d7..006fb00e4140 100644 --- a/powershell/ql/lib/semmle/code/powershell/security/UnsafeDeserializationCustomizations.qll +++ b/powershell/ql/lib/semmle/code/powershell/security/UnsafeDeserializationCustomizations.qll @@ -8,10 +8,11 @@ private import semmle.code.powershell.dataflow.DataFlow import semmle.code.powershell.ApiGraphs private import semmle.code.powershell.dataflow.flowsources.FlowSources private import semmle.code.powershell.Cfg +private import powershell module UnsafeDeserialization { /** - * A data flow source for SQL-injection vulnerabilities. + * A data flow source for unsafe deserialization vulnerabilities. */ abstract class Source extends DataFlow::Node { /** Gets a string that describes the type of this flow source. */ @@ -19,12 +20,11 @@ module UnsafeDeserialization { } /** - * A data flow sink for SQL-injection vulnerabilities. + * A data flow sink for unsafe deserialization vulnerabilities. */ abstract class Sink extends DataFlow::Node { /** Gets a description of this sink. */ abstract string getSinkType(); - } /** @@ -37,17 +37,156 @@ module UnsafeDeserialization { override string getSourceType() { result = SourceNode.super.getSourceType() } } + /** + * Holds if the `ObjectCreationNode` `ocn` constructs a type whose fully qualified name + * (lowercase) matches `fullTypeName`. Handles both `New-Object TypeName` and + * `[TypeName]::new()` patterns. + */ + private predicate objectCreationMatchesType( + DataFlow::ObjectCreationNode ocn, string fullTypeName + ) { + // New-Object TypeName: getLowerCaseConstructedTypeName() returns the full qualified name + ocn.getLowerCaseConstructedTypeName() = fullTypeName + or + // [TypeName]::new(): access the qualifier TypeNameExpr for the full qualified name + ocn.getExprNode().getExpr().(ConstructorCall).getQualifier().(TypeNameExpr) + .getPossiblyQualifiedName() = fullTypeName + } + + /** + * Holds if `typeName` (lowercase, fully qualified) is a known unsafe deserializer type + * and `methodName` (lowercase) is an unsafe deserialization instance method on that type. + */ + private predicate unsafeInstanceDeserializer(string typeName, string methodName) { + typeName = "system.runtime.serialization.formatters.soap.soapformatter" and + methodName = "deserialize" + or + typeName = "system.web.ui.objectstateformatter" and + methodName = "deserialize" + or + typeName = "system.runtime.serialization.netdatacontractserializer" and + methodName = ["deserialize", "readobject"] + or + typeName = "system.web.ui.losformatter" and + methodName = "deserialize" + or + typeName = "system.data.dataset" and + methodName = "readxmlschema" + or + typeName = "system.data.datatable" and + methodName = ["readxmlschema", "readxml"] + or + typeName = "yamldotnet.serialization.deserializer" and + methodName = "deserialize" + } + + /** + * Holds if `typeName` (lowercase, fully qualified) has a static method + * `methodName` (lowercase) that is an unsafe deserializer. + */ + private predicate unsafeStaticDeserializer(string typeName, string methodName) { + typeName = "system.windows.markup.xamlreader" and + methodName = ["parse", "load", "loadasync"] + or + typeName = "system.workflow.componentmodel.activity" and + methodName = "load" + or + typeName = "memorypack.memorypackserializer" and + methodName = "deserialize" + } + + /** + * Holds if creating an instance of `typeName` (lowercase, fully qualified) with + * untrusted arguments is an unsafe deserialization. + */ + private predicate unsafeDeserializerConstructor(string typeName) { + typeName = "system.resources.resourcereader" + or + typeName = "system.resources.resxresourcereader" + } + + /** + * An argument to a BinaryFormatter deserialization method call, including + * Deserialize, UnsafeDeserialize, and UnsafeDeserializeMethodResponse. + */ class BinaryFormatterDeserializeSink extends Sink { BinaryFormatterDeserializeSink() { - exists(DataFlow::ObjectCreationNode ocn, DataFlow::CallNode cn | - cn.getQualifier().getALocalSource() = ocn and - ocn.getExprNode().getExpr().(CallExpr).getAnArgument().getValue().asString() = "System.Runtime.Serialization.Formatters.Binary.BinaryFormatter" and - cn.getLowerCaseName() = "deserialize" and + exists(DataFlow::ObjectCreationNode ocn, DataFlow::CallNode cn | + cn.getQualifier().getALocalSource() = ocn and + objectCreationMatchesType(ocn, + "system.runtime.serialization.formatters.binary.binaryformatter") and + cn.getLowerCaseName() = + ["deserialize", "unsafedeserialize", "unsafedeserializemethodresponse"] and cn.getAnArgument() = this - ) + ) } override string getSinkType() { result = "call to BinaryFormatter.Deserialize" } + } + + /** + * An argument to an unsafe deserialization instance method call. + * Covers SoapFormatter, ObjectStateFormatter, NetDataContractSerializer, + * LosFormatter, DataSet, DataTable, and YamlDotNet deserializers. + */ + class InstanceDeserializerSink extends Sink { + string typeName; + string methodName; + + InstanceDeserializerSink() { + unsafeInstanceDeserializer(typeName, methodName) and + exists(DataFlow::ObjectCreationNode ocn, DataFlow::CallNode cn | + cn.getQualifier().getALocalSource() = ocn and + objectCreationMatchesType(ocn, typeName) and + cn.getLowerCaseName() = methodName and + cn.getAnArgument() = this + ) + } + + override string getSinkType() { result = "call to " + typeName + "." + methodName } + } + + /** + * An argument to an unsafe static deserialization method call. + * Covers XamlReader, Activity.Load, and MemoryPackSerializer. + */ + class StaticDeserializerSink extends Sink { + string typeName; + string methodName; + + StaticDeserializerSink() { + unsafeStaticDeserializer(typeName, methodName) and + exists(DataFlow::CallNode cn | + cn.getAnArgument() = this and + cn.getLowerCaseName() = methodName and + exists(InvokeMemberExpr ime | + ime = cn.getExprNode().getExpr() and + ime.isStatic() and + ime.getQualifier().(TypeNameExpr).getPossiblyQualifiedName() = typeName + ) + ) + } + + override string getSinkType() { + result = "call to [" + typeName + "]::" + methodName + } + } + + /** + * An argument to a constructor of an unsafe deserializer type. + * Covers ResourceReader and ResXResourceReader constructors. + */ + class UnsafeConstructorSink extends Sink { + string typeName; + + UnsafeConstructorSink() { + unsafeDeserializerConstructor(typeName) and + exists(DataFlow::ObjectCreationNode ocn | + objectCreationMatchesType(ocn, typeName) and + ocn.getAnArgument() = this + ) + } + override string getSinkType() { result = "constructor of " + typeName } } } diff --git a/powershell/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.expected b/powershell/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.expected index 1f4d0a846c98..7b11885a26f8 100644 --- a/powershell/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.expected +++ b/powershell/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.expected @@ -1,18 +1,174 @@ edges -| test.ps1:1:1:1:16 | untrustedBase64 | test.ps1:3:69:3:84 | untrustedBase64 | provenance | | -| test.ps1:1:20:1:47 | Call to read-host | test.ps1:1:1:1:16 | untrustedBase64 | provenance | Src:MaD:0 | -| test.ps1:3:1:3:7 | stream | test.ps1:4:31:4:37 | stream | provenance | | -| test.ps1:3:11:3:86 | Call to new | test.ps1:3:1:3:7 | stream | provenance | | -| test.ps1:3:41:3:85 | Call to frombase64string | test.ps1:3:11:3:86 | Call to new | provenance | Config | -| test.ps1:3:69:3:84 | untrustedBase64 | test.ps1:3:41:3:85 | Call to frombase64string | provenance | Config | +| test.ps1:2:1:2:16 | untrustedBase64 | test.ps1:4:69:4:84 | untrustedBase64 | provenance | | +| test.ps1:2:20:2:47 | Call to read-host | test.ps1:2:1:2:16 | untrustedBase64 | provenance | Src:MaD:0 | +| test.ps1:4:1:4:7 | stream | test.ps1:5:31:5:37 | stream | provenance | | +| test.ps1:4:11:4:86 | Call to new | test.ps1:4:1:4:7 | stream | provenance | | +| test.ps1:4:41:4:85 | Call to frombase64string | test.ps1:4:11:4:86 | Call to new | provenance | Config | +| test.ps1:4:69:4:84 | untrustedBase64 | test.ps1:4:41:4:85 | Call to frombase64string | provenance | Config | +| test.ps1:8:1:8:7 | input2 | test.ps1:10:70:10:76 | input2 | provenance | | +| test.ps1:8:11:8:32 | Call to read-host | test.ps1:8:1:8:7 | input2 | provenance | Src:MaD:0 | +| test.ps1:10:1:10:8 | stream2 | test.ps1:11:39:11:46 | stream2 | provenance | | +| test.ps1:10:12:10:78 | Call to new | test.ps1:10:1:10:8 | stream2 | provenance | | +| test.ps1:10:42:10:77 | Call to frombase64string | test.ps1:10:12:10:78 | Call to new | provenance | Config | +| test.ps1:10:70:10:76 | input2 | test.ps1:10:42:10:77 | Call to frombase64string | provenance | Config | +| test.ps1:14:1:14:7 | input3 | test.ps1:16:80:16:86 | input3 | provenance | | +| test.ps1:14:11:14:37 | Call to read-host | test.ps1:14:1:14:7 | input3 | provenance | Src:MaD:0 | +| test.ps1:16:1:16:8 | stream3 | test.ps1:17:36:17:43 | stream3 | provenance | | +| test.ps1:16:12:16:88 | Call to new | test.ps1:16:1:16:8 | stream3 | provenance | | +| test.ps1:16:42:16:87 | Call to getbytes | test.ps1:16:12:16:88 | Call to new | provenance | Config | +| test.ps1:16:80:16:86 | input3 | test.ps1:16:42:16:87 | Call to getbytes | provenance | Config | +| test.ps1:20:1:20:7 | input4 | test.ps1:22:26:22:32 | input4 | provenance | | +| test.ps1:20:11:20:38 | Call to read-host | test.ps1:20:1:20:7 | input4 | provenance | Src:MaD:0 | +| test.ps1:25:1:25:7 | input5 | test.ps1:27:80:27:86 | input5 | provenance | | +| test.ps1:25:11:25:43 | Call to read-host | test.ps1:25:1:25:7 | input5 | provenance | Src:MaD:0 | +| test.ps1:27:1:27:8 | stream5 | test.ps1:28:27:28:34 | stream5 | provenance | | +| test.ps1:27:12:27:88 | Call to new | test.ps1:27:1:27:8 | stream5 | provenance | | +| test.ps1:27:42:27:87 | Call to getbytes | test.ps1:27:12:27:88 | Call to new | provenance | Config | +| test.ps1:27:80:27:86 | input5 | test.ps1:27:42:27:87 | Call to getbytes | provenance | Config | +| test.ps1:31:1:31:7 | input6 | test.ps1:33:80:33:86 | input6 | provenance | | +| test.ps1:31:11:31:39 | Call to read-host | test.ps1:31:1:31:7 | input6 | provenance | Src:MaD:0 | +| test.ps1:33:1:33:8 | stream6 | test.ps1:34:27:34:34 | stream6 | provenance | | +| test.ps1:33:12:33:88 | Call to new | test.ps1:33:1:33:8 | stream6 | provenance | | +| test.ps1:33:42:33:87 | Call to getbytes | test.ps1:33:12:33:88 | Call to new | provenance | Config | +| test.ps1:33:80:33:86 | input6 | test.ps1:33:42:33:87 | Call to getbytes | provenance | Config | +| test.ps1:37:1:37:7 | input7 | test.ps1:39:35:39:41 | input7 | provenance | | +| test.ps1:37:11:37:36 | Call to read-host | test.ps1:37:1:37:7 | input7 | provenance | Src:MaD:0 | +| test.ps1:42:1:42:7 | input8 | test.ps1:43:51:43:57 | input8 | provenance | | +| test.ps1:42:11:42:32 | Call to read-host | test.ps1:42:1:42:7 | input8 | provenance | Src:MaD:0 | +| test.ps1:46:1:46:7 | input9 | test.ps1:47:80:47:86 | input9 | provenance | | +| test.ps1:46:11:46:44 | Call to read-host | test.ps1:46:1:46:7 | input9 | provenance | Src:MaD:0 | +| test.ps1:47:1:47:8 | stream9 | test.ps1:48:50:48:57 | stream9 | provenance | | +| test.ps1:47:12:47:88 | Call to new | test.ps1:47:1:47:8 | stream9 | provenance | | +| test.ps1:47:42:47:87 | Call to getbytes | test.ps1:47:12:47:88 | Call to new | provenance | Config | +| test.ps1:47:80:47:86 | input9 | test.ps1:47:42:47:87 | Call to getbytes | provenance | Config | +| test.ps1:51:1:51:8 | input10 | test.ps1:53:49:53:56 | input10 | provenance | | +| test.ps1:51:12:51:40 | Call to read-host | test.ps1:51:1:51:8 | input10 | provenance | Src:MaD:0 | +| test.ps1:53:49:53:56 | input10 | test.ps1:53:19:53:57 | Call to new | provenance | Config | +| test.ps1:56:1:56:8 | input11 | test.ps1:58:43:58:50 | input11 | provenance | | +| test.ps1:56:12:56:39 | Call to read-host | test.ps1:56:1:56:8 | input11 | provenance | Src:MaD:0 | +| test.ps1:58:43:58:50 | input11 | test.ps1:58:13:58:51 | Call to new | provenance | Config | +| test.ps1:61:1:61:8 | input12 | test.ps1:63:50:63:57 | input12 | provenance | | +| test.ps1:61:12:61:41 | Call to read-host | test.ps1:61:1:61:8 | input12 | provenance | Src:MaD:0 | +| test.ps1:63:50:63:57 | input12 | test.ps1:63:20:63:58 | Call to new | provenance | Config | +| test.ps1:66:1:66:8 | input13 | test.ps1:67:68:67:75 | input13 | provenance | | +| test.ps1:66:12:66:42 | Call to read-host | test.ps1:66:1:66:8 | input13 | provenance | Src:MaD:0 | +| test.ps1:70:1:70:8 | input14 | test.ps1:71:58:71:65 | input14 | provenance | | +| test.ps1:70:12:70:38 | Call to read-host | test.ps1:70:1:70:8 | input14 | provenance | Src:MaD:0 | +| test.ps1:74:1:74:8 | input15 | test.ps1:75:71:75:78 | input15 | provenance | | +| test.ps1:74:12:74:42 | Call to read-host | test.ps1:74:1:74:8 | input15 | provenance | Src:MaD:0 | +| test.ps1:75:1:75:9 | stream15 | test.ps1:76:49:76:57 | stream15 | provenance | | +| test.ps1:75:13:75:80 | Call to new | test.ps1:75:1:75:9 | stream15 | provenance | | +| test.ps1:75:43:75:79 | Call to frombase64string | test.ps1:75:13:75:80 | Call to new | provenance | Config | +| test.ps1:75:71:75:78 | input15 | test.ps1:75:43:75:79 | Call to frombase64string | provenance | Config | +| test.ps1:79:1:79:8 | input16 | test.ps1:81:40:81:47 | input16 | provenance | | +| test.ps1:79:12:79:33 | Call to read-host | test.ps1:79:1:79:8 | input16 | provenance | Src:MaD:0 | +| test.ps1:84:1:84:8 | input17 | test.ps1:85:40:85:47 | input17 | provenance | | +| test.ps1:84:12:84:40 | Call to read-host | test.ps1:84:1:84:8 | input17 | provenance | Src:MaD:0 | +| test.ps1:85:1:85:8 | bytes17 | test.ps1:86:48:86:55 | bytes17 | provenance | | +| test.ps1:85:12:85:48 | Call to frombase64string | test.ps1:85:1:85:8 | bytes17 | provenance | | +| test.ps1:85:40:85:47 | input17 | test.ps1:85:12:85:48 | Call to frombase64string | provenance | Config | nodes -| test.ps1:1:1:1:16 | untrustedBase64 | semmle.label | untrustedBase64 | -| test.ps1:1:20:1:47 | Call to read-host | semmle.label | Call to read-host | -| test.ps1:3:1:3:7 | stream | semmle.label | stream | -| test.ps1:3:11:3:86 | Call to new | semmle.label | Call to new | -| test.ps1:3:41:3:85 | Call to frombase64string | semmle.label | Call to frombase64string | -| test.ps1:3:69:3:84 | untrustedBase64 | semmle.label | untrustedBase64 | -| test.ps1:4:31:4:37 | stream | semmle.label | stream | +| test.ps1:2:1:2:16 | untrustedBase64 | semmle.label | untrustedBase64 | +| test.ps1:2:20:2:47 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:4:1:4:7 | stream | semmle.label | stream | +| test.ps1:4:11:4:86 | Call to new | semmle.label | Call to new | +| test.ps1:4:41:4:85 | Call to frombase64string | semmle.label | Call to frombase64string | +| test.ps1:4:69:4:84 | untrustedBase64 | semmle.label | untrustedBase64 | +| test.ps1:5:31:5:37 | stream | semmle.label | stream | +| test.ps1:8:1:8:7 | input2 | semmle.label | input2 | +| test.ps1:8:11:8:32 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:10:1:10:8 | stream2 | semmle.label | stream2 | +| test.ps1:10:12:10:78 | Call to new | semmle.label | Call to new | +| test.ps1:10:42:10:77 | Call to frombase64string | semmle.label | Call to frombase64string | +| test.ps1:10:70:10:76 | input2 | semmle.label | input2 | +| test.ps1:11:39:11:46 | stream2 | semmle.label | stream2 | +| test.ps1:14:1:14:7 | input3 | semmle.label | input3 | +| test.ps1:14:11:14:37 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:16:1:16:8 | stream3 | semmle.label | stream3 | +| test.ps1:16:12:16:88 | Call to new | semmle.label | Call to new | +| test.ps1:16:42:16:87 | Call to getbytes | semmle.label | Call to getbytes | +| test.ps1:16:80:16:86 | input3 | semmle.label | input3 | +| test.ps1:17:36:17:43 | stream3 | semmle.label | stream3 | +| test.ps1:20:1:20:7 | input4 | semmle.label | input4 | +| test.ps1:20:11:20:38 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:22:26:22:32 | input4 | semmle.label | input4 | +| test.ps1:25:1:25:7 | input5 | semmle.label | input5 | +| test.ps1:25:11:25:43 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:27:1:27:8 | stream5 | semmle.label | stream5 | +| test.ps1:27:12:27:88 | Call to new | semmle.label | Call to new | +| test.ps1:27:42:27:87 | Call to getbytes | semmle.label | Call to getbytes | +| test.ps1:27:80:27:86 | input5 | semmle.label | input5 | +| test.ps1:28:27:28:34 | stream5 | semmle.label | stream5 | +| test.ps1:31:1:31:7 | input6 | semmle.label | input6 | +| test.ps1:31:11:31:39 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:33:1:33:8 | stream6 | semmle.label | stream6 | +| test.ps1:33:12:33:88 | Call to new | semmle.label | Call to new | +| test.ps1:33:42:33:87 | Call to getbytes | semmle.label | Call to getbytes | +| test.ps1:33:80:33:86 | input6 | semmle.label | input6 | +| test.ps1:34:27:34:34 | stream6 | semmle.label | stream6 | +| test.ps1:37:1:37:7 | input7 | semmle.label | input7 | +| test.ps1:37:11:37:36 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:39:35:39:41 | input7 | semmle.label | input7 | +| test.ps1:42:1:42:7 | input8 | semmle.label | input8 | +| test.ps1:42:11:42:32 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:43:51:43:57 | input8 | semmle.label | input8 | +| test.ps1:46:1:46:7 | input9 | semmle.label | input9 | +| test.ps1:46:11:46:44 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:47:1:47:8 | stream9 | semmle.label | stream9 | +| test.ps1:47:12:47:88 | Call to new | semmle.label | Call to new | +| test.ps1:47:42:47:87 | Call to getbytes | semmle.label | Call to getbytes | +| test.ps1:47:80:47:86 | input9 | semmle.label | input9 | +| test.ps1:48:50:48:57 | stream9 | semmle.label | stream9 | +| test.ps1:51:1:51:8 | input10 | semmle.label | input10 | +| test.ps1:51:12:51:40 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:53:19:53:57 | Call to new | semmle.label | Call to new | +| test.ps1:53:49:53:56 | input10 | semmle.label | input10 | +| test.ps1:56:1:56:8 | input11 | semmle.label | input11 | +| test.ps1:56:12:56:39 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:58:13:58:51 | Call to new | semmle.label | Call to new | +| test.ps1:58:43:58:50 | input11 | semmle.label | input11 | +| test.ps1:61:1:61:8 | input12 | semmle.label | input12 | +| test.ps1:61:12:61:41 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:63:20:63:58 | Call to new | semmle.label | Call to new | +| test.ps1:63:50:63:57 | input12 | semmle.label | input12 | +| test.ps1:66:1:66:8 | input13 | semmle.label | input13 | +| test.ps1:66:12:66:42 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:67:68:67:75 | input13 | semmle.label | input13 | +| test.ps1:70:1:70:8 | input14 | semmle.label | input14 | +| test.ps1:70:12:70:38 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:71:58:71:65 | input14 | semmle.label | input14 | +| test.ps1:74:1:74:8 | input15 | semmle.label | input15 | +| test.ps1:74:12:74:42 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:75:1:75:9 | stream15 | semmle.label | stream15 | +| test.ps1:75:13:75:80 | Call to new | semmle.label | Call to new | +| test.ps1:75:43:75:79 | Call to frombase64string | semmle.label | Call to frombase64string | +| test.ps1:75:71:75:78 | input15 | semmle.label | input15 | +| test.ps1:76:49:76:57 | stream15 | semmle.label | stream15 | +| test.ps1:79:1:79:8 | input16 | semmle.label | input16 | +| test.ps1:79:12:79:33 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:81:40:81:47 | input16 | semmle.label | input16 | +| test.ps1:84:1:84:8 | input17 | semmle.label | input17 | +| test.ps1:84:12:84:40 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:85:1:85:8 | bytes17 | semmle.label | bytes17 | +| test.ps1:85:12:85:48 | Call to frombase64string | semmle.label | Call to frombase64string | +| test.ps1:85:40:85:47 | input17 | semmle.label | input17 | +| test.ps1:86:48:86:55 | bytes17 | semmle.label | bytes17 | subpaths #select -| test.ps1:4:31:4:37 | stream | test.ps1:1:20:1:47 | Call to read-host | test.ps1:4:31:4:37 | stream | This unsafe deserializer deserializes on a $@. | test.ps1:1:20:1:47 | Call to read-host | read from stdin | +| test.ps1:5:31:5:37 | stream | test.ps1:2:20:2:47 | Call to read-host | test.ps1:5:31:5:37 | stream | This unsafe deserializer deserializes on a $@. | test.ps1:2:20:2:47 | Call to read-host | read from stdin | +| test.ps1:11:39:11:46 | stream2 | test.ps1:8:11:8:32 | Call to read-host | test.ps1:11:39:11:46 | stream2 | This unsafe deserializer deserializes on a $@. | test.ps1:8:11:8:32 | Call to read-host | read from stdin | +| test.ps1:17:36:17:43 | stream3 | test.ps1:14:11:14:37 | Call to read-host | test.ps1:17:36:17:43 | stream3 | This unsafe deserializer deserializes on a $@. | test.ps1:14:11:14:37 | Call to read-host | read from stdin | +| test.ps1:22:26:22:32 | input4 | test.ps1:20:11:20:38 | Call to read-host | test.ps1:22:26:22:32 | input4 | This unsafe deserializer deserializes on a $@. | test.ps1:20:11:20:38 | Call to read-host | read from stdin | +| test.ps1:28:27:28:34 | stream5 | test.ps1:25:11:25:43 | Call to read-host | test.ps1:28:27:28:34 | stream5 | This unsafe deserializer deserializes on a $@. | test.ps1:25:11:25:43 | Call to read-host | read from stdin | +| test.ps1:34:27:34:34 | stream6 | test.ps1:31:11:31:39 | Call to read-host | test.ps1:34:27:34:34 | stream6 | This unsafe deserializer deserializes on a $@. | test.ps1:31:11:31:39 | Call to read-host | read from stdin | +| test.ps1:39:35:39:41 | input7 | test.ps1:37:11:37:36 | Call to read-host | test.ps1:39:35:39:41 | input7 | This unsafe deserializer deserializes on a $@. | test.ps1:37:11:37:36 | Call to read-host | read from stdin | +| test.ps1:43:51:43:57 | input8 | test.ps1:42:11:42:32 | Call to read-host | test.ps1:43:51:43:57 | input8 | This unsafe deserializer deserializes on a $@. | test.ps1:42:11:42:32 | Call to read-host | read from stdin | +| test.ps1:48:50:48:57 | stream9 | test.ps1:46:11:46:44 | Call to read-host | test.ps1:48:50:48:57 | stream9 | This unsafe deserializer deserializes on a $@. | test.ps1:46:11:46:44 | Call to read-host | read from stdin | +| test.ps1:53:19:53:57 | Call to new | test.ps1:51:12:51:40 | Call to read-host | test.ps1:53:19:53:57 | Call to new | This unsafe deserializer deserializes on a $@. | test.ps1:51:12:51:40 | Call to read-host | read from stdin | +| test.ps1:58:13:58:51 | Call to new | test.ps1:56:12:56:39 | Call to read-host | test.ps1:58:13:58:51 | Call to new | This unsafe deserializer deserializes on a $@. | test.ps1:56:12:56:39 | Call to read-host | read from stdin | +| test.ps1:63:20:63:58 | Call to new | test.ps1:61:12:61:41 | Call to read-host | test.ps1:63:20:63:58 | Call to new | This unsafe deserializer deserializes on a $@. | test.ps1:61:12:61:41 | Call to read-host | read from stdin | +| test.ps1:67:68:67:75 | input13 | test.ps1:66:12:66:42 | Call to read-host | test.ps1:67:68:67:75 | input13 | This unsafe deserializer deserializes on a $@. | test.ps1:66:12:66:42 | Call to read-host | read from stdin | +| test.ps1:71:58:71:65 | input14 | test.ps1:70:12:70:38 | Call to read-host | test.ps1:71:58:71:65 | input14 | This unsafe deserializer deserializes on a $@. | test.ps1:70:12:70:38 | Call to read-host | read from stdin | +| test.ps1:76:49:76:57 | stream15 | test.ps1:74:12:74:42 | Call to read-host | test.ps1:76:49:76:57 | stream15 | This unsafe deserializer deserializes on a $@. | test.ps1:74:12:74:42 | Call to read-host | read from stdin | +| test.ps1:81:40:81:47 | input16 | test.ps1:79:12:79:33 | Call to read-host | test.ps1:81:40:81:47 | input16 | This unsafe deserializer deserializes on a $@. | test.ps1:79:12:79:33 | Call to read-host | read from stdin | +| test.ps1:86:48:86:55 | bytes17 | test.ps1:84:12:84:40 | Call to read-host | test.ps1:86:48:86:55 | bytes17 | This unsafe deserializer deserializes on a $@. | test.ps1:84:12:84:40 | Call to read-host | read from stdin | diff --git a/powershell/ql/test/query-tests/security/cwe-502/test.ps1 b/powershell/ql/test/query-tests/security/cwe-502/test.ps1 index d35ef352cd38..69bff48f74fe 100644 --- a/powershell/ql/test/query-tests/security/cwe-502/test.ps1 +++ b/powershell/ql/test/query-tests/security/cwe-502/test.ps1 @@ -1,4 +1,86 @@ +# Test 1: BinaryFormatter.Deserialize (existing) $untrustedBase64 = Read-Host "Enter user input" $formatter = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter $stream = [System.IO.MemoryStream]::new([Convert]::FromBase64String($untrustedBase64)) $obj = $formatter.Deserialize($stream) + +# Test 2: BinaryFormatter.UnsafeDeserialize +$input2 = Read-Host "Enter data" +$formatter2 = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter +$stream2 = [System.IO.MemoryStream]::new([Convert]::FromBase64String($input2)) +$obj2 = $formatter2.UnsafeDeserialize($stream2, $null) + +# Test 3: SoapFormatter.Deserialize +$input3 = Read-Host "Enter soap data" +$soapFormatter = New-Object System.Runtime.Serialization.Formatters.Soap.SoapFormatter +$stream3 = [System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes($input3)) +$obj3 = $soapFormatter.Deserialize($stream3) + +# Test 4: ObjectStateFormatter.Deserialize +$input4 = Read-Host "Enter state data" +$osf = New-Object System.Web.UI.ObjectStateFormatter +$obj4 = $osf.Deserialize($input4) + +# Test 5: NetDataContractSerializer.Deserialize +$input5 = Read-Host "Enter serialized data" +$ndcs = New-Object System.Runtime.Serialization.NetDataContractSerializer +$stream5 = [System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes($input5)) +$obj5 = $ndcs.Deserialize($stream5) + +# Test 6: NetDataContractSerializer.ReadObject +$input6 = Read-Host "Enter object data" +$ndcs2 = New-Object System.Runtime.Serialization.NetDataContractSerializer +$stream6 = [System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes($input6)) +$obj6 = $ndcs2.ReadObject($stream6) + +# Test 7: LosFormatter.Deserialize +$input7 = Read-Host "Enter LOS data" +$losFormatter = New-Object System.Web.UI.LosFormatter +$obj7 = $losFormatter.Deserialize($input7) + +# Test 8: XamlReader.Parse (static) +$input8 = Read-Host "Enter XAML" +$obj8 = [System.Windows.Markup.XamlReader]::Parse($input8) + +# Test 9: XamlReader.Load (static) +$input9 = Read-Host "Enter XAML stream data" +$stream9 = [System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes($input9)) +$obj9 = [System.Windows.Markup.XamlReader]::Load($stream9) + +# Test 10: DataSet.ReadXmlSchema +$input10 = Read-Host "Enter schema data" +$ds = New-Object System.Data.DataSet +$ds.ReadXmlSchema([System.IO.StringReader]::new($input10)) + +# Test 11: DataTable.ReadXml +$input11 = Read-Host "Enter table data" +$dt = New-Object System.Data.DataTable +$dt.ReadXml([System.IO.StringReader]::new($input11)) + +# Test 12: DataTable.ReadXmlSchema +$input12 = Read-Host "Enter table schema" +$dt2 = New-Object System.Data.DataTable +$dt2.ReadXmlSchema([System.IO.StringReader]::new($input12)) + +# Test 13: ResourceReader constructor (New-Object) +$input13 = Read-Host "Enter resource path" +$reader = New-Object System.Resources.ResourceReader -ArgumentList $input13 + +# Test 14: ResXResourceReader constructor ([Type]::new()) +$input14 = Read-Host "Enter resx path" +$resxReader = [System.Resources.ResXResourceReader]::new($input14) + +# Test 15: Activity.Load (static) +$input15 = Read-Host "Enter activity data" +$stream15 = [System.IO.MemoryStream]::new([Convert]::FromBase64String($input15)) +[System.Workflow.ComponentModel.Activity]::Load($stream15, $null) + +# Test 16: YamlDotNet.Serialization.Deserializer.Deserialize +$input16 = Read-Host "Enter YAML" +$yamlDeserializer = New-Object YamlDotNet.Serialization.Deserializer +$obj16 = $yamlDeserializer.Deserialize($input16) + +# Test 17: MemoryPackSerializer.Deserialize (static) +$input17 = Read-Host "Enter packed data" +$bytes17 = [Convert]::FromBase64String($input17) +[MemoryPack.MemoryPackSerializer]::Deserialize($bytes17)