diff --git a/csharp/ql/lib/change-notes/2024-03-11-registry-sources.md b/csharp/ql/lib/change-notes/2024-03-11-registry-sources.md new file mode 100644 index 00000000000..1d105049185 --- /dev/null +++ b/csharp/ql/lib/change-notes/2024-03-11-registry-sources.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added the `windows-registry` source kind and threat model to represent values which come from the registry on Windows. diff --git a/csharp/ql/lib/ext/Microsoft.Win32.model.yml b/csharp/ql/lib/ext/Microsoft.Win32.model.yml new file mode 100644 index 00000000000..c7e439f2910 --- /dev/null +++ b/csharp/ql/lib/ext/Microsoft.Win32.model.yml @@ -0,0 +1,9 @@ +extensions: + - addsTo: + pack: codeql/csharp-all + extensible: sourceModel + data: + - ["Microsoft.Win32", "Registry", False, "GetValue", "(System.String,System.String,System.Object)", "", "ReturnValue", "windows-registry", "manual"] + - ["Microsoft.Win32", "RegistryKey", False, "GetSubKeyNames", "()", "", "ReturnValue", "windows-registry", "manual"] + - ["Microsoft.Win32", "RegistryKey", False, "GetValue", "", "", "ReturnValue", "windows-registry", "manual"] + - ["Microsoft.Win32", "RegistryKey", False, "GetValueNames", "()", "", "ReturnValue", "windows-registry", "manual"] \ No newline at end of file diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Local.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Local.qll index 9f3f398e5b1..7ad656e11d3 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Local.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Local.qll @@ -55,3 +55,16 @@ abstract class CommandLineArgumentSource extends LocalFlowSource { private class MainMethodArgumentSource extends CommandLineArgumentSource { MainMethodArgumentSource() { this.asParameter() = any(MainMethod mainMethod).getAParameter() } } + +/** + * A data flow source that represents the access of a value from the Windows registry. + */ +abstract class WindowsRegistrySource extends LocalFlowSource { + override string getThreatModel() { result = "windows-registry" } + + override string getSourceType() { result = "a value from the Windows registry" } +} + +private class ExternalWindowsRegistrySource extends WindowsRegistrySource { + ExternalWindowsRegistrySource() { sourceNode(this, "windows-registry") } +} diff --git a/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/Registry.expected b/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/Registry.expected new file mode 100644 index 00000000000..9a5b8ef2c0e --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/Registry.expected @@ -0,0 +1,8 @@ +| UseRegistry.cs:10:36:10:58 | call to method GetValue | windows-registry | +| UseRegistry.cs:16:36:16:58 | call to method GetValue | windows-registry | +| UseRegistry.cs:22:36:22:58 | call to method GetValue | windows-registry | +| UseRegistry.cs:28:36:28:58 | call to method GetValue | windows-registry | +| UseRegistry.cs:34:36:34:58 | call to method GetValue | windows-registry | +| UseRegistry.cs:40:36:40:58 | call to method GetValue | windows-registry | +| UseRegistry.cs:46:35:46:53 | call to method GetValueNames | windows-registry | +| UseRegistry.cs:52:36:52:55 | call to method GetSubKeyNames | windows-registry | diff --git a/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/Registry.ext.yml b/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/Registry.ext.yml new file mode 100644 index 00000000000..71007f3394c --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/Registry.ext.yml @@ -0,0 +1,7 @@ +extensions: + + - addsTo: + pack: codeql/threat-models + extensible: threatModelConfiguration + data: + - ["windows-registry", true, 0] \ No newline at end of file diff --git a/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/Registry.ql b/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/Registry.ql new file mode 100644 index 00000000000..9e2934e9e19 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/Registry.ql @@ -0,0 +1,6 @@ +import csharp +import semmle.code.csharp.security.dataflow.flowsources.FlowSources + +from DataFlow::Node source +where source instanceof ThreatModelFlowSource +select source, source.(SourceNode).getThreatModel() diff --git a/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/UseRegistry.cs b/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/UseRegistry.cs new file mode 100644 index 00000000000..e5698127be4 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/UseRegistry.cs @@ -0,0 +1,55 @@ +using Microsoft.Win32; + +namespace Test +{ + class UseRegistry + { + public static void GetRegistryValue(string keyName, string valueName) + { + RegistryKey key = Registry.LocalMachine.OpenSubKey(keyName); + string value = (string)key.GetValue(valueName); + } + + public static void GetRegistryValue2(string keyName, string valueName) + { + RegistryKey key = Registry.CurrentUser.OpenSubKey(keyName); + string value = (string)key.GetValue(valueName); + } + + public static void GetRegistryValue3(string keyName, string valueName) + { + RegistryKey key = Registry.ClassesRoot.OpenSubKey(keyName); + string value = (string)key.GetValue(valueName); + } + + public static void GetRegistryValue4(string keyName, string valueName) + { + RegistryKey key = Registry.Users.OpenSubKey(keyName); + string value = (string)key.GetValue(valueName); + } + + public static void GetRegistryValue5(string keyName, string valueName) + { + RegistryKey key = Registry.CurrentConfig.OpenSubKey(keyName); + string value = (string)key.GetValue(valueName); + } + + public static void GetRegistryValue6(string keyName, string valueName) + { + RegistryKey key = Registry.PerformanceData.OpenSubKey(keyName); + string value = (string)key.GetValue(valueName); + } + + public static void GetRegistryValueNames(string keyName, string valueName) + { + RegistryKey key = Registry.LocalMachine.OpenSubKey(keyName); + string[] valueNames = key.GetValueNames(); + } + + public static void GetRegistrySubKeyNames(string keyName, string valueName) + { + RegistryKey key = Registry.LocalMachine.OpenSubKey(keyName); + string[] subKeyNames = key.GetSubKeyNames(); + } + } +} \ No newline at end of file diff --git a/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/options b/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/options new file mode 100644 index 00000000000..f28b8af0ae4 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/flowsources/local/registry/options @@ -0,0 +1,3 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj +semmle-extractor-options: ${testdir}/../../../../../resources/stubs/Microsoft.VisualStudio.TestTools.UnitTesting.cs diff --git a/shared/mad/codeql/mad/ModelValidation.qll b/shared/mad/codeql/mad/ModelValidation.qll index 2f990af4e0f..bb3b8c174b9 100644 --- a/shared/mad/codeql/mad/ModelValidation.qll +++ b/shared/mad/codeql/mad/ModelValidation.qll @@ -120,7 +120,7 @@ module KindValidation { // Java "android-external-storage-dir", "contentprovider", // C# - "file-write", + "file-write", "windows-registry", // JavaScript "database-access-result" ] diff --git a/shared/threat-models/ext/threat-model-grouping.model.yml b/shared/threat-models/ext/threat-model-grouping.model.yml index 53107c1e32b..7cc650d3341 100644 --- a/shared/threat-models/ext/threat-model-grouping.model.yml +++ b/shared/threat-models/ext/threat-model-grouping.model.yml @@ -16,6 +16,7 @@ extensions: - ["commandargs", "local"] - ["environment", "local"] - ["file", "local"] + - ["windows-registry", "local"] # Android threat models - ["android-external-storage-dir", "android"]