CVE-2022-2084 (published 2023-04-19T22:15Z)

Sensitive data could be exposed in world readable logs of cloud-init before version 22.3 when schema failures are reported. This leak could include hashed passwords.

CVSS v3 base score: 5.5, MEDIUM (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-532
cwe_name: Insertion of Sensitive Information into Log File
description: Information written to log files can be of a sensitive nature and give valuable guidance to an attacker or expose sensitive user information.
extended_description: 
            While logging all information may be helpful during development stages, it is important that logging levels be set appropriately before a product ships so that sensitive user data and system information are not accidentally exposed to potential attackers.
            Different log files may be produced and stored for:
            
               Server log files (e.g. server.log).  This can give information on whatever application left the file. Usually this can give full path names and system information, and sometimes usernames and passwords.
               log files that are used for debugging
            
         
         

Fix information:
repo_url: https://github.com/canonical/cloud-init
commit_details: repo_url: https://github.com/canonical/cloud-init
author: James Falcon
author_date: 2022-06-29 17:27:44-05:00
msg: Remove schema errors from log (#1551)

When schema errors are encountered, the section of userdata in question
gets printed to the cloud-init log. As this could contain sensitive
data, so log a generic warning instead and redirect user to run
cloud-init schema --system as root.

LP: #1978422
CVE: 2022-2084
repository: repo_url: https://github.com/canonical/cloud-init
repo_name: canonical/cloud-init
description: Official upstream for the cloud-init: cloud instance initialization
repo_language: Python
owner: canonical
file_changes: filename: main.py
change_type: ModificationType.MODIFY
diff: @@ -455,7 +455,9 @@ def main_init(name, args):
 
     # Validate user-data adheres to schema definition
     if os.path.exists(init.paths.get_ipath_cur("userdata_raw")):
-        validate_cloudconfig_schema(config=init.cfg, strict=False)
+        validate_cloudconfig_schema(
+            config=init.cfg, strict=False, log_details=False
+        )
     else:
         LOG.debug("Skipping user-data validation. No user-data found.")
 

programming_language: Python
filename: schema.py
change_type: ModificationType.MODIFY
diff: @@ -198,6 +198,7 @@ def validate_cloudconfig_schema(
     schema: dict = None,
     strict: bool = False,
     strict_metaschema: bool = False,
+    log_details: bool = True,
 ):
     """Validate provided config meets the schema definition.
 
@@ -210,6 +211,9 @@ def validate_cloudconfig_schema(
        logging warnings.
     @param strict_metaschema: Boolean, when True validates schema using strict
        metaschema definition at runtime (currently unused)
+    @param log_details: Boolean, when True logs details of validation errors.
+       If there are concerns about logging sensitive userdata, this should
+       be set to False.
 
     @raises: SchemaValidationError when provided config does not validate
         against the provided schema.
@@ -234,12 +238,17 @@ def validate_cloudconfig_schema(
         errors += ((path, error.message),)
     if errors:
         if strict:
+            # This could output/log sensitive data
             raise SchemaValidationError(errors)
-        else:
+        if log_details:
             messages = ["{0}: {1}".format(k, msg) for k, msg in errors]
-            LOG.warning(
-                "Invalid cloud-config provided:\n%s", "\n".join(messages)
+            details = "\n" + "\n".join(messages)
+        else:
+            details = (
+                "Please run 'sudo cloud-init schema --system' to "
+                "see the schema errors."
             )
+        LOG.warning("Invalid cloud-config provided: %s", details)
 
 
 def annotated_cloudconfig_file(

programming_language: Python
filename: test_cli.py
change_type: ModificationType.MODIFY
diff: @@ -18,11 +18,18 @@ runcmd:
   - echo 'hi' > /var/tmp/test
 """
 
+# The '-' in 'hashed-password' fails schema validation
 INVALID_USER_DATA_SCHEMA = """\
 #cloud-config
-updates:
- notnetwork: -1
-apt_pipelining: bogus
+users:
+  - default
+  - name: newsuper
+    gecos: Big Stuff
+    groups: users, admin
+    sudo: ALL=(ALL) NOPASSWD:ALL
+    hashed-password: asdfasdf
+    shell: /bin/bash
+    lock_passwd: true
 """
 
 
@@ -69,11 +76,12 @@ def test_invalid_userdata_schema(client: IntegrationInstance):
     assert result.ok
     log = client.read_from_file("/var/log/cloud-init.log")
     warning = (
-        "[WARNING]: Invalid cloud-config provided:\napt_pipelining: 'bogus'"
-        " is not valid under any of the given schemas\nupdates: Additional"
-        " properties are not allowed ('notnetwork' was unexpected)"
+        "[WARNING]: Invalid cloud-config provided: Please run "
+        "'sudo cloud-init schema --system' to see the schema errors."
     )
     assert warning in log
+    assert "asdfasdf" not in log
+
     result = client.execute("cloud-init status --long")
     if not result.ok:
         raise AssertionError(

programming_language: Python
filename: test_schema.py
change_type: ModificationType.MODIFY
diff: @@ -286,10 +286,31 @@ class TestValidateCloudConfigSchema:
         assert "cloudinit.config.schema" == module
         assert logging.WARNING == log_level
         assert (
-            "Invalid cloud-config provided:\np1: -1 is not of type 'string'"
+            "Invalid cloud-config provided: \np1: -1 is not of type 'string'"
             == log_msg
         )
 
+    @skipUnlessJsonSchema()
+    def test_validateconfig_schema_sensitive(self, caplog):
+        """When log_details=False, ensure details are omitted"""
+        schema = {
+            "properties": {"hashed_password": {"type": "string"}},
+            "additionalProperties": False,
+        }
+        validate_cloudconfig_schema(
+            {"hashed-password": "secret"},
+            schema,
+            strict=False,
+            log_details=False,
+        )
+        [(module, log_level, log_msg)] = caplog.record_tuples
+        assert "cloudinit.config.schema" == module
+        assert logging.WARNING == log_level
+        assert (
+            "Invalid cloud-config provided: Please run 'sudo cloud-init "
+            "schema --system' to see the schema errors." == log_msg
+        )
+
     @skipUnlessJsonSchema()
     def test_validateconfig_schema_emits_warning_on_missing_jsonschema(
         self, caplog

programming_language: Python