CVE-2022-23506 (published 2023-01-03T21:15Z)

Spinnaker is an open source, multi-cloud continuous delivery platform for releasing software changes, and Spinnaker's Rosco microservice produces machine images. Rosco prior to versions 1.29.2, 1.28.4, and 1.27.3 does not property mask secrets generated via packer builds.  This can lead to exposure of sensitive AWS credentials in packer log files. Versions 1.29.2, 1.28.4, and 1.27.3 of Rosco contain fixes for this issue.\n\nA workaround is available. It's recommended to use short lived credentials via role assumption and IAM profiles. Additionally, credentials can be set in `/home/spinnaker/.aws/credentials` and `/home/spinnaker/.aws/config` as a volume mount for Rosco pods vs. setting credentials in roscos bake config properties. Last even with those it's recommend to use IAM Roles vs. long lived credentials. This drastically mitigates the risk of credentials exposure. If users have used static credentials, it's recommended to purge any bake logs for AWS, evaluate whether AWS_ACCESS_KEY, SECRET_KEY and/or other sensitive data has been introduced in log files and bake job logs. Then, rotate these credentials and evaluate potential improper use of those credentials.

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/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/spinnaker/rosco
commit_details: repo_url: https://github.com/spinnaker/rosco
author: David Byron
author_date: 2022-12-08 12:50:39-08:00
msg: Merge pull request from GHSA-wqq8-664f-54hh

* test(core): verify that JobRequest.maskedTokenizedCommand really masks

* feat(core): add aws.bakery-defaults.maskedPackerParameters configuration parameter

with default value [ 'aws_access_key', 'aws_secret_key' ] to match what AWSBakeHandler uses and hide potentially secret information by default.
repository: repo_url: https://github.com/spinnaker/rosco
repo_name: spinnaker/rosco
description: A bakery for deployable images
repo_language: Groovy
owner: spinnaker
file_changes: filename: AWSBakeHandler.groovy
change_type: ModificationType.MODIFY
diff: @@ -231,6 +231,11 @@ public class AWSBakeHandler extends CloudProviderBakeHandler {
     return new Bake(id: bakeId, ami: amiId, image_name: imageName, artifacts: artifacts)
   }
 
+  @Override
+  List<String> getMaskedPackerParameters() {
+    return awsBakeryDefaults.maskedPackerParameters
+  }
+
   private String lookupAmiByName(String name, String region, String account, VmType vmType, boolean mostRecent) {
     def images = AuthenticatedRequest.allowAnonymous(
       {
@@ -247,7 +252,7 @@ public class AWSBakeHandler extends CloudProviderBakeHandler {
     } else {
       image = images?.find { it.attributes.virtualizationType == vmType }
     }
-    
+
     return image?.amis?.get(region)?.first()
   }
 }

programming_language: Scala
filename: RoscoAWSConfiguration.groovy
change_type: ModificationType.MODIFY
diff: @@ -58,6 +58,7 @@ class RoscoAWSConfiguration {
     String templateFile
     BakeRequest.VmType defaultVirtualizationType
     List<AWSOperatingSystemVirtualizationSettings> baseImages = []
+    List<String> maskedPackerParameters = [ 'aws_access_key', 'aws_secret_key' ]
   }
 
   static class AWSOperatingSystemVirtualizationSettings {

programming_language: Scala
filename: AWSBakeHandlerSpec.groovy
change_type: ModificationType.MODIFY
diff: @@ -1441,6 +1441,32 @@ class AWSBakeHandlerSpec extends Specification implements TestDefaults {
       1 * packerCommandFactoryMock.buildPackerCommand("", parameterMap, null, "$configDir/$awsBakeryDefaults.templateFile")
   }
 
+  void 'getMaskedPackerParameters returns the expected default'() {
+    setup:
+      @Subject
+      AWSBakeHandler awsBakeHandler = new AWSBakeHandler(awsBakeryDefaults: new RoscoAWSConfiguration.AWSBakeryDefaults())
+
+    when:
+      def maskedPackerParams = awsBakeHandler.maskedPackerParameters
+
+    then:
+      maskedPackerParams == [ 'aws_access_key', 'aws_secret_key' ]
+  }
+
+  void 'getMaskedPackerParameters returns the expected default'() {
+    setup:
+      def paramsToMask = [ 'foo' ]
+      @Subject
+      AWSBakeHandler awsBakeHandler = new AWSBakeHandler(awsBakeryDefaults: new RoscoAWSConfiguration.AWSBakeryDefaults(maskedPackerParameters: paramsToMask))
+
+
+    when:
+      def maskedPackerParams = awsBakeHandler.maskedPackerParameters
+
+    then:
+      maskedPackerParams == paramsToMask
+  }
+
   static class NoSleepRetry extends RetrySupport {
     void sleep(long time) {}
   }

programming_language: Swift
filename: LocalJobFriendlyPackerCommandFactorySpec.groovy
change_type: ModificationType.MODIFY
diff: @@ -75,18 +75,19 @@ class LocalJobFriendlyPackerCommandFactorySpec extends Specification implements
     when:
       def packerCommand = packerCommandFactory.buildPackerCommand("", parameterMap, null, "")
       def jobRequest = new JobRequest(tokenizedCommand: packerCommand, maskedParameters: maskedPackerParameters, jobId: SOME_UUID)
-      def commandLine = new CommandLine(jobRequest.tokenizedCommand[0])
-      def arguments = (String []) Arrays.copyOfRange(jobRequest.tokenizedCommand.toArray(), 1, jobRequest.tokenizedCommand.size())
+      def maskedTokenizedCommand = jobRequest.maskedTokenizedCommand
+      def commandLine = new CommandLine(maskedTokenizedCommand[0])
+      def arguments = (String []) Arrays.copyOfRange(maskedTokenizedCommand.toArray(), 1, maskedTokenizedCommand.size())
       commandLine.addArguments(arguments, false)
       def g = commandLine.toString()
       def cmdLineList =  commandLine.toStrings().toList()
 
-
     then:
       cmdLineList  == expectedCommandLine
 
     where:
-      parameterMap                          | maskedPackerParameters | expectedCommandLine
-      [packages: "package1 package2"]       | []                     | ["packer", "build", "-color=false", "-var", "packages=package1 package2"]
+      parameterMap                                              | maskedPackerParameters | expectedCommandLine
+      [packages: "package1 package2"]                           | []                     | ["packer", "build", "-color=false", "-var", "packages=package1 package2"]
+      [packages: "package1 package2", secret: "mysecret"]       | ["secret"]             | ["packer", "build", "-color=false", "-var", "packages=package1 package2", "-var", "secret=******"]
   }
 }

programming_language: Scala