VYPR
Critical severityNVD Advisory· Published Mar 27, 2018· Updated Aug 5, 2024

CVE-2018-9057

CVE-2018-9057

Description

aws/resource_aws_iam_user_login_profile.go in the HashiCorp Terraform Amazon Web Services (AWS) provider through v1.12.0 has an inappropriate PRNG algorithm and seeding, which makes it easier for remote attackers to obtain access by leveraging an IAM account that was provisioned with a weak password.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Terraform AWS provider ≤v1.12.0 used predictable random password generator seeded with time, enabling brute-force attacks on IAM user passwords.

Vulnerability

The generatePassword function in resource_aws_iam_user_login_profile.go of the HashiCorp Terraform AWS provider through v1.12.0 used the math/rand pseudorandom number generator seeded with time.Now().UTC().UnixNano(), resulting in predictable and cryptographically insecure passwords [1][2][3].

Exploitation

An attacker who can estimate the approximate creation time of an IAM user can reconstruct the same password sequence by seeding math/rand with the corresponding time. Because the password generation pattern is deterministic, the attacker can brute-force the password offline or by attempting login within a small window of time [3][4].

Impact

Successful exploitation allows an attacker to log in as the provisioned IAM user. The impact depends on the user's permissions but could include information disclosure, resource modification, or privilege escalation within AWS [1].

Mitigation

The vulnerability was fixed in commit efa8cd4 by replacing math/rand with crypto/rand and increasing the minimum password length to 5 [2]. Users should update the provider to a version after v1.12.0. For existing IAM users, reset their passwords using a secure method [4].

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/hashicorp/terraform-provider-awsGo
< 1.14.01.14.0

Affected products

3

Patches

1
efa8cd45c648

Merge pull request #3989 from terraform-providers/jbardin/password

2 files changed · +94 29
  • aws/resource_aws_iam_user_login_profile.go+57 28 modified
    @@ -1,10 +1,11 @@
     package aws
     
     import (
    +	"bytes"
    +	"crypto/rand"
     	"fmt"
     	"log"
    -	"math/rand"
    -	"time"
    +	"math/big"
     
     	"github.com/aws/aws-sdk-go/aws"
     	"github.com/aws/aws-sdk-go/aws/awserr"
    @@ -40,7 +41,7 @@ func resourceAwsIamUserLoginProfile() *schema.Resource {
     				Type:         schema.TypeInt,
     				Optional:     true,
     				Default:      20,
    -				ValidateFunc: validation.IntBetween(4, 128),
    +				ValidateFunc: validation.IntBetween(5, 128),
     			},
     
     			"key_fingerprint": {
    @@ -55,35 +56,62 @@ func resourceAwsIamUserLoginProfile() *schema.Resource {
     	}
     }
     
    -// generatePassword generates a random password of a given length using
    -// characters that are likely to satisfy any possible AWS password policy
    -// (given sufficient length).
    -func generatePassword(length int) string {
    -	charsets := []string{
    -		"abcdefghijklmnopqrstuvwxyz",
    -		"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
    -		"012346789",
    -		"!@#$%^&*()_+-=[]{}|'",
    -	}
    +const (
    +	charLower   = "abcdefghijklmnopqrstuvwxyz"
    +	charUpper   = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    +	charNumbers = "0123456789"
    +	charSymbols = "!@#$%^&*()_+-=[]{}|'"
    +)
     
    -	// Use all character sets
    -	random := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    -	components := make(map[int]byte, length)
    -	for i := 0; i < length; i++ {
    -		charset := charsets[i%len(charsets)]
    -		components[i] = charset[random.Intn(len(charset))]
    -	}
    +// generateIAMPassword generates a random password of a given length, matching the
    +// most restrictive iam password policy.
    +func generateIAMPassword(length int) string {
    +	const charset = charLower + charUpper + charNumbers + charSymbols
     
    -	// Randomise the ordering so we don't end up with a predictable
    -	// lower case, upper case, numeric, symbol pattern
     	result := make([]byte, length)
    -	i := 0
    -	for _, b := range components {
    -		result[i] = b
    -		i = i + 1
    +	charsetSize := big.NewInt(int64(len(charset)))
    +
    +	// rather than trying to artifically add specific characters from each
    +	// class to the password to match the policy, we generate passwords
    +	// randomly and reject those that don't match.
    +	//
    +	// Even in the worst case, this tends to take less than 10 tries to find a
    +	// matching password. Any sufficiently long password is likely to succeed
    +	// on the first try
    +	for n := 0; n < 100000; n++ {
    +		for i := range result {
    +			r, err := rand.Int(rand.Reader, charsetSize)
    +			if err != nil {
    +				panic(err)
    +			}
    +			if !r.IsInt64() {
    +				panic("rand.Int() not representable as an Int64")
    +			}
    +
    +			result[i] = charset[r.Int64()]
    +		}
    +
    +		if !checkIAMPwdPolicy(result) {
    +			continue
    +		}
    +
    +		return string(result)
    +	}
    +
    +	panic("failed to generate acceptable password")
    +}
    +
    +// Check the generated password contains all character classes listed in the
    +// IAM password policy.
    +func checkIAMPwdPolicy(pass []byte) bool {
    +	if !(bytes.ContainsAny(pass, charLower) &&
    +		bytes.ContainsAny(pass, charNumbers) &&
    +		bytes.ContainsAny(pass, charSymbols) &&
    +		bytes.ContainsAny(pass, charUpper)) {
    +		return false
     	}
     
    -	return string(result)
    +	return true
     }
     
     func resourceAwsIamUserLoginProfileCreate(d *schema.ResourceData, meta interface{}) error {
    @@ -113,7 +141,8 @@ func resourceAwsIamUserLoginProfileCreate(d *schema.ResourceData, meta interface
     		}
     	}
     
    -	initialPassword := generatePassword(passwordLength)
    +	initialPassword := generateIAMPassword(passwordLength)
    +
     	fingerprint, encrypted, err := encryption.EncryptValue(encryptionKey, initialPassword, "Password")
     	if err != nil {
     		return err
    
  • aws/resource_aws_iam_user_login_profile_test.go+37 1 modified
    @@ -20,6 +20,42 @@ import (
     	"github.com/hashicorp/vault/helper/pgpkeys"
     )
     
    +func TestGenerateIAMPassword(t *testing.T) {
    +	p := generateIAMPassword(6)
    +	if len(p) != 6 {
    +		t.Fatalf("expected a 6 character password, got: %q", p)
    +	}
    +
    +	p = generateIAMPassword(128)
    +	if len(p) != 128 {
    +		t.Fatalf("expected a 128 character password, got: %q", p)
    +	}
    +}
    +
    +func TestIAMPasswordPolicyCheck(t *testing.T) {
    +	for _, tc := range []struct {
    +		pass  string
    +		valid bool
    +	}{
    +		// no symbol
    +		{pass: "abCD12", valid: false},
    +		// no number
    +		{pass: "abCD%$", valid: false},
    +		// no upper
    +		{pass: "abcd1#", valid: false},
    +		// no lower
    +		{pass: "ABCD1#", valid: false},
    +		{pass: "abCD11#$", valid: true},
    +	} {
    +		t.Run(tc.pass, func(t *testing.T) {
    +			valid := checkIAMPwdPolicy([]byte(tc.pass))
    +			if valid != tc.valid {
    +				t.Fatalf("expected %q to be valid==%t, got %t", tc.pass, tc.valid, valid)
    +			}
    +		})
    +	}
    +}
    +
     func TestAccAWSUserLoginProfile_basic(t *testing.T) {
     	var conf iam.GetLoginProfileOutput
     
    @@ -189,7 +225,7 @@ func testDecryptPasswordAndTest(nProfile, nAccessKey, key string) resource.TestC
     			iamAsCreatedUser := iam.New(iamAsCreatedUserSession)
     			_, err = iamAsCreatedUser.ChangePassword(&iam.ChangePasswordInput{
     				OldPassword: aws.String(decryptedPassword.String()),
    -				NewPassword: aws.String(generatePassword(20)),
    +				NewPassword: aws.String(generateIAMPassword(20)),
     			})
     			if err != nil {
     				if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "InvalidClientTokenId" {
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.